diff options
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/orders/create')
3 files changed, 129 insertions, 45 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx index fcf611c3c..bd9f65718 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx @@ -42,12 +42,13 @@ function createExample<Props>( export const Example = createExample(TestedComponent, { instanceConfig: { - default_max_deposit_fee: "", - default_max_wire_fee: "", default_pay_delay: { d_us: 1000 * 1000 * 60 * 60, //one hour }, - default_wire_fee_amortization: 1, + default_wire_transfer_delay: { + d_us: 1000 * 1000 * 60 * 60, //one hour + }, + use_stefan: true, }, instanceInventory: [ { diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx index fa9347c6e..ea2cf849a 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx @@ -44,6 +44,7 @@ import { OrderCreateSchema as schema } from "../../../../schemas/index.js"; import { rate } from "../../../../utils/amount.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; import { useSettings } from "../../../../hooks/useSettings.js"; +import { InputToggle } from "../../../../components/form/InputToggle.js"; interface Props { onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void; @@ -52,34 +53,38 @@ interface Props { instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[]; } interface InstanceConfig { - default_max_wire_fee: string; - default_max_deposit_fee: string; - default_wire_fee_amortization: number; + use_stefan: boolean; default_pay_delay: Duration; + default_wire_transfer_delay: Duration; } -function with_defaults(config: InstanceConfig): Partial<Entity> { +function with_defaults(config: InstanceConfig, currency: string): Partial<Entity> { const defaultPayDeadline = !config.default_pay_delay || config.default_pay_delay.d_us === "forever" ? undefined : add(new Date(), { seconds: config.default_pay_delay.d_us / (1000 * 1000), }); + const defaultWireDeadline = + !config.default_wire_transfer_delay || config.default_wire_transfer_delay.d_us === "forever" + ? undefined + : add(new Date(), { + seconds: config.default_wire_transfer_delay.d_us / (1000 * 1000), + }); return { inventoryProducts: {}, products: [], pricing: {}, payments: { - max_wire_fee: config.default_max_wire_fee, - max_fee: config.default_max_deposit_fee, - wire_fee_amortization: config.default_wire_fee_amortization, + max_fee: undefined, pay_deadline: defaultPayDeadline, refund_deadline: defaultPayDeadline, createToken: true, + wire_transfer_deadline: defaultWireDeadline, }, shipping: {}, - extra: "", + extra: {}, }; } @@ -107,8 +112,6 @@ interface Payments { wire_transfer_deadline?: Date; auto_refund_deadline?: Date; max_fee?: string; - max_wire_fee?: string; - wire_fee_amortization?: number; createToken: boolean; minimum_age?: number; } @@ -118,7 +121,7 @@ interface Entity { pricing: Partial<Pricing>; payments: Partial<Payments>; shipping: Partial<Shipping>; - extra: string; + extra: Record<string, string>; } const stringIsValidJSON = (value: string) => { @@ -136,8 +139,9 @@ export function CreatePage({ instanceConfig, instanceInventory, }: Props): VNode { - const [value, valueHandler] = useState(with_defaults(instanceConfig)); const config = useConfigContext(); + const instance_default = with_defaults(instanceConfig, config.currency) + const [value, valueHandler] = useState(instance_default); const zero = Amounts.zeroOfCurrency(config.currency); const [settings] = useSettings() const inventoryList = Object.values(value.inventoryProducts || {}); @@ -160,10 +164,10 @@ export function CreatePage({ ? i18n.str`must be greater than 0` : undefined, }), - extra: - value.extra && !stringIsValidJSON(value.extra) - ? i18n.str`not a valid json` - : undefined, + // extra: + // value.extra && !stringIsValidJSON(value.extra) + // ? i18n.str`not a valid json` + // : undefined, payments: undefinedIfEmpty({ refund_deadline: !value.payments?.refund_deadline ? undefined @@ -202,6 +206,7 @@ export function CreatePage({ ) ? i18n.str`auto refund cannot be after refund deadline` : undefined, + }), shipping: undefinedIfEmpty({ delivery_date: !value.shipping?.delivery_date @@ -225,7 +230,7 @@ export function CreatePage({ amount: order.pricing.order_price, summary: order.pricing.summary, products: productList, - extra: value.extra, + extra: JSON.stringify(value.extra), pay_deadline: value.payments.pay_deadline ? { t_s: Math.floor(value.payments.pay_deadline.getTime() / 1000), @@ -250,9 +255,7 @@ export function CreatePage({ ), } : undefined, - wire_fee_amortization: value.payments.wire_fee_amortization as number, max_fee: value.payments.max_fee as string, - max_wire_fee: value.payments.max_wire_fee as string, delivery_date: value.shipping.delivery_date ? { t_s: value.shipping.delivery_date.getTime() / 1000 } @@ -326,6 +329,8 @@ export function CreatePage({ const totalAsString = Amounts.stringify(totalPrice.amount); const allProducts = productList.concat(inventoryList.map(asProduct)); + const [newField, setNewField] = useState("") + useEffect(() => { valueHandler((v) => { return { @@ -486,16 +491,61 @@ export function CreatePage({ name="payments.pay_deadline" label={i18n.str`Payment deadline`} tooltip={i18n.str`Deadline for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline.`} + side={ + <span> + <button class="button" onClick={() => { + valueHandler({ + ...value, + payments: { + ...(value.payments ?? {}), + pay_deadline: instance_default.payments?.pay_deadline + } + }) + }}> + <i18n.Translate>default</i18n.Translate> + </button> + </span> + } /> <InputDate name="payments.refund_deadline" label={i18n.str`Refund deadline`} tooltip={i18n.str`Time until which the order can be refunded by the merchant.`} + side={ + <span> + <button class="button" onClick={() => { + valueHandler({ + ...value, + payments: { + ...(value.payments ?? {}), + refund_deadline: instance_default.payments?.refund_deadline + } + }) + }}> + <i18n.Translate>default</i18n.Translate> + </button> + </span> + } /> <InputDate name="payments.wire_transfer_deadline" label={i18n.str`Wire transfer deadline`} tooltip={i18n.str`Deadline for the exchange to make the wire transfer.`} + side={ + <span> + <button class="button" onClick={() => { + valueHandler({ + ...value, + payments: { + ...(value.payments ?? {}), + wire_transfer_deadline: instance_default.payments?.wire_transfer_deadline + } + }) + }}> + <i18n.Translate>default</i18n.Translate> + </button> + </span> + } /> <InputDate name="payments.auto_refund_deadline" @@ -505,23 +555,13 @@ export function CreatePage({ <InputCurrency name="payments.max_fee" - label={i18n.str`Maximum deposit fee`} - tooltip={i18n.str`Maximum deposit fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`} + label={i18n.str`Maximum fee`} + tooltip={i18n.str`Maximum fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`} /> - <InputCurrency - name="payments.max_wire_fee" - label={i18n.str`Maximum wire fee`} - tooltip={i18n.str`Maximum aggregate wire fees the merchant is willing to cover for this order. Wire fees exceeding this amount are to be covered by the customers.`} - /> - <InputNumber - name="payments.wire_fee_amortization" - label={i18n.str`Wire fee amortization`} - tooltip={i18n.str`Factor by which wire fees exceeding the above threshold are divided to determine the share of excess wire fees to be paid explicitly by the consumer.`} - /> - <InputBoolean + <InputToggle name="payments.createToken" label={i18n.str`Create token`} - tooltip={i18n.str`Uncheck this option if the merchant backend generated an order ID with enough entropy to prevent adversarial claims.`} + tooltip={i18n.str`If the order ID is easy to guess the token will prevent user to steal orders from others.`} /> <InputNumber name="payments.minimum_age" @@ -530,7 +570,7 @@ export function CreatePage({ help={ minAgeByProducts > 0 ? i18n.str`Min age defined by the producs is ${minAgeByProducts}` - : undefined + : i18n.str`No product with age restriction in this order` } /> </InputGroup> @@ -542,12 +582,53 @@ export function CreatePage({ label={i18n.str`Additional information`} tooltip={i18n.str`Custom information to be included in the contract for this order.`} > - <Input - name="extra" - inputType="multiline" - label={`Value`} - tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`} - /> + {Object.keys(value.extra ?? {}).map((key) => { + + return <Input + name={`extra.${key}`} + inputType="multiline" + label={key} + tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`} + side={ + <button class="button" onClick={(e) => { + if (value.extra && value.extra[key] !== undefined) { + console.log(value.extra) + delete value.extra[key] + } + valueHandler({ + ...value, + }) + }}>remove</button> + } + /> + })} + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <i18n.Translate>Custom field name</i18n.Translate> + <span class="icon has-tooltip-right" data-tooltip={"new extra field"}> + <i class="mdi mdi-information" /> + </span> + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input " value={newField} onChange={(e) => setNewField(e.currentTarget.value)} /> + </p> + </div> + </div> + <button class="button" onClick={(e) => { + setNewField("") + valueHandler({ + ...value, + extra: { + ...(value.extra ?? {}), + [newField]: "" + } + }) + }}>add</button> + </div> </InputGroup> } </FormProvider> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx index ffefd5302..2474fd042 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx @@ -38,7 +38,7 @@ export type Entity = { }; interface Props { onBack?: () => void; - onConfirm: () => void; + onConfirm: (id: string) => void; onUnauthorized: () => VNode; onNotFound: () => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode; @@ -95,7 +95,9 @@ export default function OrderCreate({ onBack={onBack} onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => { createOrder(request) - .then(onConfirm) + .then((r) => { + return onConfirm(r.data.order_id) + }) .catch((error) => { setNotif({ message: "could not create order", |