Currency Conversion Plugin
On the basis of the prepared development environment, let's develop a currency conversion plugin. First, the user needs to insert a currency field and fill in some data.
- After the installation is completed, create a new ts file named
exchange-api.ts
in the src directory and copy the following content into it.
import axios from 'axios';
interface ExchangeRatesResponse {
rates: {
[key: string]: number;
};
base: string;
date: string;
}
export async function getExchangeRate(base: string, target: string): Promise<number | undefined> {
try {
const response = await axios.get<ExchangeRatesResponse>(`https://api.exchangerate-api.com/v4/latest/${base}`);
const rate = response.data.rates[target];
if (!rate) {
throw new Error(`Exchange rate not found for target currency: ${target}`);
}
return rate;
} catch (error) {
console.info(`Error fetching exchange rate: ${(error as any).message}`);
}
}
import axios from 'axios';
interface ExchangeRatesResponse {
rates: {
[key: string]: number;
};
base: string;
date: string;
}
export async function getExchangeRate(base: string, target: string): Promise<number | undefined> {
try {
const response = await axios.get<ExchangeRatesResponse>(`https://api.exchangerate-api.com/v4/latest/${base}`);
const rate = response.data.rates[target];
if (!rate) {
throw new Error(`Exchange rate not found for target currency: ${target}`);
}
return rate;
} catch (error) {
console.info(`Error fetching exchange rate: ${(error as any).message}`);
}
}
The logic of this part of the code is to obtain the real-time exchange rate. base
refers to the current currency type, and target
refers to the currency type to be converted. This API can obtain the exchange rate with two decimal places.
- Create a new ts file named
const.ts
in the src directory, and copy the following content into it.
import { CurrencyCode } from '@lark-base-open/js-sdk';
export const CURRENCY = [
{ label: 'CNY', value: CurrencyCode.CNY },
{ label: 'USD', value: CurrencyCode.USD },
{ label: 'EUR', value: CurrencyCode.EUR },
{ label: 'AED', value: CurrencyCode.AED },
{ label: 'BRL', value: CurrencyCode.BRL },
{ label: 'CAD', value: CurrencyCode.CAD },
{ label: 'CHF', value: CurrencyCode.CHF },
{ label: 'HKD', value: CurrencyCode.HKD },
{ label: 'INR', value: CurrencyCode.INR },
{ label: 'JPY', value: CurrencyCode.JPY },
{ label: 'MXN', value: CurrencyCode.MXN },
];
import { CurrencyCode } from '@lark-base-open/js-sdk';
export const CURRENCY = [
{ label: 'CNY', value: CurrencyCode.CNY },
{ label: 'USD', value: CurrencyCode.USD },
{ label: 'EUR', value: CurrencyCode.EUR },
{ label: 'AED', value: CurrencyCode.AED },
{ label: 'BRL', value: CurrencyCode.BRL },
{ label: 'CAD', value: CurrencyCode.CAD },
{ label: 'CHF', value: CurrencyCode.CHF },
{ label: 'HKD', value: CurrencyCode.HKD },
{ label: 'INR', value: CurrencyCode.INR },
{ label: 'JPY', value: CurrencyCode.JPY },
{ label: 'MXN', value: CurrencyCode.MXN },
];
This file enumerates the currency types that can be converted. Since this is just a demo, the number of enumerations is limited.
- Provide the user with the ability to select the currency field for conversion.
First, currency conversion is performed on the original field for currency value conversion, so we need to filter the currency type fields in the current table
to allow users to select them. Here, we use the Select
component to implement this action. Each option is a currency field that can be selected in the current table
.
We modify the LoadApp
function in index.tsx
:
Define the currencyFieldMetaList
to store the currency field information, as well as the selectFieldId
to store the selected field ID for conversion and the currency
to store the selected currency type for conversion.
import { bitable, CurrencyCode, FieldType, ICurrencyField, ICurrencyFieldMeta } from '@lark-base-open/js-sdk';
import { CURRENCY } from './const';
function LoadApp() {
const [info, setInfo] = useState('get table name, please waiting ....');
const [infoType, setInfoType] = useState('info');
const [currencyFieldMetaList, setMetaList] = useState<ICurrencyFieldMeta[]>([])
const [selectFieldId, setSelectFieldId] = useState<string>();
const [currency, setCurrency] = useState<CurrencyCode>();
import { bitable, CurrencyCode, FieldType, ICurrencyField, ICurrencyFieldMeta } from '@lark-base-open/js-sdk';
import { CURRENCY } from './const';
function LoadApp() {
const [info, setInfo] = useState('get table name, please waiting ....');
const [infoType, setInfoType] = useState('info');
const [currencyFieldMetaList, setMetaList] = useState<ICurrencyFieldMeta[]>([])
const [selectFieldId, setSelectFieldId] = useState<string>();
const [currency, setCurrency] = useState<CurrencyCode>();
Modify the useEffect
function to get the currency field information in the current table
when the page is rendered.
useEffect(() => {
const fn = async () => {
const table = await bitable.base.getActiveTable();
const tableName = await table.getName();
setInfo(`The table Name is ${tableName}`);
setInfoType('success');
const fieldMetaList = await table.getFieldMetaListByType<ICurrencyFieldMeta>(FieldType.Currency);
setMetaList(fieldMetaList);
};
fn();
}, []);
useEffect(() => {
const fn = async () => {
const table = await bitable.base.getActiveTable();
const tableName = await table.getName();
setInfo(`The table Name is ${tableName}`);
setInfoType('success');
const fieldMetaList = await table.getFieldMetaListByType<ICurrencyFieldMeta>(FieldType.Currency);
setMetaList(fieldMetaList);
};
fn();
}, []);
In order, I will explain the relevant APIs used here:
bitable.base.getActiveTable
: Get the currenttable
. After obtaining thetable
, you can perform operations on the data.table.getFieldMetaListByType<ICurrencyFieldMeta>(FieldType.Currency)
: Get the corresponding field information by field type.
Then we modify the rendered component to meet the user's interactive requirements.
const formatFieldMetaList = (metaList: ICurrencyFieldMeta[]) => {
return metaList.map(meta => ({ label: meta.name, value: meta.id }));
};
return <div>
<Alert message={info} type={alertType}/>
<div style={{ margin: 10 }}>
<div>Select Field</div>
<Select style={{ width: 120 }} onSelect={setSelectFieldId} options={formatFieldMetaList(currencyFieldMetaList)}/>
</div>
<div style={{ margin: 10 }}>
<div>Select Currency</div>
<Select options={CURRENCY} style={{ width: 120 }} onSelect={setCurrency}/>
</div>
</div>
const formatFieldMetaList = (metaList: ICurrencyFieldMeta[]) => {
return metaList.map(meta => ({ label: meta.name, value: meta.id }));
};
return <div>
<Alert message={info} type={alertType}/>
<div style={{ margin: 10 }}>
<div>Select Field</div>
<Select style={{ width: 120 }} onSelect={setSelectFieldId} options={formatFieldMetaList(currencyFieldMetaList)}/>
</div>
<div style={{ margin: 10 }}>
<div>Select Currency</div>
<Select options={CURRENCY} style={{ width: 120 }} onSelect={setCurrency}/>
</div>
</div>
Now, users can select the field and the currency type they want to convert. Next, we will implement the logic to convert the currency value.
- Implementing Currency Conversion Logic
First, we import the API for getting exchange rates:
import { CURRENCY } from './const';
import { getExchangeRate } from './exchange-api';
import { CURRENCY } from './const';
import { getExchangeRate } from './exchange-api';
Next, we prepare a conversion button and the conversion function:
const transform = async () => {
}
return <div>
<div style={{ margin: 10 }}>
<div>Select Field</div>
<Select style={{ width: 120 }} onSelect={setSelectFieldId} options={formatFieldMetaList(currencyFieldMetaList)}/>
</div>
<div style={{ margin: 10 }}>
<div>Select Currency</div>
<Select options={CURRENCY} style={{ width: 120 }} onSelect={setCurrency}/>
<Button style={{ marginLeft: 10 }} onClick={transform}>transform</Button>
</div>
const transform = async () => {
}
return <div>
<div style={{ margin: 10 }}>
<div>Select Field</div>
<Select style={{ width: 120 }} onSelect={setSelectFieldId} options={formatFieldMetaList(currencyFieldMetaList)}/>
</div>
<div style={{ margin: 10 }}>
<div>Select Currency</div>
<Select options={CURRENCY} style={{ width: 120 }} onSelect={setCurrency}/>
<Button style={{ marginLeft: 10 }} onClick={transform}>transform</Button>
</div>
Next, we implement the most important step: the currency conversion and value conversion for currency fields in the transform
function:
const transform = async () => {
// If the user hasn't selected a currency or conversion field, do nothing
if (!selectFieldId || !currency) return;
const table = await bitable.base.getActiveTable();
// Get the currency field, using the ICurrencyField interface to indicate that we are getting a currency field
// With TypeScript, we get a lot of type hints to help with development when we restrict the type of the field
const currencyField = await table.getField<ICurrencyField>(selectFieldId);
const currentCurrency = await currencyField.getCurrencyCode();
// Set the currency code
await currencyField.setCurrencyCode(currency);
// Get the exchange rate for the currency
const ratio = await getExchangeRate(currentCurrency, currency);
if (!ratio) return;
// First, get the recordId
const recordIdList = await table.getRecordIdList();
// Iterate through the records
for (const recordId of recordIdList) {
// Get the current currency value
const currentVal = await currencyField.getValue(recordId);
// Calculate the new value using the exchange rate
await currencyField.setValue(recordId, currentVal * ratio);
}
}
const transform = async () => {
// If the user hasn't selected a currency or conversion field, do nothing
if (!selectFieldId || !currency) return;
const table = await bitable.base.getActiveTable();
// Get the currency field, using the ICurrencyField interface to indicate that we are getting a currency field
// With TypeScript, we get a lot of type hints to help with development when we restrict the type of the field
const currencyField = await table.getField<ICurrencyField>(selectFieldId);
const currentCurrency = await currencyField.getCurrencyCode();
// Set the currency code
await currencyField.setCurrencyCode(currency);
// Get the exchange rate for the currency
const ratio = await getExchangeRate(currentCurrency, currency);
if (!ratio) return;
// First, get the recordId
const recordIdList = await table.getRecordIdList();
// Iterate through the records
for (const recordId of recordIdList) {
// Get the current currency value
const currentVal = await currencyField.getValue(recordId);
// Calculate the new value using the exchange rate
await currencyField.setValue(recordId, currentVal * ratio);
}
}
In the above example, we pass in a restriction on the field type when getting the field, which provides us with enough type hints in the subsequent logic. This step is crucial, and we highly recommend developers to use a similar approach when getting fields to improve development experience.
When changing the currency type, we can use CurrencyField.setCurrencyCode
to change the corresponding currency type. This is made possible by providing the type when getting the field (on this basis, it is also possible to make similar modifications to the options of single-select/multi-select fields).
When setting the currency value, we use CurrencyField.getValue
to get the corresponding data, perform the calculation, and then use CurrencyField.setValue
to fill in the result. We highly recommend developers to start with fields when performing CRUD operations on values. We have fine-tuned many field types to optimize the developer experience (for example, for attachment fields, you can directly pass in a file when calling setValue
to set the corresponding value).