diff --git a/src/hooks/api/offers.ts b/src/hooks/api/offers.ts
index 78ac4b1b9..b97ca0b02 100644
--- a/src/hooks/api/offers.ts
+++ b/src/hooks/api/offers.ts
@@ -49,7 +49,10 @@ const useGetOffersByCreatorQuery = (
advancedQuery?: string;
}
>,
- configuration: UseQueryOptions = {},
+ {
+ queryArguments,
+ ...configuration
+ }: UseQueryOptions & { queryArguments?: any } = {},
) => {
const defaultQuery = `creator:(${creator?.id} OR ${creator?.email})`;
const query = advancedQuery
@@ -70,6 +73,7 @@ const useGetOffersByCreatorQuery = (
...createSortingArgument(sortOptions),
...(calendarSummaryFormats &&
createEmbededCalendarSummaries(calendarSummaryFormats)),
+ ...(queryArguments ?? {}),
},
enabled: !!(creator?.id && creator?.email),
...configuration,
diff --git a/src/hooks/api/user.ts b/src/hooks/api/user.ts
index c260c7fc3..5c3b3cfa2 100644
--- a/src/hooks/api/user.ts
+++ b/src/hooks/api/user.ts
@@ -1,3 +1,4 @@
+import { User } from '@/types/User';
import { fetchFromApi, isErrorObject } from '@/utils/fetchFromApi';
import {
diff --git a/src/i18n/de.json b/src/i18n/de.json
index 35f444faf..fd1e8e54a 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -200,6 +200,11 @@
"events": "Wo findet diese Veranstaltung oder Aktivität statt?",
"places": "Wo ist dieser Ort oder Ort?"
},
+ "recent_locations": {
+ "title": "Wählen Sie einen Standort aus, den Sie kürzlich verwendet haben",
+ "info": "Wir haben die Standorte, für die Sie kürzlich eingegeben haben, hier für Sie gespeichert. Sie können sie mit einem Klick hinzufügen.",
+ "other": "Oder wählen Sie einen anderen Standort"
+ },
"validation_messages": {
"street_and_number": {
"required": "Ihre Straße und Hausnummer entsprechen nicht den Anforderungen."
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index 1d756db4d..9a17576ae 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -200,6 +200,11 @@
"events": "Où cet événement ou cette activité aura-t-il lieu?",
"places": "Où est cet endroit ou cet emplacement?"
},
+ "recent_locations": {
+ "title": "Choisissez un lieu que vous avez utilisé récemment",
+ "info": "Nous avons mis les lieux pour lesquels vous avez récemment entré ici pour vous. Vous pouvez les ajouter en un clic.",
+ "other": "Ou choisissez un autre lieu"
+ },
"validation_messages": {
"street_and_number": {
"required": "Votre rue et votre numéro ne répondent pas aux exigences."
diff --git a/src/i18n/nl.json b/src/i18n/nl.json
index a286318fc..18d0919bb 100644
--- a/src/i18n/nl.json
+++ b/src/i18n/nl.json
@@ -200,6 +200,11 @@
"events": "Waar vindt dit evenement of deze activiteit plaats?",
"places": "Waar is deze plaats of locatie?"
},
+ "recent_locations": {
+ "title": "Kies een locatie die je recent gebruikte",
+ "info": "We hebben de locaties waarvoor je recent invoerde hier voor je klaargezet. Met één klik voeg je ze toe.",
+ "other": "Of kies een andere locatie"
+ },
"validation_messages": {
"street_and_number": {
"required": "Je straat en nummer voldoen niet aan de vereisten."
diff --git a/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx b/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx
index ed1e373bc..1ab7b356c 100644
--- a/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx
+++ b/src/pages/steps/AdditionalInformationStep/OrganizerPicker.tsx
@@ -14,6 +14,7 @@ import { Values } from '@/types/Values';
import { Alert, AlertVariants } from '@/ui/Alert';
import { Badge, BadgeVariants } from '@/ui/Badge';
import { Button, ButtonVariants } from '@/ui/Button';
+import { ButtonCard } from '@/ui/ButtonCard';
import { FormElement } from '@/ui/FormElement';
import { Icon, Icons } from '@/ui/Icon';
import { Inline } from '@/ui/Inline';
@@ -81,63 +82,17 @@ const RecentUsedOrganizers = ({
? organizer.address[organizer.mainLanguage]
: organizer.address
: '';
+
return (
-
+ badge={isUitpasOrganizer(organizer) && }
+ description={
+ address && `${address.postalCode} ${address.addressLocality}`
+ }
+ />
);
})}
diff --git a/src/pages/steps/LocationStep.tsx b/src/pages/steps/LocationStep.tsx
index a4562d759..d6e3add8f 100644
--- a/src/pages/steps/LocationStep.tsx
+++ b/src/pages/steps/LocationStep.tsx
@@ -1,6 +1,7 @@
import { TFunction } from 'i18next';
+import { uniqBy } from 'lodash';
import getConfig from 'next/config';
-import { ChangeEvent, useEffect, useMemo, useState } from 'react';
+import React, { ChangeEvent, useEffect, useMemo, useState } from 'react';
import { Controller, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import * as yup from 'yup';
@@ -13,18 +14,25 @@ import {
useChangeLocationMutation,
useChangeOnlineUrlMutation,
useDeleteOnlineUrlMutation,
+ useGetEventByIdQuery,
} from '@/hooks/api/events';
-import { useGetEventByIdQuery } from '@/hooks/api/events';
-import { useGetPlaceByIdQuery } from '@/hooks/api/places';
-import { useChangeAddressMutation } from '@/hooks/api/places';
+import { useGetOffersByCreatorQuery } from '@/hooks/api/offers';
+import {
+ useChangeAddressMutation,
+ useGetPlaceByIdQuery,
+} from '@/hooks/api/places';
+import { useGetUserQuery } from '@/hooks/api/user';
+import { SupportedLanguage } from '@/i18n/index';
import { FormData as OfferFormData } from '@/pages/create/OfferForm';
import { Address } from '@/types/Address';
import { Countries, Country } from '@/types/Country';
import { AttendanceMode, AudienceType } from '@/types/Event';
+import { Offer } from '@/types/Offer';
import { Values } from '@/types/Values';
-import { Alert } from '@/ui/Alert';
+import { Alert, AlertVariants } from '@/ui/Alert';
import { parseSpacing } from '@/ui/Box';
import { Button, ButtonVariants } from '@/ui/Button';
+import { ButtonCard } from '@/ui/ButtonCard';
import { FormElement } from '@/ui/FormElement';
import { Icon, Icons } from '@/ui/Icon';
import { Inline } from '@/ui/Inline';
@@ -34,7 +42,8 @@ import { RadioButtonTypes } from '@/ui/RadioButton';
import { RadioButtonWithLabel } from '@/ui/RadioButtonWithLabel';
import { getStackProps, Stack, StackProps } from '@/ui/Stack';
import { Text, TextVariants } from '@/ui/Text';
-import { getValueFromTheme } from '@/ui/theme';
+import { Breakpoints, getValueFromTheme } from '@/ui/theme';
+import { getLanguageObjectOrFallback } from '@/utils/getLanguageObjectOrFallback';
import { parseOfferId } from '@/utils/parseOfferId';
import { prefixUrlWithHttp } from '@/utils/url';
@@ -58,6 +67,95 @@ const API_URL = publicRuntimeConfig.apiUrl;
const getGlobalValue = getValueFromTheme('global');
+const RecentLocations = ({ onFieldChange, ...props }) => {
+ const { t, i18n } = useTranslation();
+ const getUserQuery = useGetUserQuery();
+ const getOffersQuery = useGetOffersByCreatorQuery(
+ {
+ advancedQuery: '_exists_:location.id',
+ // @ts-expect-error
+ creator: getUserQuery?.data,
+ sortOptions: {
+ field: 'modified',
+ order: 'desc',
+ },
+ paginationOptions: { start: 0, limit: 20 },
+ },
+ {
+ queryArguments: {
+ workflowStatus: 'DRAFT,READY_FOR_VALIDATION,APPROVED',
+ addressCountry: '*',
+ },
+ },
+ );
+
+ const offers: (Offer & { location: any })[] =
+ // @ts-expect-error
+ getOffersQuery?.data?.member ?? [];
+ const locations = uniqBy(
+ offers?.map((offer) => offer.location),
+ '@id',
+ ).filter((location) => location && location.name.nl !== 'Online');
+
+ return (
+
+
+
+ {t('create.location.recent_locations.title')}
+
+
+
+
+ {t('create.location.recent_locations.info')}
+
+
+ {locations.map((location) => {
+ const address =
+ location.address[location.mainLanguage] ?? location.address;
+
+ return (
+
+ onFieldChange({
+ municipality: {
+ zip: address.postalCode,
+ label: `${address.postalCode} ${address.addressLocality}`,
+ name: address.addressLocality,
+ },
+ place: location,
+ })
+ }
+ title={getLanguageObjectOrFallback(
+ location.name,
+ i18n.language as SupportedLanguage,
+ location?.mainLanguage ?? 'nl',
+ )}
+ description={
+ address && (
+ <>
+ {address.streetAddress}
+
+ {address.postalCode} {address.addressLocality}
+ >
+ )
+ }
+ />
+ );
+ })}
+
+
+ );
+};
+
const useEditLocation = ({ scope, offerId }: UseEditArguments) => {
const { i18n } = useTranslation();
const changeAddressMutation = useChangeAddressMutation();
@@ -276,6 +374,34 @@ const LocationStep = ({
const { isOnline, municipality, country } =
field?.value as OfferFormData['location'];
+ const onFieldChange = (updatedValue) => {
+ updatedValue = { ...field.value, ...updatedValue };
+ field.onChange(updatedValue);
+ onChange(updatedValue);
+ field.onBlur();
+ };
+
+ const renderFieldWithRecentLocations = (children) => (
+
+ {!isOnline && (
+
+ )}
+
+ {!isOnline && (
+
+ {t('create.location.recent_locations.other')}
+
+ )}
+ {children}
+
+
+ );
+
const OnlineToggle = (
@@ -285,15 +411,11 @@ const LocationStep = ({
type={RadioButtonTypes.SWITCH}
checked={isOnline}
onChange={(e) => {
- const updatedValue = {
- ...field.value,
+ onFieldChange({
place: undefined,
municipality: undefined,
isOnline: e.target.checked,
- };
- field.onChange(updatedValue);
- field.onBlur();
- onChange(updatedValue);
+ });
}}
css={`
.custom-switch {
@@ -314,56 +436,60 @@ const LocationStep = ({
return (
{OnlineToggle}
- {
- const prefixedUrl =
- e.target.value === ''
- ? e.target.value
- : prefixUrlWithHttp(e.target.value);
- const updatedValue = {
- ...field?.value,
- onlineUrl: prefixedUrl,
- };
- field.onChange(updatedValue);
- if (isValidUrl(prefixedUrl)) {
- onChange(updatedValue);
- setHasOnlineUrlError(false);
- } else {
- setHasOnlineUrlError(true);
- }
- }}
- onChange={(e) => {
- setOnlineUrl(e.target.value);
- }}
- placeholder={t('create.location.online_url.placeholder')}
- />
- }
- id="online-url"
- label={t('create.location.online_url.label')}
- error={
- hasOnlineUrlError &&
- t('create.validation_messages.location.online_url')
- }
- info={
-
- {t('create.location.online_url.info')}
-
- }
- />
+ {renderFieldWithRecentLocations(
+ {
+ const prefixedUrl =
+ e.target.value === ''
+ ? e.target.value
+ : prefixUrlWithHttp(e.target.value);
+ const updatedValue = {
+ ...field?.value,
+ onlineUrl: prefixedUrl,
+ };
+ field.onChange(updatedValue);
+ if (isValidUrl(prefixedUrl)) {
+ onChange(updatedValue);
+ setHasOnlineUrlError(false);
+ } else {
+ setHasOnlineUrlError(true);
+ }
+ }}
+ onChange={(e) => {
+ setOnlineUrl(e.target.value);
+ }}
+ placeholder={t(
+ 'create.location.online_url.placeholder',
+ )}
+ />
+ }
+ id="online-url"
+ label={t('create.location.online_url.label')}
+ error={
+ hasOnlineUrlError &&
+ t('create.validation_messages.location.online_url')
+ }
+ info={
+
+ {t('create.location.online_url.info')}
+
+ }
+ />,
+ )}
);
}
if (!country || municipality?.zip === '0000') {
- return (
-
+ return renderFieldWithRecentLocations(
+ <>
{
- const updatedValue = {
- ...field.value,
+ onFieldChange({
country: Countries.BE,
municipality: undefined,
- };
- field.onChange(updatedValue);
- onChange(updatedValue);
+ });
setAudienceType(AudienceType.EVERYONE);
- field.onBlur();
}}
>
{t('create.location.country.change_location')}
@@ -390,157 +512,132 @@ const LocationStep = ({
{t('create.location.country.location_school_info')}
-
+ >,
);
}
if (!municipality) {
return (
-
+ <>
{scope === OfferTypes.EVENTS && OnlineToggle}
-
- {
- const updatedValue = {
- ...field.value,
- municipality: value,
- place: undefined,
- };
- field.onChange(updatedValue);
- onChange(updatedValue);
- field.onBlur();
- }}
- width="22rem"
- />
- {
- const updatedValue = {
- ...field.value,
- country: newCountry,
- };
- field.onChange(updatedValue);
- onChange(updatedValue);
- field.onBlur();
- }}
- css={`
- & button {
- margin-bottom: 0.3rem;
+ {renderFieldWithRecentLocations(
+
+ {
+ onFieldChange({
+ municipality: value,
+ place: undefined,
+ });
+ }}
+ width="22rem"
+ />
+
+ onFieldChange({ country: newCountry })
}
- `}
- />
-
-
-
+ css={`
+ & button {
+ margin-bottom: 0.3rem;
+ }
+ `}
+ />
+
+ ,
+ )}
+ >
);
}
- return (
-
-
-
-
- {municipality.name}
-
-
- {scope === OfferTypes.EVENTS && (
-
- )}
- {scope === OfferTypes.PLACES && (
-
- {field.value.streetAndNumber ? (
-
-
- {field.value.streetAndNumber}
-
-
- ) : (
- {
- const updatedValue = {
- ...field.value,
- streetAndNumber: streetAndNumber,
- };
- field.onChange(updatedValue);
- onChange(updatedValue);
- }}
- onChange={handleChangeStreetAndNumber}
- />
- }
- id="location-streetAndNumber"
- label={t('location.add_modal.labels.streetAndNumber')}
- maxWidth="28rem"
- error={
- formState.errors.location?.streetAndNumber &&
- t('location.add_modal.errors.streetAndNumber')
- }
+ return renderFieldWithRecentLocations(
+ <>
+
+
+ {municipality.name}
+
+
+ {scope === OfferTypes.EVENTS && (
+
+ )}
+ {scope === OfferTypes.PLACES && (
+
+ {field.value.streetAndNumber ? (
+
+
- )}
-
- )}
-
-
+ {field.value.streetAndNumber}
+
+
+ ) : (
+ onFieldChange({ streetAndNumber })}
+ onChange={handleChangeStreetAndNumber}
+ />
+ }
+ id="location-streetAndNumber"
+ label={t('location.add_modal.labels.streetAndNumber')}
+ maxWidth="28rem"
+ error={
+ formState.errors.location?.streetAndNumber &&
+ t('location.add_modal.errors.streetAndNumber')
+ }
+ />
+ )}
+
+ )}
+ >,
);
}}
/>
diff --git a/src/pages/steps/PlaceStep.tsx b/src/pages/steps/PlaceStep.tsx
index c6d1b3a0e..30a90ab9b 100644
--- a/src/pages/steps/PlaceStep.tsx
+++ b/src/pages/steps/PlaceStep.tsx
@@ -39,6 +39,7 @@ type PlaceStepProps = StackProps &
country?: Country;
chooseLabel: (t: TFunction) => string;
placeholderLabel: (t: TFunction) => string;
+ onFieldChange: StepProps['onChange'];
};
const PlaceStep = ({
@@ -48,7 +49,7 @@ const PlaceStep = ({
control,
name,
loading,
- onChange,
+ onFieldChange,
terms,
municipality,
country,
@@ -134,10 +135,7 @@ const PlaceStep = ({
municipality={municipality}
country={country}
onConfirmSuccess={(place) => {
- const updatedValue = { ...field.value, place };
- field.onChange(updatedValue);
- onChange(updatedValue);
- field.onBlur();
+ onFieldChange({ place });
setIsPlaceAddModalVisible(false);
}}
/>
@@ -202,10 +200,7 @@ const PlaceStep = ({
return;
}
- const updatedValue = { ...field.value, place };
-
- field.onChange(updatedValue);
- onChange(updatedValue);
+ onFieldChange({ place });
}}
minLength={3}
placeholder={placeholderLabel(t)}
@@ -228,8 +223,11 @@ const PlaceStep = ({
color={getGlobalValue('successIcon')}
/>
- {selectedPlace.name[i18n.language] ??
- selectedPlace.name[selectedPlace.mainLanguage]}
+ {getLanguageObjectOrFallback(
+ selectedPlace.name,
+ i18n.language as SupportedLanguage,
+ selectedPlace.mainLanguage ?? 'nl',
+ )}