all repos — caroster @ 35309ef0acbe64753679fc84022199d730edead2

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

feat: ✨ Add administrators in Caroster Plus #281
Simon Mulquin simon@octree.ch
Mon, 05 Feb 2024 16:52:38 +0000
commit

35309ef0acbe64753679fc84022199d730edead2

parent

8cf97f406bef2a68157869e2f6398d45b8bec77a

M frontend/containers/CarosterPlusSettings/index.tsxfrontend/containers/CarosterPlusSettings/index.tsx

@@ -1,3 +1,4 @@

+import {useReducer, useState} from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card';

@@ -8,14 +9,24 @@ import ListItem from '@mui/material/ListItem';

import ListItemIcon from '@mui/material//ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import AddIcon from '@mui/icons-material/Add'; +import Typography from '@mui/material/Typography'; +import TextField from '@mui/material/TextField'; +import IconButton from '@mui/material/IconButton'; import AccountCircleOutlinedIcon from '@mui/icons-material/AccountCircleOutlined'; -import Typography from '@mui/material/Typography'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import {useTranslation} from 'react-i18next'; import usePermissions from '../../hooks/usePermissions'; -import {Event as EventType} from '../../generated/graphql'; +import useToastStore from '../../stores/useToastStore'; +import FormDialog from '../FormDialog'; +import {validateEmail} from '../../lib/validation'; +import { + Event as EventType, + useAddEventAdminMutation, + useDeleteEventAdminMutation, +} from '../../generated/graphql'; interface Props { - event: EventType; + event: EventType & {id: string}; } const CarosterPlusSettings = ({event}: Props) => {

@@ -23,6 +34,47 @@ const {t} = useTranslation();

const { userPermissions: {canEditEventOptions}, } = usePermissions(); + const {addToast} = useToastStore(); + + const [addAdminMutation] = useAddEventAdminMutation(); + const [deleteAdminMutation] = useDeleteEventAdminMutation(); + const [addAdminDialogOpen, toggleAddAdminDialog] = useReducer(i => !i, false); + const [adminEmail, setAdminEmail] = useState(''); + const isEmailValid = validateEmail(adminEmail); + const emailError = adminEmail !== '' && !isEmailValid; + + const addAdmin = async () => { + try { + await addAdminMutation({ + variables: { + eventId: event.id, + email: adminEmail, + }, + }); + addToast(t('options.plus.adminAdded')); + toggleAddAdminDialog(); + setAdminEmail(''); + } catch (e) { + console.error(e); + addToast(t('options.plus.addAdminError')); + } + }; + + const deleteAdmin = async ({email}) => { + try { + await deleteAdminMutation({ + variables: { + eventId: event.id, + email, + }, + }); + addToast(t('options.plus.adminDeleted')); + toggleAddAdminDialog(); + } catch (e) { + console.error(e); + addToast(t('options.plus.deleteAdminError')); + } + }; return ( <Card

@@ -53,6 +105,7 @@ <Button

variant="text" disabled={!canEditEventOptions} endIcon={<AddIcon />} + onClick={toggleAddAdminDialog} > {t('generic.add')} </Button>

@@ -68,7 +121,40 @@ <AccountCircleOutlinedIcon />

</ListItemIcon> <ListItemText primary={event.email} /> </ListItem> + {event.administrators?.map(email => ( + <ListItem + secondaryAction={ + canEditEventOptions && ( + <IconButton size="medium" onClick={() => deleteAdmin({email})}> + <DeleteOutlineIcon /> + </IconButton> + ) + } + > + <ListItemIcon> + <AccountCircleOutlinedIcon /> + </ListItemIcon> + <ListItemText primary={email} /> + </ListItem> + ))} </List> + <FormDialog + title={t("options.plus.addAdmin")} + open={addAdminDialogOpen} + cancel={toggleAddAdminDialog} + onSubmit={addAdmin} + disabled={!isEmailValid} + > + <TextField + fullWidth + error={emailError} + label={t('options.plus.addAdmin.email')} + value={adminEmail} + onChange={e => setAdminEmail(e.target.value)} + helperText={emailError && t('options.plus.addAdmin.emailHelper')} + variant="standard" + /> + </FormDialog> </Card> ); };
A frontend/containers/FormDialog/Transition.tsx

@@ -0,0 +1,16 @@

+import Slide from '@mui/material/Slide'; +import {TransitionProps} from '@mui/material/transitions'; +import {ReactElement, forwardRef} from 'react'; + +const Transition = forwardRef(function Transition( + props: TransitionProps & {children: ReactElement<any, any>}, + ref +) { + return ( + <Slide direction="up" ref={ref} {...props}> + {props.children} + </Slide> + ); +}); + +export default Transition;
A frontend/containers/FormDialog/index.tsx

@@ -0,0 +1,58 @@

+import {ReactElement} from 'react'; +import {useTranslation} from 'react-i18next'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import DialogTitle from '@mui/material/DialogTitle'; +import Button from '@mui/material/Button'; +import Transition from './Transition'; + +interface Props { + open: boolean; + cancel: () => void; + onSubmit: () => Promise<void>; + disabled: boolean; + title: string; + children: ReactElement<any, any>; +} + +const FormDialog = ({ + open, + cancel, + onSubmit, + disabled, + title, + children, +}: Props) => { + const {t} = useTranslation(); + + return ( + <Dialog + fullWidth + maxWidth="xs" + open={open} + onClose={cancel} + TransitionComponent={Transition} + > + <form + onSubmit={e => { + e.preventDefault(); + onSubmit(); + }} + > + <DialogTitle>{title}</DialogTitle> + <DialogContent>{children}</DialogContent> + <DialogActions> + <Button variant="text" onClick={cancel}> + {t('generic.cancel')} + </Button> + <Button type="submit" variant="contained" disabled={disabled}> + {t('generic.add')} + </Button> + </DialogActions> + </form> + </Dialog> + ); +}; + +export default FormDialog;
M frontend/containers/NewPassengerDialog/AddPassengerToTravel.tsxfrontend/containers/NewPassengerDialog/AddPassengerToTravel.tsx

@@ -13,7 +13,7 @@ import AddPassengerCommonFields from './AddPassengerCommonFields';

import useStyles from './useStyles'; import useToastStore from '../../stores/useToastStore'; import usePassengersActions from '../../hooks/usePassengersActions'; -import {validateEmail} from './validation'; +import {validateEmail} from '../../lib/validation'; interface Props { travel: TravelType & {id: string};
M frontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsxfrontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsx

@@ -16,7 +16,7 @@ import SubmitButton from './SubmitButton';

import Transition from './Transition'; import AddPassengerCommonFields from './AddPassengerCommonFields'; import useStyles from './useStyles'; -import {validateEmail} from './validation'; +import {validateEmail} from '../../lib/validation'; import {PassengerInput} from '../../generated/graphql'; interface Props {
M frontend/generated/graphql.tsxfrontend/generated/graphql.tsx

@@ -204,6 +204,7 @@ unpublish = 'unpublish'

} export enum Enum_Notification_Type { + AddedAsAdmin = 'AddedAsAdmin', NewPassengerInYourTrip = 'NewPassengerInYourTrip', NewTrip = 'NewTrip' }

@@ -279,6 +280,7 @@

export type Event = { __typename?: 'Event'; address?: Maybe<Scalars['String']['output']>; + administrators?: Maybe<Array<Maybe<Scalars['String']['output']>>>; createdAt?: Maybe<Scalars['DateTime']['output']>; date?: Maybe<Scalars['Date']['output']>; description?: Maybe<Scalars['String']['output']>;

@@ -320,8 +322,15 @@ __typename?: 'EventEntityResponse';

data?: Maybe<EventEntity>; }; +export type EventEntityResponseCollection = { + __typename?: 'EventEntityResponseCollection'; + data: Array<EventEntity>; + meta: ResponseCollectionMeta; +}; + export type EventFiltersInput = { address?: InputMaybe<StringFilterInput>; + administrators?: InputMaybe<StringFilterInput>; and?: InputMaybe<Array<InputMaybe<EventFiltersInput>>>; createdAt?: InputMaybe<DateTimeFilterInput>; date?: InputMaybe<DateFilterInput>;

@@ -345,6 +354,7 @@ };

export type EventInput = { address?: InputMaybe<Scalars['String']['input']>; + administrators?: InputMaybe<Scalars['String']['input']>; date?: InputMaybe<Scalars['Date']['input']>; description?: InputMaybe<Scalars['String']['input']>; email?: InputMaybe<Scalars['String']['input']>;

@@ -396,7 +406,7 @@ or?: InputMaybe<Array<InputMaybe<Scalars['Float']['input']>>>;

startsWith?: InputMaybe<Scalars['Float']['input']>; }; -export type GenericMorph = ContentReleasesRelease | ContentReleasesReleaseAction | EmailDesignerEmailTemplate | Event | I18NLocale | Module | Notification | Page | Passenger | Setting | Travel | UploadFile | UploadFolder | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsUser | Vehicle; +export type GenericMorph = ContentReleasesRelease | ContentReleasesReleaseAction | EmailDesignerEmailTemplate | Event | I18NLocale | Module | Notification | Page | Passenger | Setting | Travel | TripAlert | UploadFile | UploadFolder | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsUser | Vehicle; export type I18NLocale = { __typename?: 'I18NLocale';

@@ -549,6 +559,7 @@ };

export type Mutation = { __typename?: 'Mutation'; + addEventAdmin?: Maybe<EventEntityResponse>; /** Change user password. Confirm with the current password. */ changePassword?: Maybe<UsersPermissionsLoginPayload>; createContentReleasesRelease?: Maybe<ContentReleasesReleaseEntityResponse>;

@@ -562,6 +573,7 @@ /** Create a passenger */

createPassenger?: Maybe<PassengerEntityResponse>; createSettingLocalization?: Maybe<SettingEntityResponse>; createTravel?: Maybe<TravelEntityResponse>; + createTripAlert?: Maybe<TripAlertEntityResponse>; createUploadFile?: Maybe<UploadFileEntityResponse>; createUploadFolder?: Maybe<UploadFolderEntityResponse>; /** Create a new role */

@@ -573,12 +585,14 @@ deleteContentReleasesRelease?: Maybe<ContentReleasesReleaseEntityResponse>;

deleteContentReleasesReleaseAction?: Maybe<ContentReleasesReleaseActionEntityResponse>; deleteEmailDesignerEmailTemplate?: Maybe<EmailDesignerEmailTemplateEntityResponse>; deleteEvent?: Maybe<EventEntityResponse>; + deleteEventAdmin?: Maybe<EventEntityResponse>; deleteModule?: Maybe<ModuleEntityResponse>; deleteNotification?: Maybe<NotificationEntityResponse>; deletePage?: Maybe<PageEntityResponse>; deletePassenger?: Maybe<PassengerEntityResponse>; deleteSetting?: Maybe<SettingEntityResponse>; deleteTravel?: Maybe<TravelEntityResponse>; + deleteTripAlert?: Maybe<TripAlertEntityResponse>; deleteUploadFile?: Maybe<UploadFileEntityResponse>; deleteUploadFolder?: Maybe<UploadFolderEntityResponse>; /** Delete an existing role */

@@ -598,6 +612,7 @@ register: UsersPermissionsLoginPayload;

removeFile?: Maybe<UploadFileEntityResponse>; /** Reset user password. Confirm with a code (resetToken from forgotPassword) */ resetPassword?: Maybe<UsersPermissionsLoginPayload>; + setTripAlert?: Maybe<TripAlertEntityResponse>; updateContentReleasesRelease?: Maybe<ContentReleasesReleaseEntityResponse>; updateContentReleasesReleaseAction?: Maybe<ContentReleasesReleaseActionEntityResponse>; updateEmailDesignerEmailTemplate?: Maybe<EmailDesignerEmailTemplateEntityResponse>;

@@ -612,6 +627,7 @@ updatePage?: Maybe<PageEntityResponse>;

updatePassenger?: Maybe<PassengerEntityResponse>; updateSetting?: Maybe<SettingEntityResponse>; updateTravel?: Maybe<TravelEntityResponse>; + updateTripAlert?: Maybe<TripAlertEntityResponse>; updateUploadFile?: Maybe<UploadFileEntityResponse>; updateUploadFolder?: Maybe<UploadFolderEntityResponse>; /** Update an existing role */

@@ -623,6 +639,12 @@ upload: UploadFileEntityResponse;

}; +export type MutationAddEventAdminArgs = { + email: Scalars['String']['input']; + eventId: Scalars['ID']['input']; +}; + + export type MutationChangePasswordArgs = { currentPassword: Scalars['String']['input']; password: Scalars['String']['input'];

@@ -682,6 +704,11 @@

export type MutationCreateTravelArgs = { createVehicle?: InputMaybe<Scalars['Boolean']['input']>; data: TravelInput; +}; + + +export type MutationCreateTripAlertArgs = { + data: TripAlertInput; };

@@ -730,6 +757,12 @@ id: Scalars['ID']['input'];

}; +export type MutationDeleteEventAdminArgs = { + email: Scalars['String']['input']; + eventId: Scalars['ID']['input']; +}; + + export type MutationDeleteModuleArgs = { locale?: InputMaybe<Scalars['I18NLocaleCode']['input']>; };

@@ -760,6 +793,11 @@ id: Scalars['ID']['input'];

}; +export type MutationDeleteTripAlertArgs = { + id: Scalars['ID']['input']; +}; + + export type MutationDeleteUploadFileArgs = { id: Scalars['ID']['input']; };

@@ -830,6 +868,16 @@ passwordConfirmation: Scalars['String']['input'];

}; +export type MutationSetTripAlertArgs = { + address?: InputMaybe<Scalars['String']['input']>; + distance?: InputMaybe<Scalars['Float']['input']>; + enabled?: InputMaybe<Scalars['Boolean']['input']>; + event: Scalars['ID']['input']; + latitude?: InputMaybe<Scalars['Float']['input']>; + longitude?: InputMaybe<Scalars['Float']['input']>; +}; + + export type MutationUpdateContentReleasesReleaseArgs = { data: ContentReleasesReleaseInput; id: Scalars['ID']['input'];

@@ -903,6 +951,12 @@

export type MutationUpdateTravelArgs = { data: TravelInput; + id: Scalars['ID']['input']; +}; + + +export type MutationUpdateTripAlertArgs = { + data: TripAlertInput; id: Scalars['ID']['input']; };

@@ -1079,6 +1133,12 @@ __typename?: 'PassengerEntityResponse';

data?: Maybe<PassengerEntity>; }; +export type PassengerEntityResponseCollection = { + __typename?: 'PassengerEntityResponseCollection'; + data: Array<PassengerEntity>; + meta: ResponseCollectionMeta; +}; + export type PassengerFiltersInput = { and?: InputMaybe<Array<InputMaybe<PassengerFiltersInput>>>; createdAt?: InputMaybe<DateTimeFilterInput>;

@@ -1119,6 +1179,8 @@ emailDesignerEmailTemplates?: Maybe<EmailDesignerEmailTemplateEntityResponseCollection>;

event?: Maybe<EventEntityResponse>; /** Retrieve an event using its UUID */ eventByUUID?: Maybe<EventEntityResponse>; + eventTripAlert?: Maybe<TripAlertEntityResponse>; + events?: Maybe<EventEntityResponseCollection>; i18NLocale?: Maybe<I18NLocaleEntityResponse>; i18NLocales?: Maybe<I18NLocaleEntityResponseCollection>; me?: Maybe<UsersPermissionsMe>;

@@ -1128,8 +1190,12 @@ notifications?: Maybe<NotificationEntityResponseCollection>;

page?: Maybe<PageEntityResponse>; pages?: Maybe<PageEntityResponseCollection>; passenger?: Maybe<PassengerEntityResponse>; + passengers?: Maybe<PassengerEntityResponseCollection>; setting?: Maybe<SettingEntityResponse>; travel?: Maybe<TravelEntityResponse>; + travels?: Maybe<TravelEntityResponseCollection>; + tripAlert?: Maybe<TripAlertEntityResponse>; + tripAlerts?: Maybe<TripAlertEntityResponseCollection>; uploadFile?: Maybe<UploadFileEntityResponse>; uploadFiles?: Maybe<UploadFileEntityResponseCollection>; uploadFolder?: Maybe<UploadFolderEntityResponse>;

@@ -1137,7 +1203,9 @@ uploadFolders?: Maybe<UploadFolderEntityResponseCollection>;

usersPermissionsRole?: Maybe<UsersPermissionsRoleEntityResponse>; usersPermissionsRoles?: Maybe<UsersPermissionsRoleEntityResponseCollection>; usersPermissionsUser?: Maybe<UsersPermissionsUserEntityResponse>; + usersPermissionsUsers?: Maybe<UsersPermissionsUserEntityResponseCollection>; vehicle?: Maybe<VehicleEntityResponse>; + vehicles?: Maybe<VehicleEntityResponseCollection>; };

@@ -1187,6 +1255,18 @@ uuid: Scalars['String']['input'];

}; +export type QueryEventTripAlertArgs = { + event: Scalars['ID']['input']; +}; + + +export type QueryEventsArgs = { + filters?: InputMaybe<EventFiltersInput>; + pagination?: InputMaybe<PaginationArg>; + sort?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>; +}; + + export type QueryI18NLocaleArgs = { id?: InputMaybe<Scalars['ID']['input']>; };

@@ -1233,6 +1313,13 @@ id?: InputMaybe<Scalars['ID']['input']>;

}; +export type QueryPassengersArgs = { + filters?: InputMaybe<PassengerFiltersInput>; + pagination?: InputMaybe<PaginationArg>; + sort?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>; +}; + + export type QuerySettingArgs = { locale?: InputMaybe<Scalars['I18NLocaleCode']['input']>; };

@@ -1243,6 +1330,25 @@ id?: InputMaybe<Scalars['ID']['input']>;

}; +export type QueryTravelsArgs = { + filters?: InputMaybe<TravelFiltersInput>; + pagination?: InputMaybe<PaginationArg>; + sort?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>; +}; + + +export type QueryTripAlertArgs = { + id?: InputMaybe<Scalars['ID']['input']>; +}; + + +export type QueryTripAlertsArgs = { + filters?: InputMaybe<TripAlertFiltersInput>; + pagination?: InputMaybe<PaginationArg>; + sort?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>; +}; + + export type QueryUploadFileArgs = { id?: InputMaybe<Scalars['ID']['input']>; };

@@ -1284,8 +1390,22 @@ id?: InputMaybe<Scalars['ID']['input']>;

}; +export type QueryUsersPermissionsUsersArgs = { + filters?: InputMaybe<UsersPermissionsUserFiltersInput>; + pagination?: InputMaybe<PaginationArg>; + sort?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>; +}; + + export type QueryVehicleArgs = { id?: InputMaybe<Scalars['ID']['input']>; +}; + + +export type QueryVehiclesArgs = { + filters?: InputMaybe<VehicleFiltersInput>; + pagination?: InputMaybe<PaginationArg>; + sort?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>; }; export type ResponseCollectionMeta = {

@@ -1394,6 +1514,12 @@ __typename?: 'TravelEntityResponse';

data?: Maybe<TravelEntity>; }; +export type TravelEntityResponseCollection = { + __typename?: 'TravelEntityResponseCollection'; + data: Array<TravelEntity>; + meta: ResponseCollectionMeta; +}; + export type TravelFiltersInput = { and?: InputMaybe<Array<InputMaybe<TravelFiltersInput>>>; createdAt?: InputMaybe<DateTimeFilterInput>;

@@ -1433,6 +1559,62 @@ __typename?: 'TravelRelationResponseCollection';

data: Array<TravelEntity>; }; +export type TripAlert = { + __typename?: 'TripAlert'; + address?: Maybe<Scalars['String']['output']>; + createdAt?: Maybe<Scalars['DateTime']['output']>; + enabled?: Maybe<Scalars['Boolean']['output']>; + event?: Maybe<EventEntityResponse>; + latitude?: Maybe<Scalars['Float']['output']>; + longitude?: Maybe<Scalars['Float']['output']>; + radius?: Maybe<Scalars['Float']['output']>; + updatedAt?: Maybe<Scalars['DateTime']['output']>; + user?: Maybe<UsersPermissionsUserEntityResponse>; +}; + +export type TripAlertEntity = { + __typename?: 'TripAlertEntity'; + attributes?: Maybe<TripAlert>; + id?: Maybe<Scalars['ID']['output']>; +}; + +export type TripAlertEntityResponse = { + __typename?: 'TripAlertEntityResponse'; + data?: Maybe<TripAlertEntity>; +}; + +export type TripAlertEntityResponseCollection = { + __typename?: 'TripAlertEntityResponseCollection'; + data: Array<TripAlertEntity>; + meta: ResponseCollectionMeta; +}; + +export type TripAlertFiltersInput = { + address?: InputMaybe<StringFilterInput>; + and?: InputMaybe<Array<InputMaybe<TripAlertFiltersInput>>>; + createdAt?: InputMaybe<DateTimeFilterInput>; + enabled?: InputMaybe<BooleanFilterInput>; + event?: InputMaybe<EventFiltersInput>; + id?: InputMaybe<IdFilterInput>; + latitude?: InputMaybe<FloatFilterInput>; + longitude?: InputMaybe<FloatFilterInput>; + not?: InputMaybe<TripAlertFiltersInput>; + or?: InputMaybe<Array<InputMaybe<TripAlertFiltersInput>>>; + radius?: InputMaybe<FloatFilterInput>; + updatedAt?: InputMaybe<DateTimeFilterInput>; + user?: InputMaybe<UsersPermissionsUserFiltersInput>; +}; + +export type TripAlertInput = { + address?: InputMaybe<Scalars['String']['input']>; + enabled?: InputMaybe<Scalars['Boolean']['input']>; + event?: InputMaybe<Scalars['ID']['input']>; + latitude?: InputMaybe<Scalars['Float']['input']>; + longitude?: InputMaybe<Scalars['Float']['input']>; + radius?: InputMaybe<Scalars['Float']['input']>; + user?: InputMaybe<Scalars['ID']['input']>; +}; + export type UploadFile = { __typename?: 'UploadFile'; alternativeText?: Maybe<Scalars['String']['output']>;

@@ -1799,6 +1981,12 @@ __typename?: 'UsersPermissionsUserEntityResponse';

data?: Maybe<UsersPermissionsUserEntity>; }; +export type UsersPermissionsUserEntityResponseCollection = { + __typename?: 'UsersPermissionsUserEntityResponseCollection'; + data: Array<UsersPermissionsUserEntity>; + meta: ResponseCollectionMeta; +}; + export type UsersPermissionsUserFiltersInput = { and?: InputMaybe<Array<InputMaybe<UsersPermissionsUserFiltersInput>>>; blocked?: InputMaybe<BooleanFilterInput>;

@@ -1876,6 +2064,12 @@ __typename?: 'VehicleEntityResponse';

data?: Maybe<VehicleEntity>; }; +export type VehicleEntityResponseCollection = { + __typename?: 'VehicleEntityResponseCollection'; + data: Array<VehicleEntity>; + meta: ResponseCollectionMeta; +}; + export type VehicleFiltersInput = { and?: InputMaybe<Array<InputMaybe<VehicleFiltersInput>>>; createdAt?: InputMaybe<DateTimeFilterInput>;

@@ -1926,7 +2120,7 @@

export type ResetPasswordMutation = { __typename?: 'Mutation', resetPassword?: { __typename?: 'UsersPermissionsLoginPayload', jwt?: string | null, user: { __typename?: 'UsersPermissionsMe', id: string, username: string, email?: string | null, confirmed?: boolean | null } } | null }; -export type EventFieldsFragment = { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null }; +export type EventFieldsFragment = { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, administrators?: Array<string | null> | null, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null }; export type CreateEventMutationVariables = Exact<{ name: Scalars['String']['input'];

@@ -1940,7 +2134,7 @@ newsletter?: InputMaybe<Scalars['Boolean']['input']>;

}>; -export type CreateEventMutation = { __typename?: 'Mutation', createEvent?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null } | null } | null }; +export type CreateEventMutation = { __typename?: 'Mutation', createEvent?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, administrators?: Array<string | null> | null, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null } | null } | null }; export type UpdateEventMutationVariables = Exact<{ uuid: Scalars['String']['input'];

@@ -1948,14 +2142,30 @@ eventUpdate: EventInput;

}>; -export type UpdateEventMutation = { __typename?: 'Mutation', updateEventByUUID?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null } | null } | null }; +export type UpdateEventMutation = { __typename?: 'Mutation', updateEventByUUID?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, administrators?: Array<string | null> | null, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null } | null } | null }; + +export type AddEventAdminMutationVariables = Exact<{ + eventId: Scalars['ID']['input']; + email: Scalars['String']['input']; +}>; + + +export type AddEventAdminMutation = { __typename?: 'Mutation', addEventAdmin?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', administrators?: Array<string | null> | null } | null } | null } | null }; + +export type DeleteEventAdminMutationVariables = Exact<{ + eventId: Scalars['ID']['input']; + email: Scalars['String']['input']; +}>; + + +export type DeleteEventAdminMutation = { __typename?: 'Mutation', deleteEventAdmin?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', administrators?: Array<string | null> | null } | null } | null } | null }; export type EventByUuidQueryVariables = Exact<{ uuid: Scalars['String']['input']; }>; -export type EventByUuidQuery = { __typename?: 'Query', eventByUUID?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null } | null } | null }; +export type EventByUuidQuery = { __typename?: 'Query', eventByUUID?: { __typename?: 'EventEntityResponse', data?: { __typename?: 'EventEntity', id?: string | null, attributes?: { __typename?: 'Event', uuid?: string | null, name: string, description?: string | null, enabled_modules?: any | null, email: string, administrators?: Array<string | null> | null, date?: any | null, address?: string | null, latitude?: number | null, longitude?: number | null, position?: any | null, waitingPassengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, email?: string | null, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null, travels?: { __typename?: 'TravelRelationResponseCollection', data: Array<{ __typename?: 'TravelEntity', id?: string | null, attributes?: { __typename?: 'Travel', meeting?: string | null, meeting_latitude?: number | null, meeting_longitude?: number | null, departure?: any | null, details?: string | null, vehicleName?: string | null, phone_number?: string | null, seats?: number | null, passengers?: { __typename?: 'PassengerRelationResponseCollection', data: Array<{ __typename?: 'PassengerEntity', id?: string | null, attributes?: { __typename?: 'Passenger', name: string, location?: string | null, user?: { __typename?: 'UsersPermissionsUserEntityResponse', data?: { __typename?: 'UsersPermissionsUserEntity', id?: string | null, attributes?: { __typename?: 'UsersPermissionsUser', firstName?: string | null, lastName?: string | null } | null } | null } | null } | null }> } | null } | null }> } | null } | null } | null } | null }; export type ModuleQueryVariables = Exact<{ locale: Scalars['I18NLocaleCode']['input'];

@@ -2079,6 +2289,7 @@ name

description enabled_modules email + administrators date address latitude

@@ -2416,6 +2627,84 @@ }

export type UpdateEventMutationHookResult = ReturnType<typeof useUpdateEventMutation>; export type UpdateEventMutationResult = Apollo.MutationResult<UpdateEventMutation>; export type UpdateEventMutationOptions = Apollo.BaseMutationOptions<UpdateEventMutation, UpdateEventMutationVariables>; +export const AddEventAdminDocument = gql` + mutation addEventAdmin($eventId: ID!, $email: String!) { + addEventAdmin(eventId: $eventId, email: $email) { + data { + id + attributes { + administrators + } + } + } +} + `; +export type AddEventAdminMutationFn = Apollo.MutationFunction<AddEventAdminMutation, AddEventAdminMutationVariables>; + +/** + * __useAddEventAdminMutation__ + * + * To run a mutation, you first call `useAddEventAdminMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddEventAdminMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addEventAdminMutation, { data, loading, error }] = useAddEventAdminMutation({ + * variables: { + * eventId: // value for 'eventId' + * email: // value for 'email' + * }, + * }); + */ +export function useAddEventAdminMutation(baseOptions?: Apollo.MutationHookOptions<AddEventAdminMutation, AddEventAdminMutationVariables>) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation<AddEventAdminMutation, AddEventAdminMutationVariables>(AddEventAdminDocument, options); + } +export type AddEventAdminMutationHookResult = ReturnType<typeof useAddEventAdminMutation>; +export type AddEventAdminMutationResult = Apollo.MutationResult<AddEventAdminMutation>; +export type AddEventAdminMutationOptions = Apollo.BaseMutationOptions<AddEventAdminMutation, AddEventAdminMutationVariables>; +export const DeleteEventAdminDocument = gql` + mutation deleteEventAdmin($eventId: ID!, $email: String!) { + deleteEventAdmin(eventId: $eventId, email: $email) { + data { + id + attributes { + administrators + } + } + } +} + `; +export type DeleteEventAdminMutationFn = Apollo.MutationFunction<DeleteEventAdminMutation, DeleteEventAdminMutationVariables>; + +/** + * __useDeleteEventAdminMutation__ + * + * To run a mutation, you first call `useDeleteEventAdminMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteEventAdminMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [deleteEventAdminMutation, { data, loading, error }] = useDeleteEventAdminMutation({ + * variables: { + * eventId: // value for 'eventId' + * email: // value for 'email' + * }, + * }); + */ +export function useDeleteEventAdminMutation(baseOptions?: Apollo.MutationHookOptions<DeleteEventAdminMutation, DeleteEventAdminMutationVariables>) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation<DeleteEventAdminMutation, DeleteEventAdminMutationVariables>(DeleteEventAdminDocument, options); + } +export type DeleteEventAdminMutationHookResult = ReturnType<typeof useDeleteEventAdminMutation>; +export type DeleteEventAdminMutationResult = Apollo.MutationResult<DeleteEventAdminMutation>; +export type DeleteEventAdminMutationOptions = Apollo.BaseMutationOptions<DeleteEventAdminMutation, DeleteEventAdminMutationVariables>; export const EventByUuidDocument = gql` query eventByUUID($uuid: String!) { eventByUUID(uuid: $uuid) {
M frontend/graphql/event.gqlfrontend/graphql/event.gql

@@ -6,6 +6,7 @@ name

description enabled_modules email + administrators date address latitude

@@ -98,6 +99,28 @@ mutation updateEvent($uuid: String!, $eventUpdate: EventInput!) {

updateEventByUUID(uuid: $uuid, data: $eventUpdate) { data { ...EventFields + } + } +} + +mutation addEventAdmin($eventId: ID!, $email: String!){ + addEventAdmin(eventId: $eventId, email: $email) { + data { + id + attributes { + administrators + } + } + } +} + +mutation deleteEventAdmin($eventId: ID!, $email: String!){ + deleteEventAdmin(eventId: $eventId, email: $email) { + data { + id + attributes { + administrators + } } } }
M frontend/hooks/usePermissions.tsfrontend/hooks/usePermissions.ts

@@ -29,6 +29,7 @@ const carosterPlus = event?.enabled_modules?.includes('caroster-plus');

const userIsAnonymous = !connected; const userIsEventCreator = event && profile?.email === event.email; + const userIsEventAdmin = event?.administrators?.includes(profile?.email); const travels = event?.travels?.data || [];

@@ -44,12 +45,12 @@ };

if (carosterPlus) { if (userIsAnonymous) return {userPermissions: noPermissions}; - if (userIsEventCreator) + if (userIsEventCreator || userIsEventAdmin) return {userPermissions: {...allPermissions, canAddToTravel: false}}; const editableTravelsCollection = travels.filter(({attributes}) => { - const isDriver = attributes.user?.data?.id === userId; - return !isDriver; + const userIsDriver = attributes.user?.data?.id === userId; + return !userIsDriver; }); const carosterPlusPermissions: UserPermissions = {
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -66,11 +66,15 @@ "event.title": "{{title}} - Caroster",

"options.plus.title": "Caroster plus", "options.plus.admins": "Administrators", "options.plus.creator": "Creator", - "options.plus.adminFeature": "Administrate your Caroster by adding administrators", - "options.plus.accessFeature": "Manage access and deletion rights", - "options.plus.nearbyTripFeature": "Receive notifications for nearby departures", "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.email": "Email", + "options.plus.addAdmin.emailHelper": "The email is not valid", + "options.plus.adminAdded": "The administrator has been invited", + "options.plus.addAdminError": "An error has occurred", + "options.plus.adminDeleted": "The administrator has been deleted", + "options.plus.deleteAdminError": "An error has occurred", "generic.access": "Access", "generic.add": "Add", "generic.cancel": "Cancel",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -66,11 +66,15 @@ "event.title": "{{title}} - Caroster",

"options.plus.title": "Caroster plus", "options.plus.admins": "Administrateurs", "options.plus.creator": "Créateur", - "options.plus.adminFeature": "Contrôlez votre Caroster en ajoutant des administrateurs", - "options.plus.accessFeature": "Personnalisez les accès et les droits de suppression", - "options.plus.nearbyTripFeature": "Recevez des notifications pour les départs proches", "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.email": "E-mail", + "options.plus.addAdmin.emailHelper": "L e-mail n'est pas valide", + "options.plus.adminAdded": "L'administrateur a été invité", + "options.plus.addAdminError": "Une erreur est survenue", + "options.plus.adminDeleted": "L'administrateur a été supprimé", + "options.plus.deleteAdminError": "Une erreur est survenue", "generic.access": "Accéder", "generic.add": "Ajouter", "generic.cancel": "Annuler",
M frontend/locales/nl.jsonfrontend/locales/nl.json

@@ -65,11 +65,15 @@ "event.title": "{{title}} - Caroster",

"options.plus.title": "Caroster plus", "options.plus.admins": "Beheerders", "options.plus.creator": "Schepper", - "options.plus.adminFeature": "Beheer uw Caroster door beheerders toe te voegen", - "options.plus.accessFeature": "Toegangs- en verwijderingsrechten aanpassen", - "options.plus.nearbyTripFeature": "Ontvang meldingen voor vertrekken in de buurt", "options.plus.activationOK": "Voeg Caroster plus toe aan uw evenement", "options.plus.activationForbiden": "Alleen de beheerder is bevoegd om modules toe te voegen", + "options.plus.addAdmin": "", + "options.plus.addAdmin.email": "", + "options.plus.addAdmin.emailHelper": "", + "options.plus.adminAdded": "", + "options.plus.addAdminError": "", + "options.plus.adminDeleted": "", + "options.plus.deleteAdminError": "", "generic.access": "Toegang", "generic.add": "toevoegen", "generic.cancel": "Annuleren",
M frontend/locales/pl.jsonfrontend/locales/pl.json

@@ -68,11 +68,15 @@ "event.title": "{{title}} - Caroster",

"options.plus.title": "", "options.plus.admins": "", "options.plus.creator": "", - "options.plus.adminFeature": "", - "options.plus.accessFeature": "", - "options.plus.nearbyTripFeature": "", "options.plus.activationOK": "", "options.plus.activationForbiden": "", + "options.plus.addAdmin": "", + "options.plus.addAdmin.email": "", + "options.plus.addAdmin.emailHelper": "", + "options.plus.adminAdded": "", + "options.plus.addAdminError": "", + "options.plus.adminDeleted": "", + "options.plus.deleteAdminError": "", "generic.access": "Dostęp", "generic.add": "toevoegen", "generic.cancel": "Anuluj",
M frontend/locales/sv.jsonfrontend/locales/sv.json

@@ -65,11 +65,15 @@ "event.title": "",

"options.plus.title": "", "options.plus.admins": "", "options.plus.creator": "", - "options.plus.adminFeature": "", - "options.plus.accessFeature": "", - "options.plus.nearbyTripFeature": "", "options.plus.activationOK": "", "options.plus.activationForbiden": "", + "options.plus.addAdmin": "", + "options.plus.addAdmin.email": "", + "options.plus.addAdmin.emailHelper": "", + "options.plus.adminAdded": "", + "options.plus.addAdminError": "", + "options.plus.adminDeleted": "", + "options.plus.deleteAdminError": "", "generic.access": "", "generic.add": "toevoegen", "generic.cancel": "",