diff options
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx')
-rw-r--r-- | packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx | 249 |
1 files changed, 90 insertions, 159 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx index 2ba637f44..ad36df3cc 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx @@ -25,7 +25,7 @@ import { Duration, TalerError, TalerMerchantApi, - assertUnreachable, + TranslatedString } from "@gnu-taler/taler-util"; import { useMerchantApiContext, @@ -42,18 +42,12 @@ import { Input } from "../../../../components/form/Input.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputDuration } from "../../../../components/form/InputDuration.js"; import { InputNumber } from "../../../../components/form/InputNumber.js"; -import { InputSearchOnList } from "../../../../components/form/InputSearchOnList.js"; -import { InputTab } from "../../../../components/form/InputTab.js"; +import { InputSelector } from "../../../../components/form/InputSelector.js"; +import { InputToggle } from "../../../../components/form/InputToggle.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; +import { TextField } from "../../../../components/form/TextField.js"; import { useInstanceOtpDevices } from "../../../../hooks/otp.js"; -enum Steps { - BOTH_FIXED, - FIXED_PRICE, - FIXED_SUMMARY, - NON_FIXED, -} - // type Entity = TalerMerchantApi.TemplateAddDetails & { type: Steps }; type Entity = { id?: string; @@ -63,7 +57,9 @@ type Entity = { amount?: AmountString; minimum_age?: number; pay_duration?: Duration; - type: Steps; + summary_editable?: boolean; + amount_editable?: boolean; + currency_editable?: boolean; }; interface Props { @@ -81,9 +77,18 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { pay_duration: { d_ms: 1000 * 60 * 30, //30 min }, - type: Steps.NON_FIXED, }); + function updateState(up: (s: Partial<Entity>) => Partial<Entity>) { + setState((old) => { + const newState = up(old) + if (!newState.amount_editable) { + newState.currency_editable = false + } + return newState + }) + } + const parsedPrice = !state.amount ? undefined : Amounts.parse(state.amount); const errors: FormErrors<Entity> = { @@ -93,24 +98,14 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { ? i18n.str`no valid. only characters and numbers` : undefined, description: !state.description ? i18n.str`should not be empty` : undefined, - amount: !( - state.type === Steps.FIXED_PRICE || state.type === Steps.BOTH_FIXED - ) - ? undefined - : !state.amount - ? i18n.str`required` + amount: + !state.amount + ? undefined : !parsedPrice ? i18n.str`not valid` : Amounts.isZero(parsedPrice) ? i18n.str`must be greater than 0` : undefined, - summary: !( - state.type === Steps.FIXED_SUMMARY || state.type === Steps.BOTH_FIXED - ) - ? undefined - : !state.summary - ? i18n.str`required` - : undefined, minimum_age: state.minimum_age && state.minimum_age < 0 ? i18n.str`should be greater that 0` @@ -125,67 +120,33 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { }; const hasErrors = Object.keys(errors).some( - (k) => (errors as Record<string,unknown>)[k] !== undefined, + (k) => (errors as Record<string, unknown>)[k] !== undefined, ); const submitForm = () => { - if (hasErrors || state.type === undefined) return Promise.reject(); - switch (state.type) { - case Steps.FIXED_PRICE: - return onCreate({ - template_id: state.id!, - template_description: state.description!, - template_contract: { - minimum_age: state.minimum_age!, - pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!), - amount: state.amount!, - // summary: state.summary, - }, - otp_id: state.otpId!, - }); - case Steps.FIXED_SUMMARY: - return onCreate({ - template_id: state.id!, - template_description: state.description!, - template_contract: { - minimum_age: state.minimum_age!, - pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!), - // amount: state.amount!, - summary: state.summary, - }, - otp_id: state.otpId!, - }); - case Steps.NON_FIXED: - return onCreate({ - template_id: state.id!, - template_description: state.description!, - template_contract: { - minimum_age: state.minimum_age!, - pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!), - // amount: state.amount!, - // summary: state.summary, - }, - otp_id: state.otpId!, - }); - case Steps.BOTH_FIXED: - return onCreate({ - template_id: state.id!, - template_description: state.description!, - template_contract: { - minimum_age: state.minimum_age!, - pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!), - amount: state.amount!, - summary: state.summary, - }, - otp_id: state.otpId!, - }); - default: - assertUnreachable(state.type); - // return onCreate(state); - } + if (hasErrors) return Promise.reject(); + return onCreate({ + template_id: state.id!, + template_description: state.description!, + template_contract: { + minimum_age: state.minimum_age!, + pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!), + amount: state.amount_editable ? undefined : state.amount, + summary: state.summary_editable ? undefined : state.summary, + }, + editable_defaults: { + amount: !state.amount_editable ? undefined : state.amount, + summary: !state.summary_editable ? undefined : state.summary, + }, + otp_id: state.otpId!, + }); + }; const deviceList = !devices || devices instanceof TalerError || devices.type === "fail" ? [] : devices.body; - + const deviceMap = deviceList.reduce((prev, cur) => { + prev[cur.otp_device_id] = cur.device_description as TranslatedString + return prev + }, {} as Record<string, TranslatedString>) return ( <div> <section class="section is-main-section"> @@ -194,7 +155,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <div class="column is-four-fifths"> <FormProvider object={state} - valueHandler={setState} + valueHandler={updateState} errors={errors} > <InputWithAddon<Entity> @@ -209,59 +170,36 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { help="" tooltip={i18n.str`Describe what this template stands for`} /> - <InputTab<Entity> - name="type" - label={i18n.str`Type`} - help={(() => { - if (state.type === undefined) return ""; - switch (state.type) { - case Steps.NON_FIXED: - return i18n.str`User will be able to input price and summary before payment.`; - case Steps.FIXED_PRICE: - return i18n.str`User will be able to add a summary before payment.`; - case Steps.FIXED_SUMMARY: - return i18n.str`User will be able to set the price before payment.`; - case Steps.BOTH_FIXED: - return i18n.str`User will not be able to change the price or the summary.`; - } - })()} - tooltip={i18n.str`Define what the user be allowed to modify`} - values={[ - Steps.NON_FIXED, - Steps.FIXED_PRICE, - Steps.FIXED_SUMMARY, - Steps.BOTH_FIXED, - ]} - toStr={(v: Steps): string => { - switch (v) { - case Steps.NON_FIXED: - return i18n.str`Simple`; - case Steps.FIXED_PRICE: - return i18n.str`With price`; - case Steps.FIXED_SUMMARY: - return i18n.str`With summary`; - case Steps.BOTH_FIXED: - return i18n.str`With price and summary`; - } - }} + + <Input<Entity> + name="summary" + inputType="multiline" + label={i18n.str`Summary`} + tooltip={i18n.str`If specified, this template will create order with the same summary`} /> - {state.type === Steps.BOTH_FIXED || - state.type === Steps.FIXED_SUMMARY ? ( - <Input<Entity> - name="summary" - inputType="multiline" - label={i18n.str`Fixed summary`} - tooltip={i18n.str`If specified, this template will create order with the same summary`} - /> - ) : undefined} - {state.type === Steps.BOTH_FIXED || - state.type === Steps.FIXED_PRICE ? ( - <InputCurrency<Entity> - name="amount" - label={i18n.str`Fixed price`} - tooltip={i18n.str`If specified, this template will create order with the same price`} - /> - ) : undefined} + <InputToggle<Entity> + name="summary_editable" + label={i18n.str`Summary is editable`} + tooltip={i18n.str`Allow the user to change the summary.`} + /> + + <InputCurrency<Entity> + name="amount" + label={i18n.str`Amount`} + tooltip={i18n.str`If specified, this template will create order with the same price`} + /> + <InputToggle<Entity> + name="amount_editable" + label={i18n.str`Amount is editable`} + tooltip={i18n.str`Allow the user to select the amount to pay.`} + /> + {/* <InputToggle<Entity> + name="currency_editable" + readonly={!state.amount_editable} + label={i18n.str`Currency is editable`} + tooltip={i18n.str`Allow the user to change currency.`} + /> */} + <InputNumber<Entity> name="minimum_age" label={i18n.str`Minimum age`} @@ -274,33 +212,26 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { help="" tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`} /> - <Input<Entity> + {!deviceList.length ? <TextField name="otpId" label={i18n.str`OTP device`} - readonly - side={ - <button - class="button is-danger" - data-tooltip={i18n.str`without otp device`} - onClick={(): void => { - setState((v) => ({ ...v, otpId: undefined })); - }} - > - <span> - <i18n.Translate>remove</i18n.Translate> - </span> - </button> - } - tooltip={i18n.str`Use to verify transaction in offline mode.`} - /> - <InputSearchOnList - label={i18n.str`Search device`} - onChange={(p) => setState((v) => ({ ...v, otpId: p?.id }))} - list={deviceList.map((e) => ({ - description: e.device_description, - id: e.otp_device_id, - }))} - /> + tooltip={i18n.str`Use to verify transaction while offline.`} + > + <i18n.Translate>No OTP device.</i18n.Translate> <a href="/otp-devices/new"><i18n.Translate>Add one first</i18n.Translate></a> + </TextField> : + <InputSelector<Entity> + name="otpId" + label={i18n.str`OTP device`} + values={[undefined, ...deviceList.map(e => e.otp_device_id)]} + toStr={(v?: string) => { + if (!v) { + return i18n.str`No device` + } + return deviceMap[v] + }} + tooltip={i18n.str`Use to verify transaction in offline mode.`} + /> + } </FormProvider> <div class="buttons is-right mt-5"> |