diff options
author | Sebastian <sebasjm@gmail.com> | 2021-08-07 21:59:57 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-08-07 21:59:57 -0300 |
commit | a3fd3e81dc32df7eedff40e1dcb28fa999898b56 (patch) | |
tree | 859c6c657c7138106f69ec36c1d5a25690dbe65a /packages | |
parent | 6f892448822638bcd5bea33083d311e83352b552 (diff) | |
download | merchant-backoffice-a3fd3e81dc32df7eedff40e1dcb28fa999898b56.tar.gz merchant-backoffice-a3fd3e81dc32df7eedff40e1dcb28fa999898b56.tar.bz2 merchant-backoffice-a3fd3e81dc32df7eedff40e1dcb28fa999898b56.zip |
add creditor name
Diffstat (limited to 'packages')
9 files changed, 155 insertions, 179 deletions
diff --git a/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx b/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx index 7f68a73..873ee80 100644 --- a/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx +++ b/packages/frontend/src/components/instance/DefaultInstanceFormFields.tsx @@ -37,7 +37,7 @@ export function DefaultInstanceFormFields({ readonlyId, showId }: { readonlyId?: return <Fragment> {showId && <InputWithAddon<Entity> name="id" addonBefore={`${backend.url}/instances/`} readonly={readonlyId} - label={i18n`Identifier`} + label={i18n`Identifier`} tooltip={i18n`Name of the instance in URLs. The 'default' instance is special in that it is used to administer other instances.`} /> } @@ -45,8 +45,13 @@ export function DefaultInstanceFormFields({ readonlyId, showId }: { readonlyId?: label={i18n`Business name`} tooltip={i18n`Legal name of the business represented by this instance.`} /> - <InputPayto<Entity> name="payto_uris" - label={i18n`Bank account URI`} help="x-taler-bank/bank.taler:5882/blogger" + <Input<Entity> name="creditor_name" + label={i18n`Creditor Name`} + tooltip={i18n`name of who receive the money`} + /> + + <InputPayto<Entity> name="payto_uris_base" + label={i18n`Bank account URI`} help="x-taler-bank/bank.taler:5882/blogger" tooltip={i18n`URI specifying bank account for crediting revenue.`} /> <InputCurrency<Entity> name="default_max_deposit_fee" diff --git a/packages/frontend/src/components/modal/index.tsx b/packages/frontend/src/components/modal/index.tsx index 15157f0..a7edb9e 100644 --- a/packages/frontend/src/components/modal/index.tsx +++ b/packages/frontend/src/components/modal/index.tsx @@ -34,17 +34,18 @@ interface Props { description?: string; onCancel?: () => void; onConfirm?: () => void; + label?: string; children?: ComponentChildren; danger?: boolean; disabled?: boolean; } -export function ConfirmModal({ active, description, onCancel, onConfirm, children, danger, disabled }: Props): VNode { +export function ConfirmModal({ active, description, onCancel, onConfirm, children, danger, disabled, label = 'Confirm' }: Props): VNode { return <div class={active ? "modal is-active" : "modal"}> <div class="modal-background " onClick={onCancel} /> - <div class="modal-card"> + <div class="modal-card" style={{maxWidth: 700}}> <header class="modal-card-head"> - {!description ? null : <p class="modal-card-title">{description}</p>} + {!description ? null : <p class="modal-card-title"><b>{description}</b></p>} <button class="delete " aria-label="close" onClick={onCancel} /> </header> <section class="modal-card-body"> @@ -53,7 +54,7 @@ export function ConfirmModal({ active, description, onCancel, onConfirm, childre <footer class="modal-card-foot"> <div class="buttons is-right" style={{ width: '100%' }}> <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button> - <button class={danger ? "button is-danger " : "button is-info "} disabled={disabled} onClick={onConfirm} ><Translate>Confirm</Translate></button> + <button class={danger ? "button is-danger " : "button is-info "} disabled={disabled} onClick={onConfirm} ><Translate>{label}</Translate></button> </div> </footer> </div> @@ -82,16 +83,16 @@ export function ContinueModal({ active, description, onCancel, onConfirm, childr </div> } -export function SimpleModal({onCancel, children}: any):VNode { +export function SimpleModal({ onCancel, children }: any): VNode { return <div class="modal is-active"> - <div class="modal-background " onClick={onCancel} /> - <div class="modal-card"> - <section class="modal-card-body is-main-section"> - {children} - </section> + <div class="modal-background " onClick={onCancel} /> + <div class="modal-card"> + <section class="modal-card-body is-main-section"> + {children} + </section> + </div> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> </div> - <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> -</div> } export function ClearConfirmModal({ description, onCancel, onClear, onConfirm, children }: Props & { onClear?: () => void }): VNode { @@ -124,18 +125,18 @@ interface DeleteModalProps { } export function DeleteModal({ element, onCancel, onConfirm }: DeleteModalProps): VNode { - return <ConfirmModal description="delete_instance" danger active onCancel={onCancel} onConfirm={() => onConfirm(element.id)}> - <p>This will deactivate the instance "{element.name}" with id <b>{element.id}</b></p> - <p>The merchant will not be able to create order or make refunds anymore.</p> - <p>Please confirm this action</p> + return <ConfirmModal label={`Delete instance`} description={`Delete the instance "${element.name}"`} danger active onCancel={onCancel} onConfirm={() => onConfirm(element.id)}> + <p>If you delete the instance named <b>"{element.name}"</b> (ID: <b>{element.id}</b>), the merchant will no longer be able to process orders or refunds</p> + <p>This action deletes the instance private key, but preserves all transaction data. You can still access that data after deleting the instance.</p> + <p class="warning">Deleting an instance <b>cannot be undone</b>.</p> </ConfirmModal> } export function PurgeModal({ element, onCancel, onConfirm }: DeleteModalProps): VNode { - return <ConfirmModal description="delete_instance" danger active onCancel={onCancel} onConfirm={() => onConfirm(element.id)}> - <p>This will permanently delete instance "{element.name}" with id <b>{element.id}</b></p> - <p>The information being deleted also include tax records, make sure you have backups.</p> - <p>Please confirm this action</p> + return <ConfirmModal label={`Purge the instance`} description={`Purge the instance "${element.name}"`} danger active onCancel={onCancel} onConfirm={() => onConfirm(element.id)}> + <p>If you purge the instance named <b>"{element.name}"</b> (ID: <b>{element.id}</b>), you will also delete all it's transaction data.</p> + <p>The instance will disappear from your list, and you will no longer be able to access it's data.</p> + <p class="warning">Purging an instance <b>cannot be undone</b>.</p> </ConfirmModal> } diff --git a/packages/frontend/src/paths/admin/create/CreatePage.tsx b/packages/frontend/src/paths/admin/create/CreatePage.tsx index b2e6088..76f02e1 100644 --- a/packages/frontend/src/paths/admin/create/CreatePage.tsx +++ b/packages/frontend/src/paths/admin/create/CreatePage.tsx @@ -31,7 +31,12 @@ import { DefaultInstanceFormFields } from "../../../components/instance/DefaultI import { INSTANCE_ID_REGEX, PAYTO_REGEX } from "../../../utils/constants"; import { Amounts } from "@gnu-taler/taler-util"; -export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & { auth_token?: string } +export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & { + payto_uris_base: string[], // field to construct final payto URI + creditor_name: string, // name of the receiver for the payto URI + auth_token?: string +} + interface Props { onCreate: (d: Entity) => Promise<void>; @@ -59,12 +64,23 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { const i18n = useTranslator() + if (value.payto_uris && value.payto_uris.length > 0) { + const payto = new URL(value.payto_uris[0]) + value.creditor_name = payto.searchParams.get('receiver-name') || undefined + value.payto_uris_base = value.payto_uris.map( p => { + const payto = new URL(p) + payto.searchParams.delete('receiver-name') + return payto.toString() + }) + } + const errors: FormErrors<Entity> = { - id: !value.id ? i18n`required` : ( !INSTANCE_ID_REGEX.test(value.id) ? i18n`is not valid` : undefined), + id: !value.id ? i18n`required` : (!INSTANCE_ID_REGEX.test(value.id) ? i18n`is not valid` : undefined), name: !value.name ? i18n`required` : undefined, - payto_uris: - !value.payto_uris || !value.payto_uris.length ? i18n`required` : ( - undefinedIfEmpty(value.payto_uris.map(p => { + creditor_name: !value.creditor_name ? i18n`required` : undefined, + payto_uris_base: + !value.payto_uris_base || !value.payto_uris_base.length ? i18n`required` : ( + undefinedIfEmpty(value.payto_uris_base.map(p => { return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined })) ), @@ -107,9 +123,16 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { const newToken = value.auth_token; value.auth_token = undefined; value.auth = newToken === null || newToken === undefined ? { method: "external" } : { method: "token", token: `secret-token:${newToken}` }; - if (!value.address) value.address = {} - if (!value.jurisdiction) value.jurisdiction = {} + if (!value.address) value.address = {} + if (!value.jurisdiction) value.jurisdiction = {} // remove above use conversion + const receiverName = value.creditor_name! + value.payto_uris = value.payto_uris_base?.map(p => { + const payto = new URL(p) + payto.searchParams.set('receiver-name', receiverName) + return payto.toString() + }) || [] + value.payto_uris_base = undefined // schema.validateSync(value, { abortEarly: false }) return onCreate(value as Entity); } diff --git a/packages/frontend/src/paths/admin/list/TableActive.tsx b/packages/frontend/src/paths/admin/list/TableActive.tsx index d4e586b..9286589 100644 --- a/packages/frontend/src/paths/admin/list/TableActive.tsx +++ b/packages/frontend/src/paths/admin/list/TableActive.tsx @@ -28,12 +28,13 @@ interface Props { instances: MerchantBackend.Instances.Instance[]; onUpdate: (id: string) => void; onDelete: (id: MerchantBackend.Instances.Instance) => void; + onPurge: (id: MerchantBackend.Instances.Instance) => void; onCreate: () => void; selected?: boolean; setInstanceName: (s: string) => void; } -export function CardTable({ instances, onCreate, onUpdate, setInstanceName, onDelete, selected }: Props): VNode { +export function CardTable({ instances, onCreate, onUpdate, onPurge, setInstanceName, onDelete, selected }: Props): VNode { const [actionQueue, actionQueueHandler] = useState<Actions[]>([]); const [rowSelection, rowSelectionHandler] = useState<string[]>([]) @@ -77,7 +78,7 @@ export function CardTable({ instances, onCreate, onUpdate, setInstanceName, onDe <div class="b-table has-pagination"> <div class="table-wrapper has-mobile-cards"> {instances.length > 0 ? - <Table instances={instances} onUpdate={onUpdate} setInstanceName={setInstanceName} onDelete={onDelete} rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler} /> : + <Table instances={instances} onPurge={onPurge} onUpdate={onUpdate} setInstanceName={setInstanceName} onDelete={onDelete} rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler} /> : <EmptyTable /> } </div> @@ -90,6 +91,7 @@ interface TableProps { instances: MerchantBackend.Instances.Instance[]; onUpdate: (id: string) => void; onDelete: (id: MerchantBackend.Instances.Instance) => void; + onPurge: (id: MerchantBackend.Instances.Instance) => void; rowSelectionHandler: StateUpdater<string[]>; setInstanceName: (s:string) => void; } @@ -98,7 +100,7 @@ function toggleSelected<T>(id: T): (prev: T[]) => T[] { return (prev: T[]): T[] => prev.indexOf(id) == -1 ? [...prev, id] : prev.filter(e => e != id) } -function Table({ rowSelection, rowSelectionHandler, setInstanceName, instances, onUpdate, onDelete }: TableProps): VNode { +function Table({ rowSelection, rowSelectionHandler, setInstanceName, instances, onUpdate, onDelete, onPurge }: TableProps): VNode { return ( <div class="table-container"> <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> @@ -133,9 +135,16 @@ function Table({ rowSelection, rowSelectionHandler, setInstanceName, instances, <button class="button is-small is-success jb-modal" type="button" onClick={(): void => onUpdate(i.id)}> <Translate>Edit</Translate> </button> - <button class="button is-small is-danger jb-modal" type="button" onClick={(): void => onDelete(i)}> - <Translate>Disable</Translate> + {!i.deleted && + <button class="button is-small is-danger jb-modal is-outlined" type="button" onClick={(): void => onDelete(i)}> + <Translate>Delete</Translate> </button> + } + {i.deleted && + <button class="button is-small is-danger jb-modal" type="button" onClick={(): void => onPurge(i)}> + <Translate>Purge</Translate> + </button> + } </div> </td> </tr> diff --git a/packages/frontend/src/paths/admin/list/TableDeleted.tsx b/packages/frontend/src/paths/admin/list/TableDeleted.tsx deleted file mode 100644 index 1726877..0000000 --- a/packages/frontend/src/paths/admin/list/TableDeleted.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { h, VNode } from "preact"; -import { StateUpdater, useEffect, useState } from "preact/hooks"; -import { MerchantBackend } from "../../../declaration"; -import { Translate, useTranslator } from "../../../i18n"; - -interface Props { - instances: MerchantBackend.Instances.Instance[]; - onPurge: (id: MerchantBackend.Instances.Instance) => void; - setInstanceName: (s: string) => void; -} - -export function CardTable({ instances, onPurge, setInstanceName }: Props): VNode { - - const i18n = useTranslator() - - return <div class="card has-table"> - <header class="card-header"> - <p class="card-header-title"><span class="icon"><i class="mdi mdi-desktop-mac" /></span><Translate>Disabled instances</Translate></p> - - </header> - <div class="card-content"> - <div class="b-table has-pagination"> - <div class="table-wrapper has-mobile-cards"> - {instances.length > 0 ? - <Table instances={instances} onPurge={onPurge} setInstanceName={setInstanceName} /> : - <EmptyTable /> - } - </div> - </div> - </div> - </div> -} -interface TableProps { - instances: MerchantBackend.Instances.Instance[]; - onPurge: (id: MerchantBackend.Instances.Instance) => void; - setInstanceName: (s: string) => void; -} - -function toggleSelected<T>(id: T): (prev: T[]) => T[] { - return (prev: T[]): T[] => prev.indexOf(id) == -1 ? [...prev, id] : prev.filter(e => e != id) -} - -function Table({ instances, onPurge, setInstanceName }: TableProps): VNode { - return ( - <div class="table-container"> - <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> - <thead> - <tr> - <th class="is-checkbox-cell" /> - <th><Translate>ID</Translate></th> - <th><Translate>Name</Translate></th> - <th /> - </tr> - </thead> - <tbody> - {instances.map(i => { - return <tr key={i.id}> - <td class="is-checkbox-cell" /> - <td><a href={`#/orders?instance=${i.id}`} onClick={(e) => { - setInstanceName(i.id); - }}>{i.id}</a></td> - <td >{i.name}</td> - <td class="is-actions-cell right-sticky"> - <div class="buttons is-right"> - <button class="button is-small is-danger jb-modal" type="button" onClick={(): void => onPurge(i)}> - <Translate>Delete</Translate> - </button> - </div> - </td> - </tr> - })} - - </tbody> - </table> - </div> - ) -} - -function EmptyTable(): VNode { - return <div class="content has-text-grey has-text-centered"> - <p> - <span class="icon is-large"><i class="mdi mdi-emoticon-sad mdi-48px" /></span> - </p> - <p><Translate>There is no instances yet, add more pressing the + sign</Translate></p> - </div> -} - - -interface Actions { - element: MerchantBackend.Instances.Instance; - type: 'DELETE' | 'UPDATE'; -} - -function notEmpty<TValue>(value: TValue | null | undefined): value is TValue { - return value !== null && value !== undefined; -} - -function buildActions(intances: MerchantBackend.Instances.Instance[], selected: string[], action: 'DELETE'): Actions[] { - return selected.map(id => intances.find(i => i.id === id)) - .filter(notEmpty) - .map(id => ({ element: id, type: action })) -} - - diff --git a/packages/frontend/src/paths/admin/list/View.tsx b/packages/frontend/src/paths/admin/list/View.tsx index d7f31ec..8febcff 100644 --- a/packages/frontend/src/paths/admin/list/View.tsx +++ b/packages/frontend/src/paths/admin/list/View.tsx @@ -22,7 +22,8 @@ import { h, VNode } from "preact"; import { MerchantBackend } from "../../../declaration"; import { CardTable as CardTableActive } from './TableActive'; -import { CardTable as CardTableDeleted } from './TableDeleted'; +import { useState } from 'preact/hooks'; +import { Translate, useTranslator } from "../../../i18n"; interface Props { instances: MerchantBackend.Instances.Instance[]; @@ -35,17 +36,45 @@ interface Props { } export function View({ instances, onCreate, onDelete, onPurge, onUpdate, setInstanceName, selected }: Props): VNode { - const deletedInstances = instances.filter(i => i.deleted); + const [show, setShow] = useState<"enabled" | "disabled" | null>("enabled"); + const showIsEnabled = show === 'enabled' ? "is-active" : '' + const showIsDisabled = show === 'disabled' ? "is-active" : '' + const showAll = show === null ? "is-active" : '' + const i18n = useTranslator() + const showingInstances = showIsDisabled ? + instances.filter(i => i.deleted) : (showIsEnabled ? + instances.filter(i => !i.deleted) : + instances) + return <div id="app"> <section class="section is-main-section"> - <CardTableActive instances={instances.filter(i => !i.deleted)} onDelete={onDelete} setInstanceName={setInstanceName} onUpdate={onUpdate} selected={selected} onCreate={onCreate} /> + <div class="columns"> + <div class="column is-two-thirds"> + <div class="tabs" style={{ overflow: 'inherit' }}> + <ul> + <li class={showIsEnabled}> + <div class="has-tooltip-right" data-tooltip={i18n`only show enabled instances`}> + <a onClick={() => setShow("enabled")}><Translate>Enable</Translate></a> + </div> + </li> + <li class={showIsDisabled}> + <div class="has-tooltip-right" data-tooltip={i18n`only show disabled instances`}> + <a onClick={() => setShow("disabled")}><Translate>Disable</Translate></a> + </div> + </li> + <li class={showAll}> + <div class="has-tooltip-right" data-tooltip={i18n`show all instances`}> + <a onClick={() => setShow(null)}><Translate>All</Translate></a> + </div> + </li> + </ul> + </div> + </div> + </div> + <CardTableActive instances={showingInstances} onDelete={onDelete} onPurge={onPurge} setInstanceName={setInstanceName} onUpdate={onUpdate} selected={selected} onCreate={onCreate} /> </section> - {deletedInstances.length > 0 && <section class="section is-main-section"> - <CardTableDeleted instances={deletedInstances} onPurge={onPurge} setInstanceName={setInstanceName} /> - </section>} - </div > -}
\ No newline at end of file +} diff --git a/packages/frontend/src/paths/admin/list/index.tsx b/packages/frontend/src/paths/admin/list/index.tsx index 8dd5eef..2dd9c2b 100644 --- a/packages/frontend/src/paths/admin/list/index.tsx +++ b/packages/frontend/src/paths/admin/list/index.tsx @@ -73,12 +73,12 @@ export default function Instances({ onUnauthorized, onLoadError, onNotFound, onC await deleteInstance(deleting.id) // pushNotification({ message: 'delete_success', type: 'SUCCESS' }) setNotif({ - message: i18n`Instance disable`, + message: i18n`Instance "${deleting.name}" (ID: ${deleting.id}) has been deleted`, type: 'SUCCESS' }) } catch (error) { setNotif({ - message: i18n`Failed to disable instance`, + message: i18n`Failed to delete instance`, type: "ERROR", description: error.message }) @@ -94,12 +94,12 @@ export default function Instances({ onUnauthorized, onLoadError, onNotFound, onC try { await purgeInstance(purging.id) setNotif({ - message: i18n`Instance deleted`, + message: i18n`Instance "${purging.name}" (ID: ${purging.id}) has been disabled`, type: 'SUCCESS' }) } catch (error) { setNotif({ - message: i18n`Failed to delete instance`, + message: i18n`Failed to purge instance`, type: "ERROR", description: error.message }) diff --git a/packages/frontend/src/paths/instance/update/UpdatePage.tsx b/packages/frontend/src/paths/instance/update/UpdatePage.tsx index 6e7b9eb..4a965b6 100644 --- a/packages/frontend/src/paths/instance/update/UpdatePage.tsx +++ b/packages/frontend/src/paths/instance/update/UpdatePage.tsx @@ -34,7 +34,12 @@ import { PAYTO_REGEX } from "../../../utils/constants"; import { Amounts } from "@gnu-taler/taler-util"; -type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { auth_token?: string } +type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { + payto_uris_base: string[], // field to construct final payto URI + creditor_name: string, // name of the receiver for the payto URI + auth_token?: string +} + //MerchantBackend.Instances.InstanceAuthConfigurationMessage interface Props { onUpdate: (d: Entity) => void; @@ -51,8 +56,19 @@ function convert(from: MerchantBackend.Instances.QueryInstancesResponse): Entity default_wire_fee_amortization: 1, default_pay_delay: { d_ms: 1000 * 60 * 60 }, //one hour default_wire_transfer_delay: { d_ms: 1000 * 60 * 60 * 2 }, //two hours + creditor_name: '', + payto_uris_base: new Array<string>() + } + if (payto_uris && payto_uris.length > 0) { + const payto = new URL(payto_uris[0]) + defaults.creditor_name = payto.searchParams.get('receiver-name') || '' + defaults.payto_uris_base = payto_uris.map( p => { + const payto = new URL(p) + payto.searchParams.delete('receiver-name') + return payto.toString() + }) } - return { ...defaults, ...rest, payto_uris }; + return { ...defaults, ...rest, payto_uris: [] }; } function getTokenValuePart(t?: string): string | undefined { @@ -87,9 +103,10 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props): const errors: FormErrors<Entity> = { name: !value.name ? i18n`required` : undefined, - payto_uris: - !value.payto_uris || !value.payto_uris.length ? i18n`required` : ( - undefinedIfEmpty(value.payto_uris.map(p => { + creditor_name: !value.creditor_name ? i18n`required` : undefined, + payto_uris_base: + !value.payto_uris_base || !value.payto_uris_base.length ? i18n`required` : ( + undefinedIfEmpty(value.payto_uris_base.map(p => { return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined })) ), @@ -124,9 +141,17 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props): undefined }), }; - + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) const submit = async (): Promise<void> => { + const receiverName = value.creditor_name! + value.payto_uris = value.payto_uris_base?.map(p => { + const payto = new URL(p) + payto.searchParams.set('receiver-name', receiverName) + return payto.toString() + }) || [] + value.payto_uris_base = undefined + await onUpdate(schema.cast(value)); await onBack() return Promise.resolve() diff --git a/packages/frontend/src/scss/main.scss b/packages/frontend/src/scss/main.scss index d59a1db..f9ae0ef 100644 --- a/packages/frontend/src/scss/main.scss +++ b/packages/frontend/src/scss/main.scss @@ -178,4 +178,13 @@ span[data-tooltip] { div[data-tooltip]::before { position: absolute; -}
\ No newline at end of file +} + +.modal-card-body > p { + padding: 1em; +} + +.modal-card-body > p.warning { + background-color: #fffbdd; + border: solid 1px #f2e9bf; +} |