Merge branch 'feature/handle-errors-better' into 'master'

Handle errors better

Closes #209

See merge request framasoft/mobilizon!259
This commit is contained in:
Thomas Citharel 2019-10-13 14:20:39 +02:00
commit e8e6c98314
13 changed files with 214 additions and 78 deletions

View File

@ -15,10 +15,12 @@
"Administration": "Administration",
"All the places have already been taken": "All the places have been taken|One place is still available|{places} places are still available",
"Allow all comments": "Allow all comments",
"An error has occurred.": "An error has occurred.",
"Approve": "Approve",
"Are you sure you want to cancel the event creation? You'll lose all modifications.": "Are you sure you want to cancel the event creation? You'll lose all modifications.",
"Are you sure you want to cancel the event edition? You'll lose all modifications.": "Are you sure you want to cancel the event edition? You'll lose all modifications.",
"Are you sure you want to cancel your participation at event \"{title}\"?": "Are you sure you want to cancel your participation at event \"{title}\"?",
"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.",
"Avatar": "Avatar",
"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}",
@ -54,8 +56,10 @@
"Create your communities and your events": "Create your communities and your events",
"Create": "Create",
"Creator": "Creator",
"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",
@ -71,12 +75,15 @@
"Email": "Email",
"Ends on…": "Ends on…",
"Enter the link URL": "Enter the link URL",
"Error while communicating with the server.": "Error while communicating with the server.",
"Error while saving report.": "Error while saving report.",
"Error while validating account": "Error while validating account",
"Event already passed": "Event already passed",
"Event cancelled": "Event cancelled",
"Event creation": "Event creation",
"Event edition": "Event edition",
"Event list": "Event list",
"Event not found.": "Event not found.",
"Event to be confirmed": "Event to be confirmed",
"Event {eventTitle} deleted": "Event {eventTitle} deleted",
"Event {eventTitle} reported": "Event {eventTitle} reported",
@ -155,6 +162,9 @@
"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)",
"Page not found": "Page not found",
"Participant already was rejected.": "Participant already was rejected.",
"Participant has already been approved as participant.": "Participant has already been approved as participant.",
"Participants": "Participants",
"Participate": "Participate",
"Participation approval": "Participation approval",
@ -170,6 +180,7 @@
"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 refresh the page and retry.": "Please refresh the page and retry.",
"Please type at least 5 characters": "Please type at least 5 characters",
"Postal Code": "Postal Code",
"Private event": "Private event",
@ -214,11 +225,17 @@
"Street": "Street",
"Tentative: Will be confirmed later": "Tentative: Will be confirmed later",
"The content came from another server. Transfer an anonymous copy of the report?": "The content came from another server. Transfer an anonymous copy of the report ?",
"The draft event has been updated": "The draft event has been updated",
"The event has been created as a draft": "The event has been created as a draft",
"The event has been published": "The event has been published",
"The event has been updated and published": "The event has been updated and published",
"The event has been updated": "The event has been updated",
"The event organizer didn't add any description.": "The event organizer didn't add any description.",
"The event title will be ellipsed.": "The event title will be ellipsed.",
"The page you're looking for doesn't exist.": "The page you're looking for doesn't exist.",
"The password was successfully changed": "The password was successfully changed",
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "The report will be sent to the moderators of your instance. You can explain why you report this content below.",
"There are {participants} participants.": "There are {participants} participants.",
"These events may interest you": "These events may interest you",
"This installation (called “instance“) can easily {interconnect}, thanks to {protocol}.": "This installation (called “instance“) can easily {interconnect}, thanks to {protocol}.",
"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.",
@ -227,6 +244,7 @@
"Title": "Title",
"To achieve your registration, please create a first identity profile.": "To achieve your registration, please create a first identity profile.",
"To change the world, change the software": "To change the world, change the software",
"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}\"",
"Transfer to {outsideDomain}": "Transfer to {outsideDomain}",
"Unfortunately, this instance isn't opened to registrations": "Unfortunately, this instance isn't opened to registrations",
@ -254,9 +272,11 @@
"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 are already a participant of this event.": "You are already a participant of this event.",
"You are already logged-in.": "You are already logged-in.",
"You can add tags by hitting the Enter key or by adding a comma": "You can add tags by hitting the Enter key or by adding a comma",
"You have been disconnected": "You have been disconnected",
"You have cancelled your participation": "You have cancelled your participation",
"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",
@ -266,6 +286,8 @@
"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:",
"Your participation has been confirmed": "Your participation has been confirmed",
"Your participation has been requested": "Your participation has been requested",
"a decentralised federation protocol": "a decentralised federation protocol",
"e.g. 10 Rue Jangot": "e.g. 10 Rue Jangot",
"firstDayOfWeek": "0",

View File

@ -7,8 +7,6 @@
"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 some tags": "Ajouter des tags",
"Add to my calendar": "Ajouter à mon agenda",
@ -17,8 +15,8 @@
"Administration": "Administration",
"All the places have already been taken": "Toutes les places ont été prises|Une place est encore disponible|{places} places sont encore disponibles",
"Allow all comments": "Autoriser tous les commentaires",
"An error has occurred.": "Une erreur est survenue.",
"Approve": "Approuver",
"Are you going to this event?": "Allez-vous à cet événement ?",
"Are you sure you want to cancel the event creation? You'll lose all modifications.": "Étes-vous certain⋅e de vouloir annuler la création de l'événement ? Vous allez perdre toutes vos modifications.",
"Are you sure you want to cancel the event edition? You'll lose all modifications.": "Étes-vous certain⋅e de vouloir annuler l'édition de l'événement ? Vous allez perdre toutes vos modifications.",
"Are you sure you want to cancel your participation at event \"{title}\"?": "Êtes-vous certain⋅e de vouloir annuler votre participation à l'événement « {title} » ?",
@ -58,7 +56,6 @@
"Create your communities and your events": "Créer vos communautés et vos événements",
"Create": "Créer",
"Creator": "Créateur",
"Current": "Actuel",
"Delete event": "Supprimer un événement",
"Delete this identity": "Supprimer cette identité",
"Delete your identity": "Supprimer votre identité",
@ -67,11 +64,9 @@
"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é",
"Do you want to participate in {title}?": "Voulez-vous participer à {title} ?",
"Draft": "Brouillon",
"Drafts": "Brouillons",
"Edit": "Éditer",
@ -79,20 +74,20 @@
"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",
"Enter the link URL": "Entrez l'URL du lien",
"Error while communicating with the server.": "Erreur de communication avec le serveur.",
"Error while saving report.": "Erreur lors de l'enregistrement du signalement.",
"Error while validating account": "Erreur lors de la validation du compte",
"Event already passed": "Événement déjà passé",
"Event cancelled": "Événement annulé",
"Event creation": "Création d'événement",
"Event edition": "Édition d'événement",
"Event list": "Liste d'événements",
"Event not found.": "Événement non trouvé.",
"Event to be confirmed": "Événement à confirmer",
"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",
"Exclude": "Exclure",
"Explore": "Explorer",
@ -111,46 +106,34 @@
"Group full name": "Nom complet du groupe",
"Group name": "Nom du groupe",
"Group {displayName} created": "Groupe {displayName} créé",
"Group": "Groupe",
"Groups": "Groupes",
"Headline picture": "Image à la une",
"I create an identity": "Je crée une identité",
"I participate": "Je participe",
"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é.",
"In the meantime, please consider that the software is not (yet) finished. More information {onBlog}.": "D'ici là, veuillez considérer que le logiciel n'est pas (encore) fini. Plus d'informations {onBlog}.",
"Installing Mobilizon will allow communities to free themselves from the services of tech giants by creating <b>their own event platform</b>.": "Installer Mobilizon permettra à des collectifs de sémanciper des outils des géants du web en créant <b>leur propre plateforme dévénements</b>.",
"Join event {title}": "Rejoindre {title}",
"Join {instance}, a Mobilizon instance": "Rejoignez {instance}, une instance Mobilizon",
"Join": "Rejoindre",
"Last published event": "Dernier événement publié",
"Last week": "La semaine dernière",
"Learn more about Mobilizon": "En apprendre plus à propos de Mobilizon",
"Learn more on {0}": "En apprendre plus sur {0}",
"Learn more on": "En apprendre plus sur",
"Learn more": "En apprendre plus",
"Leave event": "Annuler ma participation à l'événement",
"Leave": "Quitter",
"Leaving event \"{title}\"": "Annuler ma participation à l'événement",
"Legal": "Mentions légales",
"Let's create a new common": "Créons un nouveau Common",
"License": "Licence",
"Limited number of places": "Nombre de places limité",
"Limited places": "Places limitées",
"Load more": "Voir plus",
"Loading…": "Chargement en cours…",
"Locality": "Commune",
"Log in": "Se connecter",
"Log out": "Se déconnecter",
"Login on Mobilizon!": "Se connecter sur Mobilizon !",
"Login": "Se connecter",
"Manage participants": "Gérer les participants",
"Manage participations": "Gérer les participations",
"Members": "Membres",
"Mobilizon is a free/libre software that will allow communities to create <b>their own spaces</b> to publish events in order to better emancipate themselves from tech giants.": "Mobilizon est un logiciel libre qui permettra à des communautés de <b>créer leurs propres espaces</b> de publication dévénements, afin de mieux sémanciper des géants du web.",
@ -166,21 +149,22 @@
"No events found": "Aucun événement trouvé",
"No group found": "Aucun groupe trouvé",
"No groups found": "Aucun groupe trouvé",
"No participants yet.": "Pas de participants pour le moment.",
"No results for \"{queryText}\"": "Pas de résultats pour « {queryText} »",
"Number of places": "Nombre de places",
"Old password": "Ancien mot de passe",
"On {date} from {startTime} to {endTime}": "On {date} de {startTime} à {endTime}",
"On {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}",
"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",
"Organize and take action, freely": "S'organiser et agir, librement",
"Organized by {name}": "Organisé par {name}",
"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)",
"Page not found": "Page non trouvée",
"Participant already was rejected.": "Le participant a déjà été refusé.",
"Participant has already been approved as participant.": "Le participant a déjà été approuvé en tant que participant.",
"Participants": "Participants",
"Participate": "Participer",
"Participation approval": "Validation des participations",
@ -196,11 +180,11 @@
"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 quil sagit dune erreur.",
"Please make sure the address is correct and that the page hasn't been moved.": "Assurezvous que ladresse est correcte et que la page na pas été déplacée.",
"Please read the full rules": "Merci de lire les règles complètes",
"Please refresh the page and retry.": "Merci de rafraîchir la page puis réessayer.",
"Please type at least 5 characters": "Merci d'entrer au moins 5 caractères",
"Postal Code": "Code postal",
"Private event": "Événement privé",
"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 event": "Événement public",
@ -228,8 +212,6 @@
"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",
"Send me an email to reset my password": "Envoyez-moi un email pour réinitialiser mon mot de passe",
"Send me the confirmation email once again": "Envoyez-moi l'email de confirmation encore une fois",
"Send the report": "Envoyer le signalement",
@ -243,14 +225,16 @@
"Street": "Rue",
"Tentative: Will be confirmed later": "Provisoire : sera confirmé plus tard",
"The content came from another server. Transfer an anonymous copy of the report?": "Le contenu provient d'une autre instance. Transférer une copie anonyme du signalement ?",
"The event came from another instance. Your participation will be confirmed after we confirm it with the other instance.": "L'événement provient d'une autre instance. Votre participation sera confirmée après que nous ayons la confirmation de l'autre instance.",
"The draft event has been updated": "L'événement brouillon a été mis à jour",
"The event has been created as a draft": "L'événement a été créé en tant que brouillon",
"The event has been published": "L'événement a été publié",
"The event has been updated and published": "L'événement a été mis à jour et publié",
"The event has been updated": "L'événement a été mis à jour",
"The event organizer didn't add any description.": "L'organisateur de l'événement n'a pas ajouté de description.",
"The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved": "L'organisateur⋅ice de l'événement a choisi d'approuver manuellement les participations à cet événement. Vous recevrez une notification lorsque votre participation sera approuvée",
"The event title will be ellipsed.": "Le titre de l'événement sera ellipsé.",
"The page you're looking for doesn't exist.": "La page que vous recherchez n'existe pas.",
"The password was successfully changed": "Le mot de passe a été changé avec succès",
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "Le signalement sera envoyé aux modérateur⋅ices de votre instance. Vous pouvez expliquer pourquoi vous signalez ce contenu ci-dessous.",
"The {date} at {time}": "Le {date} à {time}",
"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 installation (called “instance“) can easily {interconnect}, thanks to {protocol}.": "Cette installation (appelée “instance“) peut facilement {interconnect}, grâce à {protocol}.",
@ -270,7 +254,6 @@
"Upcoming": "À venir",
"Update event {name}": "Éditer l'événement {name}",
"Update my event": "Éditer mon événement",
"User logout": "Déconnexion",
"Username": "Pseudo",
"Users": "Utilisateurs",
"View event page": "Voir la page de l'événement",
@ -289,21 +272,22 @@
"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 a participant of this event.": "Vous participez déjà à cet événement.",
"You are already logged-in.": "Vous êtes déjà connecté.",
"You are an organizer.": "Vous êtes un organisateur.",
"You can add tags by hitting the Enter key or by adding a comma": "Vous pouvez ajouter des tags en appuyant sur la touche Entrée ou bien en ajoutant une virgule",
"You have been disconnected": "Vous avez été déconnecté⋅e",
"You have cancelled your participation": "Vous avez annulé votre participation",
"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",
"You're organizing this event": "Vous organisez cet événement",
"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 :",
"Your participation has been confirmed": "Votre participation a été confirmée",
"Your participation has been requested": "Votre participation a été demandée",
"a decentralised federation protocol": "un protocole de fédération décentralisée",
"e.g. 10 Rue Jangot": "par exemple : 10 Rue Jangot",
"firstDayOfWeek": "1",
@ -315,7 +299,6 @@
"respect of the fundamental freedoms": "le respect des libertés fondamentales",
"with another identity…": "avec une autre identité…",
"with {identity}": "avec {identity}",
"{actor}'s avatar": "Avatar de {actor}",
"{approved} / {total} seats": "{approved} / {total} places",
"{count} participants": "Un⋅e participant⋅e|{count} participant⋅e⋅s",
"{count} requests waiting": "Un⋅e demande en attente|{count} demandes en attente",

View File

@ -17,7 +17,7 @@
"Upcoming": "Venents",
"Unknown error.": "Error desconeguda.",
"Title": "Títol",
"The {date} from {startTime} to {endTime}": "Lo {date} de {startTime} fins a {endTime}",
"On {date} from {startTime} to {endTime}": "Lo {date} de {startTime} fins a {endTime}",
"The {date} at {time}": "Lo {date} a {time}",
"Street": "Carrièra",
"Status": "Estat",

View File

@ -2,15 +2,14 @@
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import Buefy from 'buefy';
import VueI18n from 'vue-i18n';
import Component from 'vue-class-component';
import App from '@/App.vue';
import router from '@/router';
import { apolloProvider } from './vue-apollo';
import { NotifierPlugin } from '@/plugins/notifier';
import filters from '@/filters';
import messages from '@/i18n/index';
import VueMeta from 'vue-meta';
import { i18n } from '@/utils/i18n';
Vue.config.productionTip = false;
@ -19,16 +18,6 @@ Vue.use(NotifierPlugin);
Vue.use(filters);
Vue.use(VueMeta);
const language = (window.navigator as any).userLanguage || window.navigator.language;
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: language.split('-')[0], // set locale
messages, // set locale messages
fallbackLocale: 'en_US',
});
// Register the router hooks with their names
Component.registerHooks([
'beforeRouteEnter',

View File

@ -74,11 +74,15 @@ const router = new Router({
meta: { requiredAuth: false },
},
{
path: '*',
path: '/404',
name: RouteName.PAGE_NOT_FOUND,
component: PageNotFound,
meta: { requiredAuth: false },
},
{
path: '*',
redirect: { name: RouteName.PAGE_NOT_FOUND },
},
],
});

View File

@ -86,6 +86,13 @@ export enum CommentModeration {
CLOSED = 'CLOSED',
}
export interface IEventParticipantStats {
approved: number;
unapproved: number;
rejected: number;
participants: number;
}
export interface IEvent {
id?: string;
uuid: string;
@ -108,12 +115,7 @@ export interface IEvent {
organizerActor?: IActor;
attributedTo: IActor;
participantStats: {
approved: number;
unapproved: number;
rejected: number;
participants: number;
};
participantStats: IEventParticipantStats;
participants: IParticipant[];
relatedEvents: IEvent[];

47
js/src/utils/errors.ts Normal file
View File

@ -0,0 +1,47 @@
import { i18n } from '@/utils/i18n';
export const refreshSuggestion = i18n.t('Please refresh the page and retry.') as string;
export const defaultError: IError = {
match: / /,
value: i18n.t('An error has occurred.') as string,
};
export interface IError { match: RegExp; value: string; suggestRefresh?: boolean; }
export const errors: IError[] = [
{
match: /^Event with UUID .* not found$/,
value: i18n.t('Page not found') as string,
suggestRefresh: false,
},
{
match: /^Event not found$/,
value: i18n.t('Event not found.') as string,
},
{
match: /^Event with this ID .* doesn't exist$/,
value: i18n.t('Event not found.') as string,
},
{
match: /^Error while saving report$/,
value: i18n.t('Error while saving report.') as string,
},
{
match: /^Participant already has role rejected$/,
value: i18n.t('Participant already was rejected.') as string,
},
{
match: /^Participant already has role participant$/,
value: i18n.t('Participant has already been approved as participant.') as string,
},
{
match: /^You are already a participant of this event$/,
value: i18n.t('You are already a participant of this event.') as string,
},
{
match: /NetworkError when attempting to fetch resource.$/,
value: i18n.t('Error while communicating with the server.') as string,
},
];

13
js/src/utils/i18n.ts Normal file
View File

@ -0,0 +1,13 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import messages from '@/i18n/index';
const language = (window.navigator as any).userLanguage || window.navigator.language;
Vue.use(VueI18n);
export const i18n = new VueI18n({
locale: language.split('-')[0], // set locale
messages, // set locale messages
fallbackLocale: 'en_US',
});

View File

@ -385,6 +385,14 @@ export default class EditEvent extends Vue {
refetchQueries: ({ data: { createEvent } }) => this.postRefetchQueries(createEvent),
});
this.$buefy.notification.open({
message: (this.event.draft ?
this.$i18n.t('The event has been created as a draft') :
this.$i18n.t('The event has been published')) as string,
type: 'is-success',
position: 'is-bottom-right',
duration: 5000,
});
await this.$router.push({
name: 'Event',
params: { uuid: data.createEvent.uuid },
@ -403,6 +411,12 @@ export default class EditEvent extends Vue {
refetchQueries: ({ data: { updateEvent } }) => this.postRefetchQueries(updateEvent),
});
this.$buefy.notification.open({
message: this.updateEventMessage,
type: 'is-success',
position: 'is-bottom-right',
duration: 5000,
});
await this.$router.push({
name: 'Event',
params: { uuid: this.eventId as string },
@ -412,6 +426,11 @@ export default class EditEvent extends Vue {
}
}
get updateEventMessage(): string {
if (this.unmodifiedEvent.draft && !this.event.draft) return this.$i18n.t('The event has been updated and published') as string;
return (this.event.draft ? this.$i18n.t('The draft event has been updated') : this.$i18n.t('The event has been updated')) as string;
}
/**
* Put in cache the updated or created event.
* If the event is not a draft anymore, also put in cache the participation

View File

@ -54,7 +54,7 @@ import {ParticipantRole} from "@/types/event.model";
</div>
<div class="metadata columns">
<div class="column is-three-quarters-desktop">
<p class="tags" v-if="event.tags.length > 0">
<p class="tags">
<b-tag type="is-warning" size="is-medium" v-if="event.draft">{{ $t('Draft') }}</b-tag>
<span class="event-status" v-if="event.status !== EventStatus.CONFIRMED">
<b-tag type="is-warning" v-if="event.status === EventStatus.TENTATIVE">{{ $t('Event to be confirmed') }}</b-tag>
@ -64,7 +64,7 @@ import {ParticipantRole} from "@/types/event.model";
<b-tag type="is-info" v-if="event.visibility === EventVisibility.PUBLIC">{{ $t('Public event') }}</b-tag>
<b-tag type="is-info" v-if="event.visibility === EventVisibility.UNLISTED">{{ $t('Private event') }}</b-tag>
</span>
<b-tag type="is-success" v-if="event.tags" v-for="tag in event.tags" :key="tag.title">{{ tag.title }}</b-tag>
<b-tag type="is-success" v-if="event.tags && event.tags.length > 0" v-for="tag in event.tags" :key="tag.title">{{ tag.title }}</b-tag>
<span v-if="event.tags > 0"></span>
</p>
<div class="date-and-add-to-calendar">
@ -488,7 +488,7 @@ export default class Event extends EventMixin {
}
async handleErrors(errors: GraphQLError) {
if (errors[0].message.includes('not found')) {
if (errors[0].message.includes('not found') || errors[0].message.includes('has invalid value $uuid')) {
await this.$router.push({ name: RouteName.PAGE_NOT_FOUND });
}
}

View File

@ -1,6 +1,9 @@
import {ParticipantRole} from "@/types/event.model";
import {ParticipantRole} from "@/types/event.model";
import {ParticipantRole} from "@/types/event.model";
<template>
<main class="container">
<b-tabs type="is-boxed" v-if="event">
<b-tabs type="is-boxed" v-if="event" v-model="activeTab">
<b-tab-item>
<template slot="header">
<b-icon icon="account-multiple"></b-icon>
@ -69,9 +72,9 @@
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { IEvent, IParticipant, Participant, ParticipantRole } from '@/types/event.model';
import { UPDATE_PARTICIPANT, PARTICIPANTS } from '@/graphql/event';
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { IEvent, IEventParticipantStats, IParticipant, Participant, ParticipantRole } from '@/types/event.model';
import { PARTICIPANTS, UPDATE_PARTICIPANT } from '@/graphql/event';
import ParticipantCard from '@/components/Account/ParticipantCard.vue';
import { CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
import { IPerson } from '@/types/actor';
@ -96,6 +99,9 @@ import { IPerson } from '@/types/actor';
actorId: this.currentActor.id,
};
},
skip() {
return !this.currentActor.id;
},
},
organizers: {
query: PARTICIPANTS,
@ -109,6 +115,9 @@ import { IPerson } from '@/types/actor';
};
},
update: data => data.event.participants.map(participation => new Participant(participation)),
skip() {
return !this.currentActor.id;
},
},
queue: {
query: PARTICIPANTS,
@ -122,6 +131,9 @@ import { IPerson } from '@/types/actor';
};
},
update: data => data.event.participants.map(participation => new Participant(participation)),
skip() {
return !this.currentActor.id;
},
},
rejected: {
query: PARTICIPANTS,
@ -135,6 +147,9 @@ import { IPerson } from '@/types/actor';
};
},
update: data => data.event.participants.map(participation => new Participant(participation)),
skip() {
return !this.currentActor.id;
},
},
},
})
@ -143,7 +158,6 @@ export default class Participants extends Vue {
page: number = 1;
limit: number = 10;
// participants: IParticipant[] = [];
organizers: IParticipant[] = [];
queue: IParticipant[] = [];
rejected: IParticipant[] = [];
@ -153,22 +167,29 @@ export default class Participants extends Vue {
currentActor!: IPerson;
hasMoreParticipants: boolean = false;
activeTab: number = 0;
get participants(): IParticipant[] {
return this.event.participants.map(participant => new Participant(participant));
}
get participantStats(): Object {
get participantStats(): IEventParticipantStats | null {
if (!this.event) return null;
return this.event.participantStats;
}
get participantsAndCreators(): IParticipant[] {
if (this.event) {
return [...this.organizers, ...this.participants];
return [...this.organizers, ...this.event.participants]
.filter(participant => [ParticipantRole.PARTICIPANT, ParticipantRole.CREATOR].includes(participant.role));
}
return [];
}
@Watch('participantStats', { deep: true })
watchParticipantStats(stats: IEventParticipantStats) {
if (!stats) return;
if ((stats.unapproved === 0 && this.activeTab === 1) || stats.rejected === 0 && this.activeTab === 2 ) {
this.activeTab = 0;
}
}
loadMoreParticipants() {
this.page += 1;
this.$apollo.queries.participants.fetchMore({
@ -203,9 +224,17 @@ export default class Participants extends Vue {
},
});
if (data) {
this.queue.filter(participant => participant !== data.updateParticipation.id);
this.rejected.filter(participant => participant !== data.updateParticipation.id);
this.participants.push(participant);
this.queue = this.queue.filter(participant => participant.id !== data.updateParticipation.id);
this.rejected = this.rejected.filter(participant => participant.id !== data.updateParticipation.id);
this.event.participantStats.approved += 1;
if (participant.role === ParticipantRole.NOT_APPROVED) {
this.event.participantStats.unapproved -= 1;
}
if (participant.role === ParticipantRole.REJECTED) {
this.event.participantStats.rejected -= 1;
}
participant.role = ParticipantRole.PARTICIPANT;
this.event.participants.push(participant);
}
} catch (e) {
console.error(e);
@ -223,8 +252,18 @@ export default class Participants extends Vue {
},
});
if (data) {
this.participants.filter(participant => participant !== data.updateParticipation.id);
this.queue.filter(participant => participant !== data.updateParticipation.id);
this.event.participants = this.event.participants.filter(participant => participant.id !== data.updateParticipation.id);
this.queue = this.queue.filter(participant => participant.id !== data.updateParticipation.id);
this.event.participantStats.rejected += 1;
if (participant.role === ParticipantRole.PARTICIPANT) {
this.event.participantStats.participants -= 1;
this.event.participantStats.approved -= 1;
}
if (participant.role === ParticipantRole.NOT_APPROVED) {
this.event.participantStats.unapproved -= 1;
}
participant.role = ParticipantRole.REJECTED;
this.rejected = this.rejected.filter(participantIn => participantIn.id !== participant.id);
this.rejected.push(participant);
}
} catch (e) {

View File

@ -11,6 +11,8 @@ import { isServerError } from '@/types/apollo';
import { REFRESH_TOKEN } from '@/graphql/auth';
import { AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN } from '@/constants';
import { logout, saveTokenData } from '@/utils/auth';
import { SnackbarProgrammatic as Snackbar } from 'buefy';
import { defaultError, errors, IError, refreshSuggestion } from '@/utils/errors';
// Install the vue plugin
Vue.use(VueApollo);
@ -87,14 +89,29 @@ const errorLink = onError(({ graphQLErrors, networkError, forward, operation })
}
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`),
);
graphQLErrors.forEach(({ message, locations, path }) => {
Snackbar.open({ message: computeErrorMessage(message), type: 'is-danger', position: 'is-bottom' });
console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`);
});
}
if (networkError) console.log(`[Network error]: ${networkError}`);
if (networkError) {
console.log(`[Network error]: ${networkError}`);
Snackbar.open({ message: computeErrorMessage(networkError), type: 'is-danger', position: 'is-bottom' });
}
});
const computeErrorMessage = (message) => {
const error: IError = errors.reduce((acc, error) => {
if (RegExp(error.match).test(message)) {
return error;
}
return acc;
}, defaultError);
return error.suggestRefresh === false ? error.value : `${error.value}<br>${refreshSuggestion}`;
};
const link = authMiddleware
.concat(errorLink)
.concat(uploadLink);

View File

@ -78,6 +78,7 @@ defmodule MobilizonWeb.Router do
get("/events/create", PageController, :index)
get("/events/list", PageController, :index)
get("/events/me", PageController, :index)
get("/events/explore", PageController, :index)
get("/events/:uuid/edit", PageController, :index)
# This is a hack to ease link generation into emails