From 5a7261d4053de8cc2069837c4355d2025d37a4a5 Mon Sep 17 00:00:00 2001 From: Yaroslav Kosterin Date: Wed, 19 Feb 2025 19:21:03 +0200 Subject: [PATCH 1/9] add filters to identities page --- .../pages/identities/Identities.tsx | 118 +++++++++++++++++- react/src/dashboard/i18n/i18n-nl.js | 10 ++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/react/src/dashboard/components/pages/identities/Identities.tsx b/react/src/dashboard/components/pages/identities/Identities.tsx index 849daa74d..2c30732e3 100644 --- a/react/src/dashboard/components/pages/identities/Identities.tsx +++ b/react/src/dashboard/components/pages/identities/Identities.tsx @@ -26,6 +26,9 @@ import TableDateTime from '../../elements/tables/elements/TableDateTime'; import TableEmptyValue from '../../elements/table-empty-value/TableEmptyValue'; import TableDateOnly from '../../elements/tables/elements/TableDateOnly'; import useIdentityExportService from '../../../services/exports/useIdentityExportService'; +import DatePickerControl from '../../elements/forms/controls/DatePickerControl'; +import { dateFormat, dateParse } from '../../../helpers/dates'; +import SelectControlOptions from '../../elements/select-control/templates/SelectControlOptions'; export default function Identities() { const translate = useTranslate(); @@ -43,6 +46,12 @@ export default function Identities() { const [funds, setFunds] = useState>>(null); const [identities, setIdentities] = useState>(null); + const [hasBsnOptions] = useState([ + { key: 1, name: 'Ja' }, + { key: 0, name: 'Nee' }, + { key: null, name: 'Alle' }, + ]); + const [states] = useState([ { key: null, name: 'Alle' }, { key: 'pending', name: 'In afwachting' }, @@ -55,8 +64,18 @@ export default function Identities() { q: string; state: string; page?: number; + city?: string; + has_bsn?: number; fund_id?: number; per_page?: number; + postal_code?: string; + municipality_name?: string; + birth_date_to?: string; + birth_date_from?: string; + last_login_to?: string; + last_login_from?: string; + last_activity_to?: string; + last_activity_from?: string; }>( { q: '', @@ -66,13 +85,23 @@ export default function Identities() { per_page: paginatorService.getPerPage(paginatorTransactionsKey), }, { - throttledValues: ['q'], + throttledValues: ['q', 'postal_code', 'city', 'municipality_name'], queryParams: { q: StringParam, state: StringParam, page: NumberParam, + city: StringParam, + has_bsn: NumberParam, fund_id: NumberParam, per_page: NumberParam, + postal_code: StringParam, + municipality_name: StringParam, + birth_date_to: StringParam, + birth_date_from: StringParam, + last_login_to: StringParam, + last_login_from: StringParam, + last_activity_to: StringParam, + last_activity_from: StringParam, }, }, ); @@ -186,6 +215,93 @@ export default function Identities() { /> + + filterUpdate({ birth_date_from: dateFormat(from) })} + /> + + + + filterUpdate({ birth_date_to: dateFormat(to) })} + /> + + + + filterUpdate({ postal_code: e.target.value })} + placeholder={translate('sponsor_products.filters.postal_code')} + /> + + + + filterUpdate({ municipality_name: e.target.value })} + placeholder={translate('sponsor_products.filters.municipality')} + /> + + + + filterUpdate({ city: e.target.value })} + placeholder={translate('sponsor_products.filters.city')} + /> + + + + filterUpdate({ last_activity_from: dateFormat(from) })} + /> + + + + filterUpdate({ last_activity_to: dateFormat(to) })} + /> + + + + filterUpdate({ last_login_from: dateFormat(from) })} + /> + + + + filterUpdate({ last_login_to: dateFormat(to) })} + /> + + + + filterUpdate({ has_bsn })} + /> + +
+ )} +
+ + )} + {editing && ( +
+
Mijn adresgegevens
+
+
+ + form.update({ street: e.target.value })} + data-dusk="productReserveFormStreet" + /> + +
+
+
+
+ + form.update({ house_nr: e.target.value })} + data-dusk="productReserveFormHouseNumber" + /> + +
+
+ + form.update({ house_nr_addition: e.target.value })} + data-dusk="productReserveFormHouseNumberAddition" + /> + +
+
+
+
+
+
+ + form.update({ postal_code: e.target.value })} + data-dusk="productReserveFormPostalCode" + /> + +
+ +
+ + form.update({ city: e.target.value })} + data-dusk="productReserveFormCity" + /> + +
+
+
+ )} + {editing && ( +
+
+ +
+
+
+ {addressFilled(address) && ( + + )} + {!addressProfile && ( + + )} + +
+
+ )} +
+ ); +} diff --git a/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx b/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx index 2804dd4ea..cd07b418b 100644 --- a/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx +++ b/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx @@ -25,6 +25,11 @@ import DatePickerControl from '../../../../dashboard/components/elements/forms/c import { dateFormat, dateParse } from '../../../../dashboard/helpers/dates'; import Tooltip from '../../elements/tooltip/Tooltip'; import { clickOnKeyEnter } from '../../../../dashboard/helpers/wcag'; +import BlockReservationAddress, { AddressType } from '../../elements/block-reservation-address/BlockReservationAddress'; +import { useProfileService } from '../../../../dashboard/services/ProfileService'; +import { ErrorResponse } from 'react-router-dom'; +import usePushSuccess from '../../../../dashboard/hooks/usePushSuccess'; +import classNames from 'classnames'; type VoucherType = Voucher & { amount_extra: number; @@ -60,10 +65,13 @@ export default function ModalProductReserve({ const assetUrl = useAssetUrl(); const translate = useTranslate(); const pushDanger = usePushDanger(); + const pushSuccess = usePushSuccess(); const navigateState = useNavigateState(); const productService = useProductService(); + const profileService = useProfileService(); const productReservationService = useProductReservationService(); + const composerVoucherCardData = useComposeVoucherCardData(); const [STEP_EMAIL_SETUP] = useState(0); @@ -79,10 +87,14 @@ export default function ModalProductReserve({ const [submitting, setSubmitting] = useState(false); const [errorMessage, setErrorMessage] = useState(); const [reservationId, setReservationId] = useState(null); + const [isEditingAddress, setIsEditingAddress] = useState(false); const [dateMinLimit] = useState(new Date()); const provider = useMemo(() => product.organization, [product?.organization]); + const [address, setAddress] = useState(null); + const [addressProfile, setAddressProfile] = useState(null); + const hasEmail = useMemo(() => { return !!authIdentity.email; }, [authIdentity?.email]); @@ -124,34 +136,26 @@ export default function ModalProductReserve({ const [emptyText] = useState(translate('modal_reserve_product.confirm_notes.labels.empty')); const [voucher, setVoucher] = useState(null); + const addressFilled = useCallback((address: AddressType) => { + return !!(address?.city && address?.street && address?.house_nr && address?.postal_code); + }, []); + const form = useFormBuilder<{ first_name?: string; last_name?: string; - postal_code?: string; - address?: string; - street?: string; - house_nr?: string; - house_nr_addition?: string; - city?: string; user_note?: string; custom_fields?: { [key: string]: string }; }>( { first_name: '', last_name: '', - postal_code: '', - address: '', - street: '', - house_nr: '', - house_nr_addition: '', - city: '', custom_fields: {}, }, (values) => { setSubmitting(true); productReservationService - .reserve({ ...values, voucher_id: voucher.id, product_id: product.id }) + .reserve({ ...values, ...address, voucher_id: voucher.id, product_id: product.id }) .then((res) => { setSubmitting(false); setReservationId(res.data.id); @@ -234,25 +238,15 @@ export default function ModalProductReserve({ [form, next, onError, product.id, productReservationService, voucher?.id], ); - const validateAddress = useCallback(() => { - productReservationService - .validateAddress({ - address: form.values.postal_code ? form.values.address : null, - street: form.values.street, - house_nr: form.values.house_nr, - house_nr_addition: form.values.house_nr_addition, - city: form.values.city, - postal_code: form.values.postal_code, + const validateAddress = useCallback( + (address: AddressType) => { + return productReservationService.validateAddress({ + ...address, product_id: product?.id, - }) - .then( - () => { - form.errors = {}; - setStep((step) => step + 1); - }, - (err) => onError(err, true), - ); - }, [form, onError, product?.id, productReservationService]); + }); + }, + [product?.id, productReservationService], + ); const confirmSubmit = useCallback(() => { form.submit(); @@ -311,6 +305,44 @@ export default function ModalProductReserve({ [makeReservationField, product?.reservation?.birth_date, product?.reservation?.phone], ); + const fetchProfileAddress = useCallback(() => { + profileService.profile().then((res) => { + const address = { + city: res.data?.records?.city?.[0]?.value_locale, + street: res.data?.records?.street?.[0]?.value_locale, + house_nr: res.data?.records?.house_number?.[0]?.value_locale, + house_nr_addition: res.data?.records?.house_number_addition?.[0]?.value_locale, + postal_code: res.data?.records?.postal_code?.[0]?.value_locale, + }; + + if (addressFilled(address)) { + setAddress(address); + setAddressProfile(address); + } + }); + }, [profileService, addressFilled]); + + const updateProfileAddress = useCallback( + (data: AddressType) => { + profileService + .update({ + city: data.city, + street: data.street, + house_number: data.house_nr, + house_number_addition: data.house_nr_addition, + postal_code: data.postal_code, + }) + .then(() => { + fetchProfileAddress(); + pushSuccess(translate('push.saved')); + }) + .catch((err: ErrorResponse) => { + pushDanger(translate('push.error'), err?.data?.message || err?.data?.errors?.product_id?.[0]); + }); + }, + [fetchProfileAddress, profileService, pushDanger, pushSuccess, translate], + ); + const addEmail = useCallback(() => { navigateState('identity-emails'); modal.close(); @@ -368,6 +400,10 @@ export default function ModalProductReserve({ }; }, [fundMeta?.shownExpireDate?.unix, product.name, product.organization.name, product.price, vouchers]); + useEffect(() => { + fetchProfileAddress(); + }, [fetchProfileAddress]); + useEffect(() => { updateSteps(); }, [updateSteps]); @@ -842,7 +878,14 @@ export default function ModalProductReserve({ className="modal-window form form-compact" onSubmit={(e) => { e?.preventDefault(); - validateAddress(); + + if (addressFilled(address) || product.reservation.address !== 'optional') { + validateAddress(address) + ?.then(() => setStep((step) => step + 1)) + ?.catch((err: ResponseError) => onError(err, true)); + } else { + setStep((step) => step + 1); + } }} data-dusk="productReserveAddress">
-
-
-
-
- - form.update({ street: e.target.value })} - data-dusk="productReserveFormStreet" - /> - -
-
- - form.update({ house_nr: e.target.value })} - data-dusk="productReserveFormHouseNumber" - /> - -
-
- - form.update({ house_nr_addition: e.target.value })} - data-dusk="productReserveFormHouseNumberAddition" - /> - -
-
- -
-
- - form.update({ postal_code: e.target.value })} - data-dusk="productReserveFormPostalCode" - /> - -
-
- - form.update({ city: e.target.value })} - data-dusk="productReserveFormCity" - /> - -
-
+ { + setAddress(values); + + if (save) { + updateProfileAddress(values); + } + }} + />
@@ -961,7 +941,14 @@ export default function ModalProductReserve({ -
@@ -1126,10 +1113,11 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.street')}
- {form.values.street || emptyText} + className={classNames( + `overview-item-value`, + !address?.street && 'overview-item-value-empty', + )}> + {address?.street || emptyText}
)} @@ -1140,10 +1128,11 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.house_nr')}
- {form.values.house_nr || emptyText} + className={classNames( + `overview-item-value`, + !address?.house_nr && 'overview-item-value-empty', + )}> + {address?.house_nr || emptyText}
)} @@ -1154,10 +1143,11 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.house_nr_addition')}
- {form.values.house_nr_addition || emptyText} + className={classNames( + `overview-item-value`, + !address?.house_nr_addition && 'overview-item-value-empty', + )}> + {address?.house_nr_addition || emptyText}
)} @@ -1168,10 +1158,11 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.postal_code')}
- {form.values.postal_code || emptyText} + className={classNames( + `overview-item-value`, + !address?.postal_code && 'overview-item-value-empty', + )}> + {address?.postal_code || emptyText}
)} @@ -1182,10 +1173,11 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.city')}
- {translate(form.values.city || emptyText)} + className={classNames( + `overview-item-value`, + !address?.city && 'overview-item-value-empty', + )}> + {translate(address?.city || emptyText)}
)} diff --git a/react/src/webshop/props/models/Product.ts b/react/src/webshop/props/models/Product.ts index 92d4e4e9c..abc46645a 100644 --- a/react/src/webshop/props/models/Product.ts +++ b/react/src/webshop/props/models/Product.ts @@ -2,8 +2,8 @@ import BaseProduct from '../../../dashboard/props/models/Product'; export default interface Product extends BaseProduct { reservation?: { - address: 'no'; - birth_date: 'no'; + address: 'no' | 'optional' | 'required'; + birth_date: 'no' | 'optional' | 'required'; fields: Array<{ id?: number; type?: 'text' | 'number'; From 2520ee301931d1c768557e4abcf438bdde5bc4ea Mon Sep 17 00:00:00 2001 From: dev-rminds Date: Fri, 21 Feb 2025 00:41:18 +0200 Subject: [PATCH 4/9] reservation address mobile version --- .../blocks/block-reservation-address.scss | 42 +++++- .../BlockReservationAddress.tsx | 137 +++++++++--------- .../ModalProductReserve.tsx | 2 + 3 files changed, 111 insertions(+), 70 deletions(-) diff --git a/react/assets/forus-webshop/scss/_common/sections/blocks/block-reservation-address.scss b/react/assets/forus-webshop/scss/_common/sections/blocks/block-reservation-address.scss index 1a71dc7c7..fa8fa86c2 100644 --- a/react/assets/forus-webshop/scss/_common/sections/blocks/block-reservation-address.scss +++ b/react/assets/forus-webshop/scss/_common/sections/blocks/block-reservation-address.scss @@ -7,11 +7,17 @@ .address-overview { display: flex; - flex-direction: row; + flex-direction: column; padding: 15px; border-bottom: 1px solid var(--border-color); - gap: 12px; - align-items: center; + gap: 10px; + + .address-overview-row { + display: flex; + flex-direction: row; + gap: 10px; + width: 100%; + } .address-overview-icon { flex: 0 0 42px; @@ -89,5 +95,35 @@ display: flex; flex-direction: row; padding: 15px; + gap: 10px; + + .button { + margin-right: 0; + } + } + + @media screen and (max-width: 1000px) { + .address-overview { + flex-wrap: wrap; + align-items: flex-start; + + .address-overview-icon { + flex: 0 0 36px; + height: 36px; + line-height: 36px; + font-size: 20px; + } + + .address-overview-actions { + .button { + display: flex; + justify-content: center; + } + } + } + + .address-actions { + flex-direction: column; + } } } diff --git a/react/src/webshop/components/elements/block-reservation-address/BlockReservationAddress.tsx b/react/src/webshop/components/elements/block-reservation-address/BlockReservationAddress.tsx index 2393ebcf9..4a5215858 100644 --- a/react/src/webshop/components/elements/block-reservation-address/BlockReservationAddress.tsx +++ b/react/src/webshop/components/elements/block-reservation-address/BlockReservationAddress.tsx @@ -6,6 +6,7 @@ import useFormBuilder from '../../../../dashboard/hooks/useFormBuilder'; import { ResponseError } from '../../../../dashboard/props/ApiResponses'; import { useProductReservationService } from '../../../services/ProductReservationService'; import Product from '../../../props/models/Product'; +import useIsMobile from '../../../hooks/useIsMobile'; export type AddressType = { postal_code?: string; @@ -31,6 +32,7 @@ export default function BlockReservationAddress({ }) { const [editing, setEditing] = useState(false); const [submitting, setSubmitting] = useState(false); + const isMobile = useIsMobile(); const translate = useTranslate(); const productReservationService = useProductReservationService(); @@ -86,6 +88,16 @@ export default function BlockReservationAddress({ return !!(address?.city && address?.street && address?.house_nr && address?.postal_code); }, []); + const editButton = ( + + ); + useEffect(() => { if (!address) { setEditing(true); @@ -102,29 +114,22 @@ export default function BlockReservationAddress({
-
- -
-
-
Adres wijzigen
-
- {getAddressString(address)} +
+
+ +
+
+
Adres wijzigen
+
+ {getAddressString(address)} +
+ {!isMobile && !editing &&
{editButton}
}
-
- {!editing && ( - - )} -
+ {isMobile && !editing &&
{editButton}
}
)} {editing && ( @@ -147,7 +152,7 @@ export default function BlockReservationAddress({
-
+
@@ -161,7 +166,7 @@ export default function BlockReservationAddress({ />
-
+
@@ -213,59 +218,57 @@ export default function BlockReservationAddress({ )} {editing && (
-
+ + +
+ + {addressFilled(address) && ( -
-
-
- {addressFilled(address) && ( - - )} - {!addressProfile && ( - - )} + )} + {!addressProfile && ( -
+ )} +
)}
diff --git a/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx b/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx index cd07b418b..4d3229fa8 100644 --- a/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx +++ b/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx @@ -915,6 +915,8 @@ export default function ModalProductReserve({ steps={steps} finalStep={STEP_RESERVATION_FINISHED} /> +
+
Date: Fri, 21 Feb 2025 15:29:31 +0200 Subject: [PATCH 5/9] add skip button for optional reservation address --- .../ModalProductReserve.tsx | 42 ++++++++++++++----- .../i18n/nl/modals/modal_reserve_product.mjs | 1 + react/src/webshop/i18n/translated/ar.json | 3 +- react/src/webshop/i18n/translated/de.json | 3 +- react/src/webshop/i18n/translated/en-US.json | 3 +- react/src/webshop/i18n/translated/fr.json | 3 +- react/src/webshop/i18n/translated/pl.json | 3 +- react/src/webshop/i18n/translated/ru.json | 3 +- react/src/webshop/i18n/translated/tr.json | 3 +- react/src/webshop/i18n/translated/uk.json | 3 +- translations/cache/cache.json | 4 ++ 11 files changed, 52 insertions(+), 19 deletions(-) diff --git a/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx b/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx index 4d3229fa8..93dd5a604 100644 --- a/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx +++ b/react/src/webshop/components/modals/modal-product-reserve/ModalProductReserve.tsx @@ -85,6 +85,7 @@ export default function ModalProductReserve({ const [STEP_ERROR] = useState(8); const [submitting, setSubmitting] = useState(false); + const [skipAddress, setSkipAddress] = useState(false); const [errorMessage, setErrorMessage] = useState(); const [reservationId, setReservationId] = useState(null); const [isEditingAddress, setIsEditingAddress] = useState(false); @@ -155,7 +156,7 @@ export default function ModalProductReserve({ setSubmitting(true); productReservationService - .reserve({ ...values, ...address, voucher_id: voucher.id, product_id: product.id }) + .reserve({ ...values, ...(skipAddress ? {} : address), voucher_id: voucher.id, product_id: product.id }) .then((res) => { setSubmitting(false); setReservationId(res.data.id); @@ -878,6 +879,7 @@ export default function ModalProductReserve({ className="modal-window form form-compact" onSubmit={(e) => { e?.preventDefault(); + setSkipAddress(false); if (addressFilled(address) || product.reservation.address !== 'optional') { validateAddress(address) @@ -943,6 +945,18 @@ export default function ModalProductReserve({ + {addressFilled(address) && product.reservation.address === 'optional' && ( + + )}
)} @@ -1130,11 +1145,12 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.house_nr')}
- {address?.house_nr || emptyText} + {(!skipAddress && address?.house_nr) || emptyText}
)} @@ -1145,11 +1161,13 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.house_nr_addition')}
- {address?.house_nr_addition || emptyText} + {(!skipAddress && address?.house_nr_addition) || emptyText}
)} @@ -1160,11 +1178,12 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.postal_code')}
- {address?.postal_code || emptyText} + {(!skipAddress && address?.postal_code) || emptyText}
)} @@ -1175,11 +1194,12 @@ export default function ModalProductReserve({ {translate('modal_reserve_product.confirm_notes.labels.city')}
- {translate(address?.city || emptyText)} + {(!skipAddress && address?.city) || emptyText}
)} diff --git a/react/src/webshop/i18n/nl/modals/modal_reserve_product.mjs b/react/src/webshop/i18n/nl/modals/modal_reserve_product.mjs index 8c0978c5a..e769ebe1e 100644 --- a/react/src/webshop/i18n/nl/modals/modal_reserve_product.mjs +++ b/react/src/webshop/i18n/nl/modals/modal_reserve_product.mjs @@ -114,6 +114,7 @@ export default { next: 'Volgende', confirm: 'Bevestigen', submit: 'Indienen', + skip: 'Overslaan', adjust: 'Terug', processing: 'Verwerken', go_to_reservation: 'Ga naar reservering', diff --git a/react/src/webshop/i18n/translated/ar.json b/react/src/webshop/i18n/translated/ar.json index 33f82edee..388d98bb3 100644 --- a/react/src/webshop/i18n/translated/ar.json +++ b/react/src/webshop/i18n/translated/ar.json @@ -2998,7 +2998,8 @@ "go_to_reservation": "الانتقال إلى الحجز", "next": "التالي", "processing": "العملية", - "submit": "إرسال" + "submit": "إرسال", + "skip": "تخطي" }, "cancel": "إلغاء", "choose_credit": "اختر الرصيد الذي ستدفع به", diff --git a/react/src/webshop/i18n/translated/de.json b/react/src/webshop/i18n/translated/de.json index baf7cb2b3..e98350f4b 100644 --- a/react/src/webshop/i18n/translated/de.json +++ b/react/src/webshop/i18n/translated/de.json @@ -1596,7 +1596,8 @@ "go_to_reservation": "Zur Buchung gehen", "next": "Weiter", "processing": "Prozess", - "submit": "Einreichen" + "submit": "Einreichen", + "skip": "Überspringen" }, "cancel": "Abbrechen", "choose_credit": "Wählen Sie das Guthaben, mit dem Sie bezahlen möchten", diff --git a/react/src/webshop/i18n/translated/en-US.json b/react/src/webshop/i18n/translated/en-US.json index dc944cd6e..ecfb6312b 100644 --- a/react/src/webshop/i18n/translated/en-US.json +++ b/react/src/webshop/i18n/translated/en-US.json @@ -3322,7 +3322,8 @@ "go_to_reservation": "Go to reservation", "processing": "Process", "submit": "Submit", - "next": "Next" + "next": "Next", + "skip": "Skip" }, "cancel": "Cancel", "choose_credit": "Choose the credit to pay with", diff --git a/react/src/webshop/i18n/translated/fr.json b/react/src/webshop/i18n/translated/fr.json index 43c232547..124c8a967 100644 --- a/react/src/webshop/i18n/translated/fr.json +++ b/react/src/webshop/i18n/translated/fr.json @@ -1596,7 +1596,8 @@ "go_to_reservation": "Aller à la réservation", "next": "Suivant", "processing": "Processus", - "submit": "Soumettre" + "submit": "Soumettre", + "skip": "Sauter" }, "cancel": "Annuler", "choose_credit": "Choisir le crédit pour payer", diff --git a/react/src/webshop/i18n/translated/pl.json b/react/src/webshop/i18n/translated/pl.json index 22133eca5..382e9b9f6 100644 --- a/react/src/webshop/i18n/translated/pl.json +++ b/react/src/webshop/i18n/translated/pl.json @@ -1596,7 +1596,8 @@ "go_to_reservation": "Przejdź do rezerwacji", "next": "Następny", "processing": "Proces", - "submit": "Prześlij" + "submit": "Prześlij", + "skip": "Pomiń" }, "cancel": "Anuluj", "choose_credit": "Wybierz kredyt, którym chcesz zapłacić", diff --git a/react/src/webshop/i18n/translated/ru.json b/react/src/webshop/i18n/translated/ru.json index 24a1b487c..aa0174ea1 100644 --- a/react/src/webshop/i18n/translated/ru.json +++ b/react/src/webshop/i18n/translated/ru.json @@ -1596,7 +1596,8 @@ "go_to_reservation": "Перейти к бронированию", "next": "Следующий", "processing": "Процесс", - "submit": "Отправить" + "submit": "Отправить", + "skip": "Скип" }, "cancel": "Отмена", "choose_credit": "Выберите кредит для оплаты", diff --git a/react/src/webshop/i18n/translated/tr.json b/react/src/webshop/i18n/translated/tr.json index 6ea56c1b6..53cfe18e7 100644 --- a/react/src/webshop/i18n/translated/tr.json +++ b/react/src/webshop/i18n/translated/tr.json @@ -2998,7 +2998,8 @@ "go_to_reservation": "Rezervasyona git", "next": "Sonraki", "processing": "Süreç", - "submit": "Gönder" + "submit": "Gönder", + "skip": "Atla" }, "cancel": "İptal", "choose_credit": "Ödeme yapacağınız krediyi seçin", diff --git a/react/src/webshop/i18n/translated/uk.json b/react/src/webshop/i18n/translated/uk.json index 961daf05d..2be84e912 100644 --- a/react/src/webshop/i18n/translated/uk.json +++ b/react/src/webshop/i18n/translated/uk.json @@ -1596,7 +1596,8 @@ "go_to_reservation": "Перейти до бронювання", "next": "Далі", "processing": "Процес", - "submit": "Надіслати" + "submit": "Надіслати", + "skip": "Пропустити." }, "cancel": "Скасувати", "choose_credit": "Виберіть кредит для оплати", diff --git a/translations/cache/cache.json b/translations/cache/cache.json index 1949663fa..d2b52215b 100644 --- a/translations/cache/cache.json +++ b/translations/cache/cache.json @@ -4335,6 +4335,10 @@ "modal_reserve_product.buttons.processing", "Verwerken" ], + [ + "modal_reserve_product.buttons.skip", + "Overslaan" + ], [ "modal_reserve_product.buttons.submit", "Indienen" From 7e1bcbae0dc03fa67323fde79c61c820976adf86 Mon Sep 17 00:00:00 2001 From: dev-rminds Date: Tue, 25 Feb 2025 21:03:11 +0200 Subject: [PATCH 6/9] hide profile menu on mobile --- .../sections/blocks/block-profile.scss | 260 +++++++++++++++ .../scss/_common/sections/blocks/profile.scss | 309 ------------------ .../_common/sections/blocks/vouchers.scss | 8 + react/assets/forus-webshop/scss/webshop.scss | 2 +- .../pages/vouchers/elements/VoucherCard.tsx | 2 +- 5 files changed, 270 insertions(+), 311 deletions(-) create mode 100644 react/assets/forus-webshop/scss/_common/sections/blocks/block-profile.scss delete mode 100644 react/assets/forus-webshop/scss/_common/sections/blocks/profile.scss diff --git a/react/assets/forus-webshop/scss/_common/sections/blocks/block-profile.scss b/react/assets/forus-webshop/scss/_common/sections/blocks/block-profile.scss new file mode 100644 index 000000000..ac3ffb4ce --- /dev/null +++ b/react/assets/forus-webshop/scss/_common/sections/blocks/block-profile.scss @@ -0,0 +1,260 @@ +.block.block-profile { + @include cf(); + + .profile-aside { + width: 25%; + float: left; + position: relative; + z-index: 2; + + .profile-card { + background: #fff; + border-radius: 10px; + box-shadow: 0 5px 30px rgba(0, 0, 0, 0.05); + padding: 20px; + margin-bottom: 30px; + cursor: default; + + .profile-qr_code { + padding-top: 10px; + width: 100%; + margin-bottom: 25px; + + img { + display: block; + margin: 0 auto; + max-width: 100%; + width: 160px; + } + } + + .profile-details { + text-align: center; + + .profile-heading { + font: 700 18px var(--base-font); + color: #315efd; + margin-bottom: 5px; + } + + .profile-description { + font: 400 13px var(--base-font); + color: #646f79; + margin-bottom: 20px; + } + } + + .profile-address { + margin: 0 -20px; + padding: 20px; + border-top: 1px solid #e9ecee; + border-bottom: 1px solid #e9ecee; + + .profile-address-label { + font: 400 10px var(--base-font); + color: #646f79; + margin-bottom: 5px; + } + + .profile-address-value { + font: 700 16px var(--base-font); + color: #282b39; + @include ellipsis(); + } + } + + .profile-actions { + margin: 0 -20px -40px; + padding: 20px 20px 20px; + @include cf(); + + .profile-action { + font: 700 12px var(--base-font); + color: #2f5bf6; + float: left; + margin-right: 30px; + margin-bottom: 20px; + transition: color 0.4s; + cursor: pointer; + @include cf(); + + .mdi { + color: inherit; + float: left; + margin-right: 5px; + font-size: 1.6em; + line-height: initial; + margin-top: -0.25em; + } + + &:hover { + color: var(--color-primary-light); + } + } + } + } + + .profile-aside-block { + position: relative; + padding: 20px; + width: 100%; + background: #fff; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + margin-bottom: 20px; + } + + .profile-menu { + .profile-menu-item { + padding: 14px 19px; + font: 700 13px/20px var(--base-font); + color: var(--profile-menu-item-color); + box-shadow: var(--profile-menu-item-shadow); + background-color: var(--profile-menu-item-background); + border: 1px solid var(--profile-menu-item-border-color); + border-radius: var(--profile-menu-item-border-radius); + margin-bottom: 15px; + display: block; + position: relative; + transition: + color 0.4s, + background 0.4s, + box-shadow 0.4s, + border 0.4s; + + .mdi { + color: var(--profile-menu-item-color); + position: absolute; + right: 15px; + top: 50%; + transform: translate(0, -50%); + font-size: 20px; + transition: color 0.4s; + } + + &.active, + &:hover { + color: var(--profile-menu-item-hover-color); + box-shadow: var(--profile-menu-item-hover-shadow); + background-color: var(--profile-menu-item-hover-background); + border: 1px solid var(--profile-menu-item-hover-border-color); + border-radius: var(--profile-menu-item-hover-border-radius); + + .mdi { + right: 10px; + color: var(--profile-menu-item-hover-color); + } + } + } + } + } + + .profile-content { + float: left; + position: relative; + z-index: 2; + width: 75%; + padding-left: 30px; + + .profile-content-header { + position: relative; + z-index: 1; + margin-bottom: 15px; + + .profile-content-title { + font: 700 22px/32px var(--hf); + color: var(--tc); + width: 100%; + margin: 0; + + .profile-content-header { + display: inline; + margin: 0; + font: inherit; + } + + .profile-content-title-count { + color: var(--showcase-title-count-color); + background: var(--showcase-title-count-background); + border-radius: var(--showcase-title-count-border-radius); + font: var(--showcase-title-font); + font-weight: 700; + font-size: 1rem; + float: right; + min-width: 30px; + height: 30px; + margin: 0 0 0 10px; + text-align: center; + padding: 0 5px; + } + + .block-label-tabs { + .label-tab { + font-weight: 700; + } + } + + &.profile-content-title-sm { + font: 700 18px/27px var(--hf); + } + } + + .profile-content-subtitle { + font: 400 14px/22px var(--base-font); + color: var(--tc); + width: 100%; + } + } + + &:first-child { + margin-left: 0; + padding-left: 0; + } + } + + @media screen and (max-width: 1000px) { + .profile-aside, + .profile-content { + width: 100%; + padding: 0; + margin: 0; + } + + .profile-aside { + display: none; + } + + .profile-content { + .profile-content-header { + display: flex; + flex-direction: column; + + .profile-content-subtitle { + font: 400 14px/22px var(--base-font); + padding: 10px 0; + } + + .block-label-tabs { + display: flex; + flex-direction: column; + padding: 10px 0 0; + + .label-tab-set { + margin: 0 0 10px; + display: flex; + flex-wrap: wrap; + + .label-tab { + flex-basis: 33.33%; + flex-grow: 1; + } + + &:last-child { + margin: 0 0 0; + } + } + } + } + } + } +} diff --git a/react/assets/forus-webshop/scss/_common/sections/blocks/profile.scss b/react/assets/forus-webshop/scss/_common/sections/blocks/profile.scss deleted file mode 100644 index b9ada78fd..000000000 --- a/react/assets/forus-webshop/scss/_common/sections/blocks/profile.scss +++ /dev/null @@ -1,309 +0,0 @@ -.block { - &.block-profile { - @include cf(); - - .profile-aside { - width: 25%; - float: left; - position: relative; - z-index: 2; - - .profile-card { - background: #fff; - border-radius: 10px; - box-shadow: 0 5px 30px rgba(0, 0, 0, 0.05); - padding: 20px; - margin-bottom: 30px; - cursor: default; - - .profile-qr_code { - padding-top: 10px; - width: 100%; - margin-bottom: 25px; - - img { - display: block; - margin: 0 auto; - max-width: 100%; - width: 160px; - } - } - - .profile-details { - text-align: center; - - .profile-heading { - font: 700 18px var(--base-font); - color: #315efd; - margin-bottom: 5px; - } - - .profile-description { - font: 400 13px var(--base-font); - color: #646f79; - margin-bottom: 20px; - } - } - - .profile-address { - margin: 0 -20px; - padding: 20px; - border-top: 1px solid #e9ecee; - border-bottom: 1px solid #e9ecee; - - .profile-address-label { - font: 400 10px var(--base-font); - color: #646f79; - margin-bottom: 5px; - } - - .profile-address-value { - font: 700 16px var(--base-font); - color: #282b39; - @include ellipsis(); - } - } - - .profile-actions { - margin: 0 -20px -40px; - padding: 20px 20px 20px; - @include cf(); - - .profile-action { - font: 700 12px var(--base-font); - color: #2f5bf6; - float: left; - margin-right: 30px; - margin-bottom: 20px; - transition: color .4s; - cursor: pointer; - @include cf(); - - .mdi { - color: inherit; - float: left; - margin-right: 5px; - font-size: 1.6em; - line-height: initial; - margin-top: -0.25em; - } - - &:hover { - color: var(--color-primary-light); - } - } - } - } - - .profile-aside-block { - position: relative; - padding: 20px; - width: 100%; - background: #fff; - border-radius: var(--border-radius); - box-shadow: var(--box-shadow); - margin-bottom: 20px; - } - - .profile-menu { - .profile-menu-item { - padding: 14px 19px; - font: 700 13px/20px var(--base-font); - color: var(--profile-menu-item-color); - box-shadow: var(--profile-menu-item-shadow); - background-color: var(--profile-menu-item-background); - border: 1px solid var(--profile-menu-item-border-color); - border-radius: var(--profile-menu-item-border-radius); - margin-bottom: 15px; - display: block; - position: relative; - transition: - color .4s, - background .4s, - box-shadow .4s, - border .4s; - - .mdi { - color: var(--profile-menu-item-color); - position: absolute; - right: 15px; - top: 50%; - transform: translate(0, -50%); - font-size: 20px; - transition: color .4s; - } - - &.active, - &:hover { - color: var(--profile-menu-item-hover-color); - box-shadow: var(--profile-menu-item-hover-shadow); - background-color: var(--profile-menu-item-hover-background); - border: 1px solid var(--profile-menu-item-hover-border-color); - border-radius: var(--profile-menu-item-hover-border-radius); - - .mdi { - right: 10px; - color: var(--profile-menu-item-hover-color); - } - } - } - } - } - - .profile-content { - float: left; - position: relative; - z-index: 2; - width: 75%; - padding-left: 30px; - - .profile-content-header { - position: relative; - z-index: 1; - margin-bottom: 15px; - - .profile-content-title { - font: 700 22px/32px var(--hf); - color: var(--tc); - width: 100%; - margin: 0; - - .profile-content-header { - display: inline; - margin: 0; - font: inherit; - } - - .profile-content-title-count { - color: var(--showcase-title-count-color); - background: var(--showcase-title-count-background); - border-radius: var(--showcase-title-count-border-radius); - font: var(--showcase-title-font); - font-weight: 700; - font-size: 1rem; - float: right; - min-width: 30px; - height: 30px; - margin: 0 0 0 10px; - text-align: center; - padding: 0 5px; - } - - .block-label-tabs { - .label-tab { - font-weight: 700; - } - } - - &.profile-content-title-sm { - font: 700 18px/27px var(--hf); - } - } - - .profile-content-subtitle { - font: 400 14px/22px var(--base-font); - color: var(--tc); - width: 100%; - } - } - - &:first-child { - margin-left: 0; - padding-left: 0; - } - } - } - - @media screen and (max-width: 1000px) { - &.block-profile { - .profile-aside, - .profile-content { - width: 100%; - padding: 0; - margin: 0; - } - - .profile-aside { - .profile-card { - margin: 0 0 20px; - - .profile-address { - text-align: center; - padding: 20px 40px; - } - - .profile-actions { - padding: 0; - margin-bottom: -20px; - - .profile-action { - margin: 0; - width: 50%; - text-align: center; - padding: 15px 20px; - border-right: 1px solid #e9ecee; - line-height: 27px; - - .mdi { - float: none; - margin: 0 5px 0 0; - transform: translate(0, 2px); - display: inline-block; - } - - &:last-child { - border-right: none; - } - } - } - } - - .profile-menu { - margin-bottom: 15px; - - .profile-menu-item { - padding: 12px 20px; - margin-bottom: 10px; - - &:last-child { - margin: 0; - } - } - } - } - - .profile-content { - .profile-content-header { - display: flex; - flex-direction: column; - - .profile-content-subtitle { - font: 400 14px/22px var(--base-font); - padding: 10px 0; - } - - .block-label-tabs { - display: flex; - flex-direction: column; - padding: 10px 0 0; - - .label-tab-set { - margin: 0 0 10px; - display: flex; - flex-wrap: wrap; - - .label-tab { - flex-basis: 33.33%; - flex-grow: 1; - } - - &:last-child { - margin: 0 0 0; - } - } - } - } - } - } - } -} diff --git a/react/assets/forus-webshop/scss/_common/sections/blocks/vouchers.scss b/react/assets/forus-webshop/scss/_common/sections/blocks/vouchers.scss index 3698dd37b..46b5cb9a3 100644 --- a/react/assets/forus-webshop/scss/_common/sections/blocks/vouchers.scss +++ b/react/assets/forus-webshop/scss/_common/sections/blocks/vouchers.scss @@ -59,6 +59,10 @@ font: 700 22px/26px var(--hf); color: #00122a; margin: 0 0 5px; + + .voucher-name-number { + display: none; + } } .voucher-organization { @@ -429,6 +433,10 @@ order: 2; word-break: break-word; word-wrap: break-word; + + .voucher-name-number { + display: contents; + } } .voucher-organization { diff --git a/react/assets/forus-webshop/scss/webshop.scss b/react/assets/forus-webshop/scss/webshop.scss index 87ed1f074..c04febec5 100644 --- a/react/assets/forus-webshop/scss/webshop.scss +++ b/react/assets/forus-webshop/scss/webshop.scss @@ -50,7 +50,6 @@ @import '_common/sections/blocks/map'; @import '_common/sections/blocks/phone-number'; @import '_common/sections/blocks/product'; -@import '_common/sections/blocks/profile'; @import '_common/sections/blocks/record_categories'; @import '_common/sections/blocks/records_create'; @import '_common/sections/blocks/records'; @@ -62,6 +61,7 @@ @import '_common/sections/blocks/voucher'; @import '_common/sections/blocks/vouchers'; @import '_common/sections/blocks/fund-add-records'; +@import '_common/sections/blocks/block-profile'; @import '_common/sections/blocks/block-preferences'; @import '_common/sections/blocks/block-file-uploader'; @import '_common/sections/blocks/block-auth-widget'; diff --git a/react/src/webshop/components/pages/vouchers/elements/VoucherCard.tsx b/react/src/webshop/components/pages/vouchers/elements/VoucherCard.tsx index eb6649aac..dc5f2b13b 100644 --- a/react/src/webshop/components/pages/vouchers/elements/VoucherCard.tsx +++ b/react/src/webshop/components/pages/vouchers/elements/VoucherCard.tsx @@ -62,7 +62,7 @@ export default function VoucherCard({

- #{voucherCard.number} {voucherCard.title} + #{voucherCard.number} {voucherCard.title}

From c3b7d00625d724feb028b2f32c9b94c54b3a1097 Mon Sep 17 00:00:00 2001 From: dev-rminds Date: Wed, 26 Feb 2025 16:56:03 +0200 Subject: [PATCH 7/9] add multiline feature to select-control --- .../scss/_common/components/forms.scss | 23 +++++++++++++++++-- .../scss/_common/dashboard.scss | 2 ++ .../select-controls/select-control-funds.scss | 3 --- .../scss/_common/components/_form.scss | 23 ++++++++++++++++++- .../sections/blocks/block-key-value-list.scss | 10 ++++++++ .../elements/select-control/SelectControl.tsx | 4 ++++ .../templates/SelectControlOptions.tsx | 19 +++++++++++++-- .../templates/SelectControlOptionsFund.tsx | 18 +++++++++++++-- .../modals/ModalEditProfileRecords.tsx | 1 + .../components/modals/Modal2FASetup.tsx | 3 +-- .../pages/fund-requests/FundRequests.tsx | 1 + .../pages/funds-pre-check/FundsPreCheck.tsx | 3 +++ .../steps/FundRequestStepCriteria.tsx | 1 + .../webshop/components/pages/funds/Funds.tsx | 5 ++-- .../pages/identity-security/Security2FA.tsx | 1 + .../PushNotificationPreferencesCard.tsx | 2 ++ .../components/pages/products/Products.tsx | 12 ++++------ .../IdentityRecordKeyValueListHistory.tsx | 7 +++--- .../components/pages/providers/Providers.tsx | 13 +++++------ .../pages/reimbursements/Reimbursements.tsx | 2 ++ .../pages/reservations/Reservations.tsx | 6 +++++ .../components/pages/search/Search.tsx | 3 +++ 22 files changed, 130 insertions(+), 32 deletions(-) diff --git a/react/assets/forus-platform/scss/_common/components/forms.scss b/react/assets/forus-platform/scss/_common/components/forms.scss index 07a60c1f0..71a0516e4 100644 --- a/react/assets/forus-platform/scss/_common/components/forms.scss +++ b/react/assets/forus-platform/scss/_common/components/forms.scss @@ -220,7 +220,7 @@ border: 1px solid var(--border-color); background-color: #ffffff; padding: 4px 15px; - height: 36px; + min-height: 36px; color: var(--text-color); font: 500 13px/24px var(--base-font); transition: background-color 0.4s ease, border 0.4s ease; @@ -272,6 +272,7 @@ .form-group-info-control { display: flex; flex-grow: 1; + min-width: 0; .form-control { border-top-right-radius: 0; @@ -284,7 +285,7 @@ border-left: none; box-shadow: none; height: 100%; - align-items: center; + align-items: flex-start; margin: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; @@ -635,6 +636,8 @@ overflow: hidden; display: block; height: 100%; + white-space: nowrap; + text-overflow: ellipsis; } } @@ -732,6 +735,22 @@ } } + &.multiline-selected .select-control-search .select-control-search-placeholder { + height: auto; + overflow: visible; + white-space: initial; + text-overflow: initial; + word-break: break-word; + } + + &.multiline-options .select-control-options .select-control-option { + height: auto; + overflow: visible; + white-space: initial; + text-overflow: initial; + word-break: break-word; + } + &[disabled] { pointer-events: none; } diff --git a/react/assets/forus-platform/scss/_common/dashboard.scss b/react/assets/forus-platform/scss/_common/dashboard.scss index 5c3ea66b0..33528577c 100644 --- a/react/assets/forus-platform/scss/_common/dashboard.scss +++ b/react/assets/forus-platform/scss/_common/dashboard.scss @@ -1579,10 +1579,12 @@ body { padding: 6px 0; font: 500 12px/20px var(--base-font); border-bottom: 1px solid var(--border-color); + word-break: break-word; color: #646f79; strong { color: #000; + display: contents; } &:last-child { diff --git a/react/assets/forus-platform/scss/_common/select-controls/select-control-funds.scss b/react/assets/forus-platform/scss/_common/select-controls/select-control-funds.scss index d53042ec9..dfff9f6e2 100644 --- a/react/assets/forus-platform/scss/_common/select-controls/select-control-funds.scss +++ b/react/assets/forus-platform/scss/_common/select-controls/select-control-funds.scss @@ -24,11 +24,8 @@ .select-control-options { .select-control-option { - height: auto; min-width: var(--option-height); padding-left: 10px; - word-break: break-word; - white-space: wrap; } } diff --git a/react/assets/forus-webshop/scss/_common/components/_form.scss b/react/assets/forus-webshop/scss/_common/components/_form.scss index 5027b2b90..b552faba2 100644 --- a/react/assets/forus-webshop/scss/_common/components/_form.scss +++ b/react/assets/forus-webshop/scss/_common/components/_form.scss @@ -232,6 +232,9 @@ display: block; color: var(--form-control-color); line-height: 24px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } &:focus { @@ -311,7 +314,9 @@ line-height: 20px; color: var(--dropdown-item-color); background: var(--dropdown-item-background); - @include ellipsis(); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; strong, b { @@ -339,6 +344,22 @@ border-bottom-left-radius: 0; } } + + &.multiline-selected .select-control-search .select-control-search-placeholder { + height: auto; + overflow: visible; + white-space: initial; + text-overflow: initial; + word-break: break-word; + } + + &.multiline-options .select-control-options .select-control-option { + height: auto; + overflow: visible; + white-space: initial; + text-overflow: initial; + word-break: break-word; + } } .select-control.select-control-country-codes { diff --git a/react/assets/forus-webshop/scss/_common/sections/blocks/block-key-value-list.scss b/react/assets/forus-webshop/scss/_common/sections/blocks/block-key-value-list.scss index 693ddbcad..e4aaf8bd0 100644 --- a/react/assets/forus-webshop/scss/_common/sections/blocks/block-key-value-list.scss +++ b/react/assets/forus-webshop/scss/_common/sections/blocks/block-key-value-list.scss @@ -84,9 +84,15 @@ border-bottom: 1px solid var(--border-color); color: #646f79; position: relative; + word-break: break-word; strong { color: #000; + display: contents; + } + + div { + display: contents; } .key-value-list-item-value-history-body-item-icon { @@ -148,6 +154,10 @@ .key-value-list-item-value-history-body-item { flex-direction: column; gap: 0; + + div { + display: block; + } } } } diff --git a/react/src/dashboard/components/elements/select-control/SelectControl.tsx b/react/src/dashboard/components/elements/select-control/SelectControl.tsx index c4a029aa0..63c9a7e44 100644 --- a/react/src/dashboard/components/elements/select-control/SelectControl.tsx +++ b/react/src/dashboard/components/elements/select-control/SelectControl.tsx @@ -21,6 +21,7 @@ type SelectControlProps = { scrollSize?: number; dusk?: string; optionsComponent?: FunctionComponent>; + multiline?: boolean | { selected: boolean; options: boolean }; }; export interface OptionType { @@ -55,6 +56,7 @@ export type SelectControlOptionsProp = { rawValue?: unknown; propKey?: string | null; propValue?: string | null; + multiline?: boolean | { selected: boolean; options: boolean }; }; export default function SelectControl({ @@ -74,6 +76,7 @@ export default function SelectControl({ scrollSize = 50, optionsComponent = SelectControlOptions, dusk = null, + multiline, }: SelectControlProps) { const [query, setQuery] = useState(''); const [modelValue, setModelValue] = useState(null); @@ -253,5 +256,6 @@ export default function SelectControl({ disabled, propKey, propValue, + multiline, }); } diff --git a/react/src/dashboard/components/elements/select-control/templates/SelectControlOptions.tsx b/react/src/dashboard/components/elements/select-control/templates/SelectControlOptions.tsx index 864665249..3d75597af 100644 --- a/react/src/dashboard/components/elements/select-control/templates/SelectControlOptions.tsx +++ b/react/src/dashboard/components/elements/select-control/templates/SelectControlOptions.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import ClickOutside from '../../click-outside/ClickOutside'; import { uniqueId } from 'lodash'; import { SelectControlOptionsProp } from '../SelectControl'; @@ -25,12 +25,21 @@ export default function SelectControlOptions({ searchInputChanged, onOptionsScroll, disabled, + multiline = { selected: false, options: true }, }: SelectControlOptionsProp) { const [controlId] = useState('select_control_' + uniqueId()); const input = useRef(null); const selectorRef = useRef(null); const placeholderRef = useRef(null); + const multilineSelected = useMemo(() => { + return multiline === true || (typeof multiline === 'object' && multiline?.selected === true); + }, [multiline]); + + const multilineOptions = useMemo(() => { + return multiline === true || (typeof multiline === 'object' && multiline?.options === true); + }, [multiline]); + const { onKeyDown, onBlur } = useSelectControlKeyEventHandlers( selectorRef, placeholderRef, @@ -52,7 +61,13 @@ export default function SelectControlOptions({ ref={selectorRef} onKeyDown={onKeyDown} onBlur={onBlur}> -
item).join(' ')}> +
{/* Placeholder */}
diff --git a/react/src/webshop/components/pages/fund-requests/FundRequests.tsx b/react/src/webshop/components/pages/fund-requests/FundRequests.tsx index 9ef606190..851811fe8 100644 --- a/react/src/webshop/components/pages/fund-requests/FundRequests.tsx +++ b/react/src/webshop/components/pages/fund-requests/FundRequests.tsx @@ -128,6 +128,7 @@ export default function FundRequests() { value={filterValues.fund_id} propKey={'id'} options={funds} + multiline={true} onChange={(fund_id?: number) => filterUpdate({ fund_id })} />
diff --git a/react/src/webshop/components/pages/funds-pre-check/FundsPreCheck.tsx b/react/src/webshop/components/pages/funds-pre-check/FundsPreCheck.tsx index 01c52de70..104a75788 100644 --- a/react/src/webshop/components/pages/funds-pre-check/FundsPreCheck.tsx +++ b/react/src/webshop/components/pages/funds-pre-check/FundsPreCheck.tsx @@ -455,6 +455,7 @@ export default function FundsPreCheck() { onChange={(organization_id: number) => filter.update({ organization_id }) } + multiline={true} />
@@ -468,6 +469,7 @@ export default function FundsPreCheck() { value={filter.values.tag_id} onChange={(tag_id: number) => filter.update({ tag_id })} options={tags} + multiline={true} />
@@ -531,6 +533,7 @@ export default function FundsPreCheck() { .options } placeholder={`Maak een keuze`} + multiline={true} /> )} diff --git a/react/src/webshop/components/pages/funds-request/elements/steps/FundRequestStepCriteria.tsx b/react/src/webshop/components/pages/funds-request/elements/steps/FundRequestStepCriteria.tsx index 513ec294a..a6ccd7a46 100644 --- a/react/src/webshop/components/pages/funds-request/elements/steps/FundRequestStepCriteria.tsx +++ b/react/src/webshop/components/pages/funds-request/elements/steps/FundRequestStepCriteria.tsx @@ -228,6 +228,7 @@ export default function FundRequestStepCriteria({ placeholder={translate( 'fund_request.sign_up.fund_request_step_criteria.make_a_choice', )} + multiline={true} /> )} diff --git a/react/src/webshop/components/pages/funds/Funds.tsx b/react/src/webshop/components/pages/funds/Funds.tsx index ad99b4ae9..a96ace29f 100644 --- a/react/src/webshop/components/pages/funds/Funds.tsx +++ b/react/src/webshop/components/pages/funds/Funds.tsx @@ -8,7 +8,6 @@ import Fund from '../../../props/models/Fund'; import Voucher from '../../../../dashboard/props/models/Voucher'; import Organization from '../../../../dashboard/props/models/Organization'; import SelectControl from '../../../../dashboard/components/elements/select-control/SelectControl'; -import SelectControlOptions from '../../../../dashboard/components/elements/select-control/templates/SelectControlOptions'; import { useFundService } from '../../../services/FundService'; import { useTagService } from '../../../../dashboard/services/TagService'; import useFilter from '../../../../dashboard/hooks/useFilter'; @@ -182,12 +181,12 @@ export default function Funds() { {translate('funds.labels.organization')} filter.update({ organization_id })} options={organizations || []} + multiline={true} />
@@ -195,12 +194,12 @@ export default function Funds() { {translate('funds.labels.category')} filter.update({ tag_id })} options={tags || []} + multiline={true} />
diff --git a/react/src/webshop/components/pages/identity-security/Security2FA.tsx b/react/src/webshop/components/pages/identity-security/Security2FA.tsx index cee17631b..c61270a6a 100644 --- a/react/src/webshop/components/pages/identity-security/Security2FA.tsx +++ b/react/src/webshop/components/pages/identity-security/Security2FA.tsx @@ -248,6 +248,7 @@ export default function Security2FA() { form.update({ auth_2fa_remember_ip }); }} options={auth2FARememberIpOptions} + multiline={true} /> ) : ( selectDismissTime('webshop', time)} @@ -199,6 +200,7 @@ export default function PushNotificationPreferencesCard({
selectDismissTime('bookmarks', time)} diff --git a/react/src/webshop/components/pages/products/Products.tsx b/react/src/webshop/components/pages/products/Products.tsx index 354a3fc98..6d353b690 100644 --- a/react/src/webshop/components/pages/products/Products.tsx +++ b/react/src/webshop/components/pages/products/Products.tsx @@ -11,7 +11,6 @@ import useProductCategoryService from '../../../../dashboard/services/ProductCat import Organization from '../../../../dashboard/props/models/Organization'; import useAuthIdentity from '../../../hooks/useAuthIdentity'; import SelectControl from '../../../../dashboard/components/elements/select-control/SelectControl'; -import SelectControlOptions from '../../../../dashboard/components/elements/select-control/templates/SelectControlOptions'; import FormError from '../../../../dashboard/components/elements/forms/errors/FormError'; import CmsBlocks from '../../elements/cms-blocks/CmsBlocks'; import useAppConfigs from '../../../hooks/useAppConfigs'; @@ -305,10 +304,10 @@ export default function Products({ fundType = 'budget' }: { fundType: 'budget' | id={'select_provider'} value={filterValues.organization_id} propKey={'id'} + multiline={true} allowSearch={true} onChange={(organization_id: number) => filterUpdate({ organization_id })} options={organizations || []} - optionsComponent={SelectControlOptions} />
@@ -320,11 +319,11 @@ export default function Products({ fundType = 'budget' }: { fundType: 'budget' | filterUpdate({ product_category_id: id })} options={productCategories || []} - optionsComponent={SelectControlOptions} /> @@ -339,9 +338,9 @@ export default function Products({ fundType = 'budget' }: { fundType: 'budget' | propKey={'id'} value={filterValues.product_sub_category_id} onChange={(id: number) => filterUpdate({ product_sub_category_id: id })} + multiline={true} allowSearch={true} options={productSubCategories || []} - optionsComponent={SelectControlOptions} /> )} @@ -355,10 +354,10 @@ export default function Products({ fundType = 'budget' }: { fundType: 'budget' | id={'select_fund'} propKey={'id'} value={filterValues.fund_id} + multiline={true} allowSearch={true} onChange={(fund_id: number) => filterUpdate({ fund_id })} options={funds || []} - optionsComponent={SelectControlOptions} /> )} @@ -388,10 +387,10 @@ export default function Products({ fundType = 'budget' }: { fundType: 'budget' | id={'distance'} propKey={'id'} value={filterValues.distance} + multiline={true} allowSearch={true} onChange={(distance: number) => filterUpdate({ distance })} options={distances || []} - optionsComponent={SelectControlOptions} /> @@ -436,7 +435,6 @@ export default function Products({ fundType = 'budget' }: { fundType: 'budget' | })?.value || {}, ); }} - optionsComponent={SelectControlOptions} />
diff --git a/react/src/webshop/components/pages/profile/elements/IdentityRecordKeyValueListHistory.tsx b/react/src/webshop/components/pages/profile/elements/IdentityRecordKeyValueListHistory.tsx index 9e401e849..8fb1c6888 100644 --- a/react/src/webshop/components/pages/profile/elements/IdentityRecordKeyValueListHistory.tsx +++ b/react/src/webshop/components/pages/profile/elements/IdentityRecordKeyValueListHistory.tsx @@ -33,11 +33,12 @@ export default function IdentityRecordKeyValueListHistory({ records }: { records {' '}
{`${item.created_at_locale}`}
-
+
{' • '}
- {translate('profile.history.from')}{' '} + {' ' + translate('profile.history.from') + ' '} {`'${records[index + 1]?.value_locale || ''}'`} - {translate('profile.history.to')} {`'${item.value_locale || ''}'`} + {' ' + translate('profile.history.to') + ' '} + {`'${item.value_locale || ''}'`}
))} diff --git a/react/src/webshop/components/pages/providers/Providers.tsx b/react/src/webshop/components/pages/providers/Providers.tsx index adf3769a9..864c2c7e1 100644 --- a/react/src/webshop/components/pages/providers/Providers.tsx +++ b/react/src/webshop/components/pages/providers/Providers.tsx @@ -7,7 +7,6 @@ import ProductCategory from '../../../../dashboard/props/models/ProductCategory' import useProductCategoryService from '../../../../dashboard/services/ProductCategoryService'; import StateNavLink from '../../../modules/state_router/StateNavLink'; import SelectControl from '../../../../dashboard/components/elements/select-control/SelectControl'; -import SelectControlOptions from '../../../../dashboard/components/elements/select-control/templates/SelectControlOptions'; import FormError from '../../../../dashboard/components/elements/forms/errors/FormError'; import CmsBlocks from '../../elements/cms-blocks/CmsBlocks'; import useAppConfigs from '../../../hooks/useAppConfigs'; @@ -289,7 +288,8 @@ export default function Providers() { value={filterValues.business_type_id} onChange={(business_type_id?: number) => filterUpdate({ business_type_id })} id="business_type_id" - optionsComponent={SelectControlOptions} + multiline={true} + allowSearch={false} /> @@ -302,11 +302,11 @@ export default function Providers() { filterUpdate({ product_category_id: id })} options={productCategories || []} - optionsComponent={SelectControlOptions} /> @@ -321,9 +321,9 @@ export default function Providers() { propKey={'id'} value={filterValues.product_sub_category_id} onChange={(id: number) => filterUpdate({ product_sub_category_id: id })} + multiline={true} allowSearch={true} options={productSubCategories || []} - optionsComponent={SelectControlOptions} /> )} @@ -337,10 +337,10 @@ export default function Providers() { id={'select_fund'} propKey={'id'} value={filterValues.fund_id} + multiline={true} allowSearch={true} onChange={(fund_id: number) => filterUpdate({ fund_id })} options={funds || []} - optionsComponent={SelectControlOptions} /> )} @@ -371,10 +371,10 @@ export default function Providers() { id={'select_fund'} propKey={'id'} value={filterValues.distance} + multiline={true} allowSearch={true} onChange={(distance: number) => filterUpdate({ distance })} options={distances || []} - optionsComponent={SelectControlOptions} /> @@ -432,7 +432,6 @@ export default function Providers() { })?.value || {}, ); }} - optionsComponent={SelectControlOptions} /> {appConfigs?.show_providers_map && ( diff --git a/react/src/webshop/components/pages/reimbursements/Reimbursements.tsx b/react/src/webshop/components/pages/reimbursements/Reimbursements.tsx index a83abc487..c4b27cd9b 100644 --- a/react/src/webshop/components/pages/reimbursements/Reimbursements.tsx +++ b/react/src/webshop/components/pages/reimbursements/Reimbursements.tsx @@ -123,6 +123,8 @@ export default function Reimbursements() { propKey={'id'} value={filters.values.fund_id} options={funds} + multiline={true} + allowSearch={true} onChange={(fund_id?: number) => filters.update({ fund_id })} /> diff --git a/react/src/webshop/components/pages/reservations/Reservations.tsx b/react/src/webshop/components/pages/reservations/Reservations.tsx index 47334047f..05211dd7a 100644 --- a/react/src/webshop/components/pages/reservations/Reservations.tsx +++ b/react/src/webshop/components/pages/reservations/Reservations.tsx @@ -125,6 +125,8 @@ export default function Reservations() { propKey={'id'} value={filters.values.fund_id} options={funds} + multiline={true} + allowSearch={true} onChange={(fund_id?: number) => filters.update({ fund_id })} /> @@ -139,6 +141,8 @@ export default function Reservations() { propKey={'id'} options={organizations} onChange={(organization_id?: number) => filters.update({ organization_id })} + multiline={true} + allowSearch={true} />
@@ -152,6 +156,8 @@ export default function Reservations() { propKey={'value'} options={states} onChange={(state?: string) => filters.update({ state })} + multiline={true} + allowSearch={true} />
diff --git a/react/src/webshop/components/pages/search/Search.tsx b/react/src/webshop/components/pages/search/Search.tsx index f5371c1c6..42fbd2044 100644 --- a/react/src/webshop/components/pages/search/Search.tsx +++ b/react/src/webshop/components/pages/search/Search.tsx @@ -302,6 +302,7 @@ export default function Search() { filterUpdate({ product_category_id: id })} @@ -318,6 +319,7 @@ export default function Search() { filterUpdate({ fund_id: id })} @@ -334,6 +336,7 @@ export default function Search() { filterUpdate({ organization_id: id })} From 16a2389f2f36a0e5245378429f3a6d72057cfae0 Mon Sep 17 00:00:00 2001 From: dev-rminds Date: Wed, 26 Feb 2025 18:34:01 +0200 Subject: [PATCH 8/9] add missing postcode to profile on webshop --- .../pages/profile/cards/IdentityContactInformationCard.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/react/src/webshop/components/pages/profile/cards/IdentityContactInformationCard.tsx b/react/src/webshop/components/pages/profile/cards/IdentityContactInformationCard.tsx index 8a8dedbe5..ddbd72a44 100644 --- a/react/src/webshop/components/pages/profile/cards/IdentityContactInformationCard.tsx +++ b/react/src/webshop/components/pages/profile/cards/IdentityContactInformationCard.tsx @@ -238,6 +238,10 @@ export default function IdentityContactInformationCard({ /> ), }, + { + label: recordTypesByKey?.postal_code?.name, + value: , + }, { label: recordTypesByKey?.neighborhood_name?.name, value: ( From 144b12b916d05c28c6e065bc4a0af0597638953f Mon Sep 17 00:00:00 2001 From: Yaroslav Kosterin Date: Wed, 26 Feb 2025 23:45:49 +0200 Subject: [PATCH 9/9] fix clear identities filter and console errors --- .../components/pages/identities/Identities.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/react/src/dashboard/components/pages/identities/Identities.tsx b/react/src/dashboard/components/pages/identities/Identities.tsx index 2c30732e3..05d9697c4 100644 --- a/react/src/dashboard/components/pages/identities/Identities.tsx +++ b/react/src/dashboard/components/pages/identities/Identities.tsx @@ -82,6 +82,16 @@ export default function Identities() { state: states[0].key, fund_id: null, page: 1, + city: '', + has_bsn: null, + postal_code: '', + municipality_name: '', + birth_date_to: '', + birth_date_from: '', + last_login_to: '', + last_login_from: '', + last_activity_to: '', + last_activity_from: '', per_page: paginatorService.getPerPage(paginatorTransactionsKey), }, {