mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
component to display the payment schedule
This commit is contained in:
parent
0e503e734e
commit
0fe4f13110
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* This component displays a summary of the monthly payment schedule for the current cart, with a subscription
|
||||
*/
|
||||
|
||||
import React, { useState, Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Modal from 'react-modal';
|
||||
import { react2angular } from 'react2angular';
|
||||
import moment from 'moment';
|
||||
import { IApplication } from '../models/application';
|
||||
import '../lib/i18n';
|
||||
import { IFilterService } from 'angular';
|
||||
import { PaymentSchedule } from '../models/payment-schedule';
|
||||
|
||||
declare var Application: IApplication;
|
||||
|
||||
interface PaymentScheduleSummaryProps {
|
||||
schedule: PaymentSchedule,
|
||||
$filter: IFilterService
|
||||
}
|
||||
|
||||
const PaymentScheduleSummary: React.FC<PaymentScheduleSummaryProps> = ({ schedule, $filter }) => {
|
||||
const { t } = useTranslation('shared');
|
||||
const [modal, setModal] = useState(false);
|
||||
|
||||
/**
|
||||
* Return the formatted localized date for the given date
|
||||
*/
|
||||
const formatDate = (date: Date): string => {
|
||||
return Intl.DateTimeFormat().format(moment(date).toDate());
|
||||
}
|
||||
/**
|
||||
* Return the formatted localized amount for the given price (eg. 20.5 => "20,50 €")
|
||||
*/
|
||||
const formatPrice = (price: number): string => {
|
||||
return $filter('currency')(price);
|
||||
}
|
||||
/**
|
||||
* Test if all payment deadlines have the same amount
|
||||
*/
|
||||
const hasEqualDeadlines = (): boolean => {
|
||||
const prices = schedule.items.map(i => i.price);
|
||||
return prices.every(p => p === prices[0]);
|
||||
}
|
||||
/**
|
||||
* Open or closes the modal dialog showing the full payment schedule
|
||||
*/
|
||||
const toggleFullScheduleModal = (): void => {
|
||||
setModal(!modal);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="payment-schedule-summary">
|
||||
<div>
|
||||
<h4>{ t('app.shared.cart.your_payment_schedule') }</h4>
|
||||
{hasEqualDeadlines() && <div>
|
||||
<span className="schedule-item-info">
|
||||
{t('app.shared.cart.NUMBER_monthly_payment_of_AMOUNT', { NUMBER: schedule.items.length, AMOUNT: formatPrice(schedule.items[0].price) })}
|
||||
</span>
|
||||
<span className="schedule-item-date">{t('app.shared.cart.first_debit')}</span>
|
||||
</div>}
|
||||
{!hasEqualDeadlines() && <ul>
|
||||
<li>
|
||||
<span className="schedule-item-info">{t('app.shared.cart.monthly_payment_NUMBER', { NUMBER: 1 })}</span>
|
||||
<span className="schedule-item-price">{formatPrice(schedule.items[0].price)}</span>
|
||||
<span className="schedule-item-date">{t('app.shared.cart.debit')}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="schedule-item-info">
|
||||
{t('app.shared.cart.NUMBER_monthly_payment_of_AMOUNT', { NUMBER: schedule.items.length - 1, AMOUNT: formatPrice(schedule.items[1].price) })}
|
||||
</span>
|
||||
</li>
|
||||
</ul>}
|
||||
<a className="view-full-schedule" onClick={toggleFullScheduleModal}>{t('app.shared.cart.view_full_schedule')}</a>
|
||||
{/* TODO, create a component FabModal and put this inside */}
|
||||
<Modal isOpen={modal}
|
||||
className="full-schedule-modal"
|
||||
onRequestClose={toggleFullScheduleModal}>
|
||||
{schedule.items.map(item => (
|
||||
<li>
|
||||
<span className="schedule-item-date">{formatDate(item.due_date)}</span>
|
||||
<span> </span>
|
||||
<span className="schedule-item-price">{formatPrice(item.price)}</span>
|
||||
</li>
|
||||
))}
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const PaymentScheduleSummaryWrapper: React.FC<PaymentScheduleSummaryProps> = ({ schedule, $filter }) => {
|
||||
const loading = (
|
||||
<div className="fa-3x">
|
||||
<i className="fas fa-circle-notch fa-spin" />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Suspense fallback={loading}>
|
||||
<PaymentScheduleSummary schedule={schedule} $filter={$filter} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('paymentScheduleSummary', react2angular(PaymentScheduleSummaryWrapper, ['schedule'], ['$filter']));
|
@ -16,6 +16,9 @@ i18n
|
||||
defaultNS: 'shared',
|
||||
backend: {
|
||||
loadPath: '/api/translations/{{lng}}/app.{{ns}}'
|
||||
},
|
||||
interpolation: {
|
||||
escapeValue: false
|
||||
}
|
||||
});
|
||||
|
||||
|
17
app/frontend/src/javascript/models/payment-schedule.ts
Normal file
17
app/frontend/src/javascript/models/payment-schedule.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export interface PaymentScheduleItem {
|
||||
id: number,
|
||||
price: number,
|
||||
due_date: Date
|
||||
}
|
||||
|
||||
export interface PaymentSchedule {
|
||||
id: number,
|
||||
scheduled_type: string,
|
||||
scheduled_id: number,
|
||||
total: number,
|
||||
stp_subscription_id: string,
|
||||
reference: string,
|
||||
payment_method: string,
|
||||
wallet_amount: number,
|
||||
items: Array<PaymentScheduleItem>
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
.payment-schedule-summary {
|
||||
.full-schedule-modal {
|
||||
@extend .modal;
|
||||
@extend .fade;
|
||||
}
|
||||
}
|
@ -86,13 +86,7 @@
|
||||
</div>
|
||||
|
||||
<div ng-if="schedule.payment_schedule">
|
||||
<h4 translate>{{ 'app.shared.cart.your_payment_schedule' }}</h4>
|
||||
<ul>
|
||||
<li ng-repeat="item in schedule.payment_schedule.items">
|
||||
<span>{{item.due_date | amDateFormat:'L'}}</span>
|
||||
<span>{{item.price | currency}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<payment-schedule-summary schedule="schedule.payment_schedule"></payment-schedule-summary>
|
||||
</div>
|
||||
|
||||
<div class="widget-footer no-padder" ng-if="events.reserved.length > 0 || (selectedPlan && !events.reserved)">
|
||||
|
@ -398,6 +398,11 @@ en:
|
||||
you_ve_just_selected_a_subscription_html: "You've just selected a <strong>subscription</strong>:"
|
||||
monthly_payment: "Monthly payment"
|
||||
your_payment_schedule: "Your payment schedule"
|
||||
monthly_payment_NUMBER: "{NUMBER}{NUMBER, plural, =1{st} =2{nd} =3{rd} other{th}} monthly payment:"
|
||||
NUMBER_monthly_payment_of_AMOUNT: "{NUMBER} monthly {NUMBER, plural, =1{payment} other{payments}} of {AMOUNT}."
|
||||
first_debit: "First debit on the day of the order."
|
||||
debit: "Debit on the day of the order."
|
||||
view_full_schedule: "View the complete payement schedule"
|
||||
confirm_and_pay: "Confirm and pay"
|
||||
you_have_settled_the_following_TYPE: "You have settled the following {TYPE, select, Machine{machine slots} Training{training} other{elements}}:"
|
||||
you_have_settled_a_: "You have settled a"
|
||||
|
@ -398,6 +398,11 @@ fr:
|
||||
you_ve_just_selected_a_subscription_html: "Vous venez de sélectionner un <strong>abonnement</strong> :"
|
||||
monthly_payment: "Paiement mensuel"
|
||||
your_payment_schedule: "Votre échéancier de paiement"
|
||||
monthly_payment_NUMBER: "{NUMBER}{NUMBER, plural, =1{ère} other{ème}} mensualité :"
|
||||
NUMBER_monthly_payment_of_AMOUNT: "{NUMBER} {NUMBER, plural, =1{mensualité} other{mensualités}} de {AMOUNT}."
|
||||
first_debit: "Premier prélèvement le jour de la commande."
|
||||
debit: "Prélèvement le jour de la commande."
|
||||
view_full_schedule: "Voir tout l'échéancier de paiement"
|
||||
confirm_and_pay: "Valider et payer"
|
||||
you_have_settled_the_following_TYPE: "Vous avez réglé {TYPE, select, Machine{les créneaux machines suivants} Training{la formation suivante} other{les éléments suivants}} :"
|
||||
you_have_settled_a_: "Vous avez réglé un"
|
||||
|
@ -98,6 +98,7 @@
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"react-i18next": "^11.7.3",
|
||||
"react-modal": "^3.11.2",
|
||||
"react-switch": "^5.0.1",
|
||||
"react2angular": "^4.0.6",
|
||||
"summernote": "0.8.18",
|
||||
|
29
yarn.lock
29
yarn.lock
@ -3792,6 +3792,11 @@ execa@^1.0.0:
|
||||
signal-exit "^3.0.0"
|
||||
strip-eof "^1.0.0"
|
||||
|
||||
exenv@^1.2.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
|
||||
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
|
||||
|
||||
expand-brackets@^2.1.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
|
||||
@ -7457,7 +7462,7 @@ promise-inflight@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
|
||||
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
|
||||
|
||||
prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
@ -7639,6 +7644,21 @@ react-is@^16.8.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-lifecycles-compat@^3.0.0:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-modal@^3.11.2:
|
||||
version "3.11.2"
|
||||
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.11.2.tgz#bad911976d4add31aa30dba8a41d11e21c4ac8a4"
|
||||
integrity sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==
|
||||
dependencies:
|
||||
exenv "^1.2.0"
|
||||
prop-types "^15.5.10"
|
||||
react-lifecycles-compat "^3.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-refresh@^0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
|
||||
@ -9310,6 +9330,13 @@ void-elements@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
||||
|
||||
warning@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
||||
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
watchpack-chokidar2@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user