Merge branch 'various/UI' into 'master'
Various/ui See merge request framasoft/mobilizon!108
This commit is contained in:
commit
f13ea335cc
2
.gitignore
vendored
2
.gitignore
vendored
@ -31,3 +31,5 @@ cover/
|
||||
uploads/*
|
||||
!uploads/.gitkeep
|
||||
.idea
|
||||
*.mo
|
||||
*.po~
|
||||
|
@ -35,7 +35,7 @@ setup_js_deps:
|
||||
before_script:
|
||||
- cd js
|
||||
script:
|
||||
- npm install
|
||||
- yarn install
|
||||
after_script:
|
||||
- cd ../
|
||||
cache:
|
||||
@ -46,9 +46,9 @@ js:
|
||||
stage: front
|
||||
before_script:
|
||||
- cd js
|
||||
- npm install
|
||||
- yarn install
|
||||
script:
|
||||
- npm run build
|
||||
- yarn run build
|
||||
after_script:
|
||||
- cd ../
|
||||
cache:
|
||||
@ -65,10 +65,9 @@ js_deps:
|
||||
stage: front
|
||||
before_script:
|
||||
- cd js
|
||||
- npm install
|
||||
- npm install -g npm-check-updates
|
||||
- yarn install
|
||||
script:
|
||||
- ncu --error-level 2
|
||||
- yarn outdated
|
||||
after_script:
|
||||
- cd ../
|
||||
cache:
|
||||
@ -80,9 +79,9 @@ js_check:
|
||||
stage: front
|
||||
before_script:
|
||||
- cd js
|
||||
- npm install
|
||||
- yarn install
|
||||
script:
|
||||
- npm run lint
|
||||
- yarn run lint
|
||||
after_script:
|
||||
- cd ../
|
||||
cache:
|
||||
|
@ -11,6 +11,7 @@ config :mobilizon,
|
||||
|
||||
config :mobilizon, :instance,
|
||||
name: System.get_env("MOBILIZON_INSTANCE_NAME") || "Localhost",
|
||||
description: System.get_env("MOBILIZON_INSTANCE_DESCRIPTION") || "This is a Mobilizon instance",
|
||||
version: "1.0.0-dev",
|
||||
registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN") || false
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
FROM elixir:latest
|
||||
LABEL maintainer="Thomas Citharel <tcit@tcit.fr>"
|
||||
|
||||
ENV REFRESHED_AT=2019-02-21
|
||||
ENV REFRESHED_AT=2019-04-10
|
||||
RUN apt-get update -yq && apt-get install -yq build-essential inotify-tools postgresql-client git curl gnupg
|
||||
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash && apt-get install nodejs -yq
|
||||
RUN npm install -g yarn
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
RUN mix local.hex --force && mix local.rebar --force
|
||||
RUN curl http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz --output GeoLite2-City.tar.gz -s && tar zxf GeoLite2-City.tar.gz && mkdir -p /usr/share/GeoIP && mv GeoLite2-City_*/GeoLite2-City.mmdb /usr/share/GeoIP/GeoLite2-City.mmdb
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
npm install
|
||||
npm rebuild node-sass
|
||||
npm run dev
|
||||
yan install
|
||||
yarn rebuild node-sass
|
||||
yarn run dev
|
||||
|
21
js/fragmentTypes.json
Normal file
21
js/fragmentTypes.json
Normal file
@ -0,0 +1,21 @@
|
||||
{"__schema":
|
||||
{"types":[
|
||||
{
|
||||
"possibleTypes":[
|
||||
{"name":"Person"},
|
||||
{"name":"Group"}
|
||||
],
|
||||
"name":"Actor",
|
||||
"kind":"INTERFACE"
|
||||
},
|
||||
{
|
||||
"possibleTypes":[
|
||||
{"name":"Event"},
|
||||
{"name":"Person"},
|
||||
{"name":"Group"}
|
||||
],
|
||||
"name":"SearchResult",
|
||||
"kind":"UNION"}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
const fetch = require('node-fetch');
|
||||
const fs = require('fs');
|
||||
|
||||
fetch(`http://localhost:4000/graphiql`, {
|
||||
fetch(`http://localhost:4001`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
|
15025
js/package-lock.json
generated
15025
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,9 +3,9 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vue-cli-service build --modern",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"analyze-bundle": "npm run build -- --report-json && webpack-bundle-analyzer ../priv/static/report.json",
|
||||
"analyze-bundle": "yarn run build -- --report-json && webpack-bundle-analyzer ../priv/static/report.json",
|
||||
"dev": "vue-cli-service serve",
|
||||
"test:e2e": "vue-cli-service test:e2e",
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
@ -23,19 +23,20 @@
|
||||
"graphql": "^14.2.1",
|
||||
"graphql-tag": "^2.10.1",
|
||||
"leaflet": "^1.4.0",
|
||||
"line-clamp": "0.0.9",
|
||||
"lodash": "^4.17.11",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"ngeohash": "^0.6.3",
|
||||
"npm-check-updates": "^3.1.3",
|
||||
"register-service-worker": "^1.6.2",
|
||||
"typeface-signika": "0.0.72",
|
||||
"vue": "^2.6.10",
|
||||
"vue-apollo": "^3.0.0-beta.28",
|
||||
"vue-class-component": "^7.0.2",
|
||||
"vue-gettext": "^2.1.2",
|
||||
"vue-gettext": "^2.1.3",
|
||||
"vue-property-decorator": "^8.1.0",
|
||||
"vue-router": "^3.0.2",
|
||||
"vue-router": "^3.0.3",
|
||||
"vue-simple-markdown": "^1.0.9",
|
||||
"vue2-leaflet": "^2.0.2",
|
||||
"vue2-leaflet": "^2.0.3",
|
||||
"vuex": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -53,18 +54,21 @@
|
||||
"@vue/test-utils": "^1.0.0-beta.29",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv-webpack": "^1.7.0",
|
||||
"eslint": "^5.16.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"patch-package": "^6.1.0",
|
||||
"patch-package": "^6.1.1",
|
||||
"sass-loader": "^7.1.0",
|
||||
"tslint": "^5.15.0",
|
||||
"tslint-config-airbnb": "^5.11.1",
|
||||
"typescript": "^3.4.1",
|
||||
"typescript": "^3.4.3",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack-bundle-analyzer": "^3.1.0"
|
||||
"webpack": "^4.29.6",
|
||||
"webpack-bundle-analyzer": "^3.3.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
">0.25%",
|
||||
"not ie 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
@ -6,7 +6,7 @@
|
||||
<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="stylesheet" href="//cdn.materialdesignicons.com/2.5.94/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="//cdn.materialdesignicons.com/3.5.95/css/materialdesignicons.min.css">
|
||||
<title>mobilizon</title>
|
||||
<!--server-generated-meta-->
|
||||
</head>
|
||||
@ -19,4 +19,4 @@
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
@ -1,18 +1,10 @@
|
||||
<template>
|
||||
<div id="mobilizon">
|
||||
<NavBar></NavBar>
|
||||
<main class="container">
|
||||
<router-view></router-view>
|
||||
<NavBar />
|
||||
<main>
|
||||
<router-view />
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<span
|
||||
v-translate="{
|
||||
date: new Date().getFullYear(),
|
||||
}"
|
||||
>© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks</span>
|
||||
</div>
|
||||
</footer>
|
||||
<mobilizon-footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -22,6 +14,8 @@ import { Component, Vue } from 'vue-property-decorator';
|
||||
import { AUTH_TOKEN, AUTH_USER_ACTOR, AUTH_USER_EMAIL, AUTH_USER_ID } from '@/constants';
|
||||
import { CURRENT_USER_CLIENT, UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user';
|
||||
import { ICurrentUser } from '@/types/current-user.model';
|
||||
import Footer from '@/components/Footer.vue';
|
||||
import Logo from '@/components/Logo.vue';
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
@ -30,63 +24,24 @@ import { ICurrentUser } from '@/types/current-user.model';
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Logo,
|
||||
NavBar,
|
||||
'mobilizon-footer': Footer,
|
||||
},
|
||||
})
|
||||
export default class App extends Vue {
|
||||
drawer = false;
|
||||
fab = false;
|
||||
items = [
|
||||
{
|
||||
icon: 'poll',
|
||||
text: 'Events',
|
||||
route: 'EventList',
|
||||
role: null,
|
||||
},
|
||||
{
|
||||
icon: 'group',
|
||||
text: 'Groups',
|
||||
route: 'GroupList',
|
||||
role: null,
|
||||
},
|
||||
{ icon: 'settings', text: 'Settings', role: 'ROLE_USER' },
|
||||
{ icon: 'chat_bubble', text: 'Send feedback', role: 'ROLE_USER' },
|
||||
{ icon: 'help', text: 'Help', role: null },
|
||||
{ icon: 'phonelink', text: 'App downloads', role: null },
|
||||
];
|
||||
error = {
|
||||
timeout: 3000,
|
||||
show: false,
|
||||
text: '',
|
||||
};
|
||||
currentUser!: ICurrentUser;
|
||||
|
||||
actor = localStorage.getItem(AUTH_USER_ACTOR);
|
||||
|
||||
mounted () {
|
||||
this.initializeCurrentUser();
|
||||
}
|
||||
|
||||
get displayed_name () {
|
||||
// FIXME: load actor
|
||||
return 'no implemented';
|
||||
// return this.actor.display_name === null ? this.actor.username : this.actor.display_name
|
||||
}
|
||||
|
||||
showMenuItem(elem) {
|
||||
// FIXME: load actor
|
||||
return false;
|
||||
// return elem !== null && this.user && this.user.roles !== undefined ? this.user.roles.includes(elem) : true
|
||||
async mounted () {
|
||||
await this.initializeCurrentUser();
|
||||
}
|
||||
|
||||
getUser (): ICurrentUser|false {
|
||||
return this.currentUser.id ? this.currentUser : false;
|
||||
}
|
||||
|
||||
toggleDrawer() {
|
||||
this.drawer = !this.drawer;
|
||||
}
|
||||
|
||||
private initializeCurrentUser() {
|
||||
const userId = localStorage.getItem(AUTH_USER_ID);
|
||||
const userEmail = localStorage.getItem(AUTH_USER_EMAIL);
|
||||
@ -106,7 +61,32 @@ export default class App extends Vue {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
@import "variables";
|
||||
|
||||
// Import Bulma and Buefy styles
|
||||
@import "~bulma/sass/utilities/_all";
|
||||
@import "~bulma/sass/base/_all.sass";
|
||||
@import "~bulma/sass/elements/button.sass";
|
||||
@import "~bulma/sass/elements/container.sass";
|
||||
@import "~bulma/sass/components/card.sass";
|
||||
@import "~bulma/sass/components/pagination.sass";
|
||||
@import "~bulma/sass/elements/form.sass";
|
||||
@import "~bulma/sass/layout/hero.sass";
|
||||
@import "~bulma/sass/elements/title.sass";
|
||||
@import "~bulma/sass/elements/image.sass";
|
||||
@import "~bulma/sass/elements/box.sass";
|
||||
@import "~bulma/sass/components/navbar.sass";
|
||||
@import "~bulma/sass/components/modal.sass";
|
||||
@import "~bulma/sass/grid/_all.sass";
|
||||
@import "~bulma/sass/layout/section.sass";
|
||||
@import "~bulma/sass/layout/footer.sass";
|
||||
@import "~buefy/src/scss/utils/_all";
|
||||
@import "~buefy/src/scss/components/datepicker";
|
||||
@import "~buefy/src/scss/components/modal";
|
||||
@import "~buefy/src/scss/components/form";
|
||||
@import "~buefy/src/scss/components/dropdown";
|
||||
|
||||
.router-enter-active,
|
||||
.router-leave-active {
|
||||
transition-property: opacity;
|
||||
@ -121,4 +101,8 @@ export default class App extends Vue {
|
||||
.router-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #f6f7f8;
|
||||
}
|
||||
</style>
|
||||
|
134
js/src/assets/Circle-icons-calendar.svg
Normal file
134
js/src/assets/Circle-icons-calendar.svg
Normal file
@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 64 64"
|
||||
style="enable-background:new 0 0 64 64;"
|
||||
xml:space="preserve"
|
||||
id="svg4965"
|
||||
sodipodi:docname="Circle-icons-calendar.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
|
||||
id="metadata4971"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs4969" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
id="namedview4967"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.429825"
|
||||
inkscape:cx="30.416304"
|
||||
inkscape:cy="28.788016"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="34"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4961" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style4919">
|
||||
.st0{fill:#77B3D4;}
|
||||
.st1{opacity:0.2;}
|
||||
.st2{fill:#231F20;}
|
||||
.st3{fill:#FFFFFF;}
|
||||
.st4{fill:#C75C5C;}
|
||||
.st5{fill:#4F5D73;}
|
||||
.st6{fill:#E0E0D1;}
|
||||
</style>
|
||||
<g
|
||||
id="Layer_1">
|
||||
<g
|
||||
id="g4923">
|
||||
<circle
|
||||
class="st0"
|
||||
cx="32"
|
||||
cy="32"
|
||||
r="32"
|
||||
id="circle4921" />
|
||||
</g>
|
||||
<g
|
||||
id="g4961">
|
||||
<g
|
||||
class="st1"
|
||||
id="g4927">
|
||||
<path
|
||||
class="st2"
|
||||
d="M12,25v25c0,2.2,1.8,4,4,4h32c2.2,0,4-1.8,4-4V25H12z"
|
||||
id="path4925" />
|
||||
</g>
|
||||
<g
|
||||
id="g4931">
|
||||
<path
|
||||
class="st3"
|
||||
d="M12,23v25c0,2.2,1.8,4,4,4h32c2.2,0,4-1.8,4-4V23H12z"
|
||||
id="path4929" />
|
||||
</g>
|
||||
<g
|
||||
class="st1"
|
||||
id="g4935">
|
||||
<path
|
||||
class="st2"
|
||||
d="M48,14H16c-2.2,0-4,1.8-4,4v7h40v-7C52,15.8,50.2,14,48,14z"
|
||||
id="path4933" />
|
||||
</g>
|
||||
<g
|
||||
id="g4939">
|
||||
<path
|
||||
class="st4"
|
||||
d="M48,12H16c-2.2,0-4,1.8-4,4v7h40v-7C52,13.8,50.2,12,48,12z"
|
||||
id="path4937" />
|
||||
</g>
|
||||
|
||||
<g
|
||||
class="st1"
|
||||
id="g4947">
|
||||
<path
|
||||
class="st2"
|
||||
d="M20,21c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C22,20.1,21.1,21,20,21L20,21z"
|
||||
id="path4945" />
|
||||
</g>
|
||||
<g
|
||||
class="st1"
|
||||
id="g4951">
|
||||
<path
|
||||
class="st2"
|
||||
d="M45,21c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C47,20.1,46.1,21,45,21L45,21z"
|
||||
id="path4949" />
|
||||
</g>
|
||||
<g
|
||||
id="g4955">
|
||||
<path
|
||||
class="st6"
|
||||
d="M20,19c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C22,18.1,21.1,19,20,19L20,19z"
|
||||
id="path4953" />
|
||||
</g>
|
||||
<g
|
||||
id="g4959">
|
||||
<path
|
||||
class="st6"
|
||||
d="M45,19c-1.1,0-2-0.9-2-2v-7c0-1.1,0.9-2,2-2l0,0c1.1,0,2,0.9,2,2v7C47,18.1,46.1,19,45,19L45,19z"
|
||||
id="path4957" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="Layer_2">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
BIN
js/src/assets/footer.png
Normal file
BIN
js/src/assets/footer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
748
js/src/assets/texting.svg
Normal file
748
js/src/assets/texting.svg
Normal file
@ -0,0 +1,748 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="abd2d575-db47-4a01-ab9c-7ced98725eec"
|
||||
data-name="Layer 1"
|
||||
width="1162"
|
||||
height="716.89"
|
||||
viewBox="0 0 1162 716.89"
|
||||
version="1.1"
|
||||
sodipodi:docname="texting.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14">
|
||||
<metadata
|
||||
id="metadata4844">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs4842" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
id="namedview4840"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.2625507"
|
||||
inkscape:cx="839.50648"
|
||||
inkscape:cy="437.82829"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="34"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="abd2d575-db47-4a01-ab9c-7ced98725eec" />
|
||||
<title
|
||||
id="title4701">texting</title>
|
||||
<path
|
||||
d="M19,808.44H1142S1240.45,681,1124.57,550c-43-48.58-46.27-108.65-33.39-165C1126,232.68,986.65,97.88,835.46,137.44c-2.88.76-5.79,1.53-8.72,2.33-227.89,62.15-414.47-.31-486.28-31.23a206.52,206.52,0,0,0-90.8-16.78c-80.59,3.6-202.72,34.64-89.28,205.62C320.31,538.42,19,808.44,19,808.44Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#6c63ff"
|
||||
opacity="0.1"
|
||||
id="path4703" />
|
||||
<ellipse
|
||||
cx="270.5"
|
||||
cy="666.44"
|
||||
rx="22.5"
|
||||
ry="29.45"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4705" />
|
||||
<ellipse
|
||||
cx="270.5"
|
||||
cy="630.44"
|
||||
rx="22.5"
|
||||
ry="29.45"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4707" />
|
||||
<ellipse
|
||||
cx="270.5"
|
||||
cy="594.44"
|
||||
rx="22.5"
|
||||
ry="29.45"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4709" />
|
||||
<ellipse
|
||||
cx="270.5"
|
||||
cy="558.44"
|
||||
rx="22.5"
|
||||
ry="29.45"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4711" />
|
||||
<ellipse
|
||||
cx="270.5"
|
||||
cy="522.44"
|
||||
rx="22.5"
|
||||
ry="29.45"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4713" />
|
||||
<ellipse
|
||||
cx="270.5"
|
||||
cy="486.44"
|
||||
rx="22.5"
|
||||
ry="29.45"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4715" />
|
||||
<ellipse
|
||||
cx="270.5"
|
||||
cy="450.44"
|
||||
rx="22.5"
|
||||
ry="29.45"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4717" />
|
||||
<ellipse
|
||||
cx="457.5"
|
||||
cy="675.44"
|
||||
rx="12.5"
|
||||
ry="16.36"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4719" />
|
||||
<ellipse
|
||||
cx="457.5"
|
||||
cy="655.44"
|
||||
rx="12.5"
|
||||
ry="16.36"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4721" />
|
||||
<ellipse
|
||||
cx="457.5"
|
||||
cy="635.44"
|
||||
rx="12.5"
|
||||
ry="16.36"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4723" />
|
||||
<ellipse
|
||||
cx="457.5"
|
||||
cy="615.44"
|
||||
rx="12.5"
|
||||
ry="16.36"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4725" />
|
||||
<ellipse
|
||||
cx="457.5"
|
||||
cy="595.44"
|
||||
rx="12.5"
|
||||
ry="16.36"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4727" />
|
||||
<ellipse
|
||||
cx="457.5"
|
||||
cy="575.44"
|
||||
rx="12.5"
|
||||
ry="16.36"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4729" />
|
||||
<ellipse
|
||||
cx="457.5"
|
||||
cy="555.44"
|
||||
rx="12.5"
|
||||
ry="16.36"
|
||||
fill="#3f3d56"
|
||||
id="ellipse4731" />
|
||||
<path
|
||||
d="M373.73,295.64a109.12,109.12,0,0,0,8.38-12.33L323,273.6l63.94.47A108,108,0,0,0,389,188.68l-85.79,44.5L382.31,175a107.78,107.78,0,1,0-178,120.62A107.91,107.91,0,0,0,192,315.3l76.74,39.87L186.93,327.7a107.81,107.81,0,0,0,17.38,101.21,107.77,107.77,0,1,0,169.42,0,107.81,107.81,0,0,0,0-133.27Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#6c63ff"
|
||||
id="path4733" />
|
||||
<path
|
||||
d="M181.24,362.28a107.29,107.29,0,0,0,23.07,66.63,107.77,107.77,0,1,0,169.42,0C388.17,410.57,181.24,350.18,181.24,362.28Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
opacity="0.1"
|
||||
id="path4735" />
|
||||
<path
|
||||
d="M420.28,465.61a70.68,70.68,0,0,1-5.58-8.22l39.41-6.48-42.63.32a72,72,0,0,1-1.37-56.93L467.3,424,414.56,385.2a71.85,71.85,0,1,1,118.67,80.41,71.62,71.62,0,0,1,8.19,13.1l-51.16,26.58L544.81,487a71.85,71.85,0,0,1-11.58,67.48,71.85,71.85,0,1,1-113,0,71.89,71.89,0,0,1,0-88.85Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#6c63ff"
|
||||
id="path4737" />
|
||||
<path
|
||||
d="M548.61,510a71.57,71.57,0,0,1-15.38,44.43,71.85,71.85,0,1,1-113,0C410.65,542.23,548.61,502,548.61,510Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
opacity="0.1"
|
||||
id="path4739" />
|
||||
<path
|
||||
d="M19,808.44s120.52-117,162.24-77.64,217.87,49.83,234.1,47.51,111.25-6.95,164.56-1.16,542.35-16.22,562.05,31.29Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4741" />
|
||||
<path
|
||||
d="M800.08,236h-54.2a9.23,9.23,0,0,0,2-5.8,9.33,9.33,0,0,0-.68-3.47h25a9.27,9.27,0,0,0,0-18.54H622.77a9.3,9.3,0,0,0-9.27,9.27,9.1,9.1,0,0,0,.69,3.47h-25a9.28,9.28,0,0,0,0,18.55h54.2a9.2,9.2,0,0,0-2,5.79,9.3,9.3,0,0,0,9.27,9.27H800.08a9.27,9.27,0,0,0,0-18.54Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#6c63ff"
|
||||
opacity="0.1"
|
||||
id="path4743" />
|
||||
<path
|
||||
d="M1044.6,369.23H990.4a9.23,9.23,0,0,0,2.05-5.8,9.13,9.13,0,0,0-.68-3.47h25a9.27,9.27,0,0,0,0-18.54H867.3a9.3,9.3,0,0,0-9.27,9.27,9.09,9.09,0,0,0,.68,3.47h-25a9.28,9.28,0,0,0,0,18.55h54.2a9.2,9.2,0,0,0-2,5.79,9.3,9.3,0,0,0,9.27,9.27H1044.6a9.27,9.27,0,1,0,0-18.54Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#6c63ff"
|
||||
opacity="0.1"
|
||||
id="path4745" />
|
||||
<path
|
||||
d="M555.92,312.84H524.17a5.39,5.39,0,0,0,1.2-3.39,5.53,5.53,0,0,0-.4-2h14.66a5.43,5.43,0,0,0,0-10.86H452a5.45,5.45,0,0,0-5.43,5.43,5.34,5.34,0,0,0,.41,2H432.36a5.43,5.43,0,1,0,0,10.86h31.75a5.39,5.39,0,0,0-1.2,3.39,5.45,5.45,0,0,0,5.43,5.44h87.58a5.44,5.44,0,0,0,0-10.87Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#6c63ff"
|
||||
opacity="0.1"
|
||||
id="path4747" />
|
||||
<path
|
||||
d="M708.47,755.55a23.78,23.78,0,0,1-1.74,6A43,43,0,0,0,717.34,764a1,1,0,0,0,.68-.1c.27-.21.23-.63.19-1a13.29,13.29,0,0,1,1.1-5.76q3-8.35,5.92-16.72c-3.46-1.57-10.62-.62-13.54,2C709,744.84,709.12,752.17,708.47,755.55Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#a1616a"
|
||||
id="path4749" />
|
||||
<path
|
||||
d="M758.09,764.88a1.92,1.92,0,0,0-.18.74c0,.89,1.18,1.2,2.07,1.28a56.67,56.67,0,0,1,12.62,2.63c.5-1.48-.2-3.07-.22-4.62,0-2.16,1.27-4.08,2.06-6.09,1.41-3.55,1.3-7.49,1.17-11.31-3.42.23-11.58-1.35-13.49,2.19-1.06,2-1,5-1.48,7.2A55.7,55.7,0,0,1,758.09,764.88Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#a1616a"
|
||||
id="path4751" />
|
||||
<path
|
||||
d="M737.75,611.19l-9,.53a2.58,2.58,0,0,0-1.64.48,2.46,2.46,0,0,0-.61,1.57c-1.2,9,.91,18.19-.21,27.19-.45,3.54-1.39,7-2.12,10.48-1.23,5.81-1.9,11.72-2.57,17.62a40.19,40.19,0,0,0-.34,8.83c.26,2.48,1,4.9,1.09,7.39a25.7,25.7,0,0,1-1.52,8.64,94,94,0,0,1-5.6,13.87c-1.93,3.87-4.14,7.64-5.31,11.8-1.86,6.62-1,13.83-3.21,20.34-.55,1.6-1.29,3.25-1,4.91.39,2.15,2.48,3.68,4.63,4.09a16.44,16.44,0,0,0,6.49-.49c3.51-.75,7.24-1.62,9.71-4.22a17.78,17.78,0,0,0,3.24-5.82c1.63-4,3.25-8.06,4.74-12.14a46.12,46.12,0,0,1,2.3-5.65c.82-1.56,1.84-3,2.71-4.56a46.34,46.34,0,0,0,3.48-8.4c6.42-19.7,8.38-41,18.08-59.3.46-.87.52-2,1-2.82a29.14,29.14,0,0,1,1.34,9.21,193.59,193.59,0,0,1-.51,21.27c-.4,5.16-1,10.31-1.12,15.49,0,2.36,0,4.73.05,7.1l0,1.93c.08,3.37.16,6.74.49,10.09,1.41,13.84-2.95,27.3-1.38,41.12a2.39,2.39,0,0,0,.46,1.36c.67.72,1.86.33,2.77-.06a29.76,29.76,0,0,1,9.46-2.36,6.38,6.38,0,0,0,2.87-.64c1.44-.86,1.85-2.73,2.11-4.39q3.27-20.19,6.53-40.38a96.54,96.54,0,0,0,1.24-9.73c.25-4.24-.06-8.5.35-12.72.57-6,3-11.7,4.82-17.4.94-3,1.89-5.95,2.75-8.95s1.65-6.11,2.35-9.19c1.2-5.36,2.14-10.89,1.31-16.32s-3.69-10.79-8.51-13.41c-3.53-1.93-7.67-2.22-11.66-2.66-11.39-1.25-22.74-4-34.15-3C741.68,612.07,739.79,611.07,737.75,611.19Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4753" />
|
||||
<path
|
||||
d="M712.51,794.74a12.83,12.83,0,0,0,1.42-3l3.69-10a86.92,86.92,0,0,0,3-9.13,42.77,42.77,0,0,0,1.22-11.32,42.15,42.15,0,0,1-11.11-1.56,17.17,17.17,0,0,0-4.22-.89,7.34,7.34,0,0,0-7.12,7.77,11.19,11.19,0,0,1,.16,1.82,5.69,5.69,0,0,1-.52,1.75l-3.94,9.6a21.07,21.07,0,0,0-2,7.4C692.81,796.16,706.84,802.91,712.51,794.74Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4755" />
|
||||
<path
|
||||
d="M750.51,793.87a17.17,17.17,0,0,0,0,4.53c.78,4.23,4.87,7.44,9.16,7.74a13.13,13.13,0,0,0,11.11-5.47,11.8,11.8,0,0,0,1.85-3.55,18.43,18.43,0,0,0,.62-3.77l1.59-16.83c.23-2.36,1.19-5.77,0-8-1-1.89-3.43-2.51-5.3-3.22a20.85,20.85,0,0,0-9.82-1.47c-5.83.49-5.77,6.44-6.65,11.19Q751.35,784.4,750.51,793.87Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4757" />
|
||||
<path
|
||||
d="M752.24,452.06c-2.69,3.77-6.61,6.54-9.25,10.35a19.45,19.45,0,0,0-3.41,11.85c.09,2.16.54,4.37,0,6.46A11.49,11.49,0,0,1,737,485a35.94,35.94,0,0,1-16.43,10.92c-.77.24-1.73.76-1.54,1.54a1.59,1.59,0,0,0,.71.81l13,9.31c7.63,5.47,15.3,11,23.74,15.09a84.34,84.34,0,0,0,48.5,7.63,25.29,25.29,0,0,1-3.85-11c-.5-4.7.34-9.44.25-14.17a36.62,36.62,0,0,0-3-14c-1.12-2.57-2.54-5-3.4-7.67a39.68,39.68,0,0,1-1.44-10l-.67-11.19a22.58,22.58,0,0,0-.93-6.13,15.19,15.19,0,0,0-2.38-4.22c-4.76-6.22-12-8.88-19.58-9.53-3.31-.28-7.36-.55-10.41.89C755.92,445.07,754.44,449,752.24,452.06Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4759" />
|
||||
<path
|
||||
d="M751,492.42a7.43,7.43,0,0,1-4.69,4.36,22.12,22.12,0,0,1-6.54,1L735,498a2.08,2.08,0,0,0-1.47.44,1.72,1.72,0,0,0-.42,1.07c-.3,2.54,1.18,4.91,2.66,7,4.74,6.64,10.42,12.95,17.83,16.34a29.41,29.41,0,0,0,33.9-6.84,3,3,0,0,0,.94-1.67A2.81,2.81,0,0,0,787,512c-1.58-1.17-3.44-1.93-5-3.09a15,15,0,0,1-5.43-8.21,24.7,24.7,0,0,1-.45-9.95c-5.93-1.69-12.39-.35-18.33-2-1.39-.38-4-1.84-5.46-1S751.46,491,751,492.42Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#a1616a"
|
||||
id="path4761" />
|
||||
<path
|
||||
d="M751,492.42a7.43,7.43,0,0,1-4.69,4.36,22.12,22.12,0,0,1-6.54,1L735,498a2.08,2.08,0,0,0-1.47.44,1.72,1.72,0,0,0-.42,1.07c-.3,2.54,1.18,4.91,2.66,7,4.74,6.64,10.42,12.95,17.83,16.34a29.41,29.41,0,0,0,33.9-6.84,3,3,0,0,0,.94-1.67A2.81,2.81,0,0,0,787,512c-1.58-1.17-3.44-1.93-5-3.09a15,15,0,0,1-5.43-8.21,24.7,24.7,0,0,1-.45-9.95c-5.93-1.69-12.39-.35-18.33-2-1.39-.38-4-1.84-5.46-1S751.46,491,751,492.42Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
opacity="0.1"
|
||||
id="path4763" />
|
||||
<circle
|
||||
cx="747.07"
|
||||
cy="390.79"
|
||||
r="19.97"
|
||||
fill="#a1616a"
|
||||
id="circle4765" />
|
||||
<path
|
||||
d="M780.6,502.7a65.52,65.52,0,0,1-11.46,3.72,22.06,22.06,0,0,1-11.9-.65,41.43,41.43,0,0,1-7.37-4.2c-4.14-2.65-8.66-5-13.56-5.48-2.29-.24-4.79,0-6.6,1.42a9,9,0,0,0-2.91,6c-.66,4.76.47,9.56,1.48,14.26,3.83,17.85,6,36.52,1.71,54.28-2.25,9.43.18,19.24.51,28.93a34.62,34.62,0,0,0,1.31,9.85c1.05,3.15,3.17,6.1,6.23,7.39,2.85,1.2,6.08.83,9.16.58,3.67-.29,7.5-.34,10.83,1.2,2.36,1.1,4.57,3,7.15,2.77,1.32-.12,2.52-.8,3.83-1,2.63-.44,5.15,1,7.54,2.18a46.66,46.66,0,0,0,19,4.81c3.37-7.93,1.63-16.85,2-25.46.15-3.22.63-6.42.82-9.64a107.82,107.82,0,0,0-.15-11.65L797,555.81c-.35-7.13,3.29-14.11,3-21.23a64.26,64.26,0,0,0-1.37-9.86c-.22-1.1-.45-2.19-.76-3.26-2.2-7.51-8.53-13-14.58-17.93A2.93,2.93,0,0,0,780.6,502.7Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#6c63ff"
|
||||
id="path4767" />
|
||||
<path
|
||||
d="M747.22,619.63c.08,7-2.44,13.77-4.91,20.34-.76,2-1.69,4.22-3.63,5.15s-4.11.33-6-.41a40.37,40.37,0,0,1-5.13-2.37c-1.74-1-3.7-2.07-5.57-1.43-2.29.78-3,3.63-3.31,6a63.45,63.45,0,0,1-9.35-.57,1.76,1.76,0,0,1-.82-.26,1.73,1.73,0,0,1-.52-1.12c-.32-1.67-.65-3.33-1-5-.1-.52-.48-1.21-1-1,.42-6.48-.93-12.93-1.42-19.4a51.45,51.45,0,0,1,.1-9.85,95.42,95.42,0,0,1,1.88-9.73c1.54-6.86,4.5-13.83,4.67-20.79a23.49,23.49,0,0,0-.27-4.18q-2.12-13.09-4.22-26.2c-.36-2.18-.71-4.38-1.22-6.53-.38-1.6-.74-3.15-1.15-4.69-.25-.94-.52-1.87-.83-2.81q4-11,8-22a40.43,40.43,0,0,1,2.2-5.27,20.62,20.62,0,0,1,8.95-8.73,8.07,8.07,0,0,0,3-2c.39-.51.63-1.13,1-1.66,1.41-2,4.3-2.16,6.71-1.72,2.21.39,4.5,1.23,5.84,3A11.77,11.77,0,0,1,740.5,499q2.4,5.82,4.82,11.63c1.37.66,2,2.45,2,4,0,2.88-.41,5.75-.29,8.63,0,.21,0,.41.06.62.36,2.59,2.19,4.77,2.81,7.37,2.38,9.95-1.09,20.19-3.13,30.25-.08.4-.16.79-.23,1.19a90.88,90.88,0,0,0-1.6,17.73,50.68,50.68,0,0,0,.52,7.38c.33,2.15.86,4.27,1.1,6.44a59.58,59.58,0,0,1,0,9.06C746.28,608.71,747.16,614.16,747.22,619.63Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#454b69"
|
||||
id="path4769" />
|
||||
<path
|
||||
d="M755.46,533.92a8.61,8.61,0,0,1,1.14,4,2.66,2.66,0,0,1-.6,1.66,2.5,2.5,0,0,1-2.29.63,6.29,6.29,0,0,1-2.23-1,5.17,5.17,0,0,1-2.13-2.14,5.57,5.57,0,0,1-.32-1.34l-.53-3.38a4.31,4.31,0,0,1,0-2c.45-1.39,2.47-3.85,4-2.65S754.71,532.32,755.46,533.92Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#454b69"
|
||||
id="path4771" />
|
||||
<path
|
||||
d="M814,539.48c-.59-1.54-2.35-2.19-3.79-3-3-1.67-5.08-4.59-6.66-7.63s-2.76-6.28-4.47-9.24a3.6,3.6,0,0,0-1.14-1.36,1.38,1.38,0,0,0-1.66.1,1.8,1.8,0,0,0-.37.95c-.36,2.21.27,4.44.61,6.65a25.92,25.92,0,0,1-6.34,21c-2.07,2.29-4.64,4.3-5.75,7.18a16.91,16.91,0,0,0-.6,7l.69,12.76a120.6,120.6,0,0,1-4.35,38.49c-2,7.2-5.38,14.47-4,21.81.52,2.81,1.74,5.6,1.24,8.42a2.87,2.87,0,0,0,0,1.64,2.39,2.39,0,0,0,1,.93l6.65,4.34,6-17.77a32.86,32.86,0,0,0,1.33-4.65,35.54,35.54,0,0,0,.39-5.46,230.54,230.54,0,0,1,6.17-47.44,36.41,36.41,0,0,1,2-6.56c1.73-3.89,4.64-7.11,7.51-10.27.07-1.47-.12-2.94,0-4.42a28.29,28.29,0,0,1,1.09-7.62C810.33,542.9,811.67,540.56,814,539.48Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#454b69"
|
||||
id="path4773" />
|
||||
<path
|
||||
d="M803.91,556.08a90.29,90.29,0,0,0,.19,12.63l1,16.74a24.07,24.07,0,0,0,1,6.61c.8,2.28,2.28,4.28,3.05,6.58.26.78.44,1.6.7,2.39.33,1,.78,1.87,1.2,2.8,2.7,6.11,3.44,12.89,3.63,19.56s-.15,13.36.53,20a12.07,12.07,0,0,1-5.31.26,7.43,7.43,0,0,0-2-.27,6.31,6.31,0,0,0-1.68.48,17.31,17.31,0,0,1-14.53-1.3,29,29,0,0,1-3.71-2.72,11.37,11.37,0,0,1-2.54-2.69,10,10,0,0,1-1.15-4.86c-.23-6.79,2-13.39,3.51-20a185.9,185.9,0,0,0,3-19.39c1.11-9.53.81-19.33,3-28.68a7.9,7.9,0,0,1,1.6-3.71,8,8,0,0,1,2.58-1.66C799.93,557.9,801.92,557,803.91,556.08Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#454b69"
|
||||
id="path4775" />
|
||||
<path
|
||||
d="M814,539.48c-.59-1.54-2.35-2.19-3.79-3-3-1.67-5.08-4.59-6.66-7.63s-2.76-6.28-4.47-9.24a3.6,3.6,0,0,0-1.14-1.36,1.38,1.38,0,0,0-1.66.1,1.8,1.8,0,0,0-.37.95c-.36,2.21.27,4.44.61,6.65a25.92,25.92,0,0,1-6.34,21c-2.07,2.29-4.64,4.3-5.75,7.18a16.91,16.91,0,0,0-.6,7l.69,12.76a120.6,120.6,0,0,1-4.35,38.49c-2,7.2-5.38,14.47-4,21.81.52,2.81,1.74,5.6,1.24,8.42a2.87,2.87,0,0,0,0,1.64,2.39,2.39,0,0,0,1,.93l6.65,4.34,6-17.77a32.86,32.86,0,0,0,1.33-4.65,35.54,35.54,0,0,0,.39-5.46,230.54,230.54,0,0,1,6.17-47.44,36.41,36.41,0,0,1,2-6.56c1.73-3.89,4.64-7.11,7.51-10.27.07-1.47-.12-2.94,0-4.42a28.29,28.29,0,0,1,1.09-7.62C810.33,542.9,811.67,540.56,814,539.48Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
opacity="0.1"
|
||||
id="path4777" />
|
||||
<path
|
||||
d="M806.59,644.53a3.57,3.57,0,0,1-1.3,1.88c-.67.38-1.76.06-1.82-.71-1.12-.15-2,1.17-3.08,1.26s-2.08-1-3.14-.76c-.68.18-1.32.9-1.94.58-.26-.13-.42-.41-.69-.51-.43-.14-.84.26-1.28.36-.93.21-1.61-.93-1.64-1.88s.25-2-.19-2.82a.6.6,0,0,0-.29-.32c-.3-.14-.63.14-.83.41-.74,1-.95,2.39-1.7,3.43s-2.54,1.53-3.22.45a2.55,2.55,0,0,1-.14-1.81c.45-2.56,1.45-5.31,3.75-6.51,2-1,4.4-.62,6.6-.16q4.59,1,9.17,2.07c.81.19,2.75.29,3.1,1.23S806.91,643.73,806.59,644.53Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#a1616a"
|
||||
id="path4779" />
|
||||
<path
|
||||
d="M770.57,468.25c1,5.39,4.08,10.14,6.55,15.05s4.44,10.49,3.1,15.81a55,55,0,0,1-2,5.39,50.92,50.92,0,0,0-2.06,9.58c-.66,4.42-1.32,8.91-.79,13.35s2.42,8.9,6,11.6l-.28-2a22.23,22.23,0,0,1,4.23,2.91,26.86,26.86,0,0,0,3-8.15l6.79,5.27c3,2.3,6.36,4.72,10.08,4.26l-3-6.91c-.83-1.92-1.66-3.83-2.64-5.68a17.87,17.87,0,0,1-1.62-3.46,15.61,15.61,0,0,1-.44-3.2,223.13,223.13,0,0,0-9.82-52,43.35,43.35,0,0,0-4.73-11A13.78,13.78,0,0,0,779,455c-2.49-1.59-5.55-1.93-8.49-2.23a26.19,26.19,0,0,0-6.19-.14,19,19,0,0,0-6,2.06,32,32,0,0,0-13.84,13.73,27.2,27.2,0,0,0-3.13,13.89c.39,5.1,5.25,2.78,8.53,1.51C758,480.65,765.8,475.73,770.57,468.25Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4781" />
|
||||
<path
|
||||
d="M755.46,533.92a8.61,8.61,0,0,1,1.14,4,2.66,2.66,0,0,1-.6,1.66,2.5,2.5,0,0,1-2.29.63,6.29,6.29,0,0,1-2.23-1,5.17,5.17,0,0,1-2.13-2.14,5.57,5.57,0,0,1-.32-1.34l-.53-3.38a4.31,4.31,0,0,1,0-2c.45-1.39,2.47-3.85,4-2.65S754.71,532.32,755.46,533.92Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
opacity="0.1"
|
||||
id="path4783" />
|
||||
<polygon
|
||||
points="742.71 407.39 738.96 435.48 755.44 434.48 760.8 407.39 742.71 407.39"
|
||||
fill="#454b69"
|
||||
id="polygon4785" />
|
||||
<circle
|
||||
cx="750.82"
|
||||
cy="415.75"
|
||||
r="1.25"
|
||||
fill="#fff"
|
||||
id="circle4787" />
|
||||
<g
|
||||
opacity="0.1"
|
||||
id="g4793">
|
||||
<path
|
||||
d="M780.65,495.73c.17-.49.32-1,.45-1.48,1.33-5.33-.64-10.91-3.11-15.82s-5.51-9.65-6.54-15.05c-4.77,7.48-12.55,12.4-20.68,15.56-3.28,1.27-8.15,3.58-8.54-1.51a20.47,20.47,0,0,1,0-3.41,23.69,23.69,0,0,0-.9,8.15c.39,5.1,5.25,2.78,8.53,1.51,8.14-3.15,15.91-8.07,20.68-15.55,1,5.39,4.08,10.14,6.55,15C779.09,487.09,780.74,491.43,780.65,495.73Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
id="path4789" />
|
||||
<path
|
||||
d="M796,532.2q-3.4-2.62-6.79-5.27a26.78,26.78,0,0,1-3,8.16,22,22,0,0,0-4.23-2.92l.28,2c-3.56-2.7-5.45-7.16-6-11.6s.12-8.93.78-13.35c.07-.48.15-1,.22-1.44-.47,2-.78,4.11-1.09,6.18-.66,4.43-1.32,8.91-.79,13.35s2.42,8.9,6,11.6l-.28-2a22.75,22.75,0,0,1,4.23,2.92,27,27,0,0,0,3-8.15q3.4,2.63,6.79,5.27c3,2.29,6.36,4.72,10.08,4.26l-2.16-5C800.49,535.58,798.13,533.86,796,532.2Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
id="path4791" />
|
||||
</g>
|
||||
<path
|
||||
d="M752.67,513.9a23.59,23.59,0,0,1,5.59-5.34,1.68,1.68,0,0,1,1.15-.32c.32.07.59.31.92.37a1.83,1.83,0,0,0,1.08-.26l4.62-2.22a4.9,4.9,0,0,1,2.21-.64,1.62,1.62,0,0,1,1.61,1.39c0,.86-.85,1.44-1.61,1.87a43.21,43.21,0,0,0-6.48,4.52c-1,.89-2.14,2.36-1.36,3.5a3,3,0,0,0,2.54.85,20.39,20.39,0,0,0,5-.38c1.32-.33,2.56-.92,3.89-1.23a19.13,19.13,0,0,1,3.71-.37,3,3,0,0,1,2.09.45c.55.47.5,1.6-.21,1.74,1,1.33.12,3.29-1.19,4.29s-3,1.41-4.37,2.26c1.6-.57,3.49-1.12,4.91-.18a1,1,0,0,1,.45.56c.09.47-.41.83-.85,1-6.56,3.06-14.33,2-21.2,4.27a22.55,22.55,0,0,0-7.88,4.55c1.07-1-5.52-8-4.95-10,.46-1.64,3.77-3.73,5-5Q750.1,516.89,752.67,513.9Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#a1616a"
|
||||
id="path4795" />
|
||||
<path
|
||||
d="M752.42,545.69a5.34,5.34,0,0,0,.35,2.68c.54,2.26-.25,4.63-1.34,6.69a29,29,0,0,1-4.68,6.39,61.7,61.7,0,0,1-7.06,6.26,152,152,0,0,1-15.4,10.62,12.82,12.82,0,0,1-4.45,1.95,13.33,13.33,0,0,1-3.73-.06,21.53,21.53,0,0,1-4.89-1,23.49,23.49,0,0,0-.27-4.18q-2.12-13.09-4.22-26.2c-.36-2.18-.71-4.38-1.22-6.53-.38-1.6-.74-3.15-1.15-4.69,0,0,0-.08,0-.11,1-2.52,3.93-4.12,4.22-6.8,4.23-.4,8.2-.87,12.44-1.26a28,28,0,0,1-.81,11.84,1.55,1.55,0,0,0,1.55-.95c.31-.57.41-1.23.67-1.83,1-2.28,3.93-2.82,6-4.14,3.64-2.26,5.21-7.16,9.13-8.86a1.43,1.43,0,0,1,.92-.16c.86.23,1,1.74,1.93,1.68a1.32,1.32,0,0,0,.74-.42c1.6-1.45,3.6-2.65,5.19-4.09.22.43.43.86.64,1.29q3.34,6.76,6.67,13.53c.87,1.76,1.84,3.63,3.57,4.56C756.55,543.9,753.05,543.66,752.42,545.69Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
opacity="0.1"
|
||||
id="path4797" />
|
||||
<path
|
||||
d="M707.37,528.6c-.28,2.69-3.23,4.29-4.22,6.81a12.2,12.2,0,0,0-.49,4.14c-.1,4-1,7.82-1.5,11.73s-.6,8,.84,11.68c.86,2.18,2.24,4.16,2.83,6.43.4,1.52.43,3.16,1.14,4.56,1.55,3,5.5,3.78,8.89,4.15a13.33,13.33,0,0,0,3.73.06,13,13,0,0,0,4.46-1.95,152.94,152.94,0,0,0,15.39-10.62c4.57-3.57,9-7.53,11.74-12.65,1.09-2.06,1.88-4.43,1.35-6.69a5.13,5.13,0,0,1-.35-2.68c.63-2,4.12-1.79,4.88-3.78-1.73-.92-2.7-2.79-3.56-4.55l-7.31-14.83c-1.6,1.45-3.6,2.65-5.19,4.1a1.38,1.38,0,0,1-.75.42c-.89.06-1.07-1.45-1.93-1.68a1.46,1.46,0,0,0-.91.15c-3.93,1.71-5.5,6.61-9.14,8.87-2.12,1.32-5,1.86-6,4.14-.26.59-.36,1.26-.67,1.83a1.55,1.55,0,0,1-1.55,1,27.88,27.88,0,0,0,.81-11.84C715.58,527.74,711.61,528.21,707.37,528.6Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#454b69"
|
||||
id="path4799" />
|
||||
<path
|
||||
d="M806.24,643.86c.27-.1.54-.21.81-.29a3.32,3.32,0,0,0,.9-3.6c-.35-.94-2.29-1-3.1-1.23q-4.58-1.1-9.17-2.06c-2.2-.47-4.61-.88-6.6.15-.74.39-1.35,1.69-1.85,2.35l.77.66c.83.69,1.68,1.37,2.56,2a.63.63,0,0,1,.66-.21.6.6,0,0,1,.29.32,2,2,0,0,1,.21.63A17.26,17.26,0,0,0,806.24,643.86Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#454b69"
|
||||
id="path4801" />
|
||||
<path
|
||||
d="M1157,459.81v85.44c0,2.54-4.76,4.61-10.65,4.61H920.81c-5.89,0-10.66-2.07-10.66-4.61V459.81c0-2.55,4.77-5.14,10.66-5.14h225.55C1152.25,454.67,1157,457.26,1157,459.81Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4803" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="393.65"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
id="rect4805" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="403.75"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4807" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="413.86"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4809" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="423.97"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4811" />
|
||||
<path
|
||||
d="M1157,220.13v85.45c0,2.54-4.76,4.61-10.65,4.61H920.81c-5.89,0-10.66-2.07-10.66-4.61V220.13c0-2.54,4.77-5.14,10.66-5.14h225.55C1152.25,215,1157,217.59,1157,220.13Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4815" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="153.97"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
id="rect4817" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="164.08"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4819" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="174.19"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4821" />
|
||||
<rect
|
||||
x="999.49"
|
||||
y="184.29"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4823" />
|
||||
<path
|
||||
d="M1045,339.47v85.45c0,2.54-4.77,4.6-10.66,4.6H808.76c-5.88,0-10.65-2.06-10.65-4.6V339.47c0-2.55,4.77-5.14,10.65-5.14h225.55C1040.2,334.33,1045,336.92,1045,339.47Z"
|
||||
transform="translate(-19 -91.56)"
|
||||
fill="#3f3d56"
|
||||
id="path4827" />
|
||||
<rect
|
||||
x="887.44"
|
||||
y="273.31"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
id="rect4829" />
|
||||
<rect
|
||||
x="887.44"
|
||||
y="283.42"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4833" />
|
||||
<rect
|
||||
x="887.44"
|
||||
y="293.52"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4835" />
|
||||
<rect
|
||||
x="887.44"
|
||||
y="303.63"
|
||||
width="117.48"
|
||||
height="4.15"
|
||||
fill="#6c63ff"
|
||||
opacity="0.5"
|
||||
id="rect4837" />
|
||||
<style
|
||||
id="style4919"
|
||||
type="text/css">
|
||||
.st0{fill:#77B3D4;}
|
||||
.st1{opacity:0.2;}
|
||||
.st2{fill:#231F20;}
|
||||
.st3{fill:#FFFFFF;}
|
||||
.st4{fill:#C75C5C;}
|
||||
.st5{fill:#4F5D73;}
|
||||
.st6{fill:#E0E0D1;}
|
||||
</style>
|
||||
<g
|
||||
id="g5924"
|
||||
transform="translate(911.18328,138.60391)">
|
||||
<g
|
||||
id="Layer_1">
|
||||
|
||||
<g
|
||||
id="g4961">
|
||||
<g
|
||||
id="g4927"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4925"
|
||||
d="m 12,25 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 25 Z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
|
||||
</g>
|
||||
|
||||
<g
|
||||
id="g4931">
|
||||
<path
|
||||
id="path4929"
|
||||
d="m 12,23 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 23 Z"
|
||||
class="st3"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
|
||||
</g>
|
||||
|
||||
<g
|
||||
id="g4935"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4933"
|
||||
d="M 48,14 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
|
||||
</g>
|
||||
|
||||
<g
|
||||
id="g4939">
|
||||
<path
|
||||
id="path4937"
|
||||
d="M 48,12 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
|
||||
class="st4"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#c75c5c" />
|
||||
|
||||
</g>
|
||||
|
||||
|
||||
<g
|
||||
id="g4947"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4945"
|
||||
d="m 20,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
|
||||
</g>
|
||||
|
||||
<g
|
||||
id="g4951"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4949"
|
||||
d="m 45,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
|
||||
</g>
|
||||
|
||||
<g
|
||||
id="g4955">
|
||||
<path
|
||||
id="path4953"
|
||||
d="m 20,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#e0e0d1" />
|
||||
|
||||
</g>
|
||||
|
||||
<g
|
||||
id="g4959">
|
||||
<path
|
||||
id="path4957"
|
||||
d="m 45,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#e0e0d1" />
|
||||
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</g>
|
||||
<g
|
||||
id="Layer_2">
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g5924-4"
|
||||
transform="translate(794.71,254.6)">
|
||||
<g
|
||||
id="Layer_1-5">
|
||||
<g
|
||||
id="g4961-4">
|
||||
<g
|
||||
id="g4927-7"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4925-4"
|
||||
d="m 12,25 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 25 Z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4931-4">
|
||||
<path
|
||||
id="path4929-3"
|
||||
d="m 12,23 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 23 Z"
|
||||
class="st3"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
<g
|
||||
id="g4935-0"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4933-7"
|
||||
d="M 48,14 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4939-8">
|
||||
<path
|
||||
id="path4937-6"
|
||||
d="M 48,12 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
|
||||
class="st4"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#c75c5c" />
|
||||
</g>
|
||||
<g
|
||||
id="g4947-8"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4945-8"
|
||||
d="m 20,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4951-4"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4949-3"
|
||||
d="m 45,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4955-1">
|
||||
<path
|
||||
id="path4953-4"
|
||||
d="m 20,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#e0e0d1" />
|
||||
</g>
|
||||
<g
|
||||
id="g4959-9">
|
||||
<path
|
||||
id="path4957-2"
|
||||
d="m 45,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#e0e0d1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="Layer_2-0" />
|
||||
</g>
|
||||
<g
|
||||
id="g5924-6"
|
||||
transform="translate(913.83516,374.62072)">
|
||||
<g
|
||||
id="Layer_1-8">
|
||||
<g
|
||||
id="g4961-6">
|
||||
<g
|
||||
id="g4927-6"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4925-49"
|
||||
d="m 12,25 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 25 Z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4931-5">
|
||||
<path
|
||||
id="path4929-0"
|
||||
d="m 12,23 v 25 c 0,2.2 1.8,4 4,4 h 32 c 2.2,0 4,-1.8 4,-4 V 23 Z"
|
||||
class="st3"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
<g
|
||||
id="g4935-4"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4933-8"
|
||||
d="M 48,14 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4939-7">
|
||||
<path
|
||||
id="path4937-1"
|
||||
d="M 48,12 H 16 c -2.2,0 -4,1.8 -4,4 v 7 h 40 v -7 c 0,-2.2 -1.8,-4 -4,-4 z"
|
||||
class="st4"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#c75c5c" />
|
||||
</g>
|
||||
<g
|
||||
id="g4947-7"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4945-2"
|
||||
d="m 20,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4951-7"
|
||||
class="st1"
|
||||
style="opacity:0.2">
|
||||
<path
|
||||
id="path4949-2"
|
||||
d="m 45,21 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#231f20" />
|
||||
</g>
|
||||
<g
|
||||
id="g4955-2">
|
||||
<path
|
||||
id="path4953-6"
|
||||
d="m 20,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#e0e0d1" />
|
||||
</g>
|
||||
<g
|
||||
id="g4959-1">
|
||||
<path
|
||||
id="path4957-0"
|
||||
d="m 45,19 c -1.1,0 -2,-0.9 -2,-2 v -7 c 0,-1.1 0.9,-2 2,-2 v 0 c 1.1,0 2,0.9 2,2 v 7 c 0,1.1 -0.9,2 -2,2 z"
|
||||
class="st6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#e0e0d1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="Layer_2-6" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 30 KiB |
1
js/src/assets/undraw_events.svg
Normal file
1
js/src/assets/undraw_events.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 18 KiB |
@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<span class="container">
|
||||
<time class="container" :datetime="dateObj.getUTCSeconds()">
|
||||
<span class="month">{{ month }}</span>
|
||||
<span class="day">{{ day }}</span>
|
||||
</span>
|
||||
</time>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
@Component
|
||||
export default class DateComponent extends Vue {
|
||||
export default class DateCalendarIcon extends Vue {
|
||||
@Prop({ required: true }) date!: string;
|
||||
|
||||
get dateObj() {
|
||||
return new Date(this.$props.date);
|
||||
return new Date(this.$props.date);
|
||||
}
|
||||
|
||||
get month() {
|
||||
@ -26,22 +26,27 @@ export default class DateComponent extends Vue {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: inline-flex;
|
||||
padding: 2px 0;
|
||||
width: 40px;
|
||||
background: #fff;
|
||||
time.container {
|
||||
background: #f6f7f8;
|
||||
border: 1px solid rgba(46,62,72,.12);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
/*height: 50px;*/
|
||||
width: 48px;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
flex: 0;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
display: block;
|
||||
|
||||
&.month {
|
||||
color: #fa3e3e;
|
||||
padding: 2px 0;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&.day {
|
@ -1,61 +1,124 @@
|
||||
<template>
|
||||
<div class="card">
|
||||
<router-link class="card" :to="{ name: 'Event', params: { uuid: event.uuid } }">
|
||||
<div class="card-image" v-if="!event.image">
|
||||
<figure class="image is-16by9">
|
||||
<img src="https://picsum.photos/g/400/225/?random">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<router-link :to="{ name: 'Event', params:{ uuid: event.uuid } }">
|
||||
<h2 class="title">{{ event.title }}</h2>
|
||||
</router-link>
|
||||
<DateComponent v-if="!options.hideDate" :date="event.beginsOn" />
|
||||
</div>
|
||||
<div v-if="!options.hideDetails">
|
||||
<div v-if="event.participants.length > 0 &&
|
||||
options.loggedPerson &&
|
||||
event.participants[0].actor.id === options.loggedPerson.id">
|
||||
<b-tag type="is-info"><translate>Organizer</translate></b-tag>
|
||||
</div>
|
||||
<div v-else-if="event.participants.length === 1">
|
||||
<translate
|
||||
:translate-params="{name: event.participants[0].actor.preferredUsername}"
|
||||
>%{name} organizes this event</translate>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span v-for="participant in event.participants" :key="participant.actor.uuid">
|
||||
{{ participant.actor.preferredUsername }}
|
||||
<span v-if="participant.role === ParticipantRole.CREATOR">(organizer)</span>,
|
||||
<!-- <translate
|
||||
:translate-params="{name: participant.actor.preferredUsername}"
|
||||
> %{name} is in,</translate>-->
|
||||
</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="title-wrapper">
|
||||
<div class="date-component">
|
||||
<date-calendar-icon v-if="!mergedOptions.hideDate" :date="event.beginsOn" />
|
||||
</div>
|
||||
<h2 class="title" ref="title">{{ event.title }}</h2>
|
||||
</div>
|
||||
<span>
|
||||
<span v-if="event.physicalAddress && event.physicalAddress.locality">{{ event.physicalAddress.locality }} - </span>
|
||||
<span v-if="actorDisplayName && actorDisplayName !== '@'">{{ actorDisplayName }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div v-if="!mergedOptions.hideDetails" class="details">-->
|
||||
<!-- <div v-if="event.participants.length > 0 &&-->
|
||||
<!-- mergedOptions.loggedPerson &&-->
|
||||
<!-- event.participants[0].actor.id === mergedOptions.loggedPerson.id">-->
|
||||
<!-- <b-tag type="is-info"><translate>Organizer</translate></b-tag>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-else-if="event.participants.length === 1">-->
|
||||
<!-- <translate-->
|
||||
<!-- :translate-params="{name: event.participants[0].actor.preferredUsername}"-->
|
||||
<!-- >%{name} organizes this event</translate>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-else>-->
|
||||
<!-- <span v-for="participant in event.participants" :key="participant.actor.uuid">-->
|
||||
<!-- {{ participant.actor.preferredUsername }}-->
|
||||
<!-- <span v-if="participant.role === ParticipantRole.CREATOR">(organizer)</span>,-->
|
||||
<!-- <!– <translate-->
|
||||
<!-- :translate-params="{name: participant.actor.preferredUsername}"-->
|
||||
<!-- > %{name} is in,</translate>–>-->
|
||||
<!-- </span>-->
|
||||
<!-- </div>-->
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { IEvent, ParticipantRole } from '@/types/event.model';
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import DateComponent from '@/components/Event/Date.vue';
|
||||
import DateCalendarIcon from '@/components/Event/DateCalendarIcon.vue';
|
||||
import { IActor, IPerson, Person } from '@/types/actor.model';
|
||||
const lineClamp = require('line-clamp');
|
||||
|
||||
export interface IEventCardOptions {
|
||||
hideDate: boolean;
|
||||
loggedPerson: IPerson | boolean;
|
||||
hideDetails: boolean;
|
||||
organizerActor: IActor | null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
DateComponent,
|
||||
DateCalendarIcon,
|
||||
EventCard,
|
||||
},
|
||||
mounted() {
|
||||
lineClamp(this.$refs.title, 3);
|
||||
},
|
||||
})
|
||||
export default class EventCard extends Vue {
|
||||
@Prop({ required: true }) event!: IEvent;
|
||||
@Prop({ default() { return { hideDate: false, loggedPerson: false, hideDetails: false }; } }) options!: object;
|
||||
@Prop({ required: false }) options!: IEventCardOptions;
|
||||
|
||||
data() {
|
||||
return {
|
||||
ParticipantRole,
|
||||
};
|
||||
ParticipantRole = ParticipantRole;
|
||||
|
||||
defaultOptions: IEventCardOptions = {
|
||||
hideDate: false,
|
||||
loggedPerson: false,
|
||||
hideDetails: false,
|
||||
organizerActor: null,
|
||||
};
|
||||
|
||||
get mergedOptions(): IEventCardOptions {
|
||||
return { ...this.defaultOptions, ...this.options };
|
||||
}
|
||||
|
||||
get actorDisplayName() {
|
||||
const actor = Object.assign(new Person(), this.event.organizerActor || this.mergedOptions.organizerActor);
|
||||
return actor.displayName();
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../variables";
|
||||
|
||||
a.card {
|
||||
border: none;
|
||||
background: $secondary;
|
||||
|
||||
div.card-image {
|
||||
background: $secondary;
|
||||
}
|
||||
|
||||
div.content {
|
||||
padding: 5px;
|
||||
background: $secondary;
|
||||
|
||||
div.title-wrapper {
|
||||
display: flex;
|
||||
|
||||
div.date-component {
|
||||
flex: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 400;
|
||||
line-height: 1em;
|
||||
font-size: 1.6em;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
36
js/src/components/Event/EventFullDate.vue
Normal file
36
js/src/components/Event/EventFullDate.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<translate
|
||||
v-if="!endsOn"
|
||||
:translate-params="{date: formatDate(beginsOn), time: formatTime(beginsOn)}"
|
||||
>The %{ date } at %{ time }</translate>
|
||||
<translate
|
||||
v-else-if="isSameDay()"
|
||||
:translate-params="{date: formatDate(beginsOn), startTime: formatTime(beginsOn), endTime: formatTime(endsOn)}"
|
||||
>The %{ date } from %{ startTime } to %{ endTime }</translate>
|
||||
<translate
|
||||
v-else-if="endsOn"
|
||||
:translate-params="{startDate: formatDate(beginsOn), startTime: formatTime(beginsOn), endDate: formatDate(endsOn), endTime: formatTime(endsOn)}"
|
||||
>From the %{ startDate } at %{ startTime } to the %{ endDate } at %{ endTime }</translate>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
@Component
|
||||
export default class EventFullDate extends Vue {
|
||||
@Prop({ required: true }) beginsOn!: string;
|
||||
@Prop({ required: false }) endsOn!: string;
|
||||
|
||||
formatDate(value) {
|
||||
return value ? new Date(value).toLocaleString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) : null;
|
||||
}
|
||||
|
||||
formatTime(value) {
|
||||
return value ? new Date(value).toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric' }) : null;
|
||||
}
|
||||
|
||||
isSameDay() {
|
||||
const sameDay = ((new Date(this.beginsOn)).toDateString()) === ((new Date(this.endsOn)).toDateString());
|
||||
return this.endsOn && sameDay;
|
||||
}
|
||||
}
|
||||
</script>
|
60
js/src/components/Footer.vue
Normal file
60
js/src/components/Footer.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<mobilizon-logo :invert="true" class="logo" />
|
||||
<img src="../assets/footer.png" :alt="$gettext('World map')" />
|
||||
<ul>
|
||||
<li><router-link :to="{ name: 'About'}"><translate>About</translate></router-link></li>
|
||||
<li><router-link :to="{ name: 'Licence'}"><translate>License</translate></router-link></li>
|
||||
<li><router-link :to="{ name: 'Legal'}"><translate>Legal</translate></router-link></li>
|
||||
</ul>
|
||||
<div class="content has-text-centered">
|
||||
<span
|
||||
v-translate="{
|
||||
date: new Date().getFullYear(),
|
||||
}"
|
||||
>© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks</span>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import Logo from './Logo.vue';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
'mobilizon-logo': Logo,
|
||||
},
|
||||
})
|
||||
export default class Footer extends Vue {
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../variables.scss";
|
||||
|
||||
footer.footer {
|
||||
color: $secondary;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.logo {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
div.content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: inline-flex;
|
||||
margin: auto 5px;
|
||||
|
||||
a {
|
||||
color: #eee;
|
||||
font-size: 1.5rem;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: $secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -7,11 +7,11 @@
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<router-link :to="{ name: 'Group', params:{ uuid: group.uuid } }">
|
||||
<h2 class="title">{{ group.name ? group.name : group.preferredUsername }}</h2>
|
||||
<router-link :to="{ name: RouteName.GROUP, params:{ preferredUsername: group.preferredUsername } }">
|
||||
<h2 class="title">{{ group.displayName() }}</h2>
|
||||
</router-link>
|
||||
</div>
|
||||
<div v-if="!hideDetails">
|
||||
<div>
|
||||
<p>{{ group.summary }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -20,11 +20,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { IGroup } from '../../types/actor.model';
|
||||
import { Group } from '@/types/actor.model';
|
||||
import { RouteName } from '@/router';
|
||||
|
||||
@Component
|
||||
export default class GroupCard extends Vue {
|
||||
@Prop({ required: true }) group!: IGroup;
|
||||
@Prop({ default: false }) hideDetails!: boolean;
|
||||
@Prop({ required: true }) group!: Group;
|
||||
|
||||
RouteName = RouteName;
|
||||
}
|
||||
</script>
|
||||
|
32
js/src/components/Logo.vue
Normal file
32
js/src/components/Logo.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<span class="logo" v-bind:class="{ invert }">M<span class="accent">o</span>b<span class="accent">ı</span>l<span class="accent">ı</span>z<span class="accent">o</span>n</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
@Component
|
||||
export default class Logo extends Vue {
|
||||
@Prop({ type: Boolean, required: false, default: false }) invert!: boolean;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../variables.scss";
|
||||
@import "~typeface-signika/index.css";
|
||||
|
||||
.logo {
|
||||
font-size: 3.5em;
|
||||
color: $primary;
|
||||
font-weight: 400;
|
||||
font-family: Signika,serif;
|
||||
|
||||
&.invert {
|
||||
color: $secondary;
|
||||
}
|
||||
|
||||
span.accent::after {
|
||||
content: "̇"; // U+0307
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<div style="height: 100%; width: 100%">
|
||||
<div class="map-container">
|
||||
<l-map
|
||||
:zoom="16"
|
||||
style="height: 80%; width: 100%"
|
||||
:zoom="mergedOptions.zoom"
|
||||
:style="`height: ${mergedOptions.height}; width: ${mergedOptions.width}`"
|
||||
class="leaflet-map"
|
||||
:center="[lat, lon]"
|
||||
>
|
||||
<l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"></l-tile-layer>
|
||||
@ -22,9 +23,16 @@ import { LMap, LTileLayer, LMarker, LPopup } from 'vue2-leaflet';
|
||||
@Component({
|
||||
components: { LTileLayer, LMap, LMarker, LPopup },
|
||||
})
|
||||
export default class Event extends Vue {
|
||||
export default class Map extends Vue {
|
||||
@Prop({ type: String, required: true }) coords!: string;
|
||||
@Prop({ type: String, required: false }) popup!: string;
|
||||
@Prop({ type: Object, required: false }) options!: object;
|
||||
|
||||
defaultOptions: object = {
|
||||
zoom: 15,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
};
|
||||
|
||||
mounted() {
|
||||
// this part resolve an issue where the markers would not appear
|
||||
@ -38,7 +46,21 @@ export default class Event extends Vue {
|
||||
});
|
||||
}
|
||||
|
||||
get mergedOptions(): object {
|
||||
return { ...this.defaultOptions, ...this.options };
|
||||
}
|
||||
|
||||
get lat() { return this.$props.coords.split(';')[0]; }
|
||||
get lon() { return this.$props.coords.split(';')[1]; }
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
div.map-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.leaflet-map {
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,43 +1,56 @@
|
||||
<template>
|
||||
<nav class="navbar is-fixed-top" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<router-link class="navbar-item" :to="{ name: 'Home' }">Mobilizon</router-link>
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<router-link class="navbar-item" :to="{ name: 'Home' }"><logo /></router-link>
|
||||
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger burger"
|
||||
aria-label="menu"
|
||||
aria-expanded="false"
|
||||
data-target="navbarBasicExample"
|
||||
>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<router-link class="button is-primary" v-if="!currentUser.isLoggedIn && config && config.registrationsOpen" :to="{ name: 'Register' }">
|
||||
<strong>
|
||||
<translate>Sign up</translate>
|
||||
</strong>
|
||||
</router-link>
|
||||
<router-link class="button is-light" v-if="!currentUser.isLoggedIn" :to="{ name: 'Login' }">
|
||||
<translate>Log in</translate>
|
||||
</router-link>
|
||||
<router-link
|
||||
class="button is-light"
|
||||
v-if="currentUser.isLoggedIn && loggedPerson"
|
||||
:to="{ name: 'Profile', params: { name: loggedPerson.preferredUsername} }"
|
||||
>
|
||||
<figure class="image is-24x24">
|
||||
<img :src="loggedPerson.avatarUrl">
|
||||
</figure>
|
||||
<span>{{ loggedPerson.preferredUsername }}</span>
|
||||
</router-link>
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger burger"
|
||||
aria-label="menu"
|
||||
aria-expanded="false"
|
||||
data-target="navbarBasicExample"
|
||||
@click="showNavbar = !showNavbar" :class="{ 'is-active': showNavbar }"
|
||||
>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-menu" :class="{ 'is-active': showNavbar }">
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<search-field />
|
||||
</div>
|
||||
<div class="navbar-item" v-if="!currentUser.isLoggedIn">
|
||||
<div class="buttons">
|
||||
<router-link class="button is-primary" v-if="config && config.registrationsOpen" :to="{ name: 'Register' }">
|
||||
<strong>
|
||||
<translate>Sign up</translate>
|
||||
</strong>
|
||||
</router-link>
|
||||
<router-link class="button is-primary" :to="{ name: 'Login' }">
|
||||
<translate>Log in</translate>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-item has-dropdown is-hoverable" v-else>
|
||||
<router-link
|
||||
class="navbar-link"
|
||||
v-if="currentUser.isLoggedIn && loggedPerson"
|
||||
:to="{ name: 'Profile', params: { name: loggedPerson.preferredUsername} }"
|
||||
>
|
||||
<figure class="image is-24x24">
|
||||
<img :src="loggedPerson.avatarUrl">
|
||||
</figure>
|
||||
<span>{{ loggedPerson.preferredUsername }}</span>
|
||||
</router-link>
|
||||
|
||||
<span v-if="currentUser.isLoggedIn" class="button" v-on:click="logout()">Log out</span>
|
||||
<div class="navbar-dropdown">
|
||||
<a class="navbar-item"><translate>My account</translate></a>
|
||||
<a class="navbar-item" v-on:click="logout()"><translate>Log out</translate></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -46,30 +59,19 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Watch } from 'vue-property-decorator';
|
||||
import { SEARCH } from '@/graphql/search';
|
||||
import { CURRENT_USER_CLIENT, UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user';
|
||||
import { onLogout } from '@/vue-apollo';
|
||||
import { deleteUserData } from '@/utils/auth';
|
||||
import { LOGGED_PERSON } from '@/graphql/actor';
|
||||
import { IActor, IPerson } from '@/types/actor.model';
|
||||
import { RouteName } from '@/router';
|
||||
import { IPerson } from '@/types/actor.model';
|
||||
import { CONFIG } from '@/graphql/config';
|
||||
import { IConfig } from '@/types/config.model';
|
||||
import { ICurrentUser } from '@/types/current-user.model'
|
||||
import { ICurrentUser } from '@/types/current-user.model';
|
||||
import Logo from '@/components/Logo.vue';
|
||||
import SearchField from '@/components/SearchField.vue';
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
search: {
|
||||
query: SEARCH,
|
||||
variables() {
|
||||
return {
|
||||
searchText: this.searchText,
|
||||
};
|
||||
},
|
||||
skip() {
|
||||
return !this.searchText;
|
||||
},
|
||||
},
|
||||
currentUser: {
|
||||
query: CURRENT_USER_CLIENT,
|
||||
},
|
||||
@ -77,35 +79,20 @@ import { ICurrentUser } from '@/types/current-user.model'
|
||||
query: CONFIG,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Logo,
|
||||
SearchField,
|
||||
},
|
||||
})
|
||||
export default class NavBar extends Vue {
|
||||
notifications = [
|
||||
{ header: 'Coucou' },
|
||||
{ title: "T'as une notification", subtitle: 'Et elle est cool' },
|
||||
];
|
||||
model = null;
|
||||
search: any[] = [];
|
||||
searchText: string | null = null;
|
||||
searchSelect = null;
|
||||
loggedPerson: IPerson | null = null;
|
||||
config!: IConfig;
|
||||
currentUser!: ICurrentUser;
|
||||
|
||||
get items() {
|
||||
return this.search.map(searchEntry => {
|
||||
switch (searchEntry.__typename) {
|
||||
case 'Actor':
|
||||
searchEntry.label =
|
||||
searchEntry.preferredUsername +
|
||||
(searchEntry.domain === null ? '' : `@${searchEntry.domain}`);
|
||||
break;
|
||||
case 'Event':
|
||||
searchEntry.label = searchEntry.title;
|
||||
break;
|
||||
}
|
||||
return searchEntry;
|
||||
});
|
||||
}
|
||||
showNavbar: boolean = false;
|
||||
|
||||
@Watch('currentUser')
|
||||
async onCurrentUserChanged() {
|
||||
@ -121,34 +108,6 @@ export default class NavBar extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('model')
|
||||
onModelChanged(val) {
|
||||
switch (val.__typename) {
|
||||
case 'Event':
|
||||
this.$router.push({ name: RouteName.EVENT, params: { uuid: val.uuid } });
|
||||
break;
|
||||
|
||||
case 'Actor':
|
||||
this.$router.push({
|
||||
name: RouteName.PROFILE,
|
||||
params: { name: this.usernameWithDomain(val) },
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
usernameWithDomain(actor: IActor) {
|
||||
return (
|
||||
actor.preferredUsername +
|
||||
(actor.domain === null ? '' : `@${actor.domain}`)
|
||||
);
|
||||
}
|
||||
|
||||
enter() {
|
||||
console.log('enter');
|
||||
this.$apollo.queries['search'].refetch();
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.$apollo.mutate({
|
||||
mutation: UPDATE_CURRENT_USER_CLIENT,
|
||||
@ -161,9 +120,16 @@ export default class NavBar extends Vue {
|
||||
|
||||
deleteUserData();
|
||||
|
||||
onLogout(this.$apollo)
|
||||
onLogout(this.$apollo);
|
||||
|
||||
return this.$router.push({ path: '/' })
|
||||
return this.$router.push({ path: '/' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../variables.scss";
|
||||
|
||||
nav {
|
||||
border-bottom: solid 1px #0a0a0a;
|
||||
}
|
||||
</style>
|
||||
|
22
js/src/components/SearchField.vue
Normal file
22
js/src/components/SearchField.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<b-input icon="magnify" type="search" rounded :placeholder="defaultPlaceHolder" v-model="searchText" @keyup.native.enter="enter" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { RouteName } from '@/router';
|
||||
|
||||
@Component
|
||||
export default class SearchField extends Vue {
|
||||
@Prop({ type: String, required: false }) placeholder!: string;
|
||||
searchText: string = '';
|
||||
|
||||
enter() {
|
||||
this.$router.push({ name: RouteName.SEARCH, params: { searchTerm: this.searchText } });
|
||||
}
|
||||
|
||||
get defaultPlaceHolder(): string {
|
||||
// We can't use "this" inside @Prop's default value.
|
||||
return this.placeholder || this.$gettext('Search');
|
||||
}
|
||||
}
|
||||
</script>
|
@ -95,3 +95,33 @@ mutation ($preferredUsername: String!, $name: String!, $summary: String!, $email
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const FETCH_GROUP = gql`
|
||||
query($name:String!) {
|
||||
group(preferredUsername: $name) {
|
||||
id,
|
||||
url,
|
||||
name,
|
||||
domain,
|
||||
summary,
|
||||
preferredUsername,
|
||||
suspended,
|
||||
avatarUrl,
|
||||
bannerUrl,
|
||||
organizedEvents {
|
||||
uuid,
|
||||
title,
|
||||
beginsOn
|
||||
},
|
||||
members {
|
||||
role,
|
||||
actor {
|
||||
id,
|
||||
name,
|
||||
domain,
|
||||
preferredUsername
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -4,6 +4,7 @@ export const CONFIG = gql`
|
||||
query {
|
||||
config {
|
||||
name,
|
||||
description,
|
||||
registrationsOpen
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,10 @@ export const FETCH_EVENT = gql`
|
||||
# },
|
||||
participants {
|
||||
${participantQuery}
|
||||
},
|
||||
tags {
|
||||
slug,
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,7 +79,8 @@ export const FETCH_EVENTS = gql`
|
||||
# online_address,
|
||||
# phone_address,
|
||||
physicalAddress {
|
||||
description
|
||||
description,
|
||||
locality
|
||||
}
|
||||
organizerActor {
|
||||
avatarUrl,
|
||||
|
@ -6,12 +6,14 @@ query SearchEvents($searchText: String!) {
|
||||
...on Event {
|
||||
title,
|
||||
uuid,
|
||||
beginsOn,
|
||||
__typename
|
||||
},
|
||||
...on Actor {
|
||||
avatarUrl,
|
||||
domain,
|
||||
preferredUsername,
|
||||
name,
|
||||
__typename
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
# English translations for mobilizon package.
|
||||
# Copyright (C) 2018 THE mobilizon'S COPYRIGHT HOLDER
|
||||
# Copyright (C) 2019 THE mobilizon'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the mobilizon package.
|
||||
# Automatically generated, 2018.
|
||||
# Automatically generated, 2019.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: mobilizon 0.1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-01-17 16:08+0100\n"
|
||||
"PO-Revision-Date: 2018-10-24 16:25+0200\n"
|
||||
"POT-Creation-Date: 2019-04-10 16:31+0200\n"
|
||||
"PO-Revision-Date: 2019-04-08 20:58+0200\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: en_US\n"
|
||||
@ -17,178 +17,423 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/App.vue:8
|
||||
#: src/components/Footer.vue:10
|
||||
msgid "© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks"
|
||||
msgstr ""
|
||||
msgstr "© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks"
|
||||
|
||||
#: src/components/Account/Register.vue:89
|
||||
#: src/views/Account/Register.vue:57
|
||||
msgid "A validation email was sent to %{email}"
|
||||
msgstr "A validation email was sent to %{email}"
|
||||
|
||||
#: src/components/Account/Register.vue:26
|
||||
msgid "About this instance"
|
||||
msgstr ""
|
||||
#: src/components/Footer.vue:5
|
||||
msgid "About"
|
||||
msgstr "About"
|
||||
|
||||
#: src/components/Account/Register.vue:92
|
||||
#: src/views/Event/Event.vue:138
|
||||
msgid "About this event"
|
||||
msgstr "About this event"
|
||||
|
||||
#: src/views/User/Register.vue:26
|
||||
msgid "About this instance"
|
||||
msgstr "About this instance"
|
||||
|
||||
#: src/views/Account/Identities.vue:7
|
||||
msgid "Add a new profile"
|
||||
msgstr "Add a new profile"
|
||||
|
||||
#: src/views/Event/Event.vue:44 src/views/Event/Event.vue:217
|
||||
msgid "Add to my calendar"
|
||||
msgstr "Add to my calendar"
|
||||
|
||||
#: src/views/Event/Event.vue:2
|
||||
msgid "Are you going to this event?"
|
||||
msgstr "Are you going to this event?"
|
||||
|
||||
#: src/views/Account/Register.vue:60
|
||||
msgid "Before you can login, you need to click on the link inside it to validate your account"
|
||||
msgstr "Before you can login, you need to click on the link inside it to validate your account"
|
||||
|
||||
#: src/components/Category/Create.vue:7
|
||||
msgid "Create a new category"
|
||||
msgstr ""
|
||||
#: src/views/Event/Event.vue:101
|
||||
msgid "By %{ name }"
|
||||
msgstr "By %{ name }"
|
||||
|
||||
#: src/components/Category/Create.vue:34
|
||||
msgid "Create category"
|
||||
msgstr ""
|
||||
#: src/views/Event/Create.vue:3
|
||||
msgid "Create a new event"
|
||||
msgstr "Create a new event"
|
||||
|
||||
#: src/components/Account/Register.vue:16
|
||||
#: src/views/Group/Create.vue:3
|
||||
msgid "Create a new group"
|
||||
msgstr "Create a new group"
|
||||
|
||||
#: src/views/Group/GroupList.vue:15
|
||||
msgid "Create group"
|
||||
msgstr "Create group"
|
||||
|
||||
#: src/views/Event/Create.vue:25
|
||||
msgid "Create my event"
|
||||
msgstr "Create my event"
|
||||
|
||||
#: src/views/Group/Create.vue:20
|
||||
msgid "Create my group"
|
||||
msgstr "Create my group"
|
||||
|
||||
#: src/views/Account/Register.vue:43
|
||||
msgid "Create my profile"
|
||||
msgstr "Create my profile"
|
||||
|
||||
#: src/views/Account/Profile.vue:61
|
||||
msgid "Create token"
|
||||
msgstr "Create token"
|
||||
|
||||
#: src/views/User/Register.vue:16
|
||||
msgid "Create your communities and your events"
|
||||
msgstr ""
|
||||
msgstr "Create your communities and your events"
|
||||
|
||||
#: src/components/Account/Profile.vue:48 src/components/Category/List.vue:21
|
||||
#: src/components/Event/Event.vue:41
|
||||
#: src/views/Account/Identities.vue:36
|
||||
msgid "Current"
|
||||
msgstr "Current"
|
||||
|
||||
#: src/views/Account/Profile.vue:93 src/views/Event/Event.vue:64
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
msgstr "Delete"
|
||||
|
||||
#: src/components/Account/Register.vue:80
|
||||
#: src/views/User/Register.vue:82
|
||||
msgid "Didn't receive the instructions ?"
|
||||
msgstr ""
|
||||
msgstr "Didn't receive the instructions ?"
|
||||
|
||||
#: src/components/Event/Event.vue:36
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Event/Event.vue:31
|
||||
#: src/views/Event/Event.vue:59
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Edit"
|
||||
|
||||
#: src/components/Account/Validate.vue:8
|
||||
msgid "Error while validating account"
|
||||
msgstr ""
|
||||
#: src/views/User/Validate.vue:8
|
||||
msgid "Either the account is already validated, either the validation token is incorrect."
|
||||
msgstr "Either the account is already validated, either the validation token is incorrect."
|
||||
|
||||
#: src/components/Category/List.vue:18
|
||||
msgid "Explore"
|
||||
msgstr ""
|
||||
#: src/views/Event/EventList.vue:3
|
||||
msgid "Event list"
|
||||
msgstr "Event list"
|
||||
|
||||
#: src/components/Account/Register.vue:14
|
||||
#: src/views/Search.vue:10
|
||||
msgid "Events"
|
||||
msgstr "Events"
|
||||
|
||||
#: src/views/Home.vue:68
|
||||
msgid "Events nearby you"
|
||||
msgstr "Events nearby you"
|
||||
|
||||
#: src/views/Home.vue:24
|
||||
msgid "Events you're going at"
|
||||
msgstr "Events you're going at"
|
||||
|
||||
#: src/views/User/Register.vue:14
|
||||
msgid "Features"
|
||||
msgstr ""
|
||||
msgstr "Features"
|
||||
|
||||
#: src/components/Account/Login.vue:46
|
||||
#: src/views/User/Login.vue:41
|
||||
msgid "Forgot your password ?"
|
||||
msgstr ""
|
||||
msgstr "Forgot your password ?"
|
||||
|
||||
#: src/components/Account/Register.vue:20
|
||||
#: src/components/Event/EventFullDate.vue:9
|
||||
msgid "From the %{ startDate } at %{ startTime } to the %{ endDate } at %{ endTime }"
|
||||
msgstr "From the %{ startDate } at %{ startTime } to the %{ endDate } at %{ endTime }"
|
||||
|
||||
#: src/views/Group/GroupList.vue:3
|
||||
msgid "Group List"
|
||||
msgstr "Group List"
|
||||
|
||||
#: src/views/Search.vue:28
|
||||
msgid "Groups"
|
||||
msgstr "Groups"
|
||||
|
||||
#: src/views/Account/Profile.vue:56
|
||||
msgid "iCal Feed"
|
||||
msgstr "iCal Feed"
|
||||
|
||||
#: src/views/Account/Identities.vue:4
|
||||
msgid "Identities"
|
||||
msgstr "Identities"
|
||||
|
||||
#: src/views/User/ResendConfirmation.vue:16
|
||||
msgid "If an account with this email exists, we just sent another confirmation email to %{email}"
|
||||
msgstr "If an account with this email exists, we just sent another confirmation email to %{email}"
|
||||
|
||||
#: src/views/Event/Event.vue:20
|
||||
msgid "Join"
|
||||
msgstr "Join"
|
||||
|
||||
#: src/views/User/Register.vue:20
|
||||
msgid ""
|
||||
"Learn more on\n"
|
||||
" <a target=\"_blank\" href=\"https://joinmobilizon.org\">joinmobilizon.org</a>"
|
||||
msgstr ""
|
||||
"Learn more on\n"
|
||||
" <a target=\"_blank\" href=\"https://joinmobilizon.org\">joinmobilizon.org</a>"
|
||||
|
||||
#: src/components/NavBar.vue:26
|
||||
#: src/views/Event/Event.vue:24
|
||||
msgid "Leave"
|
||||
msgstr "Leave"
|
||||
|
||||
#: src/components/Footer.vue:7
|
||||
msgid "Legal"
|
||||
msgstr "Legal"
|
||||
|
||||
#: src/components/Footer.vue:6
|
||||
msgid "License"
|
||||
msgstr "License"
|
||||
|
||||
#: src/components/NavBar.vue:32
|
||||
msgid "Log in"
|
||||
msgstr ""
|
||||
msgstr "Log in"
|
||||
|
||||
#: src/components/Account/Login.vue:38
|
||||
#: src/components/NavBar.vue:50
|
||||
msgid "Log out"
|
||||
msgstr "Log out"
|
||||
|
||||
#: src/views/User/Login.vue:33 src/views/User/Register.vue:91
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
msgstr "Login"
|
||||
|
||||
#: src/components/Account/Register.vue:32
|
||||
#: src/views/User/Register.vue:32
|
||||
msgid "meditate a bit"
|
||||
msgstr ""
|
||||
msgstr "meditate a bit"
|
||||
|
||||
#: src/components/Home.vue:33
|
||||
#: src/views/Group/Group.vue:41
|
||||
msgid "Members"
|
||||
msgstr "Members"
|
||||
|
||||
#: src/components/NavBar.vue:49
|
||||
msgid "My account"
|
||||
msgstr "My account"
|
||||
|
||||
#: src/views/Event/Event.vue:70
|
||||
msgid "No address defined"
|
||||
msgstr "No address defined"
|
||||
|
||||
#: src/views/Event/EventList.vue:15 src/views/Home.vue:78
|
||||
#: src/views/Search.vue:22
|
||||
msgid "No events found"
|
||||
msgstr ""
|
||||
msgstr "No events found"
|
||||
|
||||
#: src/components/Account/Profile.vue:29
|
||||
#: src/views/Group/Group.vue:52
|
||||
msgid "No group found"
|
||||
msgstr "No group found"
|
||||
|
||||
#: src/views/Search.vue:38
|
||||
msgid "No groups found"
|
||||
msgstr "No groups found"
|
||||
|
||||
#: src/views/Account/Profile.vue:66 src/views/Group/Group.vue:27
|
||||
msgid "Organized"
|
||||
msgstr ""
|
||||
msgstr "Organized"
|
||||
|
||||
#: src/components/Account/Register.vue:17
|
||||
#: src/components/Event/EventCard.vue:1
|
||||
msgid "Organizer"
|
||||
msgstr "Organizer"
|
||||
|
||||
#: src/views/User/Register.vue:17
|
||||
msgid "Other stuff…"
|
||||
msgstr ""
|
||||
msgstr "Other stuff…"
|
||||
|
||||
#: src/components/Account/SendPasswordReset.vue:4
|
||||
#: src/views/User/PasswordReset.vue:4 src/views/User/SendPasswordReset.vue:4
|
||||
msgid "Password reset"
|
||||
msgstr ""
|
||||
msgstr "Password reset"
|
||||
|
||||
#: src/components/Account/Register.vue:31
|
||||
#: src/views/User/Register.vue:31
|
||||
msgid "Please be nice to each other"
|
||||
msgstr ""
|
||||
msgstr "Please be nice to each other"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:21
|
||||
#: src/components/Account/SendPasswordReset.vue:22
|
||||
#: src/views/User/ResendConfirmation.vue:21
|
||||
#: src/views/User/SendPasswordReset.vue:22
|
||||
msgid "Please check you spam folder if you didn't receive the email."
|
||||
msgstr "Please check you spam folder if you didn't receive the email."
|
||||
|
||||
#: src/views/PageNotFound.vue:12
|
||||
msgid "Please contact this instance's Mobilizon admin if you think this is a mistake."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Account/Register.vue:35
|
||||
#: src/views/PageNotFound.vue:9
|
||||
msgid "Please make sure the address is correct and that the page hasn't been moved."
|
||||
msgstr ""
|
||||
|
||||
#: src/views/User/Register.vue:35
|
||||
msgid "Please read the full rules"
|
||||
msgstr ""
|
||||
msgstr "Please read the full rules"
|
||||
|
||||
#: src/components/Account/Register.vue:72 src/components/Home.vue:9
|
||||
#: src/views/Account/Profile.vue:45
|
||||
msgid "Private feeds"
|
||||
msgstr "Private feeds"
|
||||
|
||||
#: src/views/Event/Event.vue:34
|
||||
msgid "public event"
|
||||
msgstr "public event"
|
||||
|
||||
#: src/views/Account/Profile.vue:27
|
||||
msgid "Public feeds"
|
||||
msgstr "Public feeds"
|
||||
|
||||
#: src/views/Account/Profile.vue:38
|
||||
msgid "Public iCal Feed"
|
||||
msgstr "Public iCal Feed"
|
||||
|
||||
#: src/views/Account/Profile.vue:33
|
||||
msgid "Public RSS/Atom Feed"
|
||||
msgstr "Public RSS/Atom Feed"
|
||||
|
||||
#: src/views/Account/Identities.vue:16 src/views/Home.vue:8
|
||||
#: src/views/User/Login.vue:49 src/views/User/Register.vue:74
|
||||
msgid "Register"
|
||||
msgstr "Register"
|
||||
|
||||
#: src/components/Account/Register.vue:5
|
||||
#: src/views/Account/Register.vue:5 src/views/User/Register.vue:5
|
||||
msgid "Register an account on Mobilizon!"
|
||||
msgstr ""
|
||||
msgstr "Register an account on Mobilizon!"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:4
|
||||
#: src/views/Error.vue:2
|
||||
msgid "Registration is currently closed."
|
||||
msgstr "Registration is currently closed."
|
||||
|
||||
#: src/views/User/ResendConfirmation.vue:4
|
||||
msgid "Resend confirmation email"
|
||||
msgstr ""
|
||||
msgstr "Resend confirmation email"
|
||||
|
||||
#: src/components/Account/PasswordReset.vue:26
|
||||
#: src/views/User/PasswordReset.vue:29
|
||||
msgid "Reset my password"
|
||||
msgstr ""
|
||||
msgstr "Reset my password"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:11
|
||||
#: src/views/Account/Profile.vue:51
|
||||
msgid "RSS/Atom Feed"
|
||||
msgstr "RSS/Atom Feed"
|
||||
|
||||
#: src/views/PageNotFound.vue:18 src/components/SearchField.vue:19
|
||||
msgid "Search"
|
||||
msgstr "Search"
|
||||
|
||||
#: src/views/Search.vue:3
|
||||
msgid "Search results: « %{ search } »"
|
||||
msgstr "Search results: « %{ search } »"
|
||||
|
||||
#: src/views/User/ResendConfirmation.vue:11
|
||||
msgid "Send confirmation email again"
|
||||
msgstr ""
|
||||
msgstr "Send confirmation email again"
|
||||
|
||||
#: src/components/Account/SendPasswordReset.vue:12
|
||||
#: src/views/User/SendPasswordReset.vue:12
|
||||
msgid "Send email to reset my password"
|
||||
msgstr ""
|
||||
msgstr "Send email to reset my password"
|
||||
|
||||
#: src/components/NavBar.vue:22
|
||||
#: src/views/Event/Event.vue:206
|
||||
msgid "Share this event"
|
||||
msgstr "Share this event"
|
||||
|
||||
#: src/views/Event/Event.vue:79
|
||||
msgid "Show map"
|
||||
msgstr "Show map"
|
||||
|
||||
#: src/components/NavBar.vue:28
|
||||
msgid "Sign up"
|
||||
msgstr "Sign up"
|
||||
|
||||
#: src/components/Event/EventFullDate.vue:1
|
||||
msgid "The %{ date } at %{ time }"
|
||||
msgstr "The %{ date } at %{ time }"
|
||||
|
||||
#: src/components/Event/EventFullDate.vue:5
|
||||
msgid "The %{ date } from %{ startTime } to %{ endTime }"
|
||||
msgstr "The %{ date } from %{ startTime } to %{ endTime }"
|
||||
|
||||
#: src/views/Event/Event.vue:141
|
||||
msgid "The event organizer didn't add any description."
|
||||
msgstr "The event organizer didn't add any description."
|
||||
|
||||
#: src/views/PageNotFound.vue:6
|
||||
msgid "The page you're looking for doesn't exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Account/Profile.vue:43
|
||||
#: src/views/Event/Event.vue:224
|
||||
msgid "These events may interest you"
|
||||
msgstr "These events may interest you"
|
||||
|
||||
#: src/views/Home.vue:11
|
||||
msgid "This instance isn't opened to registrations, but you can register on other instances."
|
||||
msgstr "This instance isn't opened to registrations, but you can register on other instances."
|
||||
|
||||
#: src/views/Error.vue:6
|
||||
msgid "Unknown error."
|
||||
msgstr "Unknown error."
|
||||
|
||||
#: src/views/Account/Profile.vue:84
|
||||
msgid "User logout"
|
||||
msgstr ""
|
||||
msgstr "User logout"
|
||||
|
||||
#: src/components/Event/Event.vue:50
|
||||
msgid "Vous avez annoncé aller à cet événement."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Event/Event.vue:46
|
||||
msgid "Vous êtes organisateur de cet événement."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Account/SendPasswordReset.vue:17
|
||||
#: src/views/User/SendPasswordReset.vue:17
|
||||
msgid "We just sent an email to %{email}"
|
||||
msgstr ""
|
||||
msgstr "We just sent an email to %{email}"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:16
|
||||
msgid "We just sent another confirmation email to %{email}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Home.vue:16
|
||||
#: src/views/Home.vue:18
|
||||
msgid "Welcome back %{username}"
|
||||
msgstr ""
|
||||
msgstr "Welcome back %{username}"
|
||||
|
||||
#: src/components/Account/Login.vue:4
|
||||
#: src/views/User/Login.vue:4
|
||||
msgid "Welcome back!"
|
||||
msgstr ""
|
||||
msgstr "Welcome back!"
|
||||
|
||||
#: src/components/Account/Validate.vue:12
|
||||
#: src/views/Event/Event.vue:2
|
||||
msgid "You announced that you're going to this event."
|
||||
msgstr "You announced that you're going to this event."
|
||||
|
||||
#: src/views/User/Login.vue:58
|
||||
msgid "You are already logged-in."
|
||||
msgstr "You are already logged-in."
|
||||
|
||||
#: src/views/Event/Event.vue:2
|
||||
msgid "You are an organizer."
|
||||
msgstr "You are an organizer."
|
||||
|
||||
#: src/views/Home.vue:45
|
||||
msgid "You have one event in %{ days } days."
|
||||
msgid_plural "You have %{ count } events in %{ days } days"
|
||||
msgstr[0] "You have one event in %{ days } days."
|
||||
msgstr[1] "You have %{ count } events in %{ days } days"
|
||||
|
||||
#: src/views/Home.vue:29
|
||||
msgid "You have one event today."
|
||||
msgid_plural "You have %{ count } events today"
|
||||
msgstr[0] "You have one event today."
|
||||
msgstr[1] "You have %{ count } events today"
|
||||
|
||||
#: src/views/Home.vue:37
|
||||
msgid "You have one event tomorrow."
|
||||
msgid_plural "You have %{ count } events tomorrow"
|
||||
msgstr[0] "You have one event tomorrow."
|
||||
msgstr[1] "You have %{ count } events tomorrow"
|
||||
|
||||
#: src/views/User/Login.vue:9
|
||||
msgid "You need to login."
|
||||
msgstr "You need to login."
|
||||
|
||||
#: src/views/Home.vue:64
|
||||
msgid "You're not going to any event yet"
|
||||
msgstr "You're not going to any event yet"
|
||||
|
||||
#: src/views/User/Validate.vue:12
|
||||
msgid "Your account has been validated"
|
||||
msgstr ""
|
||||
msgstr "Your account has been validated"
|
||||
|
||||
#: src/components/Account/Validate.vue:3
|
||||
#: src/views/User/Validate.vue:3
|
||||
msgid "Your account is being validated"
|
||||
msgstr ""
|
||||
msgstr "Your account is being validated"
|
||||
|
||||
#: src/components/Account/Register.vue:28
|
||||
#: src/views/Account/Register.vue:52
|
||||
msgid "Your account is nearly ready, %{username}"
|
||||
msgstr "Your account is nearly ready, %{username}"
|
||||
|
||||
#: src/views/User/Register.vue:28
|
||||
msgid "Your local administrator resumed it's policy:"
|
||||
msgstr "Your local administrator resumed it's policy:"
|
||||
|
||||
#: src/components/Footer.vue:4
|
||||
msgid "World map"
|
||||
msgstr "World map"
|
||||
|
||||
#: src/views/PageNotFound.vue:40
|
||||
msgid "Search events, groups, etc."
|
||||
msgstr ""
|
||||
|
@ -1,30 +0,0 @@
|
||||
# English translations for mobilizon package.
|
||||
# Copyright (C) 2018 THE mobilizon'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the mobilizon package.
|
||||
# Automatically generated, 2018.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: mobilizon 0.1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-10-24 16:25+0200\n"
|
||||
"PO-Revision-Date: 2018-10-24 16:25+0200\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: en_US\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/components/Account/Register.vue:70
|
||||
msgid "A validation email was sent to %{email}"
|
||||
msgstr "A validation email was sent to %{email}"
|
||||
|
||||
#: src/components/Account/Register.vue:71
|
||||
msgid "Before you can login, you need to click on the link inside it to validate your account"
|
||||
msgstr "Before you can login, you need to click on the link inside it to validate your account"
|
||||
|
||||
#: src/components/Home.vue:14
|
||||
msgid "Register"
|
||||
msgstr "Register"
|
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: mobilizon 0.1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-01-17 16:08+0100\n"
|
||||
"PO-Revision-Date: 2018-10-24 16:25+0200\n"
|
||||
"POT-Creation-Date: 2019-04-10 16:31+0200\n"
|
||||
"PO-Revision-Date: 2019-04-10 16:33+0200\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: fr_FR\n"
|
||||
@ -16,179 +16,425 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
|
||||
#: src/App.vue:8
|
||||
#: src/components/Footer.vue:10
|
||||
msgid "© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks"
|
||||
msgstr ""
|
||||
msgstr "© Les contributeurs de Mobilizon %{date} - Fait avec Elixir, Phoenix, VueJS & et de l'amour et des semaines"
|
||||
|
||||
#: src/components/Account/Register.vue:89
|
||||
#: src/views/Account/Register.vue:57
|
||||
msgid "A validation email was sent to %{email}"
|
||||
msgstr ""
|
||||
msgstr "Un email de validation a été envoyé à %{email}"
|
||||
|
||||
#: src/components/Account/Register.vue:26
|
||||
#: src/components/Footer.vue:5
|
||||
msgid "About"
|
||||
msgstr "À propos"
|
||||
|
||||
#: src/views/Event/Event.vue:138
|
||||
msgid "About this event"
|
||||
msgstr "À propos de cet événement"
|
||||
|
||||
#: src/views/User/Register.vue:26
|
||||
msgid "About this instance"
|
||||
msgstr ""
|
||||
msgstr "À propos de cette instance"
|
||||
|
||||
#: src/components/Account/Register.vue:92
|
||||
#: src/views/Account/Identities.vue:7
|
||||
msgid "Add a new profile"
|
||||
msgstr "Ajouter un nouveau profil"
|
||||
|
||||
#: src/views/Event/Event.vue:44 src/views/Event/Event.vue:217
|
||||
msgid "Add to my calendar"
|
||||
msgstr "Ajouter à mon agenda"
|
||||
|
||||
#: src/views/Event/Event.vue:2
|
||||
msgid "Are you going to this event?"
|
||||
msgstr "Allez-vous à cet événement ?"
|
||||
|
||||
#: src/views/Account/Register.vue:60
|
||||
msgid "Before you can login, you need to click on the link inside it to validate your account"
|
||||
msgstr ""
|
||||
msgstr "Avant que vous puissiez vous enregistrer, vous devez cliquer sur le lien à l'intérieur pour valider votre compte"
|
||||
|
||||
#: src/components/Category/Create.vue:7
|
||||
msgid "Create a new category"
|
||||
msgstr ""
|
||||
#: src/views/Event/Event.vue:101
|
||||
msgid "By %{ name }"
|
||||
msgstr "Par %{name}"
|
||||
|
||||
#: src/components/Category/Create.vue:34
|
||||
msgid "Create category"
|
||||
msgstr ""
|
||||
#: src/views/Event/Create.vue:3
|
||||
msgid "Create a new event"
|
||||
msgstr "Créer un nouvel événement"
|
||||
|
||||
#: src/components/Account/Register.vue:16
|
||||
#: src/views/Group/Create.vue:3
|
||||
msgid "Create a new group"
|
||||
msgstr "Créer un nouveau groupe"
|
||||
|
||||
#: src/views/Group/GroupList.vue:15
|
||||
msgid "Create group"
|
||||
msgstr "Créer un groupe"
|
||||
|
||||
#: src/views/Event/Create.vue:25
|
||||
msgid "Create my event"
|
||||
msgstr "Créer mon événement"
|
||||
|
||||
#: src/views/Group/Create.vue:20
|
||||
msgid "Create my group"
|
||||
msgstr "Créer mon groupe"
|
||||
|
||||
#: src/views/Account/Register.vue:43
|
||||
msgid "Create my profile"
|
||||
msgstr "Créer mon profil"
|
||||
|
||||
#: src/views/Account/Profile.vue:61
|
||||
msgid "Create token"
|
||||
msgstr "Créer un jeton"
|
||||
|
||||
#: src/views/User/Register.vue:16
|
||||
msgid "Create your communities and your events"
|
||||
msgstr ""
|
||||
msgstr "Créer vos communautés et vos événements"
|
||||
|
||||
#: src/components/Account/Profile.vue:48 src/components/Category/List.vue:21
|
||||
#: src/components/Event/Event.vue:41
|
||||
#: src/views/Account/Identities.vue:36
|
||||
msgid "Current"
|
||||
msgstr "Actuel"
|
||||
|
||||
#: src/views/Account/Profile.vue:93 src/views/Event/Event.vue:64
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
msgstr "Supprimer"
|
||||
|
||||
#: src/components/Account/Register.vue:80
|
||||
#: src/views/User/Register.vue:82
|
||||
msgid "Didn't receive the instructions ?"
|
||||
msgstr ""
|
||||
msgstr "Vous n'avez pas reçu les instructions ?"
|
||||
|
||||
#: src/components/Event/Event.vue:36
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Event/Event.vue:31
|
||||
#: src/views/Event/Event.vue:59
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Éditer"
|
||||
|
||||
#: src/components/Account/Validate.vue:8
|
||||
msgid "Error while validating account"
|
||||
msgstr ""
|
||||
#: src/views/User/Validate.vue:8
|
||||
msgid "Either the account is already validated, either the validation token is incorrect."
|
||||
msgstr "Soit le compte est déjà validé, soit le jeton de validation est incorrect."
|
||||
|
||||
#: src/components/Category/List.vue:18
|
||||
msgid "Explore"
|
||||
msgstr ""
|
||||
#: src/views/Event/EventList.vue:3
|
||||
msgid "Event list"
|
||||
msgstr "Liste d'événements"
|
||||
|
||||
#: src/components/Account/Register.vue:14
|
||||
#: src/views/Search.vue:10
|
||||
msgid "Events"
|
||||
msgstr "Événements"
|
||||
|
||||
#: src/views/Home.vue:68
|
||||
msgid "Events nearby you"
|
||||
msgstr "Événements près de chez vous"
|
||||
|
||||
#: src/views/Home.vue:24
|
||||
msgid "Events you're going at"
|
||||
msgstr "Événements auxquels vous vous rendez"
|
||||
|
||||
#: src/views/User/Register.vue:14
|
||||
msgid "Features"
|
||||
msgstr ""
|
||||
msgstr "Fonctionnalités"
|
||||
|
||||
#: src/components/Account/Login.vue:46
|
||||
#: src/views/User/Login.vue:41
|
||||
msgid "Forgot your password ?"
|
||||
msgstr ""
|
||||
msgstr "Mot de passe oublié ?"
|
||||
|
||||
#: src/components/Account/Register.vue:20
|
||||
#: src/components/Event/EventFullDate.vue:9
|
||||
msgid "From the %{ startDate } at %{ startTime } to the %{ endDate } at %{ endTime }"
|
||||
msgstr "Du %{ startDate } à %{ startTime } au %{ endDate } à %{ endTime }"
|
||||
|
||||
#: src/views/Group/GroupList.vue:3
|
||||
msgid "Group List"
|
||||
msgstr "Liste de groupes"
|
||||
|
||||
#: src/views/Search.vue:28
|
||||
msgid "Groups"
|
||||
msgstr "Groupes"
|
||||
|
||||
#: src/views/Account/Profile.vue:56
|
||||
msgid "iCal Feed"
|
||||
msgstr "Flux iCal"
|
||||
|
||||
#: src/views/Account/Identities.vue:4
|
||||
msgid "Identities"
|
||||
msgstr "Identités"
|
||||
|
||||
#: src/views/User/ResendConfirmation.vue:16
|
||||
msgid "If an account with this email exists, we just sent another confirmation email to %{email}"
|
||||
msgstr "Si un compte avec un tel email existe, nous venons juste d'envoyer un nouvel email de confirmation à %{email}"
|
||||
|
||||
#: src/views/Event/Event.vue:20
|
||||
msgid "Join"
|
||||
msgstr "Rejoindre"
|
||||
|
||||
#: src/views/User/Register.vue:20
|
||||
msgid ""
|
||||
"Learn more on\n"
|
||||
" <a target=\"_blank\" href=\"https://joinmobilizon.org\">joinmobilizon.org</a>"
|
||||
msgstr ""
|
||||
"En apprendre plus sur\n"
|
||||
" <a target=\"_blank\" href=\"https://joinmobilizon.org\">joinmobilizon.org</a>"
|
||||
|
||||
#: src/components/NavBar.vue:26
|
||||
#: src/views/Event/Event.vue:24
|
||||
msgid "Leave"
|
||||
msgstr "Quitter"
|
||||
|
||||
#: src/components/Footer.vue:7
|
||||
msgid "Legal"
|
||||
msgstr "Mentions légales"
|
||||
|
||||
#: src/components/Footer.vue:6
|
||||
msgid "License"
|
||||
msgstr "License"
|
||||
|
||||
#: src/components/NavBar.vue:32
|
||||
msgid "Log in"
|
||||
msgstr ""
|
||||
msgstr "Se connecter"
|
||||
|
||||
#: src/components/Account/Login.vue:38
|
||||
#: src/components/NavBar.vue:50
|
||||
msgid "Log out"
|
||||
msgstr "Se déconnecter"
|
||||
|
||||
#: src/views/User/Login.vue:33 src/views/User/Register.vue:91
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
msgstr "Se connecter"
|
||||
|
||||
#: src/components/Account/Register.vue:32
|
||||
#: src/views/User/Register.vue:32
|
||||
msgid "meditate a bit"
|
||||
msgstr ""
|
||||
msgstr "méditez un peu"
|
||||
|
||||
#: src/components/Home.vue:33
|
||||
#: src/views/Group/Group.vue:41
|
||||
msgid "Members"
|
||||
msgstr "Membres"
|
||||
|
||||
#: src/components/NavBar.vue:49
|
||||
msgid "My account"
|
||||
msgstr "Mon compte"
|
||||
|
||||
#: src/views/Event/Event.vue:70
|
||||
msgid "No address defined"
|
||||
msgstr "Aucune adresse définie"
|
||||
|
||||
#: src/views/Event/EventList.vue:15 src/views/Home.vue:78
|
||||
#: src/views/Search.vue:22
|
||||
msgid "No events found"
|
||||
msgstr ""
|
||||
msgstr "Aucun événement trouvé"
|
||||
|
||||
#: src/components/Account/Profile.vue:29
|
||||
#: src/views/Group/Group.vue:52
|
||||
msgid "No group found"
|
||||
msgstr "Aucun groupe trouvé"
|
||||
|
||||
#: src/views/Search.vue:38
|
||||
msgid "No groups found"
|
||||
msgstr "Aucun groupe trouvé"
|
||||
|
||||
#: src/views/Account/Profile.vue:66 src/views/Group/Group.vue:27
|
||||
msgid "Organized"
|
||||
msgstr ""
|
||||
msgstr "Organisés"
|
||||
|
||||
#: src/components/Account/Register.vue:17
|
||||
#: src/components/Event/EventCard.vue:1
|
||||
msgid "Organizer"
|
||||
msgstr "Organisateur"
|
||||
|
||||
#: src/views/User/Register.vue:17
|
||||
msgid "Other stuff…"
|
||||
msgstr ""
|
||||
msgstr "Autres trucs…"
|
||||
|
||||
#: src/components/Account/SendPasswordReset.vue:4
|
||||
#: src/views/User/PasswordReset.vue:4 src/views/User/SendPasswordReset.vue:4
|
||||
msgid "Password reset"
|
||||
msgstr ""
|
||||
msgstr "Réinitialisation du mot de passe"
|
||||
|
||||
#: src/components/Account/Register.vue:31
|
||||
#: src/views/User/Register.vue:31
|
||||
msgid "Please be nice to each other"
|
||||
msgstr ""
|
||||
msgstr "Soyez sympas entre vous"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:21
|
||||
#: src/components/Account/SendPasswordReset.vue:22
|
||||
#: src/views/User/ResendConfirmation.vue:21
|
||||
#: src/views/User/SendPasswordReset.vue:22
|
||||
msgid "Please check you spam folder if you didn't receive the email."
|
||||
msgstr ""
|
||||
msgstr "Merci de vérifier votre dossier des indésirables si vous n'avez pas reçu l'email."
|
||||
|
||||
#: src/components/Account/Register.vue:35
|
||||
#: src/views/PageNotFound.vue:12
|
||||
msgid "Please contact this instance's Mobilizon admin if you think this is a mistake."
|
||||
msgstr "Veuillez contacter l'administrateur de cette instance Mobilizon si vous pensez qu’il s’agit d’une erreur."
|
||||
|
||||
#: src/views/PageNotFound.vue:9
|
||||
msgid "Please make sure the address is correct and that the page hasn't been moved."
|
||||
msgstr "Assurez‐vous que l’adresse est correcte et que la page n’a pas été déplacée."
|
||||
|
||||
#: src/views/User/Register.vue:35
|
||||
msgid "Please read the full rules"
|
||||
msgstr ""
|
||||
msgstr "Merci de lire les règles complètes"
|
||||
|
||||
#: src/components/Account/Register.vue:72 src/components/Home.vue:9
|
||||
#: src/views/Account/Profile.vue:45
|
||||
msgid "Private feeds"
|
||||
msgstr "Flux privés"
|
||||
|
||||
#: src/views/Event/Event.vue:34
|
||||
msgid "public event"
|
||||
msgstr "événement public"
|
||||
|
||||
#: src/views/Account/Profile.vue:27
|
||||
msgid "Public feeds"
|
||||
msgstr "Flux publics"
|
||||
|
||||
#: src/views/Account/Profile.vue:38
|
||||
msgid "Public iCal Feed"
|
||||
msgstr "Flux iCal public"
|
||||
|
||||
#: src/views/Account/Profile.vue:33
|
||||
msgid "Public RSS/Atom Feed"
|
||||
msgstr "Flux RSS/Atom public"
|
||||
|
||||
#: src/views/Account/Identities.vue:16 src/views/Home.vue:8
|
||||
#: src/views/User/Login.vue:49 src/views/User/Register.vue:74
|
||||
msgid "Register"
|
||||
msgstr "S'inscrire"
|
||||
|
||||
#: src/components/Account/Register.vue:5
|
||||
#: src/views/Account/Register.vue:5 src/views/User/Register.vue:5
|
||||
msgid "Register an account on Mobilizon!"
|
||||
msgstr ""
|
||||
msgstr "S'inscrire sur Mobilizon !"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:4
|
||||
#: src/views/Error.vue:2
|
||||
msgid "Registration is currently closed."
|
||||
msgstr "Les inscriptions sont actuellement fermées."
|
||||
|
||||
#: src/views/User/ResendConfirmation.vue:4
|
||||
msgid "Resend confirmation email"
|
||||
msgstr ""
|
||||
msgstr "Envoyer à nouveau l'email de confirmation"
|
||||
|
||||
#: src/components/Account/PasswordReset.vue:26
|
||||
#: src/views/User/PasswordReset.vue:29
|
||||
msgid "Reset my password"
|
||||
msgstr ""
|
||||
msgstr "Réinitialiser mon mot de passe"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:11
|
||||
#: src/views/Account/Profile.vue:51
|
||||
msgid "RSS/Atom Feed"
|
||||
msgstr "Flux RSS/Atom"
|
||||
|
||||
#: src/views/PageNotFound.vue:18 src/components/SearchField.vue:19
|
||||
msgid "Search"
|
||||
msgstr "Rechercher"
|
||||
|
||||
#: src/views/Search.vue:3
|
||||
msgid "Search results: « %{ search } »"
|
||||
msgstr "Résultats de recherche : « %{ search } »"
|
||||
|
||||
#: src/views/User/ResendConfirmation.vue:11
|
||||
msgid "Send confirmation email again"
|
||||
msgstr ""
|
||||
msgstr "Envoyer l'email de confirmation à nouveau"
|
||||
|
||||
#: src/components/Account/SendPasswordReset.vue:12
|
||||
#: src/views/User/SendPasswordReset.vue:12
|
||||
msgid "Send email to reset my password"
|
||||
msgstr ""
|
||||
msgstr "Envoyer un email pour réinitialiser mon mot de passe"
|
||||
|
||||
#: src/components/NavBar.vue:22
|
||||
#: src/views/Event/Event.vue:206
|
||||
msgid "Share this event"
|
||||
msgstr "Partager cet événement"
|
||||
|
||||
#: src/views/Event/Event.vue:79
|
||||
msgid "Show map"
|
||||
msgstr "Afficher la carte"
|
||||
|
||||
#: src/components/NavBar.vue:28
|
||||
msgid "Sign up"
|
||||
msgstr ""
|
||||
msgstr "S'enregistrer"
|
||||
|
||||
#: src/components/Account/Profile.vue:43
|
||||
#: src/components/Event/EventFullDate.vue:1
|
||||
msgid "The %{ date } at %{ time }"
|
||||
msgstr "Le %{ date } à %{ time }"
|
||||
|
||||
#: src/components/Event/EventFullDate.vue:5
|
||||
msgid "The %{ date } from %{ startTime } to %{ endTime }"
|
||||
msgstr "Le %{ date } de %{ startTime } à %{ endTime }"
|
||||
|
||||
#: src/views/Event/Event.vue:141
|
||||
msgid "The event organizer didn't add any description."
|
||||
msgstr "L'organisateur de l'événement n'a pas ajouté de description."
|
||||
|
||||
#: src/views/PageNotFound.vue:6
|
||||
msgid "The page you're looking for doesn't exist."
|
||||
msgstr "La page que vous recherchez n'existe pas."
|
||||
|
||||
#: src/views/Event/Event.vue:224
|
||||
msgid "These events may interest you"
|
||||
msgstr "Ces événements peuvent vous intéresser"
|
||||
|
||||
#: src/views/Home.vue:11
|
||||
msgid "This instance isn't opened to registrations, but you can register on other instances."
|
||||
msgstr "Cette instance n'autorise pas les inscriptions, mais vous pouvez vous enregistrer sur d'autres instances."
|
||||
|
||||
#: src/views/Error.vue:6
|
||||
msgid "Unknown error."
|
||||
msgstr "Erreur inconnue."
|
||||
|
||||
#: src/views/Account/Profile.vue:84
|
||||
msgid "User logout"
|
||||
msgstr ""
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: src/components/Event/Event.vue:50
|
||||
msgid "Vous avez annoncé aller à cet événement."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Event/Event.vue:46
|
||||
msgid "Vous êtes organisateur de cet événement."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Account/SendPasswordReset.vue:17
|
||||
#: src/views/User/SendPasswordReset.vue:17
|
||||
msgid "We just sent an email to %{email}"
|
||||
msgstr ""
|
||||
msgstr "Nous venons d'envoyer un email à %{email}"
|
||||
|
||||
#: src/components/Account/ResendConfirmation.vue:16
|
||||
msgid "We just sent another confirmation email to %{email}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Home.vue:16
|
||||
#: src/views/Home.vue:18
|
||||
msgid "Welcome back %{username}"
|
||||
msgstr ""
|
||||
msgstr "Bon retour %{username}"
|
||||
|
||||
#: src/components/Account/Login.vue:4
|
||||
#: src/views/User/Login.vue:4
|
||||
msgid "Welcome back!"
|
||||
msgstr ""
|
||||
msgstr "Bon retour !"
|
||||
|
||||
#: src/components/Account/Validate.vue:12
|
||||
#: src/views/Event/Event.vue:2
|
||||
msgid "You announced that you're going to this event."
|
||||
msgstr "Vous avez annoncé vous rendre à cet événement."
|
||||
|
||||
#: src/views/User/Login.vue:58
|
||||
msgid "You are already logged-in."
|
||||
msgstr "Vous êtes déjà connecté."
|
||||
|
||||
#: src/views/Event/Event.vue:2
|
||||
msgid "You are an organizer."
|
||||
msgstr "Vous êtes un organisateur."
|
||||
|
||||
#: src/views/Home.vue:45
|
||||
msgid "You have one event in %{ days } days."
|
||||
msgid_plural "You have %{ count } events in %{ days } days"
|
||||
msgstr[0] "Vous avez un événement dans %{ days } jours."
|
||||
msgstr[1] "Vous avez %{ count } événements dans %{ days } jours"
|
||||
|
||||
#: src/views/Home.vue:29
|
||||
msgid "You have one event today."
|
||||
msgid_plural "You have %{ count } events today"
|
||||
msgstr[0] "Vous avez un événement aujourd'hui."
|
||||
msgstr[1] "Vous avez %{ count } événements aujourd'hui"
|
||||
|
||||
#: src/views/Home.vue:37
|
||||
msgid "You have one event tomorrow."
|
||||
msgid_plural "You have %{ count } events tomorrow"
|
||||
msgstr[0] "Vous avez un événement demain."
|
||||
msgstr[1] "Vous avez %{ count } événements demain"
|
||||
|
||||
#: src/views/User/Login.vue:9
|
||||
msgid "You need to login."
|
||||
msgstr "Vous devez vous connecter."
|
||||
|
||||
#: src/views/Home.vue:64
|
||||
msgid "You're not going to any event yet"
|
||||
msgstr "Vous n'allez à aucun événement pour le moment"
|
||||
|
||||
#: src/views/User/Validate.vue:12
|
||||
msgid "Your account has been validated"
|
||||
msgstr ""
|
||||
msgstr "Votre compte a été validé"
|
||||
|
||||
#: src/components/Account/Validate.vue:3
|
||||
#: src/views/User/Validate.vue:3
|
||||
msgid "Your account is being validated"
|
||||
msgstr ""
|
||||
msgstr "Votre compte est en cours de validation"
|
||||
|
||||
#: src/components/Account/Register.vue:28
|
||||
#: src/views/Account/Register.vue:52
|
||||
msgid "Your account is nearly ready, %{username}"
|
||||
msgstr "Votre compte est presque prêt, %{ username }"
|
||||
|
||||
#: src/views/User/Register.vue:28
|
||||
msgid "Your local administrator resumed it's policy:"
|
||||
msgstr ""
|
||||
msgstr "Votre administrateur local a résumé sa politique ainsi :"
|
||||
|
||||
#: src/components/Footer.vue:4
|
||||
msgid "World map"
|
||||
msgstr "Carte mondiale"
|
||||
|
||||
#: src/views/PageNotFound.vue:40
|
||||
msgid "Search events, groups, etc."
|
||||
msgstr "Rechercher des événements, des groupes, etc."
|
||||
|
@ -1,30 +0,0 @@
|
||||
# French translations for mobilizon package.
|
||||
# Copyright (C) 2018 THE mobilizon'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the mobilizon package.
|
||||
# Automatically generated, 2018.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: mobilizon 0.1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-10-24 16:25+0200\n"
|
||||
"PO-Revision-Date: 2018-10-24 16:25+0200\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/components/Account/Register.vue:70
|
||||
msgid "A validation email was sent to %{email}"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Account/Register.vue:71
|
||||
msgid "Before you can login, you need to click on the link inside it to validate your account"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/Home.vue:14
|
||||
msgid "Register"
|
||||
msgstr "S'inscrire"
|
File diff suppressed because one or more lines are too long
@ -3,7 +3,6 @@
|
||||
import Vue from 'vue';
|
||||
import VueSimpleMarkdown from 'vue-simple-markdown';
|
||||
import Buefy from 'buefy';
|
||||
import 'buefy/dist/buefy.css';
|
||||
import GetTextPlugin from 'vue-gettext';
|
||||
import App from '@/App.vue';
|
||||
import router from '@/router';
|
||||
@ -20,9 +19,6 @@ Vue.use(Buefy, {
|
||||
|
||||
const language = (window.navigator as any).userLanguage || window.navigator.language;
|
||||
|
||||
Vue.filter('formatDate', value => value ? new Date(value).toLocaleString() : null);
|
||||
Vue.filter('formatDay', value => value ? new Date(value).toLocaleDateString() : null);
|
||||
|
||||
Vue.use(GetTextPlugin, {
|
||||
translations,
|
||||
defaultLanguage: 'en_US',
|
||||
|
@ -33,7 +33,7 @@ export const actorRoutes: RouteConfig[] = [
|
||||
meta: { requiredAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/~:name',
|
||||
path: '/~:preferredUsername',
|
||||
name: ActorRouteName.GROUP,
|
||||
component: Group,
|
||||
props: true,
|
||||
|
@ -12,7 +12,7 @@ export const beforeRegisterGuard: NavigationGuard = async function (to, from, ne
|
||||
|
||||
const config: IConfig = data.config;
|
||||
|
||||
if (config.registrationsOpen === false) {
|
||||
if (!config.registrationsOpen) {
|
||||
return next({
|
||||
name: ErrorRouteName.ERROR,
|
||||
query: { code: ErrorCode.REGISTRATION_CLOSED },
|
||||
|
@ -7,12 +7,24 @@ import { EventRouteName, eventRoutes } from '@/router/event';
|
||||
import { ActorRouteName, actorRoutes } from '@/router/actor';
|
||||
import { ErrorRouteName, errorRoutes } from '@/router/error';
|
||||
import { authGuardIfNeeded } from '@/router/guards/auth-guard';
|
||||
import Search from '@/views/Search.vue';
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
enum GlobalRouteName {
|
||||
HOME = 'Home',
|
||||
PAGE_NOT_FOUND = 'PageNotFound',
|
||||
SEARCH = 'Search',
|
||||
}
|
||||
|
||||
function scrollBehavior(to) {
|
||||
if (to.hash) {
|
||||
return {
|
||||
selector: to.hash,
|
||||
// , offset: { x: 0, y: 10 }
|
||||
};
|
||||
}
|
||||
return { x: 0, y: 0 };
|
||||
}
|
||||
|
||||
// Hack to merge enums
|
||||
@ -26,6 +38,7 @@ export const RouteName = {
|
||||
};
|
||||
|
||||
const router = new Router({
|
||||
scrollBehavior,
|
||||
mode: 'history',
|
||||
base: '/',
|
||||
routes: [
|
||||
@ -33,7 +46,13 @@ const router = new Router({
|
||||
...eventRoutes,
|
||||
...actorRoutes,
|
||||
...errorRoutes,
|
||||
|
||||
{
|
||||
path: '/search/:searchTerm/:searchType?',
|
||||
name: RouteName.SEARCH,
|
||||
component: Search,
|
||||
props: true,
|
||||
meta: { requiredAuth: false },
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: RouteName.HOME,
|
||||
|
@ -22,6 +22,19 @@ export class Actor implements IActor {
|
||||
summary: string = '';
|
||||
suspended: boolean = false;
|
||||
url: string = '';
|
||||
|
||||
get displayNameAndUsername(): string {
|
||||
return `${this.name} (${this.usernameWithDomain})`;
|
||||
}
|
||||
|
||||
public usernameWithDomain(): string {
|
||||
const domain = this.domain ? `@${this.domain}` : '';
|
||||
return `@${this.preferredUsername}${domain}`;
|
||||
}
|
||||
|
||||
public displayName(): string {
|
||||
return this.name != null && this.name !== '' ? this.name : this.usernameWithDomain();
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPerson extends IActor {
|
||||
@ -38,6 +51,10 @@ export class Person extends Actor implements IPerson {
|
||||
goingToEvents: IEvent[] = [];
|
||||
}
|
||||
|
||||
export class Group extends Actor implements IGroup {
|
||||
members: IMember[] = [];
|
||||
}
|
||||
|
||||
export interface IFeedToken {
|
||||
token: string;
|
||||
actor?: IPerson;
|
||||
|
@ -1,5 +1,6 @@
|
||||
export interface IConfig {
|
||||
name: string;
|
||||
description: string;
|
||||
|
||||
registrationsOpen: boolean;
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ export enum EventStatus {
|
||||
}
|
||||
|
||||
export enum EventVisibility {
|
||||
PUBLIC,
|
||||
UNLISTED,
|
||||
RESTRICTED,
|
||||
PRIVATE,
|
||||
PUBLIC = 'PUBLIC',
|
||||
UNLISTED = 'UNLISTED',
|
||||
RESTRICTED = 'RESTRICTED',
|
||||
PRIVATE = 'PRIVATE',
|
||||
}
|
||||
|
||||
export enum EventJoinOptions {
|
||||
|
3
js/src/types/search.model.ts
Normal file
3
js/src/types/search.model.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface ISearch {
|
||||
__typename: string;
|
||||
}
|
15
js/src/variables.scss
Normal file
15
js/src/variables.scss
Normal file
@ -0,0 +1,15 @@
|
||||
$primary: #424056;
|
||||
$secondary: #FAB12D;
|
||||
|
||||
// Navbar
|
||||
$navbar-background-color: $secondary;
|
||||
$navbar-item-color: $primary;
|
||||
$navbar-height: 7rem;
|
||||
|
||||
|
||||
// Footer
|
||||
$footer-padding: 3rem 1.5rem 4rem;
|
||||
$footer-background-color: $primary;
|
||||
|
||||
// Card
|
||||
$card-background-color: $secondary;
|
@ -1,105 +1,101 @@
|
||||
<template>
|
||||
<section>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="card" v-if="person">
|
||||
<div class="card-image" v-if="person.bannerUrl">
|
||||
<figure class="image">
|
||||
<img :src="person.bannerUrl">
|
||||
<section class="container">
|
||||
<div v-if="person">
|
||||
<div class="card-image" v-if="person.bannerUrl">
|
||||
<figure class="image">
|
||||
<img :src="person.bannerUrl">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img :src="person.avatarUrl">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img :src="person.avatarUrl">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title">{{ person.name }}</p>
|
||||
<p class="subtitle">@{{ person.preferredUsername }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p v-html="person.summary"></p>
|
||||
</div>
|
||||
|
||||
<b-dropdown hoverable has-link aria-role="list">
|
||||
<button class="button is-info" slot="trigger">
|
||||
<translate>Public feeds</translate>
|
||||
<b-icon icon="menu-down"></b-icon>
|
||||
</button>
|
||||
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('atom', true)">
|
||||
<translate>Public RSS/Atom Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('ics', true)">
|
||||
<translate>Public iCal Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
|
||||
<b-dropdown hoverable has-link aria-role="list" v-if="person.feedTokens.length > 0">
|
||||
<button class="button is-info" slot="trigger">
|
||||
<translate>Private feeds</translate>
|
||||
<b-icon icon="menu-down"></b-icon>
|
||||
</button>
|
||||
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('atom', false)">
|
||||
<translate>RSS/Atom Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('ics', false)">
|
||||
<translate>iCal Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
<a class="button" v-else @click="createToken">
|
||||
<translate>Create token</translate>
|
||||
</a>
|
||||
<div class="media-content">
|
||||
<p class="title">{{ person.name }}</p>
|
||||
<p class="subtitle">@{{ person.preferredUsername }}</p>
|
||||
</div>
|
||||
<section v-if="person.organizedEvents.length > 0">
|
||||
<h2 class="subtitle">
|
||||
<translate>Organized</translate>
|
||||
</h2>
|
||||
<div class="columns">
|
||||
<EventCard
|
||||
v-for="event in person.organizedEvents"
|
||||
:event="event"
|
||||
:options="{ hideDetails: true }"
|
||||
:key="event.uuid"
|
||||
class="column is-one-third"
|
||||
/>
|
||||
</div>
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<a
|
||||
class="button"
|
||||
@click="logoutUser()"
|
||||
v-if="loggedPerson && loggedPerson.id === person.id"
|
||||
>
|
||||
<translate>User logout</translate>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a
|
||||
class="button"
|
||||
@click="deleteProfile()"
|
||||
v-if="loggedPerson && loggedPerson.id === person.id"
|
||||
>
|
||||
<translate>Delete</translate>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<vue-simple-markdown :source="person.summary"></vue-simple-markdown>
|
||||
</div>
|
||||
|
||||
<b-dropdown hoverable has-link aria-role="list">
|
||||
<button class="button is-primary" slot="trigger">
|
||||
<translate>Public feeds</translate>
|
||||
<b-icon icon="menu-down"></b-icon>
|
||||
</button>
|
||||
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('atom', true)">
|
||||
<translate>Public RSS/Atom Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('ics', true)">
|
||||
<translate>Public iCal Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
|
||||
<b-dropdown hoverable has-link aria-role="list" v-if="person.feedTokens.length > 0">
|
||||
<button class="button is-info" slot="trigger">
|
||||
<translate>Private feeds</translate>
|
||||
<b-icon icon="menu-down"></b-icon>
|
||||
</button>
|
||||
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('atom', false)">
|
||||
<translate>RSS/Atom Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item aria-role="listitem">
|
||||
<a :href="feedUrls('ics', false)">
|
||||
<translate>iCal Feed</translate>
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
<a class="button" v-else-if="loggedPerson" @click="createToken">
|
||||
<translate>Create token</translate>
|
||||
</a>
|
||||
</div>
|
||||
<section v-if="person.organizedEvents.length > 0">
|
||||
<h2 class="subtitle">
|
||||
<translate>Organized</translate>
|
||||
</h2>
|
||||
<div class="columns">
|
||||
<EventCard
|
||||
v-for="event in person.organizedEvents"
|
||||
:event="event"
|
||||
:options="{ hideDetails: true, organizerActor: person }"
|
||||
:key="event.uuid"
|
||||
class="column is-one-third"
|
||||
/>
|
||||
</div>
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<a
|
||||
class="button"
|
||||
@click="logoutUser()"
|
||||
v-if="loggedPerson && loggedPerson.id === person.id"
|
||||
>
|
||||
<translate>User logout</translate>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a
|
||||
class="button"
|
||||
@click="deleteProfile()"
|
||||
v-if="loggedPerson && loggedPerson.id === person.id"
|
||||
>
|
||||
<translate>Delete</translate>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@ -172,3 +168,8 @@ export default class Profile extends Vue {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import "../../variables";
|
||||
@import "~bulma/sass/utilities/_all";
|
||||
@import "~bulma/sass/components/dropdown.sass";
|
||||
</style>
|
||||
|
@ -9,17 +9,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { ErrorCode } from '@/types/error-code.model';
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { ErrorCode } from '@/types/error-code.model';
|
||||
|
||||
@Component
|
||||
export default class ErrorPage extends Vue {
|
||||
code: ErrorCode | null = null;
|
||||
@Component
|
||||
export default class ErrorPage extends Vue {
|
||||
code: ErrorCode | null = null;
|
||||
|
||||
ErrorCode = ErrorCode;
|
||||
ErrorCode = ErrorCode;
|
||||
|
||||
mounted() {
|
||||
this.code = this.$route.query[ 'code' ] as ErrorCode;
|
||||
}
|
||||
mounted() {
|
||||
this.code = this.$route.query['code'] as ErrorCode;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -95,7 +95,7 @@ export default class CreateEvent extends Vue {
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,131 +1,258 @@
|
||||
<template>
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-three-quarters">
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<div class="card" v-if="event">
|
||||
<div class="card-image">
|
||||
<figure class="image is-4by3">
|
||||
<img src="https://picsum.photos/600/400/">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span>{{ event.beginsOn | formatDay }}</span>
|
||||
<span class="tag is-primary">{{ event.category }}</span>
|
||||
<h1 class="title">{{ event.title }}</h1>
|
||||
<router-link
|
||||
:to="{name: 'Profile', params: { name: event.organizerActor.preferredUsername } }"
|
||||
>
|
||||
<figure v-if="event.organizerActor.avatarUrl">
|
||||
<img :src="event.organizerActor.avatarUrl">
|
||||
</figure>
|
||||
</router-link>
|
||||
<span
|
||||
v-if="event.organizerActor"
|
||||
>Organisé par {{ event.organizerActor.name ? event.organizerActor.name : event.organizerActor.preferredUsername }}</span>
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<router-link
|
||||
v-if="actorIsOrganizer()"
|
||||
class="button"
|
||||
:to="{ name: 'EditEvent', params: {uuid: event.uuid}}"
|
||||
>
|
||||
<translate>Edit</translate>
|
||||
</router-link>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button" @click="downloadIcsEvent()">
|
||||
<translate>Download</translate>
|
||||
<div>
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<div v-if="event">
|
||||
<div class="header-picture container">
|
||||
<figure class="image is-3by1">
|
||||
<img src="https://picsum.photos/600/200/">
|
||||
</figure>
|
||||
</div>
|
||||
<section class="container">
|
||||
<div class="title-and-participate-button">
|
||||
<div class="title-wrapper">
|
||||
<div class="date-component">
|
||||
<date-calendar-icon :date="event.beginsOn"></date-calendar-icon>
|
||||
</div>
|
||||
<h1 class="title">{{ event.title }}</h1>
|
||||
</div>
|
||||
<div v-if="!actorIsOrganizer()" class="participate-button has-text-centered">
|
||||
<a v-if="!actorIsParticipant()" @click="joinEvent" class="button is-large is-primary is-rounded">
|
||||
<b-icon icon="circle-outline"></b-icon>
|
||||
<translate>Join</translate>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-danger" v-if="actorIsOrganizer()" @click="deleteEvent()">
|
||||
<translate>Delete</translate>
|
||||
<a v-if="actorIsParticipant()" @click="leaveEvent" class="button is-large is-primary is-rounded">
|
||||
<b-icon icon="check-circle"></b-icon>
|
||||
<translate>Leave</translate>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ event.beginsOn | formatDate }} - {{ event.endsOn | formatDate }}</span>
|
||||
</div>
|
||||
<div class="address" v-if="event.physicalAddress">
|
||||
<h3 class="subtitle">Adresse</h3>
|
||||
<address>
|
||||
<span>{{ event.physicalAddress.description }}</span><br>
|
||||
<span>{{ event.physicalAddress.floor }} {{ event.physicalAddress.street }}</span><br>
|
||||
<span>{{ event.physicalAddress.postal_code }} {{ event.physicalAddress.locality }}</span><br>
|
||||
<span>{{ event.physicalAddress.region }} {{ event.physicalAddress.country }}</span>
|
||||
</address>
|
||||
<div class="map">
|
||||
<map-leaflet
|
||||
:coords="event.physicalAddress.geom"
|
||||
:popup="event.physicalAddress.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="actorIsOrganizer()">
|
||||
<translate>You are an organizer.</translate>
|
||||
</p>
|
||||
<div v-else>
|
||||
<p v-if="actorIsParticipant()">
|
||||
<translate>You announced that you're going to this event.</translate>
|
||||
</p>
|
||||
<p v-else>
|
||||
<translate>Are you going to this event?</translate><br />
|
||||
<span>
|
||||
<translate
|
||||
:translate-n="event.participants.length"
|
||||
translate-plural="%{event.participants.length} persons are going"
|
||||
>
|
||||
One person is going.
|
||||
</translate>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="!actorIsOrganizer()">
|
||||
<a v-if="!actorIsParticipant()" @click="joinEvent" class="button">
|
||||
<translate>Join</translate>
|
||||
</a>
|
||||
<a v-if="actorIsParticipant()" @click="leaveEvent" class="button">Leave</a>
|
||||
</div>
|
||||
<h2 class="subtitle">Details</h2>
|
||||
<p v-if="event.description">
|
||||
<vue-simple-markdown :source="event.description"></vue-simple-markdown>
|
||||
</p>
|
||||
<h2 class="subtitle">Participants</h2>
|
||||
<span v-if="event.participants.length === 0">No participants yet.</span>
|
||||
<div class="columns">
|
||||
<router-link
|
||||
class="card column"
|
||||
v-for="participant in event.participants"
|
||||
:key="participant.preferredUsername"
|
||||
:to="{name: 'Profile', params: { name: participant.actor.preferredUsername }}"
|
||||
>
|
||||
<div>
|
||||
<figure>
|
||||
<img v-if="!participant.actor.avatarUrl" src="https://picsum.photos/125/125/">
|
||||
<img v-else :src="participant.actor.avatarUrl">
|
||||
</figure>
|
||||
<span>{{ participant.actor.preferredUsername }}</span>
|
||||
<div class="metadata columns">
|
||||
<div class="column is-three-quarters-desktop">
|
||||
<p class="tags" v-if="event.category || event.tags.length > 0">
|
||||
<span class="tag" v-if="event.category">{{ event.category }}</span>
|
||||
<span class="tag" v-if="event.tags" v-for="tag in event.tags">{{ tag.title }}</span>
|
||||
<span class="visibility">
|
||||
<translate v-if="event.visibility === EventVisibility.PUBLIC">public event</translate>
|
||||
</span>
|
||||
</p>
|
||||
<div class="date-and-add-to-calendar">
|
||||
<div class="date-and-privacy" v-if="event.beginsOn">
|
||||
<b-icon icon="calendar-clock" />
|
||||
<event-full-date :beginsOn="event.beginsOn" :endsOn="event.endsOn" />
|
||||
</div>
|
||||
<a class="add-to-calendar" @click="downloadIcsEvent()">
|
||||
<b-icon icon="calendar-plus" />
|
||||
<translate>Add to my calendar</translate>
|
||||
</a>
|
||||
</div>
|
||||
</router-link>
|
||||
<p class="slug">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
In aliquam libero quam, ut ultricies velit porttitor a. Maecenas mollis vestibulum dolor.
|
||||
</p>
|
||||
</div>
|
||||
<div class="column sidebar">
|
||||
<div class="field has-addons" v-if="actorIsOrganizer()">
|
||||
<p class="control">
|
||||
<router-link
|
||||
class="button"
|
||||
:to="{ name: 'EditEvent', params: {uuid: event.uuid}}"
|
||||
>
|
||||
<translate>Edit</translate>
|
||||
</router-link>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-danger" @click="deleteEvent()">
|
||||
<translate>Delete</translate>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="address-wrapper">
|
||||
<b-icon icon="map" />
|
||||
<translate v-if="!event.physicalAddress">No address defined</translate>
|
||||
<div class="address" v-if="event.physicalAddress">
|
||||
<address>
|
||||
<span class="addressDescription">{{ event.physicalAddress.description }}</span>
|
||||
<span>{{ event.physicalAddress.floor }} {{ event.physicalAddress.street }}</span>
|
||||
<span>{{ event.physicalAddress.postal_code }} {{ event.physicalAddress.locality }}</span>
|
||||
<!-- <span>{{ event.physicalAddress.region }} {{ event.physicalAddress.country }}</span>-->
|
||||
</address>
|
||||
<span class="map-show-button" @click="showMap = !showMap">
|
||||
<translate>Show map</translate>
|
||||
</span>
|
||||
</div>
|
||||
<!-- <div class="map" v-if="showMap">-->
|
||||
<!-- <map-leaflet-->
|
||||
<!-- :coords="event.physicalAddress.geom"-->
|
||||
<!-- :popup="event.physicalAddress.description"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<b-modal v-if="event.physicalAddress" :active.sync="showMap" :width="800" scroll="keep">
|
||||
<div class="map">
|
||||
<map-leaflet
|
||||
:coords="event.physicalAddress.geom"
|
||||
:popup="event.physicalAddress.description"
|
||||
/>
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
<div class="organizer">
|
||||
<router-link
|
||||
:to="{name: 'Profile', params: { name: event.organizerActor.preferredUsername } }"
|
||||
>
|
||||
<translate
|
||||
:translate-params="{name: event.organizerActor.name ? event.organizerActor.name : event.organizerActor.preferredUsername}"
|
||||
v-if="event.organizerActor">By %{ name }</translate>
|
||||
<figure v-if="event.organizerActor.avatarUrl" class="image is-48x48">
|
||||
<img
|
||||
class="is-rounded"
|
||||
:src="event.organizerActor.avatarUrl"
|
||||
:alt="$gettextInterpolate('%{actor}\'s avatar', {actor: event.organizerActor.preferredUsername})" />
|
||||
</figure>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- <p v-if="actorIsOrganizer()">-->
|
||||
<!-- <translate>You are an organizer.</translate>-->
|
||||
<!-- </p>-->
|
||||
<!-- <div v-else>-->
|
||||
<!-- <p v-if="actorIsParticipant()">-->
|
||||
<!-- <translate>You announced that you're going to this event.</translate>-->
|
||||
<!-- </p>-->
|
||||
<!-- <p v-else>-->
|
||||
<!-- <translate>Are you going to this event?</translate><br />-->
|
||||
<!-- <span>-->
|
||||
<!-- <translate-->
|
||||
<!-- :translate-n="event.participants.length"-->
|
||||
<!-- translate-plural="%{event.participants.length} persons are going"-->
|
||||
<!-- >-->
|
||||
<!-- One person is going.-->
|
||||
<!-- </translate>-->
|
||||
<!-- </span>-->
|
||||
<!-- </p>-->
|
||||
<!-- </div>-->
|
||||
<div class="description">
|
||||
<div class="description-container container">
|
||||
<h3 class="title">
|
||||
<translate>About this event</translate>
|
||||
</h3>
|
||||
<p v-if="!event.description">
|
||||
<translate>The event organizer didn't add any description.</translate>
|
||||
</p>
|
||||
<div class="columns" v-else="event.description">
|
||||
<div class="column is-half">
|
||||
<!-- <vue-simple-markdown :source="event.description" />-->
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Suspendisse vehicula ex dapibus augue volutpat, ultrices cursus mi rutrum.
|
||||
Nunc ante nunc, facilisis a tellus quis, tempor mollis diam. Aenean consectetur quis est a ultrices.
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
</p>
|
||||
<p><a href="https://framasoft.org">https://framasoft.org</a>
|
||||
<p>
|
||||
Nam sit amet est eget velit tristique commodo. Etiam sollicitudin dignissim diam, ut ultricies tortor.
|
||||
Sed quis blandit diam, a tincidunt nunc. Donec tincidunt tristique neque at rhoncus. Ut eget vulputate felis.
|
||||
Pellentesque nibh purus, viverra ac augue sed, iaculis feugiat velit. Nulla ut hendrerit elit.
|
||||
Etiam at justo eu nunc tempus sagittis. Sed ac tincidunt tellus, sit amet luctus velit.
|
||||
Nam ullamcorper eros eleifend, eleifend diam vitae, lobortis risus.
|
||||
</p>
|
||||
<p>
|
||||
<em>
|
||||
Curabitur rhoncus sapien tortor, vitae imperdiet massa scelerisque non.
|
||||
Aliquam eu augue mi. Donec hendrerit lorem orci.
|
||||
</em>
|
||||
</p>
|
||||
<p>
|
||||
Donec volutpat, enim eu laoreet dictum, urna quam varius enim, eu convallis urna est vitae massa.
|
||||
Morbi porttitor lacus a sem efficitur blandit. Mauris in est in quam tincidunt iaculis non vitae ipsum.
|
||||
Phasellus eget velit tellus. Curabitur ac neque pharetra velit viverra mollis.
|
||||
</p>
|
||||
<img src="https://framasoft.org/img/biglogo-notxt.png" alt="logo Framasoft"/>
|
||||
<p>Aenean gravida, ante vitae aliquet aliquet, elit quam tristique orci, sit amet dictum lorem ipsum nec tortor.
|
||||
Vestibulum est eros, faucibus et semper vel, dapibus ac est. Suspendisse potenti. Suspendisse potenti.
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
||||
Nulla molestie nisi ac risus hendrerit, dapibus mattis sapien scelerisque.
|
||||
</p>
|
||||
<p>Maecenas id pretium justo, nec dignissim sapien. Mauris in venenatis odio, in congue augue. </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <section class="container">-->
|
||||
<!-- <h2 class="title">Participants</h2>-->
|
||||
<!-- <span v-if="event.participants.length === 0">No participants yet.</span>-->
|
||||
<!-- <div class="columns">-->
|
||||
<!-- <router-link-->
|
||||
<!-- class="column"-->
|
||||
<!-- v-for="participant in event.participants"-->
|
||||
<!-- :key="participant.preferredUsername"-->
|
||||
<!-- :to="{name: 'Profile', params: { name: participant.actor.preferredUsername }}"-->
|
||||
<!-- >-->
|
||||
<!-- <div>-->
|
||||
<!-- <figure>-->
|
||||
<!-- <img v-if="!participant.actor.avatarUrl" src="https://picsum.photos/125/125/">-->
|
||||
<!-- <img v-else :src="participant.actor.avatarUrl">-->
|
||||
<!-- </figure>-->
|
||||
<!-- <span>{{ participant.actor.preferredUsername }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </section>-->
|
||||
<section class="share">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column is-half has-text-centered">
|
||||
<h3 class="title"><translate>Share this event</translate></h3>
|
||||
<b-icon icon="mastodon" size="is-large" type="is-primary" />
|
||||
<a :href="facebookShareUrl" target="_blank" rel="nofollow noopener"><b-icon icon="facebook" size="is-large" type="is-primary" /></a>
|
||||
<a :href="twitterShareUrl" target="_blank" rel="nofollow noopener"><b-icon icon="twitter" size="is-large" type="is-primary" /></a>
|
||||
<a :href="emailShareUrl" target="_blank" rel="nofollow noopener"><b-icon icon="email" size="is-large" type="is-primary" /></a>
|
||||
<!-- TODO: mailto: links are not used anymore, we should provide a popup to redact a message instead -->
|
||||
<a :href="linkedInShareUrl" target="_blank" rel="nofollow noopener"><b-icon icon="linkedin" size="is-large" type="is-primary" /></a>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="column is-half has-text-right add-to-calendar">
|
||||
<h3 @click="downloadIcsEvent()">
|
||||
<translate>Add to my calendar</translate>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="more-events container">
|
||||
<h3 class="title has-text-centered"><translate>These events may interest you</translate></h3>
|
||||
<div class="columns">
|
||||
<div class="column" v-for="index in 3" :key="index">
|
||||
<EventCard :event="event" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { DELETE_EVENT, FETCH_EVENT, JOIN_EVENT, LEAVE_EVENT } from '@/graphql/event';
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { LOGGED_PERSON } from '@/graphql/actor';
|
||||
import { IEvent, IParticipant } from '@/types/event.model';
|
||||
import { EventVisibility, IEvent, IParticipant } from '@/types/event.model';
|
||||
import { IPerson } from '@/types/actor.model';
|
||||
import { RouteName } from '@/router';
|
||||
import 'vue-simple-markdown/dist/vue-simple-markdown.css';
|
||||
import { GRAPHQL_API_ENDPOINT } from '@/api/_entrypoint';
|
||||
import DateCalendarIcon from '@/components/Event/DateCalendarIcon.vue';
|
||||
import BIcon from 'buefy/src/components/icon/Icon.vue';
|
||||
import EventCard from '@/components/Event/EventCard.vue';
|
||||
import EventFullDate from '@/components/Event/EventFullDate.vue';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
EventFullDate,
|
||||
EventCard,
|
||||
BIcon,
|
||||
DateCalendarIcon,
|
||||
'map-leaflet': () => import('@/components/Map.vue'),
|
||||
},
|
||||
apollo: {
|
||||
@ -148,6 +275,9 @@ export default class Event extends Vue {
|
||||
event!: IEvent;
|
||||
loggedPerson!: IPerson;
|
||||
validationSent: boolean = false;
|
||||
showMap: boolean = false;
|
||||
|
||||
EventVisibility = EventVisibility;
|
||||
|
||||
async deleteEvent() {
|
||||
const router = this.$router;
|
||||
@ -241,12 +371,255 @@ export default class Event extends Vue {
|
||||
return this.loggedPerson &&
|
||||
this.loggedPerson.id === this.event.organizerActor.id;
|
||||
}
|
||||
|
||||
get twitterShareUrl(): string {
|
||||
return `https://twitter.com/intent/tweet?url=${encodeURIComponent(this.event.url)}&text=${this.event.title}`;
|
||||
}
|
||||
|
||||
get facebookShareUrl(): string {
|
||||
return `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(this.event.url)}`;
|
||||
}
|
||||
|
||||
get linkedInShareUrl(): string {
|
||||
return `https://www.linkedin.com/shareArticle?mini=true&url=${encodeURIComponent(this.event.url)}&title=${this.event.title}`;
|
||||
}
|
||||
|
||||
get emailShareUrl(): string {
|
||||
return `mailto:?to=&body=${this.event.url}${encodeURIComponent('\n\n')}${this.event.description}&subject=${this.event.title}`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.address div.map {
|
||||
height: 400px;
|
||||
width: 400px;
|
||||
padding: 25px 35px;
|
||||
<style lang="scss" scoped>
|
||||
@import "../../variables";
|
||||
|
||||
div.sidebar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
background: #B3B3B2;
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
height: calc(100% - 60px);
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
div.address-wrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
|
||||
div.address {
|
||||
flex: 1;
|
||||
|
||||
.map-show-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
address {
|
||||
font-style: normal;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
span.addressDescription {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1 0 auto;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
:not(.addressDescription) {
|
||||
color: rgba(46, 62, 72, .6);
|
||||
flex: 1;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.map {
|
||||
height: 900px;
|
||||
width: 100%;
|
||||
padding: 25px 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
div.organizer {
|
||||
display: inline-flex;
|
||||
padding-top: 10px;
|
||||
|
||||
a {
|
||||
color: #4a4a4a;
|
||||
|
||||
span {
|
||||
line-height: 2.7rem;
|
||||
padding-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.title-and-participate-button {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/*flex-flow: row wrap;*/
|
||||
justify-content: space-between;
|
||||
/*align-self: center;*/
|
||||
align-items: stretch;
|
||||
/*align-content: space-around;*/
|
||||
padding: 15px 10px 0;
|
||||
|
||||
div.title-wrapper {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
|
||||
|
||||
div.date-component {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
h1.title {
|
||||
font-weight: normal;
|
||||
word-break: break-word;
|
||||
font-size: 1.7em;
|
||||
}
|
||||
}
|
||||
|
||||
.participate-button {
|
||||
flex: 0 1 auto;
|
||||
display: inline-flex;
|
||||
|
||||
a.button {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.metadata {
|
||||
padding: 0 10px;
|
||||
|
||||
div.date-and-add-to-calendar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
span.icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
div.date-and-privacy {
|
||||
color: $primary;
|
||||
padding: 0.3rem;
|
||||
background: $secondary;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a.add-to-calendar {
|
||||
flex: 0 0 auto;
|
||||
margin-left: 10px;
|
||||
color: #484849;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.tags {
|
||||
span {
|
||||
&.tag {
|
||||
&::before {
|
||||
content: '#';
|
||||
}
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&.visibility::before {
|
||||
content: "⋅"
|
||||
}
|
||||
|
||||
|
||||
margin: auto 5px;
|
||||
}
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h3.title {
|
||||
font-size: 3rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding-top: 10px;
|
||||
min-height: 40rem;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 800px;
|
||||
background-position: 95% 101%;
|
||||
background-image: url('../../assets/texting.svg');
|
||||
border-top: solid 1px #111;
|
||||
border-bottom: solid 1px #111;
|
||||
|
||||
p {
|
||||
margin: 10px auto;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
padding: 0.3rem;
|
||||
background: $secondary;
|
||||
color: #111;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.share {
|
||||
border-bottom: solid 1px #111;
|
||||
|
||||
.columns {
|
||||
|
||||
& > * {
|
||||
padding: 10rem 0;
|
||||
}
|
||||
|
||||
.add-to-calendar {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 400px;
|
||||
background-position: 10% 50%;
|
||||
background-image: url('../../assets/undraw_events.svg');
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content:"";
|
||||
background: #B3B3B2;
|
||||
position: absolute;
|
||||
bottom: 25%;
|
||||
left: 0;
|
||||
height: 40%;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
|
||||
h3 {
|
||||
display: block;
|
||||
color: $primary;
|
||||
font-size: 3rem;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: $secondary;
|
||||
cursor: pointer;
|
||||
max-width: 20rem;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-events {
|
||||
margin: 50px auto;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,76 +1,73 @@
|
||||
<template>
|
||||
<section>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="card" v-if="group">
|
||||
<div class="card-image" v-if="group.bannerUrl">
|
||||
<figure class="image">
|
||||
<img :src="group.bannerUrl">
|
||||
<section class="container">
|
||||
<div v-if="group">
|
||||
<div class="card-image" v-if="group.bannerUrl">
|
||||
<figure class="image">
|
||||
<img :src="group.bannerUrl">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img :src="group.avatarUrl">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<img :src="group.avatarUrl">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title">{{ group.name }}</p>
|
||||
<p class="subtitle">@{{ group.preferredUsername }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p v-html="group.summary"></p>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<p class="title">{{ group.name }}</p>
|
||||
<p class="subtitle">@{{ group.preferredUsername }}</p>
|
||||
</div>
|
||||
<section v-if="group.organizedEvents.length > 0">
|
||||
<h2 class="subtitle">
|
||||
<translate>Organized</translate>
|
||||
</h2>
|
||||
<div class="columns">
|
||||
<EventCard
|
||||
v-for="event in group.organizedEvents"
|
||||
:event="event"
|
||||
:options="{ hideDetails: true }"
|
||||
:key="event.uuid"
|
||||
class="column is-one-third"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="group.members.length > 0">
|
||||
<h2 class="subtitle">
|
||||
<translate>Members</translate>
|
||||
</h2>
|
||||
<div class="columns">
|
||||
<span
|
||||
v-for="member in group.members"
|
||||
:key="member"
|
||||
>{{ member.actor.preferredUsername }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<b-message v-if-else="!group && $apollo.loading === false" type="is-danger">
|
||||
<translate>No group found</translate>
|
||||
</b-message>
|
||||
|
||||
<div class="content">
|
||||
<p v-html="group.summary"></p>
|
||||
</div>
|
||||
</div>
|
||||
<section class="box" v-if="group.organizedEvents.length > 0">
|
||||
<h2 class="subtitle">
|
||||
<translate>Organized</translate>
|
||||
</h2>
|
||||
<div class="columns">
|
||||
<EventCard
|
||||
v-for="event in group.organizedEvents"
|
||||
:event="event"
|
||||
:options="{ hideDetails: true }"
|
||||
:key="event.uuid"
|
||||
class="column is-one-third"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="group.members.length > 0">
|
||||
<h2 class="subtitle">
|
||||
<translate>Members</translate>
|
||||
</h2>
|
||||
<div class="columns">
|
||||
<span
|
||||
v-for="member in group.members"
|
||||
:key="member.actor.preferredUsername"
|
||||
>{{ member.actor.preferredUsername }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<b-message v-else-if="!group && $apollo.loading === false" type="is-danger">
|
||||
<translate>No group found</translate>
|
||||
</b-message>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||
import EventCard from '@/components/Event/EventCard.vue';
|
||||
import { FETCH_PERSON, LOGGED_PERSON } from '@/graphql/actor';
|
||||
import { FETCH_GROUP, LOGGED_PERSON } from '@/graphql/actor';
|
||||
import { IGroup } from '@/types/actor.model';
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
person: {
|
||||
query: FETCH_PERSON,
|
||||
group: {
|
||||
query: FETCH_GROUP,
|
||||
variables() {
|
||||
return {
|
||||
name: this.$route.params.name,
|
||||
name: this.$route.params.preferredUsername,
|
||||
};
|
||||
},
|
||||
},
|
||||
@ -83,9 +80,9 @@ import { FETCH_PERSON, LOGGED_PERSON } from '@/graphql/actor';
|
||||
},
|
||||
})
|
||||
export default class Group extends Vue {
|
||||
@Prop({ type: String, required: true }) name!: string;
|
||||
@Prop({ type: String, required: true }) preferredUsername!: string;
|
||||
|
||||
group = null;
|
||||
group!: IGroup;
|
||||
loading = true;
|
||||
|
||||
created() {
|
||||
@ -110,3 +107,8 @@ export default class Group extends Vue {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
section.container {
|
||||
min-height: 30em;
|
||||
}
|
||||
</style>
|
||||
|
@ -12,7 +12,7 @@
|
||||
class="column is-one-quarter-desktop is-half-mobile"
|
||||
/>
|
||||
</div>
|
||||
<router-link class="button" :to="{ name: 'CreateGroup' }">
|
||||
<router-link class="button" :to="{ name: RouteName.CREATE_GROUP }">
|
||||
<translate>Create group</translate>
|
||||
</router-link>
|
||||
</section>
|
||||
@ -27,6 +27,8 @@ export default class GroupList extends Vue {
|
||||
groups = [];
|
||||
loading = true;
|
||||
|
||||
RouteName = RouteName;
|
||||
|
||||
created() {
|
||||
this.fetchData();
|
||||
}
|
||||
|
@ -3,11 +3,14 @@
|
||||
<section class="hero is-link" v-if="!currentUser.id || !loggedPerson">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<h1 class="title">Find events you like</h1>
|
||||
<h2 class="subtitle">Share them with Mobilizon</h2>
|
||||
<router-link class="button" :to="{ name: 'Register' }">
|
||||
<h1 class="title">{{ config.name }}</h1>
|
||||
<h2 class="subtitle">{{ config.description }}</h2>
|
||||
<router-link class="button" :to="{ name: 'Register' }" v-if="config.registrationsOpen">
|
||||
<translate>Register</translate>
|
||||
</router-link>
|
||||
<p v-else>
|
||||
<translate>This instance isn't opened to registrations, but you can register on other instances.</translate>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -18,8 +21,8 @@
|
||||
>Welcome back %{username}</translate>
|
||||
</h1>
|
||||
</section>
|
||||
<section v-if="loggedPerson">
|
||||
<span class="events-nearby title">Events you're going at</span>
|
||||
<section v-if="loggedPerson" class="container">
|
||||
<span class="events-nearby title"><translate>Events you're going at</translate></span>
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<div v-if="goingToEvents.size > 0" v-for="row in Array.from(goingToEvents.entries())">
|
||||
<!-- Iterators will be supported in v-for with VueJS 3 -->
|
||||
@ -62,16 +65,15 @@
|
||||
<translate>You're not going to any event yet</translate>
|
||||
</b-message>
|
||||
</section>
|
||||
<section>
|
||||
<span class="events-nearby title">Events nearby you</span>
|
||||
<section class="container">
|
||||
<h3 class="events-nearby title"><translate>Events nearby you</translate></h3>
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<div v-if="events.length > 0" class="columns is-multiline">
|
||||
<EventCard
|
||||
v-for="event in events"
|
||||
:key="event.uuid"
|
||||
:event="event"
|
||||
class="column is-one-quarter-desktop is-half-mobile"
|
||||
/>
|
||||
<div class="column is-one-third-desktop" v-for="event in events.slice(0, 6)" :key="event.uuid">
|
||||
<EventCard
|
||||
:event="event"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<b-message v-else type="is-danger">
|
||||
<translate>No events found</translate>
|
||||
@ -91,7 +93,9 @@ import { ICurrentUser } from '@/types/current-user.model';
|
||||
import { CURRENT_USER_CLIENT } from '@/graphql/user';
|
||||
import { RouteName } from '@/router';
|
||||
import { IEvent } from '@/types/event.model';
|
||||
import DateComponent from '@/components/Event/Date.vue';
|
||||
import DateComponent from '@/components/Event/DateCalendarIcon.vue';
|
||||
import { CONFIG } from '@/graphql/config';
|
||||
import { IConfig } from '@/types/config.model';
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
@ -105,6 +109,9 @@ import DateComponent from '@/components/Event/Date.vue';
|
||||
currentUser: {
|
||||
query: CURRENT_USER_CLIENT,
|
||||
},
|
||||
config: {
|
||||
query: CONFIG,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
DateComponent,
|
||||
@ -112,18 +119,19 @@ import DateComponent from '@/components/Event/Date.vue';
|
||||
},
|
||||
})
|
||||
export default class Home extends Vue {
|
||||
events = [];
|
||||
events: Event[] = [];
|
||||
locations = [];
|
||||
city = { name: null };
|
||||
country = { name: null };
|
||||
loggedPerson: IPerson = new Person();
|
||||
currentUser!: ICurrentUser;
|
||||
config: IConfig = { description: '', name: '', registrationsOpen: false };
|
||||
|
||||
get displayed_name() {
|
||||
return this.loggedPerson.name === null
|
||||
? this.loggedPerson.preferredUsername
|
||||
: this.loggedPerson.name;
|
||||
}
|
||||
// get displayed_name() {
|
||||
// return this.loggedPerson && this.loggedPerson.name === null
|
||||
// ? this.loggedPerson.preferredUsername
|
||||
// : this.loggedPerson.name;
|
||||
// }
|
||||
|
||||
isToday(date: string) {
|
||||
return (new Date(date)).toDateString() === (new Date()).toDateString();
|
||||
@ -153,19 +161,18 @@ export default class Home extends Vue {
|
||||
|
||||
get goingToEvents(): Map<string, IEvent[]> {
|
||||
const res = this.$data.loggedPerson.goingToEvents.filter((event) => {
|
||||
return event.beginsOn != null && this.isBefore(event.beginsOn, 0)
|
||||
return event.beginsOn != null && this.isBefore(event.beginsOn, 0);
|
||||
});
|
||||
res.sort(
|
||||
(a: IEvent, b: IEvent) => new Date(a.beginsOn) > new Date(b.beginsOn),
|
||||
);
|
||||
const groups = res.reduce((acc: Map<string, IEvent[]>, event: IEvent) => {
|
||||
return res.reduce((acc: Map<string, IEvent[]>, event: IEvent) => {
|
||||
const day = (new Date(event.beginsOn)).toDateString();
|
||||
const events: IEvent[] = acc.get(day) || [];
|
||||
events.push(event);
|
||||
acc.set(day, events);
|
||||
return acc;
|
||||
}, new Map());
|
||||
return groups;
|
||||
}, new Map());
|
||||
}
|
||||
|
||||
geoLocalize() {
|
||||
@ -210,9 +217,9 @@ export default class Home extends Vue {
|
||||
this.$router.push({ name: RouteName.EVENT, params: { uuid: event.uuid } });
|
||||
}
|
||||
|
||||
ipLocation() {
|
||||
return this.city.name ? this.city.name : this.country.name;
|
||||
}
|
||||
// ipLocation() {
|
||||
// return this.city.name ? this.city.name : this.country.name;
|
||||
// }
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,8 +1,63 @@
|
||||
<template>
|
||||
<section>
|
||||
<h1>
|
||||
<translate>Page not found!</translate>
|
||||
<img src="../assets/oh_no.jpg">
|
||||
</h1>
|
||||
<section class="container has-text-centered not-found">
|
||||
<div class="columns is-vertical">
|
||||
<div class="column is-centered">
|
||||
<img src="../assets/oh_no.jpg" alt="Not found 'oh no' picture">
|
||||
<h1 class="title">
|
||||
<translate>The page you're looking for doesn't exist.</translate>
|
||||
</h1>
|
||||
<p>
|
||||
<translate>Please make sure the address is correct and that the page hasn't been moved.</translate>
|
||||
</p>
|
||||
<p>
|
||||
<translate>Please contact this instance's Mobilizon admin if you think this is a mistake.</translate>
|
||||
</p>
|
||||
<!-- The following should just be replaced with the SearchField component but it fails for some reason -->
|
||||
<form @submit="enter">
|
||||
<b-field class="search">
|
||||
<b-input expanded icon="magnify" type="search" :placeholder="searchPlaceHolder" v-model="searchText" />
|
||||
<p class="control">
|
||||
<button type="submit" class="button is-primary"><translate>Search</translate></button>
|
||||
</p>
|
||||
</b-field>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { RouteName } from '@/router';
|
||||
import BField from 'buefy/src/components/field/Field.vue';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
BField,
|
||||
},
|
||||
})
|
||||
export default class PageNotFound extends Vue {
|
||||
searchText: string = '';
|
||||
|
||||
get searchPlaceHolder(): string {
|
||||
return this.$gettext('Search events, groups, etc.');
|
||||
}
|
||||
|
||||
enter() {
|
||||
this.$router.push({ name: RouteName.SEARCH, params: { searchTerm: this.searchText } });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.container.not-found {
|
||||
margin: auto;
|
||||
max-width: 600px;
|
||||
|
||||
img {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
140
js/src/views/Search.vue
Normal file
140
js/src/views/Search.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<section class="container">
|
||||
<h1>
|
||||
<translate :translate-params="{ search: this.searchTerm }">Search results: « %{ search } »</translate>
|
||||
</h1>
|
||||
<b-loading :active.sync="$apollo.loading" />
|
||||
<b-tabs v-model="activeTab" type="is-boxed" class="searchTabs" @change="changeTab">
|
||||
<b-tab-item>
|
||||
<template slot="header">
|
||||
<b-icon icon="calendar"></b-icon>
|
||||
<span><translate>Events</translate> <b-tag rounded>{{ events.length }}</b-tag> </span>
|
||||
</template>
|
||||
<div v-if="search.length > 0" class="columns is-multiline">
|
||||
<div class="column is-one-quarter-desktop is-half-mobile"
|
||||
v-for="event in events"
|
||||
:key="event.uuid">
|
||||
<EventCard
|
||||
:event="event"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<b-message v-else-if="$apollo.loading === false" type="is-danger">
|
||||
<translate>No events found</translate>
|
||||
</b-message>
|
||||
</b-tab-item>
|
||||
<b-tab-item>
|
||||
<template slot="header">
|
||||
<b-icon icon="account-multiple"></b-icon>
|
||||
<span><translate>Groups</translate> <b-tag rounded>{{ groups.length }}</b-tag> </span>
|
||||
</template>
|
||||
<div v-if="groups.length > 0" class="columns is-multiline">
|
||||
<div class="column is-one-quarter-desktop is-half-mobile"
|
||||
v-for="group in groups"
|
||||
:key="group.uuid">
|
||||
<group-card :group="group" />
|
||||
</div>
|
||||
</div>
|
||||
<b-message v-else-if="$apollo.loading === false" type="is-danger">
|
||||
<translate>No groups found</translate>
|
||||
</b-message>
|
||||
</b-tab-item>
|
||||
</b-tabs>
|
||||
</section>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||
import { SEARCH } from '@/graphql/search';
|
||||
import { RouteName } from '@/router';
|
||||
import { IEvent } from '@/types/event.model';
|
||||
import { ISearch } from '@/types/search.model';
|
||||
import EventCard from '@/components/Event/EventCard.vue';
|
||||
import { IGroup, Group } from '@/types/actor.model';
|
||||
import GroupCard from '@/components/Group/GroupCard.vue';
|
||||
|
||||
enum SearchTabs {
|
||||
EVENTS = 0,
|
||||
GROUPS = 1,
|
||||
PERSONS = 2, // not used right now
|
||||
}
|
||||
|
||||
const tabsName = {
|
||||
events: SearchTabs.EVENTS,
|
||||
groups: SearchTabs.GROUPS,
|
||||
};
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
search: {
|
||||
query: SEARCH,
|
||||
variables() {
|
||||
return {
|
||||
searchText: this.searchTerm,
|
||||
};
|
||||
},
|
||||
skip() {
|
||||
return !this.searchTerm;
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
GroupCard,
|
||||
EventCard,
|
||||
},
|
||||
})
|
||||
export default class Search extends Vue {
|
||||
@Prop({ type: String, required: true }) searchTerm!: string;
|
||||
@Prop({ type: String, required: false, default: 'events' }) searchType!: string;
|
||||
|
||||
search = [];
|
||||
activeTab: SearchTabs = tabsName[this.searchType];
|
||||
|
||||
changeTab(index: number) {
|
||||
switch (index) {
|
||||
case SearchTabs.EVENTS:
|
||||
this.$router.push({ name: RouteName.SEARCH, params: { searchTerm: this.searchTerm, searchType: 'events' } });
|
||||
break;
|
||||
case SearchTabs.GROUPS:
|
||||
this.$router.push({ name: RouteName.SEARCH, params: { searchTerm: this.searchTerm, searchType: 'groups' } });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('search')
|
||||
changeTabForResult() {
|
||||
if (this.events.length === 0 && this.groups.length > 0) {
|
||||
this.activeTab = SearchTabs.GROUPS;
|
||||
}
|
||||
if (this.groups.length === 0 && this.events.length > 0) {
|
||||
this.activeTab = SearchTabs.EVENTS;
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('search')
|
||||
@Watch('$route')
|
||||
async loadSearch() {
|
||||
await this.$apollo.queries['search'].refetch();
|
||||
}
|
||||
|
||||
get events(): IEvent[] {
|
||||
return this.search.filter((value: ISearch) => { return value.__typename === 'Event'; }) as IEvent[];
|
||||
}
|
||||
|
||||
get groups(): IGroup[] {
|
||||
const groups = this.search.filter((value: ISearch) => { return value.__typename === 'Group'; }) as IGroup[];
|
||||
return groups.map(group => Object.assign(new Group(), group));
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import "~bulma/sass/utilities/_all";
|
||||
@import "~bulma/sass/components/tabs";
|
||||
@import "~buefy/src/scss/components/tabs";
|
||||
@import "~bulma/sass/elements/tag";
|
||||
|
||||
.searchTabs .tab-content {
|
||||
background: #fff;
|
||||
min-height: 10em;
|
||||
}
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<section class="hero">
|
||||
<h1 class="title">
|
||||
<translate>Welcome back!</translate>
|
||||
@ -12,14 +12,14 @@
|
||||
|
||||
<section v-if="!currentUser.isLoggedIn">
|
||||
<div class="columns is-mobile is-centered">
|
||||
<div class="column is-half card">
|
||||
<div class="column is-half">
|
||||
<b-message title="Error" type="is-danger" v-for="error in errors" :key="error">{{ error }}</b-message>
|
||||
<form @submit="loginAction">
|
||||
<b-field label="Email">
|
||||
<b-field :label="$gettext('Email')">
|
||||
<b-input aria-required="true" required type="email" v-model="credentials.email"/>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Password">
|
||||
<b-field :label="$gettext('Password')">
|
||||
<b-input
|
||||
aria-required="true"
|
||||
required
|
||||
@ -70,20 +70,20 @@ import { ILogin } from '@/types/login.model';
|
||||
import { CURRENT_USER_CLIENT, UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user';
|
||||
import { onLogin } from '@/vue-apollo';
|
||||
import { RouteName } from '@/router';
|
||||
import { LoginErrorCode } from '@/types/login-error-code.model'
|
||||
import { ICurrentUser } from '@/types/current-user.model'
|
||||
import { CONFIG } from '@/graphql/config'
|
||||
import { IConfig } from '@/types/config.model'
|
||||
import { LoginErrorCode } from '@/types/login-error-code.model';
|
||||
import { ICurrentUser } from '@/types/current-user.model';
|
||||
import { CONFIG } from '@/graphql/config';
|
||||
import { IConfig } from '@/types/config.model';
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
config: {
|
||||
query: CONFIG
|
||||
query: CONFIG,
|
||||
},
|
||||
currentUser: {
|
||||
query: CURRENT_USER_CLIENT
|
||||
}
|
||||
}
|
||||
query: CURRENT_USER_CLIENT,
|
||||
},
|
||||
},
|
||||
})
|
||||
export default class Login extends Vue {
|
||||
@Prop({ type: String, required: false, default: '' }) email!: string;
|
||||
@ -113,9 +113,9 @@ export default class Login extends Vue {
|
||||
this.credentials.email = this.email;
|
||||
this.credentials.password = this.password;
|
||||
|
||||
let query = this.$route.query;
|
||||
this.errorCode = query[ 'code' ] as LoginErrorCode;
|
||||
this.redirect = query[ 'redirect' ] as string;
|
||||
const query = this.$route.query;
|
||||
this.errorCode = query['code'] as LoginErrorCode;
|
||||
this.redirect = query['redirect'] as string;
|
||||
}
|
||||
|
||||
async loginAction(e: Event) {
|
||||
@ -146,7 +146,7 @@ export default class Login extends Vue {
|
||||
onLogin(this.$apollo);
|
||||
|
||||
if (this.redirect) {
|
||||
this.$router.push(this.redirect)
|
||||
this.$router.push(this.redirect);
|
||||
} else {
|
||||
this.$router.push({ name: RouteName.HOME });
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
<div class="columns is-mobile">
|
||||
<div class="column">
|
||||
<div class="content">
|
||||
<h2 class="subtitle" v-translate>Features</h2>
|
||||
<h3 class="title" v-translate>Features</h3>
|
||||
<ul>
|
||||
<li v-translate>Create your communities and your events</li>
|
||||
<li v-translate>Other stuff…</li>
|
||||
@ -24,7 +24,7 @@
|
||||
</p>
|
||||
<hr>
|
||||
<div class="content">
|
||||
<h2 class="subtitle" v-translate>About this instance</h2>
|
||||
<h3 class="title" v-translate>About this instance</h3>
|
||||
<p>
|
||||
<translate>Your local administrator resumed it's policy:</translate>
|
||||
</p>
|
||||
@ -96,9 +96,7 @@
|
||||
</form>
|
||||
|
||||
<div v-if="errors.length > 0">
|
||||
<b-message type="is-danger" v-for="error in errors" :key="error">
|
||||
<translate>{{ error }}</translate>
|
||||
</b-message>
|
||||
<b-message type="is-danger" v-for="error in errors" :key="error">{{ error }}</b-message>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -154,6 +152,8 @@ export default class Register extends Vue {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../variables";
|
||||
|
||||
.avatar-enter-active {
|
||||
transition: opacity 1s ease;
|
||||
}
|
||||
@ -166,4 +166,9 @@ export default class Register extends Vue {
|
||||
.avatar-leave {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h3.title {
|
||||
background: $secondary;
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<section class="columns">
|
||||
<div class="column card">
|
||||
<section class="container">
|
||||
<div class="column">
|
||||
<h1 class="title">
|
||||
<translate>Resend confirmation email</translate>
|
||||
</h1>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<section class="columns">
|
||||
<div class="card column">
|
||||
<section class="container">
|
||||
<div class="column">
|
||||
<h1 class="title">
|
||||
<translate>Password reset</translate>
|
||||
</h1>
|
||||
|
@ -27,9 +27,18 @@ const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
name: 'SearchResult',
|
||||
possibleTypes: [
|
||||
{ name: 'Event' },
|
||||
{ name: 'Actor' },
|
||||
{ name: 'Person' },
|
||||
{ name: 'Group' },
|
||||
],
|
||||
}, // this is an example, put your INTERFACE and UNION kinds here!
|
||||
},
|
||||
{
|
||||
kind: 'INTERFACE',
|
||||
name: 'Actor',
|
||||
possibleTypes: [
|
||||
{ name: 'Person' },
|
||||
{ name: 'Group' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
10827
js/yarn.lock
Normal file
10827
js/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,11 @@ defmodule Mobilizon.CommonConfig do
|
||||
|> get_in([:name])
|
||||
end
|
||||
|
||||
def instance_description() do
|
||||
instance_config()
|
||||
|> get_in([:description])
|
||||
end
|
||||
|
||||
defp instance_config(), do: Application.get_env(:mobilizon, :instance)
|
||||
|
||||
defp to_bool(v), do: v == true or v == "true" or v == "True"
|
||||
|
@ -9,6 +9,11 @@ defmodule MobilizonWeb.Resolvers.Config do
|
||||
Get config
|
||||
"""
|
||||
def get_config(_parent, _params, _context) do
|
||||
{:ok, %{name: instance_name(), registrations_open: registrations_open?()}}
|
||||
{:ok,
|
||||
%{
|
||||
name: instance_name(),
|
||||
registrations_open: registrations_open?(),
|
||||
description: instance_description()
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
@ -141,7 +141,7 @@ defmodule MobilizonWeb.Resolvers.Group do
|
||||
actor_id: actor.id,
|
||||
role: role
|
||||
}) do
|
||||
{:ok, %{parent: group, person: actor, role: role}}
|
||||
{:ok, %{parent: group, actor: actor, role: role}}
|
||||
else
|
||||
{:is_owned, false} ->
|
||||
{:error, "Actor id is not owned by authenticated user"}
|
||||
@ -185,7 +185,7 @@ defmodule MobilizonWeb.Resolvers.Group do
|
||||
parent: %{
|
||||
id: group_id
|
||||
},
|
||||
person: %{
|
||||
actor: %{
|
||||
id: actor_id
|
||||
}
|
||||
}
|
||||
|
15
lib/mobilizon_web/resolvers/member.ex
Normal file
15
lib/mobilizon_web/resolvers/member.ex
Normal file
@ -0,0 +1,15 @@
|
||||
defmodule MobilizonWeb.Resolvers.Member do
|
||||
@moduledoc """
|
||||
Handles the member-related GraphQL calls
|
||||
"""
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.Actors.{Actor}
|
||||
|
||||
@doc """
|
||||
Find members for group
|
||||
"""
|
||||
def find_members_for_group(%Actor{} = actor, _args, _resolution) do
|
||||
members = Actors.memberships_for_group(actor)
|
||||
{:ok, members}
|
||||
end
|
||||
end
|
@ -31,6 +31,7 @@ defmodule MobilizonWeb.Router do
|
||||
end
|
||||
|
||||
pipeline :browser do
|
||||
plug(Plug.Static, at: "/", from: "priv/static")
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
plug(:fetch_flash)
|
||||
|
@ -6,6 +6,7 @@ defmodule MobilizonWeb.Schema.Actors.GroupType do
|
||||
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
|
||||
import_types(MobilizonWeb.Schema.Actors.MemberType)
|
||||
alias MobilizonWeb.Resolvers
|
||||
alias Mobilizon.Events
|
||||
|
||||
@desc """
|
||||
Represents a group of actors
|
||||
@ -49,7 +50,10 @@ defmodule MobilizonWeb.Schema.Actors.GroupType do
|
||||
description: "Whether the group is opened to all or has restricted access"
|
||||
)
|
||||
|
||||
field(:members, non_null(list_of(:member)), description: "List of group members")
|
||||
field(:members, non_null(list_of(:member)),
|
||||
resolve: &Resolvers.Member.find_members_for_group/3,
|
||||
description: "List of group members"
|
||||
)
|
||||
end
|
||||
|
||||
@desc """
|
||||
|
@ -11,14 +11,14 @@ defmodule MobilizonWeb.Schema.Actors.MemberType do
|
||||
"""
|
||||
object :member do
|
||||
field(:parent, :group, description: "Of which the profile is member")
|
||||
field(:person, :person, description: "Which profile is member of")
|
||||
field(:actor, :person, description: "Which profile is member of")
|
||||
field(:role, :integer, description: "The role of this membership")
|
||||
end
|
||||
|
||||
@desc "Represents a deleted member"
|
||||
object :deleted_member do
|
||||
field(:parent, :deleted_object)
|
||||
field(:person, :deleted_object)
|
||||
field(:actor, :deleted_object)
|
||||
end
|
||||
|
||||
object :member_mutations do
|
||||
|
@ -10,6 +10,7 @@ defmodule MobilizonWeb.Schema.ConfigType do
|
||||
object :config do
|
||||
# Instance name
|
||||
field(:name, :string)
|
||||
field(:description, :string)
|
||||
|
||||
field(:registrations_open, :boolean)
|
||||
end
|
||||
|
@ -18,7 +18,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||
@spec export_public_event(Event.t()) :: {:ok, String.t()}
|
||||
def export_public_event(%Event{visibility: visibility} = event)
|
||||
when visibility in [:public, :unlisted] do
|
||||
{:ok, %ICalendar{events: [do_export_event(event)]} |> ICalendar.to_ics()}
|
||||
{:ok, %ICalendar{events: [do_export_event(event)]} |> ICalendar.to_ics(vendor: "Mobilizon")}
|
||||
end
|
||||
|
||||
@spec export_public_event(Event.t()) :: {:error, :event_not_public}
|
||||
@ -29,6 +29,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||
%ICalendar.Event{
|
||||
summary: event.title,
|
||||
dtstart: event.begins_on,
|
||||
dtstamp: event.publish_at || DateTime.utc_now(),
|
||||
dtend: event.ends_on,
|
||||
description: event.description,
|
||||
uid: event.uuid,
|
||||
|
2
mix.exs
2
mix.exs
@ -67,7 +67,7 @@ defmodule Mobilizon.Mixfile do
|
||||
{:geo, "~> 3.0"},
|
||||
{:geo_postgis, "~> 3.1"},
|
||||
{:timex, "~> 3.0"},
|
||||
{:icalendar, "~> 0.7"},
|
||||
{:icalendar, github: "tcitworld/icalendar"},
|
||||
{:exgravatar, "~> 2.0.1"},
|
||||
{:httpoison, "~> 1.0"},
|
||||
{:json_ld, "~> 0.3"},
|
||||
|
2
mix.lock
2
mix.lock
@ -56,7 +56,7 @@
|
||||
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"http_sign": {:hex, :http_sign, "0.1.1", "b16edb83aa282892f3271f9a048c155e772bf36e15700ab93901484c55f8dd10", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"httpoison": {:hex, :httpoison, "1.5.0", "71ae9f304bdf7f00e9cd1823f275c955bdfc68282bc5eb5c85c3a9ade865d68e", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"icalendar": {:hex, :icalendar, "0.7.1", "323c67afc82b9dc373778c8df13d786abab28585833a0656406a858ecb539b58", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"icalendar": {:git, "https://github.com/tcitworld/icalendar.git", "bd08e872c125f70a87c3ac7d87ea2f22a5577059", []},
|
||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -38,8 +38,8 @@ once the server is running:
|
||||
|
||||
* Install the NodeJS (we guarantee support for the latest LTS and later) ![](https://img.shields.io/badge/node-%3E%3D%2010.0+-brightgreen.svg)
|
||||
* Change directory to `js/` and do:
|
||||
* Install JavaScript package dependencies: `npm install`.
|
||||
* Run the developement server in watch mode: `npm run dev`. This will open a
|
||||
* Install JavaScript package dependencies: `yarn install`.
|
||||
* Run the developement server in watch mode: `yarn run dev`. This will open a
|
||||
browser on [`localhost:8080`](http://localhost:8080) that gets
|
||||
automatically reloaded on change.
|
||||
|
||||
|
@ -13,9 +13,9 @@ These two commands must not return an error code, since they are required to pas
|
||||
# Front
|
||||
|
||||
We use `tslint` with the `tslint-config-airbnb` preset.
|
||||
Errors should be reported when running in dev mode `npm run dev` or when building a production bundle `npm run build`.
|
||||
Errors should be reported when running in dev mode `yarn run dev` or when building a production bundle `yarn run build`.
|
||||
|
||||
Please run the following command before pushing code `npm run lint`.
|
||||
Please run the following command before pushing code `yanr run lint`.
|
||||
|
||||
This command must not return an error code, since it's required to pass inside CI.
|
||||
|
||||
|
@ -18,6 +18,7 @@ sudo apt-get install curl sudo unzip vim
|
||||
[https://certbot.eff.org/all-instructions](https://certbot.eff.org/all-instructions)
|
||||
4. Install NodeJS 10.x (current LTS):
|
||||
[https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions](https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions)
|
||||
5. Install yarn, and be sure to have [a recent version](https://github.com/yarnpkg/yarn/releases/latest): [yarnpkg.com/en/docs/install#linux-tab](https://yarnpkg.com/en/docs/install#linux-tab)
|
||||
5. Install Erlang and Elixir:
|
||||
[https://elixir-lang.org/install.html#unix-and-unix-like](https://elixir-lang.org/install.html#unix-and-unix-like)
|
||||
6. Install PostGIS:
|
||||
@ -39,7 +40,7 @@ sudo systemctl start postgresql
|
||||
1. Run:
|
||||
|
||||
```
|
||||
sudo pacman -S nodejs postgresql openssl git wget unzip base-devel npm nginx elixir postgis
|
||||
sudo pacman -S nodejs postgresql openssl git wget unzip base-devel yarn nginx elixir postgis
|
||||
```
|
||||
|
||||
Now that dependencies are installed, before running MobiliZon you should start PostgreSQL and Redis:
|
||||
|
@ -77,12 +77,12 @@ cd js/
|
||||
and install the Javascript dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
yarn install
|
||||
```
|
||||
|
||||
Finally, build the front-end with
|
||||
```bash
|
||||
npm run build
|
||||
yarn run build
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
@ -23,7 +23,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
group_id: #{group.id}
|
||||
) {
|
||||
role,
|
||||
person {
|
||||
actor {
|
||||
id
|
||||
},
|
||||
parent {
|
||||
@ -41,7 +41,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
assert json_response(res, 200)["errors"] == nil
|
||||
assert json_response(res, 200)["data"]["joinGroup"]["role"] == "not_approved"
|
||||
assert json_response(res, 200)["data"]["joinGroup"]["parent"]["id"] == group.id
|
||||
assert json_response(res, 200)["data"]["joinGroup"]["person"]["id"] == actor.id
|
||||
assert json_response(res, 200)["data"]["joinGroup"]["actor"]["id"] == actor.id
|
||||
|
||||
mutation = """
|
||||
mutation {
|
||||
@ -151,7 +151,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
actor_id: #{actor.id},
|
||||
group_id: #{group.id}
|
||||
) {
|
||||
person {
|
||||
actor {
|
||||
id
|
||||
},
|
||||
parent {
|
||||
@ -168,7 +168,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
|
||||
assert json_response(res, 200)["errors"] == nil
|
||||
assert json_response(res, 200)["data"]["leaveGroup"]["parent"]["id"] == group.id
|
||||
assert json_response(res, 200)["data"]["leaveGroup"]["person"]["id"] == actor.id
|
||||
assert json_response(res, 200)["data"]["leaveGroup"]["actor"]["id"] == actor.id
|
||||
end
|
||||
|
||||
test "leave_group/3 should check if the member is the only administrator", %{
|
||||
@ -186,7 +186,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
actor_id: #{actor.id},
|
||||
group_id: #{group.id}
|
||||
) {
|
||||
person {
|
||||
actor {
|
||||
id
|
||||
},
|
||||
parent {
|
||||
@ -214,7 +214,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
actor_id: #{actor.id},
|
||||
group_id: #{group.id}
|
||||
) {
|
||||
person {
|
||||
actor {
|
||||
id
|
||||
}
|
||||
}
|
||||
@ -242,7 +242,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
actor_id: 1042,
|
||||
group_id: #{group.id}
|
||||
) {
|
||||
person {
|
||||
actor {
|
||||
id
|
||||
}
|
||||
}
|
||||
@ -271,7 +271,7 @@ defmodule MobilizonWeb.Resolvers.MemberResolverTest do
|
||||
actor_id: #{actor.id},
|
||||
group_id: 1042
|
||||
) {
|
||||
person {
|
||||
actor {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user