gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant-backoffice] branch master updated: add more information


From: gnunet
Subject: [taler-merchant-backoffice] branch master updated: add more information about reserve
Date: Mon, 07 Jun 2021 14:42:59 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository merchant-backoffice.

The following commit(s) were added to refs/heads/master by this push:
     new 0eacc9a  add more information about reserve
0eacc9a is described below

commit 0eacc9ad11caa53efe5c71add7028251d058b753
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Jun 7 09:42:16 2021 -0300

    add more information about reserve
---
 packages/frontend/src/declaration.d.ts             | 38 ++++++++-----
 .../paths/instance/orders/create/CreatePage.tsx    | 25 ++++-----
 .../reserves/create/CreatedSuccessfully.tsx        |  4 +-
 .../paths/instance/reserves/details/DetailPage.tsx | 63 ++++++++++++++++++----
 .../src/paths/instance/reserves/details/index.tsx  |  4 +-
 .../src/paths/instance/reserves/list/Table.tsx     | 12 ++---
 packages/frontend/src/utils/amount.ts              | 10 ++--
 7 files changed, 107 insertions(+), 49 deletions(-)

diff --git a/packages/frontend/src/declaration.d.ts 
b/packages/frontend/src/declaration.d.ts
index 6717566..21199f4 100644
--- a/packages/frontend/src/declaration.d.ts
+++ b/packages/frontend/src/declaration.d.ts
@@ -56,45 +56,45 @@ export namespace ExchangeBackend {
 
         // Master public key of the exchange, must match the key returned in 
/keys.
         master_public_key: EddsaPublicKey;
-      
+
         // Array of wire accounts operated by the exchange for
         // incoming wire transfers.
         accounts: WireAccount[];
-      
+
         // Object mapping names of wire methods (i.e. "sepa" or "x-taler-bank")
         // to wire fees.
-        fees: { method : AggregateTransferFee };
-      }
-      interface WireAccount {
+        fees: { method: AggregateTransferFee };
+    }
+    interface WireAccount {
         // payto:// URI identifying the account and wire method
         payto_uri: string;
-      
+
         // Signature using the exchange's offline key
         // with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
         master_sig: EddsaSignature;
-      }
-      interface AggregateTransferFee {
+    }
+    interface AggregateTransferFee {
         // Per transfer wire transfer fee.
         wire_fee: Amount;
-      
+
         // Per transfer closing fee.
         closing_fee: Amount;
-      
+
         // What date (inclusive) does this fee go into effect?
         // The different fees must cover the full time period in which
         // any of the denomination keys are valid without overlap.
         start_date: Timestamp;
-      
+
         // What date (exclusive) does this fee stop going into effect?
         // The different fees must cover the full time period in which
         // any of the denomination keys are valid without overlap.
         end_date: Timestamp;
-      
+
         // Signature of TALER_MasterWireFeePS with
         // purpose TALER_SIGNATURE_MASTER_WIRE_FEES.
         sig: EddsaSignature;
-      }
-      
+    }
+
 }
 export namespace MerchantBackend {
     interface ErrorDetail {
@@ -1000,7 +1000,17 @@ export namespace MerchantBackend {
 
             // Is this reserve active (false if it was deleted but not purged)?
             active: boolean;
+
+            // URI to use to fill the reserve, can be NULL
+            // if the reserve is inactive or was already filled
+            payto_uri: string;
+
+            // URL of the exchange hosting the reserve,
+            // NULL if the reserve is inactive
+            exchange_url: string;
+
         }
+
         interface TipStatusEntry {
 
             // Unique identifier for the tip.
diff --git a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx 
b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
index 9d22fc8..ef25500 100644
--- a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
@@ -44,7 +44,7 @@ interface Props {
   onBack?: () => void;
 }
 
-function with_defaults(): Entity {
+function with_defaults(): Partial<Entity> {
   return {
     inventoryProducts: {},
     products: [],
@@ -90,8 +90,8 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
   const [value, valueHandler] = useState(with_defaults())
   // const [errors, setErrors] = useState<FormErrors<Entity>>({})
 
-  const inventoryList = Object.values(value.inventoryProducts)
-  const productList = Object.values(value.products)
+  const inventoryList = Object.values(value.inventoryProducts || {})
+  const productList = Object.values(value.products || {})
 
   let errors: FormErrors<Entity> = {}
   try {
@@ -104,6 +104,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
 
   const submit = (): void => {
     const order = schema.cast(value)
+    if (!value.payments) return;
 
     const request: MerchantBackend.Orders.PostOrderRequest = {
       order: {
@@ -149,14 +150,14 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
 
   const addNewProduct = async (product: MerchantBackend.Product) => {
     return valueHandler(v => {
-      const products = [...v.products, product]
+      const products = v.products ? [...v.products, product] : []
       return ({ ...v, products })
     })
   }
 
   const removeFromNewProduct = (index: number) => {
     valueHandler(v => {
-      const products = [...v.products]
+      const products = v.products ? [...v.products] : []
       products.splice(index, 1)
       return ({ ...v, products })
     })
@@ -174,7 +175,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
     valueHandler(v => {
       return ({
         ...v, pricing: {
-          ...v.pricing,
+          ...v.pricing!,
           products_price: totalPrice,
           order_price: totalPrice,
         }
@@ -183,17 +184,17 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
   }, [hasProducts, totalPrice])
 
 
-  const discountOrRise = rate(value.pricing.order_price, totalPrice)
+  const discountOrRise = rate(value.pricing?.order_price || 
`${config.currency}:0`, totalPrice)
 
   useEffect(() => {
     valueHandler(v => {
       return ({
         ...v, pricing: {
-          ...v.pricing,
+          ...v.pricing!
         }
       })
     })
-  }, [value.pricing.order_price])
+  }, [value.pricing?.order_price])
 
   const details_response = useInstanceDetails()
 
@@ -236,7 +237,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
             </p>
           } tooltip={i18n`add products to the order that already exist in the 
inventory`}>
             <InventoryProductForm
-              currentProducts={value.inventoryProducts}
+              currentProducts={value.inventoryProducts || {}}
               onAddProduct={addProductToTheInventoryList}
             />
 
@@ -280,7 +281,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
                 <InputCurrency name="pricing.products_price" label={i18n`Total 
price`} readonly tooltip={i18n`total product price added up`} />
                 <InputCurrency name="pricing.order_price"
                   label={i18n`Order price`}
-                  addonAfter={value.pricing.order_price !== totalPrice && 
(discountOrRise < 1 ?
+                  addonAfter={value.pricing?.order_price !== totalPrice && 
(discountOrRise < 1 ?
                     `discount of %${Math.round((1 - discountOrRise) * 100)}` :
                     `rise of %${Math.round((discountOrRise - 1) * 100)}`)
                   }
@@ -298,7 +299,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
               <InputDate name="payments.pay_deadline" label={i18n`Payment 
deadline`} tooltip={i18n`time until the order can be paid`} />
 
               <InputDate name="payments.delivery_date" label={i18n`Delivery 
date`} tooltip={i18n`when the order will be delivered, if delivery required`} />
-              {value.payments.delivery_date && <InputGroup 
name="payments.delivery_location" label={i18n`Location`} tooltip={i18n`where 
the order will be delivered`} >
+              {value.payments?.delivery_date && <InputGroup 
name="payments.delivery_location" label={i18n`Location`} tooltip={i18n`where 
the order will be delivered`} >
                 <InputLocation name="payments.delivery_location" />
               </InputGroup>}
 
diff --git 
a/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx 
b/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
index 2deae14..d173d7b 100644
--- 
a/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
+++ 
b/packages/frontend/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
@@ -18,7 +18,7 @@ import { CreatedSuccessfully as Template } from 
"../../../../components/notifica
 import { MerchantBackend } from "../../../../declaration";
 import { Translate } from "../../../../i18n";
 
-type Entity = {request: MerchantBackend.Tips.ReserveCreateRequest, response: 
MerchantBackend.Tips.ReserveCreateConfirmation};
+type Entity = { request: MerchantBackend.Tips.ReserveCreateRequest, response: 
MerchantBackend.Tips.ReserveCreateConfirmation };
 
 interface Props {
   entity: Entity;
@@ -81,7 +81,7 @@ export function CreatedSuccessfully({ entity, onConfirm, 
onCreateAnother }: Prop
 
     <p class="is-size-5"><Translate>For example:</Translate></p>
     <pre>
-    
{entity.response.payto_uri}?message={entity.response.reserve_pub}&amount={entity.request.initial_balance}
+      
{entity.response.payto_uri}?message={entity.response.reserve_pub}&amount={entity.request.initial_balance}
     </pre>
   </Template>;
 }
diff --git 
a/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx 
b/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx
index 08b463a..08942f6 100644
--- a/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx
+++ b/packages/frontend/src/paths/instance/reserves/details/DetailPage.tsx
@@ -19,6 +19,7 @@
 * @author Sebastian Javier Marchano (sebasjm)
 */
 
+import { Amounts } from "@gnu-taler/taler-util";
 import { format, isAfter } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -42,22 +43,55 @@ type CT = MerchantBackend.ContractTerms
 interface Props {
   onBack: () => void;
   selected: Entity;
+  id: string;
 }
 
-export function DetailPage({ selected, onBack }: Props): VNode {
+export function DetailPage({ id, selected, onBack }: Props): VNode {
   const i18n = useTranslator()
+  const didExchangeAckTransfer = 
Amounts.isNonZero(Amounts.parseOrThrow(selected.exchange_initial_amount))
   return <div class="section main-section">
-    <FormProvider object={selected} valueHandler={null} >
+    <FormProvider object={{ ...selected, id }} valueHandler={null} >
       <InputDate<Entity> name="creation_time" label={i18n`Created at`} 
readonly />
       <InputDate<Entity> name="expiration_time" label={i18n`Valid until`} 
readonly />
       <InputCurrency<Entity> name="merchant_initial_amount" 
label={i18n`Created balance`} readonly />
-      <InputCurrency<Entity> name="exchange_initial_amount" 
label={i18n`Exchange balance`} readonly />
-      <InputCurrency<Entity> name="pickup_amount" label={i18n`Picked up`} 
readonly />
-      <InputCurrency<Entity> name="committed_amount" label={i18n`Committed`} 
readonly />
+      <Input<Entity> name="exchange_url" label={i18n`Exchange URL`} readonly />
+
+      {didExchangeAckTransfer && <Fragment>
+        <InputCurrency<Entity> name="exchange_initial_amount" 
label={i18n`Exchange balance`} readonly />
+        <InputCurrency<Entity> name="pickup_amount" label={i18n`Picked up`} 
readonly />
+        <InputCurrency<Entity> name="committed_amount" label={i18n`Committed`} 
readonly />
+      </Fragment>
+      }
+      <Input<Entity> name="payto_uri" label={i18n`Account address`} readonly />
+      <Input name="id" label={i18n`Subject`} readonly />
     </FormProvider>
-    {selected.tips && selected.tips.length > 0 ? <Table tips={selected.tips} 
/> : <div>
-      no tips for this reserve
-    </div>}
+
+    {didExchangeAckTransfer ? <Fragment>
+      <div class="card has-table">
+        <header class="card-header">
+          <p class="card-header-title">
+            <span class="icon"><i class="mdi mdi-cash-register" /></span>
+            <Translate>Tips</Translate>
+          </p>
+
+        </header>
+        <div class="card-content">
+          <div class="b-table has-pagination">
+            <div class="table-wrapper has-mobile-cards">
+              {selected.tips && selected.tips.length > 0 ? <Table 
tips={selected.tips} /> : <EmptyTable />}
+            </div></div>
+        </div>
+      </div>
+    </Fragment> : <Fragment>
+      <p class="is-size-5"><Translate>Now you should transfer to the exchange 
into the account address indicated above and the transaction must carry the 
subject message.</Translate></p>
+
+      <p class="is-size-5"><Translate>For example :</Translate></p>
+      <pre>
+        
{selected.payto_uri}?message={id}&amount={selected.merchant_initial_amount}
+      </pre>
+    </Fragment>
+    }
+
     <div class="columns">
       <div class="column" />
       <div class="column is-two-thirds">
@@ -71,6 +105,16 @@ export function DetailPage({ selected, onBack }: Props): 
VNode {
   </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>No tips has been authorized from this reserve</Translate></p>
+  </div>
+}
+
+
 async function copyToClipboard(text: string) {
   return navigator.clipboard.writeText(text)
 }
@@ -124,4 +168,5 @@ function TipRow({ id, entry }: { id: string, entry: 
MerchantBackend.Tips.TipStat
     <td>{info.reason}</td>
     <td>{info.expiration.t_ms === "never" ? "never" : 
format(info.expiration.t_ms, 'yyyy/MM/dd HH:mm:ss')}</td>
   </tr>
-}
\ No newline at end of file
+}
+
diff --git a/packages/frontend/src/paths/instance/reserves/details/index.tsx 
b/packages/frontend/src/paths/instance/reserves/details/index.tsx
index 569ef5b..aaf275b 100644
--- a/packages/frontend/src/paths/instance/reserves/details/index.tsx
+++ b/packages/frontend/src/paths/instance/reserves/details/index.tsx
@@ -34,7 +34,7 @@ interface Props {
   onDelete: () => void;
   onBack: () => void;
 }
-export default function DetailReserve({rid, onUnauthorized, onLoadError, 
onNotFound, onBack, onDelete}: Props):VNode {
+export default function DetailReserve({ rid, onUnauthorized, onLoadError, 
onNotFound, onBack, onDelete }: Props): VNode {
   const result = useReserveDetails(rid)
 
   if (result.clientError && result.isUnauthorized) return onUnauthorized()
@@ -42,6 +42,6 @@ export default function DetailReserve({rid, onUnauthorized, 
onLoadError, onNotFo
   if (result.loading) return <Loading />
   if (!result.ok) return onLoadError(result)
   return <Fragment>
-    <DetailPage selected={result.data} onBack={onBack} />
+    <DetailPage selected={result.data} onBack={onBack} id={rid} />
   </Fragment>
 }
diff --git a/packages/frontend/src/paths/instance/reserves/list/Table.tsx 
b/packages/frontend/src/paths/instance/reserves/list/Table.tsx
index 83e4fb8..c53dd2a 100644
--- a/packages/frontend/src/paths/instance/reserves/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/reserves/list/Table.tsx
@@ -21,10 +21,8 @@
 
 import { format } from "date-fns"
 import { Fragment, h, VNode } from "preact"
-import { StateUpdater, useEffect, useState } from "preact/hooks"
 import { MerchantBackend, WithId } from "../../../../declaration"
 import { Translate } from "../../../../i18n"
-import { Actions, buildActions } from "../../../../utils/table"
 
 type Entity = MerchantBackend.Tips.ReserveStatusEntry & WithId
 
@@ -54,11 +52,6 @@ export function CardTable({ instances, onCreate, onSelect, 
onNewTip, onDelete }:
     {withoutFunds.length > 0 && <div class="card has-table">
       <header class="card-header">
         <p class="card-header-title"><span class="icon"><i class="mdi 
mdi-cash" /></span><Translate>Reserves not yet funded</Translate></p>
-        <div class="card-header-icon" aria-label="more options">
-          <button class="button is-info" type="button" onClick={onCreate}>
-            <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" 
/></span>
-          </button>
-        </div>
       </header>
       <div class="card-content">
         <div class="b-table has-pagination">
@@ -73,6 +66,11 @@ export function CardTable({ instances, onCreate, onSelect, 
onNewTip, onDelete }:
       <header class="card-header">
         <p class="card-header-title"><span class="icon"><i class="mdi 
mdi-cash" /></span><Translate>Reserves ready</Translate></p>
         <div class="card-header-icon" aria-label="more options" />
+        <div class="card-header-icon" aria-label="more options">
+          <button class="button is-info" type="button" onClick={onCreate}>
+            <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" 
/></span>
+          </button>
+        </div>
       </header>
       <div class="card-content">
         <div class="b-table has-pagination">
diff --git a/packages/frontend/src/utils/amount.ts 
b/packages/frontend/src/utils/amount.ts
index 8fdf048..823ad9d 100644
--- a/packages/frontend/src/utils/amount.ts
+++ b/packages/frontend/src/utils/amount.ts
@@ -13,7 +13,7 @@
  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/>
  */
-import { Amounts } from "@gnu-taler/taler-util";
+import { amountFractionalBase, AmountJson, Amounts } from 
"@gnu-taler/taler-util";
 import { MerchantBackend } from "../declaration";
 
 /**
@@ -71,8 +71,12 @@ export const subtractPrices = (one: string, two: string) => {
 export const rate = (one: string, two: string) => {
   const a = Amounts.parseOrThrow(one)
   const b = Amounts.parseOrThrow(two)
-  const af = Amounts.toFloat(a)
-  const bf = Amounts.toFloat(b)
+  const af = toFloat(a)
+  const bf = toFloat(b)
+  if (bf === 0) return 0
   return af / bf
 }
 
+function toFloat(amount: AmountJson) {
+  return amount.value + (amount.fraction / amountFractionalBase);
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]