Work on dashboard

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2019-09-18 17:32:37 +02:00
parent 48fd14bf9c
commit ffa4ec9209
33 changed files with 931 additions and 204 deletions

View File

@@ -69,7 +69,7 @@
</router-link>
</p>
<p class="control" v-if="actorIsOrganizer()">
<a class="button is-danger" @click="openDeleteEventModal()">
<a class="button is-danger" @click="openDeleteEventModalWrapper">
{{ $t('Delete') }}
</a>
</p>
@@ -111,7 +111,7 @@
<img
class="is-rounded"
:src="event.organizerActor.avatar.url"
:alt="$t("{actor}'s avatar", {actor: event.organizerActor.preferredUsername})" />
:alt="event.organizerActor.avatar.alt" />
</figure>
</actor-link>
</div>
@@ -262,6 +262,7 @@ import ReportModal from '@/components/Report/ReportModal.vue';
import ParticipationModal from '@/components/Event/ParticipationModal.vue';
import { IReport } from '@/types/report.model';
import { CREATE_REPORT } from '@/graphql/report';
import EventMixin from '@/mixins/event';
@Component({
components: {
@@ -290,7 +291,7 @@ import { CREATE_REPORT } from '@/graphql/report';
},
},
})
export default class Event extends Vue {
export default class Event extends EventMixin {
@Prop({ type: String, required: true }) uuid!: string;
event!: IEvent;
@@ -302,31 +303,12 @@ export default class Event extends Vue {
EventVisibility = EventVisibility;
async openDeleteEventModal () {
const participantsLength = this.event.participants.length;
const prefix = participantsLength
? this.$tc('There are {participants} participants.', this.event.participants.length, {
participants: this.event.participants.length,
})
: '';
this.$buefy.dialog.prompt({
type: 'is-danger',
title: this.$t('Delete event') as string,
message: `${prefix}
${this.$t('Are you sure you want to delete this event? This action cannot be reverted.')}
<br><br>
${this.$t('To confirm, type your event title "{eventTitle}"', { eventTitle: this.event.title })}`,
confirmText: this.$t(
'Delete {eventTitle}',
{ eventTitle: this.event.title },
) as string,
inputAttrs: {
placeholder: this.event.title,
pattern: this.event.title,
},
onConfirm: () => this.deleteEvent(),
});
/**
* Delete the event, then redirect to home.
*/
async openDeleteEventModalWrapper() {
await this.openDeleteEventModal(this.event, this.currentActor);
await this.$router.push({ name: RouteName.HOME });
}
async reportEvent(content: string, forward: boolean) {
@@ -464,31 +446,6 @@ export default class Event extends Vue {
return `mailto:?to=&body=${this.event.url}${encodeURIComponent('\n\n')}${this.event.description}&subject=${this.event.title}`;
}
private async deleteEvent() {
const router = this.$router;
const eventTitle = this.event.title;
try {
await this.$apollo.mutate<IParticipant>({
mutation: DELETE_EVENT,
variables: {
eventId: this.event.id,
actorId: this.currentActor.id,
},
});
await router.push({ name: RouteName.HOME });
this.$buefy.notification.open({
message: this.$t('Event {eventTitle} deleted', { eventTitle }) as string,
type: 'is-success',
position: 'is-bottom-right',
duration: 5000,
});
} catch (error) {
console.error(error);
}
}
}
</script>
<style lang="scss" scoped>

View File

@@ -0,0 +1,201 @@
<template>
<main class="container">
<h1 class="title">
{{ $t('My events') }}
</h1>
<b-loading :active.sync="$apollo.loading"></b-loading>
<section v-if="futureParticipations.length > 0">
<h2 class="subtitle">
{{ $t('Upcoming') }}
</h2>
<transition-group name="list" tag="p">
<div v-for="month in monthlyFutureParticipations" :key="month[0]">
<h3>{{ month[0] }}</h3>
<EventListCard
v-for="participation in month[1]"
:key="`${participation.event.uuid}${participation.actor.id}`"
:participation="participation"
:options="{ hideDate: false }"
@eventDeleted="eventDeleted"
class="participation"
/>
</div>
</transition-group>
<div class="columns is-centered">
<b-button class="column is-narrow"
v-if="hasMoreFutureParticipations && (futureParticipations.length === limit)" @click="loadMoreFutureParticipations" size="is-large" type="is-primary">{{ $t('Load more') }}</b-button>
</div>
</section>
<section v-if="pastParticipations.length > 0">
<h2 class="subtitle">
{{ $t('Past events') }}
</h2>
<transition-group name="list" tag="p">
<div v-for="month in monthlyPastParticipations" :key="month[0]">
<h3>{{ month[0] }}</h3>
<EventListCard
v-for="participation in month[1]"
:key="`${participation.event.uuid}${participation.actor.id}`"
:participation="participation"
:options="{ hideDate: false }"
@eventDeleted="eventDeleted"
class="participation"
/>
</div>
</transition-group>
<div class="columns is-centered">
<b-button class="column is-narrow"
v-if="hasMorePastParticipations && (pastParticipations.length === limit)" @click="loadMorePastParticipations" size="is-large" type="is-primary">{{ $t('Load more') }}</b-button>
</div>
</section>
<b-message v-if="futureParticipations.length === 0 && pastParticipations.length === 0 && $apollo.loading === false" type="is-danger">
{{ $t('No events found') }}
</b-message>
</main>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { LOGGED_USER_PARTICIPATIONS } from '@/graphql/actor';
import { IParticipant, Participant } from '@/types/event.model';
import EventListCard from '@/components/Event/EventListCard.vue';
@Component({
components: {
EventListCard,
},
apollo: {
futureParticipations: {
query: LOGGED_USER_PARTICIPATIONS,
variables: {
page: 1,
limit: 10,
afterDateTime: (new Date()).toISOString(),
},
update: data => data.loggedUser.participations.map(participation => new Participant(participation)),
},
pastParticipations: {
query: LOGGED_USER_PARTICIPATIONS,
variables: {
page: 1,
limit: 10,
beforeDateTime: (new Date()).toISOString(),
},
update: data => data.loggedUser.participations.map(participation => new Participant(participation)),
},
},
})
export default class MyEvents extends Vue {
@Prop(String) location!: string;
futurePage: number = 1;
pastPage: number = 1;
limit: number = 10;
futureParticipations: IParticipant[] = [];
hasMoreFutureParticipations: boolean = true;
pastParticipations: IParticipant[] = [];
hasMorePastParticipations: boolean = true;
private monthlyParticipations(participations: IParticipant[]): Map<string, Participant[]> {
const res = participations.filter(({ event }) => event.beginsOn != null);
res.sort(
(a: IParticipant, b: IParticipant) => a.event.beginsOn.getTime() - b.event.beginsOn.getTime(),
);
return res.reduce((acc: Map<string, IParticipant[]>, participation: IParticipant) => {
const month = (new Date(participation.event.beginsOn)).toLocaleDateString(undefined, { year: 'numeric', month: 'long' });
const participations: IParticipant[] = acc.get(month) || [];
participations.push(participation);
acc.set(month, participations);
return acc;
}, new Map());
}
get monthlyFutureParticipations(): Map<string, Participant[]> {
return this.monthlyParticipations(this.futureParticipations);
}
get monthlyPastParticipations(): Map<string, Participant[]> {
return this.monthlyParticipations(this.pastParticipations);
}
loadMoreFutureParticipations() {
this.futurePage += 1;
this.$apollo.queries.futureParticipations.fetchMore({
// New variables
variables: {
page: this.futurePage,
limit: this.limit,
},
// Transform the previous result with new data
updateQuery: (previousResult, { fetchMoreResult }) => {
const newParticipations = fetchMoreResult.loggedUser.participations;
this.hasMoreFutureParticipations = newParticipations.length === this.limit;
return {
loggedUser: {
__typename: previousResult.loggedUser.__typename,
participations: [...previousResult.loggedUser.participations, ...newParticipations],
},
};
},
});
}
loadMorePastParticipations() {
this.pastPage += 1;
this.$apollo.queries.pastParticipations.fetchMore({
// New variables
variables: {
page: this.pastPage,
limit: this.limit,
},
// Transform the previous result with new data
updateQuery: (previousResult, { fetchMoreResult }) => {
const newParticipations = fetchMoreResult.loggedUser.participations;
this.hasMorePastParticipations = newParticipations.length === this.limit;
return {
loggedUser: {
__typename: previousResult.loggedUser.__typename,
participations: [...previousResult.loggedUser.participations, ...newParticipations],
},
};
},
});
}
eventDeleted(eventid) {
this.futureParticipations = this.futureParticipations.filter(participation => participation.event.id !== eventid);
this.pastParticipations = this.pastParticipations.filter(participation => participation.event.id !== eventid);
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
@import "../../variables";
.participation {
margin: 1rem auto;
}
section {
margin: 3rem auto;
& > h2 {
display: block;
color: $primary;
font-size: 3rem;
text-decoration: underline;
text-decoration-color: $secondary;
}
h3 {
margin-top: 2rem;
font-weight: bold;
}
}
</style>