gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated: Add support for adding and showing ban


From: gnunet
Subject: [libeufin] branch master updated: Add support for adding and showing bank connections/bank accounts
Date: Fri, 26 Jun 2020 05:32:57 +0200

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

heng-yeow pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new 67767c6  Add support for adding and showing bank connections/bank 
accounts
67767c6 is described below

commit 67767c666bcd1ee6ca01df2747b55bf6e5a70485
Author: tanhengyeow <E0032242@u.nus.edu>
AuthorDate: Fri Jun 26 11:32:45 2020 +0800

    Add support for adding and showing bank connections/bank accounts
---
 .../bank-accounts/AddBankConnectionDrawer.tsx      | 247 +++++++++++++++++++++
 .../bank-accounts/BankConnectionCard.tsx           |  29 +++
 .../bank-accounts/BankConnectionDrawer.tsx         | 164 ++++++++++++++
 frontend/src/components/bank-accounts/Index.tsx    | 110 +++++----
 4 files changed, 494 insertions(+), 56 deletions(-)

diff --git a/frontend/src/components/bank-accounts/AddBankConnectionDrawer.tsx 
b/frontend/src/components/bank-accounts/AddBankConnectionDrawer.tsx
new file mode 100644
index 0000000..57b85cc
--- /dev/null
+++ b/frontend/src/components/bank-accounts/AddBankConnectionDrawer.tsx
@@ -0,0 +1,247 @@
+import React, { useState } from 'react';
+import { Button, Drawer, Input, Form, Steps } from 'antd';
+const { Step } = Steps;
+
+const layout = {
+  labelCol: { span: 4 },
+};
+
+const AddBankConnectionDrawer = (props) => {
+  const { visible, onClose } = props;
+  const [currentStep, setCurrentStep] = useState(0);
+  const [printLink, setPrintLink] = useState('');
+
+  const [name, setName] = useState('');
+  const [serverURL, setServerURL] = useState('');
+  const [hostID, setHostID] = useState('');
+  const [partnerID, setPartnerID] = useState('');
+  const [userID, setUserID] = useState('');
+  const [systemID, setSystemID] = useState('');
+
+  const steps = [{ title: 'Fill up details' }, { title: 'Print document' }];
+
+  const createBankConnection = async () => {
+    const authHeader = await window.localStorage.getItem('authHeader');
+    await fetch(`/bank-connections`, {
+      headers: new Headers({
+        Authorization: `Basic ${authHeader}`,
+        'Content-Type': 'application/json',
+      }),
+      method: 'POST',
+      body: JSON.stringify({
+        name: name,
+        source: 'new',
+        type: 'ebics',
+        data: {
+          ebicsURL: serverURL,
+          hostID: hostID,
+          partnerID: partnerID,
+          userID: userID,
+        },
+      }),
+    })
+      .then((response) => {
+        if (!response.ok) {
+          throw 'Cannot create bank connection';
+        }
+      })
+      .catch((err) => {
+        throw new Error(err);
+      });
+  };
+
+  const connectBankConnection = async () => {
+    const authHeader = await window.localStorage.getItem('authHeader');
+    await fetch(`/bank-connections/${name}/connect`, {
+      headers: new Headers({
+        Authorization: `Basic ${authHeader}`,
+      }),
+      method: 'POST',
+    })
+      .then((response) => {
+        if (!response.ok) {
+          throw 'Cannot connect bank connection';
+        }
+      })
+      .catch((err) => {
+        throw new Error(err);
+      });
+  };
+
+  const fetchKeyLetter = async () => {
+    const authHeader = await window.localStorage.getItem('authHeader');
+    await fetch(`/bank-connections/${name}/keyletter`, {
+      headers: new Headers({
+        Authorization: `Basic ${authHeader}`,
+      }),
+    })
+      .then((response) => {
+        if (response.ok) {
+          return response.blob();
+        }
+        throw 'Cannot fetch keyletter';
+      })
+      .then(async (blob) => {
+        const pdfLink = URL.createObjectURL(blob);
+        setPrintLink(pdfLink);
+      })
+      .catch((err) => {
+        throw new Error(err);
+      });
+  };
+
+  const updateBankAccounts = async () => {
+    const authHeader = await window.localStorage.getItem('authHeader');
+    await fetch(`/bank-connections/${name}/fetch-accounts`, {
+      headers: new Headers({
+        Authorization: `Basic ${authHeader}`,
+      }),
+      method: 'POST',
+    })
+      .then((response) => {
+        if (!response.ok) {
+          throw 'Cannot update bank accounts';
+        }
+      })
+      .catch((err) => {
+        throw new Error(err);
+      });
+  };
+
+  const next = async () => {
+    await createBankConnection();
+    await connectBankConnection();
+    await fetchKeyLetter();
+    await updateBankAccounts();
+
+    setServerURL('');
+    setHostID('');
+    setPartnerID('');
+    setUserID('');
+    setSystemID('');
+    setCurrentStep(currentStep + 1);
+  };
+
+  const closeDrawer = () => {
+    setCurrentStep(0);
+    onClose();
+  };
+
+  return (
+    <Drawer
+      title="Add bank connection"
+      placement="right"
+      closable={false}
+      onClose={onClose}
+      visible={visible}
+      width={850}
+    >
+      <div className="steps-row">
+        <Steps current={currentStep}>
+          {steps.map((item) => (
+            <Step key={item.title} title={item.title} />
+          ))}
+        </Steps>
+      </div>
+      <div>
+        {currentStep < steps.length - 1 ? (
+          <Form {...layout} name="basic">
+            <Form.Item
+              label="Server URL"
+              name="Server URL"
+              rules={[
+                { required: true, message: 'Please input the Server URL!' },
+              ]}
+            >
+              <Input onChange={(e) => setServerURL(e.target.value)} />
+            </Form.Item>
+            <Form.Item
+              label="Name"
+              name="Name"
+              rules={[
+                {
+                  required: true,
+                  message: 'Please input the name of the bank connection!',
+                },
+              ]}
+            >
+              <Input onChange={(e) => setName(e.target.value)} />
+            </Form.Item>
+            <Form.Item
+              label="Host ID"
+              name="Host ID"
+              rules={[{ required: true, message: 'Please input the Host ID!' 
}]}
+            >
+              <Input onChange={(e) => setHostID(e.target.value)} />
+            </Form.Item>
+            <Form.Item
+              label="Partner ID"
+              name="Partner ID"
+              rules={[
+                { required: true, message: 'Please input the Partner ID!' },
+              ]}
+            >
+              <Input onChange={(e) => setPartnerID(e.target.value)} />
+            </Form.Item>
+            <Form.Item
+              label="User ID"
+              name="User ID"
+              rules={[{ required: true, message: 'Please input the User ID!' 
}]}
+            >
+              <Input onChange={(e) => setUserID(e.target.value)} />
+            </Form.Item>
+            <Form.Item label="System ID" name="System ID">
+              <Input onChange={(e) => setSystemID(e.target.value)} />
+            </Form.Item>
+          </Form>
+        ) : (
+          <div
+            style={{
+              fontSize: 24,
+              display: 'flex',
+              flexDirection: 'column',
+              alignItems: 'center',
+            }}
+          >
+            <div>Please print out this document and send it to the bank.</div>
+            <div>
+              <a href={printLink} target="_blank">
+                Link to document
+              </a>{' '}
+            </div>
+          </div>
+        )}
+      </div>
+      <div className="steps-action">
+        <Button
+          style={{ marginRight: '20px' }}
+          size="large"
+          onClick={() => closeDrawer()}
+        >
+          Cancel
+        </Button>
+        {currentStep < steps.length - 1 ? (
+          <Button
+            style={{ marginRight: '40px' }}
+            type="primary"
+            size="large"
+            onClick={() => next()}
+          >
+            Next
+          </Button>
+        ) : (
+          <Button
+            style={{ marginRight: '40px' }}
+            type="primary"
+            size="large"
+            onClick={() => closeDrawer()}
+          >
+            Done
+          </Button>
+        )}
+      </div>
+    </Drawer>
+  );
+};
+
+export default AddBankConnectionDrawer;
diff --git a/frontend/src/components/bank-accounts/BankConnectionCard.tsx 
b/frontend/src/components/bank-accounts/BankConnectionCard.tsx
new file mode 100644
index 0000000..36f21f9
--- /dev/null
+++ b/frontend/src/components/bank-accounts/BankConnectionCard.tsx
@@ -0,0 +1,29 @@
+import React, { useState } from 'react';
+import { Card } from 'antd';
+import BankConnectionDrawer from './BankConnectionDrawer';
+
+const BankConnectionCard = (props) => {
+  const { type, name, updateBankAccountsTab } = props;
+  const [visible, setVisible] = useState(false);
+  const showDrawer = () => {
+    setVisible(true);
+  };
+  const onClose = () => {
+    setVisible(false);
+  };
+  return (
+    <>
+      <Card title={type} bordered={false} onClick={() => showDrawer()}>
+        <p>Name: {name}</p>
+      </Card>
+      <BankConnectionDrawer
+        updateBankAccountsTab={updateBankAccountsTab}
+        name={name}
+        visible={visible}
+        onClose={onClose}
+      />
+    </>
+  );
+};
+
+export default BankConnectionCard;
diff --git a/frontend/src/components/bank-accounts/BankConnectionDrawer.tsx 
b/frontend/src/components/bank-accounts/BankConnectionDrawer.tsx
new file mode 100644
index 0000000..cb22ebf
--- /dev/null
+++ b/frontend/src/components/bank-accounts/BankConnectionDrawer.tsx
@@ -0,0 +1,164 @@
+import React, { useState } from 'react';
+import { Button, Drawer, Table } from 'antd';
+
+const columns = [
+  {
+    title: 'Account ID',
+    dataIndex: 'offeredAccountId',
+  },
+  {
+    title: 'Owner name',
+    dataIndex: 'ownerName',
+  },
+  {
+    title: 'IBAN',
+    dataIndex: 'iban',
+  },
+  {
+    title: 'BIC',
+    dataIndex: 'bic',
+  },
+];
+
+const BankConnectionDrawer = (props) => {
+  const { visible, onClose, name, updateBankAccountsTab } = props;
+  const [printLink, setPrintLink] = useState('');
+  const [accountsList, setAccountsList] = useState([]);
+  const [selectedRowKeys, setSelectedRowKeys] = useState([]);
+
+  const onSelectChange = (selectedRowKeys) => {
+    setSelectedRowKeys(selectedRowKeys);
+  };
+
+  const fetchKeyLetter = async () => {
+    const authHeader = await window.localStorage.getItem('authHeader');
+    await fetch(`/bank-connections/${name}/keyletter`, {
+      headers: new Headers({
+        Authorization: `Basic ${authHeader}`,
+      }),
+    })
+      .then((response) => {
+        if (response.ok) {
+          return response.blob();
+        }
+        throw 'Cannot fetch keyletter';
+      })
+      .then(async (blob) => {
+        const pdfLink = URL.createObjectURL(blob);
+        setPrintLink(pdfLink);
+      });
+  };
+
+  const fetchBankAccounts = async () => {
+    const authHeader = await window.localStorage.getItem('authHeader');
+
+    await fetch(`/bank-connections/${name}/accounts`, {
+      headers: new Headers({
+        Authorization: `Basic ${authHeader}`,
+      }),
+    })
+      .then((response) => {
+        if (!response.ok) {
+          throw 'Cannot fetch bank accounts';
+        }
+        return response.json();
+      })
+      .then((response) => {
+        setAccountsList(
+          response.map((account, index) => ({
+            ...account,
+            key: index,
+          }))
+        );
+      })
+      .catch((err) => {
+        throw new Error(err);
+      });
+  };
+
+  const importBankAccounts = async () => {
+    for (let i = 0; i < selectedRowKeys.length; i++) {
+      const { offeredAccountId } = accountsList[i];
+      await importBankAccount(offeredAccountId);
+    }
+    await updateBankAccountsTab(); // refresh bank accounts tab
+    onClose();
+  };
+
+  const importBankAccount = async (offeredAccountId) => {
+    const authHeader = await window.localStorage.getItem('authHeader');
+    await fetch(`/bank-connections/${name}/import-account`, {
+      headers: new Headers({
+        Authorization: `Basic ${authHeader}`,
+        'Content-Type': 'application/json',
+      }),
+      method: 'POST',
+      body: JSON.stringify({
+        offeredAccountId: offeredAccountId ? offeredAccountId : '',
+        nexusBankAccountId: offeredAccountId,
+      }),
+    })
+      .then((response) => {
+        if (!response.ok) {
+          throw 'Cannot import bank account';
+        }
+      })
+      .catch((err) => {
+        throw new Error(err);
+      });
+  };
+
+  React.useEffect(() => {
+    fetchKeyLetter();
+    fetchBankAccounts();
+  }, []);
+
+  return (
+    <Drawer
+      title={name}
+      placement="right"
+      closable={false}
+      onClose={onClose}
+      visible={visible}
+      width={850}
+    >
+      <div
+        style={{
+          position: 'absolute',
+          right: 20,
+        }}
+      >
+        <a href={printLink} target="_blank">
+          Print document link
+        </a>{' '}
+      </div>
+      <h2>Import Bank Accounts</h2>
+      <Table
+        rowSelection={{
+          selectedRowKeys,
+          onChange: onSelectChange,
+        }}
+        columns={columns}
+        dataSource={accountsList}
+      />
+      <div className="steps-action">
+        <Button
+          style={{ marginRight: '20px' }}
+          size="large"
+          onClick={() => onClose()}
+        >
+          Cancel
+        </Button>
+        <Button
+          style={{ marginRight: '20px' }}
+          size="large"
+          onClick={() => importBankAccounts()}
+        >
+          Import selected
+        </Button>
+      </div>
+    </Drawer>
+  );
+};
+
+export default BankConnectionDrawer;
diff --git a/frontend/src/components/bank-accounts/Index.tsx 
b/frontend/src/components/bank-accounts/Index.tsx
index 69f2f2c..2ec785c 100644
--- a/frontend/src/components/bank-accounts/Index.tsx
+++ b/frontend/src/components/bank-accounts/Index.tsx
@@ -1,9 +1,11 @@
 import React, { useState } from 'react';
-import { Button, Card, Col, Row, Tabs } from 'antd';
+import { Button, Card, Col, Collapse, Row, Tabs } from 'antd';
 import './BankAccounts.less';
-// import AddBankConnectionDrawer from './AddBankConnectionDrawer';
+import AddBankConnectionDrawer from './AddBankConnectionDrawer';
+import BankConnectionCard from './BankConnectionCard';
 
 const { TabPane } = Tabs;
+const { Panel } = Collapse;
 
 const BankAccounts = () => {
   const [connectionsList, setConnectionsList] = useState([]);
@@ -17,7 +19,6 @@ const BankAccounts = () => {
       }),
     })
       .then((response) => {
-        console.log(response);
         if (response.ok) {
           return response.json();
         }
@@ -27,7 +28,6 @@ const BankAccounts = () => {
         setConnectionsList(response.bankConnections);
       })
       .catch((err) => {
-        console.log(err);
         throw new Error(err);
       });
   };
@@ -49,7 +49,6 @@ const BankAccounts = () => {
         setAccountsList(response.accounts);
       })
       .catch((err) => {
-        console.log(err);
         throw new Error(err);
       });
   };
@@ -69,61 +68,60 @@ const BankAccounts = () => {
     fetchBankAccounts();
   };
 
+  const bankAccountsContent =
+    accountsList.length > 0 ? (
+      <Row gutter={[40, 40]}>
+        {accountsList.map((bankAccount) => (
+          <Col span={8}>
+            <Card title={bankAccount['account']} bordered={false}>
+              <p>Holder: {bankAccount['holder']}</p>
+              <p>IBAN: {bankAccount['iban']}</p>
+              <p>BIC: {bankAccount['bic']}</p>
+            </Card>
+          </Col>
+        ))}
+      </Row>
+    ) : (
+      <div style={{ display: 'flex', justifyContent: 'center' }}>
+        <b>
+          No bank accounts found. Import your bank accounts from a bank
+          connection.
+        </b>
+      </div>
+    );
+
   return (
     <div className="bank-accounts">
       <Tabs defaultActiveKey="1" type="card" size="large">
-        <TabPane tab="Bank connections" key="1">
-          <div className="buttons-row">
-            <Button type="primary" size="middle" onClick={showDrawer}>
-              Add bank connection
-            </Button>
-            {/* <AddBankConnectionDrawer visible={visible} onClose={onClose} 
/> */}
-            <Button type="primary" size="middle">
-              Import from backup
-            </Button>
-            <Button type="primary" size="middle">
-              Reload connections
-            </Button>
-          </div>
-          <Row gutter={[40, 40]}>
-            {connectionsList
-              ? connectionsList.map((bankConnection) => (
-                  <Col span={8}>
-                    <Card
-                      title={String(bankConnection['type']).toUpperCase()}
-                      bordered={false}
-                    >
-                      <p>Name: {bankConnection['name']}</p>
-                    </Card>
-                  </Col>
-                ))
-              : null}
-          </Row>
-        </TabPane>
-        <TabPane tab="Your accounts" key="2">
-          <div className="buttons-row">
-            <Button type="primary" size="middle">
-              Add bank account
-            </Button>
-          </div>
-          <Row gutter={[40, 40]}>
-            {accountsList
-              ? accountsList.map((bankAccount) => (
-                  <Col span={8}>
-                    <Card
-                      title={String(bankAccount['account']).toUpperCase()}
-                      bordered={false}
-                    >
-                      <p>Holder: {bankAccount['holder']}</p>
-                      <p>IBAN: {bankAccount['iban']}</p>
-                      <p>BIC: {bankAccount['bic']}</p>
-                    </Card>
-                  </Col>
-                ))
-              : null}
-          </Row>
+        <TabPane tab="Your accounts" key="1">
+          <Collapse defaultActiveKey="2">
+            <Panel header="Bank connections" key="1">
+              <div className="buttons-row">
+                <Button type="primary" size="middle" onClick={showDrawer}>
+                  Add bank connection
+                </Button>
+                <AddBankConnectionDrawer visible={visible} onClose={onClose} />
+              </div>
+              <Row gutter={[40, 40]}>
+                {connectionsList
+                  ? connectionsList.map((bankConnection) => (
+                      <Col span={8}>
+                        <BankConnectionCard
+                          type={String(bankConnection['type']).toUpperCase()}
+                          name={bankConnection['name']}
+                          updateBankAccountsTab={() => fetchBankAccounts()}
+                        />
+                      </Col>
+                    ))
+                  : null}
+              </Row>
+            </Panel>
+            <Panel header="Bank accounts" key="2">
+              {bankAccountsContent}
+            </Panel>
+          </Collapse>
         </TabPane>
-        <TabPane tab="Recipient accounts" key="3">
+        <TabPane tab="Recipient accounts" key="2">
           Placeholder
         </TabPane>
       </Tabs>

-- 
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]