all repos — caroster @ 296817c73e419caa195ca5d6fa90a5db7578e6c5

[Octree] Group carpool to your event https://caroster.io

feat: :sparkles: Send notification to passengers when travel is removed for Caroster plus
Tim Izzo tim@octree.ch
Mon, 12 Feb 2024 13:45:58 +0000
commit

296817c73e419caa195ca5d6fa90a5db7578e6c5

parent

589ba138174504e3663d8e893cf9bdaed9e25d59

M backend/src/api/email/locales/en.jsonbackend/src/api/email/locales/en.json

@@ -8,6 +8,10 @@ "NewTrip": {

"title": "<%= event.name %> - A new car is available", "content": "## New car available for \"<%= event.name %>\"\n\n**Car: <%= travel.vehicleName %>**\nCount of seats: <%= travel.seats %>\nDeparture location: <%= travel.meeting %>\nInformation: <%= travel.details %>.\n\n[Book my seat](<%= host %>/e/<%= event.uuid %>).\n\nYou are receiving this email because you are on the waiting list to find a carpool spot in this event." }, + "DeletedTrip": { + "title": "<%= event.name %> - Car removed", + "content": "Car <%= travel.vehicleName %> in which you've subscribed to as been removed. Go to the [Caroster](<%= host %>/e/<%= event.uuid %>) page to find another car." + }, "NewPassengerInYourTrip": { "title": "<%= event.name %> - A passenger has been added to your trip", "content": "A passenger has been added to your trip. on Caroster [<%= event.name %>](<%= host %>/e/<%= event.uuid %>)"
M backend/src/api/email/locales/fr.jsonbackend/src/api/email/locales/fr.json

@@ -8,6 +8,10 @@ "NewTrip": {

"title": "<%= event.name %> - Une nouvelle voiture est disponible", "content": "## Nouvelle voiture pour \"<%= event.name %>\"\n\n**Voiture: <%= travel.vehicleName %>**\nNombre de places: <%= travel.seats %>\nLieu de départ: <%= travel.meeting %>\nInformations: <%= travel.details %>.\n\n[Réserver ma place](<%= host %>/e/<%= event.uuid %>).\n\nVous recevez cet e-mail, car vous êtes inscrit en liste d'attente pour trouver une place de covoiturage dans cet événement." }, + "DeletedTrip": { + "title": "<%= event.name %> - Voiture supprimée", + "content": "La voiture <%= travel.vehicleName %> pour laquelle vous vous êtes inscrit.e.s a été supprimée. Rendez-vous sur la page du [Caroster](<%= host %>/e/<%= event.uuid %>) pour trouver une autre voiture." + }, "NewPassengerInYourTrip": { "title": "<%= event.name %> - Un passager a été ajouté à votre trajet", "content": "Hey <%= user.firstname %>\n\nUn passager a été ajouté à votre trajet dans le Caroster [<%= event.name %>](<%= host %>/e/<%= event.uuid %>)."
M backend/src/api/notification/content-types/notification/schema.jsonbackend/src/api/notification/content-types/notification/schema.json

@@ -17,6 +17,7 @@ "type": "enumeration",

"enum": [ "NewPassengerInYourTrip", "NewTrip", + "DeletedTrip", "AddedAsAdmin", "EventCreated", "EventEnded",
M backend/src/api/travel/content-types/travel/lifecycles.tsbackend/src/api/travel/content-types/travel/lifecycles.ts

@@ -47,13 +47,47 @@ );

} }, - async beforeDelete({ params }) { - const travel = await strapi.db - .query("api::travel.travel") - .findOne({ ...params, populate: { event: true, passengers: true } }); + async beforeDelete({ params, ...others }) { + const travel = await strapi.entityService.findOne( + "api::travel.travel", + params.where?.id, + { + populate: { + event: true, + passengers: { + populate: ["user"], + }, + }, + } + ); + if (!travel) return; + + const hasPassengers = travel?.passengers?.length > 0; + const enabledModules = travel.event?.enabled_modules as String[]; + const isEventCarosterPlus = enabledModules?.includes("caroster-plus"); + // If Caroster Plus, send notification to passengers + if (isEventCarosterPlus && hasPassengers) { + const users = travel.passengers + .map((passenger) => passenger.user) + .filter(Boolean); + await pMap( + users, + async (user) => + strapi.entityService.create("api::notification.notification", { + data: { + type: "DeletedTrip", + event: travel.event.id, + user: user.id, + // @ts-expect-error + payload: { travel }, + }, + }), + { concurrency: 5 } + ); + } // Move travel's passengers to event's waiting list - if (travel?.passengers?.length > 0) { + else if (hasPassengers) { const { passengers = [] } = travel; await Promise.all( passengers.map(movePassengerToWaitingList(travel.event.id))

@@ -116,10 +150,11 @@ { concurrency: 5 }

); }; -const movePassengerToWaitingList = (eventId: string) => async (passenger) => - strapi.entityService.update("api::passenger.passenger", passenger.id, { - data: { - travel: null, - event: eventId, - }, - }); +const movePassengerToWaitingList = + (eventId: string | number) => async (passenger) => + strapi.entityService.update("api::passenger.passenger", passenger.id, { + data: { + travel: null, + event: eventId, + }, + });
M backend/types/generated/contentTypes.d.tsbackend/types/generated/contentTypes.d.ts

@@ -957,6 +957,7 @@ type: Attribute.Enumeration<

[ 'NewPassengerInYourTrip', 'NewTrip', + 'DeletedTrip', 'AddedAsAdmin', 'EventCreated', 'EventEnded',
M frontend/containers/Travel/HeaderEditing.tsxfrontend/containers/Travel/HeaderEditing.tsx

@@ -12,11 +12,15 @@ import RemoveDialog from '../RemoveDialog';

import useActions from './useActions'; import Box from '@mui/material/Box'; import PlaceInput from '../PlaceInput'; +import useEventStore from '../../stores/useEventStore'; const HeaderEditing = ({travel, toggleEditing}) => { const {t} = useTranslation(); const theme = useTheme(); const actions = useActions({travel}); + const isCarosterPlus = useEventStore(s => + s.event.enabled_modules?.includes('caroster-plus') + ); const [removing, toggleRemoving] = useReducer(i => !i, false); const dateMoment = useMemo( () => (travel?.departure ? moment(travel.departure) : null),

@@ -70,7 +74,11 @@ toggleEditing();

}; const onRemove = async () => { - await actions.removeTravel(); + await actions.removeTravel( + isCarosterPlus + ? t`travel.actions.removed.caroster_plus` + : t`travel.actions.removed` + ); toggleEditing(); };

@@ -192,7 +200,11 @@ {t('generic.remove')}

</Button> </Box> <RemoveDialog - text={t('travel.actions.remove_alert')} + text={ + isCarosterPlus + ? t`travel.actions.remove_alert.caroster_plus` + : t`travel.actions.remove_alert` + } open={removing} onClose={toggleRemoving} onRemove={onRemove}
M frontend/containers/Travel/useActions.tsxfrontend/containers/Travel/useActions.tsx

@@ -81,7 +81,7 @@ addToast(t('travel.errors.cant_update'));

} }; - const removeTravel = async () => { + const removeTravel = async (successText?: string) => { try { await deleteTravelMutation({ variables: {

@@ -91,7 +91,7 @@ refetchQueries: [

{query: EventByUuidDocument, variables: {uuid: event.uuid}}, ], }); - addToast(t('travel.actions.removed')); + addToast(successText || t('travel.actions.removed')); } catch (error) { console.error(error); addToast(t('travel.errors.cant_remove'));
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -68,7 +68,7 @@ "options.plus.admins": "Administrators",

"options.plus.creator": "Creator", "options.plus.activationOK": "Add Caroster plus to your event", "options.plus.activationForbiden": "Only the administrator is authorized to add modules", - "options.plus.addAdmin": "Add an administrator", + "options.plus.addAdmin": "Add an administrator", "options.plus.addAdmin.email": "Email", "options.plus.addAdmin.emailHelper": "The email is not valid", "options.plus.adminAdded": "The administrator has been invited",

@@ -115,6 +115,7 @@ "notifications.markAllRead": "Mark all in read",

"notifications.content": "No notification", "notification.type.NewPassengerInYourTrip.content": "A passenger has been added to your trip.", "notification.type.NewTrip.content": "A departure close to you is available.", + "notification.type.DeletedTrip.content": "A car has been removed.", "notification.type.AddedAsAdmin.content": "You have been added as admin to an event.", "passenger.actions.place": "Assign", "passenger.actions.remove_alert": "Are you sure you want to remove <italic> <bold> {{name}} </bold> </italic> from the waitlist?",

@@ -195,7 +196,9 @@ "tour.welcome.onboard": "OK, let's go!",

"tour.welcome.text": "Would you like to take a feature tour?", "tour.welcome.title": "Welcome to Caroster!", "travel.actions.remove_alert": "Are you sure you want to remove this car and add the subscribers to the waiting list?", + "travel.actions.remove_alert.caroster_plus": "Are you sure you want to remove this car? Passengers will be notified.", "travel.actions.removed": "The car has been removed and its passengers moved to the waiting list.", + "travel.actions.removed.caroster_plus": "The car has been removed and its passengers have been notified.", "travel.creation.car.title": "Car and driver", "travel.creation.created": "The car has been created", "travel.creation.date": "Date of departure",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -68,7 +68,7 @@ "options.plus.admins": "Administrateurs",

"options.plus.creator": "Créateur", "options.plus.activationOK": "Ajoutez Caroster plus à votre évènement", "options.plus.activationForbiden": "Seul l'administrateur est autorisé à ajouter des modules", - "options.plus.addAdmin": "Ajouter un administrateur", + "options.plus.addAdmin": "Ajouter un administrateur", "options.plus.addAdmin.email": "E-mail", "options.plus.addAdmin.emailHelper": "L e-mail n'est pas valide", "options.plus.adminAdded": "L'administrateur a été invité",

@@ -115,6 +115,7 @@ "notifications.markAllRead": "Tout marquer en lu",

"notifications.content": "Aucune notification", "notification.type.NewPassengerInYourTrip.content": "Un passager a été ajouté à votre trajet.", "notification.type.NewTrip.content": "Un départ proche de vous est disponible.", + "notification.type.DeletedTrip.content": "Une voiture a été supprimée.", "notification.type.AddedAsAdmin.content": "Vous avez été ajouté en tant qu'admin à un événement.", "passenger.actions.place": "Placer", "passenger.actions.remove_alert": "Voulez-vous vraiment supprimer <italic><bold>{{name}}</bold></italic> de la liste d'attente ?",

@@ -195,7 +196,9 @@ "tour.welcome.onboard": "OK, c'est parti!",

"tour.welcome.text": "Faire un tour des fonctionnalités.", "tour.welcome.title": "Bienvenue sur Caroster !", "travel.actions.remove_alert": "Voulez-vous vraiment supprimer cette voiture et ajouter les inscrits à la liste d'attente ?", + "travel.actions.remove_alert.caroster_plus": "Voulez-vous vraiment supprimer cette voiture ? Les inscrits seront avertis.", "travel.actions.removed": "La voiture a été supprimée et ses passagers déplacés dans la liste d'attente.", + "travel.actions.removed.caroster_plus": "La voiture a été supprimée et ses passagers ont été notifiés.", "travel.creation.car.title": "Voiture et contact", "travel.creation.created": "La voiture a été créée", "travel.creation.date": "Date de départ",
M frontend/locales/nl.jsonfrontend/locales/nl.json

@@ -109,6 +109,13 @@ "menu.logout": "Uitloggen",

"menu.new_event": "Caroster aanmaken", "menu.profile": "Mijn profiel", "menu.register": "Registreren", + "notifications.title": "", + "notifications.markAllRead": "", + "notifications.content": "", + "notification.type.NewPassengerInYourTrip.content": "", + "notification.type.NewTrip.content": "", + "notification.type.DeletedTrip.content": "", + "notification.type.AddedAsAdmin.content": "", "passenger.actions.place": "Toewijzen", "passenger.actions.remove_alert": "Weet u zeker dat u <italic><bold>{{name}}</bold></italic> van de wachtlijst wilt halen?", "passenger.availability.seats": "Er is {{count}} plaats beschikbaar",

@@ -188,7 +195,9 @@ "tour.welcome.onboard": "Ja, graag!",

"tour.welcome.text": "Wilt u een rondleiding nemen?", "tour.welcome.title": "Welkom bij Caroster!", "travel.actions.remove_alert": "Weet u zeker dat u deze auto wilt verwijderen en daardoor de passagiers wilt toevoegen aan de wachtlijst?", + "travel.actions.remove_alert.caroster_plus": "", "travel.actions.removed": "De auto is verwijderd en de passagiers zijn toegevoegd aan de wachtlijst.", + "travel.actions.removed.caroster_plus": "", "travel.creation.car.title": "Auto en bestuurder", "travel.creation.created": "De auto is aangemaakt", "travel.creation.date": "Vertrekdatum",
M frontend/locales/pl.jsonfrontend/locales/pl.json

@@ -112,6 +112,13 @@ "menu.logout": "Wyloguj się",

"menu.new_event": "", "menu.profile": "Mój profil", "menu.register": "Zarejestruj się", + "notifications.title": "", + "notifications.markAllRead": "", + "notifications.content": "", + "notification.type.NewPassengerInYourTrip.content": "", + "notification.type.NewTrip.content": "", + "notification.type.DeletedTrip.content": "", + "notification.type.AddedAsAdmin.content": "", "passenger.assign.title": "", "passenger.actions.place": "Przypisz", "passenger.actions.remove_alert": "Czy na pewno chcesz usunąć <italic> <bold> {{name}} </bold> </italic> z listy oczekujących?",

@@ -192,7 +199,9 @@ "tour.welcome.onboard": "Tak, ruszajmy!",

"tour.welcome.text": "Czy chcesz zobaczyć w prezentację naszych funkcji?", "tour.welcome.title": "Witamy w Caroster!", "travel.actions.remove_alert": "", + "travel.actions.remove_alert.caroster_plus": "", "travel.actions.removed": "", + "travel.actions.removed.caroster_plus": "", "travel.creation.car.title": "", "travel.creation.created": "", "travel.creation.date": "",
M frontend/locales/sv.jsonfrontend/locales/sv.json

@@ -109,6 +109,13 @@ "menu.logout": "",

"menu.new_event": "", "menu.profile": "", "menu.register": "", + "notifications.title": "", + "notifications.markAllRead": "", + "notifications.content": "", + "notification.type.NewPassengerInYourTrip.content": "", + "notification.type.NewTrip.content": "", + "notification.type.DeletedTrip.content": "", + "notification.type.AddedAsAdmin.content": "", "passenger.assign.title": "", "passenger.actions.place": "", "passenger.actions.remove_alert": "",

@@ -188,7 +195,9 @@ "tour.welcome.onboard": "",

"tour.welcome.text": "", "tour.welcome.title": "", "travel.actions.remove_alert": "", + "travel.actions.remove_alert.caroster_plus": "", "travel.actions.removed": "", + "travel.actions.removed.caroster_plus": "", "travel.creation.car.title": "", "travel.creation.created": "", "travel.creation.date": "",