diff --git a/config/dev.exs b/config/dev.exs
index ae2ab7e5e..e3b746515 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -12,7 +12,7 @@ config :mobilizon, MobilizonWeb.Endpoint,
],
url: [
host: System.get_env("MOBILIZON_INSTANCE_HOST") || "mobilizon.local",
- port: 80,
+ port: System.get_env("MOBILIZON_INSTANCE_PORT") || 4000,
scheme: "http"
],
debug_errors: true,
diff --git a/js/Makefile b/js/Makefile
deleted file mode 100644
index c55170452..000000000
--- a/js/Makefile
+++ /dev/null
@@ -1,58 +0,0 @@
-# On OSX the PATH variable isn't exported unless "SHELL" is also set, see: http://stackoverflow.com/a/25506676
-SHELL = /bin/bash
-NODE_BINDIR = ./node_modules/.bin
-export PATH := $(NODE_BINDIR):$(PATH)
-
-# Where to find input files (it can be multiple paths).
-INPUT_FILES = ./src
-
-# Where to write the files generated by this makefile.
-OUTPUT_DIR = ./src/i18n
-
-# Available locales for the app.
-LOCALES = en_US fr_FR
-
-# Name of the generated .po files for each available locale.
-LOCALE_FILES ?= $(patsubst %,$(OUTPUT_DIR)/locale/%/LC_MESSAGES/app.po,$(LOCALES))
-
-GETTEXT_HTML_SOURCES = $(shell find $(INPUT_FILES) -name '*.vue' -o -name '*.html' 2> /dev/null)
-GETTEXT_JS_SOURCES = $(shell find $(INPUT_FILES) -name '*.vue' -o -name '*.js')
-
-# Makefile Targets
-.PHONY: clean makemessages translations
-
-clean:
- rm -f /tmp/template.pot $(OUTPUT_DIR)/translations.json
-
-makemessages: /tmp/template.pot
-
-translations: ./$(OUTPUT_DIR)/translations.json
-
-# Create a main .pot template, then generate .po files for each available language.
-# Thanx to Systematic: https://github.com/Polyconseil/systematic/blob/866d5a/mk/main.mk#L167-L183
-/tmp/template.pot: $(GETTEXT_HTML_SOURCES)
-# `dir` is a Makefile built-in expansion function which extracts the directory-part of `$@`.
-# `$@` is a Makefile automatic variable: the file name of the target of the rule.
-# => `mkdir -p /tmp/`
- mkdir -p $(dir $@)
- which gettext-extract
-# Extract gettext strings from templates files and create a POT dictionary template.
- gettext-extract --attribute v-translate --quiet --parseScript false --output $@ $(GETTEXT_HTML_SOURCES)
-# Extract gettext strings from JavaScript files.
- xgettext --language=JavaScript --keyword=npgettext:1c,2,3 \
- --from-code=utf-8 --join-existing --no-wrap \
- --package-name=$(shell node -e "console.log(require('./package.json').name);") \
- --package-version=$(shell node -e "console.log(require('./package.json').version);") \
- --output $@ $(GETTEXT_JS_SOURCES)
-# Generate .po files for each available language.
- @for lang in $(LOCALES); do \
- export PO_FILE=$(OUTPUT_DIR)/locale/$$lang/LC_MESSAGES/app.po; \
- echo "msgmerge --update $$PO_FILE $@"; \
- mkdir -p $$(dirname $$PO_FILE); \
- [ -f $$PO_FILE ] && msgmerge --lang=$$lang --update $$PO_FILE $@ || msginit --no-translator --locale=$$lang --input=$@ --output-file=$$PO_FILE; \
- msgattrib --no-wrap --no-obsolete -o $$PO_FILE $$PO_FILE; \
- done;
-
-$(OUTPUT_DIR)/translations.json: clean /tmp/template.pot
- mkdir -p $(OUTPUT_DIR)
- gettext-compile --output $@ $(LOCALE_FILES)
diff --git a/js/package.json b/js/package.json
index 4eb804f7f..26e3fdbcc 100644
--- a/js/package.json
+++ b/js/package.json
@@ -9,7 +9,7 @@
"dev": "vue-cli-service build --watch",
"test:e2e": "vue-cli-service test:e2e",
"test:unit": "vue-cli-service test:unit",
- "prepare": "patch-package"
+ "vue-i18n-extract": "vue-i18n-extract"
},
"dependencies": {
"apollo-absinthe-upload-link": "^1.5.0",
@@ -18,7 +18,6 @@
"apollo-link": "^1.2.11",
"apollo-link-http": "^1.5.14",
"buefy": "^0.8.2",
- "easygettext": "^2.7.0",
"graphql": "^14.2.1",
"graphql-tag": "^2.10.1",
"leaflet": "^1.4.0",
@@ -33,7 +32,7 @@
"vue": "^2.6.10",
"vue-apollo": "^3.0.0-rc.1",
"vue-class-component": "^7.0.2",
- "vue-gettext": "^2.1.3",
+ "vue-i18n": "^8.14.0",
"vue-property-decorator": "^8.1.0",
"vue-router": "^3.0.6",
"vue2-leaflet": "^2.0.3",
@@ -58,12 +57,12 @@
"eslint": "^6.0.1",
"graphql-cli": "^3.0.12",
"node-sass": "^4.11.0",
- "patch-package": "^6.1.2",
"sass-loader": "^8.0.0",
"tslint": "^5.16.0",
"tslint-config-airbnb": "^5.11.1",
"typescript": "^3.4.3",
"vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0",
+ "vue-i18n-extract": "^1.0.2",
"vue-svg-inline-loader": "^1.2.15",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.30.0"
diff --git a/js/patches/easygettext+2.7.0.patch b/js/patches/easygettext+2.7.0.patch
deleted file mode 100644
index bdb2ce213..000000000
--- a/js/patches/easygettext+2.7.0.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-patch-package
---- a/node_modules/easygettext/src/extract-cli.js
-+++ b/node_modules/easygettext/src/extract-cli.js
-@@ -22,9 +22,12 @@ const endDelimiter = argv.endDelimiter === undefined ? constants.DEFAULT_DELIMIT
- const extraAttribute = argv.attribute || false;
- const extraFilter = argv.filter || false;
- const filterPrefix = argv.filterPrefix || constants.DEFAULT_FILTER_PREFIX;
-+const parseScript = argv.parseScript === undefined ? true : argv.parseScript === 'true';
-
- if (!quietMode && (!files || files.length === 0)) {
-- console.log('Usage:\n\tgettext-extract [--attribute EXTRA-ATTRIBUTE] [--filterPrefix FILTER-PREFIX] [--output OUTFILE] ');
-+ console.log(
-+ 'Usage:\n\tgettext-extract [--attribute EXTRA-ATTRIBUTE] [--filterPrefix FILTER-PREFIX] [--parseScript BOOLEAN] [--output OUTFILE] ',
-+ );
- process.exit(1);
- }
-
-@@ -54,7 +57,7 @@ const extractor = new extract.Extractor({
- });
-
-
--files.forEach(function(filename) {
-+files.forEach(function (filename) {
- let file = filename;
- const ext = file.split('.').pop();
- if (ALLOWED_EXTENSIONS.indexOf(ext) === -1) {
-@@ -63,9 +66,13 @@ files.forEach(function(filename) {
- }
- console.log(`[${PROGRAM_NAME}] extracting: '${filename}`);
- try {
-- let data = fs.readFileSync(file, {encoding: 'utf-8'}).toString();
-+ let data = fs.readFileSync(file, { encoding: 'utf-8' }).toString();
- extractor.parse(file, extract.preprocessTemplate(data, ext));
-
-+ if (!parseScript) {
-+ return;
-+ }
-+
- if (ext !== 'js') {
- data = extract.preprocessScriptTags(data, ext);
- }
diff --git a/js/src/App.vue b/js/src/App.vue
index 73839d96e..c9fbf8be8 100644
--- a/js/src/App.vue
+++ b/js/src/App.vue
@@ -11,11 +11,20 @@
@@ -74,6 +98,8 @@ export default class App extends Vue {
@import "~bulma/sass/components/navbar.sass";
@import "~bulma/sass/components/pagination.sass";
@import "~bulma/sass/components/dropdown.sass";
+@import "~bulma/sass/components/breadcrumb.sass";
+@import "~bulma/sass/components/list.sass";
@import "~bulma/sass/elements/box.sass";
@import "~bulma/sass/elements/button.sass";
@import "~bulma/sass/elements/container.sass";
@@ -84,6 +110,7 @@ export default class App extends Vue {
@import "~bulma/sass/elements/tag.sass";
@import "~bulma/sass/elements/title.sass";
@import "~bulma/sass/elements/notification";
+@import "~bulma/sass/elements/table";
@import "~bulma/sass/grid/_all.sass";
@import "~bulma/sass/layout/_all.sass";
@@ -100,6 +127,7 @@ export default class App extends Vue {
@import "~buefy/src/scss/components/upload";
@import "~buefy/src/scss/components/radio";
@import "~buefy/src/scss/components/switch";
+@import "~buefy/src/scss/components/table";
.router-enter-active,
.router-leave-active {
diff --git a/js/src/apollo/user.ts b/js/src/apollo/user.ts
index f5920c6e0..be3273999 100644
--- a/js/src/apollo/user.ts
+++ b/js/src/apollo/user.ts
@@ -1,5 +1,6 @@
import { ApolloCache } from 'apollo-cache';
import { NormalizedCacheObject } from 'apollo-cache-inmemory';
+import { ICurrentUserRole } from '@/types/current-user.model';
export function buildCurrentUserResolver(cache: ApolloCache) {
cache.writeData({
@@ -9,22 +10,44 @@ export function buildCurrentUserResolver(cache: ApolloCache {
+ updateCurrentUser: (_, { id, email, isLoggedIn, role }, { cache }) => {
const data = {
currentUser: {
id,
email,
isLoggedIn,
+ role,
__typename: 'CurrentUser',
},
};
+ cache.writeData({ data });
+ },
+ updateCurrentActor: (_, { id, preferredUsername, avatar, name }, { cache }) => {
+ const data = {
+ currentActor: {
+ id,
+ preferredUsername,
+ avatar,
+ name,
+ __typename: 'CurrentActor',
+ },
+ };
+
cache.writeData({ data });
},
},
diff --git a/js/src/components/Account/Identities.vue b/js/src/components/Account/Identities.vue
index fb0870efa..c42c3f512 100644
--- a/js/src/components/Account/Identities.vue
+++ b/js/src/components/Account/Identities.vue
@@ -1,7 +1,7 @@
- My identities
+ {{ $t('My identities') }}
- Create a new identity
+ {{ $t('Create a new identity') }}
@@ -55,7 +55,7 @@
+
\ No newline at end of file
diff --git a/js/src/components/Event/TagInput.vue b/js/src/components/Event/TagInput.vue
index 30c5326ef..6b357c94d 100644
--- a/js/src/components/Event/TagInput.vue
+++ b/js/src/components/Event/TagInput.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/js/src/components/Footer.vue b/js/src/components/Footer.vue
index 4f7cbb001..a2df207cf 100644
--- a/js/src/components/Footer.vue
+++ b/js/src/components/Footer.vue
@@ -1,18 +1,13 @@
diff --git a/js/src/components/NavBar.vue b/js/src/components/NavBar.vue
index efbc62f84..fb8d6b14b 100644
--- a/js/src/components/NavBar.vue
+++ b/js/src/components/NavBar.vue
@@ -27,34 +27,54 @@
@@ -66,21 +86,30 @@
diff --git a/js/src/components/PictureUpload.vue b/js/src/components/PictureUpload.vue
index 5d5e8c518..da1ed36e4 100644
--- a/js/src/components/PictureUpload.vue
+++ b/js/src/components/PictureUpload.vue
@@ -8,7 +8,7 @@
- Click to upload
+ {{ $t('Click to upload') }}
@@ -41,8 +41,12 @@ export default class PictureUpload extends Vue {
imageSrc: string | null = null;
+ mounted() {
+ this.updatePreview(this.pictureFile);
+ }
+
@Watch('pictureFile')
- onPictureFileChanged (val: File) {
+ onPictureFileChanged(val: File) {
this.updatePreview(val);
}
diff --git a/js/src/components/Report/ReportCard.vue b/js/src/components/Report/ReportCard.vue
new file mode 100644
index 000000000..e0bc9d46d
--- /dev/null
+++ b/js/src/components/Report/ReportCard.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
Reported by
@{{ report.reporter.preferredUsername }}
+
+
+
{{ report.event.title }}
+
+
{{ report.reportContent }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/js/src/components/Report/ReportModal.vue b/js/src/components/Report/ReportModal.vue
new file mode 100644
index 000000000..b4ef61959
--- /dev/null
+++ b/js/src/components/Report/ReportModal.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/js/src/components/SearchField.vue b/js/src/components/SearchField.vue
index baaba5de6..bc83c04ca 100644
--- a/js/src/components/SearchField.vue
+++ b/js/src/components/SearchField.vue
@@ -16,7 +16,7 @@ export default class SearchField extends Vue {
get defaultPlaceHolder(): string {
// We can't use "this" inside @Prop's default value.
- return this.placeholder || this.$gettext('Search');
+ return this.placeholder || this.$t('Search') as string;
}
}
diff --git a/js/src/constants.ts b/js/src/constants.ts
index 79dbe270f..3a2bb6fe7 100644
--- a/js/src/constants.ts
+++ b/js/src/constants.ts
@@ -2,4 +2,5 @@ export const AUTH_ACCESS_TOKEN = 'auth-access-token';
export const AUTH_REFRESH_TOKEN = 'auth-refresh-token';
export const AUTH_USER_ID = 'auth-user-id';
export const AUTH_USER_EMAIL = 'auth-user-email';
-export const AUTH_USER_ACTOR = 'auth-user-actor';
+export const AUTH_USER_ACTOR_ID = 'auth-user-actor-id';
+export const AUTH_USER_ROLE = 'auth-user-role';
diff --git a/js/src/filters/datetime.ts b/js/src/filters/datetime.ts
new file mode 100644
index 000000000..89b149476
--- /dev/null
+++ b/js/src/filters/datetime.ts
@@ -0,0 +1,19 @@
+function parseDateTime(value: string): Date {
+ return new Date(value);
+}
+
+function formatDateString(value: string): string {
+ return parseDateTime(value).toLocaleString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
+}
+
+function formatTimeString(value: string): string {
+ return parseDateTime(value).toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric' });
+}
+
+function formatDateTimeString(value: string): string {
+ return parseDateTime(value).toLocaleTimeString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' });
+}
+
+
+
+export { formatDateString, formatTimeString, formatDateTimeString };
diff --git a/js/src/filters/index.ts b/js/src/filters/index.ts
new file mode 100644
index 000000000..89a7267a5
--- /dev/null
+++ b/js/src/filters/index.ts
@@ -0,0 +1,9 @@
+import { formatDateString, formatTimeString, formatDateTimeString } from './datetime';
+
+export default {
+ install(vue) {
+ vue.filter('formatDateString', formatDateString);
+ vue.filter('formatTimeString', formatTimeString);
+ vue.filter('formatDateTimeString', formatDateTimeString);
+ },
+};
diff --git a/js/src/graphql/actor.ts b/js/src/graphql/actor.ts
index 7dc92ca20..2231c9d46 100644
--- a/js/src/graphql/actor.ts
+++ b/js/src/graphql/actor.ts
@@ -40,6 +40,25 @@ query {
}
}`;
+export const CURRENT_ACTOR_CLIENT = gql`
+ query {
+ currentActor @client {
+ id,
+ avatar {
+ url
+ },
+ preferredUsername,
+ name
+ }
+ }
+`;
+
+export const UPDATE_CURRENT_ACTOR_CLIENT = gql`
+ mutation UpdateCurrentActor($id: String!, $avatar: String, $preferredUsername: String!, $name: String!) {
+ updateCurrentActor(id: $id, avatar: $avatar, preferredUsername: $preferredUsername, name: $name) @client
+ }
+`;
+
export const LOGGED_PERSON_WITH_GOING_TO_EVENTS = gql`
query {
loggedPerson {
@@ -177,7 +196,7 @@ query($name:String!) {
export const CREATE_GROUP = gql`
mutation CreateGroup(
- $creatorActorId: Int!,
+ $creatorActorId: ID!,
$preferredUsername: String!,
$name: String!,
$summary: String,
diff --git a/js/src/graphql/admin.ts b/js/src/graphql/admin.ts
new file mode 100644
index 000000000..d646bb565
--- /dev/null
+++ b/js/src/graphql/admin.ts
@@ -0,0 +1,19 @@
+import gql from 'graphql-tag';
+
+export const DASHBOARD = gql`
+ query {
+ dashboard {
+ lastPublicEventPublished {
+ title,
+ picture {
+ alt
+ url
+ },
+ },
+ numberOfUsers,
+ numberOfEvents,
+ numberOfComments,
+ numberOfReports
+ }
+ }
+ `;
diff --git a/js/src/graphql/auth.ts b/js/src/graphql/auth.ts
index 580236196..438c46c18 100644
--- a/js/src/graphql/auth.ts
+++ b/js/src/graphql/auth.ts
@@ -7,7 +7,8 @@ mutation Login($email: String!, $password: String!) {
refreshToken,
user {
id,
- email
+ email,
+ role
}
},
}
diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts
index eac64045d..791339dc6 100644
--- a/js/src/graphql/event.ts
+++ b/js/src/graphql/event.ts
@@ -12,6 +12,43 @@ const participantQuery = `
}
`;
+const physicalAddressQuery = `
+ description,
+ floor,
+ street,
+ locality,
+ postalCode,
+ region,
+ country,
+ geom
+`;
+
+const tagsQuery = `
+ id,
+ slug,
+ title
+`;
+
+const optionsQuery = `
+ maximumAttendeeCapacity,
+ remainingAttendeeCapacity,
+ showRemainingAttendeeCapacity,
+ offers {
+ price,
+ priceCurrency,
+ url
+ },
+ participationConditions {
+ title,
+ content,
+ url
+ },
+ attendees,
+ program,
+ commentModeration,
+ showParticipationPrice
+`;
+
export const FETCH_EVENT = gql`
query($uuid:UUID!) {
event(uuid: $uuid) {
@@ -29,20 +66,14 @@ export const FETCH_EVENT = gql`
picture {
id
url
+ name
},
publishAt,
category,
- # online_address,
- # phone_address,
+ onlineAddress,
+ phoneAddress,
physicalAddress {
- description,
- floor,
- street,
- locality,
- postalCode,
- region,
- country,
- geom
+ ${physicalAddressQuery}
}
organizerActor {
avatar {
@@ -64,10 +95,12 @@ export const FETCH_EVENT = gql`
participants {
${participantQuery}
},
+ participantStats {
+ approved,
+ unapproved
+ },
tags {
- id,
- slug,
- title
+ ${tagsQuery}
},
relatedEvents {
uuid,
@@ -86,23 +119,7 @@ export const FETCH_EVENT = gql`
}
},
options {
- maximumAttendeeCapacity,
- remainingAttendeeCapacity,
- showRemainingAttendeeCapacity,
- offers {
- price,
- priceCurrency,
- url
- },
- participationConditions {
- title,
- content,
- url
- },
- attendees,
- program,
- commentModeration,
- showParticipationPrice
+ ${optionsQuery}
}
}
}
@@ -159,37 +176,62 @@ export const FETCH_EVENTS = gql`
`;
export const CREATE_EVENT = gql`
- mutation CreateEvent(
+ mutation createEvent(
+ $organizerActorId: ID!,
$title: String!,
$description: String!,
- $organizerActorId: ID!,
- $category: String,
$beginsOn: DateTime!,
$endsOn: DateTime,
- $picture: PictureInput,
- $tags: [String],
- $options: EventOptionsInput,
- $physicalAddress: AddressInput,
+ $status: EventStatus,
$visibility: EventVisibility
+ $tags: [String],
+ $picture: PictureInput,
+ $onlineAddress: String,
+ $phoneAddress: String,
+ $category: String,
+ $physicalAddress: AddressInput,
+ $options: EventOptionsInput,
) {
createEvent(
+ organizerActorId: $organizerActorId,
title: $title,
description: $description,
beginsOn: $beginsOn,
endsOn: $endsOn,
- organizerActorId: $organizerActorId,
- category: $category,
- options: $options,
- picture: $picture,
+ status: $status,
+ visibility: $visibility,
tags: $tags,
- physicalAddress: $physicalAddress,
- visibility: $visibility
+ picture: $picture,
+ onlineAddress: $onlineAddress,
+ phoneAddress: $phoneAddress,
+ category: $category,
+ physicalAddress: $physicalAddress
+ options: $options,
) {
id,
uuid,
title,
+ description,
+ beginsOn,
+ endsOn,
+ status,
+ visibility,
picture {
+ id
url
+ },
+ publishAt,
+ category,
+ onlineAddress,
+ phoneAddress,
+ physicalAddress {
+ ${physicalAddressQuery}
+ },
+ tags {
+ ${tagsQuery}
+ },
+ options {
+ ${optionsQuery}
}
}
}
@@ -197,38 +239,68 @@ export const CREATE_EVENT = gql`
export const EDIT_EVENT = gql`
mutation updateEvent(
- $id: ID!,
- $title: String!,
- $description: String!,
- $organizerActorId: ID!,
- $category: String,
- $beginsOn: DateTime!,
- $endsOn: DateTime,
- $picture: PictureInput,
- $tags: [String],
- $options: EventOptionsInput,
- $physicalAddress: AddressInput,
- $visibility: EventVisibility
+ $id: ID!,
+ $title: String,
+ $description: String,
+ $beginsOn: DateTime,
+ $endsOn: DateTime,
+ $status: EventStatus,
+ $visibility: EventVisibility
+ $tags: [String],
+ $picture: PictureInput,
+ $onlineAddress: String,
+ $phoneAddress: String,
+ $category: String,
+ $physicalAddress: AddressInput,
+ $options: EventOptionsInput,
) {
- updateEvent(eventId: $id,
- title: $title,
- description: $description,
- beginsOn: $beginsOn,
- endsOn: $endsOn,
- organizerActorId: $organizerActorId,
- category: $category,
- options: $options,
- picture: $picture,
- tags: $tags,
- physicalAddress: $physicalAddress,
- visibility: $visibility) {
- uuid
+ updateEvent(
+ eventId: $id,
+ title: $title,
+ description: $description,
+ beginsOn: $beginsOn,
+ endsOn: $endsOn,
+ status: $status,
+ visibility: $visibility,
+ tags: $tags,
+ picture: $picture,
+ onlineAddress: $onlineAddress,
+ phoneAddress: $phoneAddress,
+ category: $category,
+ physicalAddress: $physicalAddress
+ options: $options,
+ ) {
+ id,
+ uuid,
+ title,
+ description,
+ beginsOn,
+ endsOn,
+ status,
+ visibility,
+ picture {
+ id
+ url
+ },
+ publishAt,
+ category,
+ onlineAddress,
+ phoneAddress,
+ physicalAddress {
+ ${physicalAddressQuery}
+ },
+ tags {
+ ${tagsQuery}
+ },
+ options {
+ ${optionsQuery}
+ }
}
}
`;
export const JOIN_EVENT = gql`
- mutation JoinEvent($eventId: Int!, $actorId: Int!) {
+ mutation JoinEvent($eventId: ID!, $actorId: ID!) {
joinEvent(
eventId: $eventId,
actorId: $actorId
@@ -239,7 +311,7 @@ export const JOIN_EVENT = gql`
`;
export const LEAVE_EVENT = gql`
- mutation LeaveEvent($eventId: Int!, $actorId: Int!) {
+ mutation LeaveEvent($eventId: ID!, $actorId: ID!) {
leaveEvent(
eventId: $eventId,
actorId: $actorId
@@ -252,9 +324,9 @@ export const LEAVE_EVENT = gql`
`;
export const DELETE_EVENT = gql`
- mutation DeleteEvent($id: Int!, $actorId: Int!) {
+ mutation DeleteEvent($eventId: ID!, $actorId: ID!) {
deleteEvent(
- eventId: $id,
+ eventId: $eventId,
actorId: $actorId
) {
id
diff --git a/js/src/graphql/feed_tokens.ts b/js/src/graphql/feed_tokens.ts
index e43c11ca9..eeb17f5cd 100644
--- a/js/src/graphql/feed_tokens.ts
+++ b/js/src/graphql/feed_tokens.ts
@@ -12,7 +12,7 @@ query {
}`;
export const CREATE_FEED_TOKEN_ACTOR = gql`
-mutation createFeedToken($actor_id: Int!) {
+mutation createFeedToken($actor_id: ID!) {
createFeedToken(actorId: $actor_id) {
token,
actor {
diff --git a/js/src/graphql/report.ts b/js/src/graphql/report.ts
new file mode 100644
index 000000000..9cd58c2f1
--- /dev/null
+++ b/js/src/graphql/report.ts
@@ -0,0 +1,161 @@
+import gql from 'graphql-tag';
+
+export const REPORTS = gql`
+ query Reports($status: ReportStatus) {
+ reports(status: $status) {
+ id,
+ reported {
+ id,
+ preferredUsername,
+ name,
+ avatar {
+ url
+ }
+ },
+ reporter {
+ id,
+ preferredUsername,
+ name,
+ avatar {
+ url
+ }
+ },
+ event {
+ id,
+ uuid,
+ title,
+ picture {
+ url
+ }
+ },
+ status
+ }
+ }
+`;
+
+const REPORT_FRAGMENT = gql`
+ fragment ReportFragment on Report {
+ id,
+ reported {
+ id,
+ preferredUsername,
+ name,
+ avatar {
+ url
+ }
+ },
+ reporter {
+ id,
+ preferredUsername,
+ name,
+ avatar {
+ url
+ }
+ },
+ event {
+ id,
+ uuid,
+ title,
+ description,
+ picture {
+ url
+ }
+ },
+ notes {
+ id,
+ content
+ moderator {
+ preferredUsername,
+ name,
+ avatar {
+ url
+ }
+ },
+ insertedAt
+ },
+ insertedAt,
+ updatedAt,
+ status,
+ content
+ }
+`;
+
+export const REPORT = gql`
+ query Report($id: ID!) {
+ report(id: $id) {
+ ...ReportFragment
+ }
+ }
+ ${REPORT_FRAGMENT}
+`;
+
+export const CREATE_REPORT = gql`
+ mutation CreateReport(
+ $eventId: ID!,
+ $reporterActorId: ID!,
+ $reportedActorId: ID!,
+ $content: String
+ ) {
+ createReport(eventId: $eventId, reporterActorId: $reporterActorId, reportedActorId: $reportedActorId, content: $content) {
+ id
+ }
+ }
+ `;
+
+export const UPDATE_REPORT = gql`
+ mutation UpdateReport(
+ $reportId: ID!,
+ $moderatorId: ID!,
+ $status: ReportStatus!
+ ) {
+ updateReportStatus(reportId: $reportId, moderatorId: $moderatorId, status: $status) {
+ ...ReportFragment
+ }
+ }
+ ${REPORT_FRAGMENT}
+`;
+
+export const CREATE_REPORT_NOTE = gql`
+ mutation CreateReportNote(
+ $reportId: ID!,
+ $moderatorId: ID!,
+ $content: String!
+ ) {
+ createReportNote(reportId: $reportId, moderatorId: $moderatorId, content: $content) {
+ id,
+ content,
+ insertedAt
+ }
+ }
+ `;
+
+export const LOGS = gql`
+ query {
+ actionLogs {
+ id,
+ action,
+ actor {
+ id,
+ preferredUsername
+ avatar {
+ url
+ }
+ },
+ object {
+ ...on Report {
+ id
+ },
+ ... on ReportNote {
+ report {
+ id,
+ }
+ }
+ ... on Event {
+ id,
+ title
+ }
+ },
+ insertedAt
+ }
+ }
+ `;
diff --git a/js/src/graphql/user.ts b/js/src/graphql/user.ts
index b23b5da32..61afe904d 100644
--- a/js/src/graphql/user.ts
+++ b/js/src/graphql/user.ts
@@ -31,12 +31,13 @@ query {
id,
email,
isLoggedIn,
+ role
}
}
`;
export const UPDATE_CURRENT_USER_CLIENT = gql`
-mutation UpdateCurrentUser($id: Int!, $email: String!, $isLoggedIn: Boolean!) {
- updateCurrentUser(id: $id, email: $email, isLoggedIn: $isLoggedIn) @client
+mutation UpdateCurrentUser($id: String!, $email: String!, $isLoggedIn: Boolean!, $role: UserRole!) {
+ updateCurrentUser(id: $id, email: $email, isLoggedIn: $isLoggedIn, role: $role) @client
}
`;
diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json
new file mode 100644
index 000000000..6cadb7099
--- /dev/null
+++ b/js/src/i18n/en_US.json
@@ -0,0 +1,200 @@
+{
+ "A validation email was sent to {email}": "A validation email was sent to {email}",
+ "About this event": "About this event",
+ "About this instance": "About this instance",
+ "About": "About",
+ "Add a new profile": "Add a new profile",
+ "Add a tag": "Add a tag",
+ "Add an address": "Add an address",
+ "Add to my calendar": "Add to my calendar",
+ "Add": "Add",
+ "Administration": "Administration",
+ "Allow all comments": "Allow all comments",
+ "Are you going to this event?": "Are you going to this event?",
+ "Are you sure you want to delete this event? This action cannot be reverted.": "Are you sure you want to delete this event? This action cannot be reverted.",
+ "Before you can login, you need to click on the link inside it to validate your account": "Before you can login, you need to click on the link inside it to validate your account",
+ "By {name}": "By {name}",
+ "Category": "Category",
+ "Change": "Change",
+ "Clear": "Clear",
+ "Click to select": "Click to select",
+ "Click to upload": "Click to upload",
+ "Close comments for all (except for admins)": "Close comments for all (except for admins)",
+ "Comments on the event page": "Comments on the event page",
+ "Comments": "Comments",
+ "Confirmed: Will happen": "Confirmed: Will happen",
+ "Country": "Country",
+ "Create a new event": "Create a new event",
+ "Create a new group": "Create a new group",
+ "Create a new identity": "Create a new identity",
+ "Create group": "Create group",
+ "Create my event": "Create my event",
+ "Create my group": "Create my group",
+ "Create my profile": "Create my profile",
+ "Create token": "Create token",
+ "Create your communities and your events": "Create your communities and your events",
+ "Create": "Create",
+ "Current": "Current",
+ "Delete event": "Delete event",
+ "Delete this identity": "Delete this identity",
+ "Delete your identity": "Delete your identity",
+ "Delete {eventTitle}": "Delete {eventTitle}",
+ "Delete {preferredUsername}": "Delete {preferredUsername}",
+ "Delete": "Delete",
+ "Description": "Description",
+ "Didn't receive the instructions ?": "Didn't receive the instructions ?",
+ "Disallow promoting on Mobilizon": "Disallow promoting on Mobilizon",
+ "Display name": "Display name",
+ "Display participation price": "Display participation price",
+ "Displayed name": "Displayed name",
+ "Edit": "Edit",
+ "Either the account is already validated, either the validation token is incorrect.": "Either the account is already validated, either the validation token is incorrect.",
+ "Email": "Email",
+ "Ends on…": "Ends on…",
+ "Enter some tags": "Enter some tags",
+ "Error while validating account": "Error while validating account",
+ "Event list": "Event list",
+ "Event {eventTitle} deleted": "Event {eventTitle} deleted",
+ "Event {eventTitle} reported": "Event {eventTitle} reported",
+ "Event": "Event",
+ "Events nearby you": "Events nearby you",
+ "Events you're going at": "Events you're going at",
+ "Events": "Events",
+ "Features": "Features",
+ "Find an address": "Find an address",
+ "Forgot your password ?": "Forgot your password ?",
+ "From the {startDate} at {startTime} to the {endDate} at {endTime}": "From the {startDate} at {startTime} to the {endDate} at {endTime}",
+ "General information": "General information",
+ "Group List": "Group List",
+ "Group full name": "Group full name",
+ "Group name": "Group name",
+ "Group {displayName} created": "Group {displayName} created",
+ "Group": "Group",
+ "Groups": "Groups",
+ "I create an identity": "I create an identity",
+ "I want to approve every participation request": "I want to approve every participation request",
+ "Identities": "Identities",
+ "Identity {displayName} created": "Identity {displayName} created",
+ "Identity {displayName} deleted": "Identity {displayName} deleted",
+ "Identity {displayName} updated": "Identity {displayName} updated",
+ "Identity": "Identity",
+ "If an account with this email exists, we just sent another confirmation email to {email}": "If an account with this email exists, we just sent another confirmation email to {email}",
+ "If this identity is the only administrator of some groups, you need to delete them before being able to delete this identity.": "If this identity is the only administrator of some groups, you need to delete them before being able to delete this identity.",
+ "Join": "Join",
+ "Last published event": "Last published event",
+ "Learn more on {0}": "Learn more on {0}",
+ "Learn more on": "Learn more on",
+ "Leave": "Leave",
+ "Legal": "Legal",
+ "License": "License",
+ "Limited places": "Limited places",
+ "Loading…": "Loading…",
+ "Locality": "Locality",
+ "Log in": "Log in",
+ "Log out": "Log out",
+ "Login": "Login",
+ "Members": "Members",
+ "Moderated comments (shown after approval)": "Moderated comments (shown after approval)",
+ "My account": "My account",
+ "My identities": "My identities",
+ "Name": "Name",
+ "No address defined": "No address defined",
+ "No events found": "No events found",
+ "No group found": "No group found",
+ "No groups found": "No groups found",
+ "No results for \"{queryText}\"": "No results for \"{queryText}\"",
+ "Number of places": "Number of places",
+ "One person is going": "No one is going | One person is going | {approved} persons are going",
+ "Only accessible through link and search (private)": "Only accessible through link and search (private)",
+ "Opened reports": "Opened reports",
+ "Organized": "Organized",
+ "Organizer": "Organizer",
+ "Other stuff…": "Other stuff…",
+ "Otherwise this identity will just be removed from the group administrators.": "Otherwise this identity will just be removed from the group administrators.",
+ "Page limited to my group (asks for auth)": "Page limited to my group (asks for auth)",
+ "Participation approval": "Participation approval",
+ "Password reset": "Password reset",
+ "Password": "Password",
+ "Pick an identity": "Pick an identity",
+ "Please be nice to each other": "Please be nice to each other",
+ "Please check you spam folder if you didn't receive the email.": "Please check you spam folder if you didn't receive the email.",
+ "Please contact this instance's Mobilizon admin if you think this is a mistake.": "Please contact this instance's Mobilizon admin if you think this is a mistake.",
+ "Please make sure the address is correct and that the page hasn't been moved.": "Please make sure the address is correct and that the page hasn't been moved.",
+ "Please read the full rules": "Please read the full rules",
+ "Please type at least 5 characters": "Please type at least 5 characters",
+ "Postal Code": "Postal Code",
+ "Private feeds": "Private feeds",
+ "Promotion": "Promotion",
+ "Public RSS/Atom Feed": "Public RSS/Atom Feed",
+ "Public comment moderation": "Public comment moderation",
+ "Public feeds": "Public feeds",
+ "Public iCal Feed": "Public iCal Feed",
+ "Published events": "Published events",
+ "RSS/Atom Feed": "RSS/Atom Feed",
+ "Region": "Region",
+ "Register an account on Mobilizon!": "Register an account on Mobilizon!",
+ "Register": "Register",
+ "Registration is currently closed.": "Registration is currently closed.",
+ "Report": "Signaler",
+ "Resend confirmation email": "Resend confirmation email",
+ "Reset my password": "Reset my password",
+ "Save": "Save",
+ "Search events, groups, etc.": "Search events, groups, etc.",
+ "Search results: \"{search}\"": "Search results: \"{search}\"",
+ "Search": "Search",
+ "Searching…": "Searching…",
+ "Send confirmation email again": "Send confirmation email again",
+ "Send email to reset my password": "Send email to reset my password",
+ "Share this event": "Share this event",
+ "Show map": "Show map",
+ "Show remaining number of places": "Show remaining number of places",
+ "Sign up": "Sign up",
+ "Starts on…": "Starts on…",
+ "Status": "Status",
+ "Street": "Street",
+ "Tentative: Will be confirmed later": "Tentative: Will be confirmed later",
+ "The event organizer didn't add any description.": "The event organizer didn't add any description.",
+ "The page you're looking for doesn't exist.": "The page you're looking for doesn't exist.",
+ "The {date} at {time}": "The {date} at {time}",
+ "The {date} from {startTime} to {endTime}": "The {date} from {startTime} to {endTime}",
+ "There are {participants} participants.": "There's only one participant | There are {participants} participants.",
+ "These events may interest you": "These events may interest you",
+ "This instance isn't opened to registrations, but you can register on other instances.": "This instance isn't opened to registrations, but you can register on other instances.",
+ "This will delete / anonymize all content (events, comments, messages, participations…) created from this identity.": "This will delete / anonymize all content (events, comments, messages, participations…) created from this identity.",
+ "Title": "Title",
+ "To confirm, type your event title \"{eventTitle}\"": "To confirm, type your event title \"{eventTitle}\"",
+ "To confirm, type your identity username \"{preferredUsername}\"": "To confirm, type your identity username \"{preferredUsername}\"",
+ "Unknown error.": "Unknown error.",
+ "Update event {name}": "Update event {name}",
+ "Update my event": "Update my event",
+ "User logout": "User logout",
+ "Username": "Username",
+ "Users": "Users",
+ "Visible everywhere on the web (public)": "Visible everywhere on the web (public)",
+ "We just sent an email to {email}": "We just sent an email to {email}",
+ "Website / URL": "Website / URL",
+ "Welcome back {username}": "Welcome back {username}",
+ "Welcome back!": "Welcome back!",
+ "Welcome on your administration panel": "Welcome on your administration panel",
+ "Who can view this event and participate": "Who can view this event and participate",
+ "World map": "World map",
+ "You and one other person are going to this event": "You're the only one going to this event | You and one other person are going to this event | You and {approved} persons are going to this event.",
+ "You announced that you're going to this event.": "You announced that you're going to this event.",
+ "You are already logged-in.": "You are already logged-in.",
+ "You are an organizer.": "You are an organizer.",
+ "You have one event in {days} days.": "You have no events in {days} days | You have one event in {days} days. | You have {count} events in {days} days",
+ "You have one event today.": "You have no events today | You have one event today. | You have {count} events today",
+ "You have one event tomorrow.": "You have no events tomorrow | You have one event tomorrow. | You have {count} events tomorrow",
+ "You need to login.": "You need to login.",
+ "You're not going to any event yet": "You're not going to any event yet",
+ "Your account has been validated": "Your account has been validated",
+ "Your account is being validated": "Your account is being validated",
+ "Your account is nearly ready, {username}": "Your account is nearly ready, {username}",
+ "Your local administrator resumed it's policy:": "Your local administrator resumed it's policy:",
+ "e.g. 10 Rue Jangot": "e.g. 10 Rue Jangot",
+ "iCal Feed": "iCal Feed",
+ "meditate a bit": "meditate a bit",
+ "public event": "public event",
+ "{actor}'s avatar": "{actor}'s avatar",
+ "© The Mobilizon Contributors {date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks": "© The Mobilizon Contributors {date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks"
+}
\ No newline at end of file
diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json
new file mode 100644
index 000000000..92db9529a
--- /dev/null
+++ b/js/src/i18n/fr_FR.json
@@ -0,0 +1,200 @@
+{
+ "A validation email was sent to {email}": "Un email de validation a été envoyé à {email}",
+ "About this event": "À propos de cet événement",
+ "About this instance": "À propos de cette instance",
+ "About": "À propos",
+ "Add a new profile": "Ajouter un nouveau profil",
+ "Add a tag": "Ajouter un tag",
+ "Add an address": "Ajouter une adresse",
+ "Add to my calendar": "Ajouter à mon agenda",
+ "Add": "Ajouter",
+ "Administration": "Administration",
+ "Allow all comments": "Autoriser tous les commentaires",
+ "Are you going to this event?": "Allez-vous à cet événement ?",
+ "Are you sure you want to delete this event? This action cannot be reverted.": "Êtes-vous certain⋅e de vouloir supprimer cet événement ? Cette action ne peut être annulée.",
+ "Before you can login, you need to click on the link inside it to validate your account": "Avant que vous puissiez vous enregistrer, vous devez cliquer sur le lien à l'intérieur pour valider votre compte",
+ "By {name}": "Par {name}",
+ "Category": "Catégorie",
+ "Change": "Modifier",
+ "Clear": "Effacer",
+ "Click to select": "Cliquez pour sélectionner",
+ "Click to upload": "Cliquez pour uploader",
+ "Close comments for all (except for admins)": "Fermer les commentaires à tout le monde (excepté les administrateurs)",
+ "Comments on the event page": "Commentaires sur la page de l'événement",
+ "Comments": "Commentaires",
+ "Confirmed: Will happen": "Confirmé : aura lieu",
+ "Country": "Pays",
+ "Create a new event": "Créer un nouvel événement",
+ "Create a new group": "Créer un nouveau groupe",
+ "Create a new identity": "Créer une nouvelle identité",
+ "Create group": "Créer un groupe",
+ "Create my event": "Créer mon événement",
+ "Create my group": "Créer mon groupe",
+ "Create my profile": "Créer mon profil",
+ "Create token": "Créer un jeton",
+ "Create your communities and your events": "Créer vos communautés et vos événements",
+ "Create": "Créer",
+ "Current": "Actuel",
+ "Delete event": "Supprimer un événement",
+ "Delete this identity": "Supprimer cette identité",
+ "Delete your identity": "Supprimer votre identité",
+ "Delete {eventTitle}": "Supprimer {eventTitle}",
+ "Delete {preferredUsername}": "Supprimer {preferredUsername}",
+ "Delete": "Supprimer",
+ "Description": "Description",
+ "Didn't receive the instructions ?": "Vous n'avez pas reçu les instructions ?",
+ "Disallow promoting on Mobilizon": "Refuser la mise en avant sur Mobilizon",
+ "Display name": "Nom affiché",
+ "Display participation price": "Afficher un prix de participation",
+ "Displayed name": "Nom affiché",
+ "Edit": "Éditer",
+ "Either the account is already validated, either the validation token is incorrect.": "Soit le compte est déjà validé, soit le jeton de validation est incorrect.",
+ "Email": "Email",
+ "Ends on…": "Se termine le…",
+ "Enter some tags": "Écrire des tags",
+ "Error while validating account": "Erreur lors de la validation du compte",
+ "Event list": "Liste d'événements",
+ "Event {eventTitle} deleted": "Événement {eventTitle} supprimé",
+ "Event {eventTitle} reported": "Événement {eventTitle} signalé",
+ "Event": "Événement",
+ "Events nearby you": "Événements près de chez vous",
+ "Events you're going at": "Événements auxquels vous vous rendez",
+ "Events": "Événements",
+ "Features": "Fonctionnalités",
+ "Find an address": "Trouver une adresse",
+ "Forgot your password ?": "Mot de passe oublié ?",
+ "From the {startDate} at {startTime} to the {endDate} at {endTime}": "Du {startDate} à {startTime} au {endDate} à {endTime}",
+ "General information": "Information générales",
+ "Group List": "Liste de groupes",
+ "Group full name": "Nom complet du groupe",
+ "Group name": "Nom du groupe",
+ "Group {displayName} created": "Groupe {displayName} créé",
+ "Group": "Groupe",
+ "Groups": "Groupes",
+ "I create an identity": "Je crée une identité",
+ "I want to approve every participation request": "Je veux approuver chaque demande de participation",
+ "Identities": "Identités",
+ "Identity {displayName} created": "Identité {displayName} créée",
+ "Identity {displayName} deleted": "Identité {displayName} supprimée",
+ "Identity {displayName} updated": "Identité {displayName} mise à jour",
+ "Identity": "Identité",
+ "If an account with this email exists, we just sent another confirmation email to {email}": "Si un compte avec un tel email existe, nous venons juste d'envoyer un nouvel email de confirmation à {email}",
+ "If this identity is the only administrator of some groups, you need to delete them before being able to delete this identity.": "Si cette identité est la seule administratrice de certains groupes, vous devez les supprimer avant de pouvoir supprimer cette identité.",
+ "Join": "Rejoindre",
+ "Last published event": "Dernier événement publié",
+ "Learn more on {0}": "En apprendre plus sur {0}",
+ "Learn more on": "En apprendre plus sur",
+ "Leave": "Quitter",
+ "Legal": "Mentions légales",
+ "License": "Licence",
+ "Limited places": "Places limitées",
+ "Loading…": "Chargement en cours…",
+ "Locality": "Commune",
+ "Log in": "Se connecter",
+ "Log out": "Se déconnecter",
+ "Login": "Se connecter",
+ "Members": "Membres",
+ "Moderated comments (shown after approval)": "Commentaires modérés (affichés après validation)",
+ "My account": "Mon compte",
+ "My identities": "Mes identités",
+ "Name": "Nom",
+ "No address defined": "Aucune adresse définie",
+ "No events found": "Aucun événement trouvé",
+ "No group found": "Aucun groupe trouvé",
+ "No groups found": "Aucun groupe trouvé",
+ "No results for \"{queryText}\"": "Pas de résultats pour « {queryText} »",
+ "Number of places": "Nombre de places",
+ "One person is going": "Personne n'y va | Une personne y va | {approved} personnes y vont",
+ "Only accessible through link and search (private)": "Uniquement accessibles par lien et la recherche (privé)",
+ "Opened reports": "Signalements ouverts",
+ "Organized": "Organisés",
+ "Organizer": "Organisateur",
+ "Other stuff…": "Autres trucs…",
+ "Otherwise this identity will just be removed from the group administrators.": "Sinon cette identité sera juste supprimée des administrateurs du groupe.",
+ "Page limited to my group (asks for auth)": "Accès limité à mon groupe (demande authentification)",
+ "Participation approval": "Validation des participations",
+ "Password reset": "Réinitialisation du mot de passe",
+ "Password": "Mot de passe",
+ "Pick an identity": "Choisissez une identité",
+ "Please be nice to each other": "Soyez sympas entre vous",
+ "Please check you spam folder if you didn't receive the email.": "Merci de vérifier votre dossier des indésirables si vous n'avez pas reçu l'email.",
+ "Please contact this instance's Mobilizon admin if you think this is a mistake.": "Veuillez contacter l'administrateur de cette instance Mobilizon si vous pensez qu’il s’agit d’une erreur.",
+ "Please make sure the address is correct and that the page hasn't been moved.": "Assurez‐vous que l’adresse est correcte et que la page n’a pas été déplacée.",
+ "Please read the full rules": "Merci de lire les règles complètes",
+ "Please type at least 5 characters": "Merci d'entrer au moins 5 caractères",
+ "Postal Code": "Code postal",
+ "Private feeds": "Flux privés",
+ "Promotion": "Mise en avant",
+ "Public RSS/Atom Feed": "Flux RSS/Atom public",
+ "Public comment moderation": "Modération des commentaires publics",
+ "Public feeds": "Flux publics",
+ "Public iCal Feed": "Flux iCal public",
+ "Published events": "Événements publiés",
+ "RSS/Atom Feed": "Flux RSS/Atom",
+ "Region": "Région",
+ "Register an account on Mobilizon!": "S'inscrire sur Mobilizon !",
+ "Register": "S'inscrire",
+ "Registration is currently closed.": "Les inscriptions sont actuellement fermées.",
+ "Report": "Report",
+ "Resend confirmation email": "Envoyer à nouveau l'email de confirmation",
+ "Reset my password": "Réinitialiser mon mot de passe",
+ "Save": "Enregistrer",
+ "Search events, groups, etc.": "Rechercher des événements, des groupes, etc.",
+ "Search results: \"{search}\"": "Résultats de recherche: « {search} »",
+ "Search": "Rechercher",
+ "Searching…": "Recherche en cours…",
+ "Send confirmation email again": "Envoyer l'email de confirmation à nouveau",
+ "Send email to reset my password": "Envoyer un email pour réinitialiser mon mot de passe",
+ "Share this event": "Partager l'événement",
+ "Show map": "Afficher la carte",
+ "Show remaining number of places": "Afficher le nombre de places restantes",
+ "Sign up": "S'enregistrer",
+ "Starts on…": "Débute le…",
+ "Status": "Statut",
+ "Street": "Rue",
+ "Tentative: Will be confirmed later": "Provisoire : sera confirmé plus tard",
+ "The event organizer didn't add any description.": "L'organisateur de l'événement n'a pas ajouté de description.",
+ "The page you're looking for doesn't exist.": "La page que vous recherchez n'existe pas.",
+ "The {date} at {time}": "Le {date} à {time}",
+ "The {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}",
+ "There are {participants} participants.": "Il n'y a qu'un⋅e participant⋅e. | Il y a {participants} participants.",
+ "These events may interest you": "Ces événements peuvent vous intéresser",
+ "This instance isn't opened to registrations, but you can register on other instances.": "Cette instance n'autorise pas les inscriptions, mais vous pouvez vous enregistrer sur d'autres instances.",
+ "This will delete / anonymize all content (events, comments, messages, participations…) created from this identity.": "Cela supprimera / anonymisera tout le contenu (événements, commentaires, messages, participations…) créés avec cette identité.",
+ "Title": "Titre",
+ "To confirm, type your event title \"{eventTitle}\"": "Pour confirmer, entrez le titre de l'événement « {eventTitle} »",
+ "To confirm, type your identity username \"{preferredUsername}\"": "Pour confirmer, entrez le nom de l’identité « {preferredUsername} »",
+ "Unknown error.": "Erreur inconnue.",
+ "Update event {name}": "Éditer l'événement {name}",
+ "Update my event": "Éditer mon événement",
+ "User logout": "Déconnexion",
+ "Username": "Pseudo",
+ "Users": "Utilisateurs",
+ "Visible everywhere on the web (public)": "Visible partout sur le web (public)",
+ "We just sent an email to {email}": "Nous venons d'envoyer un email à {email}",
+ "Website / URL": "Site web / URL",
+ "Welcome back {username}": "Bon retour {username}",
+ "Welcome back!": "Bon retour !",
+ "Welcome on your administration panel": "Bienvenue sur votre espace d'administration",
+ "Who can view this event and participate": "Qui peut voir cet événement et y participer",
+ "World map": "Carte mondiale",
+ "You and one other person are going to this event": "Vous êtes le ou la seule à vous rendre à cet événement | Vous et une autre personne vous rendez à cet événement | Vous et {approved} autres personnes vous rendez à cet événement.",
+ "You announced that you're going to this event.": "Vous avez annoncé vous rendre à cet événement.",
+ "You are already logged-in.": "Vous êtes déjà connecté.",
+ "You are an organizer.": "Vous êtes un organisateur.",
+ "You have one event in {days} days.": "Vous n'avez pas d'événements dans {days} jours | Vous avez un événement dans {days} jours. | Vous avez {count} événements dans {days} jours",
+ "You have one event today.": "Vous n'avez pas d'évenement aujourd'hui | Vous avez un événement aujourd'hui. | Vous avez {count} événements aujourd'hui",
+ "You have one event tomorrow.": "Vous n'avez pas d'événement demain | Vous avez un événement demain. | Vous avez {count} événements demain",
+ "You need to login.": "Vous devez vous connecter.",
+ "You're not going to any event yet": "Vous n'allez à aucun événement pour le moment",
+ "Your account has been validated": "Votre compte a été validé",
+ "Your account is being validated": "Votre compte est en cours de validation",
+ "Your account is nearly ready, {username}": "Votre compte est presque prêt, {username}",
+ "Your local administrator resumed it's policy:": "Votre administrateur local a résumé sa politique ainsi :",
+ "e.g. 10 Rue Jangot": "par exemple : 10 Rue Jangot",
+ "iCal Feed": "Flux iCal",
+ "meditate a bit": "méditez un peu",
+ "public event": "événement public",
+ "{actor}'s avatar": "Avatar de {actor}",
+ "© The Mobilizon Contributors {date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks": "© Les contributeurs de Mobilizon {date} - Fait avec Elixir, Phoenix, VueJS & et de l'amour et des semaines"
+}
\ No newline at end of file
diff --git a/js/src/i18n/index.js b/js/src/i18n/index.js
new file mode 100644
index 000000000..cc43b375c
--- /dev/null
+++ b/js/src/i18n/index.js
@@ -0,0 +1,7 @@
+import en_US from './en_US';
+import fr_FR from './fr_FR';
+
+export default {
+ en_US,
+ fr_FR
+}
\ No newline at end of file
diff --git a/js/src/i18n/locale/en_US/LC_MESSAGES/app.po b/js/src/i18n/locale/en_US/LC_MESSAGES/app.po
deleted file mode 100644
index 3a6b4c277..000000000
--- a/js/src/i18n/locale/en_US/LC_MESSAGES/app.po
+++ /dev/null
@@ -1,439 +0,0 @@
-# English translations for mobilizon package.
-# Copyright (C) 2019 THE mobilizon'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the mobilizon package.
-# Automatically generated, 2019.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: mobilizon 0.1.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-04-12 16:47+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"
-"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/Footer.vue:10
-msgid "© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks"
-msgstr "© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks"
-
-#: src/views/Account/Register.vue:57
-msgid "A validation email was sent to %{email}"
-msgstr "A validation email was sent to %{email}"
-
-#: src/components/Footer.vue:5
-msgid "About"
-msgstr "About"
-
-#: src/views/Event/Event.vue:137
-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:216
-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/views/Event/Event.vue:100
-msgid "By %{ name }"
-msgstr "By %{ name }"
-
-#: src/views/Event/Create.vue:3
-msgid "Create a new event"
-msgstr "Create a new event"
-
-#: 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 "Create your communities and your events"
-
-#: src/views/Account/Identities.vue:36
-msgid "Current"
-msgstr "Current"
-
-#: src/views/Account/Profile.vue:93 src/views/Event/Event.vue:63
-msgid "Delete"
-msgstr "Delete"
-
-#: src/views/User/Register.vue:82
-msgid "Didn't receive the instructions ?"
-msgstr "Didn't receive the instructions ?"
-
-#: src/views/Event/Event.vue:58
-msgid "Edit"
-msgstr "Edit"
-
-#: 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/views/Event/EventList.vue:3
-msgid "Event list"
-msgstr "Event list"
-
-#: 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 "Features"
-
-#: src/views/User/Login.vue:41
-msgid "Forgot your password ?"
-msgstr "Forgot your password ?"
-
-#: 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"
-" joinmobilizon.org"
-msgstr ""
-"Learn more on\n"
-" joinmobilizon.org"
-
-#: 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 "Log in"
-
-#: 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 "Login"
-
-#: src/views/User/Register.vue:32
-msgid "meditate a bit"
-msgstr "meditate a bit"
-
-#: 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:69
-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 "No events found"
-
-#: 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 "Organized"
-
-#: src/components/Event/EventCard.vue:1
-msgid "Organizer"
-msgstr "Organizer"
-
-#: src/views/User/Register.vue:17
-msgid "Other stuff…"
-msgstr "Other stuff…"
-
-#: src/views/User/PasswordReset.vue:4 src/views/User/SendPasswordReset.vue:4
-msgid "Password reset"
-msgstr "Password reset"
-
-#: src/views/User/Register.vue:31
-msgid "Please be nice to each other"
-msgstr "Please be nice to each other"
-
-#: 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/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 "Please read the full rules"
-
-#: 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/views/Account/Register.vue:5 src/views/User/Register.vue:5
-msgid "Register an account on Mobilizon!"
-msgstr "Register an account on Mobilizon!"
-
-#: 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 "Resend confirmation email"
-
-#: src/views/User/PasswordReset.vue:29
-msgid "Reset my password"
-msgstr "Reset my password"
-
-#: src/views/Account/Profile.vue:51
-msgid "RSS/Atom Feed"
-msgstr "RSS/Atom Feed"
-
-#: src/views/PageNotFound.vue:19 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 "Send confirmation email again"
-
-#: src/views/User/SendPasswordReset.vue:12
-msgid "Send email to reset my password"
-msgstr "Send email to reset my password"
-
-#: src/views/Event/Event.vue:205
-msgid "Share this event"
-msgstr "Share this event"
-
-#: src/views/Event/Event.vue:78
-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:140
-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/views/Event/Event.vue:223
-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 "User logout"
-
-#: src/views/User/SendPasswordReset.vue:17
-msgid "We just sent an email to %{email}"
-msgstr "We just sent an email to %{email}"
-
-#: src/views/Home.vue:18
-msgid "Welcome back %{username}"
-msgstr "Welcome back %{username}"
-
-#: src/views/User/Login.vue:4
-msgid "Welcome back!"
-msgstr "Welcome back!"
-
-#: 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 "Your account has been validated"
-
-#: src/views/User/Validate.vue:3
-msgid "Your account is being validated"
-msgstr "Your account is being validated"
-
-#: 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:42
-msgid "Search events, groups, etc."
-msgstr ""
diff --git a/js/src/i18n/locale/fr_FR/LC_MESSAGES/app.po b/js/src/i18n/locale/fr_FR/LC_MESSAGES/app.po
deleted file mode 100644
index ef0b304f4..000000000
--- a/js/src/i18n/locale/fr_FR/LC_MESSAGES/app.po
+++ /dev/null
@@ -1,440 +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: 2019-04-12 16:47+0200\n"
-"PO-Revision-Date: 2019-04-12 16:45+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"
-"X-Generator: Poedit 2.2.1\n"
-
-#: src/components/Footer.vue:10
-msgid "© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks"
-msgstr "© Les contributeurs de Mobilizon %{date} - Fait avec Elixir, Phoenix, VueJS & et de l'amour et des semaines"
-
-#: src/views/Account/Register.vue:57
-msgid "A validation email was sent to %{email}"
-msgstr "Un email de validation a été envoyé à %{email}"
-
-#: src/components/Footer.vue:5
-msgid "About"
-msgstr "À propos"
-
-#: src/views/Event/Event.vue:137
-msgid "About this event"
-msgstr "À propos de cet événement"
-
-#: src/views/User/Register.vue:26
-msgid "About this instance"
-msgstr "À propos de cette instance"
-
-#: 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:216
-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 "Avant que vous puissiez vous enregistrer, vous devez cliquer sur le lien à l'intérieur pour valider votre compte"
-
-#: src/views/Event/Event.vue:100
-msgid "By %{ name }"
-msgstr "Par %{name}"
-
-#: src/views/Event/Create.vue:3
-msgid "Create a new event"
-msgstr "Créer un nouvel événement"
-
-#: 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 "Créer vos communautés et vos événements"
-
-#: src/views/Account/Identities.vue:36
-msgid "Current"
-msgstr "Actuel"
-
-#: src/views/Account/Profile.vue:93 src/views/Event/Event.vue:63
-msgid "Delete"
-msgstr "Supprimer"
-
-#: src/views/User/Register.vue:82
-msgid "Didn't receive the instructions ?"
-msgstr "Vous n'avez pas reçu les instructions ?"
-
-#: src/views/Event/Event.vue:58
-msgid "Edit"
-msgstr "Éditer"
-
-#: 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/views/Event/EventList.vue:3
-msgid "Event list"
-msgstr "Liste d'événements"
-
-#: 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 "Fonctionnalités"
-
-#: src/views/User/Login.vue:41
-msgid "Forgot your password ?"
-msgstr "Mot de passe oublié ?"
-
-#: 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"
-" joinmobilizon.org"
-msgstr ""
-"En apprendre plus sur\n"
-" joinmobilizon.org"
-
-#: 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 "Se connecter"
-
-#: 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 "Se connecter"
-
-#: src/views/User/Register.vue:32
-msgid "meditate a bit"
-msgstr "méditez un peu"
-
-#: 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:69
-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 "Aucun événement trouvé"
-
-#: 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 "Organisés"
-
-#: src/components/Event/EventCard.vue:1
-msgid "Organizer"
-msgstr "Organisateur"
-
-#: src/views/User/Register.vue:17
-msgid "Other stuff…"
-msgstr "Autres trucs…"
-
-#: src/views/User/PasswordReset.vue:4 src/views/User/SendPasswordReset.vue:4
-msgid "Password reset"
-msgstr "Réinitialisation du mot de passe"
-
-#: src/views/User/Register.vue:31
-msgid "Please be nice to each other"
-msgstr "Soyez sympas entre vous"
-
-#: 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 "Merci de vérifier votre dossier des indésirables si vous n'avez pas reçu l'email."
-
-#: 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 "Merci de lire les règles complètes"
-
-#: 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/views/Account/Register.vue:5 src/views/User/Register.vue:5
-msgid "Register an account on Mobilizon!"
-msgstr "S'inscrire sur Mobilizon !"
-
-#: 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 "Envoyer à nouveau l'email de confirmation"
-
-#: src/views/User/PasswordReset.vue:29
-msgid "Reset my password"
-msgstr "Réinitialiser mon mot de passe"
-
-#: src/views/Account/Profile.vue:51
-msgid "RSS/Atom Feed"
-msgstr "Flux RSS/Atom"
-
-#: src/views/PageNotFound.vue:19 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 "Envoyer l'email de confirmation à nouveau"
-
-#: src/views/User/SendPasswordReset.vue:12
-msgid "Send email to reset my password"
-msgstr "Envoyer un email pour réinitialiser mon mot de passe"
-
-#: src/views/Event/Event.vue:205
-msgid "Share this event"
-msgstr "Partager l'événement"
-
-#: src/views/Event/Event.vue:78
-msgid "Show map"
-msgstr "Afficher la carte"
-
-#: src/components/NavBar.vue:28
-msgid "Sign up"
-msgstr "S'enregistrer"
-
-#: 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:140
-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:223
-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 "Déconnexion"
-
-#: src/views/User/SendPasswordReset.vue:17
-msgid "We just sent an email to %{email}"
-msgstr "Nous venons d'envoyer un email à %{email}"
-
-#: src/views/Home.vue:18
-msgid "Welcome back %{username}"
-msgstr "Bon retour %{username}"
-
-#: src/views/User/Login.vue:4
-msgid "Welcome back!"
-msgstr "Bon retour !"
-
-#: 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 "Votre compte a été validé"
-
-#: src/views/User/Validate.vue:3
-msgid "Your account is being validated"
-msgstr "Votre compte est en cours de validation"
-
-#: 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 "Votre administrateur local a résumé sa politique ainsi :"
-
-#: src/components/Footer.vue:4
-msgid "World map"
-msgstr "Carte mondiale"
-
-#: src/views/PageNotFound.vue:42
-msgid "Search events, groups, etc."
-msgstr "Rechercher des événements, des groupes, etc."
diff --git a/js/src/i18n/translations.json b/js/src/i18n/translations.json
deleted file mode 100644
index 8e863a82c..000000000
--- a/js/src/i18n/translations.json
+++ /dev/null
@@ -1 +0,0 @@
-{"en_US":{"© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks":"© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks","A validation email was sent to %{email}":"A validation email was sent to %{email}","About":"About","About this event":"About this event","About this instance":"About this instance","Add a new profile":"Add a new profile","Add to my calendar":"Add to my calendar","Are you going to this event?":"Are you going to this event?","Before you can login, you need to click on the link inside it to validate your account":"Before you can login, you need to click on the link inside it to validate your account","By %{ name }":"By %{ name }","Create a new event":"Create a new event","Create a new group":"Create a new group","Create group":"Create group","Create my event":"Create my event","Create my group":"Create my group","Create my profile":"Create my profile","Create token":"Create token","Create your communities and your events":"Create your communities and your events","Current":"Current","Delete":"Delete","Didn't receive the instructions ?":"Didn't receive the instructions ?","Edit":"Edit","Either the account is already validated, either the validation token is incorrect.":"Either the account is already validated, either the validation token is incorrect.","Event list":"Event list","Events":"Events","Events nearby you":"Events nearby you","Events you're going at":"Events you're going at","Features":"Features","Forgot your password ?":"Forgot your password ?","From the %{ startDate } at %{ startTime } to the %{ endDate } at %{ endTime }":"From the %{ startDate } at %{ startTime } to the %{ endDate } at %{ endTime }","Group List":"Group List","Groups":"Groups","iCal Feed":"iCal Feed","Identities":"Identities","If an account with this email exists, we just sent another confirmation email to %{email}":"If an account with this email exists, we just sent another confirmation email to %{email}","Join":"Join","Learn more on\n joinmobilizon.org":"Learn more on\n joinmobilizon.org","Leave":"Leave","Legal":"Legal","License":"License","Log in":"Log in","Log out":"Log out","Login":"Login","meditate a bit":"meditate a bit","Members":"Members","My account":"My account","No address defined":"No address defined","No events found":"No events found","No group found":"No group found","No groups found":"No groups found","Organized":"Organized","Organizer":"Organizer","Other stuff…":"Other stuff…","Password reset":"Password reset","Please be nice to each other":"Please be nice to each other","Please check you spam folder if you didn't receive the email.":"Please check you spam folder if you didn't receive the email.","Please read the full rules":"Please read the full rules","Private feeds":"Private feeds","public event":"public event","Public feeds":"Public feeds","Public iCal Feed":"Public iCal Feed","Public RSS/Atom Feed":"Public RSS/Atom Feed","Register":"Register","Register an account on Mobilizon!":"Register an account on Mobilizon!","Registration is currently closed.":"Registration is currently closed.","Resend confirmation email":"Resend confirmation email","Reset my password":"Reset my password","RSS/Atom Feed":"RSS/Atom Feed","Search":"Search","Search results: « %{ search } »":"Search results: « %{ search } »","Send confirmation email again":"Send confirmation email again","Send email to reset my password":"Send email to reset my password","Share this event":"Share this event","Show map":"Show map","Sign up":"Sign up","The %{ date } at %{ time }":"The %{ date } at %{ time }","The %{ date } from %{ startTime } to %{ endTime }":"The %{ date } from %{ startTime } to %{ endTime }","The event organizer didn't add any description.":"The event organizer didn't add any description.","These events may interest you":"These events may interest you","This instance isn't opened to registrations, but you can register on other instances.":"This instance isn't opened to registrations, but you can register on other instances.","Unknown error.":"Unknown error.","User logout":"User logout","We just sent an email to %{email}":"We just sent an email to %{email}","Welcome back %{username}":"Welcome back %{username}","Welcome back!":"Welcome back!","You announced that you're going to this event.":"You announced that you're going to this event.","You are already logged-in.":"You are already logged-in.","You are an organizer.":"You are an organizer.","You have one event in %{ days } days.":["You have one event in %{ days } days.","You have %{ count } events in %{ days } days"],"You have one event today.":["You have one event today.","You have %{ count } events today"],"You have one event tomorrow.":["You have one event tomorrow.","You have %{ count } events tomorrow"],"You need to login.":"You need to login.","You're not going to any event yet":"You're not going to any event yet","Your account has been validated":"Your account has been validated","Your account is being validated":"Your account is being validated","Your account is nearly ready, %{username}":"Your account is nearly ready, %{username}","Your local administrator resumed it's policy:":"Your local administrator resumed it's policy:","World map":"World map"},"fr_FR":{"© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks":"© Les contributeurs de Mobilizon %{date} - Fait avec Elixir, Phoenix, VueJS & et de l'amour et des semaines","A validation email was sent to %{email}":"Un email de validation a été envoyé à %{email}","About":"À propos","About this event":"À propos de cet événement","About this instance":"À propos de cette instance","Add a new profile":"Ajouter un nouveau profil","Add to my calendar":"Ajouter à mon agenda","Are you going to this event?":"Allez-vous à cet événement ?","Before you can login, you need to click on the link inside it to validate your account":"Avant que vous puissiez vous enregistrer, vous devez cliquer sur le lien à l'intérieur pour valider votre compte","By %{ name }":"Par %{name}","Create a new event":"Créer un nouvel événement","Create a new group":"Créer un nouveau groupe","Create group":"Créer un groupe","Create my event":"Créer mon événement","Create my group":"Créer mon groupe","Create my profile":"Créer mon profil","Create token":"Créer un jeton","Create your communities and your events":"Créer vos communautés et vos événements","Current":"Actuel","Delete":"Supprimer","Didn't receive the instructions ?":"Vous n'avez pas reçu les instructions ?","Edit":"Éditer","Either the account is already validated, either the validation token is incorrect.":"Soit le compte est déjà validé, soit le jeton de validation est incorrect.","Event list":"Liste d'événements","Events":"Événements","Events nearby you":"Événements près de chez vous","Events you're going at":"Événements auxquels vous vous rendez","Features":"Fonctionnalités","Forgot your password ?":"Mot de passe oublié ?","From the %{ startDate } at %{ startTime } to the %{ endDate } at %{ endTime }":"Du %{ startDate } à %{ startTime } au %{ endDate } à %{ endTime }","Group List":"Liste de groupes","Groups":"Groupes","iCal Feed":"Flux iCal","Identities":"Identités","If an account with this email exists, we just sent another confirmation email to %{email}":"Si un compte avec un tel email existe, nous venons juste d'envoyer un nouvel email de confirmation à %{email}","Join":"Rejoindre","Learn more on\n joinmobilizon.org":"En apprendre plus sur\n joinmobilizon.org","Leave":"Quitter","Legal":"Mentions légales","License":"License","Log in":"Se connecter","Log out":"Se déconnecter","Login":"Se connecter","meditate a bit":"méditez un peu","Members":"Membres","My account":"Mon compte","No address defined":"Aucune adresse définie","No events found":"Aucun événement trouvé","No group found":"Aucun groupe trouvé","No groups found":"Aucun groupe trouvé","Organized":"Organisés","Organizer":"Organisateur","Other stuff…":"Autres trucs…","Password reset":"Réinitialisation du mot de passe","Please be nice to each other":"Soyez sympas entre vous","Please check you spam folder if you didn't receive the email.":"Merci de vérifier votre dossier des indésirables si vous n'avez pas reçu l'email.","Please contact this instance's Mobilizon admin if you think this is a mistake.":"Veuillez contacter l'administrateur de cette instance Mobilizon si vous pensez qu’il s’agit d’une erreur.","Please make sure the address is correct and that the page hasn't been moved.":"Assurez‐vous que l’adresse est correcte et que la page n’a pas été déplacée.","Please read the full rules":"Merci de lire les règles complètes","Private feeds":"Flux privés","public event":"événement public","Public feeds":"Flux publics","Public iCal Feed":"Flux iCal public","Public RSS/Atom Feed":"Flux RSS/Atom public","Register":"S'inscrire","Register an account on Mobilizon!":"S'inscrire sur Mobilizon !","Registration is currently closed.":"Les inscriptions sont actuellement fermées.","Resend confirmation email":"Envoyer à nouveau l'email de confirmation","Reset my password":"Réinitialiser mon mot de passe","RSS/Atom Feed":"Flux RSS/Atom","Search":"Rechercher","Search results: « %{ search } »":"Résultats de recherche : « %{ search } »","Send confirmation email again":"Envoyer l'email de confirmation à nouveau","Send email to reset my password":"Envoyer un email pour réinitialiser mon mot de passe","Share this event":"Partager l'événement","Show map":"Afficher la carte","Sign up":"S'enregistrer","The %{ date } at %{ time }":"Le %{ date } à %{ time }","The %{ date } from %{ startTime } to %{ endTime }":"Le %{ date } de %{ startTime } à %{ endTime }","The event organizer didn't add any description.":"L'organisateur de l'événement n'a pas ajouté de description.","The page you're looking for doesn't exist.":"La page que vous recherchez n'existe pas.","These events may interest you":"Ces événements peuvent vous intéresser","This instance isn't opened to registrations, but you can register on other instances.":"Cette instance n'autorise pas les inscriptions, mais vous pouvez vous enregistrer sur d'autres instances.","Unknown error.":"Erreur inconnue.","User logout":"Déconnexion","We just sent an email to %{email}":"Nous venons d'envoyer un email à %{email}","Welcome back %{username}":"Bon retour %{username}","Welcome back!":"Bon retour !","You announced that you're going to this event.":"Vous avez annoncé vous rendre à cet événement.","You are already logged-in.":"Vous êtes déjà connecté.","You are an organizer.":"Vous êtes un organisateur.","You have one event in %{ days } days.":["Vous avez un événement dans %{ days } jours.","Vous avez %{ count } événements dans %{ days } jours"],"You have one event today.":["Vous avez un événement aujourd'hui.","Vous avez %{ count } événements aujourd'hui"],"You have one event tomorrow.":["Vous avez un événement demain.","Vous avez %{ count } événements demain"],"You need to login.":"Vous devez vous connecter.","You're not going to any event yet":"Vous n'allez à aucun événement pour le moment","Your account has been validated":"Votre compte a été validé","Your account is being validated":"Votre compte est en cours de validation","Your account is nearly ready, %{username}":"Votre compte est presque prêt, %{ username }","Your local administrator resumed it's policy:":"Votre administrateur local a résumé sa politique ainsi :","World map":"Carte mondiale","Search events, groups, etc.":"Rechercher des événements, des groupes, etc."}}
\ No newline at end of file
diff --git a/js/src/main.ts b/js/src/main.ts
index 25a5e6068..9f3d4c79c 100644
--- a/js/src/main.ts
+++ b/js/src/main.ts
@@ -2,28 +2,28 @@
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import Buefy from 'buefy';
-import GetTextPlugin from 'vue-gettext';
+import VueI18n from 'vue-i18n';
import App from '@/App.vue';
import router from '@/router';
import { apolloProvider } from './vue-apollo';
import { NotifierPlugin } from '@/plugins/notifier';
-
-const translations = require('@/i18n/translations.json');
+import filters from '@/filters';
+import messages from '@/i18n/index';
Vue.config.productionTip = false;
Vue.use(Buefy);
Vue.use(NotifierPlugin);
+Vue.use(filters);
const language = (window.navigator as any).userLanguage || window.navigator.language;
-Vue.use(GetTextPlugin, {
- translations,
- defaultLanguage: 'en_US',
- silent: true,
-});
+Vue.use(VueI18n);
-Vue.config.language = language.replace('-', '_');
+const i18n = new VueI18n({
+ locale: language.replace('-', '_'), // set locale
+ messages, // set locale messages
+});
/* eslint-disable no-new */
new Vue({
@@ -32,4 +32,5 @@ new Vue({
el: '#app',
template: '',
components: { App },
+ i18n,
});
diff --git a/js/src/plugins/notifier.ts b/js/src/plugins/notifier.ts
index 00f302e2b..add57d5fe 100644
--- a/js/src/plugins/notifier.ts
+++ b/js/src/plugins/notifier.ts
@@ -4,6 +4,7 @@ declare module 'vue/types/vue' {
interface Vue {
$notifier: {
success: (message: string) => void;
+ error: (message: string) => void;
};
}
}
@@ -16,7 +17,7 @@ export class Notifier {
}
success(message: string) {
- this.vue.prototype.$notification.open({
+ this.vue.prototype.$buefy.notification.open({
message,
duration: 5000,
position: 'is-bottom-right',
@@ -24,6 +25,16 @@ export class Notifier {
hasIcon: true,
});
}
+
+ error(message: string) {
+ this.vue.prototype.$buefy.notification.open({
+ message,
+ duration: 5000,
+ position: 'is-bottom-right',
+ type: 'is-danger',
+ hasIcon: true,
+ });
+ }
}
// tslint:disable
diff --git a/js/src/router/admin.ts b/js/src/router/admin.ts
new file mode 100644
index 000000000..58ed37e64
--- /dev/null
+++ b/js/src/router/admin.ts
@@ -0,0 +1,16 @@
+import { RouteConfig } from 'vue-router';
+import Dashboard from '@/views/Admin/Dashboard.vue';
+
+export enum AdminRouteName {
+ DASHBOARD = 'Dashboard',
+}
+
+export const adminRoutes: RouteConfig[] = [
+ {
+ path: '/admin',
+ name: AdminRouteName.DASHBOARD,
+ component: Dashboard,
+ props: true,
+ meta: { requiredAuth: true },
+ },
+];
diff --git a/js/src/router/index.ts b/js/src/router/index.ts
index cca5e3726..44534e842 100644
--- a/js/src/router/index.ts
+++ b/js/src/router/index.ts
@@ -5,9 +5,11 @@ import Home from '@/views/Home.vue';
import { UserRouteName, userRoutes } from './user';
import { EventRouteName, eventRoutes } from '@/router/event';
import { ActorRouteName, actorRoutes, MyAccountRouteName } from '@/router/actor';
+import { AdminRouteName, adminRoutes } from '@/router/admin';
import { ErrorRouteName, errorRoutes } from '@/router/error';
import { authGuardIfNeeded } from '@/router/guards/auth-guard';
import Search from '@/views/Search.vue';
+import { ModerationRouteName, moderationRoutes } from '@/router/moderation';
Vue.use(Router);
@@ -35,6 +37,8 @@ export const RouteName = {
...EventRouteName,
...ActorRouteName,
...MyAccountRouteName,
+ ...AdminRouteName,
+ ...ModerationRouteName,
...ErrorRouteName,
};
@@ -46,6 +50,8 @@ const router = new Router({
...userRoutes,
...eventRoutes,
...actorRoutes,
+ ...adminRoutes,
+ ...moderationRoutes,
...errorRoutes,
{
path: '/search/:searchTerm/:searchType?',
diff --git a/js/src/router/moderation.ts b/js/src/router/moderation.ts
new file mode 100644
index 000000000..528345242
--- /dev/null
+++ b/js/src/router/moderation.ts
@@ -0,0 +1,34 @@
+import { RouteConfig } from 'vue-router';
+import ReportList from '@/views/Moderation/ReportList.vue';
+import Report from '@/views/Moderation/Report.vue';
+import Logs from '@/views/Moderation/Logs.vue';
+
+export enum ModerationRouteName {
+ REPORTS = 'Reports',
+ REPORT = 'Report',
+ LOGS = 'Logs',
+}
+
+export const moderationRoutes: RouteConfig[] = [
+ {
+ path: '/moderation/reports/:filter?',
+ name: ModerationRouteName.REPORTS,
+ component: ReportList,
+ props: true,
+ meta: { requiredAuth: true },
+ },
+ {
+ path: '/moderation/report/:reportId',
+ name: ModerationRouteName.REPORT,
+ component: Report,
+ props: true,
+ meta: { requiredAuth: true },
+ },
+ {
+ path: '/moderation/logs',
+ name: ModerationRouteName.LOGS,
+ component: Logs,
+ props: true,
+ meta: { requiredAuth: true },
+ },
+];
diff --git a/js/src/types/actor/actor.model.ts b/js/src/types/actor/actor.model.ts
index 9f04f1da2..b83503bba 100644
--- a/js/src/types/actor/actor.model.ts
+++ b/js/src/types/actor/actor.model.ts
@@ -36,7 +36,7 @@ export class Actor implements IActor {
return `@${this.preferredUsername}${domain}`;
}
- displayName(): string {
+ public displayName(): string {
return this.name != null && this.name !== '' ? this.name : this.usernameWithDomain();
}
}
diff --git a/js/src/types/admin.model.ts b/js/src/types/admin.model.ts
new file mode 100644
index 000000000..226b2fb59
--- /dev/null
+++ b/js/src/types/admin.model.ts
@@ -0,0 +1,9 @@
+import { IEvent } from '@/types/event.model';
+
+export interface IDashboard {
+ lastPublicEventPublished: IEvent;
+ numberOfUsers: number;
+ numberOfEvents: number;
+ numberOfComments: number;
+ numberOfReports: number;
+}
diff --git a/js/src/types/current-user.model.ts b/js/src/types/current-user.model.ts
index 0c1f6277b..0bafaac51 100644
--- a/js/src/types/current-user.model.ts
+++ b/js/src/types/current-user.model.ts
@@ -1,5 +1,12 @@
+export enum ICurrentUserRole {
+ USER = 'USER',
+ MODERATOR = 'MODERATOR',
+ ADMINISTRATOR = 'ADMINISTRATOR',
+}
+
export interface ICurrentUser {
id: number;
email: string;
isLoggedIn: boolean;
+ role: ICurrentUserRole;
}
diff --git a/js/src/types/event.model.ts b/js/src/types/event.model.ts
index b92244dde..79b3621d0 100644
--- a/js/src/types/event.model.ts
+++ b/js/src/types/event.model.ts
@@ -90,8 +90,12 @@ export interface IEvent {
picture: IPicture | null;
- organizerActor: IActor;
+ organizerActor?: IActor;
attributedTo: IActor;
+ participantStats: {
+ approved: number;
+ unapproved: number;
+ };
participants: IParticipant[];
relatedEvents: IEvent[];
@@ -117,15 +121,15 @@ export interface IEventOptions {
}
export class EventOptions implements IEventOptions {
- maximumAttendeeCapacity: number = 0;
- remainingAttendeeCapacity: number = 0;
- showRemainingAttendeeCapacity: boolean = false;
+ maximumAttendeeCapacity = 0;
+ remainingAttendeeCapacity = 0;
+ showRemainingAttendeeCapacity = false;
offers: IOffer[] = [];
participationConditions: IParticipationCondition[] = [];
attendees: string[] = [];
- program: string = '';
- commentModeration: CommentModeration = CommentModeration.ALLOW_ALL;
- showParticipationPrice: boolean = false;
+ program = '';
+ commentModeration = CommentModeration.ALLOW_ALL;
+ showParticipationPrice = false;
}
export class EventModel implements IEvent {
@@ -154,12 +158,13 @@ export class EventModel implements IEvent {
publishAt = new Date();
+ participantStats = { approved: 0, unapproved: 0 };
participants: IParticipant[] = [];
relatedEvents: IEvent[] = [];
attributedTo = new Actor();
- organizerActor = new Actor();
+ organizerActor?: IActor;
tags: ITag[] = [];
options: IEventOptions = new EventOptions();
@@ -200,6 +205,25 @@ export class EventModel implements IEvent {
this.physicalAddress = hash.physicalAddress;
this.tags = hash.tags;
- this.options = hash.options;
+ if (hash.options) this.options = hash.options;
+ }
+
+ toEditJSON () {
+ return {
+ id: this.id,
+ title: this.title,
+ description: this.description,
+ beginsOn: this.beginsOn.toISOString(),
+ endsOn: this.endsOn ? this.endsOn.toISOString() : null,
+ status: this.status,
+ visibility: this.visibility,
+ tags: this.tags.map(t => t.title),
+ picture: this.picture,
+ onlineAddress: this.onlineAddress,
+ phoneAddress: this.phoneAddress,
+ category: this.category,
+ physicalAddress: this.physicalAddress,
+ options: this.options,
+ };
}
}
diff --git a/js/src/types/report.model.ts b/js/src/types/report.model.ts
new file mode 100644
index 000000000..b1cea059f
--- /dev/null
+++ b/js/src/types/report.model.ts
@@ -0,0 +1,47 @@
+import { IActor, IPerson } from '@/types/actor';
+import { IEvent } from '@/types/event.model';
+
+export enum ReportStatusEnum {
+ OPEN = 'OPEN',
+ CLOSED = 'CLOSED',
+ RESOLVED = 'RESOLVED',
+}
+
+export interface IReport extends IActionLogObject {
+ id: string;
+ reported: IActor;
+ reporter: IPerson;
+ event?: IEvent;
+ content: string;
+ notes: IReportNote[];
+ insertedAt: Date;
+ updatedAt: Date;
+ status: ReportStatusEnum;
+}
+
+export interface IReportNote extends IActionLogObject{
+ id: string;
+ content: string;
+ moderator: IActor;
+}
+
+export interface IActionLogObject {
+ id: string;
+}
+
+export enum ActionLogAction {
+ NOTE_CREATION = 'NOTE_CREATION',
+ NOTE_DELETION = 'NOTE_DELETION',
+ REPORT_UPDATE_CLOSED = 'REPORT_UPDATE_CLOSED',
+ REPORT_UPDATE_OPENED = 'REPORT_UPDATE_OPENED',
+ REPORT_UPDATE_RESOLVED = 'REPORT_UPDATE_RESOLVED',
+ EVENT_DELETION = 'EVENT_DELETION',
+}
+
+export interface IActionLog {
+ id: string;
+ object: IReport|IReportNote|IEvent;
+ actor: IActor;
+ action: ActionLogAction;
+ insertedAt: Date;
+}
diff --git a/js/src/utils/auth.ts b/js/src/utils/auth.ts
index 29698bf7a..146b6bc80 100644
--- a/js/src/utils/auth.ts
+++ b/js/src/utils/auth.ts
@@ -1,27 +1,50 @@
-import { AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN, AUTH_USER_EMAIL, AUTH_USER_ID } from '@/constants';
+import {
+ AUTH_ACCESS_TOKEN,
+ AUTH_REFRESH_TOKEN,
+ AUTH_USER_ACTOR_ID,
+ AUTH_USER_EMAIL,
+ AUTH_USER_ID,
+ AUTH_USER_ROLE,
+} from '@/constants';
import { ILogin, IToken } from '@/types/login.model';
import { UPDATE_CURRENT_USER_CLIENT } from '@/graphql/user';
import { onLogout } from '@/vue-apollo';
import ApolloClient from 'apollo-client';
+import { ICurrentUserRole } from '@/types/current-user.model';
+import { IPerson } from '@/types/actor';
+import { UPDATE_CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
export function saveUserData(obj: ILogin) {
localStorage.setItem(AUTH_USER_ID, `${obj.user.id}`);
localStorage.setItem(AUTH_USER_EMAIL, obj.user.email);
+ localStorage.setItem(AUTH_USER_ROLE, obj.user.role);
saveTokenData(obj);
}
+export function saveActorData(obj: IPerson) {
+ localStorage.setItem(AUTH_USER_ACTOR_ID, `${obj.id}`);
+}
+
export function saveTokenData(obj: IToken) {
localStorage.setItem(AUTH_ACCESS_TOKEN, obj.accessToken);
localStorage.setItem(AUTH_REFRESH_TOKEN, obj.refreshToken);
}
export function deleteUserData() {
- for (const key of [AUTH_USER_ID, AUTH_USER_EMAIL, AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN]) {
+ for (const key of [AUTH_USER_ID, AUTH_USER_EMAIL, AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN, AUTH_USER_ROLE, AUTH_USER_ACTOR_ID]) {
localStorage.removeItem(key);
}
}
+export async function changeIdentity(apollo: ApolloClient, identity: IPerson) {
+ await apollo.mutate({
+ mutation: UPDATE_CURRENT_ACTOR_CLIENT,
+ variables: identity,
+ });
+ saveActorData(identity);
+}
+
export function logout(apollo: ApolloClient) {
apollo.mutate({
mutation: UPDATE_CURRENT_USER_CLIENT,
@@ -29,6 +52,7 @@ export function logout(apollo: ApolloClient) {
id: null,
email: null,
isLoggedIn: false,
+ role: ICurrentUserRole.USER,
},
});
diff --git a/js/src/views/Account/IdentityPicker.vue b/js/src/views/Account/IdentityPicker.vue
new file mode 100644
index 000000000..89a182ab2
--- /dev/null
+++ b/js/src/views/Account/IdentityPicker.vue
@@ -0,0 +1,61 @@
+
+
+
{{ currentIdentity.name || `@${currentIdentity.preferredUsername}` }}
+
+ {{ $t('Change') }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/js/src/views/Account/MyAccount.vue b/js/src/views/Account/MyAccount.vue
index 98802cc05..d09514e45 100644
--- a/js/src/views/Account/MyAccount.vue
+++ b/js/src/views/Account/MyAccount.vue
@@ -1,9 +1,9 @@
-
+
@@ -31,7 +31,7 @@
+
-
diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue
index 5467b6ba5..4c36f0251 100644
--- a/js/src/views/Event/Event.vue
+++ b/js/src/views/Event/Event.vue
@@ -18,14 +18,20 @@
{{ event.title }}
+
+ {{ $tc('One person is going', event.participantStats.approved, {approved: event.participantStats.approved}) }}
+
+
+ {{ $tc('You and one other person are going to this event', event.participantStats.approved - 1, {approved: event.participantStats.approved - 1}) }}
+
@@ -35,7 +41,7 @@
{{ event.category }}
{{ tag.title }}
- public event
+ {{ $t('public event') }}
@@ -45,7 +51,7 @@
- Add to my calendar
+ {{ $t('Add to my calendar') }}
@@ -53,24 +59,29 @@