@uplab/vendure-plugin-exchange-rates 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +369 -0
- package/dist/api/admin.resolver.d.ts +16 -0
- package/dist/api/admin.resolver.d.ts.map +1 -0
- package/dist/api/admin.resolver.js +66 -0
- package/dist/api/admin.resolver.js.map +1 -0
- package/dist/api/api-extensions.d.ts +6 -0
- package/dist/api/api-extensions.d.ts.map +1 -0
- package/dist/api/api-extensions.js +57 -0
- package/dist/api/api-extensions.js.map +1 -0
- package/dist/api/shop.resolver.d.ts +15 -0
- package/dist/api/shop.resolver.d.ts.map +1 -0
- package/dist/api/shop.resolver.js +63 -0
- package/dist/api/shop.resolver.js.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +13 -0
- package/dist/constants.js.map +1 -0
- package/dist/dashboard/format-rate.ts +8 -0
- package/dist/dashboard/graphql.ts +74 -0
- package/dist/dashboard/i18n/en.po +109 -0
- package/dist/dashboard/i18n/uk.po +109 -0
- package/dist/dashboard/index.tsx +7 -0
- package/dist/dashboard/routes/currency-exchange-rate-detail.tsx +194 -0
- package/dist/dashboard/routes/currency-exchange-rate-list.tsx +105 -0
- package/dist/derive-rates.d.ts +13 -0
- package/dist/derive-rates.d.ts.map +1 -0
- package/dist/derive-rates.js +40 -0
- package/dist/derive-rates.js.map +1 -0
- package/dist/effective-rate.d.ts +8 -0
- package/dist/effective-rate.d.ts.map +1 -0
- package/dist/effective-rate.js +13 -0
- package/dist/effective-rate.js.map +1 -0
- package/dist/entities/currency-exchange-rate.entity.d.ts +13 -0
- package/dist/entities/currency-exchange-rate.entity.d.ts.map +1 -0
- package/dist/entities/currency-exchange-rate.entity.js +55 -0
- package/dist/entities/currency-exchange-rate.entity.js.map +1 -0
- package/dist/events/currency-exchange-rate.event.d.ts +18 -0
- package/dist/events/currency-exchange-rate.event.d.ts.map +1 -0
- package/dist/events/currency-exchange-rate.event.js +18 -0
- package/dist/events/currency-exchange-rate.event.js.map +1 -0
- package/dist/exchange-rate-source-error.d.ts +16 -0
- package/dist/exchange-rate-source-error.d.ts.map +1 -0
- package/dist/exchange-rate-source-error.js +18 -0
- package/dist/exchange-rate-source-error.js.map +1 -0
- package/dist/exchange-rate-sync.task.d.ts +3 -0
- package/dist/exchange-rate-sync.task.d.ts.map +1 -0
- package/dist/exchange-rate-sync.task.js +18 -0
- package/dist/exchange-rate-sync.task.js.map +1 -0
- package/dist/exchange-rates.plugin.d.ts +24 -0
- package/dist/exchange-rates.plugin.d.ts.map +1 -0
- package/dist/exchange-rates.plugin.js +101 -0
- package/dist/exchange-rates.plugin.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/services/currency-exchange-rate-sync.service.d.ts +30 -0
- package/dist/services/currency-exchange-rate-sync.service.d.ts.map +1 -0
- package/dist/services/currency-exchange-rate-sync.service.js +145 -0
- package/dist/services/currency-exchange-rate-sync.service.js.map +1 -0
- package/dist/services/currency-exchange-rate.service.d.ts +30 -0
- package/dist/services/currency-exchange-rate.service.d.ts.map +1 -0
- package/dist/services/currency-exchange-rate.service.js +93 -0
- package/dist/services/currency-exchange-rate.service.js.map +1 -0
- package/dist/sources/ecb-exchange-rate-source.d.ts +20 -0
- package/dist/sources/ecb-exchange-rate-source.d.ts.map +1 -0
- package/dist/sources/ecb-exchange-rate-source.js +33 -0
- package/dist/sources/ecb-exchange-rate-source.js.map +1 -0
- package/dist/sources/fetch-body.d.ts +9 -0
- package/dist/sources/fetch-body.d.ts.map +1 -0
- package/dist/sources/fetch-body.js +59 -0
- package/dist/sources/fetch-body.js.map +1 -0
- package/dist/sources/frankfurter-exchange-rate-source.d.ts +23 -0
- package/dist/sources/frankfurter-exchange-rate-source.d.ts.map +1 -0
- package/dist/sources/frankfurter-exchange-rate-source.js +35 -0
- package/dist/sources/frankfurter-exchange-rate-source.js.map +1 -0
- package/dist/sources/monobank-exchange-rate-source.d.ts +29 -0
- package/dist/sources/monobank-exchange-rate-source.d.ts.map +1 -0
- package/dist/sources/monobank-exchange-rate-source.js +46 -0
- package/dist/sources/monobank-exchange-rate-source.js.map +1 -0
- package/dist/sources/nbu-exchange-rate-source.d.ts +20 -0
- package/dist/sources/nbu-exchange-rate-source.d.ts.map +1 -0
- package/dist/sources/nbu-exchange-rate-source.js +25 -0
- package/dist/sources/nbu-exchange-rate-source.js.map +1 -0
- package/dist/sources/static-exchange-rate-source.d.ts +20 -0
- package/dist/sources/static-exchange-rate-source.d.ts.map +1 -0
- package/dist/sources/static-exchange-rate-source.js +23 -0
- package/dist/sources/static-exchange-rate-source.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_RATE = exports.DEFAULT_SOURCE_TIMEOUT = exports.SYNC_TASK_ID = exports.DEFAULT_SYNC_SCHEDULE = exports.loggerCtx = exports.EXCHANGE_RATES_PLUGIN_OPTIONS = void 0;
|
|
4
|
+
exports.EXCHANGE_RATES_PLUGIN_OPTIONS = Symbol('EXCHANGE_RATES_PLUGIN_OPTIONS');
|
|
5
|
+
exports.loggerCtx = 'ExchangeRatesPlugin';
|
|
6
|
+
/** Every 3 hours at minute 40. */
|
|
7
|
+
exports.DEFAULT_SYNC_SCHEDULE = '40 2-23/3 * * *';
|
|
8
|
+
exports.SYNC_TASK_ID = 'currency-exchange-rate-updater';
|
|
9
|
+
/** How long a source request may take before it is aborted, in milliseconds. */
|
|
10
|
+
exports.DEFAULT_SOURCE_TIMEOUT = 10_000;
|
|
11
|
+
/** decimal(19, 8) holds 11 integer digits. */
|
|
12
|
+
exports.MAX_RATE = 1e11;
|
|
13
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,6BAA6B,GAAG,MAAM,CAAC,+BAA+B,CAAC,CAAC;AACxE,QAAA,SAAS,GAAG,qBAAqB,CAAC;AAE/C,kCAAkC;AACrB,QAAA,qBAAqB,GAAG,iBAAiB,CAAC;AAE1C,QAAA,YAAY,GAAG,gCAAgC,CAAC;AAE7D,gFAAgF;AACnE,QAAA,sBAAsB,GAAG,MAAM,CAAC;AAE7C,8CAA8C;AACjC,QAAA,QAAQ,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Rates span many magnitudes (JPY in USD is 0.0063…), so a fixed number of decimals would hide them. */
|
|
2
|
+
const listFormat: Intl.NumberFormatOptions = { maximumSignificantDigits: 6 };
|
|
3
|
+
/** Every decimal a rate is stored with. */
|
|
4
|
+
const fullFormat: Intl.NumberFormatOptions = { maximumFractionDigits: 8 };
|
|
5
|
+
|
|
6
|
+
export function formatRate(locale: string, value: number, precision: 'list' | 'full' = 'list'): string {
|
|
7
|
+
return new Intl.NumberFormat(locale, precision === 'full' ? fullFormat : listFormat).format(value);
|
|
8
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { graphql } from '@vendure/dashboard';
|
|
2
|
+
import type { DocumentNode } from 'graphql';
|
|
3
|
+
|
|
4
|
+
// The dashboard's `graphql()` is typed against core's schema only, so these documents carry
|
|
5
|
+
// hand-written result types instead. The shape matches `TypedDocumentNode`, which the pages expect.
|
|
6
|
+
type TypedDocument<Result, Variables> = DocumentNode & { __apiType?: (variables: Variables) => Result };
|
|
7
|
+
|
|
8
|
+
const typedDocument = <Result, Variables>(source: string) =>
|
|
9
|
+
(graphql as (source: string) => DocumentNode)(source) as TypedDocument<Result, Variables>;
|
|
10
|
+
|
|
11
|
+
export interface CurrencyExchangeRateFields {
|
|
12
|
+
id: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
code: string;
|
|
16
|
+
baseCurrency: string;
|
|
17
|
+
rate: number;
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
useCustomRate: boolean;
|
|
20
|
+
customRate: number | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const FIELDS = `
|
|
24
|
+
fragment CurrencyExchangeRateFields on CurrencyExchangeRate {
|
|
25
|
+
id
|
|
26
|
+
createdAt
|
|
27
|
+
updatedAt
|
|
28
|
+
code
|
|
29
|
+
baseCurrency
|
|
30
|
+
rate
|
|
31
|
+
enabled
|
|
32
|
+
useCustomRate
|
|
33
|
+
customRate
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export const getCurrencyExchangeRateListDocument = typedDocument<
|
|
38
|
+
{ currencyExchangeRates: { items: CurrencyExchangeRateFields[]; totalItems: number } },
|
|
39
|
+
{ options?: Record<string, unknown> }
|
|
40
|
+
>(`
|
|
41
|
+
query GetCurrencyExchangeRateList($options: CurrencyExchangeRateListOptions) {
|
|
42
|
+
currencyExchangeRates(options: $options) {
|
|
43
|
+
items {
|
|
44
|
+
...CurrencyExchangeRateFields
|
|
45
|
+
}
|
|
46
|
+
totalItems
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
${FIELDS}
|
|
50
|
+
`);
|
|
51
|
+
|
|
52
|
+
export const getCurrencyExchangeRateDetailDocument = typedDocument<
|
|
53
|
+
{ currencyExchangeRate: CurrencyExchangeRateFields | null },
|
|
54
|
+
{ id: string }
|
|
55
|
+
>(`
|
|
56
|
+
query GetCurrencyExchangeRateDetail($id: ID!) {
|
|
57
|
+
currencyExchangeRate(id: $id) {
|
|
58
|
+
...CurrencyExchangeRateFields
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
${FIELDS}
|
|
62
|
+
`);
|
|
63
|
+
|
|
64
|
+
export const updateCurrencyExchangeRateDocument = typedDocument<
|
|
65
|
+
{ updateCurrencyExchangeRate: CurrencyExchangeRateFields },
|
|
66
|
+
{ input: { id: string; enabled: boolean; useCustomRate: boolean; customRate?: number | null } }
|
|
67
|
+
>(`
|
|
68
|
+
mutation UpdateCurrencyExchangeRate($input: UpdateCurrencyExchangeRateInput!) {
|
|
69
|
+
updateCurrencyExchangeRate(input: $input) {
|
|
70
|
+
...CurrencyExchangeRateFields
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
${FIELDS}
|
|
74
|
+
`);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
msgid ""
|
|
2
|
+
msgstr ""
|
|
3
|
+
"POT-Creation-Date: 2025-11-24 15:07+0200\n"
|
|
4
|
+
"MIME-Version: 1.0\n"
|
|
5
|
+
"Content-Type: text/plain; charset=utf-8\n"
|
|
6
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
7
|
+
"X-Generator: @lingui/cli\n"
|
|
8
|
+
"Language: en\n"
|
|
9
|
+
"Project-Id-Version: \n"
|
|
10
|
+
"Report-Msgid-Bugs-To: \n"
|
|
11
|
+
"PO-Revision-Date: \n"
|
|
12
|
+
"Last-Translator: \n"
|
|
13
|
+
"Language-Team: \n"
|
|
14
|
+
"Plural-Forms: \n"
|
|
15
|
+
|
|
16
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
17
|
+
msgid "Code"
|
|
18
|
+
msgstr "Code"
|
|
19
|
+
|
|
20
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
21
|
+
msgid "Currency exchange rate"
|
|
22
|
+
msgstr "Currency exchange rate"
|
|
23
|
+
|
|
24
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
25
|
+
msgid "Currency exchange rate updated"
|
|
26
|
+
msgstr "Currency exchange rate updated"
|
|
27
|
+
|
|
28
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
29
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
30
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
31
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
32
|
+
msgid "Currency exchange rates"
|
|
33
|
+
msgstr "Currency exchange rates"
|
|
34
|
+
|
|
35
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
36
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
37
|
+
msgid "Custom rate"
|
|
38
|
+
msgstr "Custom rate"
|
|
39
|
+
|
|
40
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
41
|
+
msgid "Details"
|
|
42
|
+
msgstr "Details"
|
|
43
|
+
|
|
44
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
45
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
46
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
47
|
+
msgid "Disabled"
|
|
48
|
+
msgstr "Disabled"
|
|
49
|
+
|
|
50
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
51
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
52
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
53
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
54
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
55
|
+
msgid "Enabled"
|
|
56
|
+
msgstr "Enabled"
|
|
57
|
+
|
|
58
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
59
|
+
msgid "Enter a custom rate greater than 0"
|
|
60
|
+
msgstr "Enter a custom rate greater than 0"
|
|
61
|
+
|
|
62
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
63
|
+
msgid "Failed to update currency exchange rate"
|
|
64
|
+
msgstr "Failed to update currency exchange rate"
|
|
65
|
+
|
|
66
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
67
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
68
|
+
msgid "Fetched rate"
|
|
69
|
+
msgstr "Fetched rate"
|
|
70
|
+
|
|
71
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
72
|
+
msgid "Last updated"
|
|
73
|
+
msgstr "Last updated"
|
|
74
|
+
|
|
75
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
76
|
+
msgid "Reference information for this currency"
|
|
77
|
+
msgstr "Reference information for this currency"
|
|
78
|
+
|
|
79
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
80
|
+
msgid "Status"
|
|
81
|
+
msgstr "Status"
|
|
82
|
+
|
|
83
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
84
|
+
msgid "Update"
|
|
85
|
+
msgstr "Update"
|
|
86
|
+
|
|
87
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
88
|
+
msgid "Use custom rate"
|
|
89
|
+
msgstr "Use custom rate"
|
|
90
|
+
|
|
91
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
92
|
+
msgid "Using custom rate"
|
|
93
|
+
msgstr "Using custom rate"
|
|
94
|
+
|
|
95
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
96
|
+
msgid "Base currency"
|
|
97
|
+
msgstr "Base currency"
|
|
98
|
+
|
|
99
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
100
|
+
msgid "{0} per 1 {1}"
|
|
101
|
+
msgstr "{0} per 1 {1}"
|
|
102
|
+
|
|
103
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
104
|
+
msgid "In the same units, used instead of the fetched rate while “Use custom rate” is on"
|
|
105
|
+
msgstr "In the same units, used instead of the fetched rate while “Use custom rate” is on"
|
|
106
|
+
|
|
107
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
108
|
+
msgid "Unknown error"
|
|
109
|
+
msgstr "Unknown error"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
msgid ""
|
|
2
|
+
msgstr ""
|
|
3
|
+
"POT-Creation-Date: 2025-11-24 15:07+0200\n"
|
|
4
|
+
"MIME-Version: 1.0\n"
|
|
5
|
+
"Content-Type: text/plain; charset=utf-8\n"
|
|
6
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
7
|
+
"X-Generator: @lingui/cli\n"
|
|
8
|
+
"Language: uk\n"
|
|
9
|
+
"Project-Id-Version: \n"
|
|
10
|
+
"Report-Msgid-Bugs-To: \n"
|
|
11
|
+
"PO-Revision-Date: \n"
|
|
12
|
+
"Last-Translator: \n"
|
|
13
|
+
"Language-Team: \n"
|
|
14
|
+
"Plural-Forms: \n"
|
|
15
|
+
|
|
16
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
17
|
+
msgid "Code"
|
|
18
|
+
msgstr "Код"
|
|
19
|
+
|
|
20
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
21
|
+
msgid "Currency exchange rate"
|
|
22
|
+
msgstr "Курс обміну валюти"
|
|
23
|
+
|
|
24
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
25
|
+
msgid "Currency exchange rate updated"
|
|
26
|
+
msgstr "Курс обміну валюти оновлено"
|
|
27
|
+
|
|
28
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
29
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
30
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
31
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
32
|
+
msgid "Currency exchange rates"
|
|
33
|
+
msgstr "Курси обміну валют"
|
|
34
|
+
|
|
35
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
36
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
37
|
+
msgid "Custom rate"
|
|
38
|
+
msgstr "Власний курс"
|
|
39
|
+
|
|
40
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
41
|
+
msgid "Details"
|
|
42
|
+
msgstr "Деталі"
|
|
43
|
+
|
|
44
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
45
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
46
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
47
|
+
msgid "Disabled"
|
|
48
|
+
msgstr "Вимкнено"
|
|
49
|
+
|
|
50
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
51
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
52
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
53
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
54
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
55
|
+
msgid "Enabled"
|
|
56
|
+
msgstr "Увімкнено"
|
|
57
|
+
|
|
58
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
59
|
+
msgid "Enter a custom rate greater than 0"
|
|
60
|
+
msgstr "Введіть власний курс, більший за 0"
|
|
61
|
+
|
|
62
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
63
|
+
msgid "Failed to update currency exchange rate"
|
|
64
|
+
msgstr "Не вдалося оновити курс обміну валюти"
|
|
65
|
+
|
|
66
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
67
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
68
|
+
msgid "Fetched rate"
|
|
69
|
+
msgstr "Курс із джерела"
|
|
70
|
+
|
|
71
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
72
|
+
msgid "Last updated"
|
|
73
|
+
msgstr "Останнє оновлення"
|
|
74
|
+
|
|
75
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
76
|
+
msgid "Reference information for this currency"
|
|
77
|
+
msgstr "Довідкова інформація для цієї валюти"
|
|
78
|
+
|
|
79
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
80
|
+
msgid "Status"
|
|
81
|
+
msgstr "Статус"
|
|
82
|
+
|
|
83
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
84
|
+
msgid "Update"
|
|
85
|
+
msgstr "Оновити"
|
|
86
|
+
|
|
87
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
88
|
+
msgid "Use custom rate"
|
|
89
|
+
msgstr "Використовувати власний курс"
|
|
90
|
+
|
|
91
|
+
#: src/dashboard/routes/currency-exchange-rate-list.tsx
|
|
92
|
+
msgid "Using custom rate"
|
|
93
|
+
msgstr "Використовується власний курс"
|
|
94
|
+
|
|
95
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
96
|
+
msgid "Base currency"
|
|
97
|
+
msgstr "Базова валюта"
|
|
98
|
+
|
|
99
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
100
|
+
msgid "{0} per 1 {1}"
|
|
101
|
+
msgstr "{0} за 1 {1}"
|
|
102
|
+
|
|
103
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
104
|
+
msgid "In the same units, used instead of the fetched rate while “Use custom rate” is on"
|
|
105
|
+
msgstr "У тих самих одиницях; застосовується замість курсу із джерела, поки ввімкнено «Використовувати власний курс»"
|
|
106
|
+
|
|
107
|
+
#: src/dashboard/routes/currency-exchange-rate-detail.tsx
|
|
108
|
+
msgid "Unknown error"
|
|
109
|
+
msgstr "Невідома помилка"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineDashboardExtension } from '@vendure/dashboard';
|
|
2
|
+
import { currencyExchangeRateDetailRoute } from './routes/currency-exchange-rate-detail';
|
|
3
|
+
import { currencyExchangeRateListRoute } from './routes/currency-exchange-rate-list';
|
|
4
|
+
|
|
5
|
+
defineDashboardExtension({
|
|
6
|
+
routes: [currencyExchangeRateListRoute, currencyExchangeRateDetailRoute],
|
|
7
|
+
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { Trans, useLingui } from '@lingui/react/macro';
|
|
2
|
+
import {
|
|
3
|
+
Badge,
|
|
4
|
+
Button,
|
|
5
|
+
DashboardRouteDefinition,
|
|
6
|
+
DetailFormGrid,
|
|
7
|
+
FormFieldWrapper,
|
|
8
|
+
Input,
|
|
9
|
+
Page,
|
|
10
|
+
PageActionBar,
|
|
11
|
+
PageActionBarRight,
|
|
12
|
+
PageBlock,
|
|
13
|
+
PageLayout,
|
|
14
|
+
PageTitle,
|
|
15
|
+
PermissionGuard,
|
|
16
|
+
Switch,
|
|
17
|
+
detailPageRouteLoader,
|
|
18
|
+
toast,
|
|
19
|
+
useDetailPage,
|
|
20
|
+
useLocalFormat,
|
|
21
|
+
} from '@vendure/dashboard';
|
|
22
|
+
import { useMemo } from 'react';
|
|
23
|
+
import { formatRate } from '../format-rate';
|
|
24
|
+
import { getCurrencyExchangeRateDetailDocument, updateCurrencyExchangeRateDocument } from '../graphql';
|
|
25
|
+
|
|
26
|
+
const pageId = 'currency-exchange-rate-detail';
|
|
27
|
+
|
|
28
|
+
export const currencyExchangeRateDetailRoute: DashboardRouteDefinition = {
|
|
29
|
+
path: '/currency-exchange-rates/$id',
|
|
30
|
+
loader: detailPageRouteLoader({
|
|
31
|
+
pageId,
|
|
32
|
+
queryDocument: getCurrencyExchangeRateDetailDocument,
|
|
33
|
+
breadcrumb: (_isNew, entity) => [
|
|
34
|
+
{ path: '/currency-exchange-rates', label: <Trans>Currency exchange rates</Trans> },
|
|
35
|
+
entity?.code ?? '',
|
|
36
|
+
],
|
|
37
|
+
}),
|
|
38
|
+
component: (route) => <CurrencyExchangeRateDetailPage route={route} />,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type Route = Parameters<DashboardRouteDefinition['component']>[0];
|
|
42
|
+
|
|
43
|
+
function CurrencyExchangeRateDetailPage({ route }: { route: Route }) {
|
|
44
|
+
const { t, i18n } = useLingui();
|
|
45
|
+
const params = route.useParams();
|
|
46
|
+
const { formatDate } = useLocalFormat();
|
|
47
|
+
|
|
48
|
+
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
49
|
+
pageId,
|
|
50
|
+
entityName: 'CurrencyExchangeRate',
|
|
51
|
+
queryDocument: getCurrencyExchangeRateDetailDocument,
|
|
52
|
+
updateDocument: updateCurrencyExchangeRateDocument,
|
|
53
|
+
setValuesForUpdate: (rate) => ({
|
|
54
|
+
id: rate.id,
|
|
55
|
+
enabled: rate.enabled,
|
|
56
|
+
useCustomRate: rate.useCustomRate,
|
|
57
|
+
customRate: rate.customRate ?? null,
|
|
58
|
+
}),
|
|
59
|
+
params: { id: params.id },
|
|
60
|
+
onSuccess: () => {
|
|
61
|
+
toast.success(t`Currency exchange rate updated`);
|
|
62
|
+
resetForm();
|
|
63
|
+
},
|
|
64
|
+
onError: (error) => {
|
|
65
|
+
toast.error(t`Failed to update currency exchange rate`, {
|
|
66
|
+
description: error instanceof Error ? error.message : t`Unknown error`,
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const useCustomRate = form.watch('useCustomRate');
|
|
72
|
+
|
|
73
|
+
const customRateRules = useMemo(
|
|
74
|
+
() => ({
|
|
75
|
+
validate: (value: number | null | undefined) => {
|
|
76
|
+
if (!form.getValues('useCustomRate')) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
if (!value || value <= 0) {
|
|
80
|
+
return t`Enter a custom rate greater than 0`;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
[form, t],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
90
|
+
<PageTitle>{entity?.code ?? t`Currency exchange rate`}</PageTitle>
|
|
91
|
+
<PageActionBar>
|
|
92
|
+
<PageActionBarRight>
|
|
93
|
+
<PermissionGuard requires={['UpdateSettings']}>
|
|
94
|
+
<Button type="submit" disabled={!form.formState.isDirty || !form.formState.isValid || isPending}>
|
|
95
|
+
<Trans>Update</Trans>
|
|
96
|
+
</Button>
|
|
97
|
+
</PermissionGuard>
|
|
98
|
+
</PageActionBarRight>
|
|
99
|
+
</PageActionBar>
|
|
100
|
+
<PageLayout>
|
|
101
|
+
<PageBlock
|
|
102
|
+
column="side"
|
|
103
|
+
blockId="currency-exchange-rate-info"
|
|
104
|
+
title={<Trans>Details</Trans>}
|
|
105
|
+
description={<Trans>Reference information for this currency</Trans>}
|
|
106
|
+
>
|
|
107
|
+
<dl className="space-y-3 text-sm">
|
|
108
|
+
<div className="flex flex-col">
|
|
109
|
+
<dt className="text-muted-foreground">
|
|
110
|
+
<Trans>Code</Trans>
|
|
111
|
+
</dt>
|
|
112
|
+
<dd className="font-mono text-base">{entity?.code ?? '\u2014'}</dd>
|
|
113
|
+
</div>
|
|
114
|
+
<div className="flex flex-col">
|
|
115
|
+
<dt className="text-muted-foreground">
|
|
116
|
+
<Trans>Base currency</Trans>
|
|
117
|
+
</dt>
|
|
118
|
+
<dd className="font-mono text-base">{entity?.baseCurrency ?? '\u2014'}</dd>
|
|
119
|
+
</div>
|
|
120
|
+
<div className="flex flex-col">
|
|
121
|
+
<dt className="text-muted-foreground">
|
|
122
|
+
<Trans>Status</Trans>
|
|
123
|
+
</dt>
|
|
124
|
+
<dd>
|
|
125
|
+
<Badge variant={entity?.enabled ? 'success' : 'secondary'}>
|
|
126
|
+
{entity?.enabled ? t`Enabled` : t`Disabled`}
|
|
127
|
+
</Badge>
|
|
128
|
+
</dd>
|
|
129
|
+
</div>
|
|
130
|
+
<div className="flex flex-col">
|
|
131
|
+
<dt className="text-muted-foreground">
|
|
132
|
+
<Trans>Last updated</Trans>
|
|
133
|
+
</dt>
|
|
134
|
+
<dd>{entity?.updatedAt ? formatDate(entity.updatedAt) : '\u2014'}</dd>
|
|
135
|
+
</div>
|
|
136
|
+
</dl>
|
|
137
|
+
</PageBlock>
|
|
138
|
+
<PageBlock column="main" blockId="currency-exchange-rate-form">
|
|
139
|
+
<div className="space-y-6">
|
|
140
|
+
<div>
|
|
141
|
+
<p className="text-sm font-medium text-muted-foreground">
|
|
142
|
+
<Trans>Fetched rate</Trans>
|
|
143
|
+
</p>
|
|
144
|
+
<p className="text-2xl font-semibold">
|
|
145
|
+
{entity ? formatRate(i18n.locale, entity.rate, 'full') : '\u2014'}
|
|
146
|
+
</p>
|
|
147
|
+
{entity && (
|
|
148
|
+
<p className="text-sm text-muted-foreground">
|
|
149
|
+
<Trans>
|
|
150
|
+
{entity.baseCurrency} per 1 {entity.code}
|
|
151
|
+
</Trans>
|
|
152
|
+
</p>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
<DetailFormGrid>
|
|
156
|
+
<FormFieldWrapper
|
|
157
|
+
control={form.control}
|
|
158
|
+
name="enabled"
|
|
159
|
+
label={<Trans>Enabled</Trans>}
|
|
160
|
+
render={({ field }) => <Switch checked={!!field.value} onCheckedChange={field.onChange} />}
|
|
161
|
+
/>
|
|
162
|
+
<FormFieldWrapper
|
|
163
|
+
control={form.control}
|
|
164
|
+
name="useCustomRate"
|
|
165
|
+
label={<Trans>Use custom rate</Trans>}
|
|
166
|
+
rules={{ deps: ['customRate'] }}
|
|
167
|
+
render={({ field }) => <Switch checked={!!field.value} onCheckedChange={field.onChange} />}
|
|
168
|
+
/>
|
|
169
|
+
<FormFieldWrapper
|
|
170
|
+
control={form.control}
|
|
171
|
+
name="customRate"
|
|
172
|
+
label={<Trans>Custom rate</Trans>}
|
|
173
|
+
description={
|
|
174
|
+
<Trans>In the same units, used instead of the fetched rate while “Use custom rate” is on</Trans>
|
|
175
|
+
}
|
|
176
|
+
rules={customRateRules}
|
|
177
|
+
render={({ field }) => (
|
|
178
|
+
<Input
|
|
179
|
+
type="number"
|
|
180
|
+
min="0"
|
|
181
|
+
step="any"
|
|
182
|
+
value={field.value ?? ''}
|
|
183
|
+
onChange={(event) => field.onChange(event.target.value === '' ? null : Number(event.target.value))}
|
|
184
|
+
disabled={!useCustomRate}
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
/>
|
|
188
|
+
</DetailFormGrid>
|
|
189
|
+
</div>
|
|
190
|
+
</PageBlock>
|
|
191
|
+
</PageLayout>
|
|
192
|
+
</Page>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Trans, useLingui } from '@lingui/react/macro';
|
|
2
|
+
import { Badge, DashboardRouteDefinition, DetailPageButton, ListPage, useLocalFormat } from '@vendure/dashboard';
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { formatRate } from '../format-rate';
|
|
5
|
+
import { getCurrencyExchangeRateListDocument } from '../graphql';
|
|
6
|
+
|
|
7
|
+
const pageId = 'currency-exchange-rate-list';
|
|
8
|
+
|
|
9
|
+
export const currencyExchangeRateListRoute: DashboardRouteDefinition = {
|
|
10
|
+
path: '/currency-exchange-rates',
|
|
11
|
+
navMenuItem: {
|
|
12
|
+
sectionId: 'settings',
|
|
13
|
+
id: 'currency-exchange-rates',
|
|
14
|
+
title: 'Currency exchange rates',
|
|
15
|
+
requiresPermission: 'ReadSettings',
|
|
16
|
+
},
|
|
17
|
+
loader: () => ({
|
|
18
|
+
breadcrumb: () => <Trans>Currency exchange rates</Trans>,
|
|
19
|
+
title: () => <Trans>Currency exchange rates</Trans>,
|
|
20
|
+
}),
|
|
21
|
+
component: (route) => <CurrencyExchangeRateListPage route={route} />,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function CurrencyExchangeRateListPage({ route }: { route: Parameters<DashboardRouteDefinition['component']>[0] }) {
|
|
25
|
+
const { t, i18n } = useLingui();
|
|
26
|
+
const { formatDate } = useLocalFormat();
|
|
27
|
+
|
|
28
|
+
const booleanOptions = useMemo(
|
|
29
|
+
() => [
|
|
30
|
+
{ value: true, label: t`Enabled` },
|
|
31
|
+
{ value: false, label: t`Disabled` },
|
|
32
|
+
],
|
|
33
|
+
[t],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<ListPage
|
|
38
|
+
pageId={pageId}
|
|
39
|
+
title={<Trans>Currency exchange rates</Trans>}
|
|
40
|
+
listQuery={getCurrencyExchangeRateListDocument}
|
|
41
|
+
route={route}
|
|
42
|
+
defaultSort={[{ id: 'code', desc: false }]}
|
|
43
|
+
defaultVisibility={{
|
|
44
|
+
code: true,
|
|
45
|
+
rate: true,
|
|
46
|
+
enabled: true,
|
|
47
|
+
useCustomRate: true,
|
|
48
|
+
// Shown inside the rate cell.
|
|
49
|
+
baseCurrency: false,
|
|
50
|
+
customRate: false,
|
|
51
|
+
// The dashboard pins `updatedAt` to the front; the detail page shows it instead.
|
|
52
|
+
updatedAt: false,
|
|
53
|
+
}}
|
|
54
|
+
defaultColumnOrder={['code', 'rate', 'enabled', 'useCustomRate']}
|
|
55
|
+
onSearchTermChange={(term) => (term ? { code: { contains: term } } : {})}
|
|
56
|
+
facetedFilters={{
|
|
57
|
+
enabled: {
|
|
58
|
+
title: t`Enabled`,
|
|
59
|
+
options: booleanOptions,
|
|
60
|
+
},
|
|
61
|
+
useCustomRate: {
|
|
62
|
+
title: t`Custom rate`,
|
|
63
|
+
options: booleanOptions,
|
|
64
|
+
},
|
|
65
|
+
}}
|
|
66
|
+
customizeColumns={{
|
|
67
|
+
code: {
|
|
68
|
+
cell: ({ row }) => <DetailPageButton id={row.original.id} label={row.original.code} />,
|
|
69
|
+
},
|
|
70
|
+
rate: {
|
|
71
|
+
meta: { dependencies: ['customRate', 'useCustomRate', 'baseCurrency'] },
|
|
72
|
+
cell: ({ row }) => {
|
|
73
|
+
const value = row.original.useCustomRate
|
|
74
|
+
? (row.original.customRate ?? row.original.rate)
|
|
75
|
+
: row.original.rate;
|
|
76
|
+
return (
|
|
77
|
+
<span>
|
|
78
|
+
{formatRate(i18n.locale, value ?? 0)}{' '}
|
|
79
|
+
<span className="text-muted-foreground">{row.original.baseCurrency}</span>
|
|
80
|
+
</span>
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
enabled: {
|
|
85
|
+
cell: ({ row }) => (
|
|
86
|
+
<Badge variant={row.original.enabled ? 'success' : 'secondary'}>
|
|
87
|
+
{row.original.enabled ? t`Enabled` : t`Disabled`}
|
|
88
|
+
</Badge>
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
useCustomRate: {
|
|
92
|
+
header: () => <Trans>Custom rate</Trans>,
|
|
93
|
+
cell: ({ row }) => (
|
|
94
|
+
<Badge variant={row.original.useCustomRate ? 'success' : 'secondary'}>
|
|
95
|
+
{row.original.useCustomRate ? t`Using custom rate` : t`Fetched rate`}
|
|
96
|
+
</Badge>
|
|
97
|
+
),
|
|
98
|
+
},
|
|
99
|
+
updatedAt: {
|
|
100
|
+
cell: ({ row }) => formatDate(row.original.updatedAt),
|
|
101
|
+
},
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CurrencyCode } from '@vendure/core';
|
|
2
|
+
import type { ExchangeRateQuote } from './types';
|
|
3
|
+
/** The 8 decimals a rate is stored with. */
|
|
4
|
+
export declare const round8: (value: number) => number;
|
|
5
|
+
/**
|
|
6
|
+
* @description
|
|
7
|
+
* Re-bases `quotes` (units of `sourceBase` per unit) onto `shopBase`. With a different base the cross
|
|
8
|
+
* rate is `rate(c) / rate(shopBase)`, so the source must quote the shop's base currency; it throws when
|
|
9
|
+
* it does not. A quote for the shop's base itself is dropped, and so is a result that rounds to 0 or does
|
|
10
|
+
* not fit decimal(19, 8).
|
|
11
|
+
*/
|
|
12
|
+
export declare function deriveRates(source: string, shopBase: CurrencyCode, sourceBase: CurrencyCode, quotes: ExchangeRateQuote[]): ExchangeRateQuote[];
|
|
13
|
+
//# sourceMappingURL=derive-rates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-rates.d.ts","sourceRoot":"","sources":["../src/derive-rates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,4CAA4C;AAC5C,eAAO,MAAM,MAAM,GAAI,OAAO,MAAM,KAAG,MAAuC,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,YAAY,EACxB,MAAM,EAAE,iBAAiB,EAAE,GAC1B,iBAAiB,EAAE,CAwBrB"}
|