diff options
402 files changed, 78542 insertions, 743 deletions
diff --git a/packages/demobank-ui/.gitignore b/packages/demobank-ui/.gitignore new file mode 100644 index 000000000..32d0a5057 --- /dev/null +++ b/packages/demobank-ui/.gitignore @@ -0,0 +1,5 @@ +node_modules +/build +/*.log +/size-plugin.json +/storybook-static/ diff --git a/packages/demobank-ui/.storybook/.babelrc b/packages/demobank-ui/.storybook/.babelrc new file mode 100644 index 000000000..610b6f339 --- /dev/null +++ b/packages/demobank-ui/.storybook/.babelrc @@ -0,0 +1,25 @@ +/* + 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) + */ +{ + "presets": [ + "preact-cli/babel" + ] +}
\ No newline at end of file diff --git a/packages/demobank-ui/.storybook/main.js b/packages/demobank-ui/.storybook/main.js new file mode 100644 index 000000000..f8e4bbcc7 --- /dev/null +++ b/packages/demobank-ui/.storybook/main.js @@ -0,0 +1,57 @@ +/* + 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) +*/ + + +module.exports = { + "stories": [ + "../src/**/*.stories.mdx", + "../src/**/*.stories.@(js|jsx|ts|tsx)" + ], + "addons": [ + "@storybook/preset-scss", + "@storybook/addon-a11y", + "@storybook/addon-essentials" //docs, control, actions, viewpot, toolbar, background + ], + // sb does not yet support new jsx transform by default + // https://github.com/storybookjs/storybook/issues/12881 + // https://github.com/storybookjs/storybook/issues/12952 + babel: async (options) => ({ + ...options, + presets: [ + ...options.presets, + [ + '@babel/preset-react', { + runtime: 'automatic', + }, + 'preset-react-jsx-transform' + ], + ], + }), + webpackFinal: (config) => { + // should be removed after storybook 6.3 + // https://github.com/storybookjs/storybook/issues/12853#issuecomment-821576113 + config.resolve.alias = { + react: "preact/compat", + "react-dom": "preact/compat", + }; + return config; + }, +}
\ No newline at end of file diff --git a/packages/demobank-ui/.storybook/preview.js b/packages/demobank-ui/.storybook/preview.js new file mode 100644 index 000000000..9ab4d9404 --- /dev/null +++ b/packages/demobank-ui/.storybook/preview.js @@ -0,0 +1,55 @@ +/* + 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/> + */ + +import "../src/scss/main.scss" +import { TranslationProvider } from '../src/context/translation' +import { h } from 'preact'; + + +export const parameters = { + controls: { expanded: true }, + options: { + storySort: (a, b) => { + return (a[1].args.order ?? 0) - (b[1].args.order ?? 0) + // return a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, undefined, { numeric: true }) + } + }, +} + +export const globalTypes = { + locale: { + name: 'Locale', + description: 'Internationalization locale', + defaultValue: 'en', + toolbar: { + icon: 'globe', + items: [ + { value: 'en', right: '🇺🇸', title: 'English' }, + { value: 'es', right: '🇪🇸', title: 'Spanish' }, + ], + }, + }, +}; + +export const decorators = [ + (Story, { globals }) => { + document.body.parentElement.classList = "has-aside-left has-aside-mobile-transition has-navbar-fixed-top has-aside-expanded" + return <Story /> + }, + (Story, { globals }) => <TranslationProvider initial='en' forceLang={globals.locale}> + <Story /> + </TranslationProvider>, +]; diff --git a/packages/demobank-ui/README.md b/packages/demobank-ui/README.md new file mode 100644 index 000000000..c014929ce --- /dev/null +++ b/packages/demobank-ui/README.md @@ -0,0 +1,19 @@ +# bank web + +## CLI Commands + +- `npm install`: Installs dependencies + +- `npm run dev`: Run a development, HMR server + +- `npm run serve`: Run a production-like server + +- `npm run build`: Production-ready build + +- `npm run lint`: Pass TypeScript files using ESLint + +- `npm run test`: Run Jest and Enzyme with + [`enzyme-adapter-preact-pure`](https://github.com/preactjs/enzyme-adapter-preact-pure) for + your tests + +For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md). diff --git a/packages/demobank-ui/TODO b/packages/demobank-ui/TODO new file mode 100644 index 000000000..a399ada58 --- /dev/null +++ b/packages/demobank-ui/TODO @@ -0,0 +1,45 @@ +Urgent TODOs: + +- General: + * not only Nora dark-theme, but default light! (CSS) + * auto-focus on input fields is not working well + * buttons should be visibly insensitive + as long as required input fields are not + working + * next required invalid/missing input field is + not properly highlighted in red + * Logout button needs more padding to the right (CSS) + +- Error bar: + * shows JSON, should only show good error message + and numeric code, not JSON syntax + * should auto-hide after next action, no need for + "clear"! + * need variant "status bar" in green (or blue) + which shows status of last operation + +* H1-Titles: + * Center more (currently way on the left) (CSS) + +- Assets: + * Numeric amount needs to be shown MUCH bigger (CSS) + * Center more? (CSS) + +- Payments: + * Amount to withdraw currently shown in white-on-white (CSS) + * Big frame drawn around notebook-tabs is not nice (CSS) + * Center more? (CSS) + * "Wire to bank account" + - maybe split two types (payto and IBAN) into + two tabs? + - currently cannot switch back from payto to IBAN + +- Withdraw: + * Should use new 'status' bar at the end, instead + of extra dialog with "close" button + * ditto for bank-wire-transfer final stage + +- Footer: + * overlaps with transaction history or other + content, needs to consistently show at the + end! => change rendering logic!? (CSS?) diff --git a/packages/demobank-ui/build-bank-translations.sh b/packages/demobank-ui/build-bank-translations.sh new file mode 100755 index 000000000..85c8ad0c1 --- /dev/null +++ b/packages/demobank-ui/build-bank-translations.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -eu + +# NOTE: the <Translate> node somehow didn't get +# the strings extracted. Only i18n`` did + +function build { + POTGEN=node_modules/@gnu-taler/pogen/bin/pogen + PACKAGE_NAME=$1 + + find src/ \( -type f -name "*.ts" -or -name "*.tsx" \) ! -name "*.d.ts" \ + | xargs node $POTGEN \ + | msguniq \ + | msgmerge src/i18n/poheader - \ + > src/i18n/$PACKAGE_NAME.pot + + # merge existing translations: fails when NO .po-files were found. + for pofile in $(ls src/i18n/*.po 2> /dev/null || true); do + echo merging $pofile; + msgmerge -o $pofile $pofile src/i18n/$PACKAGE_NAME.pot; + done; + + # generate .ts file containing all translations + cat src/i18n/strings-prelude > src/i18n/strings.ts + for pofile in $(ls src/i18n/*.po 2> /dev/null || true); do \ + echo appending $pofile; \ + ./contrib/po2ts $pofile >> src/i18n/strings.ts; \ + done; +} + +build bank diff --git a/packages/demobank-ui/contrib/po2ts b/packages/demobank-ui/contrib/po2ts new file mode 100755 index 000000000..a135da61b --- /dev/null +++ b/packages/demobank-ui/contrib/po2ts @@ -0,0 +1,42 @@ +#!/usr/bin/env node +/* + This file is part of GNU Taler + (C) 2020 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/> + */ + +/** + * Convert a <lang>.po file into a JavaScript / TypeScript expression. + */ + +const po2json = require("po2json"); + +const filename = process.argv[2]; + +if (!filename) { + console.error("error: missing filename"); + process.exit(1); +} + +const m = filename.match(/([a-zA-Z0-9-_]+).po/); + +if (!m) { + console.error("error: unexpected filename (expected <lang>.po)"); + process.exit(1); +} + +const lang = m[1]; +const pojson = po2json.parseFileSync(filename, { format: "jed1.x", fuzzy: true }); +const s = + "strings['" + lang + "'] = " + JSON.stringify(pojson, null, " ") + ";\n"; +console.log(s); diff --git a/packages/demobank-ui/mocks/json-server/db.json b/packages/demobank-ui/mocks/json-server/db.json new file mode 100644 index 000000000..44d3ccab2 --- /dev/null +++ b/packages/demobank-ui/mocks/json-server/db.json @@ -0,0 +1,4 @@ +{ + "register": {}, + "transactions": {} +} diff --git a/packages/demobank-ui/mocks/window.js b/packages/demobank-ui/mocks/window.js new file mode 100644 index 000000000..f396ff9e3 --- /dev/null +++ b/packages/demobank-ui/mocks/window.js @@ -0,0 +1,27 @@ +Object.defineProperty(window, 'requestAnimationFrame', { + value: function(cb) {} // Silence the browser. +}) + +Object.defineProperty(window, 'localStorage', { + value: { + store: {}, + getItem: function(key) { + return this.store[key]; + }, + setItem: function(key, value) { + return this.store[key] = value; + }, + clear: function() { + this.store = {}; + } + } +}); +Object.defineProperty(window, 'location', { + value: { + origin: "http://localhost:8080", /* where taler-local rev proxy listens to */ + search: "", + pathname: "/sandbox/demobanks/default", + } +}) + +export default window; diff --git a/packages/demobank-ui/package.json b/packages/demobank-ui/package.json new file mode 100644 index 000000000..bd686365b --- /dev/null +++ b/packages/demobank-ui/package.json @@ -0,0 +1,100 @@ +{ + "private": true, + "name": "bank", + "version": "0.1.0", + "license": "AGPL-3.0-OR-LATER", + "scripts": { + "dev": "preact watch --port ${PORT:=9090} --no-sw --no-esm -c preact.mock.js", + "build": "preact build --no-sw --no-esm -c preact.single-config.js --dest build && sh remove-link-stylesheet.sh", + "serve": "sirv build --port ${PORT:=8080} --cors --single", + "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", + "test": "jest ./tests", + "build-storybook": "build-storybook", + "serve-single": "sirv single --port ${PORT:=8080} --cors --single", + "pretty": "prettier --write src", + "storybook": "start-storybook -p 6006" + }, + "eslintConfig": { + "parser": "@typescript-eslint/parser", + "extends": [ + "preact", + "plugin:@typescript-eslint/recommended" + ], + "ignorePatterns": [ + "build/" + ], + "rules": { + "@typescript-eslint/no-explicit-any": [0], + "@typescript-eslint/ban-ts-comment": [1], + "quotes": [2, "single", {"allowTemplateLiterals": true,"avoidEscape": false}], + "indent": [2,2], + "prefer-arrow-callback": [2, {"allowNamedFunctions": false, "allowUnboundThis": true}], + "curly": [2,"multi"], + "prefer-template": [1] + } + }, + "dependencies": { + "base64-inline-loader": "1.1.1", + "date-fns": "2.25.0", + "jed": "1.1.1", + "preact": "^10.5.15", + "preact-render-to-string": "^5.1.19", + "preact-router": "^3.2.1", + "qrcode-generator": "^1.4.4", + "swr": "1.1" + }, + "devDependencies": { + "@babel/core": "^7.13.16", + "@babel/plugin-transform-react-jsx": "^7.12.13", + "@babel/plugin-transform-react-jsx-source": "^7.12.13", + "@babel/preset-env": "^7.16.7", + "@creativebulma/bulma-tooltip": "^1.2.0", + "@gnu-taler/pogen": "^0.0.5", + "@storybook/addon-a11y": "6.2.9", + "@storybook/addon-actions": "6.2.9", + "@storybook/addon-essentials": "6.2.9", + "@storybook/addon-links": "6.2.9", + "@storybook/preact": "6.2.9", + "@storybook/preset-scss": "^1.0.3", + "@testing-library/jest-dom": "^5.16.1", + "@testing-library/preact": "^2.0.1", + "@testing-library/preact-hooks": "^1.1.0", + "@types/enzyme": "^3.10.10", + "@types/jest": "^27.0.2", + "@typescript-eslint/eslint-plugin": "^5.3.0", + "@typescript-eslint/parser": "^5.3.0", + "babel-loader": "^8.2.2", + "base64-inline-loader": "^1.1.1", + "bulma": "^0.9.3", + "bulma-checkbox": "^1.1.1", + "bulma-radio": "^1.1.1", + "enzyme": "^3.11.0", + "enzyme-adapter-preact-pure": "^3.2.0", + "eslint": "^8.1.0", + "eslint-config-preact": "^1.2.0", + "html-webpack-inline-chunk-plugin": "^1.1.1", + "html-webpack-inline-source-plugin": "0.0.10", + "html-webpack-skip-assets-plugin": "^1.0.1", + "inline-chunk-html-plugin": "^1.1.1", + "jest": "^27.3.1", + "jest-fetch-mock": "^3.0.3", + "jest-preset-preact": "^4.0.5", + "jest-watch-typeahead": "^1.0.0", + "jest-environment-jsdom": "^27.4.6", + "jssha": "^3.2.0", + "po2json": "^0.4.5", + "preact-cli": "3.0.5", + "sass": "1.32.13", + "sass-loader": "^10", + "script-ext-html-webpack-plugin": "^2.1.5", + "sirv-cli": "^1.0.14", + "typescript": "^4.4.4" + }, + "jest": { + "preset": "jest-preset-preact", + "setupFiles": [ + "<rootDir>/tests/__mocks__/browserMocks.ts", + "<rootDir>/tests/__mocks__/setupTests.ts" + ] + } +} diff --git a/packages/demobank-ui/preact.config.js b/packages/demobank-ui/preact.config.js new file mode 100644 index 000000000..8e640f3ff --- /dev/null +++ b/packages/demobank-ui/preact.config.js @@ -0,0 +1,70 @@ +/* + 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 { DefinePlugin } from 'webpack'; + +import pack from './package.json'; +import * as cp from 'child_process'; + +const commitHash = cp.execSync('git rev-parse --short HEAD').toString(); + +export default { + webpack(config, env, helpers) { + // ensure that process.env will not be undefined on runtime + config.node.process = 'mock' + + // add __VERSION__ to be use in the html + config.plugins.push( + new DefinePlugin({ + 'process.env.__VERSION__': JSON.stringify(env.isProd ? pack.version : `dev-${commitHash}`) , + }), + ); + + // suddenly getting out of memory error from build process, error below [1] + // FIXME: remove preact-cli, use rollup + let { index } = helpers.getPluginsByName(config, 'WebpackFixStyleOnlyEntriesPlugin')[0] + config.plugins.splice(index, 1) + } +} + + + +/* [1] from this error decided to remove plugin 'webpack-fix-style-only-entries + leaving this error for future reference + + +<--- Last few GCs ---> + +[32479:0x2e01870] 19969 ms: Mark-sweep 1869.4 (1950.2) -> 1443.1 (1504.1) MB, 497.5 / 0.0 ms (average mu = 0.631, current mu = 0.455) allocation failure scavenge might not succeed +[32479:0x2e01870] 21907 ms: Mark-sweep 2016.9 (2077.9) -> 1628.6 (1681.4) MB, 1596.0 / 0.0 ms (average mu = 0.354, current mu = 0.176) allocation failure scavenge might not succeed + +<--- JS stacktrace ---> + +==== JS stack trace ========================================= + + 0: ExitFrame [pc: 0x13cf099] +Security context: 0x2f4ca66c08d1 <JSObject> + 1: /* anonymous * / [0x35d05555b4b9] [...path/merchant-backoffice/node_modules/.pnpm/webpack-fix-style-only-entries@0.5.2/node_modules/webpack-fix-style-only-entries/index.js:~80] [pc=0x2145e699d1a4](this=0x1149465410e9 <GlobalObject Object map = 0xff481b5b5f9>,0x047e52e36a49 <Dependency map = 0x1ed1fe41cd19>) + 2: arguments adaptor frame: 3... + +FATAL ERROR: invalid array length Allocation failed - JavaScript heap out of memory + +*/
\ No newline at end of file diff --git a/packages/demobank-ui/preact.mock.js b/packages/demobank-ui/preact.mock.js new file mode 100644 index 000000000..dc3ceb66d --- /dev/null +++ b/packages/demobank-ui/preact.mock.js @@ -0,0 +1,55 @@ +/* + 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 { DefinePlugin, ProvidePlugin } from 'webpack'; + +import pack from './package.json'; +import * as cp from 'child_process'; + +const commitHash = cp.execSync('git rev-parse --short HEAD').toString(); +import path from 'path'; + +export default { + webpack(config, env, helpers) { + // Ensure that process.env will not be undefined at runtime. + config.node.process = 'mock' + let DEMO_SITES = { + "Blog": process.env.TALER_ENV_URL_MERCHANT_BLOG, + "Donations": process.env.TALER_ENV_URL_MERCHANT_DONATIONS, + "Survey": process.env.TALER_ENV_URL_MERCHANT_SURVEY, + "Landing": process.env.TALER_ENV_URL_INTRO, + "Bank": process.env.TALER_ENV_URL_BANK, + } + console.log("demo links found", DEMO_SITES); + // Add __VERSION__ to be use in the html. + config.plugins.push( + new DefinePlugin({ + 'process.env.__VERSION__': JSON.stringify(env.isProd ? pack.version : `dev-${commitHash}`) , + }), + // 'window' gets mocked to point at a running euFin instance. + new ProvidePlugin({window: path.resolve("mocks/window")}), + new DefinePlugin({"DEMO_SITES": JSON.stringify(DEMO_SITES)}) + ); + + let { index } = helpers.getPluginsByName(config, 'WebpackFixStyleOnlyEntriesPlugin')[0] + config.plugins.splice(index, 1) + } +} diff --git a/packages/demobank-ui/preact.single-config.js b/packages/demobank-ui/preact.single-config.js new file mode 100644 index 000000000..0fb6f1d0e --- /dev/null +++ b/packages/demobank-ui/preact.single-config.js @@ -0,0 +1,60 @@ +/* + 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 defaultConfig from './preact.config' + +export default { + webpack(config, env, helpers, options) { + defaultConfig.webpack(config, env, helpers, options) + + //1. check no file is under /routers or /component/{routers,async} to prevent async components + // https://github.com/preactjs/preact-cli#route-based-code-splitting + + //2. remove devtools to prevent sourcemaps + config.devtool = false + + //3. change assetLoader to load assets inline + const loaders = helpers.getLoaders(config) + const assetsLoader = loaders.find(lo => lo.rule.test.test('something.woff')) + if (assetsLoader) { + assetsLoader.rule.use = 'base64-inline-loader' + assetsLoader.rule.loader = undefined + } + + //4. remove critters + //critters remove the css bundle from htmlWebpackPlugin.files.css + //for now, pushing all the content into the html is enough + const crittersWrapper = helpers.getPluginsByName(config, 'Critters') + if (crittersWrapper && crittersWrapper.length > 0) { + const [{ index }] = crittersWrapper + config.plugins.splice(index, 1) + } + + //5. remove favicon from src/assets + + //6. remove performance hints since we now that this is going to be big + if (config.performance) { + config.performance.hints = false + } + + //7. template.html should have a favicon and add js/css content + } +} diff --git a/packages/demobank-ui/remove-link-stylesheet.sh b/packages/demobank-ui/remove-link-stylesheet.sh new file mode 100755 index 000000000..d3376b8e6 --- /dev/null +++ b/packages/demobank-ui/remove-link-stylesheet.sh @@ -0,0 +1,8 @@ +# This script has been placed in the public domain. + +FILE=$(ls build/bundle.*.css) +BUNDLE=${FILE#build} +grep -q '<link href="'$BUNDLE'" rel="stylesheet">' build/index.html || { echo bundle $BUNDLE not found in index.html; exit 1; } +echo -n Removing link from index.html ... +sed 's_<link href="'$BUNDLE'" rel="stylesheet">__' -i build/index.html +echo done diff --git a/packages/demobank-ui/src/.babelrc b/packages/demobank-ui/src/.babelrc new file mode 100644 index 000000000..05f4dcc81 --- /dev/null +++ b/packages/demobank-ui/src/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["preact-cli/babel"] +} diff --git a/packages/demobank-ui/src/assets/empty.png b/packages/demobank-ui/src/assets/empty.png Binary files differnew file mode 100644 index 000000000..5120d3138 --- /dev/null +++ b/packages/demobank-ui/src/assets/empty.png diff --git a/packages/demobank-ui/src/assets/example/id1.jpg b/packages/demobank-ui/src/assets/example/id1.jpg Binary files differnew file mode 100644 index 000000000..5d022a379 --- /dev/null +++ b/packages/demobank-ui/src/assets/example/id1.jpg diff --git a/packages/demobank-ui/src/assets/favicon.ico b/packages/demobank-ui/src/assets/favicon.ico Binary files differnew file mode 100644 index 000000000..07419145b --- /dev/null +++ b/packages/demobank-ui/src/assets/favicon.ico diff --git a/packages/demobank-ui/src/assets/icons/android-chrome-192x192.png b/packages/demobank-ui/src/assets/icons/android-chrome-192x192.png Binary files differnew file mode 100644 index 000000000..93ebe2e2c --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/android-chrome-192x192.png diff --git a/packages/demobank-ui/src/assets/icons/android-chrome-512x512.png b/packages/demobank-ui/src/assets/icons/android-chrome-512x512.png Binary files differnew file mode 100644 index 000000000..52d1623ea --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/android-chrome-512x512.png diff --git a/packages/demobank-ui/src/assets/icons/apple-touch-icon.png b/packages/demobank-ui/src/assets/icons/apple-touch-icon.png Binary files differnew file mode 100644 index 000000000..254e4bb4d --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/apple-touch-icon.png diff --git a/packages/demobank-ui/src/assets/icons/auth_method/email.svg b/packages/demobank-ui/src/assets/icons/auth_method/email.svg new file mode 100644 index 000000000..3e44b8779 --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/auth_method/email.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z"/></svg>
\ No newline at end of file diff --git a/packages/demobank-ui/src/assets/icons/auth_method/postal.svg b/packages/demobank-ui/src/assets/icons/auth_method/postal.svg new file mode 100644 index 000000000..3787b8350 --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/auth_method/postal.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 15h2v2h-2zM17 11h2v2h-2zM17 7h2v2h-2zM13.74 7l1.26.84V7z"/><path d="M10 3v1.51l2 1.33V5h9v14h-4v2h6V3z"/><path d="M8.17 5.7L15 10.25V21H1V10.48L8.17 5.7zM10 19h3v-7.84L8.17 8.09 3 11.38V19h3v-6h4v6z"/></svg>
\ No newline at end of file diff --git a/packages/demobank-ui/src/assets/icons/auth_method/question.svg b/packages/demobank-ui/src/assets/icons/auth_method/question.svg new file mode 100644 index 000000000..a346556b2 --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/auth_method/question.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 23.59v-3.6c-5.01-.26-9-4.42-9-9.49C2 5.26 6.26 1 11.5 1S21 5.26 21 10.5c0 4.95-3.44 9.93-8.57 12.4l-1.43.69zM11.5 3C7.36 3 4 6.36 4 10.5S7.36 18 11.5 18H13v2.3c3.64-2.3 6-6.08 6-9.8C19 6.36 15.64 3 11.5 3zm-1 11.5h2v2h-2zm2-1.5h-2c0-3.25 3-3 3-5 0-1.1-.9-2-2-2s-2 .9-2 2h-2c0-2.21 1.79-4 4-4s4 1.79 4 4c0 2.5-3 2.75-3 5z"/></svg>
\ No newline at end of file diff --git a/packages/demobank-ui/src/assets/icons/auth_method/sms.svg b/packages/demobank-ui/src/assets/icons/auth_method/sms.svg new file mode 100644 index 000000000..ed15679bf --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/auth_method/sms.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 1.01L7 1c-1.1 0-1.99.9-1.99 2v18c0 1.1.89 2 1.99 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"/></svg>
\ No newline at end of file diff --git a/packages/demobank-ui/src/assets/icons/auth_method/video.svg b/packages/demobank-ui/src/assets/icons/auth_method/video.svg new file mode 100644 index 000000000..69de5e0b4 --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/auth_method/video.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><path d="M18,10.48V6c0-1.1-0.9-2-2-2H4C2.9,4,2,4.9,2,6v12c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-4.48l4,3.98v-11L18,10.48z M16,9.69V18H4V6h12V9.69z"/><circle cx="10" cy="10" r="2"/><path d="M14,15.43c0-0.81-0.48-1.53-1.22-1.85C11.93,13.21,10.99,13,10,13c-0.99,0-1.93,0.21-2.78,0.58C6.48,13.9,6,14.62,6,15.43 V16h8V15.43z"/></g></g></svg>
\ No newline at end of file diff --git a/packages/demobank-ui/src/assets/icons/favicon-16x16.png b/packages/demobank-ui/src/assets/icons/favicon-16x16.png Binary files differnew file mode 100644 index 000000000..e81177dcb --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/favicon-16x16.png diff --git a/packages/demobank-ui/src/assets/icons/favicon-32x32.png b/packages/demobank-ui/src/assets/icons/favicon-32x32.png Binary files differnew file mode 100644 index 000000000..40e9b5b47 --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/favicon-32x32.png diff --git a/packages/demobank-ui/src/assets/icons/languageicon.svg b/packages/demobank-ui/src/assets/icons/languageicon.svg new file mode 100644 index 000000000..22d58da65 --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/languageicon.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 2411.2 2794" style="enable-background:new 0 0 2411.2 2794;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+</style>
+<g id="Layer_2">
+</g>
+<g id="Layer_x5F_1_x5F_1">
+ <g>
+ <polygon points="1204.6,359.2 271.8,30 271.8,2060.1 1204.6,1758.3 "/>
+ <polygon class="st0" points="1182.2,358.1 2150.6,29 2150.6,2059 1182.2,1757.3 "/>
+ <polygon class="st0" points="30,2415.4 1182.2,2031.4 1182.2,357.9 30,742 "/>
+ <polygon points="1707.2,2440.7 1870.5,2709.4 1956.6,2459.8 "/>
+ <g>
+ <path d="M421.7,934.8c-6.1-6,8,49.1,27.6,68.9c34.8,35.1,61.9,39.6,76.4,40.2c32,1.3,71.5-8,94.9-17.8
+ c22.7-9.7,62.4-30,77.5-59.6c3.2-6.3,11.9-17,6.4-43.2c-4.2-20.2-17-27.3-32.7-26.2c-15.7,1.1-63.2,13.7-86.1,20.8
+ c-23,7-70.3,21.4-90.9,25.8C474.3,948.2,429,941.7,421.7,934.8z"/>
+ <path d="M1003.1,1593.7c-9.1-3.3-196.9-81.1-223.6-93.9c-21.8-10.5-75.2-33.1-100.4-43.3c70.8-109.2,115.5-191.6,121.5-204.1
+ c11-23,86-169.6,87.7-178.7c1.7-9.1,3.8-42.9,2.2-51c-1.7-8.2-29.1,7.6-66.4,20.2c-37.4,12.6-108.4,58.8-135.8,64.6
+ c-27.5,5.7-115.5,39.1-160.5,54c-45,14.9-130.2,40.9-165.2,50.4c-35.1,9.5-65.7,10.2-85.3,16.2c0,0,2.6,27.5,7.8,35.7
+ c5.2,8.2,23.7,28.4,45.3,34.1c21.6,5.7,57.3,3.4,73.6-0.3c16.3-3.8,44.4-17.5,48.2-23.6c3.8-6.1-2-24.9,4.5-30.6
+ c6.5-5.6,92.2-25.7,124.6-35.4c32.4-10,156.3-52.6,173.1-50.5c-5.3,17.7-105,215.1-137.1,274c-32.1,58.9-218.6,318-258.3,363.6
+ c-30.1,34.7-103.2,123.5-128.5,143.6c6.4,1.8,51.6-2.1,59.9-7.2c51.3-31.6,136.9-138.1,164.4-170.5
+ c81.9-96,153.8-196.8,210.8-283.4h0.1c11.1,4.6,100.9,77.8,124.4,94c23.4,16.2,115.9,67.8,136,76.4c20,8.7,97.1,44.2,100.3,32.2
+ C1029.4,1668,1012.2,1597.1,1003.1,1593.7z"/>
+ </g>
+ <path class="st1" d="M569,2572c18,11,35,20,54,29c38,19,81,39,122,54c56,21,112,38,168,51c31,7,65,13,98,18c3,0,92,11,110,11h90
+ c35-3,68-5,103-10c28-4,59-9,89-16c22-5,45-10,67-17c21-6,45-14,68-22c15-5,31-12,47-18c13-6,29-13,44-19c18-8,39-19,59-29
+ c16-8,34-18,51-28c13-7,43-30,59-30c18,0,30,16,30,30c0,29-39,38-57,51c-19,13-42,23-62,34c-40,21-81,39-120,54
+ c-51,19-107,37-157,49c-19,4-38,9-57,12c-10,2-114,18-143,18h-132c-35-3-72-7-107-12c-31-5-64-11-95-18c-24-5-50-12-73-19
+ c-40-11-79-25-117-40c-69-26-141-60-209-105c-12-8-13-16-13-25c0-15,11-29,29-29C531,2546,563,2569,569,2572z"/>
+ <path class="st1" d="M1151,2009L61,2372V764l1090-363V2009z M1212,354v1680c-1,5-3,10-7,15c-2,3-6,7-9,8c-25,10-1151,388-1166,388
+ c-12,0-23-8-29-21c0-1-1-2-1-4V739c2-5,3-12,7-16c8-11,22-13,31-16c17-6,1126-378,1142-378C1190,329,1212,336,1212,354z"/>
+ <path class="st1" d="M2120,2017l-907-282V380l907-308V2017z M2181,32v2023c-1,23-17,33-32,33c-13,0-107-32-123-37
+ c-126-39-253-78-378-117c-28-9-57-18-84-27c-24-7-50-15-74-23c-107-33-216-66-323-102c-4-1-14-15-14-18V351c2-5,4-11,9-15
+ c8-9,351-123,486-168c36-13,487-168,501-168C2167,0,2181,13,2181,32z"/>
+ <polygon points="2411.2,2440.7 1199.5,2054.5 1204.6,373.2 2411.2,757.2 "/>
+ <g>
+ <path class="st2" d="M1800.3,1124.6L1681.4,1412l218.6,66.3L1800.3,1124.6z M1729,853.2l156.1,47.3l284.4,1025l-160.3-48.7
+ l-57.6-210.4L1620.2,1566l-71.3,171.4l-160.4-48.7L1729,853.2z"/>
+ </g>
+ </g>
+</g>
+</svg>
diff --git a/packages/demobank-ui/src/assets/icons/mstile-150x150.png b/packages/demobank-ui/src/assets/icons/mstile-150x150.png Binary files differnew file mode 100644 index 000000000..9cfb889be --- /dev/null +++ b/packages/demobank-ui/src/assets/icons/mstile-150x150.png diff --git a/packages/demobank-ui/src/assets/logo-white.svg b/packages/demobank-ui/src/assets/logo-white.svg new file mode 100644 index 000000000..cb1f023c5 --- /dev/null +++ b/packages/demobank-ui/src/assets/logo-white.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + width="670" + height="300" + viewBox="0 0 201 90" + version="1.1" + id="svg8"> + <g + id="logo"> + <g + id="circles" + style="fill:#FFF;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.327943"> + <path + d="m 86.662153,1.1211936 c 15.589697,0 29.129227,9.4011664 35.961027,23.2018054 h -5.81736 C 110.4866,13.623304 99.349002,6.5180852 86.662153,6.5180852 c -19.690571,0 -35.652876,17.1120008 -35.652876,38.2205688 0,10.331797 3.825597,19.704678 10.03957,26.582945 -1.342357,1.120912 -2.771532,2.127905 -4.275488,3.006754 C 50.071485,66.553412 45.974857,56.15992 45.974857,44.738654 c 0,-24.089211 18.216325,-43.6174604 40.687296,-43.6174604 z M 122.51416,65.375898 c -6.86645,13.680134 -20.34561,22.980218 -35.852007,22.980218 -1.052702,0 -2.096093,-0.04291 -3.128683,-0.127026 3.052192,-1.561167 5.913582,-3.480387 8.538307,-5.707305 10.320963,-1.684389 19.185983,-8.113638 24.601813,-17.145887 z" + id="path2350" /> + <path + d="m 64.212372,1.1211936 c 1.052607,0 2.095998,0.042919 3.128684,0.1270583 C 64.288864,2.8094199 61.427378,4.728606 58.802653,6.9555572 41.679542,9.7498571 28.559494,25.601563 28.559494,44.738654 c 0,14.264563 7.29059,26.702023 18.093843,33.268925 -1.593656,0.26719 -3.226966,0.406948 -4.890748,0.406948 -1.239545,0 -2.46151,-0.07952 -3.663522,-0.229364 C 29.191129,70.184015 23.525076,58.171633 23.525076,44.738654 23.525076,20.649443 41.7414,1.1211936 64.212372,1.1211936 Z M 69.62209,82.521785 C 79.943207,80.837396 88.808164,74.407841 94.224059,65.375422 h 5.840511 c -6.866354,13.680305 -20.345548,22.980694 -35.852198,22.980694 -1.052703,0 -2.095999,-0.04291 -3.128684,-0.127026 3.052002,-1.561371 5.913836,-3.480218 8.538402,-5.707305 z M 94.355885,24.322999 c -3.13939,-5.314721 -7.467551,-9.74275 -12.584511,-12.853269 1.593656,-0.26719 3.226904,-0.406948 4.890779,-0.406948 1.239451,0 2.461512,0.07952 3.663524,0.229364 4.016018,3.607242 7.373195,8.030111 9.849053,13.030853 z" + id="path2352" /> + <path + d="m 41.762589,1.1211936 c 1.064296,0 2.118804,0.044379 3.162607,0.1302161 -3.046523,1.558961 -5.903162,3.4745139 -8.52358,5.6968133 C 19.254624,9.7205882 6.1097128,25.583465 6.1097128,44.738654 c 0,21.108568 15.9624012,38.22057 35.6528762,38.22057 12.599746,0 23.672446,-7.007056 30.013748,-17.583802 h 5.838515 C 70.748498,79.055727 57.26924,88.356116 41.762589,88.356116 c -22.470907,0 -40.6871998,-19.52825 -40.6871998,-43.617462 0,-24.089211 18.2162928,-43.6174604 40.6871998,-43.6174604 z M 71.905375,24.322999 c -1.31192,-2.220567 -2.830984,-4.287049 -4.528877,-6.166508 1.342452,-1.120945 2.771374,-2.128381 4.275139,-3.00723 2.372984,2.753011 4.418875,5.834636 6.072489,9.173738 z" + id="path2354" /> + </g> + <g + id="letters" + style="fill:#FFF"> + <path + d="m 76.135411,34.409066 h 9.161042 V 29.36588 H 61.857537 v 5.043186 h 9.161137 v 25.92317 h 5.116737 z" + id="path2346" /> + <path + d="m 92.647571,52.856334 h 13.659009 l 2.93009,7.476072 h 5.36461 L 101.89122,29.144903 H 97.187186 L 84.477089,60.332406 h 5.199533 z m 11.802109,-4.822276 h -9.944771 l 4.951718,-12.386462 z" + id="path2362" /> + <path + d="m 123.80641,29.366084 h -4.58038 v 30.966322 h 20.54728 v -4.910253 c -5.32227,0 -10.64463,0 -15.9669,0 z" + id="path2356" /> + <path + d="m 166.4722,29.366084 h -21.37564 v 30.966322 h 21.58203 v -4.910253 h -16.54771 v -8.27275 h 14.48439 V 42.23925 h -14.48439 v -7.962811 h 16.34132 z" + id="path2360" /> + <path + d="m 191.19035,39.474593 c 0,1.59947 -0.53646,2.87535 -1.61628,3.818883 -1.07281,0.95124 -2.52409,1.422837 -4.34678,1.422837 h -7.44851 V 34.276439 h 7.4073 c 1.9051,0 3.38376,0.435027 4.42939,1.312178 1.05226,0.870258 1.57488,2.167734 1.57488,3.885976 z m 6.06602,20.857813 -7.79911,-11.723191 c 1.01771,-0.294794 1.94631,-0.714813 2.78553,-1.260566 0.83885,-0.545619 1.56122,-1.209263 2.16629,-1.990627 0.60541,-0.781738 1.07981,-1.681096 1.42369,-2.698345 0.34378,-1.017553 0.51561,-2.175238 0.51561,-3.472883 0,-1.50409 -0.24743,-2.867948 -0.74267,-4.092048 -0.49515,-1.223794 -1.20344,-2.256186 -2.12499,-3.096734 -0.92173,-0.840446 -2.04957,-1.489252 -3.38375,-1.946452 -1.33447,-0.457267 -2.82692,-0.685476 -4.4774,-0.685476 h -12.87512 v 30.966322 h 5.03433 V 49.538522 h 6.37569 l 7.11829,10.793884 z" + id="path2358" /> + </g> + </g> +</svg> diff --git a/packages/demobank-ui/src/assets/logo.jpeg b/packages/demobank-ui/src/assets/logo.jpeg Binary files differnew file mode 100644 index 000000000..489832f7c --- /dev/null +++ b/packages/demobank-ui/src/assets/logo.jpeg diff --git a/packages/demobank-ui/src/components/AsyncButton.tsx b/packages/demobank-ui/src/components/AsyncButton.tsx new file mode 100644 index 000000000..0c4305668 --- /dev/null +++ b/packages/demobank-ui/src/components/AsyncButton.tsx @@ -0,0 +1,66 @@ +/* + 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 { ComponentChildren, h, VNode } from 'preact'; +import { useLayoutEffect, useRef } from 'preact/hooks'; +// import { LoadingModal } from "../modal"; +import { useAsync } from '../hooks/async'; +// import { Translate } from "../../i18n"; + +type Props = { + children: ComponentChildren; + disabled?: boolean; + onClick?: () => Promise<void>; + grabFocus?: boolean; + [rest: string]: any; +}; + +export function AsyncButton({ + onClick, + grabFocus, + disabled, + children, + ...rest +}: Props): VNode { + const { isLoading, request } = useAsync(onClick); + + const buttonRef = useRef<HTMLButtonElement>(null); + useLayoutEffect(() => { + if (grabFocus) + buttonRef.current?.focus(); + + }, [grabFocus]); + + // if (isSlow) { + // return <LoadingModal onCancel={cancel} />; + // } + if (isLoading) + return <button class="button">Loading...</button>; + + + return ( + <span data-tooltip={rest['data-tooltip']} style={{ marginLeft: 5 }}> + <button {...rest} ref={buttonRef} onClick={request} disabled={disabled}> + {children} + </button> + </span> + ); +} diff --git a/packages/demobank-ui/src/components/FileButton.tsx b/packages/demobank-ui/src/components/FileButton.tsx new file mode 100644 index 000000000..dba86ccbf --- /dev/null +++ b/packages/demobank-ui/src/components/FileButton.tsx @@ -0,0 +1,57 @@ +import { h, VNode } from 'preact'; +import { useRef, useState } from 'preact/hooks'; + +const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024; + +export interface FileTypeContent { + content: string; + type: string; + name: string; +} + +interface Props { + label: string; + onChange: (v: FileTypeContent | undefined) => void; +} +export function FileButton(props: Props): VNode { + const fileInputRef = useRef<HTMLInputElement>(null); + const [sizeError, setSizeError] = useState(false); + return ( + <div> + <button class="button" onClick={(e) => fileInputRef.current?.click()}> + <span>{props.label}</span> + </button> + <input + ref={fileInputRef} + style={{ display: 'none' }} + type="file" + onChange={(e) => { + const f: FileList | null = e.currentTarget.files; + if (!f || f.length != 1) + return props.onChange(undefined); + + console.log(f); + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true); + return props.onChange(undefined); + } + setSizeError(false); + return f[0].arrayBuffer().then((b) => { + const content = new Uint8Array(b).reduce( + (data, byte) => data + String.fromCharCode(byte), + '', + ); + return props.onChange({ + content, + name: f[0].name, + type: f[0].type, + }); + }); + }} + /> + {sizeError && ( + <p class="help is-danger">File should be smaller than 1 MB</p> + )} + </div> + ); +} diff --git a/packages/demobank-ui/src/components/Notifications.tsx b/packages/demobank-ui/src/components/Notifications.tsx new file mode 100644 index 000000000..09329442a --- /dev/null +++ b/packages/demobank-ui/src/components/Notifications.tsx @@ -0,0 +1,74 @@ +/* + 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'; + +export interface Notification { + message: string; + description?: string | VNode; + type: MessageType; +} + +export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'; + +interface Props { + notifications: Notification[]; + removeNotification?: (n: Notification) => void; +} + +function messageStyle(type: MessageType): string { + switch (type) { + case 'INFO': + return 'message is-info'; + case 'WARN': + return 'message is-warning'; + case 'ERROR': + return 'message is-danger'; + case 'SUCCESS': + return 'message is-success'; + default: + return 'message'; + } +} + +export function Notifications({ + notifications, + removeNotification, +}: Props): VNode { + return ( + <div class="block"> + {notifications.map((n, i) => ( + <article key={i} class={messageStyle(n.type)}> + <div class="message-header"> + <p>{n.message}</p> + {removeNotification && ( + <button + class="delete" + onClick={() => removeNotification && removeNotification(n)} + /> + )} + </div> + {n.description && <div class="message-body">{n.description}</div>} + </article> + ))} + </div> + ); +} diff --git a/packages/demobank-ui/src/components/QR.tsx b/packages/demobank-ui/src/components/QR.tsx new file mode 100644 index 000000000..ee5b73c69 --- /dev/null +++ b/packages/demobank-ui/src/components/QR.tsx @@ -0,0 +1,48 @@ +/* + 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/> + */ + +import { h, VNode } from 'preact'; +import { useEffect, useRef } from 'preact/hooks'; +import qrcode from 'qrcode-generator'; + +export function QR({ text }: { text: string }): VNode { + const divRef = useRef<HTMLDivElement>(null); + useEffect(() => { + const qr = qrcode(0, 'L'); + qr.addData(text); + qr.make(); + if (divRef.current) + divRef.current.innerHTML = qr.createSvgTag({ + scalable: true, + }); + }); + + return ( + <div + style={{ + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'left', + }} + > + <div + style={{ width: '50%', minWidth: 200, maxWidth: 300 }} + ref={divRef} + /> + </div> + ); +} diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx new file mode 100644 index 000000000..5338c548e --- /dev/null +++ b/packages/demobank-ui/src/components/app.tsx @@ -0,0 +1,14 @@ +import { FunctionalComponent, h } from 'preact'; +import { TranslationProvider } from '../context/translation'; +import { BankHome } from '../pages/home/index'; +import { Menu } from './menu'; + +const App: FunctionalComponent = () => { + return ( + <TranslationProvider> + <BankHome /> + </TranslationProvider> + ); +}; + +export default App; diff --git a/packages/demobank-ui/src/components/fields/DateInput.tsx b/packages/demobank-ui/src/components/fields/DateInput.tsx new file mode 100644 index 000000000..06ec4b6a7 --- /dev/null +++ b/packages/demobank-ui/src/components/fields/DateInput.tsx @@ -0,0 +1,90 @@ +import { format, subYears } from 'date-fns'; +import { h, VNode } from 'preact'; +import { useLayoutEffect, useRef, useState } from 'preact/hooks'; +import { DatePicker } from '../picker/DatePicker'; + +export interface DateInputProps { + label: string; + grabFocus?: boolean; + tooltip?: string; + error?: string; + years?: Array<number>; + onConfirm?: () => void; + bind: [string, (x: string) => void]; +} + +export function DateInput(props: DateInputProps): VNode { + const inputRef = useRef<HTMLInputElement>(null); + useLayoutEffect(() => { + if (props.grabFocus) + inputRef.current?.focus(); + + }, [props.grabFocus]); + const [opened, setOpened] = useState(false); + + const value = props.bind[0] || ''; + const [dirty, setDirty] = useState(false); + const showError = dirty && props.error; + + const calendar = subYears(new Date(), 30); + + return ( + <div class="field"> + <label class="label"> + {props.label} + {props.tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={props.tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + <div class="control"> + <div class="field has-addons"> + <p class="control"> + <input + type="text" + class={showError ? 'input is-danger' : 'input'} + value={value} + onKeyPress={(e) => { + if (e.key === 'Enter' && props.onConfirm) + props.onConfirm() + + }} + onInput={(e) => { + const text = e.currentTarget.value; + setDirty(true); + props.bind[1](text); + }} + ref={inputRef} + /> + </p> + <p class="control"> + <a + class="button" + onClick={() => { + setOpened(true); + }} + > + <span class="icon"> + <i class="mdi mdi-calendar" /> + </span> + </a> + </p> + </div> + </div> + <p class="help">Using the format yyyy-mm-dd</p> + {showError && <p class="help is-danger">{props.error}</p>} + <DatePicker + opened={opened} + initialDate={calendar} + years={props.years} + closeFunction={() => setOpened(false)} + dateReceiver={(d) => { + setDirty(true); + const v = format(d, 'yyyy-MM-dd'); + props.bind[1](v); + }} + /> + </div> + ); +} diff --git a/packages/demobank-ui/src/components/fields/EmailInput.tsx b/packages/demobank-ui/src/components/fields/EmailInput.tsx new file mode 100644 index 000000000..8b64264ed --- /dev/null +++ b/packages/demobank-ui/src/components/fields/EmailInput.tsx @@ -0,0 +1,57 @@ +import { h, VNode } from 'preact'; +import { useLayoutEffect, useRef, useState } from 'preact/hooks'; + +export interface TextInputProps { + label: string; + grabFocus?: boolean; + error?: string; + placeholder?: string; + tooltip?: string; + onConfirm?: () => void; + bind: [string, (x: string) => void]; +} + +export function EmailInput(props: TextInputProps): VNode { + const inputRef = useRef<HTMLInputElement>(null); + useLayoutEffect(() => { + if (props.grabFocus) + inputRef.current?.focus(); + + }, [props.grabFocus]); + const value = props.bind[0]; + const [dirty, setDirty] = useState(false); + const showError = dirty && props.error; + return ( + <div class="field"> + <label class="label"> + {props.label} + {props.tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={props.tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + <div class="control has-icons-right"> + <input + value={value} + required + placeholder={props.placeholder} + type="email" + class={showError ? 'input is-danger' : 'input'} + onKeyPress={(e) => { + if (e.key === 'Enter' && props.onConfirm) + props.onConfirm() + + }} + onInput={(e) => { + setDirty(true); + props.bind[1]((e.target as HTMLInputElement).value); + }} + ref={inputRef} + style={{ display: 'block' }} + /> + </div> + {showError && <p class="help is-danger">{props.error}</p>} + </div> + ); +} diff --git a/packages/demobank-ui/src/components/fields/FileInput.tsx b/packages/demobank-ui/src/components/fields/FileInput.tsx new file mode 100644 index 000000000..17413b907 --- /dev/null +++ b/packages/demobank-ui/src/components/fields/FileInput.tsx @@ -0,0 +1,104 @@ +/* + 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 { useLayoutEffect, useRef, useState } from 'preact/hooks'; + +const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024; + +export interface FileTypeContent { + content: string; + type: string; + name: string; +} + +export interface FileInputProps { + label: string; + grabFocus?: boolean; + disabled?: boolean; + error?: string; + placeholder?: string; + tooltip?: string; + onChange: (v: FileTypeContent | undefined) => void; +} + +export function FileInput(props: FileInputProps): VNode { + const inputRef = useRef<HTMLInputElement>(null); + useLayoutEffect(() => { + if (props.grabFocus) + inputRef.current?.focus(); + + }, [props.grabFocus]); + + const fileInputRef = useRef<HTMLInputElement>(null); + const [sizeError, setSizeError] = useState(false); + return ( + <div class="field"> + <label class="label"> + <a class="button" onClick={(e) => fileInputRef.current?.click()}> + <div class="icon is-small "> + <i class="mdi mdi-folder" /> + </div> + <span> + {props.label} + </span> + </a> + {props.tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={props.tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + <div class="control"> + <input + ref={fileInputRef} + style={{ display: 'none' }} + type="file" + // name={String(name)} + onChange={(e) => { + const f: FileList | null = e.currentTarget.files; + if (!f || f.length != 1) + return props.onChange(undefined); + + console.log(f) + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true); + return props.onChange(undefined); + } + setSizeError(false); + return f[0].arrayBuffer().then((b) => { + const b64 = btoa( + new Uint8Array(b).reduce( + (data, byte) => data + String.fromCharCode(byte), + '', + ), + ); + return props.onChange({content: `data:${f[0].type};base64,${b64}`, name: f[0].name, type: f[0].type}); + }); + }} + /> + {props.error && <p class="help is-danger">{props.error}</p>} + {sizeError && ( + <p class="help is-danger">File should be smaller than 1 MB</p> + )} + </div> + </div> + ); +} diff --git a/packages/demobank-ui/src/components/fields/ImageInput.tsx b/packages/demobank-ui/src/components/fields/ImageInput.tsx new file mode 100644 index 000000000..98457af21 --- /dev/null +++ b/packages/demobank-ui/src/components/fields/ImageInput.tsx @@ -0,0 +1,93 @@ +/* + 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 { useLayoutEffect, useRef, useState } from 'preact/hooks'; +import emptyImage from '../../assets/empty.png'; +import { TextInputProps } from './TextInput'; + +const MAX_IMAGE_UPLOAD_SIZE = 1024 * 1024; + +export function ImageInput(props: TextInputProps): VNode { + const inputRef = useRef<HTMLInputElement>(null); + useLayoutEffect(() => { + if (props.grabFocus) + inputRef.current?.focus(); + + }, [props.grabFocus]); + + const value = props.bind[0]; + // const [dirty, setDirty] = useState(false) + const image = useRef<HTMLInputElement>(null); + const [sizeError, setSizeError] = useState(false); + function onChange(v: string): void { + // setDirty(true); + props.bind[1](v); + } + return ( + <div class="field"> + <label class="label"> + {props.label} + {props.tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={props.tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + <div class="control"> + <img + src={!value ? emptyImage : value} + style={{ width: 200, height: 200 }} + onClick={() => image.current?.click()} + /> + <input + ref={image} + style={{ display: 'none' }} + type="file" + name={String(name)} + onChange={(e) => { + const f: FileList | null = e.currentTarget.files; + if (!f || f.length != 1) + return onChange(emptyImage); + + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true); + return onChange(emptyImage); + } + setSizeError(false); + return f[0].arrayBuffer().then((b) => { + const b64 = btoa( + new Uint8Array(b).reduce( + (data, byte) => data + String.fromCharCode(byte), + '', + ), + ); + return onChange(`data:${f[0].type};base64,${b64}` as any); + }); + }} + /> + {props.error && <p class="help is-danger">{props.error}</p>} + {sizeError && ( + <p class="help is-danger">Image should be smaller than 1 MB</p> + )} + </div> + </div> + ); +} diff --git a/packages/demobank-ui/src/components/fields/NumberInput.tsx b/packages/demobank-ui/src/components/fields/NumberInput.tsx new file mode 100644 index 000000000..881c61c57 --- /dev/null +++ b/packages/demobank-ui/src/components/fields/NumberInput.tsx @@ -0,0 +1,56 @@ +import { h, VNode } from 'preact'; +import { useLayoutEffect, useRef, useState } from 'preact/hooks'; + +export interface TextInputProps { + label: string; + grabFocus?: boolean; + error?: string; + placeholder?: string; + tooltip?: string; + onConfirm?: () => void; + bind: [string, (x: string) => void]; +} + +export function PhoneNumberInput(props: TextInputProps): VNode { + const inputRef = useRef<HTMLInputElement>(null); + useLayoutEffect(() => { + if (props.grabFocus) + inputRef.current?.focus(); + + }, [props.grabFocus]); + const value = props.bind[0]; + const [dirty, setDirty] = useState(false); + const showError = dirty && props.error; + return ( + <div class="field"> + <label class="label"> + {props.label} + {props.tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={props.tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + <div class="control has-icons-right"> + <input + value={value} + type="tel" + placeholder={props.placeholder} + class={showError ? 'input is-danger' : 'input'} + onKeyPress={(e) => { + if (e.key === 'Enter' && props.onConfirm) + props.onConfirm() + + }} + onInput={(e) => { + setDirty(true); + props.bind[1]((e.target as HTMLInputElement).value); + }} + ref={inputRef} + style={{ display: 'block' }} + /> + </div> + {showError && <p class="help is-danger">{props.error}</p>} + </div> + ); +} diff --git a/packages/demobank-ui/src/components/fields/TextInput.tsx b/packages/demobank-ui/src/components/fields/TextInput.tsx new file mode 100644 index 000000000..5cc9f32ad --- /dev/null +++ b/packages/demobank-ui/src/components/fields/TextInput.tsx @@ -0,0 +1,68 @@ +import { h, VNode } from 'preact'; +import { useLayoutEffect, useRef, useState } from 'preact/hooks'; + +export interface TextInputProps { + inputType?: 'text' | 'number' | 'multiline' | 'password'; + label: string; + grabFocus?: boolean; + disabled?: boolean; + error?: string; + placeholder?: string; + tooltip?: string; + onConfirm?: () => void; + bind: [string, (x: string) => void]; +} + +const TextInputType = function ({ inputType, grabFocus, ...rest }: any): VNode { + const inputRef = useRef<HTMLInputElement>(null); + useLayoutEffect(() => { + if (grabFocus) + inputRef.current?.focus(); + + }, [grabFocus]); + + return inputType === 'multiline' ? ( + <textarea {...rest} rows={5} ref={inputRef} style={{ height: 'unset' }} /> + ) : ( + <input {...rest} type={inputType} ref={inputRef} /> + ); +}; + +export function TextInput(props: TextInputProps): VNode { + const value = props.bind[0]; + const [dirty, setDirty] = useState(false); + const showError = dirty && props.error; + return ( + <div class="field"> + <label class="label"> + {props.label} + {props.tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={props.tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + <div class="control has-icons-right"> + <TextInputType + inputType={props.inputType} + value={value} + grabFocus={props.grabFocus} + disabled={props.disabled} + placeholder={props.placeholder} + class={showError ? 'input is-danger' : 'input'} + onKeyPress={(e: any) => { + if (e.key === 'Enter' && props.onConfirm) + props.onConfirm(); + + }} + onInput={(e: any) => { + setDirty(true); + props.bind[1]((e.target as HTMLInputElement).value); + }} + style={{ display: 'block' }} + /> + </div> + {showError && <p class="help is-danger">{props.error}</p>} + </div> + ); +} diff --git a/packages/demobank-ui/src/components/menu/LangSelector.tsx b/packages/demobank-ui/src/components/menu/LangSelector.tsx new file mode 100644 index 000000000..221237a5b --- /dev/null +++ b/packages/demobank-ui/src/components/menu/LangSelector.tsx @@ -0,0 +1,101 @@ +/* + 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, Fragment } from 'preact'; +import { useCallback, useEffect, useState } from 'preact/hooks'; +import langIcon from '../../assets/icons/languageicon.svg'; +import { useTranslationContext } from '../../context/translation'; +import { strings as messages } from '../../i18n/strings'; + +type LangsNames = { + [P in keyof typeof messages]: string; +}; + +const names: LangsNames = { + es: 'Español [es]', + en: 'English [en]', + fr: 'Français [fr]', + de: 'Deutsch [de]', + sv: 'Svenska [sv]', + it: 'Italiano [it]', +}; + +function getLangName(s: keyof LangsNames | string): string { + if (names[s]) return names[s]; + return String(s); +} + +// FIXME: explain "like py". +export function LangSelectorLikePy(): VNode { + const [updatingLang, setUpdatingLang] = useState(false); + const { lang, changeLanguage } = useTranslationContext(); + const [hidden, setHidden] = useState(true) + useEffect(() => { + function bodyKeyPress(event:KeyboardEvent) { + if (event.code === 'Escape') + setHidden(true); + + } + function bodyOnClick(event:Event) { + setHidden(true); + } + document.body.addEventListener('click', bodyOnClick) + document.body.addEventListener('keydown', bodyKeyPress as any) + return () => { + document.body.removeEventListener('keydown', bodyKeyPress as any) + document.body.removeEventListener('click', bodyOnClick) + } + },[]) + return ( + <Fragment> + <button name="language" onClick={(ev) => { + setHidden(h => !h); + ev.stopPropagation(); + }}> + {getLangName(lang)} + </button> + <div id="lang" class={hidden ? 'hide' : ''}> + <div style="position: relative; overflow: visible;"> + <div + class="nav" + style="position: absolute; max-height: 60vh; overflow-y: scroll"> + {Object.keys(messages) + .filter((l) => l !== lang) + .map((l) => ( + <a + key={l} + href="#" + class="navbtn langbtn" + value={l} + onClick={() => { + changeLanguage(l); + setUpdatingLang(false); + }}> + {getLangName(l)} + </a> + ))} + <br /> + </div> + </div> + </div> + </Fragment> + ); +} diff --git a/packages/demobank-ui/src/components/menu/NavigationBar.tsx b/packages/demobank-ui/src/components/menu/NavigationBar.tsx new file mode 100644 index 000000000..9e540213d --- /dev/null +++ b/packages/demobank-ui/src/components/menu/NavigationBar.tsx @@ -0,0 +1,53 @@ +/* + 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 logo from '../../assets/logo.jpeg'; +import { LangSelectorLikePy as LangSelector } from './LangSelector'; + +interface Props { + onMobileMenu: () => void; + title: string; +} + +export function NavigationBar({ onMobileMenu, title }: Props): VNode { + return ( + <nav + class="navbar is-fixed-top" + role="navigation" + aria-label="main navigation" + > + <div class="navbar-brand"> + <span class="navbar-item" style={{ fontSize: 24, fontWeight: 900 }}> + {title} + </span> + </div> + + <div class="navbar-menu "> + <div class="navbar-end"> + <div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}> + {/* <LangSelector /> */} + </div> + </div> + </div> + </nav> + ); +} diff --git a/packages/demobank-ui/src/components/menu/SideBar.tsx b/packages/demobank-ui/src/components/menu/SideBar.tsx new file mode 100644 index 000000000..7f9981a1c --- /dev/null +++ b/packages/demobank-ui/src/components/menu/SideBar.tsx @@ -0,0 +1,73 @@ +/* + 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 { Translate } from '../../i18n'; + +interface Props { + mobile?: boolean; +} + +export function Sidebar({ mobile }: Props): VNode { + // const config = useConfigContext(); + const config = { version: 'none' }; + // FIXME: add replacement for __VERSION__ with the current version + const process = { env: { __VERSION__: '0.0.0' } }; + + return ( + <aside class="aside is-placed-left is-expanded"> + <div class="aside-tools"> + <div class="aside-tools-label"> + <div> + <b>euFin bank</b> + </div> + <div + class="is-size-7 has-text-right" + style={{ lineHeight: 0, marginTop: -10 }} + > + Version {process.env.__VERSION__} ({config.version}) + </div> + </div> + </div> + <div class="menu is-menu-main"> + <p class="menu-label"> + <Translate>Bank menu</Translate> + </p> + <ul class="menu-list"> + <li> + <div class="ml-4"> + <span class="menu-item-label"> + <Translate>Select option1</Translate> + </span> + </div> + </li> + <li> + <div class="ml-4"> + <span class="menu-item-label"> + <Translate>Select option2</Translate> + </span> + </div> + </li> + </ul> + </div> + </aside> + ); +} diff --git a/packages/demobank-ui/src/components/menu/index.tsx b/packages/demobank-ui/src/components/menu/index.tsx new file mode 100644 index 000000000..07e1c5265 --- /dev/null +++ b/packages/demobank-ui/src/components/menu/index.tsx @@ -0,0 +1,135 @@ +/* + 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/> + */ + +import { ComponentChildren, Fragment, h, VNode } from 'preact'; +import Match from 'preact-router/match'; +import { useEffect, useState } from 'preact/hooks'; +import { NavigationBar } from './NavigationBar'; +import { Sidebar } from './SideBar'; + +interface MenuProps { + title: string; +} + +function WithTitle({ + title, + children, +}: { + title: string; + children: ComponentChildren; +}): VNode { + useEffect(() => { + document.title = `${title}`; + }, [title]); + return <Fragment>{children}</Fragment>; +} + +export function Menu({ title }: MenuProps): VNode { + const [mobileOpen, setMobileOpen] = useState(false); + + return ( + <Match> + {({ path }: { path: string }) => { + const titleWithSubtitle = title; // title ? title : (!admin ? getInstanceTitle(path, instance) : getAdminTitle(path, instance)) + return ( + <WithTitle title={titleWithSubtitle}> + <div + class={mobileOpen ? 'has-aside-mobile-expanded' : ''} + onClick={() => setMobileOpen(false)} + > + <NavigationBar + onMobileMenu={() => setMobileOpen(!mobileOpen)} + title={titleWithSubtitle} + /> + + <Sidebar mobile={mobileOpen} /> + </div> + </WithTitle> + ); + }} + </Match> + ); +} + +interface NotYetReadyAppMenuProps { + title: string; + onLogout?: () => void; +} + +interface NotifProps { + notification?: Notification; +} +export function NotificationCard({ + notification: n, +}: NotifProps): VNode | null { + if (!n) return null; + return ( + <div class="notification"> + <div class="columns is-vcentered"> + <div class="column is-12"> + <article + class={ + n.type === 'ERROR' + ? 'message is-danger' + : n.type === 'WARN' + ? 'message is-warning' + : 'message is-info' + } + > + <div class="message-header"> + <p>{n.message}</p> + </div> + {n.description && <div class="message-body">{n.description}</div>} + </article> + </div> + </div> + </div> + ); +} + +export function NotYetReadyAppMenu({ + onLogout, + title, +}: NotYetReadyAppMenuProps): VNode { + const [mobileOpen, setMobileOpen] = useState(false); + + useEffect(() => { + document.title = `Taler Backoffice: ${title}`; + }, [title]); + + return ( + <div + class="has-aside-mobile-expanded" + // class={mobileOpen ? "has-aside-mobile-expanded" : ""} + onClick={() => setMobileOpen(false)} + > + <NavigationBar + onMobileMenu={() => setMobileOpen(!mobileOpen)} + title={title} + /> + {onLogout && <Sidebar mobile={mobileOpen} />} + </div> + ); +} + +export interface Notification { + message: string; + description?: string | VNode; + type: MessageType; +} + +export type ValueOrFunction<T> = T | ((p: T) => T); +export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'; diff --git a/packages/demobank-ui/src/components/picker/DatePicker.tsx b/packages/demobank-ui/src/components/picker/DatePicker.tsx new file mode 100644 index 000000000..94dbc9458 --- /dev/null +++ b/packages/demobank-ui/src/components/picker/DatePicker.tsx @@ -0,0 +1,356 @@ +/* + 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, Component } from 'preact'; + +interface Props { + closeFunction?: () => void; + dateReceiver?: (d: Date) => void; + initialDate?: Date; + years?: Array<number>; + opened?: boolean; +} +interface State { + displayedMonth: number; + displayedYear: number; + selectYearMode: boolean; + currentDate: Date; +} +const now = new Date(); + +const monthArrShortFull = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', +]; + +const monthArrShort = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', +]; + +const dayArr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + +const yearArr: number[] = []; + +// inspired by https://codepen.io/m4r1vs/pen/MOOxyE +export class DatePicker extends Component<Props, State> { + closeDatePicker() { + this.props.closeFunction && this.props.closeFunction(); // Function gets passed by parent + } + + /** + * Gets fired when a day gets clicked. + * @param {object} e The event thrown by the <span /> element clicked + */ + dayClicked(e: any) { + const element = e.target; // the actual element clicked + + if (element.innerHTML === '') return false; // don't continue if <span /> empty + + // get date from clicked element (gets attached when rendered) + const date = new Date(element.getAttribute('data-value')); + + // update the state + this.setState({ currentDate: date }); + this.passDateToParent(date); + } + + /** + * returns days in month as array + * @param {number} month the month to display + * @param {number} year the year to display + */ + getDaysByMonth(month: number, year: number) { + const calendar = []; + + const date = new Date(year, month, 1); // month to display + + const firstDay = new Date(year, month, 1).getDay(); // first weekday of month + const lastDate = new Date(year, month + 1, 0).getDate(); // last date of month + + let day: number | null = 0; + + // the calendar is 7*6 fields big, so 42 loops + for (let i = 0; i < 42; i++) { + if (i >= firstDay && day !== null) day = day + 1; + if (day !== null && day > lastDate) day = null; + + // append the calendar Array + calendar.push({ + day: day === 0 || day === null ? null : day, // null or number + date: day === 0 || day === null ? null : new Date(year, month, day), // null or Date() + today: + day === now.getDate() && + month === now.getMonth() && + year === now.getFullYear(), // boolean + }); + } + + return calendar; + } + + /** + * Display previous month by updating state + */ + displayPrevMonth() { + if (this.state.displayedMonth <= 0) + this.setState({ + displayedMonth: 11, + displayedYear: this.state.displayedYear - 1, + }); + else + this.setState({ + displayedMonth: this.state.displayedMonth - 1, + }); + + } + + /** + * Display next month by updating state + */ + displayNextMonth() { + if (this.state.displayedMonth >= 11) + this.setState({ + displayedMonth: 0, + displayedYear: this.state.displayedYear + 1, + }); + else + this.setState({ + displayedMonth: this.state.displayedMonth + 1, + }); + + } + + /** + * Display the selected month (gets fired when clicking on the date string) + */ + displaySelectedMonth() { + if (this.state.selectYearMode) + this.toggleYearSelector(); + else { + if (!this.state.currentDate) return false; + this.setState({ + displayedMonth: this.state.currentDate.getMonth(), + displayedYear: this.state.currentDate.getFullYear(), + }); + } + } + + toggleYearSelector() { + this.setState({ selectYearMode: !this.state.selectYearMode }); + } + + changeDisplayedYear(e: any) { + const element = e.target; + this.toggleYearSelector(); + this.setState({ + displayedYear: parseInt(element.innerHTML, 10), + displayedMonth: 0, + }); + } + + /** + * Pass the selected date to parent when 'OK' is clicked + */ + passSavedDateDateToParent() { + this.passDateToParent(this.state.currentDate); + } + passDateToParent(date: Date) { + if (typeof this.props.dateReceiver === 'function') + this.props.dateReceiver(date); + this.closeDatePicker(); + } + + componentDidUpdate() { + // if (this.state.selectYearMode) { + // document.getElementsByClassName('selected')[0].scrollIntoView(); // works in every browser incl. IE, replace with scrollIntoViewIfNeeded when browsers support it + // } + } + + constructor(props: any) { + super(props); + + this.closeDatePicker = this.closeDatePicker.bind(this); + this.dayClicked = this.dayClicked.bind(this); + this.displayNextMonth = this.displayNextMonth.bind(this); + this.displayPrevMonth = this.displayPrevMonth.bind(this); + this.getDaysByMonth = this.getDaysByMonth.bind(this); + this.changeDisplayedYear = this.changeDisplayedYear.bind(this); + this.passDateToParent = this.passDateToParent.bind(this); + this.toggleYearSelector = this.toggleYearSelector.bind(this); + this.displaySelectedMonth = this.displaySelectedMonth.bind(this); + + const initial = props.initialDate || now; + + this.state = { + currentDate: initial, + displayedMonth: initial.getMonth(), + displayedYear: initial.getFullYear(), + selectYearMode: false, + }; + } + + render() { + const { + currentDate, + displayedMonth, + displayedYear, + selectYearMode, + } = this.state; + + return ( + <div> + <div class={`datePicker ${this.props.opened && 'datePicker--opened'}`}> + <div class="datePicker--titles"> + <h3 + style={{ + color: selectYearMode + ? 'rgba(255,255,255,.87)' + : 'rgba(255,255,255,.57)', + }} + onClick={this.toggleYearSelector} + > + {currentDate.getFullYear()} + </h3> + <h2 + style={{ + color: !selectYearMode + ? 'rgba(255,255,255,.87)' + : 'rgba(255,255,255,.57)', + }} + onClick={this.displaySelectedMonth} + > + {dayArr[currentDate.getDay()]},{' '} + {monthArrShort[currentDate.getMonth()]} {currentDate.getDate()} + </h2> + </div> + + {!selectYearMode && ( + <nav> + <span onClick={this.displayPrevMonth} class="icon"> + <i + style={{ transform: 'rotate(180deg)' }} + class="mdi mdi-forward" + /> + </span> + <h4> + {monthArrShortFull[displayedMonth]} {displayedYear} + </h4> + <span onClick={this.displayNextMonth} class="icon"> + <i class="mdi mdi-forward" /> + </span> + </nav> + )} + + <div class="datePicker--scroll"> + {!selectYearMode && ( + <div class="datePicker--calendar"> + <div class="datePicker--dayNames"> + {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day, i) => ( + <span key={i}>{day}</span> + ))} + </div> + + <div onClick={this.dayClicked} class="datePicker--days"> + {/* + Loop through the calendar object returned by getDaysByMonth(). + */} + + {this.getDaysByMonth( + this.state.displayedMonth, + this.state.displayedYear, + ).map((day) => { + let selected = false; + + if (currentDate && day.date) + selected = + currentDate.toLocaleDateString() === + day.date.toLocaleDateString(); + + return ( + <span + key={day.day} + class={ + (day.today ? 'datePicker--today ' : '') + + (selected ? 'datePicker--selected' : '') + } + disabled={!day.date} + data-value={day.date} + > + {day.day} + </span> + ); + })} + </div> + </div> + )} + + {selectYearMode && ( + <div class="datePicker--selectYear"> + {(this.props.years || yearArr).map((year) => ( + <span + key={year} + class={year === displayedYear ? 'selected' : ''} + onClick={this.changeDisplayedYear} + > + {year} + </span> + ))} + </div> + )} + </div> + </div> + + <div + class="datePicker--background" + onClick={this.closeDatePicker} + style={{ + display: this.props.opened ? 'block' : 'none', + }} + /> + </div> + ); + } +} + +for (let i = 2010; i <= now.getFullYear() + 10; i++) + yearArr.push(i); + diff --git a/packages/demobank-ui/src/components/picker/DurationPicker.stories.tsx b/packages/demobank-ui/src/components/picker/DurationPicker.stories.tsx new file mode 100644 index 000000000..5e9930522 --- /dev/null +++ b/packages/demobank-ui/src/components/picker/DurationPicker.stories.tsx @@ -0,0 +1,55 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { useState } from 'preact/hooks'; +import { DurationPicker as TestedComponent } from './DurationPicker'; + +export default { + title: 'Components/Picker/Duration', + component: TestedComponent, + argTypes: { + onCreate: { action: 'onCreate' }, + goBack: { action: 'goBack' }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props>, +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const Example = createExample(TestedComponent, { + days: true, + minutes: true, + hours: true, + seconds: true, + value: 10000000, +}); + +export const WithState = () => { + const [v, s] = useState<number>(1000000); + return <TestedComponent value={v} onChange={s} days minutes hours seconds />; +}; diff --git a/packages/demobank-ui/src/components/picker/DurationPicker.tsx b/packages/demobank-ui/src/components/picker/DurationPicker.tsx new file mode 100644 index 000000000..542ff2f01 --- /dev/null +++ b/packages/demobank-ui/src/components/picker/DurationPicker.tsx @@ -0,0 +1,211 @@ +/* + 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 { useState } from 'preact/hooks'; +import { useTranslator } from '../../i18n'; +import '../../scss/DurationPicker.scss'; + +export interface Props { + hours?: boolean; + minutes?: boolean; + seconds?: boolean; + days?: boolean; + onChange: (value: number) => void; + value: number; +} + +// inspiration taken from https://github.com/flurmbo/react-duration-picker +export function DurationPicker({ + days, + hours, + minutes, + seconds, + onChange, + value, +}: Props): VNode { + const ss = 1000; + const ms = ss * 60; + const hs = ms * 60; + const ds = hs * 24; + const i18n = useTranslator(); + + return ( + <div class="rdp-picker"> + {days && ( + <DurationColumn + unit={i18n`days`} + max={99} + value={Math.floor(value / ds)} + onDecrease={value >= ds ? () => onChange(value - ds) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ds) : undefined} + onChange={(diff) => onChange(value + diff * ds)} + /> + )} + {hours && ( + <DurationColumn + unit={i18n`hours`} + max={23} + min={1} + value={Math.floor(value / hs) % 24} + onDecrease={value >= hs ? () => onChange(value - hs) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + hs) : undefined} + onChange={(diff) => onChange(value + diff * hs)} + /> + )} + {minutes && ( + <DurationColumn + unit={i18n`minutes`} + max={59} + min={1} + value={Math.floor(value / ms) % 60} + onDecrease={value >= ms ? () => onChange(value - ms) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ms) : undefined} + onChange={(diff) => onChange(value + diff * ms)} + /> + )} + {seconds && ( + <DurationColumn + unit={i18n`seconds`} + max={59} + value={Math.floor(value / ss) % 60} + onDecrease={value >= ss ? () => onChange(value - ss) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ss) : undefined} + onChange={(diff) => onChange(value + diff * ss)} + /> + )} + </div> + ); +} + +interface ColProps { + unit: string; + min?: number; + max: number; + value: number; + onIncrease?: () => void; + onDecrease?: () => void; + onChange?: (diff: number) => void; +} + +function InputNumber({ + initial, + onChange, +}: { + initial: number; + onChange: (n: number) => void; +}) { + const [value, handler] = useState<{ v: string }>({ + v: toTwoDigitString(initial), + }); + + return ( + <input + value={value.v} + onBlur={(e) => onChange(parseInt(value.v, 10))} + onInput={(e) => { + e.preventDefault(); + const n = Number.parseInt(e.currentTarget.value, 10); + if (isNaN(n)) return handler({ v: toTwoDigitString(initial) }); + return handler({ v: toTwoDigitString(n) }); + }} + style={{ + width: 50, + border: 'none', + fontSize: 'inherit', + background: 'inherit', + }} + /> + ); +} + +function DurationColumn({ + unit, + min = 0, + max, + value, + onIncrease, + onDecrease, + onChange, +}: ColProps): VNode { + const cellHeight = 35; + return ( + <div class="rdp-column-container"> + <div class="rdp-masked-div"> + <hr class="rdp-reticule" style={{ top: cellHeight * 2 - 1 }} /> + <hr class="rdp-reticule" style={{ top: cellHeight * 3 - 1 }} /> + + <div class="rdp-column" style={{ top: 0 }}> + <div class="rdp-cell" key={value - 2}> + {onDecrease && ( + <button + style={{ width: '100%', textAlign: 'center', margin: 5 }} + onClick={onDecrease} + > + <span class="icon"> + <i class="mdi mdi-chevron-up" /> + </span> + </button> + )} + </div> + <div class="rdp-cell" key={value - 1}> + {value > min ? toTwoDigitString(value - 1) : ''} + </div> + <div class="rdp-cell rdp-center" key={value}> + {onChange ? ( + <InputNumber + initial={value} + onChange={(n) => onChange(n - value)} + /> + ) : ( + toTwoDigitString(value) + )} + <div>{unit}</div> + </div> + + <div class="rdp-cell" key={value + 1}> + {value < max ? toTwoDigitString(value + 1) : ''} + </div> + + <div class="rdp-cell" key={value + 2}> + {onIncrease && ( + <button + style={{ width: '100%', textAlign: 'center', margin: 5 }} + onClick={onIncrease} + > + <span class="icon"> + <i class="mdi mdi-chevron-down" /> + </span> + </button> + )} + </div> + </div> + </div> + </div> + ); +} + +function toTwoDigitString(n: number) { + if (n < 10) + return `0${n}`; + + return `${n}`; +} diff --git a/packages/demobank-ui/src/context/translation.ts b/packages/demobank-ui/src/context/translation.ts new file mode 100644 index 000000000..1879fe43e --- /dev/null +++ b/packages/demobank-ui/src/context/translation.ts @@ -0,0 +1,73 @@ +/* + 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 { createContext, h, VNode } from 'preact'; +import { useContext, useEffect } from 'preact/hooks'; +import { useLang } from '../hooks'; +import * as jedLib from 'jed'; +import { strings } from '../i18n/strings'; + +interface Type { + lang: string; + handler: any; + changeLanguage: (l: string) => void; +} +const initial = { + lang: 'en', + handler: null, + changeLanguage: () => { + /** + * This function will be replaced by one with + * the same signature _but_ coming from the state. + * FIXME: clarify this design. + */ + }, +}; +const Context = createContext<Type>(initial); + +interface Props { + initial?: string; + children: any; + forceLang?: string; +} + +// Outmost UI wrapper. +export const TranslationProvider = ({ + initial, + children, + forceLang, +}: Props): VNode => { + + const [lang, changeLanguage] = useLang(initial); + useEffect(() => { + if (forceLang) + changeLanguage(forceLang); + + }); + console.log('lang store', strings); + const handler = new jedLib.Jed(strings[lang] || strings['en']); + return h(Context.Provider, { + value: { lang, handler, changeLanguage }, + children, + }); +}; + +export const useTranslationContext = (): Type => useContext(Context); diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts new file mode 100644 index 000000000..73bccac71 --- /dev/null +++ b/packages/demobank-ui/src/declaration.d.ts @@ -0,0 +1,20 @@ +declare module '*.css' { + const mapping: Record<string, string>; + export default mapping; +} +declare module '*.svg' { + const content: any; + export default content; +} +declare module '*.jpeg' { + const content: any; + export default content; +} +declare module '*.png' { + const content: any; + export default content; +} +declare module 'jed' { + const x: any; + export = x; +} diff --git a/packages/demobank-ui/src/hooks/async.ts b/packages/demobank-ui/src/hooks/async.ts new file mode 100644 index 000000000..be41b4e07 --- /dev/null +++ b/packages/demobank-ui/src/hooks/async.ts @@ -0,0 +1,80 @@ +/* + 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 { useState } from 'preact/hooks'; +// import { cancelPendingRequest } from "./backend"; + +export interface Options { + slowTolerance: number; +} + +export interface AsyncOperationApi<T> { + request: (...a: any) => void; + cancel: () => void; + data: T | undefined; + isSlow: boolean; + isLoading: boolean; + error: string | undefined; +} + +export function useAsync<T>( + fn?: (...args: any) => Promise<T>, + { slowTolerance: tooLong }: Options = { slowTolerance: 1000 }, +): AsyncOperationApi<T> { + const [data, setData] = useState<T | undefined>(undefined); + const [isLoading, setLoading] = useState<boolean>(false); + const [error, setError] = useState<any>(undefined); + const [isSlow, setSlow] = useState(false); + + const request = async (...args: any) => { + if (!fn) return; + setLoading(true); + const handler = setTimeout(() => { + setSlow(true); + }, tooLong); + + try { + console.log('calling async', args); + const result = await fn(...args); + console.log('async back', result); + setData(result); + } catch (error) { + setError(error); + } + setLoading(false); + setSlow(false); + clearTimeout(handler); + }; + + function cancel() { + // cancelPendingRequest() + setLoading(false); + setSlow(false); + } + + return { + request, + cancel, + data, + isSlow, + isLoading, + error, + }; +} diff --git a/packages/demobank-ui/src/hooks/index.ts b/packages/demobank-ui/src/hooks/index.ts new file mode 100644 index 000000000..795df909d --- /dev/null +++ b/packages/demobank-ui/src/hooks/index.ts @@ -0,0 +1,151 @@ +/* + 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 { StateUpdater, useState } from 'preact/hooks'; +export type ValueOrFunction<T> = T | ((p: T) => T); + +const calculateRootPath = () => { + const rootPath = + typeof window !== undefined + ? window.location.origin + window.location.pathname + : '/'; + return rootPath; +}; + +export function useBackendURL( + url?: string, +): [string, boolean, StateUpdater<string>, () => void] { + const [value, setter] = useNotNullLocalStorage( + 'backend-url', + url || calculateRootPath(), + ); + const [triedToLog, setTriedToLog] = useLocalStorage('tried-login'); + + const checkedSetter = (v: ValueOrFunction<string>) => { + setTriedToLog('yes'); + return setter((p) => (v instanceof Function ? v(p) : v).replace(/\/$/, '')); + }; + + const resetBackend = () => { + setTriedToLog(undefined); + }; + return [value, !!triedToLog, checkedSetter, resetBackend]; +} + +export function useBackendDefaultToken(): [ + string | undefined, + StateUpdater<string | undefined>, + ] { + return useLocalStorage('backend-token'); +} + +export function useBackendInstanceToken( + id: string, +): [string | undefined, StateUpdater<string | undefined>] { + const [token, setToken] = useLocalStorage(`backend-token-${id}`); + const [defaultToken, defaultSetToken] = useBackendDefaultToken(); + + // instance named 'default' use the default token + if (id === 'default') + return [defaultToken, defaultSetToken]; + + return [token, setToken]; +} + +export function useLang(initial?: string): [string, StateUpdater<string>] { + const browserLang = + typeof window !== 'undefined' + ? navigator.language || (navigator as any).userLanguage + : undefined; + const defaultLang = (browserLang || initial || 'en').substring(0, 2); + const [value, setValue] = useNotNullLocalStorage('lang-preference', defaultLang); + function updateValue(newValue: (string | ((v: string) => string))) { + if (document.body.parentElement) { + const htmlElement = document.body.parentElement + if (typeof newValue === 'string') { + htmlElement.lang = newValue; + setValue(newValue) + } else if (typeof newValue === 'function') + setValue((old) => { + const nv = newValue(old) + htmlElement.lang = nv; + return nv + }) + } else setValue(newValue) + } + return [value, updateValue] +} + +export function useLocalStorage( + key: string, + initialValue?: string, +): [string | undefined, StateUpdater<string | undefined>] { + const [storedValue, setStoredValue] = useState<string | undefined>((): + | string + | undefined => { + return typeof window !== 'undefined' + ? window.localStorage.getItem(key) || initialValue + : initialValue; + }); + + const setValue = ( + value?: string | ((val?: string) => string | undefined), + ) => { + setStoredValue((p) => { + const toStore = value instanceof Function ? value(p) : value; + if (typeof window !== 'undefined') + if (!toStore) + window.localStorage.removeItem(key); + else + window.localStorage.setItem(key, toStore); + + + return toStore; + }); + }; + + return [storedValue, setValue]; +} + +export function useNotNullLocalStorage( + key: string, + initialValue: string, +): [string, StateUpdater<string>] { + const [storedValue, setStoredValue] = useState<string>((): string => { + return typeof window !== 'undefined' + ? window.localStorage.getItem(key) || initialValue + : initialValue; + }); + + const setValue = (value: string | ((val: string) => string)) => { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + if (typeof window !== 'undefined') + if (!valueToStore) + window.localStorage.removeItem(key); + else + window.localStorage.setItem(key, valueToStore); + + + }; + + return [storedValue, setValue]; +} diff --git a/packages/demobank-ui/src/i18n/bank.pot b/packages/demobank-ui/src/i18n/bank.pot new file mode 100644 index 000000000..dcaba009d --- /dev/null +++ b/packages/demobank-ui/src/i18n/bank.pot @@ -0,0 +1,258 @@ +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:55 +#, c-format +msgid "days" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:65 +#, c-format +msgid "hours" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:76 +#, c-format +msgid "minutes" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:87 +#, c-format +msgid "seconds" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:734 +#, c-format +msgid "Clear" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:761 +#, c-format +msgid "Logout" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:782 +#, c-format +msgid "Demo Bank" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:837 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:840 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1189 +#, c-format +msgid "Go back" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:845 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:906 +#, c-format +msgid "Wire transfer" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:846 +#, c-format +msgid "Transfer money to another account of this bank:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:897 +#, c-format +msgid "Want to try the raw payto://-format?" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:907 +#, c-format +msgid "Transfer money via the Payto system:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:916 +#, c-format +msgid "payto address" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:926 +#, c-format +msgid "Confirm" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:956 +#, c-format +msgid "Confirm Withdrawal" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1026 +#, c-format +msgid "Waiting the bank to create the operaion..." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1044 +#, c-format +msgid "This withdrawal was aborted!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1051 +#, c-format +msgid "Withdraw to a Taler Wallet" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1052 +#, c-format +msgid "You can use this QR code to withdraw to your mobile wallet:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1054 +#, c-format +msgid "this link" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1060 +#, c-format +msgid "Abort" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1084 +#, c-format +msgid "Start withdrawal" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1101 +#, c-format +msgid "Withdraw Money into a Taler wallet" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1105 +#, c-format +msgid "Amount to withdraw" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1137 +#, c-format +msgid "Please login!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1169 +#, c-format +msgid "Login" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1184 +#, c-format +msgid "Register to the euFin bank!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1194 +#, c-format +msgid "Registration form" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1232 +#, c-format +msgid "Register" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1272 +#, c-format +msgid "Date" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1273 +#, c-format +msgid "Amount" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1274 +#, c-format +msgid "Counterpart" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1275 +#, c-format +msgid "Subject" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1343 +#, c-format +msgid "Username or account label '%1$s' not found. Won't login." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1365 +#, c-format +msgid "Wrong credentials given." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1374 +#, c-format +msgid "Account information could not be retrieved." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1394 +#, c-format +msgid "Close wire transfer" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1412 +#, c-format +msgid "Close Taler withdrawal" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1457 +#, c-format +msgid "Bank account balance:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1469 +#, c-format +msgid "Latest transactions:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1474 +#, c-format +msgid "Transfer money manually" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1543 +#, c-format +msgid "List of public accounts was not found." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1552 +#, c-format +msgid "List of public accounts could not be retrieved." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1584 +#, c-format +msgid "History of public accounts" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1643 +#, c-format +msgid "Page has a problem: logged in but backend state is lost." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1667 +#, c-format +msgid "Welcome to the euFin bank!" +msgstr "" + +# 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/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" diff --git a/packages/demobank-ui/src/i18n/de.po b/packages/demobank-ui/src/i18n/de.po new file mode 100644 index 000000000..bd4158037 --- /dev/null +++ b/packages/demobank-ui/src/i18n/de.po @@ -0,0 +1,257 @@ +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:55 +#, c-format +msgid "days" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:65 +#, c-format +msgid "hours" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:76 +#, c-format +msgid "minutes" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:87 +#, c-format +msgid "seconds" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:734 +#, c-format +msgid "Clear" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:761 +#, c-format +msgid "Logout" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:782 +#, c-format +msgid "Demo Bank" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:837 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:840 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1189 +#, c-format +msgid "Go back" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:845 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:906 +#, c-format +msgid "Wire transfer" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:846 +#, c-format +msgid "Transfer money to another account of this bank:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:897 +#, c-format +msgid "Want to try the raw payto://-format?" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:907 +#, c-format +msgid "Transfer money via the Payto system:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:916 +#, c-format +msgid "payto address" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:926 +#, c-format +msgid "Confirm" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:956 +#, c-format +msgid "Confirm Withdrawal" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1026 +#, c-format +msgid "Waiting the bank to create the operaion..." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1044 +#, c-format +msgid "This withdrawal was aborted!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1051 +#, c-format +msgid "Withdraw to a Taler Wallet" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1052 +#, c-format +msgid "You can use this QR code to withdraw to your mobile wallet:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1054 +#, c-format +msgid "this link" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1060 +#, c-format +msgid "Abort" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1084 +#, c-format +msgid "Start withdrawal" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1101 +#, c-format +msgid "Withdraw Money into a Taler wallet" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1105 +#, c-format +msgid "Amount to withdraw" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1137 +#, c-format +msgid "Please login!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1169 +#, c-format +msgid "Login" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1184 +#, c-format +msgid "Register to the euFin bank!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1194 +#, c-format +msgid "Registration form" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1232 +#, c-format +msgid "Register" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1272 +#, c-format +msgid "Date" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1273 +#, c-format +msgid "Amount" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1274 +#, c-format +msgid "Counterpart" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1275 +#, c-format +msgid "Subject" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1343 +#, c-format +msgid "Username or account label '%1$s' not found. Won't login." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1365 +#, c-format +msgid "Wrong credentials given." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1374 +#, c-format +msgid "Account information could not be retrieved." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1394 +#, c-format +msgid "Close wire transfer" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1412 +#, c-format +msgid "Close Taler withdrawal" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1457 +#, c-format +msgid "Bank account balance:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1469 +#, c-format +msgid "Latest transactions:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1474 +#, c-format +msgid "Transfer money manually" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1543 +#, c-format +msgid "List of public accounts was not found." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1552 +#, c-format +msgid "List of public accounts could not be retrieved." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1584 +#, c-format +msgid "History of public accounts" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1643 +#, c-format +msgid "Page has a problem: logged in but backend state is lost." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1667 +#, c-format +msgid "Welcome to the euFin bank!" +msgstr "" + +# 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/> +# +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: 2022-01-08 09:57+0100\n" +"Last-Translator: <translate@taler.net>\n" +"Language-Team: German\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" diff --git a/packages/demobank-ui/src/i18n/en.po b/packages/demobank-ui/src/i18n/en.po new file mode 100644 index 000000000..4cbc9e74c --- /dev/null +++ b/packages/demobank-ui/src/i18n/en.po @@ -0,0 +1,266 @@ +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:55 +#, c-format +msgid "days" +msgstr "days" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:65 +#, c-format +msgid "hours" +msgstr "hours" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:76 +#, c-format +msgid "minutes" +msgstr "minutes" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:87 +#, c-format +msgid "seconds" +msgstr "seconds" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:734 +#, c-format +msgid "Clear" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:761 +#, c-format +msgid "Logout" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:782 +#, c-format +msgid "Demo Bank" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:837 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:840 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1189 +#, c-format +msgid "Go back" +msgstr "Go back" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:845 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:906 +#, c-format +msgid "Wire transfer" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:846 +#, c-format +msgid "Transfer money to another account of this bank:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:897 +#, c-format +msgid "Want to try the raw payto://-format?" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:907 +#, c-format +msgid "Transfer money via the Payto system:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:916 +#, c-format +msgid "payto address" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:926 +#, c-format +msgid "Confirm" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:956 +#, fuzzy, c-format +msgid "Confirm Withdrawal" +msgstr "Confirm withdrawal" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1026 +#, c-format +msgid "Waiting the bank to create the operaion..." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1044 +#, c-format +msgid "This withdrawal was aborted!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1051 +#, fuzzy, c-format +msgid "Withdraw to a Taler Wallet" +msgstr "Charge Taler wallet" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1052 +#, c-format +msgid "You can use this QR code to withdraw to your mobile wallet:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1054 +#, c-format +msgid "this link" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1060 +#, c-format +msgid "Abort" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1084 +#, fuzzy, c-format +msgid "Start withdrawal" +msgstr "Start withdrawal" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1101 +#, fuzzy, c-format +msgid "Withdraw Money into a Taler wallet" +msgstr "Charge Taler wallet" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1105 +#, fuzzy, c-format +msgid "Amount to withdraw" +msgstr "Amount to withdraw" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1137 +#, c-format +msgid "Please login!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1169 +#, c-format +msgid "Login" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1184 +#, c-format +msgid "Register to the euFin bank!" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1194 +#, c-format +msgid "Registration form" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1232 +#, c-format +msgid "Register" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1272 +#, c-format +msgid "Date" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1273 +#, c-format +msgid "Amount" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1274 +#, c-format +msgid "Counterpart" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1275 +#, c-format +msgid "Subject" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1343 +#, c-format +msgid "Username or account label '%1$s' not found. Won't login." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1365 +#, c-format +msgid "Wrong credentials given." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1374 +#, c-format +msgid "Account information could not be retrieved." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1394 +#, c-format +msgid "Close wire transfer" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1412 +#, fuzzy, c-format +msgid "Close Taler withdrawal" +msgstr "Close Taler withdrawal" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1457 +#, c-format +msgid "Bank account balance:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1469 +#, c-format +msgid "Latest transactions:" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1474 +#, c-format +msgid "Transfer money manually" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1543 +#, c-format +msgid "List of public accounts was not found." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1552 +#, c-format +msgid "List of public accounts could not be retrieved." +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1584 +#, c-format +msgid "History of public accounts" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1643 +#, c-format +msgid "Page has a problem: logged in but backend state is lost." +msgstr "Page has a problem: logged in but backend state is lost." + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1667 +#, fuzzy, c-format +msgid "Welcome to the euFin bank!" +msgstr "Welcome to euFin bank: Taler+IBAN now possible!" + +# 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/> +# +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: 2022-01-08 09:57+0100\n" +"Last-Translator: <translate@taler.net>\n" +"Language-Team: English\n" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#~ msgid "Page has a problem:" +#~ msgstr "Page has a problem:" + +#~ msgid "Close" +#~ msgstr "Close" + +#~ msgid "Sign in" +#~ msgstr "Sign in" diff --git a/packages/demobank-ui/src/i18n/index.tsx b/packages/demobank-ui/src/i18n/index.tsx new file mode 100644 index 000000000..9882525a1 --- /dev/null +++ b/packages/demobank-ui/src/i18n/index.tsx @@ -0,0 +1,211 @@ +/* + 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/> + */ + +/** + * Translation helpers for React components and template literals. + */ + +/** + * Imports + */ +import { ComponentChild, ComponentChildren, h, Fragment, VNode } from 'preact'; + +import { useTranslationContext } from '../context/translation'; + +export function useTranslator() { + const ctx = useTranslationContext(); + const jed = ctx.handler; + return function str( + stringSeq: TemplateStringsArray, + ...values: any[] + ): string { + const s = toI18nString(stringSeq); + if (!s) return s; + const tr = jed + .translate(s) + .ifPlural(1, s) + .fetch(...values); + return tr; + }; +} + +/** + * Convert template strings to a msgid + */ +function toI18nString(stringSeq: ReadonlyArray<string>): string { + let s = ''; + for (let i = 0; i < stringSeq.length; i++) { + s += stringSeq[i]; + if (i < stringSeq.length - 1) + s += `%${i + 1}$s`; + + } + return s; +} + +interface TranslateSwitchProps { + target: number; + children: ComponentChildren; +} + +function stringifyChildren(children: ComponentChildren): string { + let n = 1; + const ss = (children instanceof Array ? children : [children]).map((c) => { + if (typeof c === 'string') + return c; + + return `%${n++}$s`; + }); + const s = ss.join('').replace(/ +/g, ' ').trim(); + return s; +} + +interface TranslateProps { + children: ComponentChildren; + /** + * Component that the translated element should be wrapped in. + * Defaults to "div". + */ + wrap?: any; + + /** + * Props to give to the wrapped component. + */ + wrapProps?: any; +} + +function getTranslatedChildren( + translation: string, + children: ComponentChildren, +): ComponentChild[] { + const tr = translation.split(/%(\d+)\$s/); + const childArray = children instanceof Array ? children : [children]; + // Merge consecutive string children. + const placeholderChildren = Array<ComponentChild>(); + for (let i = 0; i < childArray.length; i++) { + const x = childArray[i]; + if (x === undefined) + continue; + else if (typeof x === 'string') + continue; + else + placeholderChildren.push(x); + + } + const result = Array<ComponentChild>(); + for (let i = 0; i < tr.length; i++) + if (i % 2 == 0) + // Text + result.push(tr[i]); + else { + const childIdx = Number.parseInt(tr[i], 10) - 1; + result.push(placeholderChildren[childIdx]); + } + + return result; +} + +/** + * Translate text node children of this component. + * If a child component might produce a text node, it must be wrapped + * in a another non-text element. + * + * Example: + * ``` + * <Translate> + * Hello. Your score is <span><PlayerScore player={player} /></span> + * </Translate> + * ``` + */ +export function Translate({ children }: TranslateProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext(); + const translation: string = ctx.handler.ngettext(s, s, 1); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; +} + +/** + * Switch translation based on singular or plural based on the target prop. + * Should only contain TranslateSingular and TransplatePlural as children. + * + * Example: + * ``` + * <TranslateSwitch target={n}> + * <TranslateSingular>I have {n} apple.</TranslateSingular> + * <TranslatePlural>I have {n} apples.</TranslatePlural> + * </TranslateSwitch> + * ``` + */ +export function TranslateSwitch({ children, target }: TranslateSwitchProps) { + let singular: VNode<TranslationPluralProps> | undefined; + let plural: VNode<TranslationPluralProps> | undefined; + // const children = this.props.children; + if (children) + (children instanceof Array ? children : [children]).forEach( + (child: any) => { + if (child.type === TranslatePlural) + plural = child; + + if (child.type === TranslateSingular) + singular = child; + + }, + ); + + if (!singular || !plural) { + console.error('translation not found'); + return h('span', {}, ['translation not found']); + } + singular.props.target = target; + plural.props.target = target; + // We're looking up the translation based on the + // singular, even if we must use the plural form. + return singular; +} + +interface TranslationPluralProps { + children: ComponentChildren; + target: number; +} + +/** + * See [[TranslateSwitch]]. + */ +export function TranslatePlural({ + children, + target, +}: TranslationPluralProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext(); + const translation = ctx.handler.ngettext(s, s, 1); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; +} + +/** + * See [[TranslateSwitch]]. + */ +export function TranslateSingular({ + children, + target, +}: TranslationPluralProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext(); + const translation = ctx.handler.ngettext(s, s, target); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; +} diff --git a/packages/demobank-ui/src/i18n/it.po b/packages/demobank-ui/src/i18n/it.po new file mode 100644 index 000000000..91a30b947 --- /dev/null +++ b/packages/demobank-ui/src/i18n/it.po @@ -0,0 +1,258 @@ +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:55 +#, c-format +msgid "days" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:65 +#, c-format +msgid "hours" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:76 +#, c-format +msgid "minutes" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/components/picker/DurationPicker.tsx:87 +#, c-format +msgid "seconds" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:734 +#, c-format +msgid "Clear" +msgstr "Cancella" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:761 +#, c-format +msgid "Logout" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:782 +#, c-format +msgid "Demo Bank" +msgstr "Banca 'demo'" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:837 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:840 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1189 +#, c-format +msgid "Go back" +msgstr "Indietro" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:845 +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:906 +#, c-format +msgid "Wire transfer" +msgstr "Bonifico" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:846 +#, c-format +msgid "Transfer money to another account of this bank:" +msgstr "Trasferisci fondi a un altro conto di questa banca:" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:897 +#, c-format +msgid "Want to try the raw payto://-format?" +msgstr "Prova il trasferimento tramite il formato Payto!" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:907 +#, c-format +msgid "Transfer money via the Payto system:" +msgstr "Effettua un bonifico tramite il sistema Payto:" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:916 +#, c-format +msgid "payto address" +msgstr "indirizzo Payto" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:926 +#, c-format +msgid "Confirm" +msgstr "Conferma" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:956 +#, c-format +msgid "Confirm Withdrawal" +msgstr "Conferma il ritiro" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1026 +#, c-format +msgid "Waiting the bank to create the operaion..." +msgstr "La banca sta creando l'operazione..." + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1044 +#, c-format +msgid "This withdrawal was aborted!" +msgstr "Questo ritiro è stato annullato!" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1051 +#, c-format +msgid "Withdraw to a Taler Wallet" +msgstr "Ritira contante nel portafoglio Taler" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1052 +#, c-format +msgid "You can use this QR code to withdraw to your mobile wallet:" +msgstr "Usa questo codice QR per ritirare contante nel tuo wallet:" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1054 +#, c-format +msgid "this link" +msgstr "questo link" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1060 +#, c-format +msgid "Abort" +msgstr "Annulla" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1084 +#, c-format +msgid "Start withdrawal" +msgstr "Ritira contante" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1101 +#, c-format +msgid "Withdraw Money into a Taler wallet" +msgstr "Ritira contante nel portafoglio Taler" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1105 +#, c-format +msgid "Amount to withdraw" +msgstr "Somma da ritirare" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1137 +#, c-format +msgid "Please login!" +msgstr "Accedi!" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1169 +#, c-format +msgid "Login" +msgstr "Accedi" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1184 +#, c-format +msgid "Register to the euFin bank!" +msgstr "Apri un conto in banca euFin!" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1194 +#, c-format +msgid "Registration form" +msgstr "Registrazione" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1232 +#, c-format +msgid "Register" +msgstr "Registrati" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1272 +#, c-format +msgid "Date" +msgstr "" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1273 +#, c-format +msgid "Amount" +msgstr "Somma" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1274 +#, c-format +msgid "Counterpart" +msgstr "Controparte" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1275 +#, c-format +msgid "Subject" +msgstr "Causale" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1343 +#, c-format +msgid "Username or account label '%1$s' not found. Won't login." +msgstr "L'utente '%1$s' non esiste. Login impossibile" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1365 +#, c-format +msgid "Wrong credentials given." +msgstr "Credenziali invalide." + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1374 +#, c-format +msgid "Account information could not be retrieved." +msgstr "Impossibile ricevere le informazioni relative al conto." + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1394 +#, c-format +msgid "Close wire transfer" +msgstr "Chiudi il bonifico" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1412 +#, c-format +msgid "Close Taler withdrawal" +msgstr "Chiudi il ritiro Taler" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1457 +#, c-format +msgid "Bank account balance:" +msgstr "Bilancio:" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1469 +#, c-format +msgid "Latest transactions:" +msgstr "Ultime transazioni:" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1474 +#, c-format +msgid "Transfer money manually" +msgstr "Effettua un bonifico" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1543 +#, c-format +msgid "List of public accounts was not found." +msgstr "Lista conti pubblici non trovata." + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1552 +#, c-format +msgid "List of public accounts could not be retrieved." +msgstr "Lista conti pubblici non pervenuta." + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1584 +#, c-format +msgid "History of public accounts" +msgstr "Storico dei conti pubblici" + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1643 +#, c-format +msgid "Page has a problem: logged in but backend state is lost." +msgstr "" +"Stato inconsistente: accesso utente effettuato ma stato con server perso." + +#: /home/job/backoffice/packages/bank/src/pages/home/index.tsx:1667 +#, fuzzy, c-format +msgid "Welcome to the euFin bank!" +msgstr "Benvenuti in banca euFin!" + +# 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/> +# +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: 2022-01-08 10:05+0100\n" +"Last-Translator: <translate@taler.net>\n" +"Language-Team: Italian\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" diff --git a/packages/demobank-ui/src/i18n/poheader b/packages/demobank-ui/src/i18n/poheader new file mode 100644 index 000000000..ee3fcd7be --- /dev/null +++ b/packages/demobank-ui/src/i18n/poheader @@ -0,0 +1,27 @@ +# 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/> + +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: taler@gnu.org\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" diff --git a/packages/demobank-ui/src/i18n/strings-prelude b/packages/demobank-ui/src/i18n/strings-prelude new file mode 100644 index 000000000..cca13afad --- /dev/null +++ b/packages/demobank-ui/src/i18n/strings-prelude @@ -0,0 +1,19 @@ +/* + 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/> + */ + +/*eslint quote-props: ["error", "consistent"]*/ +export const strings: {[s: string]: any} = {}; + diff --git a/packages/demobank-ui/src/i18n/strings.ts b/packages/demobank-ui/src/i18n/strings.ts new file mode 100644 index 000000000..1a3c72f85 --- /dev/null +++ b/packages/demobank-ui/src/i18n/strings.ts @@ -0,0 +1,472 @@ +/* + 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/> + */ + +/*eslint quote-props: ["error", "consistent"]*/ +export const strings: {[s: string]: any} = {}; + +strings['de'] = { + 'domain': 'messages', + 'locale_data': { + 'messages': { + 'days': [ + '' + ], + 'hours': [ + '' + ], + 'minutes': [ + '' + ], + 'seconds': [ + '' + ], + 'Clear': [ + '' + ], + 'Logout': [ + '' + ], + 'Demo Bank': [ + '' + ], + 'Go back': [ + '' + ], + 'Wire transfer': [ + '' + ], + 'Transfer money to another account of this bank:': [ + '' + ], + 'Want to try the raw payto://-format?': [ + '' + ], + 'Transfer money via the Payto system:': [ + '' + ], + 'payto address': [ + '' + ], + 'Confirm': [ + '' + ], + 'Confirm Withdrawal': [ + '' + ], + 'Waiting the bank to create the operaion...': [ + '' + ], + 'This withdrawal was aborted!': [ + '' + ], + 'Withdraw to a Taler Wallet': [ + '' + ], + 'You can use this QR code to withdraw to your mobile wallet:': [ + '' + ], + 'this link': [ + '' + ], + 'Abort': [ + '' + ], + 'Start withdrawal': [ + '' + ], + 'Withdraw Money into a Taler wallet': [ + '' + ], + 'Amount to withdraw': [ + '' + ], + 'Please login!': [ + '' + ], + 'Login': [ + '' + ], + 'Register to the euFin bank!': [ + '' + ], + 'Registration form': [ + '' + ], + 'Register': [ + '' + ], + 'Date': [ + '' + ], + 'Amount': [ + '' + ], + 'Counterpart': [ + '' + ], + 'Subject': [ + '' + ], + 'Username or account label \'%1$s\' not found. Won\'t login.': [ + '' + ], + 'Wrong credentials given.': [ + '' + ], + 'Account information could not be retrieved.': [ + '' + ], + 'Close wire transfer': [ + '' + ], + 'Close Taler withdrawal': [ + '' + ], + 'Bank account balance:': [ + '' + ], + 'Latest transactions:': [ + '' + ], + 'Transfer money manually': [ + '' + ], + 'List of public accounts was not found.': [ + '' + ], + 'List of public accounts could not be retrieved.': [ + '' + ], + 'History of public accounts': [ + '' + ], + 'Page has a problem: logged in but backend state is lost.': [ + '' + ], + 'Welcome to the euFin bank!': [ + '' + ], + '': { + 'domain': 'messages', + 'plural_forms': 'nplurals=2; plural=(n != 1);', + 'lang': 'de' + } + } + } +}; + +strings['en'] = { + 'domain': 'messages', + 'locale_data': { + 'messages': { + 'days': [ + 'days' + ], + 'hours': [ + 'hours' + ], + 'minutes': [ + 'minutes' + ], + 'seconds': [ + 'seconds' + ], + 'Clear': [ + '' + ], + 'Logout': [ + '' + ], + 'Demo Bank': [ + '' + ], + 'Go back': [ + 'Go back' + ], + 'Wire transfer': [ + '' + ], + 'Transfer money to another account of this bank:': [ + '' + ], + 'Want to try the raw payto://-format?': [ + '' + ], + 'Transfer money via the Payto system:': [ + '' + ], + 'payto address': [ + '' + ], + 'Confirm': [ + '' + ], + 'Confirm Withdrawal': [ + 'Confirm withdrawal' + ], + 'Waiting the bank to create the operaion...': [ + '' + ], + 'This withdrawal was aborted!': [ + '' + ], + 'Withdraw to a Taler Wallet': [ + 'Charge Taler wallet' + ], + 'You can use this QR code to withdraw to your mobile wallet:': [ + '' + ], + 'this link': [ + '' + ], + 'Abort': [ + '' + ], + 'Start withdrawal': [ + 'Start withdrawal' + ], + 'Withdraw Money into a Taler wallet': [ + 'Charge Taler wallet' + ], + 'Amount to withdraw': [ + 'Amount to withdraw' + ], + 'Please login!': [ + '' + ], + 'Login': [ + '' + ], + 'Register to the euFin bank!': [ + '' + ], + 'Registration form': [ + '' + ], + 'Register': [ + '' + ], + 'Date': [ + '' + ], + 'Amount': [ + '' + ], + 'Counterpart': [ + '' + ], + 'Subject': [ + '' + ], + 'Username or account label \'%1$s\' not found. Won\'t login.': [ + '' + ], + 'Wrong credentials given.': [ + '' + ], + 'Account information could not be retrieved.': [ + '' + ], + 'Close wire transfer': [ + '' + ], + 'Close Taler withdrawal': [ + 'Close Taler withdrawal' + ], + 'Bank account balance:': [ + '' + ], + 'Latest transactions:': [ + '' + ], + 'Transfer money manually': [ + '' + ], + 'List of public accounts was not found.': [ + '' + ], + 'List of public accounts could not be retrieved.': [ + '' + ], + 'History of public accounts': [ + '' + ], + 'Page has a problem: logged in but backend state is lost.': [ + 'Page has a problem: logged in but backend state is lost.' + ], + 'Welcome to the euFin bank!': [ + 'Welcome to euFin bank: Taler+IBAN now possible!' + ], + '': { + 'domain': 'messages', + 'plural_forms': 'nplurals=2; plural=(n != 1);', + 'lang': 'en' + } + } + } +}; + +strings['it'] = { + 'domain': 'messages', + 'locale_data': { + 'messages': { + 'days': [ + '' + ], + 'hours': [ + '' + ], + 'minutes': [ + '' + ], + 'seconds': [ + '' + ], + 'Clear': [ + 'Cancella' + ], + 'Logout': [ + '' + ], + 'Demo Bank': [ + 'Banca \'demo\'' + ], + 'Go back': [ + 'Indietro' + ], + 'Wire transfer': [ + 'Bonifico' + ], + 'Transfer money to another account of this bank:': [ + 'Trasferisci fondi a un altro conto di questa banca:' + ], + 'Want to try the raw payto://-format?': [ + 'Prova il trasferimento tramite il formato Payto!' + ], + 'Transfer money via the Payto system:': [ + 'Effettua un bonifico tramite il sistema Payto:' + ], + 'payto address': [ + 'indirizzo Payto' + ], + 'Confirm': [ + 'Conferma' + ], + 'Confirm Withdrawal': [ + 'Conferma il ritiro' + ], + 'Waiting the bank to create the operaion...': [ + 'La banca sta creando l\'operazione...' + ], + 'This withdrawal was aborted!': [ + 'Questo ritiro è stato annullato!' + ], + 'Withdraw to a Taler Wallet': [ + 'Ritira contante nel portafoglio Taler' + ], + 'You can use this QR code to withdraw to your mobile wallet:': [ + 'Usa questo codice QR per ritirare contante nel tuo wallet:' + ], + 'this link': [ + 'questo link' + ], + 'Abort': [ + 'Annulla' + ], + 'Start withdrawal': [ + 'Ritira contante' + ], + 'Withdraw Money into a Taler wallet': [ + 'Ritira contante nel portafoglio Taler' + ], + 'Amount to withdraw': [ + 'Somma da ritirare' + ], + 'Please login!': [ + 'Accedi!' + ], + 'Login': [ + 'Accedi' + ], + 'Register to the euFin bank!': [ + 'Apri un conto in banca euFin!' + ], + 'Registration form': [ + 'Registrazione' + ], + 'Register': [ + 'Registrati' + ], + 'Date': [ + '' + ], + 'Amount': [ + 'Somma' + ], + 'Counterpart': [ + 'Controparte' + ], + 'Subject': [ + 'Causale' + ], + 'Username or account label \'%1$s\' not found. Won\'t login.': [ + 'L\'utente \'%1$s\' non esiste. Login impossibile' + ], + 'Wrong credentials given.': [ + 'Credenziali invalide.' + ], + 'Account information could not be retrieved.': [ + 'Impossibile ricevere le informazioni relative al conto.' + ], + 'Close wire transfer': [ + 'Chiudi il bonifico' + ], + 'Close Taler withdrawal': [ + 'Chiudi il ritiro Taler' + ], + 'Bank account balance:': [ + 'Bilancio:' + ], + 'Latest transactions:': [ + 'Ultime transazioni:' + ], + 'Transfer money manually': [ + 'Effettua un bonifico' + ], + 'List of public accounts was not found.': [ + 'Lista conti pubblici non trovata.' + ], + 'List of public accounts could not be retrieved.': [ + 'Lista conti pubblici non pervenuta.' + ], + 'History of public accounts': [ + 'Storico dei conti pubblici' + ], + 'Page has a problem: logged in but backend state is lost.': [ + 'Stato inconsistente: accesso utente effettuato ma stato con server perso.' + ], + 'Welcome to the euFin bank!': [ + 'Benvenuti in banca euFin!' + ], + '': { + 'domain': 'messages', + 'plural_forms': 'nplurals=2; plural=(n != 1);', + 'lang': 'it' + } + } + } +}; + diff --git a/packages/demobank-ui/src/index.tsx b/packages/demobank-ui/src/index.tsx new file mode 100644 index 000000000..a2f7b30f8 --- /dev/null +++ b/packages/demobank-ui/src/index.tsx @@ -0,0 +1,3 @@ +import App from './components/app'; + +export default App; diff --git a/packages/demobank-ui/src/manifest.json b/packages/demobank-ui/src/manifest.json new file mode 100644 index 000000000..feca12a0e --- /dev/null +++ b/packages/demobank-ui/src/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "taler-bank", + "short_name": "taler-bank", + "start_url": "/", + "display": "standalone", + "orientation": "portrait", + "background_color": "#fff", + "theme_color": "#673ab8", + "icons": [ + { + "src": "/assets/icons/android-chrome-192x192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "/assets/icons/android-chrome-512x512.png", + "type": "image/png", + "sizes": "512x512" + } + ] +}
\ No newline at end of file diff --git a/packages/demobank-ui/src/pages/home/index.tsx b/packages/demobank-ui/src/pages/home/index.tsx new file mode 100644 index 000000000..bf9764b78 --- /dev/null +++ b/packages/demobank-ui/src/pages/home/index.tsx @@ -0,0 +1,2018 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import useSWR, { SWRConfig as _SWRConfig, useSWRConfig } from 'swr'; +import { h, Fragment, VNode, createContext } from 'preact'; +import { useRef, useState, useEffect, StateUpdater, useContext } from 'preact/hooks'; +import { Buffer } from 'buffer'; +import { useTranslator, Translate } from '../../i18n'; +import { QR } from '../../components/QR'; +import { useNotNullLocalStorage, useLocalStorage } from '../../hooks'; +import '../../scss/main.scss'; +import talerLogo from '../../assets/logo-white.svg'; +import { LangSelectorLikePy as LangSelector } from '../../components/menu/LangSelector'; + +// FIXME: Fix usages of SWRConfig, doing this isn't the best practice (but hey, it works for now) +const SWRConfig = _SWRConfig as any; + +const UI_ALLOW_REGISTRATIONS = ('__LIBEUFIN_UI_ALLOW_REGISTRATIONS__') ?? 1; +const UI_IS_DEMO = ('__LIBEUFIN_UI_IS_DEMO__') ?? 0; +const UI_BANK_NAME = ('__LIBEUFIN_UI_BANK_NAME__') ?? 'Taler Bank'; + +/** + * FIXME: + * + * - INPUT elements have their 'required' attribute ignored. + * + * - the page needs a "home" button that either redirects to + * the profile page (when the user is logged in), or to + * the very initial home page. + * + * - histories 'pages' are grouped in UL elements that cause + * the rendering to visually separate each UL. History elements + * should instead line up without any separation caused by + * a implementation detail. + * + * - Many strings need to be i18n-wrapped. + */ + +/*********** + * Globals * + **********/ + +/************ + * Contexts * + ***********/ +const CurrencyContext = createContext<any>(null); +const PageContext = createContext<any>(null); + +/********************************************** + * Type definitions for states and API calls. * + *********************************************/ + +/** + * Has the information to reach and + * authenticate at the bank's backend. + */ +interface BackendStateType { + url: string; + username: string; + password: string; +} + +/** + * Request body of POST /transactions. + * + * If the amount appears twice: both as a Payto parameter and + * in the JSON dedicate field, the one on the Payto URI takes + * precedence. + */ +interface TransactionRequestType { + paytoUri: string; + amount?: string; // with currency. +} + +/** + * Request body of /register. + */ +interface CredentialsRequestType { + username: string; + password: string; +} + +/** + * Request body of /register. + */ +interface LoginRequestType { + username: string; + password: string; +} + +interface WireTransferRequestType { + iban: string; + subject: string; + amount: string; +} + +interface Amount { + value: string; + currency: string; +} + +/** + * Track page state. + */ +interface PageStateType { + isLoggedIn: boolean; + isRawPayto: boolean; + tryRegister: boolean; + showPublicHistories: boolean; + hasError: boolean; + hasInfo: boolean; + withdrawalInProgress: boolean; + error?: string; + info?: string; + talerWithdrawUri?: string; + /** + * Not strictly a presentational value, could + * be moved in a future "withdrawal state" object. + */ + withdrawalId?: string; +} + +/** + * Bank account specific information. + */ +interface AccountStateType { + balance: string; + /* FIXME: Need history here. */ +} + +/************ + * Helpers. * + ***********/ + +function maybeDemoContent(content: VNode) { + if (UI_IS_DEMO) return content; +} + +async function fetcher(url: string) { + return fetch(url).then((r) => (r.json())); +} + +function genCaptchaNumbers(): string { + return `${Math.floor(Math.random() * 10)} + ${Math.floor(Math.random() * 10)}`; +} +/** + * Bring the state to show the public accounts page. + */ +function goPublicAccounts(pageStateSetter: StateUpdater<PageStateType>) { + return () => pageStateSetter((prevState) => ({ ...prevState, showPublicHistories: true })) +} + +/** + * Validate (the number part of) an amount. If needed, + * replace comma with a dot. Returns 'false' whenever + * the input is invalid, the valid amount otherwise. + */ +function validateAmount(maybeAmount: string): any { + const amountRegex = '^[0-9]+(\.[0-9]+)?$'; + if (!maybeAmount) { + console.log(`Entered amount (${maybeAmount}) mismatched <input> pattern.`); + return; + } + if (typeof maybeAmount !== 'undefined' || maybeAmount !== '') { + console.log(`Maybe valid amount: ${maybeAmount}`); + // tolerating comma instead of point. + const re = RegExp(amountRegex) + if (!re.test(maybeAmount)) { + console.log(`Not using invalid amount '${maybeAmount}'.`); + return false; + } + } + return maybeAmount; +} + +/** + * Extract IBAN from a Payto URI. + */ +function getIbanFromPayto(url: string): string { + const pathSplit = new URL(url).pathname.split('/'); + let lastIndex = pathSplit.length - 1; + // Happens if the path ends with "/". + if (pathSplit[lastIndex] === '') lastIndex--; + const iban = pathSplit[lastIndex]; + return iban; +} + +/** + * Extract value and currency from a $currency:x.y amount. + */ +function parseAmount(val: string): Amount { + const format = /^[A-Z]+:[0-9]+(\.[0-9]+)?$/; + if (!format.test(val)) + throw Error(`Backend gave invalid amount: ${val}.`) + const amountSplit = val.split(':'); + return { value: amountSplit[1], currency: amountSplit[0] } +} + +/** + * Get username from the backend state, and throw + * exception if not found. + */ +function getUsername(backendState: BackendStateTypeOpt): string { + if (typeof backendState === 'undefined') + throw Error('Username can\'t be found in a undefined backend state.') + + return backendState.username; +} + +/** + * Helps extracting the credentials from the state + * and wraps the actual call to 'fetch'. Should be + * enclosed in a try-catch block by the caller. + */ +async function postToBackend( + uri: string, + backendState: BackendStateTypeOpt, + body: string +): Promise<any> { + if (typeof backendState === 'undefined') + throw Error('Credentials can\'t be found in a undefined backend state.') + + const { username, password } = backendState; + const headers = prepareHeaders(username, password); + // Backend URL must have been stored _with_ a final slash. + const url = new URL(uri, backendState.url) + return await fetch(url.href, { + method: 'POST', + headers, + body, + } + ); +} + +function useTransactionPageNumber(): [number, StateUpdater<number>] { + const ret = useNotNullLocalStorage('transaction-page', '0'); + const retObj = JSON.parse(ret[0]); + const retSetter: StateUpdater<number> = function (val) { + const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) + ret[1](newVal) + } + return [retObj, retSetter]; +} + +/** + * Craft headers with Authorization and Content-Type. + */ +function prepareHeaders(username: string, password: string) { + const headers = new Headers(); + headers.append( + 'Authorization', + `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}` + ); + headers.append( + 'Content-Type', + 'application/json' + ) + return headers; +} + +// Window can be mocked this way: +// https://gist.github.com/theKashey/07090691c0a4680ed773375d8dbeebc1#file-webpack-conf-js +// That allows the app to be pointed to a arbitrary +// euFin backend when launched via "pnpm dev". +const getRootPath = () => { + const maybeRootPath = typeof window !== undefined + ? window.location.origin + window.location.pathname + : '/'; + if (!maybeRootPath.endsWith('/')) return `${maybeRootPath}/`; + return maybeRootPath; +}; + +/******************* + * State managers. * + ******************/ + +/** + * Stores in the state a object containing a 'username' + * and 'password' field, in order to avoid losing the + * handle of the data entered by the user in <input> fields. + */ +function useShowPublicAccount( + state?: string +): [string | undefined, StateUpdater<string | undefined>] { + + const ret = useLocalStorage('show-public-account', JSON.stringify(state)); + const retObj: string | undefined = ret[0] ? JSON.parse(ret[0]) : ret[0]; + const retSetter: StateUpdater<string | undefined> = function (val) { + const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) + ret[1](newVal) + } + return [retObj, retSetter] +} + +/** + * Stores the raw Payto value entered by the user in the state. + */ +type RawPaytoInputType = string; +type RawPaytoInputTypeOpt = RawPaytoInputType | undefined; +function useRawPaytoInputType( + state?: RawPaytoInputType +): [RawPaytoInputTypeOpt, StateUpdater<RawPaytoInputTypeOpt>] { + + const ret = useLocalStorage('raw-payto-input-state', state); + const retObj: RawPaytoInputTypeOpt = ret[0]; + const retSetter: StateUpdater<RawPaytoInputTypeOpt> = function (val) { + const newVal = val instanceof Function ? val(retObj) : val + ret[1](newVal) + } + return [retObj, retSetter] +} + +/** + * Stores in the state a object representing a wire transfer, + * in order to avoid losing the handle of the data entered by + * the user in <input> fields. FIXME: name not matching the + * purpose, as this is not a HTTP request body but rather the + * state of the <input>-elements. + */ +type WireTransferRequestTypeOpt = WireTransferRequestType | undefined; +function useWireTransferRequestType( + state?: WireTransferRequestType +): [WireTransferRequestTypeOpt, StateUpdater<WireTransferRequestTypeOpt>] { + + const ret = useLocalStorage('wire-transfer-request-state', JSON.stringify(state)); + const retObj: WireTransferRequestTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0]; + const retSetter: StateUpdater<WireTransferRequestTypeOpt> = function (val) { + const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) + ret[1](newVal) + } + return [retObj, retSetter] +} + +/** + * Stores in the state a object containing a 'username' + * and 'password' field, in order to avoid losing the + * handle of the data entered by the user in <input> fields. + */ +type CredentialsRequestTypeOpt = CredentialsRequestType | undefined; +function useCredentialsRequestType( + state?: CredentialsRequestType +): [CredentialsRequestTypeOpt, StateUpdater<CredentialsRequestTypeOpt>] { + + const ret = useLocalStorage('credentials-request-state', JSON.stringify(state)); + const retObj: CredentialsRequestTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0]; + const retSetter: StateUpdater<CredentialsRequestTypeOpt> = function (val) { + const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) + ret[1](newVal) + } + return [retObj, retSetter] +} + +/** + * Return getters and setters for + * login credentials and backend's + * base URL. + */ +type BackendStateTypeOpt = BackendStateType | undefined; +function useBackendState( + state?: BackendStateType +): [BackendStateTypeOpt, StateUpdater<BackendStateTypeOpt>] { + + const ret = useLocalStorage('backend-state', JSON.stringify(state)); + const retObj: BackendStateTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0]; + const retSetter: StateUpdater<BackendStateTypeOpt> = function (val) { + const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) + ret[1](newVal) + } + return [retObj, retSetter] +} + +/** + * Keep mere business information, like account balance or + * transactions history. + */ +type AccountStateTypeOpt = AccountStateType | undefined; +function useAccountState( + state?: AccountStateType +): [AccountStateTypeOpt, StateUpdater<AccountStateTypeOpt>] { + + const ret = useLocalStorage('account-state', JSON.stringify(state)); + const retObj: AccountStateTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0]; + const retSetter: StateUpdater<AccountStateTypeOpt> = function (val) { + const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) + ret[1](newVal) + } + return [retObj, retSetter] +} + +/** + * Wrapper providing defaults. + */ +function usePageState( + state: PageStateType = { + isLoggedIn: false, + isRawPayto: false, + tryRegister: false, + showPublicHistories: false, + hasError: false, + hasInfo: false, + withdrawalInProgress: false, + } +): [PageStateType, StateUpdater<PageStateType>] { + const ret = useNotNullLocalStorage('page-state', JSON.stringify(state)); + const retObj: PageStateType = JSON.parse(ret[0]); + console.log('Current page state', retObj); + const retSetter: StateUpdater<PageStateType> = function (val) { + const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : JSON.stringify(val) + console.log('Setting new page state', newVal) + ret[1](newVal) + } + return [retObj, retSetter]; +} + +/** + * Request preparators. + * + * These functions aim at sanitizing the input received + * from users - for example via a HTML form - and create + * a HTTP request object out of that. + */ + +/****************** + * HTTP wrappers. * + *****************/ + +/** + * A 'wrapper' is typically a function that prepares one + * particular API call and updates the state accordingly. */ + +/** + * Abort a withdrawal operation via the Access API's /abort. + */ +async function abortWithdrawalCall( + backendState: BackendStateTypeOpt, + withdrawalId: string | undefined, + pageStateSetter: StateUpdater<PageStateType> +) { + if (typeof backendState === 'undefined') { + console.log('No credentials found.'); + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No credentials found.' })) + return; + } + if (typeof withdrawalId === 'undefined') { + console.log('No withdrawal ID found.'); + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No withdrawal ID found.' })) + return; + } + let res:any; + try { + const { username, password } = backendState; + const headers = prepareHeaders(username, password); + /** + * NOTE: tests show that when a same object is being + * POSTed, caching might prevent same requests from being + * made. Hence, trying to POST twice the same amount might + * get silently ignored. Needs more observation! + * + * headers.append("cache-control", "no-store"); + * headers.append("cache-control", "no-cache"); + * headers.append("pragma", "no-cache"); + * */ + + // Backend URL must have been stored _with_ a final slash. + const url = new URL( + `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/abort`, + backendState.url + ) + res = await fetch(url.href, { method: 'POST', headers }) + } catch (error) { + console.log('Could not abort the withdrawal', error); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Could not abort the withdrawal: ${error}` + })) + return; + } + if (!res.ok) { + console.log(`Withdrawal abort gave response error (${res.status})`, res.statusText); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Withdrawal abortion gave response error (${res.status})` + })) + return; + } + console.log('Withdrawal operation aborted!'); + pageStateSetter((prevState) => { + const { ...rest } = prevState; + return { + ...rest, + info: 'Withdrawal aborted!' + } + }) + +} + +/** + * This function confirms a withdrawal operation AFTER + * the wallet has given the exchange's payment details + * to the bank (via the Integration API). Such details + * can be given by scanning a QR code or by passing the + * raw taler://withdraw-URI to the CLI wallet. + * + * This function will set the confirmation status in the + * 'page state' and let the related components refresh. + */ +async function confirmWithdrawalCall( + backendState: BackendStateTypeOpt, + withdrawalId: string | undefined, + pageStateSetter: StateUpdater<PageStateType> +) { + + if (typeof backendState === 'undefined') { + console.log('No credentials found.'); + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No credentials found.' })) + return; + } + if (typeof withdrawalId === 'undefined') { + console.log('No withdrawal ID found.'); + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No withdrawal ID found.' })) + return; + } + let res: Response; + try { + const { username, password } = backendState; + const headers = prepareHeaders(username, password); + /** + * NOTE: tests show that when a same object is being + * POSTed, caching might prevent same requests from being + * made. Hence, trying to POST twice the same amount might + * get silently ignored. + * + * headers.append("cache-control", "no-store"); + * headers.append("cache-control", "no-cache"); + * headers.append("pragma", "no-cache"); + * */ + + // Backend URL must have been stored _with_ a final slash. + const url = new URL( + `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/confirm`, + backendState.url + ) + res = await fetch(url.href, { + method: 'POST', + headers + }) + } catch (error) { + console.log('Could not POST withdrawal confirmation to the bank', error); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Could not confirm the withdrawal: ${error}` + })) + return; + } + if (res ? !res.ok : true) { // assume not ok if res is null + console.log(`Withdrawal confirmation gave response error (${res.status})`, res.statusText); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Withdrawal confirmation gave response error (${res.status})` + })) + return; + } + console.log('Withdrawal operation confirmed!'); + pageStateSetter((prevState) => { + const { talerWithdrawUri, ...rest } = prevState; + return { + ...rest, + info: 'Withdrawal confirmed!' + } + }) + +} + +/** + * This function creates a new transaction. It reads a Payto + * address entered by the user and POSTs it to the bank. No + * sanity-check of the input happens before the POST as this is + * already conducted by the backend. + */ +async function createTransactionCall( + req: TransactionRequestType, + backendState: BackendStateTypeOpt, + pageStateSetter: StateUpdater<PageStateType>, + /** + * Optional since the raw payto form doesn't have + * a stateful management of the input data yet. + */ + cleanUpForm: () => void +) { + let res:any; + try { + res = await postToBackend( + `access-api/accounts/${getUsername(backendState)}/transactions`, + backendState, + JSON.stringify(req) + ) + } + catch (error) { + console.log('Could not POST transaction request to the bank', error); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Could not create the wire transfer: ${error}` + })) + return; + } + // POST happened, status not sure yet. + if (!res.ok) { + const responseText = JSON.stringify(await res.json()); + console.log(`Transfer creation gave response error: ${responseText} (${res.status})`); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Transfer creation gave response error: ${responseText} (${res.status})` + })) + return; + } + // status is 200 OK here, tell the user. + console.log('Wire transfer created!'); + pageStateSetter((prevState) => ({ + ...prevState, + hasInfo: true, + info: 'Wire transfer created!' + })) + + // Only at this point the input data can + // be discarded. + cleanUpForm(); +} + +/** + * This function creates a withdrawal operation via the Access API. + * + * After having successfully created the withdrawal operation, the + * user should receive a QR code of the "taler://withdraw/" type and + * supposed to scan it with their phone. + * + * TODO: (1) after the scan, the page should refresh itself and inform + * the user about the operation's outcome. (2) use POST helper. */ +async function createWithdrawalCall( + amount: string, + backendState: BackendStateTypeOpt, + pageStateSetter: StateUpdater<PageStateType> +) { + if (typeof backendState === 'undefined') { + console.log('Page has a problem: no credentials found in the state.'); + pageStateSetter((prevState) => ({ ...prevState, hasError: true, error: 'No credentials given.' })) + return; + } + + let res:any; + try { + const { username, password } = backendState; + const headers = prepareHeaders(username, password); + + // Let bank generate withdraw URI: + const url = new URL( + `access-api/accounts/${backendState.username}/withdrawals`, + backendState.url + ) + res = await fetch(url.href, { + method: 'POST', + headers, + body: JSON.stringify({ amount }), + } + ); + } catch (error) { + console.log('Could not POST withdrawal request to the bank', error); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Could not create withdrawal operation: ${error}` + })) + return; + } + if (!res.ok) { + const responseText = await res.text(); + console.log(`Withdrawal creation gave response error: ${responseText} (${res.status})`); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Withdrawal creation gave response error: ${responseText} (${res.status})` + })) + return; + } + + console.log('Withdrawal operation created!'); + const resp = await res.json(); + pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + withdrawalInProgress: true, + talerWithdrawUri: resp.taler_withdraw_uri, + withdrawalId: resp.withdrawal_id + })) +} + +async function loginCall( + req: CredentialsRequestType, + /** + * FIXME: figure out if the two following + * functions can be retrieved from the state. + */ + backendStateSetter: StateUpdater<BackendStateTypeOpt>, + pageStateSetter: StateUpdater<PageStateType> +) { + + /** + * Optimistically setting the state as 'logged in', and + * let the Account component request the balance to check + * whether the credentials are valid. */ + pageStateSetter((prevState) => ({ ...prevState, isLoggedIn: true })); + let baseUrl = getRootPath(); + if (!baseUrl.endsWith('/')) + baseUrl += '/'; + + backendStateSetter((prevState) => ({ + ...prevState, + url: baseUrl, + username: req.username, + password: req.password, + })); +} + + +/** + * This function requests /register. + * + * This function is responsible to change two states: + * the backend's (to store the login credentials) and + * the page's (to indicate a successful login or a problem). + */ +async function registrationCall( + req: CredentialsRequestType, + /** + * FIXME: figure out if the two following + * functions can be retrieved somewhat from + * the state. + */ + backendStateSetter: StateUpdater<BackendStateTypeOpt>, + pageStateSetter: StateUpdater<PageStateType> +) { + + let baseUrl = getRootPath(); + /** + * If the base URL doesn't end with slash and the path + * is not empty, then the concatenation made by URL() + * drops the last path element. + */ + if (!baseUrl.endsWith('/')) + baseUrl += '/' + + const headers = new Headers(); + headers.append( + 'Content-Type', + 'application/json' + ) + const url = new URL('access-api/testing/register', baseUrl) + let res:any; + try { + res = await fetch(url.href, { + method: 'POST', + body: JSON.stringify(req), + headers + }); + } catch (error) { + console.log(`Could not POST new registration to the bank (${url.href})`, error); + pageStateSetter((prevState) => ({ + ...prevState, hasError: true, error: 'Registration failed, please report.' + })); + return; + } + if (!res.ok) { + const errorRaw = await res.text(); + console.log(`New registration gave response error (${res.status})`, errorRaw); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: errorRaw + })); + } else { + pageStateSetter((prevState) => ({ + ...prevState, + isLoggedIn: true, + tryRegister: false + })); + backendStateSetter((prevState) => ({ + ...prevState, + url: baseUrl, + username: req.username, + password: req.password, + })); + } +} + +/************************** + * Functional components. * + *************************/ + +function Currency(): VNode { + const { data, error } = useSWR(`${getRootPath()}integration-api/config`, fetcher); + if (typeof error !== 'undefined') + return <b>error: currency could not be retrieved</b>; + + if (typeof data === 'undefined') return <Fragment>"..."</Fragment>; + console.log('found bank config', data); + return data.currency; +} + +function ErrorBanner(Props: any): VNode | null { + const [pageState, pageStateSetter] = Props.pageState; + const i18n = useTranslator(); + if (!pageState.hasError) return null; + + const rval = ( + <p class="informational informational-fail">{pageState.error} + </p>); + delete pageState.error; + pageState.hasError = false; + return rval; +} + +function StatusBanner(Props: any): VNode | null { + const [pageState, pageStateSetter] = Props.pageState; + const i18n = useTranslator(); + if (!pageState.hasInfo) return null; + + const rval = ( + <p class="informational">{pageState.error} + </p>); + delete pageState.info_msg; + pageState.hasInfo = false; + return rval; +} + +function BankFrame(Props: any): VNode { + const i18n = useTranslator(); + const [pageState, pageStateSetter] = useContext(PageContext); + console.log('BankFrame state', pageState); + const logOut = ( + <div class="logout"> + <a + href="#" + class="pure-button logout-button" + onClick={() => { + pageStateSetter((prevState: PageStateType) => { + const { + talerWithdrawUri, + withdrawalId, ...rest } = prevState; + return { + ...rest, + isLoggedIn: false, + withdrawalInProgress: false, + hasInfo: false, + hasError: false, + isRawPayto: false + }; + }); + }}>{i18n`Logout`}</a></div>); + + // Prepare demo sites links. + const DEMO_SITES = [ + ['Landing', '__DEMO_SITE_LANDING_URL__'], + ['Bank', '__DEMO_SITE_BANK_URL__'], + ['Essay Shop', '__DEMO_SITE_BLOG_URL__'], + ['Donations', '__DEMO_SITE_DONATIONS_URL__'], + ['Survey', '__DEMO_SITE_SURVEY_URL__'], + ]; + const demo_sites = []; + for (const i in DEMO_SITES) + demo_sites.push(<a href={DEMO_SITES[i][1]}>{DEMO_SITES[i][0]}</a>) + + return ( + <Fragment> + <header class="demobar" style="display: flex; flex-direction: row; justify-content: space-between;"> + <a href="#main" class="skip">{i18n`Skip to main content`}</a> + <div style="max-width: 50em; margin-left: 2em;"> + <h1> + <span class="it"> + <a href="/">{ + UI_BANK_NAME + } + </a> + </span> + </h1>{ + maybeDemoContent(<p><Translate> + This part of the demo shows how a bank that supports + Taler directly would work. In addition to using your own + bank account, you can also see the transaction history of + some <a href="#" onClick={goPublicAccounts(pageStateSetter)}>Public Accounts</a>. + </Translate></p> + ) + } + </div> + <a href="https://taler.net/"> + <img + src={talerLogo} + alt="{i18n`Taler logo`}" + height="100" + width="224" + style="margin: 2em 2em" /> + </a> + </header> + <div style="display:flex; flex-direction: column;" class="navcontainer"> + <nav class="demolist"> + {maybeDemoContent(<Fragment>{demo_sites}</Fragment>)} + <div class="right"> + <LangSelector /> + </div> + </nav> + </div> + <section id="main" class="content"> + <ErrorBanner pageState={[pageState, pageStateSetter]} /> + <StatusBanner pageState={[pageState, pageStateSetter]} /> + {pageState.isLoggedIn ? logOut : null} + {Props.children} + </section> + <section id="footer" class="footer"> + <div class="footer"> + <hr /> + <div> + <p>You can learn more about GNU Taler on our <a href="https://taler.net">main website</a>.</p> + </div> + <div style="flex-grow:1" /> + <p>Copyright © 2014—2022 Taler Systems SA</p> + </div> + </section> + </Fragment>); +} + + +function PaytoWireTransfer(Props: any): VNode { + const currency = useContext(CurrencyContext); + const [pageState, pageStateSetter] = useContext(PageContext); // NOTE: used for go-back button? + const [submitData, submitDataSetter] = useWireTransferRequestType(); + const [rawPaytoInput, rawPaytoInputSetter] = useRawPaytoInputType(); + const i18n = useTranslator(); + const { focus, backendState } = Props + const amountRegex = '^[0-9]+(\.[0-9]+)?$'; + const ibanRegex = '^[A-Z][A-Z][0-9]+$'; + const receiverInput = ''; + const subjectInput = ''; + let transactionData: TransactionRequestType; + const ref = useRef<HTMLInputElement>(null) + useEffect(() => { + if (focus) ref.current?.focus(); + }, [focus, pageState.isRawPayto]); + + if (!pageState.isRawPayto) + return ( + <div> + <div class="pure-form" + name="wire-transfer-form"> + <p> + <label for="iban">{i18n`Receiver IBAN:`}</label> + <input + ref={ref} + type="text" + id="iban" + name="iban" + value={submitData?.iban} + placeholder="CC0123456789" + required + pattern={ibanRegex} + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + iban: e.currentTarget.value, + })) + }} /><br /><br /> + <label for="subject">{i18n`Transfer subject:`}</label> + <input + type="text" + name="subject" + id="subject" + placeholder="subject" + value={submitData?.subject} + required + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + subject: e.currentTarget.value, + })) + }} /><br /><br /> + <label for="amount">{i18n`Amount:`}</label> + <input + type="number" + name="amount" + id="amount" + placeholder="amount" + required + value={submitData?.amount} + pattern={amountRegex} + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + amount: e.currentTarget.value.replace(',', '.'), + })) + }} /> + + <input + type="text" + readonly + class="currency-indicator" + size={currency.length} + maxLength={currency.length} + tabIndex={-1} value={currency} /> + </p> + <p> + <input + type="submit" + class="pure-button pure-button-primary" + value="Send" + onClick={async () => { + if ( + typeof submitData === 'undefined' + || (typeof submitData.iban === 'undefined' + || submitData.iban === '') + || (typeof submitData.subject === 'undefined' + || submitData.subject === '') + || (typeof submitData.amount === 'undefined' + || submitData.amount === '') + ) { + console.log('Not all the fields were given.'); + pageStateSetter((prevState: PageStateType) => + ({ ...prevState, hasError: true, error: i18n`Field(s) missing.` })) + return; + } + transactionData = { + paytoUri: `payto://iban/${submitData.iban}?message=${encodeURIComponent(submitData.subject)}`, + amount: `${currency}:${submitData.amount}` + }; + return await createTransactionCall( + transactionData, + backendState, + pageStateSetter, + () => submitDataSetter(p => ({ + amount: '', + iban: '', + subject: '' + })) + ); + }} /> + </p> + </div> + <p><a + href="#" + onClick={() => { + console.log('switch to raw payto form'); + pageStateSetter((prevState: any) => ({ ...prevState, isRawPayto: true })); + }}>{i18n`Want to try the raw payto://-format?`} + </a></p> + </div> + ); + + return ( + <div> + <p> + {i18n`Transfer money to account identified by payto:// URI:`} + </p> + <div class="pure-form" + name="payto-form"> + <p> + <label for="address">{i18n`payto URI:`}</label> + <input + name="address" + type="text" + size={90} + ref={ref} + id="address" + value={rawPaytoInput} + required + placeholder={i18n`payto address`} + pattern={`payto://iban/[A-Z][A-Z][0-9]+\?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(\.[0-9]+)?`} + onInput={(e): void => { + rawPaytoInputSetter(e.currentTarget.value) + }} /> + <br /> + <div class="hint"> + Hint: + <code> + payto://iban/[receiver-iban]?message=[subject]&amount=[{currency}:X.Y] + </code> + </div> + </p> + <p> + <input class="pure-button pure-button-primary" + type="submit" + value={i18n`Send`} + onClick={async () => { + // empty string evaluates to false. + if (!rawPaytoInput) { + console.log('Didn\'t get any raw Payto string!'); + return; + } + transactionData = { paytoUri: rawPaytoInput }; + if (typeof transactionData.paytoUri === 'undefined' || + transactionData.paytoUri.length === 0) return; + + return await createTransactionCall( + transactionData, + backendState, + pageStateSetter, + () => rawPaytoInputSetter(p => '') + ); + }} /> + </p> + <p><a + href="#" + onClick={() => { + console.log('switch to wire-transfer-form'); + pageStateSetter((prevState: any) => ({ ...prevState, isRawPayto: false })); + }}>{i18n`Use wire-transfer form?`} + </a></p> + </div> + </div>); +} + +/** + * Additional authentication required to complete the operation. + * Not providing a back button, only abort. + */ +function TalerWithdrawalConfirmationQuestion(Props: any): VNode { + const [pageState, pageStateSetter] = useContext(PageContext); + const { backendState } = Props; + const i18n = useTranslator(); + const captchaNumbers = { + a: Math.floor(Math.random() * 10), + b: Math.floor(Math.random() * 10) + } + let captchaAnswer = ''; + + return (<Fragment> + <h1 class="nav">{i18n`Confirm Withdrawal`}</h1> + <article> + <div class="challenge-div"> + <form class="challenge-form"> + <div class="pure-form" + id="captcha" + name="capcha-form"> + <h2>{i18n`Authorize withdrawal by solving challenge`}</h2> + <p> + <label for="answer">{i18n`What is`} <em>{captchaNumbers.a} + {captchaNumbers.b}</em>? </label> + <input + name="answer" + id="answer" + type="text" + required + onInput={(e): void => { + captchaAnswer = e.currentTarget.value; + }} /> + </p> + <p> + <button + class="pure-button pure-button-primary btn-confirm" + onClick={() => { + if (captchaAnswer == (captchaNumbers.a + captchaNumbers.b).toString()) { + confirmWithdrawalCall( + backendState, + pageState.withdrawalId, + pageStateSetter) + return; + } + pageStateSetter((prevState: PageStateType) => + ({ ...prevState, hasError: true, error: i18n`Answer is wrong.` })) + }}> + {i18n`Confirm`} + </button> + + <button + class="pure-button pure-button-secondary btn-cancel" + onClick={() => + abortWithdrawalCall( + backendState, + pageState.withdrawalId, + pageStateSetter + )}> + {i18n`Cancel`} + </button> + </p> + </div> + </form> + <div class="hint"> + <p><Translate> + A this point, a <b>real</b> bank would ask for an additional + authentication proof (PIN/TAN, one time password, ..), instead + of a simple calculation. + </Translate></p> + </div> + </div> + </article> + </Fragment>); +} + +function QrCodeSection({ talerWithdrawUri, abortButton }: { talerWithdrawUri: string, abortButton: h.JSX.Element }) { + const i18n = useTranslator(); + useEffect(() => { + //Taler Wallet WebExtension is listening to headers response and tab updates. + //In the SPA there is no header response with the Taler URI so + //this hack manually triggers the tab update after the QR is in the DOM. + window.location.href = `${window.location.href.split('#')[0]}#` + }, []) + + return <section id="main" class="content"> + <h1 class="nav">{i18n`Charge Taler Wallet`}</h1> + <p>{i18n`You can use this QR code to withdraw to your mobile wallet:`}</p> + {QR({ text: talerWithdrawUri })} + <p>Click <a id="linkqr" href={talerWithdrawUri}>{i18n`this link`}</a> to open your Taler wallet!</p> + <br /> + {abortButton} + </section> +} + +/** + * Offer the QR code (and a clickable taler://-link) to + * permit the passing of exchange and reserve details to + * the bank. Poll the backend until such operation is done. + */ +function TalerWithdrawalQRCode(Props: any): VNode { + // turns true when the wallet POSTed the reserve details: + const [pageState, pageStateSetter] = useContext(PageContext); + const { + withdrawalId, + talerWithdrawUri, + accountLabel, + backendState } = Props; + const i18n = useTranslator(); + const abortButton = <a class="pure-button" onClick={() => { + pageStateSetter((prevState: PageStateType) => { + const { withdrawalId, talerWithdrawUri, ...rest } = prevState; + return { ...rest, withdrawalInProgress: false }; + }) + }}>{i18n`Abort`}</a> + + console.log(`Showing withdraw URI: ${talerWithdrawUri}`); + // waiting for the wallet: + + const { data, error, mutate } = useSWR(`integration-api/withdrawal-operation/${withdrawalId}`); + + if (typeof error !== 'undefined') { + console.log(`withdrawal (${withdrawalId}) was never (correctly) created at the bank...`, error); + pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + hasError: true, + error: i18n`withdrawal (${withdrawalId}) was never (correctly) created at the bank...` + })) + return (<Fragment><br /><br />{abortButton}</Fragment>); + } + + // data didn't arrive yet and wallet didn't communicate: + if (typeof data === 'undefined') + return <p>{i18n`Waiting the bank to create the operaion...`}</p> + + + /** + * Wallet didn't communicate withdrawal details yet: + */ + console.log('withdrawal status', data); + if (data.aborted) + pageStateSetter((prevState: PageStateType) => { + const { + withdrawalId, + talerWithdrawUri, + ...rest } = prevState; + return { + ...rest, + withdrawalInProgress: false, + hasError: true, + error: i18n`This withdrawal was aborted!` + }; + }) + + + if (!data.selection_done) { + setTimeout(() => mutate(), 1000); // check again after 1 second. + return (<QrCodeSection talerWithdrawUri={talerWithdrawUri} abortButton={abortButton} />); + } + /** + * Wallet POSTed the withdrawal details! Ask the + * user to authorize the operation (here CAPTCHA). + */ + return (<TalerWithdrawalConfirmationQuestion backendState={backendState} />); +} + + + +function WalletWithdraw(Props: any): VNode { + const { backendState, pageStateSetter, focus } = Props; + const currency = useContext(CurrencyContext); + const i18n = useTranslator(); + let submitAmount = '5.00'; + const amountRegex = '^[0-9]+(\.[0-9]+)?$'; + + const ref = useRef<HTMLInputElement>(null) + useEffect(() => { + if (focus) ref.current?.focus(); + }, [focus]); + return ( + <div id="reserve-form" + class="pure-form" + name="tform"> + <p> + <label for="withdraw-amount">{i18n`Amount to withdraw:`}</label> + <input + type="number" + ref={ref} + id="withdraw-amount" + name="withdraw-amount" + value={submitAmount} + pattern={amountRegex} + class="amount" + onChange={(e): void => { + // FIXME: validate using 'parseAmount()', + // deactivate submit button as long as + // amount is not valid + submitAmount = e.currentTarget.value; + }} /> + + <input + type="text" + readonly + class="currency-indicator" + size={currency.length} + maxLength={currency.length} + tabIndex={-1} value={currency} /> + </p> + <p> + <div> + <input + id="select-exchange" + class="pure-button pure-button-primary" + type="submit" + value={i18n`Withdraw`} + onClick={() => { + submitAmount = validateAmount(submitAmount); + /** + * By invalid amounts, the validator prints error messages + * on the console, and the browser colourizes the amount input + * box to indicate a error. + */ + if (!submitAmount) return; + createWithdrawalCall( + `${currency}:${submitAmount}`, + backendState, + pageStateSetter + ) + }} /> + </div> + </p> + </div> + ) +} + + +/** + * Let the user choose a payment option, + * then specify the details trigger the action. + */ +function PaymentOptions(Props: any): VNode { + const { backendState, pageStateSetter, focus } = Props; + const currency = useContext(CurrencyContext); + const i18n = useTranslator(); + + const [tab, setTab] = useState<'charge-wallet' | 'wire-transfer'>('charge-wallet') + + + return (<article> + <div class="payments"> + <div class="tab"> + <button class={tab === 'charge-wallet' ? 'tablinks active' : 'tablinks'} + onClick={(): void => { setTab('charge-wallet') }}> + {i18n`Charge Taler wallet`} + </button> + <button class={tab === 'wire-transfer' ? 'tablinks active' : 'tablinks'} + onClick={(): void => { setTab('wire-transfer') }}> + {i18n`Wire to bank account`} + </button> + </div> + {tab === 'charge-wallet' && + <div id='charge-wallet' class='tabcontent active'> + <h3>{i18n`Charge Taler wallet`}</h3> + <WalletWithdraw + backendState={backendState} + focus + pageStateSetter={pageStateSetter} /> + </div> + } + {tab === 'wire-transfer' && + <div id='wire-transfer' class='tabcontent active'> + <h3>{i18n`Wire to bank account`}</h3> + <PaytoWireTransfer + backendState={backendState} + focus + pageStateSetter={pageStateSetter} /> + </div> + } + </div> + </article>); +} + +function RegistrationButton(Props: any): VNode { + const { backendStateSetter, pageStateSetter } = Props; + const i18n = useTranslator(); + if (UI_ALLOW_REGISTRATIONS) + return (<button + class="pure-button pure-button-secondary btn-cancel" + onClick={() => { + pageStateSetter((prevState: PageStateType) => ({ ...prevState, tryRegister: true })) + }}> + {i18n`Register`} + </button>); + + + return (<span />); + +} + +/** + * Collect and submit login data. + */ +function LoginForm(Props: any): VNode { + const { backendStateSetter, pageStateSetter } = Props; + const [submitData, submitDataSetter] = useCredentialsRequestType(); + const i18n = useTranslator(); + const ref = useRef<HTMLInputElement>(null) + useEffect(() => { + ref.current?.focus(); + }, []); + return (<div class="login-div"> + <form action="javascript:void(0);" class="login-form"> + <div class="pure-form"> + <h2>{i18n`Please login!`}</h2> + <p class="unameFieldLabel loginFieldLabel formFieldLabel"><label for="username">{i18n`Username:`}</label></p> + <input + ref={ref} + autoFocus + type="text" + name="username" + id="username" + value={submitData && submitData.username} + placeholder="Username" + required + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + username: e.currentTarget.value, + })) + }} + /> + <p class="passFieldLabel loginFieldLabel formFieldLabel"><label for="password">{i18n`Password:`}</label></p> + <input + type="password" + name="password" + id="password" + value={submitData && submitData.password} + placeholder="Password" + required + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + password: e.currentTarget.value, + })) + }} /> + <br /> + <button + type="submit" + class="pure-button pure-button-primary" + onClick={() => { + if (typeof submitData === 'undefined') { + console.log('login data is undefined', submitData); + return; + } + if (submitData.password.length == 0 || submitData.username.length == 0) { + console.log('username or password is the empty string', submitData); + return; + } + loginCall( + // Deep copy, to avoid the cleanup + // below make data disappear. + { ...submitData }, + backendStateSetter, + pageStateSetter + ); + submitDataSetter(undefined); + }}>{i18n`Login`} + </button> + {RegistrationButton(Props)} + </div> + </form> + </div>); +} + +/** + * Collect and submit registration data. + */ +function RegistrationForm(Props: any): VNode { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [pageState, pageStateSetter] = useContext(PageContext); + const [submitData, submitDataSetter] = useCredentialsRequestType(); + const i18n = useTranslator(); + // https://stackoverflow.com/questions/36683770/how-to-get-the-value-of-an-input-field-using-reactjs + return ( + <Fragment> + <h1 class="nav"> + { + i18n`Welcome to ${UI_BANK_NAME}!` + } + </h1> + <article> + <div class="register-div"> + <form action="javascript:void(0);" class="register-form"> + <div class="pure-form"> + <h2>{i18n`Please register!`}</h2> + <p class="unameFieldLabel registerFieldLabel formFieldLabel"><label for="register-un">{i18n`Username:`}</label></p> + <input + id="register-un" + name="register-un" + type="text" + placeholder="Username" + value={submitData && submitData.username} + required + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + username: e.currentTarget.value, + })) + }} /> + <br /> + <p class="unameFieldLabel registerFieldLabel formFieldLabel"><label for="register-pw">{i18n`Password:`}</label></p> + <input + type="password" + name="register-pw" + id="register-pw" + placeholder="Password" + value={submitData && submitData.password} + required + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + password: e.currentTarget.value, + })) + }} /> + <br /> + {/* + <label for="phone">{i18n`Phone number:`}</label> + // FIXME: add input validation (must start with +, otherwise only numbers) + <input + name="phone" + id="phone" + type="phone" + placeholder="+CC-123456789" + value={submitData && submitData.phone} + required + onInput={(e): void => { + submitDataSetter((submitData: any) => ({ + ...submitData, + phone: e.currentTarget.value, + }))}} /> + <br /> + */} + <button + class="pure-button pure-button-primary btn-register" + onClick={() => { + console.log('maybe submitting the registration..'); + console.log(submitData); + if (typeof submitData === 'undefined') { + console.log(`submit data ${submitData} is undefined`); + return; + } + if ((typeof submitData.password === 'undefined') || + (typeof submitData.username === 'undefined')) { + console.log('username or password is undefined'); + return; + } + if (submitData.password.length === 0 || + submitData.username.length === 0) { + console.log('username or password are the empty string'); + return; + } + console.log('submitting the registration..'); + registrationCall( + { ...submitData }, + Props.backendStateSetter, // will store BE URL, if OK. + pageStateSetter + ); + console.log('Clearing the input data'); + /** + * FIXME: clearing the data should be done by setting + * it to undefined, instead of the empty strings, just + * like done in the login function. Now set to the empty + * strings due to a non lively update of the <input> fields + * after setting to undefined. + */ + submitDataSetter({ username: '', password: '' }) + }}> + {i18n`Register`} + </button> + {/* FIXME: should use a different color */} + <button + class="pure-button pure-button-secondary btn-cancel" + onClick={() => { + pageStateSetter((prevState: PageStateType) => ({ ...prevState, tryRegister: false })) + }}> + {i18n`Cancel`} + </button> + </div> + </form> + </div> + </article> + </Fragment> + ) +} + +/** + * Show one page of transactions. + */ +function Transactions(Props: any): VNode { + const { pageNumber, accountLabel } = Props; + const i18n = useTranslator(); + const { data, error } = useSWR( + `access-api/accounts/${accountLabel}/transactions?page=${pageNumber}` + ); + if (typeof error !== 'undefined') { + console.log('transactions not found error', error); + switch (error.status) { + case 404: { + return <p>Transactions page {pageNumber} was not found.</p> + } + case 401: { + return <p>Wrong credentials given.</p> + } + default: { + return <p>Transaction page {pageNumber} could not be retrieved.</p> + } + } + } + if (!data) { + console.log(`History data of ${accountLabel} not arrived`); + return <p>"Transactions page loading..."</p>; + } + console.log(`History data of ${accountLabel}`, data); + return (<div class="results"> + <table class="pure-table pure-table-striped"> + <thead> + <tr> + <th>{i18n`Date`}</th> + <th>{i18n`Amount`}</th> + <th>{i18n`Counterpart`}</th> + <th>{i18n`Subject`}</th> + </tr> + </thead> + <tbody> + {data.transactions.map((item: any, idx: number) => { + const sign = item.direction == 'DBIT' ? '-' : ''; + const counterpart = item.direction == 'DBIT' ? item.creditorIban : item.debtorIban; + // Pattern: + // + // DD/MM YYYY subject -5 EUR + // DD/MM YYYY subject 5 EUR + const dateRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{1,2})/ + const dateParse = dateRegex.exec(item.date) + const date = dateParse !== null ? `${dateParse[3]}/${dateParse[2]} ${dateParse[1]}` : 'date not found' + return (<tr key={idx}> + <td>{date}</td> + <td>{sign}{item.amount} {item.currency}</td> + <td>{counterpart}</td> + <td>{item.subject}</td> + </tr>); + })} + </tbody> + </table> + </div>); +} + +/** + * Show only the account's balance. NOTE: the backend state + * is mostly needed to provide the user's credentials to POST + * to the bank. + */ +function Account(Props: any): VNode { + const { cache } = useSWRConfig(); + const { accountLabel, backendState } = Props; + // Getting the bank account balance: + const endpoint = `access-api/accounts/${accountLabel}`; + const { data, error } = useSWR(endpoint); + const [pageState, pageStateSetter] = useContext(PageContext); + const { + withdrawalInProgress, + withdrawalId, + isLoggedIn, + talerWithdrawUri } = pageState; + const i18n = useTranslator(); + /** + * This part shows a list of transactions: with 5 elements by + * default and offers a "load more" button. + */ + const [txPageNumber, setTxPageNumber] = useTransactionPageNumber() + const txsPages = [] + for (let i = 0; i <= txPageNumber; i++) + txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} />) + + if (typeof error !== 'undefined') { + console.log('account error', error); + /** + * FIXME: to minimize the code, try only one invocation + * of pageStateSetter, after having decided the error + * message in the case-branch. + */ + switch (error.status) { + case 404: { + pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + hasError: true, + isLoggedIn: false, + error: i18n`Username or account label '${accountLabel}' not found. Won't login.` + })); + + /** + * 404 should never stick to the cache, because they + * taint successful future registrations. How? After + * registering, the user gets navigated to this page, + * therefore a previous 404 on this SWR key (the requested + * resource) would still appear as valid and cause this + * page not to be shown! A typical case is an attempted + * login of a unregistered user X, and then a registration + * attempt of the same user X: in this case, the failed + * login would cache a 404 error to X's profile, resulting + * in the legitimate request after the registration to still + * be flagged as 404. Clearing the cache should prevent + * this. */ + (cache as any).clear(); + return <p>Profile not found...</p>; + } + case 401: { + pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + hasError: true, + isLoggedIn: false, + error: i18n`Wrong credentials given.` + })); + return <p>Wrong credentials...</p>; + } + default: { + pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + hasError: true, + isLoggedIn: false, + error: i18n`Account information could not be retrieved.` + })); + return <p>Unknown problem...</p>; + } + } + } + if (!data) return <p>Retrieving the profile page...</p>; + + /** + * This block shows the withdrawal QR code. + * + * A withdrawal operation replaces everything in the page and + * (ToDo:) starts polling the backend until either the wallet + * selected a exchange and reserve public key, or a error / abort + * happened. + * + * After reaching one of the above states, the user should be + * brought to this ("Account") page where they get informed about + * the outcome. + */ + console.log(`maybe new withdrawal ${talerWithdrawUri}`); + if (talerWithdrawUri) { + console.log('Bank created a new Taler withdrawal'); + return ( + <BankFrame> + <TalerWithdrawalQRCode + accountLabel={accountLabel} + backendState={backendState} + withdrawalId={withdrawalId} + talerWithdrawUri={talerWithdrawUri} /> + </BankFrame> + ); + } + const balance = parseAmount(data.balance.amount) + + return (<BankFrame> + <div> + <h1 class="nav welcome-text"> + <Translate>Welcome, {accountLabel} ({getIbanFromPayto(data.paytoUri)})!</Translate> + </h1> + </div> + <section id="assets"> + <div class="asset-summary"> + <h2>{i18n`Bank account balance`}</h2> + {data.balance.credit_debit_indicator == 'debit' ? (<b>-</b>) : null} + <div class="large-amount amount"><span class="value">{`${balance.value}`}</span> <span class="currency">{`${balance.currency}`}</span></div> + </div> + </section> + <section id="payments"> + <div class="payments"> + <h2>{i18n`Payments`}</h2> + {/* FIXME: turn into button! */} + <CurrencyContext.Provider value={balance.currency}> + {Props.children} + <PaymentOptions + backendState={backendState} + pageStateSetter={pageStateSetter} /> + </CurrencyContext.Provider> + </div> + </section> + <section id="main"> + <article> + <h2>{i18n`Latest transactions:`}</h2> + <Transactions pageNumber="0" accountLabel={accountLabel} /> + </article> + </section> + </BankFrame>); +} + +/** + * Factor out login credentials. + */ +function SWRWithCredentials(props: any): VNode { + const { username, password, backendUrl } = props; + const headers = new Headers(); + headers.append( + 'Authorization', + `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}` + ); + console.log('Likely backend base URL', backendUrl); + return ( + <SWRConfig + value={{ + fetcher: (url: string) => + fetch(backendUrl + url || '', { headers }).then( + (r) => { + if (!r.ok) + throw { status: r.status, json: r.json() }; + + return r.json() + } + ), + }}>{props.children}</SWRConfig> + ); +} + +function SWRWithoutCredentials(Props: any): VNode { + const { baseUrl } = Props; + console.log('Base URL', baseUrl); + return ( + <SWRConfig + value={{ + fetcher: (url: string) => + fetch(baseUrl + url || '').then( + (r) => { + if (!r.ok) + throw { status: r.status, json: r.json() }; + + return r.json() + } + ), + }}>{Props.children}</SWRConfig> + ); +} + +/** + * Show histories of public accounts. + */ +function PublicHistories(Props: any): VNode { + const [showAccount, setShowAccount] = useShowPublicAccount(); + const { data, error } = useSWR('access-api/public-accounts'); + const i18n = useTranslator(); + + if (typeof error !== 'undefined') { + console.log('account error', error); + switch (error.status) { + case 404: + console.log('public accounts: 404', error); + Props.pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + hasError: true, + showPublicHistories: false, + error: i18n`List of public accounts was not found.` + })); + break; + default: + console.log('public accounts: non-404 error', error); + Props.pageStateSetter((prevState: PageStateType) => ({ + ...prevState, + hasError: true, + showPublicHistories: false, + error: i18n`List of public accounts could not be retrieved.` + })); + break; + } + } + if (!data) + return (<p>Waiting public accounts list...</p>) + const txs: any = {}; + const accountsBar = []; + + /** + * Show the account specified in the props, or just one + * from the list if that's not given. + */ + if (typeof showAccount === 'undefined' && data.publicAccounts.length > 0) + setShowAccount(data.publicAccounts[1].accountLabel); + console.log(`Public history tab: ${showAccount}`); + + // Ask story of all the public accounts. + for (const account of data.publicAccounts) { + console.log('Asking transactions for', account.accountLabel) + const isSelected = account.accountLabel == showAccount; + accountsBar.push( + <li class={isSelected ? 'pure-menu-selected pure-menu-item' : 'pure-menu-item pure-menu'}> + <a href="#" + class="pure-menu-link" + onClick={() => setShowAccount(account.accountLabel)}>{account.accountLabel}</a> + </li> + ); + txs[account.accountLabel] = <Transactions accountLabel={account.accountLabel} pageNumber={0} /> + } + + return (<Fragment> + <h1 class="nav">{i18n`History of public accounts`}</h1> + <section id="main"> + <article> + <div class="pure-menu pure-menu-horizontal" name="accountMenu"> + <ul class="pure-menu-list">{accountsBar}</ul> + {typeof showAccount !== 'undefined' ? txs[showAccount] : <p>No public transactions found.</p>} + {Props.children} + </div> + </article> + </section> + </Fragment>); +} + +/** + * If the user is logged in, it displays + * the balance, otherwise it offers to login. + */ +export function BankHome(): VNode { + const [backendState, backendStateSetter] = useBackendState(); + const [pageState, pageStateSetter] = usePageState(); + const [accountState, accountStateSetter] = useAccountState(); + const setTxPageNumber = useTransactionPageNumber()[1]; + const i18n = useTranslator(); + + if (pageState.showPublicHistories) + return (<SWRWithoutCredentials baseUrl={getRootPath()}> + <PageContext.Provider value={[pageState, pageStateSetter]}> + <BankFrame> + <PublicHistories pageStateSetter={pageStateSetter}> + <br /> + <a class="pure-button" onClick={() => { + pageStateSetter((prevState: PageStateType) => + ({ ...prevState, showPublicHistories: false })) + }}>Go back</a> + </PublicHistories> + </BankFrame> + </PageContext.Provider> + </SWRWithoutCredentials>); + + if (pageState.tryRegister) { + console.log('allow registrations?', UI_ALLOW_REGISTRATIONS); + if (UI_ALLOW_REGISTRATIONS) + return ( + <PageContext.Provider value={[pageState, pageStateSetter]}> + <BankFrame> + <RegistrationForm backendStateSetter={backendStateSetter} /> + </BankFrame> + </PageContext.Provider> + ); + + return ( + <PageContext.Provider value={[pageState, pageStateSetter]}> + <BankFrame> + <p>{i18n`Currently, the bank is not accepting new registrations!`}</p> + </BankFrame> + </PageContext.Provider> + ); + } + if (pageState.isLoggedIn) { + if (typeof backendState === 'undefined') { + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + isLoggedIn: false, + error: i18n`Page has a problem: logged in but backend state is lost.` + })); + return (<p>Error: waiting for details...</p>); + } + console.log('Showing the profile page..'); + return ( + <SWRWithCredentials + username={backendState.username} + password={backendState.password} + backendUrl={backendState.url}> + <PageContext.Provider value={[pageState, pageStateSetter]}> + <Account accountLabel={backendState.username} backendState={backendState} /> + </PageContext.Provider> + </SWRWithCredentials> + ); + } // end of logged-in state. + + return ( + <PageContext.Provider value={[pageState, pageStateSetter]}> + <BankFrame> + <h1 class="nav"> + { + i18n`Welcome to ${UI_BANK_NAME}!` + } + </h1> + <LoginForm + pageStateSetter={pageStateSetter} + backendStateSetter={backendStateSetter} /> + </BankFrame> + </PageContext.Provider> + ); +} diff --git a/packages/demobank-ui/src/pages/notfound/index.tsx b/packages/demobank-ui/src/pages/notfound/index.tsx new file mode 100644 index 000000000..fd99259a1 --- /dev/null +++ b/packages/demobank-ui/src/pages/notfound/index.tsx @@ -0,0 +1,16 @@ +import { FunctionalComponent, h } from 'preact'; +import { Link } from 'preact-router/match'; + +const Notfound: FunctionalComponent = () => { + return ( + <div> + <h1>Error 404</h1> + <p>That page doesn't exist.</p> + <Link href="/"> + <h4>Back to Home</h4> + </Link> + </div> + ); +}; + +export default Notfound; diff --git a/packages/demobank-ui/src/pages/notfound/style.css b/packages/demobank-ui/src/pages/notfound/style.css new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/demobank-ui/src/pages/notfound/style.css diff --git a/packages/demobank-ui/src/pages/profile/index.stories.tsx b/packages/demobank-ui/src/pages/profile/index.stories.tsx new file mode 100644 index 000000000..15fd7c7e5 --- /dev/null +++ b/packages/demobank-ui/src/pages/profile/index.stories.tsx @@ -0,0 +1,38 @@ +/* + 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 } from 'preact'; +import Profile from './index'; + + +export default { + title: 'Profile/View', + component: Profile, + argTypes: { + onSelect: { action: 'onSelect' }, + }, +}; + +export const Empty = (a: any) => <Profile {...a} />; +Empty.args = { + instances: [] +} + diff --git a/packages/demobank-ui/src/pages/profile/index.tsx b/packages/demobank-ui/src/pages/profile/index.tsx new file mode 100644 index 000000000..3b9824488 --- /dev/null +++ b/packages/demobank-ui/src/pages/profile/index.tsx @@ -0,0 +1,42 @@ +import { FunctionalComponent, h } from 'preact'; +import { useEffect, useState } from 'preact/hooks'; + +interface Props { + user: string; +} + +const Profile: FunctionalComponent<Props> = (props: Props) => { + const { user } = props; + const [time, setTime] = useState<number>(Date.now()); + const [count, setCount] = useState<number>(0); + + // gets called when this route is navigated to + useEffect(() => { + const timer = window.setInterval(() => setTime(Date.now()), 1000); + + // gets called just before navigating away from the route + return (): void => { + clearInterval(timer); + }; + }, []); + + // update the current time + const increment = (): void => { + setCount(count + 1); + }; + + return ( + <div> + <h1>Profile: {user}</h1> + <p>This is the user profile for a user named {user}.</p> + + <div>Current time: {new Date(time).toLocaleString()}</div> + + <p> + <button onClick={increment}>Click Me</button> Clicked {count} times. + </p> + </div> + ); +}; + +export default Profile; diff --git a/packages/demobank-ui/src/pages/profile/style.css b/packages/demobank-ui/src/pages/profile/style.css new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/demobank-ui/src/pages/profile/style.css diff --git a/packages/demobank-ui/src/scss/DurationPicker.scss b/packages/demobank-ui/src/scss/DurationPicker.scss new file mode 100644 index 000000000..aa75b9916 --- /dev/null +++ b/packages/demobank-ui/src/scss/DurationPicker.scss @@ -0,0 +1,70 @@ +.rdp-picker { + display: flex; + height: 175px; +} + +@media (max-width: 400px) { + .rdp-picker { + width: 250px; + } +} + +.rdp-masked-div { + overflow: hidden; + height: 175px; + position: relative; +} + +.rdp-column-container { + flex-grow: 1; + display: inline-block; +} + +.rdp-column { + position: absolute; + z-index: 0; + width: 100%; +} + +.rdp-reticule { + border: 0; + border-top: 2px solid rgba(109, 202, 236, 1); + height: 2px; + position: absolute; + width: 80%; + margin: 0; + z-index: 100; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} + +.rdp-text-overlay { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + height: 35px; + font-size: 20px; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} + +.rdp-cell div { + font-size: 17px; + color: gray; + font-style: italic; +} + +.rdp-cell { + display: flex; + align-items: center; + justify-content: center; + height: 35px; + font-size: 18px; +} + +.rdp-center { + font-size: 25px; +} diff --git a/packages/demobank-ui/src/scss/_aside.scss b/packages/demobank-ui/src/scss/_aside.scss new file mode 100644 index 000000000..11809990b --- /dev/null +++ b/packages/demobank-ui/src/scss/_aside.scss @@ -0,0 +1,128 @@ +/* + 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) + */ + +html { + &.has-aside-left { + &.has-aside-expanded { + nav.navbar, + body { + padding-left: $aside-width; + } + } + aside.is-placed-left { + display: block; + } + } +} + +aside.aside.is-expanded { + width: $aside-width; + + .menu-list { + @include icon-with-update-mark($aside-icon-width); + + span.menu-item-label { + display: inline-block; + } + + li.is-active { + ul { + display: block; + } + background-color: $body-background-color; + } + } +} + +aside.aside { + display: none; + position: fixed; + top: 0; + left: 0; + z-index: 40; + height: 100vh; + padding: 0; + box-shadow: $aside-box-shadow; + background: $aside-background-color; + + .aside-tools { + display: flex; + flex-direction: row; + width: 100%; + background-color: $aside-tools-background-color; + color: $aside-tools-color; + line-height: $navbar-height; + height: $navbar-height; + padding-left: $default-padding * 0.5; + flex: 1; + + .icon { + margin-right: $default-padding * 0.5; + } + } + + .menu-list { + li { + a { + &.has-dropdown-icon { + position: relative; + padding-right: $aside-icon-width; + + .dropdown-icon { + position: absolute; + top: $size-base * 0.5; + right: 0; + } + } + } + ul { + display: none; + border-left: 0; + background-color: darken($base-color, 2.5%); + padding-left: 0; + margin: 0 0 $default-padding * 0.5; + + li { + a { + padding: $default-padding * 0.5 0 $default-padding * 0.5 + $default-padding * 0.5; + font-size: $aside-submenu-font-size; + + &.has-icon { + padding-left: 0; + } + &.is-active { + &:not(:hover) { + background: transparent; + } + } + } + } + } + } + } + + .menu-label { + padding: 0 $default-padding * 0.5; + margin-top: $default-padding * 0.5; + margin-bottom: $default-padding * 0.5; + } +} diff --git a/packages/demobank-ui/src/scss/_card.scss b/packages/demobank-ui/src/scss/_card.scss new file mode 100644 index 000000000..3f71aeb6a --- /dev/null +++ b/packages/demobank-ui/src/scss/_card.scss @@ -0,0 +1,69 @@ +/* + 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) + */ + +.card:not(:last-child) { + margin-bottom: $default-padding; +} + +.card { + border-radius: $radius-large; + border: $card-border; + + &.has-table { + .card-content { + padding: 0; + } + .b-table { + border-radius: $radius-large; + overflow: hidden; + } + } + + &.is-card-widget { + .card-content { + padding: $default-padding * 0.5; + } + } + + .card-header { + border-bottom: 1px solid $base-color-light; + } + + .card-content { + hr { + margin-left: $card-content-padding * -1; + margin-right: $card-content-padding * -1; + } + } + + .is-widget-icon { + .icon { + width: 5rem; + height: 5rem; + } + } + + .is-widget-label { + .subtitle { + color: $grey; + } + } +} diff --git a/packages/demobank-ui/src/scss/_custom-calendar.scss b/packages/demobank-ui/src/scss/_custom-calendar.scss new file mode 100644 index 000000000..e0334b62d --- /dev/null +++ b/packages/demobank-ui/src/scss/_custom-calendar.scss @@ -0,0 +1,263 @@ +/* + 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/> + */ + +:root { + --primary-color: #3298dc; + + --primary-text-color-dark: rgba(0, 0, 0, 0.87); + --secondary-text-color-dark: rgba(0, 0, 0, 0.57); + --disabled-text-color-dark: rgba(0, 0, 0, 0.13); + + --primary-text-color-light: rgba(255, 255, 255, 0.87); + --secondary-text-color-light: rgba(255, 255, 255, 0.57); + --disabled-text-color-light: rgba(255, 255, 255, 0.13); + + --font-stack: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + + --primary-card-color: #fff; + --primary-background-color: #f2f2f2; + + --box-shadow-lvl-1: 0 1px 3px rgba(0, 0, 0, 0.12), + 0 1px 2px rgba(0, 0, 0, 0.24); + --box-shadow-lvl-2: 0 3px 6px rgba(0, 0, 0, 0.16), + 0 3px 6px rgba(0, 0, 0, 0.23); + --box-shadow-lvl-3: 0 10px 20px rgba(0, 0, 0, 0.19), + 0 6px 6px rgba(0, 0, 0, 0.23); + --box-shadow-lvl-4: 0 14px 28px rgba(0, 0, 0, 0.25), + 0 10px 10px rgba(0, 0, 0, 0.22); +} + +.home .datePicker div { + margin-top: 0px; + margin-bottom: 0px; +} +.datePicker { + text-align: left; + background: var(--primary-card-color); + border-radius: 3px; + z-index: 200; + position: fixed; + height: auto; + max-height: 90vh; + width: 90vw; + max-width: 448px; + transform-origin: top left; + transition: transform 0.22s ease-in-out, opacity 0.22s ease-in-out; + top: 50%; + left: 50%; + opacity: 0; + transform: scale(0) translate(-50%, -50%); + user-select: none; + + &.datePicker--opened { + opacity: 1; + transform: scale(1) translate(-50%, -50%); + } + + .datePicker--titles { + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 24px; + height: 100px; + background: var(--primary-color); + + h2, + h3 { + cursor: pointer; + color: #fff; + line-height: 1; + padding: 0; + margin: 0; + font-size: 32px; + } + + h3 { + color: rgba(255, 255, 255, 0.57); + font-size: 18px; + padding-bottom: 2px; + } + } + + nav { + padding: 20px; + height: 56px; + + h4 { + width: calc(100% - 60px); + text-align: center; + display: inline-block; + padding: 0; + font-size: 14px; + line-height: 24px; + margin: 0; + position: relative; + top: -9px; + color: var(--primary-text-color); + } + + i { + cursor: pointer; + color: var(--secondary-text-color); + font-size: 26px; + user-select: none; + border-radius: 50%; + + &:hover { + background: var(--disabled-text-color-dark); + } + } + } + + .datePicker--scroll { + overflow-y: auto; + max-height: calc(90vh - 56px - 100px); + } + + .datePicker--calendar { + padding: 0 20px; + + .datePicker--dayNames { + width: 100%; + display: grid; + text-align: center; + + // there's probably a better way to do this, but wanted to try out CSS grid + grid-template-columns: calc(100% / 7) calc(100% / 7) calc(100% / 7) calc( + 100% / 7 + ) calc(100% / 7) calc(100% / 7) calc(100% / 7); + + span { + color: var(--secondary-text-color-dark); + font-size: 14px; + line-height: 42px; + display: inline-grid; + } + } + + .datePicker--days { + width: 100%; + display: grid; + text-align: center; + grid-template-columns: calc(100% / 7) calc(100% / 7) calc(100% / 7) calc( + 100% / 7 + ) calc(100% / 7) calc(100% / 7) calc(100% / 7); + + span { + color: var(--primary-text-color-dark); + line-height: 42px; + font-size: 14px; + display: inline-grid; + transition: color 0.22s; + height: 42px; + position: relative; + cursor: pointer; + user-select: none; + border-radius: 50%; + + &::before { + content: ""; + position: absolute; + z-index: -1; + height: 42px; + width: 42px; + left: calc(50% - 21px); + background: var(--primary-color); + border-radius: 50%; + transition: transform 0.22s, opacity 0.22s; + transform: scale(0); + opacity: 0; + } + + &[disabled="true"] { + cursor: unset; + } + + &.datePicker--today { + font-weight: 700; + } + + &.datePicker--selected { + color: rgba(255, 255, 255, 0.87); + + &:before { + transform: scale(1); + opacity: 1; + } + } + } + } + } + + .datePicker--selectYear { + padding: 0 20px; + display: block; + width: 100%; + text-align: center; + max-height: 362px; + + span { + display: block; + width: 100%; + font-size: 24px; + margin: 20px auto; + cursor: pointer; + + &.selected { + font-size: 42px; + color: var(--primary-color); + } + } + } + + div.datePicker--actions { + width: 100%; + padding: 8px; + text-align: right; + + button { + margin-bottom: 0; + font-size: 15px; + cursor: pointer; + color: var(--primary-text-color); + border: none; + margin-left: 8px; + min-width: 64px; + line-height: 36px; + background-color: transparent; + appearance: none; + padding: 0 16px; + border-radius: 3px; + transition: background-color 0.13s; + + &:hover, + &:focus { + outline: none; + background-color: var(--disabled-text-color-dark); + } + } + } +} + +.datePicker--background { + z-index: 199; + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: rgba(0, 0, 0, 0.52); + animation: fadeIn 0.22s forwards; +} diff --git a/packages/demobank-ui/src/scss/_footer.scss b/packages/demobank-ui/src/scss/_footer.scss new file mode 100644 index 000000000..112522ed8 --- /dev/null +++ b/packages/demobank-ui/src/scss/_footer.scss @@ -0,0 +1,35 @@ +/* + 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) + */ + +footer.footer { + .logo { + img { + width: auto; + height: $footer-logo-height; + } + } +} + +@include mobile { + .footer-copyright { + text-align: center; + } +} diff --git a/packages/demobank-ui/src/scss/_form.scss b/packages/demobank-ui/src/scss/_form.scss new file mode 100644 index 000000000..786044eff --- /dev/null +++ b/packages/demobank-ui/src/scss/_form.scss @@ -0,0 +1,71 @@ +/* + 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) + */ + +.field { + &.has-check { + .field-body { + margin-top: $default-padding * 0.125; + } + } + .control { + .mdi-24px.mdi-set, + .mdi-24px.mdi:before { + font-size: inherit; + } + } +} +.upload { + .upload-draggable { + display: block; + } +} + +.input, +.textarea, +select { + box-shadow: none; + + &:focus, + &:active { + box-shadow: none !important; + } +} + +.switch input[type="checkbox"] + .check:before { + box-shadow: none; +} + +.switch, +.b-checkbox.checkbox { + input[type="checkbox"] { + &:focus + .check, + &:focus:checked + .check { + box-shadow: none !important; + } + } +} + +.b-checkbox.checkbox input[type="checkbox"], +.b-radio.radio input[type="radio"] { + & + .check { + border: $checkbox-border; + } +} diff --git a/packages/demobank-ui/src/scss/_hero-bar.scss b/packages/demobank-ui/src/scss/_hero-bar.scss new file mode 100644 index 000000000..31b7e623e --- /dev/null +++ b/packages/demobank-ui/src/scss/_hero-bar.scss @@ -0,0 +1,55 @@ +/* + 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) + */ + +section.hero.is-hero-bar { + background-color: $hero-bar-background; + border-bottom: $light-border; + + .hero-body { + padding: $default-padding; + + .level-item { + &.is-hero-avatar-item { + margin-right: $default-padding; + } + + > div > .level { + margin-bottom: $default-padding * 0.5; + } + + .subtitle + p { + margin-top: $default-padding * 0.5; + } + } + + .button { + &.is-hero-button { + background-color: rgba($white, 0.5); + font-weight: 300; + @include transition(background-color); + + &:hover { + background-color: $white; + } + } + } + } +} diff --git a/packages/demobank-ui/src/scss/_loading.scss b/packages/demobank-ui/src/scss/_loading.scss new file mode 100644 index 000000000..d25bf8048 --- /dev/null +++ b/packages/demobank-ui/src/scss/_loading.scss @@ -0,0 +1,51 @@ +/* + 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/> + */ + +.lds-ring { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} +.lds-ring div { + box-sizing: border-box; + display: block; + position: absolute; + width: 64px; + height: 64px; + margin: 8px; + border: 8px solid black; + border-radius: 50%; + animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: black transparent transparent transparent; +} +.lds-ring div:nth-child(1) { + animation-delay: -0.45s; +} +.lds-ring div:nth-child(2) { + animation-delay: -0.3s; +} +.lds-ring div:nth-child(3) { + animation-delay: -0.15s; +} +@keyframes lds-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/packages/demobank-ui/src/scss/_main-section.scss b/packages/demobank-ui/src/scss/_main-section.scss new file mode 100644 index 000000000..01edc24bf --- /dev/null +++ b/packages/demobank-ui/src/scss/_main-section.scss @@ -0,0 +1,24 @@ +/* + 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) + */ + +section.section.is-main-section { + padding-top: $default-padding; +} diff --git a/packages/demobank-ui/src/scss/_misc.scss b/packages/demobank-ui/src/scss/_misc.scss new file mode 100644 index 000000000..65bd28dbd --- /dev/null +++ b/packages/demobank-ui/src/scss/_misc.scss @@ -0,0 +1,50 @@ +/* + 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) + */ + +.is-user-avatar { + &.has-max-width { + max-width: $size-base * 7; + } + + &.is-aligned-center { + margin: 0 auto; + } + + img { + margin: 0 auto; + border-radius: $radius-rounded; + } +} + +.icon.has-update-mark { + position: relative; + + &:after { + content: ""; + width: $icon-update-mark-size; + height: $icon-update-mark-size; + position: absolute; + top: 1px; + right: 1px; + background-color: $icon-update-mark-color; + border-radius: $radius-rounded; + } +} diff --git a/packages/demobank-ui/src/scss/_mixins.scss b/packages/demobank-ui/src/scss/_mixins.scss new file mode 100644 index 000000000..b52e590e3 --- /dev/null +++ b/packages/demobank-ui/src/scss/_mixins.scss @@ -0,0 +1,34 @@ +/* + 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) + */ + +@mixin transition($t) { + transition: $t 250ms ease-in-out 50ms; +} + +@mixin icon-with-update-mark($icon-base-width) { + .icon { + width: $icon-base-width; + + &.has-update-mark:after { + right: ($icon-base-width / 2) - 0.85; + } + } +} diff --git a/packages/demobank-ui/src/scss/_modal.scss b/packages/demobank-ui/src/scss/_modal.scss new file mode 100644 index 000000000..b3a31ebf1 --- /dev/null +++ b/packages/demobank-ui/src/scss/_modal.scss @@ -0,0 +1,35 @@ +/* + 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) + */ + +.modal-card { + width: $modal-card-width; +} + +.modal-card-foot { + background-color: $modal-card-foot-background-color; +} + +@include mobile { + .modal .animation-content .modal-card { + width: $modal-card-width-mobile; + margin: 0 auto; + } +} diff --git a/packages/demobank-ui/src/scss/_nav-bar.scss b/packages/demobank-ui/src/scss/_nav-bar.scss new file mode 100644 index 000000000..c6dd04263 --- /dev/null +++ b/packages/demobank-ui/src/scss/_nav-bar.scss @@ -0,0 +1,144 @@ +/* + 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) + */ + +nav.navbar { + box-shadow: $navbar-box-shadow; + + .navbar-item { + &.has-user-avatar { + .is-user-avatar { + margin-right: $default-padding * 0.5; + display: inline-flex; + width: $navbar-avatar-size; + height: $navbar-avatar-size; + } + } + + &.has-divider { + border-right: $navbar-divider-border; + } + + &.no-left-space { + padding-left: 0; + } + + &.has-dropdown { + padding-right: 0; + padding-left: 0; + + .navbar-link { + padding-right: $navbar-item-h-padding; + padding-left: $navbar-item-h-padding; + } + } + + &.has-control { + padding-top: 0; + padding-bottom: 0; + } + + .control { + .input { + color: $navbar-input-color; + border: 0; + box-shadow: none; + background: transparent; + + &::placeholder { + color: $navbar-input-placeholder-color; + } + } + } + } +} + +@include touch { + nav.navbar { + display: flex; + padding-right: 0; + + .navbar-brand { + flex: 1; + + &.is-right { + flex: none; + } + } + + .navbar-item { + &.no-left-space-touch { + padding-left: 0; + } + } + + .navbar-menu { + position: absolute; + width: 100vw; + padding-top: 0; + top: $navbar-height; + left: 0; + + .navbar-item { + .icon:first-child { + margin-right: $default-padding * 0.5; + } + + &.has-dropdown { + > .navbar-link { + background-color: $white-ter; + .icon:last-child { + display: none; + } + } + } + + &.has-user-avatar { + > .navbar-link { + display: flex; + align-items: center; + padding-top: $default-padding * 0.5; + padding-bottom: $default-padding * 0.5; + } + } + } + } + } +} + +@include desktop { + nav.navbar { + .navbar-item { + padding-right: $navbar-item-h-padding; + padding-left: $navbar-item-h-padding; + + &:not(.is-desktop-icon-only) { + .icon:first-child { + margin-right: $default-padding * 0.5; + } + } + &.is-desktop-icon-only { + span:not(.icon) { + display: none; + } + } + } + } +} diff --git a/packages/demobank-ui/src/scss/_table.scss b/packages/demobank-ui/src/scss/_table.scss new file mode 100644 index 000000000..b68d50e4f --- /dev/null +++ b/packages/demobank-ui/src/scss/_table.scss @@ -0,0 +1,179 @@ +/* + 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) + */ + +table.table { + thead { + th { + border-bottom-width: 1px; + } + } + + td, + th { + &.checkbox-cell { + .b-checkbox.checkbox:not(.button) { + margin-right: 0; + width: 20px; + + .control-label { + display: none; + padding: 0; + } + } + } + } + + td { + .image { + margin: 0 auto; + width: $table-avatar-size; + height: $table-avatar-size; + } + + &.is-progress-col { + min-width: 5rem; + vertical-align: middle; + } + } +} + +.b-table { + .table { + border: 0; + border-radius: 0; + } + + /* This stylizes buefy's pagination */ + .table-wrapper { + margin-bottom: 0; + } + + .table-wrapper + .level { + padding: $notification-padding; + padding-left: $card-content-padding; + padding-right: $card-content-padding; + margin: 0; + border-top: $base-color-light; + background: $notification-background-color; + + .pagination-link { + background: $button-background-color; + color: $button-color; + border-color: $button-border-color; + + &.is-current { + border-color: $button-active-border-color; + } + } + + .pagination-previous, + .pagination-next, + .pagination-link { + border-color: $button-border-color; + color: $base-color; + + &[disabled] { + background-color: transparent; + } + } + } +} + +@include mobile { + .card { + &.has-table { + .b-table { + .table-wrapper + .level { + .level-left + .level-right { + margin-top: 0; + } + } + } + } + &.has-mobile-sort-spaced { + .b-table { + .field.table-mobile-sort { + padding-top: $default-padding * 0.5; + } + } + } + } + .b-table { + .field.table-mobile-sort { + padding: 0 $default-padding * 0.5; + } + + .table-wrapper.has-mobile-cards { + tr { + box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1); + margin-bottom: 3px !important; + } + td { + &.is-progress-col { + span, + progress { + display: flex; + width: 45%; + align-items: center; + align-self: center; + } + } + + &.checkbox-cell, + &.is-image-cell { + border-bottom: 0 !important; + } + + &.checkbox-cell, + &.is-actions-cell { + &:before { + display: none; + } + } + + &.has-no-head-mobile { + &:before { + display: none; + } + + span { + display: block; + width: 100%; + } + + &.is-progress-col { + progress { + width: 100%; + } + } + + &.is-image-cell { + .image { + width: $table-avatar-size-mobile; + height: auto; + margin: 0 auto $default-padding * 0.25; + } + } + } + } + } + } +} diff --git a/packages/demobank-ui/src/scss/_theme-default.scss b/packages/demobank-ui/src/scss/_theme-default.scss new file mode 100644 index 000000000..538dfd4da --- /dev/null +++ b/packages/demobank-ui/src/scss/_theme-default.scss @@ -0,0 +1,136 @@ +/* + 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) + */ + +/* We'll need some initial vars to use here */ +@import "node_modules/bulma/sass/utilities/initial-variables"; + +/* Base: Size */ +$size-base: 1rem; +$default-padding: $size-base * 1.5; + +/* Default font */ +$family-sans-serif: "Nunito", sans-serif; + +/* Base color */ +$base-color: #2e323a; +$base-color-light: rgba(24, 28, 33, 0.06); + +/* General overrides */ +$primary: $turquoise; +$body-background-color: #f8f8f8; +$link: $blue; +$link-visited: $purple; +$light-border: 1px solid $base-color-light; +$hr-height: 1px; + +/* NavBar: specifics */ +$navbar-input-color: $grey-darker; +$navbar-input-placeholder-color: $grey-lighter; +$navbar-box-shadow: 0 1px 0 rgba(24, 28, 33, 0.04); +$navbar-divider-border: 1px solid rgba($grey-lighter, 0.25); +$navbar-item-h-padding: $default-padding * 0.75; +$navbar-avatar-size: 1.75rem; + +/* Aside: Bulma override */ +$menu-item-radius: 0; +$menu-list-link-padding: $size-base * 0.5 0; +$menu-label-color: lighten($base-color, 25%); +$menu-item-color: lighten($base-color, 30%); +$menu-item-hover-color: $white; +$menu-item-hover-background-color: darken($base-color, 3.5%); +$menu-item-active-color: $white; +$menu-item-active-background-color: darken($base-color, 2.5%); + +/* Aside: specifics */ +$aside-width: $size-base * 14; +$aside-mobile-width: $size-base * 15; +$aside-icon-width: $size-base * 3; +$aside-submenu-font-size: $size-base * 0.95; +$aside-box-shadow: none; +$aside-background-color: $base-color; +$aside-tools-background-color: darken($aside-background-color, 10%); +$aside-tools-color: $white; + +/* Title Bar: specifics */ +$title-bar-color: $grey; +$title-bar-active-color: $black-ter; + +/* Hero Bar: specifics */ +$hero-bar-background: $white; + +/* Card: Bulma override */ +$card-shadow: none; +$card-header-shadow: none; + +/* Card: specifics */ +$card-border: 1px solid $base-color-light; +$card-header-border-bottom-color: $base-color-light; + +/* Table: Bulma override */ +$table-cell-border: 1px solid $white-bis; + +/* Table: specifics */ +$table-avatar-size: $size-base * 1.5; +$table-avatar-size-mobile: 25vw; + +/* Form */ +$checkbox-border: 1px solid $base-color; + +/* Modal card: Bulma override */ +$modal-card-head-background-color: $white-ter; +$modal-card-title-size: $size-base; +$modal-card-body-padding: $default-padding 20px; +$modal-card-head-border-bottom: 1px solid $white-ter; +$modal-card-foot-border-top: 0; + +/* Modal card: specifics */ +$modal-card-width: 80vw; +$modal-card-width-mobile: 90vw; +$modal-card-foot-background-color: $white-ter; + +/* Notification: Bulma override */ +$notification-padding: $default-padding * 0.75 $default-padding; + +/* Footer: Bulma override */ +$footer-background-color: $white; +$footer-padding: $default-padding * 0.33 $default-padding; + +/* Footer: specifics */ +$footer-logo-height: $size-base * 2; + +/* Progress: Bulma override */ +$progress-bar-background-color: $grey-lighter; + +/* Icon: specifics */ +$icon-update-mark-size: $size-base * 0.5; +$icon-update-mark-color: $yellow; + +$input-disabled-border-color: $grey-lighter; +$table-row-hover-background-color: hsl(0, 0%, 80%); + +.menu-list { + div { + border-radius: $menu-item-radius; + color: $menu-item-color; + display: block; + padding: $menu-list-link-padding; + } +} diff --git a/packages/demobank-ui/src/scss/_tiles.scss b/packages/demobank-ui/src/scss/_tiles.scss new file mode 100644 index 000000000..e69d995f0 --- /dev/null +++ b/packages/demobank-ui/src/scss/_tiles.scss @@ -0,0 +1,24 @@ +/* + 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) + */ + +.is-tiles-wrapper { + margin-bottom: $default-padding; +} diff --git a/packages/demobank-ui/src/scss/_title-bar.scss b/packages/demobank-ui/src/scss/_title-bar.scss new file mode 100644 index 000000000..932f8e65d --- /dev/null +++ b/packages/demobank-ui/src/scss/_title-bar.scss @@ -0,0 +1,50 @@ +/* + 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) + */ + +section.section.is-title-bar { + padding: $default-padding; + border-bottom: $light-border; + + ul { + li { + display: inline-block; + padding: 0 $default-padding * 0.5 0 0; + font-size: $default-padding; + color: $title-bar-color; + + &:after { + display: inline-block; + content: "/"; + padding-left: $default-padding * 0.5; + } + + &:last-child { + padding-right: 0; + font-weight: 900; + color: $title-bar-active-color; + + &:after { + display: none; + } + } + } + } +} diff --git a/packages/demobank-ui/src/scss/bank.scss b/packages/demobank-ui/src/scss/bank.scss new file mode 100644 index 000000000..b524cfe29 --- /dev/null +++ b/packages/demobank-ui/src/scss/bank.scss @@ -0,0 +1,264 @@ +.navcontainer:not(.default-navcontainer) { + margin-bottom: 0 !important; +} + +.abort-button { + margin-left: 2px; + border: 2px solid rgb(0, 120, 231); + color: rgb(0, 120, 231); + font-size: 87%; + margin-top: 1px; + background: white; +} + +div.pages-list { + margin-top: 15px; +} + +.login-div, +.register-div { + display: block; + text-align: center; +} + +a.page-number { + color: blue; +} + +a.current-page-number { + color: inherit; + background-color: inherit; +} + +.cancelled { + text-decoration: line-through; +} + +input[type="number"]::-webkit-outer-spin-button, +input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* This CSS code styles the tab */ +.tab { + overflow: hidden; +} + +.logout { + float: right; + border: 20px; + margin-right: 15px; + margin-top: 15px; +} + +.tab button { + background-color: lightgray; + color: black; + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 18px 19px; + border: 2px solid #c1c1c1; + transition: 0.5s; + font-weight: bold; +} + +.tab button:hover { + background-color: yellow; + border: 2px solid #c1c1c1; + color: black; +} + +.tab button.active { + background-color: orange; + border: 2px solid #c1c1c1; + color: black; + font-weight: bold; +} + +.tabcontent { + display: none; + padding: 8px 16px; + border: 2px solid #c1c1c1; + width: max-content; +} + +.tabcontent.active { + display: block; +} + +input[type="number"] { + -moz-appearance: textfield; +} + +#transfer-fields { + display: flex; + flex-wrap: wrap; +} + +#id_amount { + width: 6em; + display: inline-block; + border-radius: 4px 0px 0px 4px; +} + +/** + * Amount without the currency, + * placed left to a .currency-indicator. + */ +#main .amount { + width: 6em; + display: inline-block; + border-radius: 4px 0px 0px 4px; +} + +input { + background-color: inherit; +} + +.large-amount { + font-weight: bold; + font-size: x-large; +} + +.currency { + font-style: oblique; +} + +/* + * Currency indicator to the right of input fields, + * with non-rounded corners to the left. + */ +#main .currency-indicator { + color: black; + border-radius: 0px 4px 4px 0px; + position: relative; +} + +#main .fieldlabel { + display: block; + padding-bottom: 0.5em; +} + +#main .fieldbox { + margin-right: 1em; + margin-bottom: 0.5em; +} + +#logout-button { + display: block; + width: fit-content; +} + +.register-form > .pure-form, +.login-form > .pure-form { + background: #4a4a4a; + color: #ffffff; + display: inline-block; + text-align: left; + margin-left: auto; + margin-right: auto; + padding: 16px 16px; + border-radius: 8px; + width: max-content; + .formFieldLabel { + margin: 2px 2px; + } + input[type="text"], + input[type="password"] { + border: none; + border-radius: 4px; + background: #6a6a6a; + color: #fefefe; + box-shadow: none; + } + input[placeholder="Password"][type="password"] { + margin-bottom: 8px; + } + .btn-register, + .btn-login { + float: left; + } + .btn-cancel { + float: right; + } + h2 { + margin-top: 0; + margin-bottom: 10px; + } +} + + +.challenge-div { + display: block; + text-align: center; +} + +.challenge-form > .pure-form { + background: #4a4a4a; + color: #ffffff; + display: inline-block; + text-align: left; + margin-left: auto; + margin-right: auto; + padding: 16px 16px; + border-radius: 8px; + width: max-content; + .formFieldLabel { + margin: 2px 2px; + } + input[type="text"] { + border: none; + border-radius: 4px; + background: #6a6a6a; + color: #fefefe; + box-shadow: none; + } + .btn-confirm { + float: left; + } + .btn-cancel { + float: right; + } + h2 { + margin-top: 0; + margin-bottom: 10px; + } +} + + +.wire-transfer-form > .pure-form, +.payto-form > .pure-form, +.reserve-form > .pure-form { + background: #4a4a4a; + color: #ffffff; + display: inline-block; + text-align: left; + margin-left: auto; + margin-right: auto; + padding: 16px 16px; + border-radius: 8px; + width: max-content; + .formFieldLabel { + margin: 2px 2px; + } + input[type="text"] { + border: none; + border-radius: 4px; + background: #6a6a6a; + color: #fefefe; + box-shadow: none; + } +} + + +html { + background: #ffffff; + color: #2a2a2a; +} + +.hint { + scale: 0.7; +} diff --git a/packages/demobank-ui/src/scss/colors-bank.scss b/packages/demobank-ui/src/scss/colors-bank.scss new file mode 100644 index 000000000..c34610948 --- /dev/null +++ b/packages/demobank-ui/src/scss/colors-bank.scss @@ -0,0 +1,31 @@ +nav, +nav a, +nav span, +.navcontainer, +nav button, +.demobar, +.navbtn { + color: white; + background: #a00000; +} + +nav a.active, +nav button, +nav span.active, +.navbtn.active { + background-color: #7a0606; +} + +nav a.active:hover, +nav span.active:hover, +.navbtn.active:hover, +nav button:hover, +nav a:hover, +nav span:hover, +.navbtn:hover { + background: #df3d3d; +} + +nav a.navbtn.langbtn:focus { + background-color: #df3d3d; +}
\ No newline at end of file diff --git a/packages/demobank-ui/src/scss/demo.scss b/packages/demobank-ui/src/scss/demo.scss new file mode 100644 index 000000000..71db4b41e --- /dev/null +++ b/packages/demobank-ui/src/scss/demo.scss @@ -0,0 +1,157 @@ +@charset "UTF-8"; +/* +Style common to all demo pages. + +Colors: +- #1e2739 (dark blue) +- #0042b2 (default blue) +- #3daee9 (highlight blue) +*/ + +.demobar h1 { + text-align: center; +} + +.demobar > p { + padding: 0.5em; +} + +.demobar a, +.demobar a:visited { + color: inherit; + background-color: inherit; +} + +.tt { + font-family: "Lucida Console", Monaco, monospace; +} + +.informational-ok { + background: lightgreen; + border-radius: 1em; + padding: 0.5em; +} + +.informational-fail { + background: lightpink; + border-radius: 1em; + padding: 0.5em; +} + +.content { + margin-left: 2em; + overflow-x: auto; +} + +.demobar { + overflow-x: auto; + background-color: #0042b2; + color: white; +} + +body { + overflow-x: hidden; + overflow-y: auto; +} + +.navcontainer { + background: #0042b2; + margin-bottom: 50px; + width: 100%; + color: white; + position: -webkit-sticky; + position: sticky; + top: 0px; + width: 100vw; + backdrop-filter: blur(10px); + opacity: 1; + z-index: 10000; +} + +nav { + left: 1vw; + position: relative; + background: #0042b2; + z-index: 10000; +} + +nav a, +nav button, +nav span, +.navbtn { + border: none; + color: white; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + background: #0042b2; + height: inherit; +} + +nav a, +nav button, +nav span, +.navbtn { + padding: 15px 32px; +} + +nav a:hover, +nav span:hover, +.navbtn:hover { + background: #3daee9; +} + +nav a.active, +nav span.active, +.navbtn.active { + background-color: #1e2739; +} + +nav a.active:hover, +nav button.active:hover, +nav span.active:hover, +.navbtn.active:hover { + background: #3daee9; +} + +nav a, +nav span, +.navbtn { + cursor: pointer; +} + +nav .right { + float: right; + margin-right: 5vw; +} +nav .hide div.nav { + display: none; +} +// nav .right div.nav:hover { +// display: block; +// } + +// nav .right:hover div.nav { +// display: block; +// } + +.langbtn { + width: 100%; + text-align: left; +} + +.skip { + position: absolute; + left: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; +} + +.skip:focus { + position: static; + width: auto; + height: auto; +} diff --git a/packages/demobank-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf b/packages/demobank-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf Binary files differnew file mode 100644 index 000000000..7665ee336 --- /dev/null +++ b/packages/demobank-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf diff --git a/packages/demobank-ui/src/scss/fonts/nunito.css b/packages/demobank-ui/src/scss/fonts/nunito.css new file mode 100644 index 000000000..ab30db36b --- /dev/null +++ b/packages/demobank-ui/src/scss/fonts/nunito.css @@ -0,0 +1,22 @@ +/* + 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/> + */ + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + src: url(./XRXV3I6Li01BKofINeaE.ttf) format('truetype'); +} diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot Binary files differnew file mode 100644 index 000000000..ab6b25ded --- /dev/null +++ b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf Binary files differnew file mode 100644 index 000000000..824be10fa --- /dev/null +++ b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff Binary files differnew file mode 100644 index 000000000..7e087c1de --- /dev/null +++ b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 Binary files differnew file mode 100644 index 000000000..b5caa4ddc --- /dev/null +++ b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 diff --git a/packages/demobank-ui/src/scss/icons/materialdesignicons-4.9.95.min.css b/packages/demobank-ui/src/scss/icons/materialdesignicons-4.9.95.min.css new file mode 100644 index 000000000..24a89d639 --- /dev/null +++ b/packages/demobank-ui/src/scss/icons/materialdesignicons-4.9.95.min.css @@ -0,0 +1,3 @@ +@font-face{font-family:"Material Design Icons";src:url("./fonts/materialdesignicons-webfont-4.9.95.eot");src:url("./fonts/materialdesignicons-webfont-4.9.95.woff2") format("woff2"),url("./fonts/materialdesignicons-webfont-4.9.95.woff") format("woff"),url("./fonts/materialdesignicons-webfont-4.9.95.ttf") format("truetype");font-weight:normal;font-style:normal}.mdi:before,.mdi-set{display:inline-block;font:normal normal normal 24px/1 "Material Design Icons";font-size:inherit;text-rendering:auto;line-height:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.mdi-ab-testing::before{content:"\F001C"}.mdi-abjad-arabic::before{content:"\F0353"}.mdi-abjad-hebrew::before{content:"\F0354"}.mdi-abugida-devanagari::before{content:"\F0355"}.mdi-abugida-thai::before{content:"\F0356"}.mdi-access-point::before{content:"\F002"}.mdi-access-point-network::before{content:"\F003"}.mdi-access-point-network-off::before{content:"\FBBD"}.mdi-account::before{content:"\F004"}.mdi-account-alert::before{content:"\F005"}.mdi-account-alert-outline::before{content:"\FB2C"}.mdi-account-arrow-left::before{content:"\FB2D"}.mdi-account-arrow-left-outline::before{content:"\FB2E"}.mdi-account-arrow-right::before{content:"\FB2F"}.mdi-account-arrow-right-outline::before{content:"\FB30"}.mdi-account-badge::before{content:"\FD83"}.mdi-account-badge-alert::before{content:"\FD84"}.mdi-account-badge-alert-outline::before{content:"\FD85"}.mdi-account-badge-horizontal::before{content:"\FDF0"}.mdi-account-badge-horizontal-outline::before{content:"\FDF1"}.mdi-account-badge-outline::before{content:"\FD86"}.mdi-account-box::before{content:"\F006"}.mdi-account-box-multiple::before{content:"\F933"}.mdi-account-box-multiple-outline::before{content:"\F002C"}.mdi-account-box-outline::before{content:"\F007"}.mdi-account-cancel::before{content:"\F030A"}.mdi-account-cancel-outline::before{content:"\F030B"}.mdi-account-card-details::before{content:"\F5D2"}.mdi-account-card-details-outline::before{content:"\FD87"}.mdi-account-cash::before{content:"\F00C2"}.mdi-account-cash-outline::before{content:"\F00C3"}.mdi-account-check::before{content:"\F008"}.mdi-account-check-outline::before{content:"\FBBE"}.mdi-account-child::before{content:"\FA88"}.mdi-account-child-circle::before{content:"\FA89"}.mdi-account-child-outline::before{content:"\F00F3"}.mdi-account-circle::before{content:"\F009"}.mdi-account-circle-outline::before{content:"\FB31"}.mdi-account-clock::before{content:"\FB32"}.mdi-account-clock-outline::before{content:"\FB33"}.mdi-account-cog::before{content:"\F039B"}.mdi-account-cog-outline::before{content:"\F039C"}.mdi-account-convert::before{content:"\F00A"}.mdi-account-convert-outline::before{content:"\F032C"}.mdi-account-details::before{content:"\F631"}.mdi-account-details-outline::before{content:"\F039D"}.mdi-account-edit::before{content:"\F6BB"}.mdi-account-edit-outline::before{content:"\F001D"}.mdi-account-group::before{content:"\F848"}.mdi-account-group-outline::before{content:"\FB34"}.mdi-account-heart::before{content:"\F898"}.mdi-account-heart-outline::before{content:"\FBBF"}.mdi-account-key::before{content:"\F00B"}.mdi-account-key-outline::before{content:"\FBC0"}.mdi-account-lock::before{content:"\F0189"}.mdi-account-lock-outline::before{content:"\F018A"}.mdi-account-minus::before{content:"\F00D"}.mdi-account-minus-outline::before{content:"\FAEB"}.mdi-account-multiple::before{content:"\F00E"}.mdi-account-multiple-check::before{content:"\F8C4"}.mdi-account-multiple-check-outline::before{content:"\F0229"}.mdi-account-multiple-minus::before{content:"\F5D3"}.mdi-account-multiple-minus-outline::before{content:"\FBC1"}.mdi-account-multiple-outline::before{content:"\F00F"}.mdi-account-multiple-plus::before{content:"\F010"}.mdi-account-multiple-plus-outline::before{content:"\F7FF"}.mdi-account-multiple-remove::before{content:"\F0235"}.mdi-account-multiple-remove-outline::before{content:"\F0236"}.mdi-account-network::before{content:"\F011"}.mdi-account-network-outline::before{content:"\FBC2"}.mdi-account-off::before{content:"\F012"}.mdi-account-off-outline::before{content:"\FBC3"}.mdi-account-outline::before{content:"\F013"}.mdi-account-plus::before{content:"\F014"}.mdi-account-plus-outline::before{content:"\F800"}.mdi-account-question::before{content:"\FB35"}.mdi-account-question-outline::before{content:"\FB36"}.mdi-account-remove::before{content:"\F015"}.mdi-account-remove-outline::before{content:"\FAEC"}.mdi-account-search::before{content:"\F016"}.mdi-account-search-outline::before{content:"\F934"}.mdi-account-settings::before{content:"\F630"}.mdi-account-settings-outline::before{content:"\F00F4"}.mdi-account-star::before{content:"\F017"}.mdi-account-star-outline::before{content:"\FBC4"}.mdi-account-supervisor::before{content:"\FA8A"}.mdi-account-supervisor-circle::before{content:"\FA8B"}.mdi-account-supervisor-outline::before{content:"\F0158"}.mdi-account-switch::before{content:"\F019"}.mdi-account-tie::before{content:"\FCBF"}.mdi-account-tie-outline::before{content:"\F00F5"}.mdi-account-tie-voice::before{content:"\F0333"}.mdi-account-tie-voice-off::before{content:"\F0335"}.mdi-account-tie-voice-off-outline::before{content:"\F0336"}.mdi-account-tie-voice-outline::before{content:"\F0334"}.mdi-accusoft::before{content:"\F849"}.mdi-adjust::before{content:"\F01A"}.mdi-adobe::before{content:"\F935"}.mdi-adobe-acrobat::before{content:"\FFBD"}.mdi-air-conditioner::before{content:"\F01B"}.mdi-air-filter::before{content:"\FD1F"}.mdi-air-horn::before{content:"\FD88"}.mdi-air-humidifier::before{content:"\F00C4"}.mdi-air-purifier::before{content:"\FD20"}.mdi-airbag::before{content:"\FBC5"}.mdi-airballoon::before{content:"\F01C"}.mdi-airballoon-outline::before{content:"\F002D"}.mdi-airplane::before{content:"\F01D"}.mdi-airplane-landing::before{content:"\F5D4"}.mdi-airplane-off::before{content:"\F01E"}.mdi-airplane-takeoff::before{content:"\F5D5"}.mdi-airplay::before{content:"\F01F"}.mdi-airport::before{content:"\F84A"}.mdi-alarm::before{content:"\F020"}.mdi-alarm-bell::before{content:"\F78D"}.mdi-alarm-check::before{content:"\F021"}.mdi-alarm-light::before{content:"\F78E"}.mdi-alarm-light-outline::before{content:"\FBC6"}.mdi-alarm-multiple::before{content:"\F022"}.mdi-alarm-note::before{content:"\FE8E"}.mdi-alarm-note-off::before{content:"\FE8F"}.mdi-alarm-off::before{content:"\F023"}.mdi-alarm-plus::before{content:"\F024"}.mdi-alarm-snooze::before{content:"\F68D"}.mdi-album::before{content:"\F025"}.mdi-alert::before{content:"\F026"}.mdi-alert-box::before{content:"\F027"}.mdi-alert-box-outline::before{content:"\FCC0"}.mdi-alert-circle::before{content:"\F028"}.mdi-alert-circle-check::before{content:"\F0218"}.mdi-alert-circle-check-outline::before{content:"\F0219"}.mdi-alert-circle-outline::before{content:"\F5D6"}.mdi-alert-decagram::before{content:"\F6BC"}.mdi-alert-decagram-outline::before{content:"\FCC1"}.mdi-alert-octagon::before{content:"\F029"}.mdi-alert-octagon-outline::before{content:"\FCC2"}.mdi-alert-octagram::before{content:"\F766"}.mdi-alert-octagram-outline::before{content:"\FCC3"}.mdi-alert-outline::before{content:"\F02A"}.mdi-alert-rhombus::before{content:"\F01F9"}.mdi-alert-rhombus-outline::before{content:"\F01FA"}.mdi-alien::before{content:"\F899"}.mdi-alien-outline::before{content:"\F00F6"}.mdi-align-horizontal-center::before{content:"\F01EE"}.mdi-align-horizontal-left::before{content:"\F01ED"}.mdi-align-horizontal-right::before{content:"\F01EF"}.mdi-align-vertical-bottom::before{content:"\F01F0"}.mdi-align-vertical-center::before{content:"\F01F1"}.mdi-align-vertical-top::before{content:"\F01F2"}.mdi-all-inclusive::before{content:"\F6BD"}.mdi-allergy::before{content:"\F0283"}.mdi-alpha::before{content:"\F02B"}.mdi-alpha-a::before{content:"\41"}.mdi-alpha-a-box::before{content:"\FAED"}.mdi-alpha-a-box-outline::before{content:"\FBC7"}.mdi-alpha-a-circle::before{content:"\FBC8"}.mdi-alpha-a-circle-outline::before{content:"\FBC9"}.mdi-alpha-b::before{content:"\42"}.mdi-alpha-b-box::before{content:"\FAEE"}.mdi-alpha-b-box-outline::before{content:"\FBCA"}.mdi-alpha-b-circle::before{content:"\FBCB"}.mdi-alpha-b-circle-outline::before{content:"\FBCC"}.mdi-alpha-c::before{content:"\43"}.mdi-alpha-c-box::before{content:"\FAEF"}.mdi-alpha-c-box-outline::before{content:"\FBCD"}.mdi-alpha-c-circle::before{content:"\FBCE"}.mdi-alpha-c-circle-outline::before{content:"\FBCF"}.mdi-alpha-d::before{content:"\44"}.mdi-alpha-d-box::before{content:"\FAF0"}.mdi-alpha-d-box-outline::before{content:"\FBD0"}.mdi-alpha-d-circle::before{content:"\FBD1"}.mdi-alpha-d-circle-outline::before{content:"\FBD2"}.mdi-alpha-e::before{content:"\45"}.mdi-alpha-e-box::before{content:"\FAF1"}.mdi-alpha-e-box-outline::before{content:"\FBD3"}.mdi-alpha-e-circle::before{content:"\FBD4"}.mdi-alpha-e-circle-outline::before{content:"\FBD5"}.mdi-alpha-f::before{content:"\46"}.mdi-alpha-f-box::before{content:"\FAF2"}.mdi-alpha-f-box-outline::before{content:"\FBD6"}.mdi-alpha-f-circle::before{content:"\FBD7"}.mdi-alpha-f-circle-outline::before{content:"\FBD8"}.mdi-alpha-g::before{content:"\47"}.mdi-alpha-g-box::before{content:"\FAF3"}.mdi-alpha-g-box-outline::before{content:"\FBD9"}.mdi-alpha-g-circle::before{content:"\FBDA"}.mdi-alpha-g-circle-outline::before{content:"\FBDB"}.mdi-alpha-h::before{content:"\48"}.mdi-alpha-h-box::before{content:"\FAF4"}.mdi-alpha-h-box-outline::before{content:"\FBDC"}.mdi-alpha-h-circle::before{content:"\FBDD"}.mdi-alpha-h-circle-outline::before{content:"\FBDE"}.mdi-alpha-i::before{content:"\49"}.mdi-alpha-i-box::before{content:"\FAF5"}.mdi-alpha-i-box-outline::before{content:"\FBDF"}.mdi-alpha-i-circle::before{content:"\FBE0"}.mdi-alpha-i-circle-outline::before{content:"\FBE1"}.mdi-alpha-j::before{content:"\4A"}.mdi-alpha-j-box::before{content:"\FAF6"}.mdi-alpha-j-box-outline::before{content:"\FBE2"}.mdi-alpha-j-circle::before{content:"\FBE3"}.mdi-alpha-j-circle-outline::before{content:"\FBE4"}.mdi-alpha-k::before{content:"\4B"}.mdi-alpha-k-box::before{content:"\FAF7"}.mdi-alpha-k-box-outline::before{content:"\FBE5"}.mdi-alpha-k-circle::before{content:"\FBE6"}.mdi-alpha-k-circle-outline::before{content:"\FBE7"}.mdi-alpha-l::before{content:"\4C"}.mdi-alpha-l-box::before{content:"\FAF8"}.mdi-alpha-l-box-outline::before{content:"\FBE8"}.mdi-alpha-l-circle::before{content:"\FBE9"}.mdi-alpha-l-circle-outline::before{content:"\FBEA"}.mdi-alpha-m::before{content:"\4D"}.mdi-alpha-m-box::before{content:"\FAF9"}.mdi-alpha-m-box-outline::before{content:"\FBEB"}.mdi-alpha-m-circle::before{content:"\FBEC"}.mdi-alpha-m-circle-outline::before{content:"\FBED"}.mdi-alpha-n::before{content:"\4E"}.mdi-alpha-n-box::before{content:"\FAFA"}.mdi-alpha-n-box-outline::before{content:"\FBEE"}.mdi-alpha-n-circle::before{content:"\FBEF"}.mdi-alpha-n-circle-outline::before{content:"\FBF0"}.mdi-alpha-o::before{content:"\4F"}.mdi-alpha-o-box::before{content:"\FAFB"}.mdi-alpha-o-box-outline::before{content:"\FBF1"}.mdi-alpha-o-circle::before{content:"\FBF2"}.mdi-alpha-o-circle-outline::before{content:"\FBF3"}.mdi-alpha-p::before{content:"\50"}.mdi-alpha-p-box::before{content:"\FAFC"}.mdi-alpha-p-box-outline::before{content:"\FBF4"}.mdi-alpha-p-circle::before{content:"\FBF5"}.mdi-alpha-p-circle-outline::before{content:"\FBF6"}.mdi-alpha-q::before{content:"\51"}.mdi-alpha-q-box::before{content:"\FAFD"}.mdi-alpha-q-box-outline::before{content:"\FBF7"}.mdi-alpha-q-circle::before{content:"\FBF8"}.mdi-alpha-q-circle-outline::before{content:"\FBF9"}.mdi-alpha-r::before{content:"\52"}.mdi-alpha-r-box::before{content:"\FAFE"}.mdi-alpha-r-box-outline::before{content:"\FBFA"}.mdi-alpha-r-circle::before{content:"\FBFB"}.mdi-alpha-r-circle-outline::before{content:"\FBFC"}.mdi-alpha-s::before{content:"\53"}.mdi-alpha-s-box::before{content:"\FAFF"}.mdi-alpha-s-box-outline::before{content:"\FBFD"}.mdi-alpha-s-circle::before{content:"\FBFE"}.mdi-alpha-s-circle-outline::before{content:"\FBFF"}.mdi-alpha-t::before{content:"\54"}.mdi-alpha-t-box::before{content:"\FB00"}.mdi-alpha-t-box-outline::before{content:"\FC00"}.mdi-alpha-t-circle::before{content:"\FC01"}.mdi-alpha-t-circle-outline::before{content:"\FC02"}.mdi-alpha-u::before{content:"\55"}.mdi-alpha-u-box::before{content:"\FB01"}.mdi-alpha-u-box-outline::before{content:"\FC03"}.mdi-alpha-u-circle::before{content:"\FC04"}.mdi-alpha-u-circle-outline::before{content:"\FC05"}.mdi-alpha-v::before{content:"\56"}.mdi-alpha-v-box::before{content:"\FB02"}.mdi-alpha-v-box-outline::before{content:"\FC06"}.mdi-alpha-v-circle::before{content:"\FC07"}.mdi-alpha-v-circle-outline::before{content:"\FC08"}.mdi-alpha-w::before{content:"\57"}.mdi-alpha-w-box::before{content:"\FB03"}.mdi-alpha-w-box-outline::before{content:"\FC09"}.mdi-alpha-w-circle::before{content:"\FC0A"}.mdi-alpha-w-circle-outline::before{content:"\FC0B"}.mdi-alpha-x::before{content:"\58"}.mdi-alpha-x-box::before{content:"\FB04"}.mdi-alpha-x-box-outline::before{content:"\FC0C"}.mdi-alpha-x-circle::before{content:"\FC0D"}.mdi-alpha-x-circle-outline::before{content:"\FC0E"}.mdi-alpha-y::before{content:"\59"}.mdi-alpha-y-box::before{content:"\FB05"}.mdi-alpha-y-box-outline::before{content:"\FC0F"}.mdi-alpha-y-circle::before{content:"\FC10"}.mdi-alpha-y-circle-outline::before{content:"\FC11"}.mdi-alpha-z::before{content:"\5A"}.mdi-alpha-z-box::before{content:"\FB06"}.mdi-alpha-z-box-outline::before{content:"\FC12"}.mdi-alpha-z-circle::before{content:"\FC13"}.mdi-alpha-z-circle-outline::before{content:"\FC14"}.mdi-alphabet-aurebesh::before{content:"\F0357"}.mdi-alphabet-cyrillic::before{content:"\F0358"}.mdi-alphabet-greek::before{content:"\F0359"}.mdi-alphabet-latin::before{content:"\F035A"}.mdi-alphabet-piqad::before{content:"\F035B"}.mdi-alphabet-tengwar::before{content:"\F0362"}.mdi-alphabetical::before{content:"\F02C"}.mdi-alphabetical-off::before{content:"\F002E"}.mdi-alphabetical-variant::before{content:"\F002F"}.mdi-alphabetical-variant-off::before{content:"\F0030"}.mdi-altimeter::before{content:"\F5D7"}.mdi-amazon::before{content:"\F02D"}.mdi-amazon-alexa::before{content:"\F8C5"}.mdi-amazon-drive::before{content:"\F02E"}.mdi-ambulance::before{content:"\F02F"}.mdi-ammunition::before{content:"\FCC4"}.mdi-ampersand::before{content:"\FA8C"}.mdi-amplifier::before{content:"\F030"}.mdi-amplifier-off::before{content:"\F01E0"}.mdi-anchor::before{content:"\F031"}.mdi-android::before{content:"\F032"}.mdi-android-auto::before{content:"\FA8D"}.mdi-android-debug-bridge::before{content:"\F033"}.mdi-android-head::before{content:"\F78F"}.mdi-android-messages::before{content:"\FD21"}.mdi-android-studio::before{content:"\F034"}.mdi-angle-acute::before{content:"\F936"}.mdi-angle-obtuse::before{content:"\F937"}.mdi-angle-right::before{content:"\F938"}.mdi-angular::before{content:"\F6B1"}.mdi-angularjs::before{content:"\F6BE"}.mdi-animation::before{content:"\F5D8"}.mdi-animation-outline::before{content:"\FA8E"}.mdi-animation-play::before{content:"\F939"}.mdi-animation-play-outline::before{content:"\FA8F"}.mdi-ansible::before{content:"\F00C5"}.mdi-antenna::before{content:"\F0144"}.mdi-anvil::before{content:"\F89A"}.mdi-apache-kafka::before{content:"\F0031"}.mdi-api::before{content:"\F00C6"}.mdi-api-off::before{content:"\F0282"}.mdi-apple::before{content:"\F035"}.mdi-apple-finder::before{content:"\F036"}.mdi-apple-icloud::before{content:"\F038"}.mdi-apple-ios::before{content:"\F037"}.mdi-apple-keyboard-caps::before{content:"\F632"}.mdi-apple-keyboard-command::before{content:"\F633"}.mdi-apple-keyboard-control::before{content:"\F634"}.mdi-apple-keyboard-option::before{content:"\F635"}.mdi-apple-keyboard-shift::before{content:"\F636"}.mdi-apple-safari::before{content:"\F039"}.mdi-application::before{content:"\F614"}.mdi-application-export::before{content:"\FD89"}.mdi-application-import::before{content:"\FD8A"}.mdi-approximately-equal::before{content:"\FFBE"}.mdi-approximately-equal-box::before{content:"\FFBF"}.mdi-apps::before{content:"\F03B"}.mdi-apps-box::before{content:"\FD22"}.mdi-arch::before{content:"\F8C6"}.mdi-archive::before{content:"\F03C"}.mdi-archive-arrow-down::before{content:"\F0284"}.mdi-archive-arrow-down-outline::before{content:"\F0285"}.mdi-archive-arrow-up::before{content:"\F0286"}.mdi-archive-arrow-up-outline::before{content:"\F0287"}.mdi-archive-outline::before{content:"\F0239"}.mdi-arm-flex::before{content:"\F008F"}.mdi-arm-flex-outline::before{content:"\F0090"}.mdi-arrange-bring-forward::before{content:"\F03D"}.mdi-arrange-bring-to-front::before{content:"\F03E"}.mdi-arrange-send-backward::before{content:"\F03F"}.mdi-arrange-send-to-back::before{content:"\F040"}.mdi-arrow-all::before{content:"\F041"}.mdi-arrow-bottom-left::before{content:"\F042"}.mdi-arrow-bottom-left-bold-outline::before{content:"\F9B6"}.mdi-arrow-bottom-left-thick::before{content:"\F9B7"}.mdi-arrow-bottom-right::before{content:"\F043"}.mdi-arrow-bottom-right-bold-outline::before{content:"\F9B8"}.mdi-arrow-bottom-right-thick::before{content:"\F9B9"}.mdi-arrow-collapse::before{content:"\F615"}.mdi-arrow-collapse-all::before{content:"\F044"}.mdi-arrow-collapse-down::before{content:"\F791"}.mdi-arrow-collapse-horizontal::before{content:"\F84B"}.mdi-arrow-collapse-left::before{content:"\F792"}.mdi-arrow-collapse-right::before{content:"\F793"}.mdi-arrow-collapse-up::before{content:"\F794"}.mdi-arrow-collapse-vertical::before{content:"\F84C"}.mdi-arrow-decision::before{content:"\F9BA"}.mdi-arrow-decision-auto::before{content:"\F9BB"}.mdi-arrow-decision-auto-outline::before{content:"\F9BC"}.mdi-arrow-decision-outline::before{content:"\F9BD"}.mdi-arrow-down::before{content:"\F045"}.mdi-arrow-down-bold::before{content:"\F72D"}.mdi-arrow-down-bold-box::before{content:"\F72E"}.mdi-arrow-down-bold-box-outline::before{content:"\F72F"}.mdi-arrow-down-bold-circle::before{content:"\F047"}.mdi-arrow-down-bold-circle-outline::before{content:"\F048"}.mdi-arrow-down-bold-hexagon-outline::before{content:"\F049"}.mdi-arrow-down-bold-outline::before{content:"\F9BE"}.mdi-arrow-down-box::before{content:"\F6BF"}.mdi-arrow-down-circle::before{content:"\FCB7"}.mdi-arrow-down-circle-outline::before{content:"\FCB8"}.mdi-arrow-down-drop-circle::before{content:"\F04A"}.mdi-arrow-down-drop-circle-outline::before{content:"\F04B"}.mdi-arrow-down-thick::before{content:"\F046"}.mdi-arrow-expand::before{content:"\F616"}.mdi-arrow-expand-all::before{content:"\F04C"}.mdi-arrow-expand-down::before{content:"\F795"}.mdi-arrow-expand-horizontal::before{content:"\F84D"}.mdi-arrow-expand-left::before{content:"\F796"}.mdi-arrow-expand-right::before{content:"\F797"}.mdi-arrow-expand-up::before{content:"\F798"}.mdi-arrow-expand-vertical::before{content:"\F84E"}.mdi-arrow-horizontal-lock::before{content:"\F0186"}.mdi-arrow-left::before{content:"\F04D"}.mdi-arrow-left-bold::before{content:"\F730"}.mdi-arrow-left-bold-box::before{content:"\F731"}.mdi-arrow-left-bold-box-outline::before{content:"\F732"}.mdi-arrow-left-bold-circle::before{content:"\F04F"}.mdi-arrow-left-bold-circle-outline::before{content:"\F050"}.mdi-arrow-left-bold-hexagon-outline::before{content:"\F051"}.mdi-arrow-left-bold-outline::before{content:"\F9BF"}.mdi-arrow-left-box::before{content:"\F6C0"}.mdi-arrow-left-circle::before{content:"\FCB9"}.mdi-arrow-left-circle-outline::before{content:"\FCBA"}.mdi-arrow-left-drop-circle::before{content:"\F052"}.mdi-arrow-left-drop-circle-outline::before{content:"\F053"}.mdi-arrow-left-right::before{content:"\FE90"}.mdi-arrow-left-right-bold::before{content:"\FE91"}.mdi-arrow-left-right-bold-outline::before{content:"\F9C0"}.mdi-arrow-left-thick::before{content:"\F04E"}.mdi-arrow-right::before{content:"\F054"}.mdi-arrow-right-bold::before{content:"\F733"}.mdi-arrow-right-bold-box::before{content:"\F734"}.mdi-arrow-right-bold-box-outline::before{content:"\F735"}.mdi-arrow-right-bold-circle::before{content:"\F056"}.mdi-arrow-right-bold-circle-outline::before{content:"\F057"}.mdi-arrow-right-bold-hexagon-outline::before{content:"\F058"}.mdi-arrow-right-bold-outline::before{content:"\F9C1"}.mdi-arrow-right-box::before{content:"\F6C1"}.mdi-arrow-right-circle::before{content:"\FCBB"}.mdi-arrow-right-circle-outline::before{content:"\FCBC"}.mdi-arrow-right-drop-circle::before{content:"\F059"}.mdi-arrow-right-drop-circle-outline::before{content:"\F05A"}.mdi-arrow-right-thick::before{content:"\F055"}.mdi-arrow-split-horizontal::before{content:"\F93A"}.mdi-arrow-split-vertical::before{content:"\F93B"}.mdi-arrow-top-left::before{content:"\F05B"}.mdi-arrow-top-left-bold-outline::before{content:"\F9C2"}.mdi-arrow-top-left-bottom-right::before{content:"\FE92"}.mdi-arrow-top-left-bottom-right-bold::before{content:"\FE93"}.mdi-arrow-top-left-thick::before{content:"\F9C3"}.mdi-arrow-top-right::before{content:"\F05C"}.mdi-arrow-top-right-bold-outline::before{content:"\F9C4"}.mdi-arrow-top-right-bottom-left::before{content:"\FE94"}.mdi-arrow-top-right-bottom-left-bold::before{content:"\FE95"}.mdi-arrow-top-right-thick::before{content:"\F9C5"}.mdi-arrow-up::before{content:"\F05D"}.mdi-arrow-up-bold::before{content:"\F736"}.mdi-arrow-up-bold-box::before{content:"\F737"}.mdi-arrow-up-bold-box-outline::before{content:"\F738"}.mdi-arrow-up-bold-circle::before{content:"\F05F"}.mdi-arrow-up-bold-circle-outline::before{content:"\F060"}.mdi-arrow-up-bold-hexagon-outline::before{content:"\F061"}.mdi-arrow-up-bold-outline::before{content:"\F9C6"}.mdi-arrow-up-box::before{content:"\F6C2"}.mdi-arrow-up-circle::before{content:"\FCBD"}.mdi-arrow-up-circle-outline::before{content:"\FCBE"}.mdi-arrow-up-down::before{content:"\FE96"}.mdi-arrow-up-down-bold::before{content:"\FE97"}.mdi-arrow-up-down-bold-outline::before{content:"\F9C7"}.mdi-arrow-up-drop-circle::before{content:"\F062"}.mdi-arrow-up-drop-circle-outline::before{content:"\F063"}.mdi-arrow-up-thick::before{content:"\F05E"}.mdi-arrow-vertical-lock::before{content:"\F0187"}.mdi-artist::before{content:"\F802"}.mdi-artist-outline::before{content:"\FCC5"}.mdi-artstation::before{content:"\FB37"}.mdi-aspect-ratio::before{content:"\FA23"}.mdi-assistant::before{content:"\F064"}.mdi-asterisk::before{content:"\F6C3"}.mdi-at::before{content:"\F065"}.mdi-atlassian::before{content:"\F803"}.mdi-atm::before{content:"\FD23"}.mdi-atom::before{content:"\F767"}.mdi-atom-variant::before{content:"\FE98"}.mdi-attachment::before{content:"\F066"}.mdi-audio-video::before{content:"\F93C"}.mdi-audio-video-off::before{content:"\F01E1"}.mdi-audiobook::before{content:"\F067"}.mdi-augmented-reality::before{content:"\F84F"}.mdi-auto-download::before{content:"\F03A9"}.mdi-auto-fix::before{content:"\F068"}.mdi-auto-upload::before{content:"\F069"}.mdi-autorenew::before{content:"\F06A"}.mdi-av-timer::before{content:"\F06B"}.mdi-aws::before{content:"\FDF2"}.mdi-axe::before{content:"\F8C7"}.mdi-axis::before{content:"\FD24"}.mdi-axis-arrow::before{content:"\FD25"}.mdi-axis-arrow-lock::before{content:"\FD26"}.mdi-axis-lock::before{content:"\FD27"}.mdi-axis-x-arrow::before{content:"\FD28"}.mdi-axis-x-arrow-lock::before{content:"\FD29"}.mdi-axis-x-rotate-clockwise::before{content:"\FD2A"}.mdi-axis-x-rotate-counterclockwise::before{content:"\FD2B"}.mdi-axis-x-y-arrow-lock::before{content:"\FD2C"}.mdi-axis-y-arrow::before{content:"\FD2D"}.mdi-axis-y-arrow-lock::before{content:"\FD2E"}.mdi-axis-y-rotate-clockwise::before{content:"\FD2F"}.mdi-axis-y-rotate-counterclockwise::before{content:"\FD30"}.mdi-axis-z-arrow::before{content:"\FD31"}.mdi-axis-z-arrow-lock::before{content:"\FD32"}.mdi-axis-z-rotate-clockwise::before{content:"\FD33"}.mdi-axis-z-rotate-counterclockwise::before{content:"\FD34"}.mdi-azure::before{content:"\F804"}.mdi-azure-devops::before{content:"\F0091"}.mdi-babel::before{content:"\FA24"}.mdi-baby::before{content:"\F06C"}.mdi-baby-bottle::before{content:"\FF56"}.mdi-baby-bottle-outline::before{content:"\FF57"}.mdi-baby-carriage::before{content:"\F68E"}.mdi-baby-carriage-off::before{content:"\FFC0"}.mdi-baby-face::before{content:"\FE99"}.mdi-baby-face-outline::before{content:"\FE9A"}.mdi-backburger::before{content:"\F06D"}.mdi-backspace::before{content:"\F06E"}.mdi-backspace-outline::before{content:"\FB38"}.mdi-backspace-reverse::before{content:"\FE9B"}.mdi-backspace-reverse-outline::before{content:"\FE9C"}.mdi-backup-restore::before{content:"\F06F"}.mdi-bacteria::before{content:"\FEF2"}.mdi-bacteria-outline::before{content:"\FEF3"}.mdi-badminton::before{content:"\F850"}.mdi-bag-carry-on::before{content:"\FF58"}.mdi-bag-carry-on-check::before{content:"\FD41"}.mdi-bag-carry-on-off::before{content:"\FF59"}.mdi-bag-checked::before{content:"\FF5A"}.mdi-bag-personal::before{content:"\FDF3"}.mdi-bag-personal-off::before{content:"\FDF4"}.mdi-bag-personal-off-outline::before{content:"\FDF5"}.mdi-bag-personal-outline::before{content:"\FDF6"}.mdi-baguette::before{content:"\FF5B"}.mdi-balloon::before{content:"\FA25"}.mdi-ballot::before{content:"\F9C8"}.mdi-ballot-outline::before{content:"\F9C9"}.mdi-ballot-recount::before{content:"\FC15"}.mdi-ballot-recount-outline::before{content:"\FC16"}.mdi-bandage::before{content:"\FD8B"}.mdi-bandcamp::before{content:"\F674"}.mdi-bank::before{content:"\F070"}.mdi-bank-minus::before{content:"\FD8C"}.mdi-bank-outline::before{content:"\FE9D"}.mdi-bank-plus::before{content:"\FD8D"}.mdi-bank-remove::before{content:"\FD8E"}.mdi-bank-transfer::before{content:"\FA26"}.mdi-bank-transfer-in::before{content:"\FA27"}.mdi-bank-transfer-out::before{content:"\FA28"}.mdi-barcode::before{content:"\F071"}.mdi-barcode-off::before{content:"\F0261"}.mdi-barcode-scan::before{content:"\F072"}.mdi-barley::before{content:"\F073"}.mdi-barley-off::before{content:"\FB39"}.mdi-barn::before{content:"\FB3A"}.mdi-barrel::before{content:"\F074"}.mdi-baseball::before{content:"\F851"}.mdi-baseball-bat::before{content:"\F852"}.mdi-basecamp::before{content:"\F075"}.mdi-bash::before{content:"\F01AE"}.mdi-basket::before{content:"\F076"}.mdi-basket-fill::before{content:"\F077"}.mdi-basket-outline::before{content:"\F01AC"}.mdi-basket-unfill::before{content:"\F078"}.mdi-basketball::before{content:"\F805"}.mdi-basketball-hoop::before{content:"\FC17"}.mdi-basketball-hoop-outline::before{content:"\FC18"}.mdi-bat::before{content:"\FB3B"}.mdi-battery::before{content:"\F079"}.mdi-battery-10::before{content:"\F07A"}.mdi-battery-10-bluetooth::before{content:"\F93D"}.mdi-battery-20::before{content:"\F07B"}.mdi-battery-20-bluetooth::before{content:"\F93E"}.mdi-battery-30::before{content:"\F07C"}.mdi-battery-30-bluetooth::before{content:"\F93F"}.mdi-battery-40::before{content:"\F07D"}.mdi-battery-40-bluetooth::before{content:"\F940"}.mdi-battery-50::before{content:"\F07E"}.mdi-battery-50-bluetooth::before{content:"\F941"}.mdi-battery-60::before{content:"\F07F"}.mdi-battery-60-bluetooth::before{content:"\F942"}.mdi-battery-70::before{content:"\F080"}.mdi-battery-70-bluetooth::before{content:"\F943"}.mdi-battery-80::before{content:"\F081"}.mdi-battery-80-bluetooth::before{content:"\F944"}.mdi-battery-90::before{content:"\F082"}.mdi-battery-90-bluetooth::before{content:"\F945"}.mdi-battery-alert::before{content:"\F083"}.mdi-battery-alert-bluetooth::before{content:"\F946"}.mdi-battery-alert-variant::before{content:"\F00F7"}.mdi-battery-alert-variant-outline::before{content:"\F00F8"}.mdi-battery-bluetooth::before{content:"\F947"}.mdi-battery-bluetooth-variant::before{content:"\F948"}.mdi-battery-charging::before{content:"\F084"}.mdi-battery-charging-10::before{content:"\F89B"}.mdi-battery-charging-100::before{content:"\F085"}.mdi-battery-charging-20::before{content:"\F086"}.mdi-battery-charging-30::before{content:"\F087"}.mdi-battery-charging-40::before{content:"\F088"}.mdi-battery-charging-50::before{content:"\F89C"}.mdi-battery-charging-60::before{content:"\F089"}.mdi-battery-charging-70::before{content:"\F89D"}.mdi-battery-charging-80::before{content:"\F08A"}.mdi-battery-charging-90::before{content:"\F08B"}.mdi-battery-charging-high::before{content:"\F02D1"}.mdi-battery-charging-low::before{content:"\F02CF"}.mdi-battery-charging-medium::before{content:"\F02D0"}.mdi-battery-charging-outline::before{content:"\F89E"}.mdi-battery-charging-wireless::before{content:"\F806"}.mdi-battery-charging-wireless-10::before{content:"\F807"}.mdi-battery-charging-wireless-20::before{content:"\F808"}.mdi-battery-charging-wireless-30::before{content:"\F809"}.mdi-battery-charging-wireless-40::before{content:"\F80A"}.mdi-battery-charging-wireless-50::before{content:"\F80B"}.mdi-battery-charging-wireless-60::before{content:"\F80C"}.mdi-battery-charging-wireless-70::before{content:"\F80D"}.mdi-battery-charging-wireless-80::before{content:"\F80E"}.mdi-battery-charging-wireless-90::before{content:"\F80F"}.mdi-battery-charging-wireless-alert::before{content:"\F810"}.mdi-battery-charging-wireless-outline::before{content:"\F811"}.mdi-battery-heart::before{content:"\F023A"}.mdi-battery-heart-outline::before{content:"\F023B"}.mdi-battery-heart-variant::before{content:"\F023C"}.mdi-battery-high::before{content:"\F02CE"}.mdi-battery-low::before{content:"\F02CC"}.mdi-battery-medium::before{content:"\F02CD"}.mdi-battery-minus::before{content:"\F08C"}.mdi-battery-negative::before{content:"\F08D"}.mdi-battery-off::before{content:"\F0288"}.mdi-battery-off-outline::before{content:"\F0289"}.mdi-battery-outline::before{content:"\F08E"}.mdi-battery-plus::before{content:"\F08F"}.mdi-battery-positive::before{content:"\F090"}.mdi-battery-unknown::before{content:"\F091"}.mdi-battery-unknown-bluetooth::before{content:"\F949"}.mdi-battlenet::before{content:"\FB3C"}.mdi-beach::before{content:"\F092"}.mdi-beaker::before{content:"\FCC6"}.mdi-beaker-alert::before{content:"\F0254"}.mdi-beaker-alert-outline::before{content:"\F0255"}.mdi-beaker-check::before{content:"\F0256"}.mdi-beaker-check-outline::before{content:"\F0257"}.mdi-beaker-minus::before{content:"\F0258"}.mdi-beaker-minus-outline::before{content:"\F0259"}.mdi-beaker-outline::before{content:"\F68F"}.mdi-beaker-plus::before{content:"\F025A"}.mdi-beaker-plus-outline::before{content:"\F025B"}.mdi-beaker-question::before{content:"\F025C"}.mdi-beaker-question-outline::before{content:"\F025D"}.mdi-beaker-remove::before{content:"\F025E"}.mdi-beaker-remove-outline::before{content:"\F025F"}.mdi-beats::before{content:"\F097"}.mdi-bed-double::before{content:"\F0092"}.mdi-bed-double-outline::before{content:"\F0093"}.mdi-bed-empty::before{content:"\F89F"}.mdi-bed-king::before{content:"\F0094"}.mdi-bed-king-outline::before{content:"\F0095"}.mdi-bed-queen::before{content:"\F0096"}.mdi-bed-queen-outline::before{content:"\F0097"}.mdi-bed-single::before{content:"\F0098"}.mdi-bed-single-outline::before{content:"\F0099"}.mdi-bee::before{content:"\FFC1"}.mdi-bee-flower::before{content:"\FFC2"}.mdi-beehive-outline::before{content:"\F00F9"}.mdi-beer::before{content:"\F098"}.mdi-beer-outline::before{content:"\F0337"}.mdi-behance::before{content:"\F099"}.mdi-bell::before{content:"\F09A"}.mdi-bell-alert::before{content:"\FD35"}.mdi-bell-alert-outline::before{content:"\FE9E"}.mdi-bell-check::before{content:"\F0210"}.mdi-bell-check-outline::before{content:"\F0211"}.mdi-bell-circle::before{content:"\FD36"}.mdi-bell-circle-outline::before{content:"\FD37"}.mdi-bell-off::before{content:"\F09B"}.mdi-bell-off-outline::before{content:"\FA90"}.mdi-bell-outline::before{content:"\F09C"}.mdi-bell-plus::before{content:"\F09D"}.mdi-bell-plus-outline::before{content:"\FA91"}.mdi-bell-ring::before{content:"\F09E"}.mdi-bell-ring-outline::before{content:"\F09F"}.mdi-bell-sleep::before{content:"\F0A0"}.mdi-bell-sleep-outline::before{content:"\FA92"}.mdi-beta::before{content:"\F0A1"}.mdi-betamax::before{content:"\F9CA"}.mdi-biathlon::before{content:"\FDF7"}.mdi-bible::before{content:"\F0A2"}.mdi-bicycle::before{content:"\F00C7"}.mdi-bicycle-basket::before{content:"\F0260"}.mdi-bike::before{content:"\F0A3"}.mdi-bike-fast::before{content:"\F014A"}.mdi-billboard::before{content:"\F0032"}.mdi-billiards::before{content:"\FB3D"}.mdi-billiards-rack::before{content:"\FB3E"}.mdi-bing::before{content:"\F0A4"}.mdi-binoculars::before{content:"\F0A5"}.mdi-bio::before{content:"\F0A6"}.mdi-biohazard::before{content:"\F0A7"}.mdi-bitbucket::before{content:"\F0A8"}.mdi-bitcoin::before{content:"\F812"}.mdi-black-mesa::before{content:"\F0A9"}.mdi-blackberry::before{content:"\F0AA"}.mdi-blender::before{content:"\FCC7"}.mdi-blender-software::before{content:"\F0AB"}.mdi-blinds::before{content:"\F0AC"}.mdi-blinds-open::before{content:"\F0033"}.mdi-block-helper::before{content:"\F0AD"}.mdi-blogger::before{content:"\F0AE"}.mdi-blood-bag::before{content:"\FCC8"}.mdi-bluetooth::before{content:"\F0AF"}.mdi-bluetooth-audio::before{content:"\F0B0"}.mdi-bluetooth-connect::before{content:"\F0B1"}.mdi-bluetooth-off::before{content:"\F0B2"}.mdi-bluetooth-settings::before{content:"\F0B3"}.mdi-bluetooth-transfer::before{content:"\F0B4"}.mdi-blur::before{content:"\F0B5"}.mdi-blur-linear::before{content:"\F0B6"}.mdi-blur-off::before{content:"\F0B7"}.mdi-blur-radial::before{content:"\F0B8"}.mdi-bolnisi-cross::before{content:"\FCC9"}.mdi-bolt::before{content:"\FD8F"}.mdi-bomb::before{content:"\F690"}.mdi-bomb-off::before{content:"\F6C4"}.mdi-bone::before{content:"\F0B9"}.mdi-book::before{content:"\F0BA"}.mdi-book-information-variant::before{content:"\F009A"}.mdi-book-lock::before{content:"\F799"}.mdi-book-lock-open::before{content:"\F79A"}.mdi-book-minus::before{content:"\F5D9"}.mdi-book-minus-multiple::before{content:"\FA93"}.mdi-book-multiple::before{content:"\F0BB"}.mdi-book-open::before{content:"\F0BD"}.mdi-book-open-outline::before{content:"\FB3F"}.mdi-book-open-page-variant::before{content:"\F5DA"}.mdi-book-open-variant::before{content:"\F0BE"}.mdi-book-outline::before{content:"\FB40"}.mdi-book-play::before{content:"\FE9F"}.mdi-book-play-outline::before{content:"\FEA0"}.mdi-book-plus::before{content:"\F5DB"}.mdi-book-plus-multiple::before{content:"\FA94"}.mdi-book-remove::before{content:"\FA96"}.mdi-book-remove-multiple::before{content:"\FA95"}.mdi-book-search::before{content:"\FEA1"}.mdi-book-search-outline::before{content:"\FEA2"}.mdi-book-variant::before{content:"\F0BF"}.mdi-book-variant-multiple::before{content:"\F0BC"}.mdi-bookmark::before{content:"\F0C0"}.mdi-bookmark-check::before{content:"\F0C1"}.mdi-bookmark-check-outline::before{content:"\F03A6"}.mdi-bookmark-minus::before{content:"\F9CB"}.mdi-bookmark-minus-outline::before{content:"\F9CC"}.mdi-bookmark-multiple::before{content:"\FDF8"}.mdi-bookmark-multiple-outline::before{content:"\FDF9"}.mdi-bookmark-music::before{content:"\F0C2"}.mdi-bookmark-music-outline::before{content:"\F03A4"}.mdi-bookmark-off::before{content:"\F9CD"}.mdi-bookmark-off-outline::before{content:"\F9CE"}.mdi-bookmark-outline::before{content:"\F0C3"}.mdi-bookmark-plus::before{content:"\F0C5"}.mdi-bookmark-plus-outline::before{content:"\F0C4"}.mdi-bookmark-remove::before{content:"\F0C6"}.mdi-bookmark-remove-outline::before{content:"\F03A5"}.mdi-bookshelf::before{content:"\F028A"}.mdi-boom-gate::before{content:"\FEA3"}.mdi-boom-gate-alert::before{content:"\FEA4"}.mdi-boom-gate-alert-outline::before{content:"\FEA5"}.mdi-boom-gate-down::before{content:"\FEA6"}.mdi-boom-gate-down-outline::before{content:"\FEA7"}.mdi-boom-gate-outline::before{content:"\FEA8"}.mdi-boom-gate-up::before{content:"\FEA9"}.mdi-boom-gate-up-outline::before{content:"\FEAA"}.mdi-boombox::before{content:"\F5DC"}.mdi-boomerang::before{content:"\F00FA"}.mdi-bootstrap::before{content:"\F6C5"}.mdi-border-all::before{content:"\F0C7"}.mdi-border-all-variant::before{content:"\F8A0"}.mdi-border-bottom::before{content:"\F0C8"}.mdi-border-bottom-variant::before{content:"\F8A1"}.mdi-border-color::before{content:"\F0C9"}.mdi-border-horizontal::before{content:"\F0CA"}.mdi-border-inside::before{content:"\F0CB"}.mdi-border-left::before{content:"\F0CC"}.mdi-border-left-variant::before{content:"\F8A2"}.mdi-border-none::before{content:"\F0CD"}.mdi-border-none-variant::before{content:"\F8A3"}.mdi-border-outside::before{content:"\F0CE"}.mdi-border-right::before{content:"\F0CF"}.mdi-border-right-variant::before{content:"\F8A4"}.mdi-border-style::before{content:"\F0D0"}.mdi-border-top::before{content:"\F0D1"}.mdi-border-top-variant::before{content:"\F8A5"}.mdi-border-vertical::before{content:"\F0D2"}.mdi-bottle-soda::before{content:"\F009B"}.mdi-bottle-soda-classic::before{content:"\F009C"}.mdi-bottle-soda-classic-outline::before{content:"\F038E"}.mdi-bottle-soda-outline::before{content:"\F009D"}.mdi-bottle-tonic::before{content:"\F0159"}.mdi-bottle-tonic-outline::before{content:"\F015A"}.mdi-bottle-tonic-plus::before{content:"\F015B"}.mdi-bottle-tonic-plus-outline::before{content:"\F015C"}.mdi-bottle-tonic-skull::before{content:"\F015D"}.mdi-bottle-tonic-skull-outline::before{content:"\F015E"}.mdi-bottle-wine::before{content:"\F853"}.mdi-bottle-wine-outline::before{content:"\F033B"}.mdi-bow-tie::before{content:"\F677"}.mdi-bowl::before{content:"\F617"}.mdi-bowling::before{content:"\F0D3"}.mdi-box::before{content:"\F0D4"}.mdi-box-cutter::before{content:"\F0D5"}.mdi-box-shadow::before{content:"\F637"}.mdi-boxing-glove::before{content:"\FB41"}.mdi-braille::before{content:"\F9CF"}.mdi-brain::before{content:"\F9D0"}.mdi-bread-slice::before{content:"\FCCA"}.mdi-bread-slice-outline::before{content:"\FCCB"}.mdi-bridge::before{content:"\F618"}.mdi-briefcase::before{content:"\F0D6"}.mdi-briefcase-account::before{content:"\FCCC"}.mdi-briefcase-account-outline::before{content:"\FCCD"}.mdi-briefcase-check::before{content:"\F0D7"}.mdi-briefcase-check-outline::before{content:"\F0349"}.mdi-briefcase-clock::before{content:"\F00FB"}.mdi-briefcase-clock-outline::before{content:"\F00FC"}.mdi-briefcase-download::before{content:"\F0D8"}.mdi-briefcase-download-outline::before{content:"\FC19"}.mdi-briefcase-edit::before{content:"\FA97"}.mdi-briefcase-edit-outline::before{content:"\FC1A"}.mdi-briefcase-minus::before{content:"\FA29"}.mdi-briefcase-minus-outline::before{content:"\FC1B"}.mdi-briefcase-outline::before{content:"\F813"}.mdi-briefcase-plus::before{content:"\FA2A"}.mdi-briefcase-plus-outline::before{content:"\FC1C"}.mdi-briefcase-remove::before{content:"\FA2B"}.mdi-briefcase-remove-outline::before{content:"\FC1D"}.mdi-briefcase-search::before{content:"\FA2C"}.mdi-briefcase-search-outline::before{content:"\FC1E"}.mdi-briefcase-upload::before{content:"\F0D9"}.mdi-briefcase-upload-outline::before{content:"\FC1F"}.mdi-brightness-1::before{content:"\F0DA"}.mdi-brightness-2::before{content:"\F0DB"}.mdi-brightness-3::before{content:"\F0DC"}.mdi-brightness-4::before{content:"\F0DD"}.mdi-brightness-5::before{content:"\F0DE"}.mdi-brightness-6::before{content:"\F0DF"}.mdi-brightness-7::before{content:"\F0E0"}.mdi-brightness-auto::before{content:"\F0E1"}.mdi-brightness-percent::before{content:"\FCCE"}.mdi-broom::before{content:"\F0E2"}.mdi-brush::before{content:"\F0E3"}.mdi-buddhism::before{content:"\F94A"}.mdi-buffer::before{content:"\F619"}.mdi-bug::before{content:"\F0E4"}.mdi-bug-check::before{content:"\FA2D"}.mdi-bug-check-outline::before{content:"\FA2E"}.mdi-bug-outline::before{content:"\FA2F"}.mdi-bugle::before{content:"\FD90"}.mdi-bulldozer::before{content:"\FB07"}.mdi-bullet::before{content:"\FCCF"}.mdi-bulletin-board::before{content:"\F0E5"}.mdi-bullhorn::before{content:"\F0E6"}.mdi-bullhorn-outline::before{content:"\FB08"}.mdi-bullseye::before{content:"\F5DD"}.mdi-bullseye-arrow::before{content:"\F8C8"}.mdi-bulma::before{content:"\F0312"}.mdi-bunk-bed::before{content:"\F032D"}.mdi-bus::before{content:"\F0E7"}.mdi-bus-alert::before{content:"\FA98"}.mdi-bus-articulated-end::before{content:"\F79B"}.mdi-bus-articulated-front::before{content:"\F79C"}.mdi-bus-clock::before{content:"\F8C9"}.mdi-bus-double-decker::before{content:"\F79D"}.mdi-bus-marker::before{content:"\F023D"}.mdi-bus-multiple::before{content:"\FF5C"}.mdi-bus-school::before{content:"\F79E"}.mdi-bus-side::before{content:"\F79F"}.mdi-bus-stop::before{content:"\F0034"}.mdi-bus-stop-covered::before{content:"\F0035"}.mdi-bus-stop-uncovered::before{content:"\F0036"}.mdi-cached::before{content:"\F0E8"}.mdi-cactus::before{content:"\FD91"}.mdi-cake::before{content:"\F0E9"}.mdi-cake-layered::before{content:"\F0EA"}.mdi-cake-variant::before{content:"\F0EB"}.mdi-calculator::before{content:"\F0EC"}.mdi-calculator-variant::before{content:"\FA99"}.mdi-calendar::before{content:"\F0ED"}.mdi-calendar-account::before{content:"\FEF4"}.mdi-calendar-account-outline::before{content:"\FEF5"}.mdi-calendar-alert::before{content:"\FA30"}.mdi-calendar-arrow-left::before{content:"\F015F"}.mdi-calendar-arrow-right::before{content:"\F0160"}.mdi-calendar-blank::before{content:"\F0EE"}.mdi-calendar-blank-multiple::before{content:"\F009E"}.mdi-calendar-blank-outline::before{content:"\FB42"}.mdi-calendar-check::before{content:"\F0EF"}.mdi-calendar-check-outline::before{content:"\FC20"}.mdi-calendar-clock::before{content:"\F0F0"}.mdi-calendar-edit::before{content:"\F8A6"}.mdi-calendar-export::before{content:"\FB09"}.mdi-calendar-heart::before{content:"\F9D1"}.mdi-calendar-import::before{content:"\FB0A"}.mdi-calendar-minus::before{content:"\FD38"}.mdi-calendar-month::before{content:"\FDFA"}.mdi-calendar-month-outline::before{content:"\FDFB"}.mdi-calendar-multiple::before{content:"\F0F1"}.mdi-calendar-multiple-check::before{content:"\F0F2"}.mdi-calendar-multiselect::before{content:"\FA31"}.mdi-calendar-outline::before{content:"\FB43"}.mdi-calendar-plus::before{content:"\F0F3"}.mdi-calendar-question::before{content:"\F691"}.mdi-calendar-range::before{content:"\F678"}.mdi-calendar-range-outline::before{content:"\FB44"}.mdi-calendar-remove::before{content:"\F0F4"}.mdi-calendar-remove-outline::before{content:"\FC21"}.mdi-calendar-repeat::before{content:"\FEAB"}.mdi-calendar-repeat-outline::before{content:"\FEAC"}.mdi-calendar-search::before{content:"\F94B"}.mdi-calendar-star::before{content:"\F9D2"}.mdi-calendar-text::before{content:"\F0F5"}.mdi-calendar-text-outline::before{content:"\FC22"}.mdi-calendar-today::before{content:"\F0F6"}.mdi-calendar-week::before{content:"\FA32"}.mdi-calendar-week-begin::before{content:"\FA33"}.mdi-calendar-weekend::before{content:"\FEF6"}.mdi-calendar-weekend-outline::before{content:"\FEF7"}.mdi-call-made::before{content:"\F0F7"}.mdi-call-merge::before{content:"\F0F8"}.mdi-call-missed::before{content:"\F0F9"}.mdi-call-received::before{content:"\F0FA"}.mdi-call-split::before{content:"\F0FB"}.mdi-camcorder::before{content:"\F0FC"}.mdi-camcorder-box::before{content:"\F0FD"}.mdi-camcorder-box-off::before{content:"\F0FE"}.mdi-camcorder-off::before{content:"\F0FF"}.mdi-camera::before{content:"\F100"}.mdi-camera-account::before{content:"\F8CA"}.mdi-camera-burst::before{content:"\F692"}.mdi-camera-control::before{content:"\FB45"}.mdi-camera-enhance::before{content:"\F101"}.mdi-camera-enhance-outline::before{content:"\FB46"}.mdi-camera-front::before{content:"\F102"}.mdi-camera-front-variant::before{content:"\F103"}.mdi-camera-gopro::before{content:"\F7A0"}.mdi-camera-image::before{content:"\F8CB"}.mdi-camera-iris::before{content:"\F104"}.mdi-camera-metering-center::before{content:"\F7A1"}.mdi-camera-metering-matrix::before{content:"\F7A2"}.mdi-camera-metering-partial::before{content:"\F7A3"}.mdi-camera-metering-spot::before{content:"\F7A4"}.mdi-camera-off::before{content:"\F5DF"}.mdi-camera-outline::before{content:"\FD39"}.mdi-camera-party-mode::before{content:"\F105"}.mdi-camera-plus::before{content:"\FEF8"}.mdi-camera-plus-outline::before{content:"\FEF9"}.mdi-camera-rear::before{content:"\F106"}.mdi-camera-rear-variant::before{content:"\F107"}.mdi-camera-retake::before{content:"\FDFC"}.mdi-camera-retake-outline::before{content:"\FDFD"}.mdi-camera-switch::before{content:"\F108"}.mdi-camera-timer::before{content:"\F109"}.mdi-camera-wireless::before{content:"\FD92"}.mdi-camera-wireless-outline::before{content:"\FD93"}.mdi-campfire::before{content:"\FEFA"}.mdi-cancel::before{content:"\F739"}.mdi-candle::before{content:"\F5E2"}.mdi-candycane::before{content:"\F10A"}.mdi-cannabis::before{content:"\F7A5"}.mdi-caps-lock::before{content:"\FA9A"}.mdi-car::before{content:"\F10B"}.mdi-car-2-plus::before{content:"\F0037"}.mdi-car-3-plus::before{content:"\F0038"}.mdi-car-back::before{content:"\FDFE"}.mdi-car-battery::before{content:"\F10C"}.mdi-car-brake-abs::before{content:"\FC23"}.mdi-car-brake-alert::before{content:"\FC24"}.mdi-car-brake-hold::before{content:"\FD3A"}.mdi-car-brake-parking::before{content:"\FD3B"}.mdi-car-brake-retarder::before{content:"\F0039"}.mdi-car-child-seat::before{content:"\FFC3"}.mdi-car-clutch::before{content:"\F003A"}.mdi-car-connected::before{content:"\F10D"}.mdi-car-convertible::before{content:"\F7A6"}.mdi-car-coolant-level::before{content:"\F003B"}.mdi-car-cruise-control::before{content:"\FD3C"}.mdi-car-defrost-front::before{content:"\FD3D"}.mdi-car-defrost-rear::before{content:"\FD3E"}.mdi-car-door::before{content:"\FB47"}.mdi-car-door-lock::before{content:"\F00C8"}.mdi-car-electric::before{content:"\FB48"}.mdi-car-esp::before{content:"\FC25"}.mdi-car-estate::before{content:"\F7A7"}.mdi-car-hatchback::before{content:"\F7A8"}.mdi-car-info::before{content:"\F01E9"}.mdi-car-key::before{content:"\FB49"}.mdi-car-light-dimmed::before{content:"\FC26"}.mdi-car-light-fog::before{content:"\FC27"}.mdi-car-light-high::before{content:"\FC28"}.mdi-car-limousine::before{content:"\F8CC"}.mdi-car-multiple::before{content:"\FB4A"}.mdi-car-off::before{content:"\FDFF"}.mdi-car-parking-lights::before{content:"\FD3F"}.mdi-car-pickup::before{content:"\F7A9"}.mdi-car-seat::before{content:"\FFC4"}.mdi-car-seat-cooler::before{content:"\FFC5"}.mdi-car-seat-heater::before{content:"\FFC6"}.mdi-car-shift-pattern::before{content:"\FF5D"}.mdi-car-side::before{content:"\F7AA"}.mdi-car-sports::before{content:"\F7AB"}.mdi-car-tire-alert::before{content:"\FC29"}.mdi-car-traction-control::before{content:"\FD40"}.mdi-car-turbocharger::before{content:"\F003C"}.mdi-car-wash::before{content:"\F10E"}.mdi-car-windshield::before{content:"\F003D"}.mdi-car-windshield-outline::before{content:"\F003E"}.mdi-caravan::before{content:"\F7AC"}.mdi-card::before{content:"\FB4B"}.mdi-card-bulleted::before{content:"\FB4C"}.mdi-card-bulleted-off::before{content:"\FB4D"}.mdi-card-bulleted-off-outline::before{content:"\FB4E"}.mdi-card-bulleted-outline::before{content:"\FB4F"}.mdi-card-bulleted-settings::before{content:"\FB50"}.mdi-card-bulleted-settings-outline::before{content:"\FB51"}.mdi-card-outline::before{content:"\FB52"}.mdi-card-plus::before{content:"\F022A"}.mdi-card-plus-outline::before{content:"\F022B"}.mdi-card-search::before{content:"\F009F"}.mdi-card-search-outline::before{content:"\F00A0"}.mdi-card-text::before{content:"\FB53"}.mdi-card-text-outline::before{content:"\FB54"}.mdi-cards::before{content:"\F638"}.mdi-cards-club::before{content:"\F8CD"}.mdi-cards-diamond::before{content:"\F8CE"}.mdi-cards-diamond-outline::before{content:"\F003F"}.mdi-cards-heart::before{content:"\F8CF"}.mdi-cards-outline::before{content:"\F639"}.mdi-cards-playing-outline::before{content:"\F63A"}.mdi-cards-spade::before{content:"\F8D0"}.mdi-cards-variant::before{content:"\F6C6"}.mdi-carrot::before{content:"\F10F"}.mdi-cart::before{content:"\F110"}.mdi-cart-arrow-down::before{content:"\FD42"}.mdi-cart-arrow-right::before{content:"\FC2A"}.mdi-cart-arrow-up::before{content:"\FD43"}.mdi-cart-minus::before{content:"\FD44"}.mdi-cart-off::before{content:"\F66B"}.mdi-cart-outline::before{content:"\F111"}.mdi-cart-plus::before{content:"\F112"}.mdi-cart-remove::before{content:"\FD45"}.mdi-case-sensitive-alt::before{content:"\F113"}.mdi-cash::before{content:"\F114"}.mdi-cash-100::before{content:"\F115"}.mdi-cash-marker::before{content:"\FD94"}.mdi-cash-minus::before{content:"\F028B"}.mdi-cash-multiple::before{content:"\F116"}.mdi-cash-plus::before{content:"\F028C"}.mdi-cash-refund::before{content:"\FA9B"}.mdi-cash-register::before{content:"\FCD0"}.mdi-cash-remove::before{content:"\F028D"}.mdi-cash-usd::before{content:"\F01A1"}.mdi-cash-usd-outline::before{content:"\F117"}.mdi-cassette::before{content:"\F9D3"}.mdi-cast::before{content:"\F118"}.mdi-cast-audio::before{content:"\F0040"}.mdi-cast-connected::before{content:"\F119"}.mdi-cast-education::before{content:"\FE6D"}.mdi-cast-off::before{content:"\F789"}.mdi-castle::before{content:"\F11A"}.mdi-cat::before{content:"\F11B"}.mdi-cctv::before{content:"\F7AD"}.mdi-ceiling-light::before{content:"\F768"}.mdi-cellphone::before{content:"\F11C"}.mdi-cellphone-android::before{content:"\F11D"}.mdi-cellphone-arrow-down::before{content:"\F9D4"}.mdi-cellphone-basic::before{content:"\F11E"}.mdi-cellphone-dock::before{content:"\F11F"}.mdi-cellphone-erase::before{content:"\F94C"}.mdi-cellphone-information::before{content:"\FF5E"}.mdi-cellphone-iphone::before{content:"\F120"}.mdi-cellphone-key::before{content:"\F94D"}.mdi-cellphone-link::before{content:"\F121"}.mdi-cellphone-link-off::before{content:"\F122"}.mdi-cellphone-lock::before{content:"\F94E"}.mdi-cellphone-message::before{content:"\F8D2"}.mdi-cellphone-message-off::before{content:"\F00FD"}.mdi-cellphone-nfc::before{content:"\FEAD"}.mdi-cellphone-nfc-off::before{content:"\F0303"}.mdi-cellphone-off::before{content:"\F94F"}.mdi-cellphone-play::before{content:"\F0041"}.mdi-cellphone-screenshot::before{content:"\FA34"}.mdi-cellphone-settings::before{content:"\F123"}.mdi-cellphone-settings-variant::before{content:"\F950"}.mdi-cellphone-sound::before{content:"\F951"}.mdi-cellphone-text::before{content:"\F8D1"}.mdi-cellphone-wireless::before{content:"\F814"}.mdi-celtic-cross::before{content:"\FCD1"}.mdi-centos::before{content:"\F0145"}.mdi-certificate::before{content:"\F124"}.mdi-certificate-outline::before{content:"\F01B3"}.mdi-chair-rolling::before{content:"\FFBA"}.mdi-chair-school::before{content:"\F125"}.mdi-charity::before{content:"\FC2B"}.mdi-chart-arc::before{content:"\F126"}.mdi-chart-areaspline::before{content:"\F127"}.mdi-chart-areaspline-variant::before{content:"\FEAE"}.mdi-chart-bar::before{content:"\F128"}.mdi-chart-bar-stacked::before{content:"\F769"}.mdi-chart-bell-curve::before{content:"\FC2C"}.mdi-chart-bell-curve-cumulative::before{content:"\FFC7"}.mdi-chart-bubble::before{content:"\F5E3"}.mdi-chart-donut::before{content:"\F7AE"}.mdi-chart-donut-variant::before{content:"\F7AF"}.mdi-chart-gantt::before{content:"\F66C"}.mdi-chart-histogram::before{content:"\F129"}.mdi-chart-line::before{content:"\F12A"}.mdi-chart-line-stacked::before{content:"\F76A"}.mdi-chart-line-variant::before{content:"\F7B0"}.mdi-chart-multiline::before{content:"\F8D3"}.mdi-chart-multiple::before{content:"\F023E"}.mdi-chart-pie::before{content:"\F12B"}.mdi-chart-ppf::before{content:"\F03AB"}.mdi-chart-scatter-plot::before{content:"\FEAF"}.mdi-chart-scatter-plot-hexbin::before{content:"\F66D"}.mdi-chart-snakey::before{content:"\F020A"}.mdi-chart-snakey-variant::before{content:"\F020B"}.mdi-chart-timeline::before{content:"\F66E"}.mdi-chart-timeline-variant::before{content:"\FEB0"}.mdi-chart-tree::before{content:"\FEB1"}.mdi-chat::before{content:"\FB55"}.mdi-chat-alert::before{content:"\FB56"}.mdi-chat-alert-outline::before{content:"\F02F4"}.mdi-chat-outline::before{content:"\FEFB"}.mdi-chat-processing::before{content:"\FB57"}.mdi-chat-processing-outline::before{content:"\F02F5"}.mdi-chat-sleep::before{content:"\F02FC"}.mdi-chat-sleep-outline::before{content:"\F02FD"}.mdi-check::before{content:"\F12C"}.mdi-check-all::before{content:"\F12D"}.mdi-check-bold::before{content:"\FE6E"}.mdi-check-box-multiple-outline::before{content:"\FC2D"}.mdi-check-box-outline::before{content:"\FC2E"}.mdi-check-circle::before{content:"\F5E0"}.mdi-check-circle-outline::before{content:"\F5E1"}.mdi-check-decagram::before{content:"\F790"}.mdi-check-network::before{content:"\FC2F"}.mdi-check-network-outline::before{content:"\FC30"}.mdi-check-outline::before{content:"\F854"}.mdi-check-underline::before{content:"\FE70"}.mdi-check-underline-circle::before{content:"\FE71"}.mdi-check-underline-circle-outline::before{content:"\FE72"}.mdi-checkbook::before{content:"\FA9C"}.mdi-checkbox-blank::before{content:"\F12E"}.mdi-checkbox-blank-circle::before{content:"\F12F"}.mdi-checkbox-blank-circle-outline::before{content:"\F130"}.mdi-checkbox-blank-off::before{content:"\F0317"}.mdi-checkbox-blank-off-outline::before{content:"\F0318"}.mdi-checkbox-blank-outline::before{content:"\F131"}.mdi-checkbox-intermediate::before{content:"\F855"}.mdi-checkbox-marked::before{content:"\F132"}.mdi-checkbox-marked-circle::before{content:"\F133"}.mdi-checkbox-marked-circle-outline::before{content:"\F134"}.mdi-checkbox-marked-outline::before{content:"\F135"}.mdi-checkbox-multiple-blank::before{content:"\F136"}.mdi-checkbox-multiple-blank-circle::before{content:"\F63B"}.mdi-checkbox-multiple-blank-circle-outline::before{content:"\F63C"}.mdi-checkbox-multiple-blank-outline::before{content:"\F137"}.mdi-checkbox-multiple-marked::before{content:"\F138"}.mdi-checkbox-multiple-marked-circle::before{content:"\F63D"}.mdi-checkbox-multiple-marked-circle-outline::before{content:"\F63E"}.mdi-checkbox-multiple-marked-outline::before{content:"\F139"}.mdi-checkerboard::before{content:"\F13A"}.mdi-checkerboard-minus::before{content:"\F022D"}.mdi-checkerboard-plus::before{content:"\F022C"}.mdi-checkerboard-remove::before{content:"\F022E"}.mdi-cheese::before{content:"\F02E4"}.mdi-chef-hat::before{content:"\FB58"}.mdi-chemical-weapon::before{content:"\F13B"}.mdi-chess-bishop::before{content:"\F85B"}.mdi-chess-king::before{content:"\F856"}.mdi-chess-knight::before{content:"\F857"}.mdi-chess-pawn::before{content:"\F858"}.mdi-chess-queen::before{content:"\F859"}.mdi-chess-rook::before{content:"\F85A"}.mdi-chevron-double-down::before{content:"\F13C"}.mdi-chevron-double-left::before{content:"\F13D"}.mdi-chevron-double-right::before{content:"\F13E"}.mdi-chevron-double-up::before{content:"\F13F"}.mdi-chevron-down::before{content:"\F140"}.mdi-chevron-down-box::before{content:"\F9D5"}.mdi-chevron-down-box-outline::before{content:"\F9D6"}.mdi-chevron-down-circle::before{content:"\FB0B"}.mdi-chevron-down-circle-outline::before{content:"\FB0C"}.mdi-chevron-left::before{content:"\F141"}.mdi-chevron-left-box::before{content:"\F9D7"}.mdi-chevron-left-box-outline::before{content:"\F9D8"}.mdi-chevron-left-circle::before{content:"\FB0D"}.mdi-chevron-left-circle-outline::before{content:"\FB0E"}.mdi-chevron-right::before{content:"\F142"}.mdi-chevron-right-box::before{content:"\F9D9"}.mdi-chevron-right-box-outline::before{content:"\F9DA"}.mdi-chevron-right-circle::before{content:"\FB0F"}.mdi-chevron-right-circle-outline::before{content:"\FB10"}.mdi-chevron-triple-down::before{content:"\FD95"}.mdi-chevron-triple-left::before{content:"\FD96"}.mdi-chevron-triple-right::before{content:"\FD97"}.mdi-chevron-triple-up::before{content:"\FD98"}.mdi-chevron-up::before{content:"\F143"}.mdi-chevron-up-box::before{content:"\F9DB"}.mdi-chevron-up-box-outline::before{content:"\F9DC"}.mdi-chevron-up-circle::before{content:"\FB11"}.mdi-chevron-up-circle-outline::before{content:"\FB12"}.mdi-chili-hot::before{content:"\F7B1"}.mdi-chili-medium::before{content:"\F7B2"}.mdi-chili-mild::before{content:"\F7B3"}.mdi-chip::before{content:"\F61A"}.mdi-christianity::before{content:"\F952"}.mdi-christianity-outline::before{content:"\FCD2"}.mdi-church::before{content:"\F144"}.mdi-cigar::before{content:"\F01B4"}.mdi-circle::before{content:"\F764"}.mdi-circle-double::before{content:"\FEB2"}.mdi-circle-edit-outline::before{content:"\F8D4"}.mdi-circle-expand::before{content:"\FEB3"}.mdi-circle-medium::before{content:"\F9DD"}.mdi-circle-off-outline::before{content:"\F00FE"}.mdi-circle-outline::before{content:"\F765"}.mdi-circle-slice-1::before{content:"\FA9D"}.mdi-circle-slice-2::before{content:"\FA9E"}.mdi-circle-slice-3::before{content:"\FA9F"}.mdi-circle-slice-4::before{content:"\FAA0"}.mdi-circle-slice-5::before{content:"\FAA1"}.mdi-circle-slice-6::before{content:"\FAA2"}.mdi-circle-slice-7::before{content:"\FAA3"}.mdi-circle-slice-8::before{content:"\FAA4"}.mdi-circle-small::before{content:"\F9DE"}.mdi-circular-saw::before{content:"\FE73"}.mdi-cisco-webex::before{content:"\F145"}.mdi-city::before{content:"\F146"}.mdi-city-variant::before{content:"\FA35"}.mdi-city-variant-outline::before{content:"\FA36"}.mdi-clipboard::before{content:"\F147"}.mdi-clipboard-account::before{content:"\F148"}.mdi-clipboard-account-outline::before{content:"\FC31"}.mdi-clipboard-alert::before{content:"\F149"}.mdi-clipboard-alert-outline::before{content:"\FCD3"}.mdi-clipboard-arrow-down::before{content:"\F14A"}.mdi-clipboard-arrow-down-outline::before{content:"\FC32"}.mdi-clipboard-arrow-left::before{content:"\F14B"}.mdi-clipboard-arrow-left-outline::before{content:"\FCD4"}.mdi-clipboard-arrow-right::before{content:"\FCD5"}.mdi-clipboard-arrow-right-outline::before{content:"\FCD6"}.mdi-clipboard-arrow-up::before{content:"\FC33"}.mdi-clipboard-arrow-up-outline::before{content:"\FC34"}.mdi-clipboard-check::before{content:"\F14C"}.mdi-clipboard-check-multiple::before{content:"\F028E"}.mdi-clipboard-check-multiple-outline::before{content:"\F028F"}.mdi-clipboard-check-outline::before{content:"\F8A7"}.mdi-clipboard-file::before{content:"\F0290"}.mdi-clipboard-file-outline::before{content:"\F0291"}.mdi-clipboard-flow::before{content:"\F6C7"}.mdi-clipboard-flow-outline::before{content:"\F0142"}.mdi-clipboard-list::before{content:"\F00FF"}.mdi-clipboard-list-outline::before{content:"\F0100"}.mdi-clipboard-multiple::before{content:"\F0292"}.mdi-clipboard-multiple-outline::before{content:"\F0293"}.mdi-clipboard-outline::before{content:"\F14D"}.mdi-clipboard-play::before{content:"\FC35"}.mdi-clipboard-play-multiple::before{content:"\F0294"}.mdi-clipboard-play-multiple-outline::before{content:"\F0295"}.mdi-clipboard-play-outline::before{content:"\FC36"}.mdi-clipboard-plus::before{content:"\F750"}.mdi-clipboard-plus-outline::before{content:"\F034A"}.mdi-clipboard-pulse::before{content:"\F85C"}.mdi-clipboard-pulse-outline::before{content:"\F85D"}.mdi-clipboard-text::before{content:"\F14E"}.mdi-clipboard-text-multiple::before{content:"\F0296"}.mdi-clipboard-text-multiple-outline::before{content:"\F0297"}.mdi-clipboard-text-outline::before{content:"\FA37"}.mdi-clipboard-text-play::before{content:"\FC37"}.mdi-clipboard-text-play-outline::before{content:"\FC38"}.mdi-clippy::before{content:"\F14F"}.mdi-clock::before{content:"\F953"}.mdi-clock-alert::before{content:"\F954"}.mdi-clock-alert-outline::before{content:"\F5CE"}.mdi-clock-check::before{content:"\FFC8"}.mdi-clock-check-outline::before{content:"\FFC9"}.mdi-clock-digital::before{content:"\FEB4"}.mdi-clock-end::before{content:"\F151"}.mdi-clock-fast::before{content:"\F152"}.mdi-clock-in::before{content:"\F153"}.mdi-clock-out::before{content:"\F154"}.mdi-clock-outline::before{content:"\F150"}.mdi-clock-start::before{content:"\F155"}.mdi-close::before{content:"\F156"}.mdi-close-box::before{content:"\F157"}.mdi-close-box-multiple::before{content:"\FC39"}.mdi-close-box-multiple-outline::before{content:"\FC3A"}.mdi-close-box-outline::before{content:"\F158"}.mdi-close-circle::before{content:"\F159"}.mdi-close-circle-outline::before{content:"\F15A"}.mdi-close-network::before{content:"\F15B"}.mdi-close-network-outline::before{content:"\FC3B"}.mdi-close-octagon::before{content:"\F15C"}.mdi-close-octagon-outline::before{content:"\F15D"}.mdi-close-outline::before{content:"\F6C8"}.mdi-closed-caption::before{content:"\F15E"}.mdi-closed-caption-outline::before{content:"\FD99"}.mdi-cloud::before{content:"\F15F"}.mdi-cloud-alert::before{content:"\F9DF"}.mdi-cloud-braces::before{content:"\F7B4"}.mdi-cloud-check::before{content:"\F160"}.mdi-cloud-check-outline::before{content:"\F02F7"}.mdi-cloud-circle::before{content:"\F161"}.mdi-cloud-download::before{content:"\F162"}.mdi-cloud-download-outline::before{content:"\FB59"}.mdi-cloud-lock::before{content:"\F021C"}.mdi-cloud-lock-outline::before{content:"\F021D"}.mdi-cloud-off-outline::before{content:"\F164"}.mdi-cloud-outline::before{content:"\F163"}.mdi-cloud-print::before{content:"\F165"}.mdi-cloud-print-outline::before{content:"\F166"}.mdi-cloud-question::before{content:"\FA38"}.mdi-cloud-search::before{content:"\F955"}.mdi-cloud-search-outline::before{content:"\F956"}.mdi-cloud-sync::before{content:"\F63F"}.mdi-cloud-sync-outline::before{content:"\F0301"}.mdi-cloud-tags::before{content:"\F7B5"}.mdi-cloud-upload::before{content:"\F167"}.mdi-cloud-upload-outline::before{content:"\FB5A"}.mdi-clover::before{content:"\F815"}.mdi-coach-lamp::before{content:"\F0042"}.mdi-coat-rack::before{content:"\F00C9"}.mdi-code-array::before{content:"\F168"}.mdi-code-braces::before{content:"\F169"}.mdi-code-braces-box::before{content:"\F0101"}.mdi-code-brackets::before{content:"\F16A"}.mdi-code-equal::before{content:"\F16B"}.mdi-code-greater-than::before{content:"\F16C"}.mdi-code-greater-than-or-equal::before{content:"\F16D"}.mdi-code-less-than::before{content:"\F16E"}.mdi-code-less-than-or-equal::before{content:"\F16F"}.mdi-code-not-equal::before{content:"\F170"}.mdi-code-not-equal-variant::before{content:"\F171"}.mdi-code-parentheses::before{content:"\F172"}.mdi-code-parentheses-box::before{content:"\F0102"}.mdi-code-string::before{content:"\F173"}.mdi-code-tags::before{content:"\F174"}.mdi-code-tags-check::before{content:"\F693"}.mdi-codepen::before{content:"\F175"}.mdi-coffee::before{content:"\F176"}.mdi-coffee-maker::before{content:"\F00CA"}.mdi-coffee-off::before{content:"\FFCA"}.mdi-coffee-off-outline::before{content:"\FFCB"}.mdi-coffee-outline::before{content:"\F6C9"}.mdi-coffee-to-go::before{content:"\F177"}.mdi-coffee-to-go-outline::before{content:"\F0339"}.mdi-coffin::before{content:"\FB5B"}.mdi-cog-clockwise::before{content:"\F0208"}.mdi-cog-counterclockwise::before{content:"\F0209"}.mdi-cogs::before{content:"\F8D5"}.mdi-coin::before{content:"\F0196"}.mdi-coin-outline::before{content:"\F178"}.mdi-coins::before{content:"\F694"}.mdi-collage::before{content:"\F640"}.mdi-collapse-all::before{content:"\FAA5"}.mdi-collapse-all-outline::before{content:"\FAA6"}.mdi-color-helper::before{content:"\F179"}.mdi-comma::before{content:"\FE74"}.mdi-comma-box::before{content:"\FE75"}.mdi-comma-box-outline::before{content:"\FE76"}.mdi-comma-circle::before{content:"\FE77"}.mdi-comma-circle-outline::before{content:"\FE78"}.mdi-comment::before{content:"\F17A"}.mdi-comment-account::before{content:"\F17B"}.mdi-comment-account-outline::before{content:"\F17C"}.mdi-comment-alert::before{content:"\F17D"}.mdi-comment-alert-outline::before{content:"\F17E"}.mdi-comment-arrow-left::before{content:"\F9E0"}.mdi-comment-arrow-left-outline::before{content:"\F9E1"}.mdi-comment-arrow-right::before{content:"\F9E2"}.mdi-comment-arrow-right-outline::before{content:"\F9E3"}.mdi-comment-check::before{content:"\F17F"}.mdi-comment-check-outline::before{content:"\F180"}.mdi-comment-edit::before{content:"\F01EA"}.mdi-comment-edit-outline::before{content:"\F02EF"}.mdi-comment-eye::before{content:"\FA39"}.mdi-comment-eye-outline::before{content:"\FA3A"}.mdi-comment-multiple::before{content:"\F85E"}.mdi-comment-multiple-outline::before{content:"\F181"}.mdi-comment-outline::before{content:"\F182"}.mdi-comment-plus::before{content:"\F9E4"}.mdi-comment-plus-outline::before{content:"\F183"}.mdi-comment-processing::before{content:"\F184"}.mdi-comment-processing-outline::before{content:"\F185"}.mdi-comment-question::before{content:"\F816"}.mdi-comment-question-outline::before{content:"\F186"}.mdi-comment-quote::before{content:"\F0043"}.mdi-comment-quote-outline::before{content:"\F0044"}.mdi-comment-remove::before{content:"\F5DE"}.mdi-comment-remove-outline::before{content:"\F187"}.mdi-comment-search::before{content:"\FA3B"}.mdi-comment-search-outline::before{content:"\FA3C"}.mdi-comment-text::before{content:"\F188"}.mdi-comment-text-multiple::before{content:"\F85F"}.mdi-comment-text-multiple-outline::before{content:"\F860"}.mdi-comment-text-outline::before{content:"\F189"}.mdi-compare::before{content:"\F18A"}.mdi-compass::before{content:"\F18B"}.mdi-compass-off::before{content:"\FB5C"}.mdi-compass-off-outline::before{content:"\FB5D"}.mdi-compass-outline::before{content:"\F18C"}.mdi-compass-rose::before{content:"\F03AD"}.mdi-concourse-ci::before{content:"\F00CB"}.mdi-console::before{content:"\F18D"}.mdi-console-line::before{content:"\F7B6"}.mdi-console-network::before{content:"\F8A8"}.mdi-console-network-outline::before{content:"\FC3C"}.mdi-consolidate::before{content:"\F0103"}.mdi-contact-mail::before{content:"\F18E"}.mdi-contact-mail-outline::before{content:"\FEB5"}.mdi-contact-phone::before{content:"\FEB6"}.mdi-contact-phone-outline::before{content:"\FEB7"}.mdi-contactless-payment::before{content:"\FD46"}.mdi-contacts::before{content:"\F6CA"}.mdi-contain::before{content:"\FA3D"}.mdi-contain-end::before{content:"\FA3E"}.mdi-contain-start::before{content:"\FA3F"}.mdi-content-copy::before{content:"\F18F"}.mdi-content-cut::before{content:"\F190"}.mdi-content-duplicate::before{content:"\F191"}.mdi-content-paste::before{content:"\F192"}.mdi-content-save::before{content:"\F193"}.mdi-content-save-alert::before{content:"\FF5F"}.mdi-content-save-alert-outline::before{content:"\FF60"}.mdi-content-save-all::before{content:"\F194"}.mdi-content-save-all-outline::before{content:"\FF61"}.mdi-content-save-edit::before{content:"\FCD7"}.mdi-content-save-edit-outline::before{content:"\FCD8"}.mdi-content-save-move::before{content:"\FE79"}.mdi-content-save-move-outline::before{content:"\FE7A"}.mdi-content-save-outline::before{content:"\F817"}.mdi-content-save-settings::before{content:"\F61B"}.mdi-content-save-settings-outline::before{content:"\FB13"}.mdi-contrast::before{content:"\F195"}.mdi-contrast-box::before{content:"\F196"}.mdi-contrast-circle::before{content:"\F197"}.mdi-controller-classic::before{content:"\FB5E"}.mdi-controller-classic-outline::before{content:"\FB5F"}.mdi-cookie::before{content:"\F198"}.mdi-coolant-temperature::before{content:"\F3C8"}.mdi-copyright::before{content:"\F5E6"}.mdi-cordova::before{content:"\F957"}.mdi-corn::before{content:"\F7B7"}.mdi-counter::before{content:"\F199"}.mdi-cow::before{content:"\F19A"}.mdi-cowboy::before{content:"\FEB8"}.mdi-cpu-32-bit::before{content:"\FEFC"}.mdi-cpu-64-bit::before{content:"\FEFD"}.mdi-crane::before{content:"\F861"}.mdi-creation::before{content:"\F1C9"}.mdi-creative-commons::before{content:"\FD47"}.mdi-credit-card::before{content:"\F0010"}.mdi-credit-card-clock::before{content:"\FEFE"}.mdi-credit-card-clock-outline::before{content:"\FFBC"}.mdi-credit-card-marker::before{content:"\F6A7"}.mdi-credit-card-marker-outline::before{content:"\FD9A"}.mdi-credit-card-minus::before{content:"\FFCC"}.mdi-credit-card-minus-outline::before{content:"\FFCD"}.mdi-credit-card-multiple::before{content:"\F0011"}.mdi-credit-card-multiple-outline::before{content:"\F19C"}.mdi-credit-card-off::before{content:"\F0012"}.mdi-credit-card-off-outline::before{content:"\F5E4"}.mdi-credit-card-outline::before{content:"\F19B"}.mdi-credit-card-plus::before{content:"\F0013"}.mdi-credit-card-plus-outline::before{content:"\F675"}.mdi-credit-card-refund::before{content:"\F0014"}.mdi-credit-card-refund-outline::before{content:"\FAA7"}.mdi-credit-card-remove::before{content:"\FFCE"}.mdi-credit-card-remove-outline::before{content:"\FFCF"}.mdi-credit-card-scan::before{content:"\F0015"}.mdi-credit-card-scan-outline::before{content:"\F19D"}.mdi-credit-card-settings::before{content:"\F0016"}.mdi-credit-card-settings-outline::before{content:"\F8D6"}.mdi-credit-card-wireless::before{content:"\F801"}.mdi-credit-card-wireless-outline::before{content:"\FD48"}.mdi-cricket::before{content:"\FD49"}.mdi-crop::before{content:"\F19E"}.mdi-crop-free::before{content:"\F19F"}.mdi-crop-landscape::before{content:"\F1A0"}.mdi-crop-portrait::before{content:"\F1A1"}.mdi-crop-rotate::before{content:"\F695"}.mdi-crop-square::before{content:"\F1A2"}.mdi-crosshairs::before{content:"\F1A3"}.mdi-crosshairs-gps::before{content:"\F1A4"}.mdi-crosshairs-off::before{content:"\FF62"}.mdi-crosshairs-question::before{content:"\F0161"}.mdi-crown::before{content:"\F1A5"}.mdi-crown-outline::before{content:"\F01FB"}.mdi-cryengine::before{content:"\F958"}.mdi-crystal-ball::before{content:"\FB14"}.mdi-cube::before{content:"\F1A6"}.mdi-cube-outline::before{content:"\F1A7"}.mdi-cube-scan::before{content:"\FB60"}.mdi-cube-send::before{content:"\F1A8"}.mdi-cube-unfolded::before{content:"\F1A9"}.mdi-cup::before{content:"\F1AA"}.mdi-cup-off::before{content:"\F5E5"}.mdi-cup-off-outline::before{content:"\F03A8"}.mdi-cup-outline::before{content:"\F033A"}.mdi-cup-water::before{content:"\F1AB"}.mdi-cupboard::before{content:"\FF63"}.mdi-cupboard-outline::before{content:"\FF64"}.mdi-cupcake::before{content:"\F959"}.mdi-curling::before{content:"\F862"}.mdi-currency-bdt::before{content:"\F863"}.mdi-currency-brl::before{content:"\FB61"}.mdi-currency-btc::before{content:"\F1AC"}.mdi-currency-cny::before{content:"\F7B9"}.mdi-currency-eth::before{content:"\F7BA"}.mdi-currency-eur::before{content:"\F1AD"}.mdi-currency-eur-off::before{content:"\F0340"}.mdi-currency-gbp::before{content:"\F1AE"}.mdi-currency-ils::before{content:"\FC3D"}.mdi-currency-inr::before{content:"\F1AF"}.mdi-currency-jpy::before{content:"\F7BB"}.mdi-currency-krw::before{content:"\F7BC"}.mdi-currency-kzt::before{content:"\F864"}.mdi-currency-ngn::before{content:"\F1B0"}.mdi-currency-php::before{content:"\F9E5"}.mdi-currency-rial::before{content:"\FEB9"}.mdi-currency-rub::before{content:"\F1B1"}.mdi-currency-sign::before{content:"\F7BD"}.mdi-currency-try::before{content:"\F1B2"}.mdi-currency-twd::before{content:"\F7BE"}.mdi-currency-usd::before{content:"\F1B3"}.mdi-currency-usd-off::before{content:"\F679"}.mdi-current-ac::before{content:"\F95A"}.mdi-current-dc::before{content:"\F95B"}.mdi-cursor-default::before{content:"\F1B4"}.mdi-cursor-default-click::before{content:"\FCD9"}.mdi-cursor-default-click-outline::before{content:"\FCDA"}.mdi-cursor-default-gesture::before{content:"\F0152"}.mdi-cursor-default-gesture-outline::before{content:"\F0153"}.mdi-cursor-default-outline::before{content:"\F1B5"}.mdi-cursor-move::before{content:"\F1B6"}.mdi-cursor-pointer::before{content:"\F1B7"}.mdi-cursor-text::before{content:"\F5E7"}.mdi-database::before{content:"\F1B8"}.mdi-database-check::before{content:"\FAA8"}.mdi-database-edit::before{content:"\FB62"}.mdi-database-export::before{content:"\F95D"}.mdi-database-import::before{content:"\F95C"}.mdi-database-lock::before{content:"\FAA9"}.mdi-database-marker::before{content:"\F0321"}.mdi-database-minus::before{content:"\F1B9"}.mdi-database-plus::before{content:"\F1BA"}.mdi-database-refresh::before{content:"\FCDB"}.mdi-database-remove::before{content:"\FCDC"}.mdi-database-search::before{content:"\F865"}.mdi-database-settings::before{content:"\FCDD"}.mdi-death-star::before{content:"\F8D7"}.mdi-death-star-variant::before{content:"\F8D8"}.mdi-deathly-hallows::before{content:"\FB63"}.mdi-debian::before{content:"\F8D9"}.mdi-debug-step-into::before{content:"\F1BB"}.mdi-debug-step-out::before{content:"\F1BC"}.mdi-debug-step-over::before{content:"\F1BD"}.mdi-decagram::before{content:"\F76B"}.mdi-decagram-outline::before{content:"\F76C"}.mdi-decimal::before{content:"\F00CC"}.mdi-decimal-comma::before{content:"\F00CD"}.mdi-decimal-comma-decrease::before{content:"\F00CE"}.mdi-decimal-comma-increase::before{content:"\F00CF"}.mdi-decimal-decrease::before{content:"\F1BE"}.mdi-decimal-increase::before{content:"\F1BF"}.mdi-delete::before{content:"\F1C0"}.mdi-delete-alert::before{content:"\F00D0"}.mdi-delete-alert-outline::before{content:"\F00D1"}.mdi-delete-circle::before{content:"\F682"}.mdi-delete-circle-outline::before{content:"\FB64"}.mdi-delete-empty::before{content:"\F6CB"}.mdi-delete-empty-outline::before{content:"\FEBA"}.mdi-delete-forever::before{content:"\F5E8"}.mdi-delete-forever-outline::before{content:"\FB65"}.mdi-delete-off::before{content:"\F00D2"}.mdi-delete-off-outline::before{content:"\F00D3"}.mdi-delete-outline::before{content:"\F9E6"}.mdi-delete-restore::before{content:"\F818"}.mdi-delete-sweep::before{content:"\F5E9"}.mdi-delete-sweep-outline::before{content:"\FC3E"}.mdi-delete-variant::before{content:"\F1C1"}.mdi-delta::before{content:"\F1C2"}.mdi-desk::before{content:"\F0264"}.mdi-desk-lamp::before{content:"\F95E"}.mdi-deskphone::before{content:"\F1C3"}.mdi-desktop-classic::before{content:"\F7BF"}.mdi-desktop-mac::before{content:"\F1C4"}.mdi-desktop-mac-dashboard::before{content:"\F9E7"}.mdi-desktop-tower::before{content:"\F1C5"}.mdi-desktop-tower-monitor::before{content:"\FAAA"}.mdi-details::before{content:"\F1C6"}.mdi-dev-to::before{content:"\FD4A"}.mdi-developer-board::before{content:"\F696"}.mdi-deviantart::before{content:"\F1C7"}.mdi-devices::before{content:"\FFD0"}.mdi-diabetes::before{content:"\F0151"}.mdi-dialpad::before{content:"\F61C"}.mdi-diameter::before{content:"\FC3F"}.mdi-diameter-outline::before{content:"\FC40"}.mdi-diameter-variant::before{content:"\FC41"}.mdi-diamond::before{content:"\FB66"}.mdi-diamond-outline::before{content:"\FB67"}.mdi-diamond-stone::before{content:"\F1C8"}.mdi-dice-1::before{content:"\F1CA"}.mdi-dice-1-outline::before{content:"\F0175"}.mdi-dice-2::before{content:"\F1CB"}.mdi-dice-2-outline::before{content:"\F0176"}.mdi-dice-3::before{content:"\F1CC"}.mdi-dice-3-outline::before{content:"\F0177"}.mdi-dice-4::before{content:"\F1CD"}.mdi-dice-4-outline::before{content:"\F0178"}.mdi-dice-5::before{content:"\F1CE"}.mdi-dice-5-outline::before{content:"\F0179"}.mdi-dice-6::before{content:"\F1CF"}.mdi-dice-6-outline::before{content:"\F017A"}.mdi-dice-d10::before{content:"\F017E"}.mdi-dice-d10-outline::before{content:"\F76E"}.mdi-dice-d12::before{content:"\F017F"}.mdi-dice-d12-outline::before{content:"\F866"}.mdi-dice-d20::before{content:"\F0180"}.mdi-dice-d20-outline::before{content:"\F5EA"}.mdi-dice-d4::before{content:"\F017B"}.mdi-dice-d4-outline::before{content:"\F5EB"}.mdi-dice-d6::before{content:"\F017C"}.mdi-dice-d6-outline::before{content:"\F5EC"}.mdi-dice-d8::before{content:"\F017D"}.mdi-dice-d8-outline::before{content:"\F5ED"}.mdi-dice-multiple::before{content:"\F76D"}.mdi-dice-multiple-outline::before{content:"\F0181"}.mdi-dictionary::before{content:"\F61D"}.mdi-digital-ocean::before{content:"\F0262"}.mdi-dip-switch::before{content:"\F7C0"}.mdi-directions::before{content:"\F1D0"}.mdi-directions-fork::before{content:"\F641"}.mdi-disc::before{content:"\F5EE"}.mdi-disc-alert::before{content:"\F1D1"}.mdi-disc-player::before{content:"\F95F"}.mdi-discord::before{content:"\F66F"}.mdi-dishwasher::before{content:"\FAAB"}.mdi-dishwasher-alert::before{content:"\F01E3"}.mdi-dishwasher-off::before{content:"\F01E4"}.mdi-disqus::before{content:"\F1D2"}.mdi-disqus-outline::before{content:"\F1D3"}.mdi-distribute-horizontal-center::before{content:"\F01F4"}.mdi-distribute-horizontal-left::before{content:"\F01F3"}.mdi-distribute-horizontal-right::before{content:"\F01F5"}.mdi-distribute-vertical-bottom::before{content:"\F01F6"}.mdi-distribute-vertical-center::before{content:"\F01F7"}.mdi-distribute-vertical-top::before{content:"\F01F8"}.mdi-diving-flippers::before{content:"\FD9B"}.mdi-diving-helmet::before{content:"\FD9C"}.mdi-diving-scuba::before{content:"\FD9D"}.mdi-diving-scuba-flag::before{content:"\FD9E"}.mdi-diving-scuba-tank::before{content:"\FD9F"}.mdi-diving-scuba-tank-multiple::before{content:"\FDA0"}.mdi-diving-snorkel::before{content:"\FDA1"}.mdi-division::before{content:"\F1D4"}.mdi-division-box::before{content:"\F1D5"}.mdi-dlna::before{content:"\FA40"}.mdi-dna::before{content:"\F683"}.mdi-dns::before{content:"\F1D6"}.mdi-dns-outline::before{content:"\FB68"}.mdi-do-not-disturb::before{content:"\F697"}.mdi-do-not-disturb-off::before{content:"\F698"}.mdi-dock-bottom::before{content:"\F00D4"}.mdi-dock-left::before{content:"\F00D5"}.mdi-dock-right::before{content:"\F00D6"}.mdi-dock-window::before{content:"\F00D7"}.mdi-docker::before{content:"\F867"}.mdi-doctor::before{content:"\FA41"}.mdi-dog::before{content:"\FA42"}.mdi-dog-service::before{content:"\FAAC"}.mdi-dog-side::before{content:"\FA43"}.mdi-dolby::before{content:"\F6B2"}.mdi-dolly::before{content:"\FEBB"}.mdi-domain::before{content:"\F1D7"}.mdi-domain-off::before{content:"\FD4B"}.mdi-domain-plus::before{content:"\F00D8"}.mdi-domain-remove::before{content:"\F00D9"}.mdi-domino-mask::before{content:"\F0045"}.mdi-donkey::before{content:"\F7C1"}.mdi-door::before{content:"\F819"}.mdi-door-closed::before{content:"\F81A"}.mdi-door-closed-lock::before{content:"\F00DA"}.mdi-door-open::before{content:"\F81B"}.mdi-doorbell::before{content:"\F0311"}.mdi-doorbell-video::before{content:"\F868"}.mdi-dot-net::before{content:"\FAAD"}.mdi-dots-horizontal::before{content:"\F1D8"}.mdi-dots-horizontal-circle::before{content:"\F7C2"}.mdi-dots-horizontal-circle-outline::before{content:"\FB69"}.mdi-dots-vertical::before{content:"\F1D9"}.mdi-dots-vertical-circle::before{content:"\F7C3"}.mdi-dots-vertical-circle-outline::before{content:"\FB6A"}.mdi-douban::before{content:"\F699"}.mdi-download::before{content:"\F1DA"}.mdi-download-lock::before{content:"\F034B"}.mdi-download-lock-outline::before{content:"\F034C"}.mdi-download-multiple::before{content:"\F9E8"}.mdi-download-network::before{content:"\F6F3"}.mdi-download-network-outline::before{content:"\FC42"}.mdi-download-off::before{content:"\F00DB"}.mdi-download-off-outline::before{content:"\F00DC"}.mdi-download-outline::before{content:"\FB6B"}.mdi-drag::before{content:"\F1DB"}.mdi-drag-horizontal::before{content:"\F1DC"}.mdi-drag-horizontal-variant::before{content:"\F031B"}.mdi-drag-variant::before{content:"\FB6C"}.mdi-drag-vertical::before{content:"\F1DD"}.mdi-drag-vertical-variant::before{content:"\F031C"}.mdi-drama-masks::before{content:"\FCDE"}.mdi-draw::before{content:"\FF66"}.mdi-drawing::before{content:"\F1DE"}.mdi-drawing-box::before{content:"\F1DF"}.mdi-dresser::before{content:"\FF67"}.mdi-dresser-outline::before{content:"\FF68"}.mdi-dribbble::before{content:"\F1E0"}.mdi-dribbble-box::before{content:"\F1E1"}.mdi-drone::before{content:"\F1E2"}.mdi-dropbox::before{content:"\F1E3"}.mdi-drupal::before{content:"\F1E4"}.mdi-duck::before{content:"\F1E5"}.mdi-dumbbell::before{content:"\F1E6"}.mdi-dump-truck::before{content:"\FC43"}.mdi-ear-hearing::before{content:"\F7C4"}.mdi-ear-hearing-off::before{content:"\FA44"}.mdi-earth::before{content:"\F1E7"}.mdi-earth-arrow-right::before{content:"\F033C"}.mdi-earth-box::before{content:"\F6CC"}.mdi-earth-box-off::before{content:"\F6CD"}.mdi-earth-off::before{content:"\F1E8"}.mdi-edge::before{content:"\F1E9"}.mdi-edge-legacy::before{content:"\F027B"}.mdi-egg::before{content:"\FAAE"}.mdi-egg-easter::before{content:"\FAAF"}.mdi-eight-track::before{content:"\F9E9"}.mdi-eject::before{content:"\F1EA"}.mdi-eject-outline::before{content:"\FB6D"}.mdi-electric-switch::before{content:"\FEBC"}.mdi-electric-switch-closed::before{content:"\F0104"}.mdi-electron-framework::before{content:"\F0046"}.mdi-elephant::before{content:"\F7C5"}.mdi-elevation-decline::before{content:"\F1EB"}.mdi-elevation-rise::before{content:"\F1EC"}.mdi-elevator::before{content:"\F1ED"}.mdi-elevator-down::before{content:"\F02ED"}.mdi-elevator-passenger::before{content:"\F03AC"}.mdi-elevator-up::before{content:"\F02EC"}.mdi-ellipse::before{content:"\FEBD"}.mdi-ellipse-outline::before{content:"\FEBE"}.mdi-email::before{content:"\F1EE"}.mdi-email-alert::before{content:"\F6CE"}.mdi-email-alert-outline::before{content:"\FD1E"}.mdi-email-box::before{content:"\FCDF"}.mdi-email-check::before{content:"\FAB0"}.mdi-email-check-outline::before{content:"\FAB1"}.mdi-email-edit::before{content:"\FF00"}.mdi-email-edit-outline::before{content:"\FF01"}.mdi-email-lock::before{content:"\F1F1"}.mdi-email-mark-as-unread::before{content:"\FB6E"}.mdi-email-minus::before{content:"\FF02"}.mdi-email-minus-outline::before{content:"\FF03"}.mdi-email-multiple::before{content:"\FF04"}.mdi-email-multiple-outline::before{content:"\FF05"}.mdi-email-newsletter::before{content:"\FFD1"}.mdi-email-open::before{content:"\F1EF"}.mdi-email-open-multiple::before{content:"\FF06"}.mdi-email-open-multiple-outline::before{content:"\FF07"}.mdi-email-open-outline::before{content:"\F5EF"}.mdi-email-outline::before{content:"\F1F0"}.mdi-email-plus::before{content:"\F9EA"}.mdi-email-plus-outline::before{content:"\F9EB"}.mdi-email-receive::before{content:"\F0105"}.mdi-email-receive-outline::before{content:"\F0106"}.mdi-email-search::before{content:"\F960"}.mdi-email-search-outline::before{content:"\F961"}.mdi-email-send::before{content:"\F0107"}.mdi-email-send-outline::before{content:"\F0108"}.mdi-email-sync::before{content:"\F02F2"}.mdi-email-sync-outline::before{content:"\F02F3"}.mdi-email-variant::before{content:"\F5F0"}.mdi-ember::before{content:"\FB15"}.mdi-emby::before{content:"\F6B3"}.mdi-emoticon::before{content:"\FC44"}.mdi-emoticon-angry::before{content:"\FC45"}.mdi-emoticon-angry-outline::before{content:"\FC46"}.mdi-emoticon-confused::before{content:"\F0109"}.mdi-emoticon-confused-outline::before{content:"\F010A"}.mdi-emoticon-cool::before{content:"\FC47"}.mdi-emoticon-cool-outline::before{content:"\F1F3"}.mdi-emoticon-cry::before{content:"\FC48"}.mdi-emoticon-cry-outline::before{content:"\FC49"}.mdi-emoticon-dead::before{content:"\FC4A"}.mdi-emoticon-dead-outline::before{content:"\F69A"}.mdi-emoticon-devil::before{content:"\FC4B"}.mdi-emoticon-devil-outline::before{content:"\F1F4"}.mdi-emoticon-excited::before{content:"\FC4C"}.mdi-emoticon-excited-outline::before{content:"\F69B"}.mdi-emoticon-frown::before{content:"\FF69"}.mdi-emoticon-frown-outline::before{content:"\FF6A"}.mdi-emoticon-happy::before{content:"\FC4D"}.mdi-emoticon-happy-outline::before{content:"\F1F5"}.mdi-emoticon-kiss::before{content:"\FC4E"}.mdi-emoticon-kiss-outline::before{content:"\FC4F"}.mdi-emoticon-lol::before{content:"\F023F"}.mdi-emoticon-lol-outline::before{content:"\F0240"}.mdi-emoticon-neutral::before{content:"\FC50"}.mdi-emoticon-neutral-outline::before{content:"\F1F6"}.mdi-emoticon-outline::before{content:"\F1F2"}.mdi-emoticon-poop::before{content:"\F1F7"}.mdi-emoticon-poop-outline::before{content:"\FC51"}.mdi-emoticon-sad::before{content:"\FC52"}.mdi-emoticon-sad-outline::before{content:"\F1F8"}.mdi-emoticon-tongue::before{content:"\F1F9"}.mdi-emoticon-tongue-outline::before{content:"\FC53"}.mdi-emoticon-wink::before{content:"\FC54"}.mdi-emoticon-wink-outline::before{content:"\FC55"}.mdi-engine::before{content:"\F1FA"}.mdi-engine-off::before{content:"\FA45"}.mdi-engine-off-outline::before{content:"\FA46"}.mdi-engine-outline::before{content:"\F1FB"}.mdi-epsilon::before{content:"\F010B"}.mdi-equal::before{content:"\F1FC"}.mdi-equal-box::before{content:"\F1FD"}.mdi-equalizer::before{content:"\FEBF"}.mdi-equalizer-outline::before{content:"\FEC0"}.mdi-eraser::before{content:"\F1FE"}.mdi-eraser-variant::before{content:"\F642"}.mdi-escalator::before{content:"\F1FF"}.mdi-escalator-down::before{content:"\F02EB"}.mdi-escalator-up::before{content:"\F02EA"}.mdi-eslint::before{content:"\FC56"}.mdi-et::before{content:"\FAB2"}.mdi-ethereum::before{content:"\F869"}.mdi-ethernet::before{content:"\F200"}.mdi-ethernet-cable::before{content:"\F201"}.mdi-ethernet-cable-off::before{content:"\F202"}.mdi-etsy::before{content:"\F203"}.mdi-ev-station::before{content:"\F5F1"}.mdi-eventbrite::before{content:"\F7C6"}.mdi-evernote::before{content:"\F204"}.mdi-excavator::before{content:"\F0047"}.mdi-exclamation::before{content:"\F205"}.mdi-exclamation-thick::before{content:"\F0263"}.mdi-exit-run::before{content:"\FA47"}.mdi-exit-to-app::before{content:"\F206"}.mdi-expand-all::before{content:"\FAB3"}.mdi-expand-all-outline::before{content:"\FAB4"}.mdi-expansion-card::before{content:"\F8AD"}.mdi-expansion-card-variant::before{content:"\FFD2"}.mdi-exponent::before{content:"\F962"}.mdi-exponent-box::before{content:"\F963"}.mdi-export::before{content:"\F207"}.mdi-export-variant::before{content:"\FB6F"}.mdi-eye::before{content:"\F208"}.mdi-eye-check::before{content:"\FCE0"}.mdi-eye-check-outline::before{content:"\FCE1"}.mdi-eye-circle::before{content:"\FB70"}.mdi-eye-circle-outline::before{content:"\FB71"}.mdi-eye-minus::before{content:"\F0048"}.mdi-eye-minus-outline::before{content:"\F0049"}.mdi-eye-off::before{content:"\F209"}.mdi-eye-off-outline::before{content:"\F6D0"}.mdi-eye-outline::before{content:"\F6CF"}.mdi-eye-plus::before{content:"\F86A"}.mdi-eye-plus-outline::before{content:"\F86B"}.mdi-eye-settings::before{content:"\F86C"}.mdi-eye-settings-outline::before{content:"\F86D"}.mdi-eyedropper::before{content:"\F20A"}.mdi-eyedropper-variant::before{content:"\F20B"}.mdi-face::before{content:"\F643"}.mdi-face-agent::before{content:"\FD4C"}.mdi-face-outline::before{content:"\FB72"}.mdi-face-profile::before{content:"\F644"}.mdi-face-profile-woman::before{content:"\F00A1"}.mdi-face-recognition::before{content:"\FC57"}.mdi-face-woman::before{content:"\F00A2"}.mdi-face-woman-outline::before{content:"\F00A3"}.mdi-facebook::before{content:"\F20C"}.mdi-facebook-box::before{content:"\F20D"}.mdi-facebook-messenger::before{content:"\F20E"}.mdi-facebook-workplace::before{content:"\FB16"}.mdi-factory::before{content:"\F20F"}.mdi-fan::before{content:"\F210"}.mdi-fan-off::before{content:"\F81C"}.mdi-fast-forward::before{content:"\F211"}.mdi-fast-forward-10::before{content:"\FD4D"}.mdi-fast-forward-30::before{content:"\FCE2"}.mdi-fast-forward-5::before{content:"\F0223"}.mdi-fast-forward-outline::before{content:"\F6D1"}.mdi-fax::before{content:"\F212"}.mdi-feather::before{content:"\F6D2"}.mdi-feature-search::before{content:"\FA48"}.mdi-feature-search-outline::before{content:"\FA49"}.mdi-fedora::before{content:"\F8DA"}.mdi-ferris-wheel::before{content:"\FEC1"}.mdi-ferry::before{content:"\F213"}.mdi-file::before{content:"\F214"}.mdi-file-account::before{content:"\F73A"}.mdi-file-account-outline::before{content:"\F004A"}.mdi-file-alert::before{content:"\FA4A"}.mdi-file-alert-outline::before{content:"\FA4B"}.mdi-file-cabinet::before{content:"\FAB5"}.mdi-file-cad::before{content:"\FF08"}.mdi-file-cad-box::before{content:"\FF09"}.mdi-file-cancel::before{content:"\FDA2"}.mdi-file-cancel-outline::before{content:"\FDA3"}.mdi-file-certificate::before{content:"\F01B1"}.mdi-file-certificate-outline::before{content:"\F01B2"}.mdi-file-chart::before{content:"\F215"}.mdi-file-chart-outline::before{content:"\F004B"}.mdi-file-check::before{content:"\F216"}.mdi-file-check-outline::before{content:"\FE7B"}.mdi-file-clock::before{content:"\F030C"}.mdi-file-clock-outline::before{content:"\F030D"}.mdi-file-cloud::before{content:"\F217"}.mdi-file-cloud-outline::before{content:"\F004C"}.mdi-file-code::before{content:"\F22E"}.mdi-file-code-outline::before{content:"\F004D"}.mdi-file-compare::before{content:"\F8A9"}.mdi-file-delimited::before{content:"\F218"}.mdi-file-delimited-outline::before{content:"\FEC2"}.mdi-file-document::before{content:"\F219"}.mdi-file-document-box::before{content:"\F21A"}.mdi-file-document-box-check::before{content:"\FEC3"}.mdi-file-document-box-check-outline::before{content:"\FEC4"}.mdi-file-document-box-minus::before{content:"\FEC5"}.mdi-file-document-box-minus-outline::before{content:"\FEC6"}.mdi-file-document-box-multiple::before{content:"\FAB6"}.mdi-file-document-box-multiple-outline::before{content:"\FAB7"}.mdi-file-document-box-outline::before{content:"\F9EC"}.mdi-file-document-box-plus::before{content:"\FEC7"}.mdi-file-document-box-plus-outline::before{content:"\FEC8"}.mdi-file-document-box-remove::before{content:"\FEC9"}.mdi-file-document-box-remove-outline::before{content:"\FECA"}.mdi-file-document-box-search::before{content:"\FECB"}.mdi-file-document-box-search-outline::before{content:"\FECC"}.mdi-file-document-edit::before{content:"\FDA4"}.mdi-file-document-edit-outline::before{content:"\FDA5"}.mdi-file-document-outline::before{content:"\F9ED"}.mdi-file-download::before{content:"\F964"}.mdi-file-download-outline::before{content:"\F965"}.mdi-file-edit::before{content:"\F0212"}.mdi-file-edit-outline::before{content:"\F0213"}.mdi-file-excel::before{content:"\F21B"}.mdi-file-excel-box::before{content:"\F21C"}.mdi-file-excel-box-outline::before{content:"\F004E"}.mdi-file-excel-outline::before{content:"\F004F"}.mdi-file-export::before{content:"\F21D"}.mdi-file-export-outline::before{content:"\F0050"}.mdi-file-eye::before{content:"\FDA6"}.mdi-file-eye-outline::before{content:"\FDA7"}.mdi-file-find::before{content:"\F21E"}.mdi-file-find-outline::before{content:"\FB73"}.mdi-file-hidden::before{content:"\F613"}.mdi-file-image::before{content:"\F21F"}.mdi-file-image-outline::before{content:"\FECD"}.mdi-file-import::before{content:"\F220"}.mdi-file-import-outline::before{content:"\F0051"}.mdi-file-key::before{content:"\F01AF"}.mdi-file-key-outline::before{content:"\F01B0"}.mdi-file-link::before{content:"\F01A2"}.mdi-file-link-outline::before{content:"\F01A3"}.mdi-file-lock::before{content:"\F221"}.mdi-file-lock-outline::before{content:"\F0052"}.mdi-file-move::before{content:"\FAB8"}.mdi-file-move-outline::before{content:"\F0053"}.mdi-file-multiple::before{content:"\F222"}.mdi-file-multiple-outline::before{content:"\F0054"}.mdi-file-music::before{content:"\F223"}.mdi-file-music-outline::before{content:"\FE7C"}.mdi-file-outline::before{content:"\F224"}.mdi-file-pdf::before{content:"\F225"}.mdi-file-pdf-box::before{content:"\F226"}.mdi-file-pdf-box-outline::before{content:"\FFD3"}.mdi-file-pdf-outline::before{content:"\FE7D"}.mdi-file-percent::before{content:"\F81D"}.mdi-file-percent-outline::before{content:"\F0055"}.mdi-file-phone::before{content:"\F01A4"}.mdi-file-phone-outline::before{content:"\F01A5"}.mdi-file-plus::before{content:"\F751"}.mdi-file-plus-outline::before{content:"\FF0A"}.mdi-file-powerpoint::before{content:"\F227"}.mdi-file-powerpoint-box::before{content:"\F228"}.mdi-file-powerpoint-box-outline::before{content:"\F0056"}.mdi-file-powerpoint-outline::before{content:"\F0057"}.mdi-file-presentation-box::before{content:"\F229"}.mdi-file-question::before{content:"\F86E"}.mdi-file-question-outline::before{content:"\F0058"}.mdi-file-remove::before{content:"\FB74"}.mdi-file-remove-outline::before{content:"\F0059"}.mdi-file-replace::before{content:"\FB17"}.mdi-file-replace-outline::before{content:"\FB18"}.mdi-file-restore::before{content:"\F670"}.mdi-file-restore-outline::before{content:"\F005A"}.mdi-file-search::before{content:"\FC58"}.mdi-file-search-outline::before{content:"\FC59"}.mdi-file-send::before{content:"\F22A"}.mdi-file-send-outline::before{content:"\F005B"}.mdi-file-settings::before{content:"\F00A4"}.mdi-file-settings-outline::before{content:"\F00A5"}.mdi-file-settings-variant::before{content:"\F00A6"}.mdi-file-settings-variant-outline::before{content:"\F00A7"}.mdi-file-star::before{content:"\F005C"}.mdi-file-star-outline::before{content:"\F005D"}.mdi-file-swap::before{content:"\FFD4"}.mdi-file-swap-outline::before{content:"\FFD5"}.mdi-file-sync::before{content:"\F0241"}.mdi-file-sync-outline::before{content:"\F0242"}.mdi-file-table::before{content:"\FC5A"}.mdi-file-table-box::before{content:"\F010C"}.mdi-file-table-box-multiple::before{content:"\F010D"}.mdi-file-table-box-multiple-outline::before{content:"\F010E"}.mdi-file-table-box-outline::before{content:"\F010F"}.mdi-file-table-outline::before{content:"\FC5B"}.mdi-file-tree::before{content:"\F645"}.mdi-file-undo::before{content:"\F8DB"}.mdi-file-undo-outline::before{content:"\F005E"}.mdi-file-upload::before{content:"\FA4C"}.mdi-file-upload-outline::before{content:"\FA4D"}.mdi-file-video::before{content:"\F22B"}.mdi-file-video-outline::before{content:"\FE10"}.mdi-file-word::before{content:"\F22C"}.mdi-file-word-box::before{content:"\F22D"}.mdi-file-word-box-outline::before{content:"\F005F"}.mdi-file-word-outline::before{content:"\F0060"}.mdi-film::before{content:"\F22F"}.mdi-filmstrip::before{content:"\F230"}.mdi-filmstrip-off::before{content:"\F231"}.mdi-filter::before{content:"\F232"}.mdi-filter-menu::before{content:"\F0110"}.mdi-filter-menu-outline::before{content:"\F0111"}.mdi-filter-minus::before{content:"\FF0B"}.mdi-filter-minus-outline::before{content:"\FF0C"}.mdi-filter-outline::before{content:"\F233"}.mdi-filter-plus::before{content:"\FF0D"}.mdi-filter-plus-outline::before{content:"\FF0E"}.mdi-filter-remove::before{content:"\F234"}.mdi-filter-remove-outline::before{content:"\F235"}.mdi-filter-variant::before{content:"\F236"}.mdi-filter-variant-minus::before{content:"\F013D"}.mdi-filter-variant-plus::before{content:"\F013E"}.mdi-filter-variant-remove::before{content:"\F0061"}.mdi-finance::before{content:"\F81E"}.mdi-find-replace::before{content:"\F6D3"}.mdi-fingerprint::before{content:"\F237"}.mdi-fingerprint-off::before{content:"\FECE"}.mdi-fire::before{content:"\F238"}.mdi-fire-extinguisher::before{content:"\FF0F"}.mdi-fire-hydrant::before{content:"\F0162"}.mdi-fire-hydrant-alert::before{content:"\F0163"}.mdi-fire-hydrant-off::before{content:"\F0164"}.mdi-fire-truck::before{content:"\F8AA"}.mdi-firebase::before{content:"\F966"}.mdi-firefox::before{content:"\F239"}.mdi-fireplace::before{content:"\FE11"}.mdi-fireplace-off::before{content:"\FE12"}.mdi-firework::before{content:"\FE13"}.mdi-fish::before{content:"\F23A"}.mdi-fishbowl::before{content:"\FF10"}.mdi-fishbowl-outline::before{content:"\FF11"}.mdi-fit-to-page::before{content:"\FF12"}.mdi-fit-to-page-outline::before{content:"\FF13"}.mdi-flag::before{content:"\F23B"}.mdi-flag-checkered::before{content:"\F23C"}.mdi-flag-minus::before{content:"\FB75"}.mdi-flag-minus-outline::before{content:"\F00DD"}.mdi-flag-outline::before{content:"\F23D"}.mdi-flag-plus::before{content:"\FB76"}.mdi-flag-plus-outline::before{content:"\F00DE"}.mdi-flag-remove::before{content:"\FB77"}.mdi-flag-remove-outline::before{content:"\F00DF"}.mdi-flag-triangle::before{content:"\F23F"}.mdi-flag-variant::before{content:"\F240"}.mdi-flag-variant-outline::before{content:"\F23E"}.mdi-flare::before{content:"\FD4E"}.mdi-flash::before{content:"\F241"}.mdi-flash-alert::before{content:"\FF14"}.mdi-flash-alert-outline::before{content:"\FF15"}.mdi-flash-auto::before{content:"\F242"}.mdi-flash-circle::before{content:"\F81F"}.mdi-flash-off::before{content:"\F243"}.mdi-flash-outline::before{content:"\F6D4"}.mdi-flash-red-eye::before{content:"\F67A"}.mdi-flashlight::before{content:"\F244"}.mdi-flashlight-off::before{content:"\F245"}.mdi-flask::before{content:"\F093"}.mdi-flask-empty::before{content:"\F094"}.mdi-flask-empty-minus::before{content:"\F0265"}.mdi-flask-empty-minus-outline::before{content:"\F0266"}.mdi-flask-empty-outline::before{content:"\F095"}.mdi-flask-empty-plus::before{content:"\F0267"}.mdi-flask-empty-plus-outline::before{content:"\F0268"}.mdi-flask-empty-remove::before{content:"\F0269"}.mdi-flask-empty-remove-outline::before{content:"\F026A"}.mdi-flask-minus::before{content:"\F026B"}.mdi-flask-minus-outline::before{content:"\F026C"}.mdi-flask-outline::before{content:"\F096"}.mdi-flask-plus::before{content:"\F026D"}.mdi-flask-plus-outline::before{content:"\F026E"}.mdi-flask-remove::before{content:"\F026F"}.mdi-flask-remove-outline::before{content:"\F0270"}.mdi-flask-round-bottom::before{content:"\F0276"}.mdi-flask-round-bottom-empty::before{content:"\F0277"}.mdi-flask-round-bottom-empty-outline::before{content:"\F0278"}.mdi-flask-round-bottom-outline::before{content:"\F0279"}.mdi-flattr::before{content:"\F246"}.mdi-fleur-de-lis::before{content:"\F032E"}.mdi-flickr::before{content:"\FCE3"}.mdi-flip-horizontal::before{content:"\F0112"}.mdi-flip-to-back::before{content:"\F247"}.mdi-flip-to-front::before{content:"\F248"}.mdi-flip-vertical::before{content:"\F0113"}.mdi-floor-lamp::before{content:"\F8DC"}.mdi-floor-lamp-dual::before{content:"\F0062"}.mdi-floor-lamp-variant::before{content:"\F0063"}.mdi-floor-plan::before{content:"\F820"}.mdi-floppy::before{content:"\F249"}.mdi-floppy-variant::before{content:"\F9EE"}.mdi-flower::before{content:"\F24A"}.mdi-flower-outline::before{content:"\F9EF"}.mdi-flower-poppy::before{content:"\FCE4"}.mdi-flower-tulip::before{content:"\F9F0"}.mdi-flower-tulip-outline::before{content:"\F9F1"}.mdi-focus-auto::before{content:"\FF6B"}.mdi-focus-field::before{content:"\FF6C"}.mdi-focus-field-horizontal::before{content:"\FF6D"}.mdi-focus-field-vertical::before{content:"\FF6E"}.mdi-folder::before{content:"\F24B"}.mdi-folder-account::before{content:"\F24C"}.mdi-folder-account-outline::before{content:"\FB78"}.mdi-folder-alert::before{content:"\FDA8"}.mdi-folder-alert-outline::before{content:"\FDA9"}.mdi-folder-clock::before{content:"\FAB9"}.mdi-folder-clock-outline::before{content:"\FABA"}.mdi-folder-download::before{content:"\F24D"}.mdi-folder-download-outline::before{content:"\F0114"}.mdi-folder-edit::before{content:"\F8DD"}.mdi-folder-edit-outline::before{content:"\FDAA"}.mdi-folder-google-drive::before{content:"\F24E"}.mdi-folder-heart::before{content:"\F0115"}.mdi-folder-heart-outline::before{content:"\F0116"}.mdi-folder-home::before{content:"\F00E0"}.mdi-folder-home-outline::before{content:"\F00E1"}.mdi-folder-image::before{content:"\F24F"}.mdi-folder-information::before{content:"\F00E2"}.mdi-folder-information-outline::before{content:"\F00E3"}.mdi-folder-key::before{content:"\F8AB"}.mdi-folder-key-network::before{content:"\F8AC"}.mdi-folder-key-network-outline::before{content:"\FC5C"}.mdi-folder-key-outline::before{content:"\F0117"}.mdi-folder-lock::before{content:"\F250"}.mdi-folder-lock-open::before{content:"\F251"}.mdi-folder-marker::before{content:"\F0298"}.mdi-folder-marker-outline::before{content:"\F0299"}.mdi-folder-move::before{content:"\F252"}.mdi-folder-move-outline::before{content:"\F0271"}.mdi-folder-multiple::before{content:"\F253"}.mdi-folder-multiple-image::before{content:"\F254"}.mdi-folder-multiple-outline::before{content:"\F255"}.mdi-folder-music::before{content:"\F0384"}.mdi-folder-music-outline::before{content:"\F0385"}.mdi-folder-network::before{content:"\F86F"}.mdi-folder-network-outline::before{content:"\FC5D"}.mdi-folder-open::before{content:"\F76F"}.mdi-folder-open-outline::before{content:"\FDAB"}.mdi-folder-outline::before{content:"\F256"}.mdi-folder-plus::before{content:"\F257"}.mdi-folder-plus-outline::before{content:"\FB79"}.mdi-folder-pound::before{content:"\FCE5"}.mdi-folder-pound-outline::before{content:"\FCE6"}.mdi-folder-remove::before{content:"\F258"}.mdi-folder-remove-outline::before{content:"\FB7A"}.mdi-folder-search::before{content:"\F967"}.mdi-folder-search-outline::before{content:"\F968"}.mdi-folder-settings::before{content:"\F00A8"}.mdi-folder-settings-outline::before{content:"\F00A9"}.mdi-folder-settings-variant::before{content:"\F00AA"}.mdi-folder-settings-variant-outline::before{content:"\F00AB"}.mdi-folder-star::before{content:"\F69C"}.mdi-folder-star-outline::before{content:"\FB7B"}.mdi-folder-swap::before{content:"\FFD6"}.mdi-folder-swap-outline::before{content:"\FFD7"}.mdi-folder-sync::before{content:"\FCE7"}.mdi-folder-sync-outline::before{content:"\FCE8"}.mdi-folder-table::before{content:"\F030E"}.mdi-folder-table-outline::before{content:"\F030F"}.mdi-folder-text::before{content:"\FC5E"}.mdi-folder-text-outline::before{content:"\FC5F"}.mdi-folder-upload::before{content:"\F259"}.mdi-folder-upload-outline::before{content:"\F0118"}.mdi-folder-zip::before{content:"\F6EA"}.mdi-folder-zip-outline::before{content:"\F7B8"}.mdi-font-awesome::before{content:"\F03A"}.mdi-food::before{content:"\F25A"}.mdi-food-apple::before{content:"\F25B"}.mdi-food-apple-outline::before{content:"\FC60"}.mdi-food-croissant::before{content:"\F7C7"}.mdi-food-fork-drink::before{content:"\F5F2"}.mdi-food-off::before{content:"\F5F3"}.mdi-food-variant::before{content:"\F25C"}.mdi-foot-print::before{content:"\FF6F"}.mdi-football::before{content:"\F25D"}.mdi-football-australian::before{content:"\F25E"}.mdi-football-helmet::before{content:"\F25F"}.mdi-forklift::before{content:"\F7C8"}.mdi-format-align-bottom::before{content:"\F752"}.mdi-format-align-center::before{content:"\F260"}.mdi-format-align-justify::before{content:"\F261"}.mdi-format-align-left::before{content:"\F262"}.mdi-format-align-middle::before{content:"\F753"}.mdi-format-align-right::before{content:"\F263"}.mdi-format-align-top::before{content:"\F754"}.mdi-format-annotation-minus::before{content:"\FABB"}.mdi-format-annotation-plus::before{content:"\F646"}.mdi-format-bold::before{content:"\F264"}.mdi-format-clear::before{content:"\F265"}.mdi-format-color-fill::before{content:"\F266"}.mdi-format-color-highlight::before{content:"\FE14"}.mdi-format-color-marker-cancel::before{content:"\F033E"}.mdi-format-color-text::before{content:"\F69D"}.mdi-format-columns::before{content:"\F8DE"}.mdi-format-float-center::before{content:"\F267"}.mdi-format-float-left::before{content:"\F268"}.mdi-format-float-none::before{content:"\F269"}.mdi-format-float-right::before{content:"\F26A"}.mdi-format-font::before{content:"\F6D5"}.mdi-format-font-size-decrease::before{content:"\F9F2"}.mdi-format-font-size-increase::before{content:"\F9F3"}.mdi-format-header-1::before{content:"\F26B"}.mdi-format-header-2::before{content:"\F26C"}.mdi-format-header-3::before{content:"\F26D"}.mdi-format-header-4::before{content:"\F26E"}.mdi-format-header-5::before{content:"\F26F"}.mdi-format-header-6::before{content:"\F270"}.mdi-format-header-decrease::before{content:"\F271"}.mdi-format-header-equal::before{content:"\F272"}.mdi-format-header-increase::before{content:"\F273"}.mdi-format-header-pound::before{content:"\F274"}.mdi-format-horizontal-align-center::before{content:"\F61E"}.mdi-format-horizontal-align-left::before{content:"\F61F"}.mdi-format-horizontal-align-right::before{content:"\F620"}.mdi-format-indent-decrease::before{content:"\F275"}.mdi-format-indent-increase::before{content:"\F276"}.mdi-format-italic::before{content:"\F277"}.mdi-format-letter-case::before{content:"\FB19"}.mdi-format-letter-case-lower::before{content:"\FB1A"}.mdi-format-letter-case-upper::before{content:"\FB1B"}.mdi-format-letter-ends-with::before{content:"\FFD8"}.mdi-format-letter-matches::before{content:"\FFD9"}.mdi-format-letter-starts-with::before{content:"\FFDA"}.mdi-format-line-spacing::before{content:"\F278"}.mdi-format-line-style::before{content:"\F5C8"}.mdi-format-line-weight::before{content:"\F5C9"}.mdi-format-list-bulleted::before{content:"\F279"}.mdi-format-list-bulleted-square::before{content:"\FDAC"}.mdi-format-list-bulleted-triangle::before{content:"\FECF"}.mdi-format-list-bulleted-type::before{content:"\F27A"}.mdi-format-list-checkbox::before{content:"\F969"}.mdi-format-list-checks::before{content:"\F755"}.mdi-format-list-numbered::before{content:"\F27B"}.mdi-format-list-numbered-rtl::before{content:"\FCE9"}.mdi-format-list-text::before{content:"\F029A"}.mdi-format-overline::before{content:"\FED0"}.mdi-format-page-break::before{content:"\F6D6"}.mdi-format-paint::before{content:"\F27C"}.mdi-format-paragraph::before{content:"\F27D"}.mdi-format-pilcrow::before{content:"\F6D7"}.mdi-format-quote-close::before{content:"\F27E"}.mdi-format-quote-close-outline::before{content:"\F01D3"}.mdi-format-quote-open::before{content:"\F756"}.mdi-format-quote-open-outline::before{content:"\F01D2"}.mdi-format-rotate-90::before{content:"\F6A9"}.mdi-format-section::before{content:"\F69E"}.mdi-format-size::before{content:"\F27F"}.mdi-format-strikethrough::before{content:"\F280"}.mdi-format-strikethrough-variant::before{content:"\F281"}.mdi-format-subscript::before{content:"\F282"}.mdi-format-superscript::before{content:"\F283"}.mdi-format-text::before{content:"\F284"}.mdi-format-text-rotation-angle-down::before{content:"\FFDB"}.mdi-format-text-rotation-angle-up::before{content:"\FFDC"}.mdi-format-text-rotation-down::before{content:"\FD4F"}.mdi-format-text-rotation-down-vertical::before{content:"\FFDD"}.mdi-format-text-rotation-none::before{content:"\FD50"}.mdi-format-text-rotation-up::before{content:"\FFDE"}.mdi-format-text-rotation-vertical::before{content:"\FFDF"}.mdi-format-text-variant::before{content:"\FE15"}.mdi-format-text-wrapping-clip::before{content:"\FCEA"}.mdi-format-text-wrapping-overflow::before{content:"\FCEB"}.mdi-format-text-wrapping-wrap::before{content:"\FCEC"}.mdi-format-textbox::before{content:"\FCED"}.mdi-format-textdirection-l-to-r::before{content:"\F285"}.mdi-format-textdirection-r-to-l::before{content:"\F286"}.mdi-format-title::before{content:"\F5F4"}.mdi-format-underline::before{content:"\F287"}.mdi-format-vertical-align-bottom::before{content:"\F621"}.mdi-format-vertical-align-center::before{content:"\F622"}.mdi-format-vertical-align-top::before{content:"\F623"}.mdi-format-wrap-inline::before{content:"\F288"}.mdi-format-wrap-square::before{content:"\F289"}.mdi-format-wrap-tight::before{content:"\F28A"}.mdi-format-wrap-top-bottom::before{content:"\F28B"}.mdi-forum::before{content:"\F28C"}.mdi-forum-outline::before{content:"\F821"}.mdi-forward::before{content:"\F28D"}.mdi-forwardburger::before{content:"\FD51"}.mdi-fountain::before{content:"\F96A"}.mdi-fountain-pen::before{content:"\FCEE"}.mdi-fountain-pen-tip::before{content:"\FCEF"}.mdi-foursquare::before{content:"\F28E"}.mdi-freebsd::before{content:"\F8DF"}.mdi-frequently-asked-questions::before{content:"\FED1"}.mdi-fridge::before{content:"\F290"}.mdi-fridge-alert::before{content:"\F01DC"}.mdi-fridge-alert-outline::before{content:"\F01DD"}.mdi-fridge-bottom::before{content:"\F292"}.mdi-fridge-off::before{content:"\F01DA"}.mdi-fridge-off-outline::before{content:"\F01DB"}.mdi-fridge-outline::before{content:"\F28F"}.mdi-fridge-top::before{content:"\F291"}.mdi-fruit-cherries::before{content:"\F0064"}.mdi-fruit-citrus::before{content:"\F0065"}.mdi-fruit-grapes::before{content:"\F0066"}.mdi-fruit-grapes-outline::before{content:"\F0067"}.mdi-fruit-pineapple::before{content:"\F0068"}.mdi-fruit-watermelon::before{content:"\F0069"}.mdi-fuel::before{content:"\F7C9"}.mdi-fullscreen::before{content:"\F293"}.mdi-fullscreen-exit::before{content:"\F294"}.mdi-function::before{content:"\F295"}.mdi-function-variant::before{content:"\F870"}.mdi-furigana-horizontal::before{content:"\F00AC"}.mdi-furigana-vertical::before{content:"\F00AD"}.mdi-fuse::before{content:"\FC61"}.mdi-fuse-blade::before{content:"\FC62"}.mdi-gamepad::before{content:"\F296"}.mdi-gamepad-circle::before{content:"\FE16"}.mdi-gamepad-circle-down::before{content:"\FE17"}.mdi-gamepad-circle-left::before{content:"\FE18"}.mdi-gamepad-circle-outline::before{content:"\FE19"}.mdi-gamepad-circle-right::before{content:"\FE1A"}.mdi-gamepad-circle-up::before{content:"\FE1B"}.mdi-gamepad-down::before{content:"\FE1C"}.mdi-gamepad-left::before{content:"\FE1D"}.mdi-gamepad-right::before{content:"\FE1E"}.mdi-gamepad-round::before{content:"\FE1F"}.mdi-gamepad-round-down::before{content:"\FE7E"}.mdi-gamepad-round-left::before{content:"\FE7F"}.mdi-gamepad-round-outline::before{content:"\FE80"}.mdi-gamepad-round-right::before{content:"\FE81"}.mdi-gamepad-round-up::before{content:"\FE82"}.mdi-gamepad-square::before{content:"\FED2"}.mdi-gamepad-square-outline::before{content:"\FED3"}.mdi-gamepad-up::before{content:"\FE83"}.mdi-gamepad-variant::before{content:"\F297"}.mdi-gamepad-variant-outline::before{content:"\FED4"}.mdi-gamma::before{content:"\F0119"}.mdi-gantry-crane::before{content:"\FDAD"}.mdi-garage::before{content:"\F6D8"}.mdi-garage-alert::before{content:"\F871"}.mdi-garage-alert-variant::before{content:"\F0300"}.mdi-garage-open::before{content:"\F6D9"}.mdi-garage-open-variant::before{content:"\F02FF"}.mdi-garage-variant::before{content:"\F02FE"}.mdi-gas-cylinder::before{content:"\F647"}.mdi-gas-station::before{content:"\F298"}.mdi-gas-station-outline::before{content:"\FED5"}.mdi-gate::before{content:"\F299"}.mdi-gate-and::before{content:"\F8E0"}.mdi-gate-arrow-right::before{content:"\F0194"}.mdi-gate-nand::before{content:"\F8E1"}.mdi-gate-nor::before{content:"\F8E2"}.mdi-gate-not::before{content:"\F8E3"}.mdi-gate-open::before{content:"\F0195"}.mdi-gate-or::before{content:"\F8E4"}.mdi-gate-xnor::before{content:"\F8E5"}.mdi-gate-xor::before{content:"\F8E6"}.mdi-gatsby::before{content:"\FE84"}.mdi-gauge::before{content:"\F29A"}.mdi-gauge-empty::before{content:"\F872"}.mdi-gauge-full::before{content:"\F873"}.mdi-gauge-low::before{content:"\F874"}.mdi-gavel::before{content:"\F29B"}.mdi-gender-female::before{content:"\F29C"}.mdi-gender-male::before{content:"\F29D"}.mdi-gender-male-female::before{content:"\F29E"}.mdi-gender-male-female-variant::before{content:"\F016A"}.mdi-gender-non-binary::before{content:"\F016B"}.mdi-gender-transgender::before{content:"\F29F"}.mdi-gentoo::before{content:"\F8E7"}.mdi-gesture::before{content:"\F7CA"}.mdi-gesture-double-tap::before{content:"\F73B"}.mdi-gesture-pinch::before{content:"\FABC"}.mdi-gesture-spread::before{content:"\FABD"}.mdi-gesture-swipe::before{content:"\FD52"}.mdi-gesture-swipe-down::before{content:"\F73C"}.mdi-gesture-swipe-horizontal::before{content:"\FABE"}.mdi-gesture-swipe-left::before{content:"\F73D"}.mdi-gesture-swipe-right::before{content:"\F73E"}.mdi-gesture-swipe-up::before{content:"\F73F"}.mdi-gesture-swipe-vertical::before{content:"\FABF"}.mdi-gesture-tap::before{content:"\F740"}.mdi-gesture-tap-box::before{content:"\F02D4"}.mdi-gesture-tap-button::before{content:"\F02D3"}.mdi-gesture-tap-hold::before{content:"\FD53"}.mdi-gesture-two-double-tap::before{content:"\F741"}.mdi-gesture-two-tap::before{content:"\F742"}.mdi-ghost::before{content:"\F2A0"}.mdi-ghost-off::before{content:"\F9F4"}.mdi-gif::before{content:"\FD54"}.mdi-gift::before{content:"\FE85"}.mdi-gift-outline::before{content:"\F2A1"}.mdi-git::before{content:"\F2A2"}.mdi-github-box::before{content:"\F2A3"}.mdi-github-circle::before{content:"\F2A4"}.mdi-github-face::before{content:"\F6DA"}.mdi-gitlab::before{content:"\FB7C"}.mdi-glass-cocktail::before{content:"\F356"}.mdi-glass-flute::before{content:"\F2A5"}.mdi-glass-mug::before{content:"\F2A6"}.mdi-glass-mug-variant::before{content:"\F0141"}.mdi-glass-pint-outline::before{content:"\F0338"}.mdi-glass-stange::before{content:"\F2A7"}.mdi-glass-tulip::before{content:"\F2A8"}.mdi-glass-wine::before{content:"\F875"}.mdi-glassdoor::before{content:"\F2A9"}.mdi-glasses::before{content:"\F2AA"}.mdi-globe-light::before{content:"\F0302"}.mdi-globe-model::before{content:"\F8E8"}.mdi-gmail::before{content:"\F2AB"}.mdi-gnome::before{content:"\F2AC"}.mdi-go-kart::before{content:"\FD55"}.mdi-go-kart-track::before{content:"\FD56"}.mdi-gog::before{content:"\FB7D"}.mdi-gold::before{content:"\F027A"}.mdi-golf::before{content:"\F822"}.mdi-golf-cart::before{content:"\F01CF"}.mdi-golf-tee::before{content:"\F00AE"}.mdi-gondola::before{content:"\F685"}.mdi-goodreads::before{content:"\FD57"}.mdi-google::before{content:"\F2AD"}.mdi-google-adwords::before{content:"\FC63"}.mdi-google-analytics::before{content:"\F7CB"}.mdi-google-assistant::before{content:"\F7CC"}.mdi-google-cardboard::before{content:"\F2AE"}.mdi-google-chrome::before{content:"\F2AF"}.mdi-google-circles::before{content:"\F2B0"}.mdi-google-circles-communities::before{content:"\F2B1"}.mdi-google-circles-extended::before{content:"\F2B2"}.mdi-google-circles-group::before{content:"\F2B3"}.mdi-google-classroom::before{content:"\F2C0"}.mdi-google-cloud::before{content:"\F0221"}.mdi-google-controller::before{content:"\F2B4"}.mdi-google-controller-off::before{content:"\F2B5"}.mdi-google-downasaur::before{content:"\F038D"}.mdi-google-drive::before{content:"\F2B6"}.mdi-google-earth::before{content:"\F2B7"}.mdi-google-fit::before{content:"\F96B"}.mdi-google-glass::before{content:"\F2B8"}.mdi-google-hangouts::before{content:"\F2C9"}.mdi-google-home::before{content:"\F823"}.mdi-google-keep::before{content:"\F6DB"}.mdi-google-lens::before{content:"\F9F5"}.mdi-google-maps::before{content:"\F5F5"}.mdi-google-my-business::before{content:"\F006A"}.mdi-google-nearby::before{content:"\F2B9"}.mdi-google-pages::before{content:"\F2BA"}.mdi-google-photos::before{content:"\F6DC"}.mdi-google-physical-web::before{content:"\F2BB"}.mdi-google-play::before{content:"\F2BC"}.mdi-google-plus::before{content:"\F2BD"}.mdi-google-plus-box::before{content:"\F2BE"}.mdi-google-podcast::before{content:"\FED6"}.mdi-google-spreadsheet::before{content:"\F9F6"}.mdi-google-street-view::before{content:"\FC64"}.mdi-google-translate::before{content:"\F2BF"}.mdi-gradient::before{content:"\F69F"}.mdi-grain::before{content:"\FD58"}.mdi-graph::before{content:"\F006B"}.mdi-graph-outline::before{content:"\F006C"}.mdi-graphql::before{content:"\F876"}.mdi-grave-stone::before{content:"\FB7E"}.mdi-grease-pencil::before{content:"\F648"}.mdi-greater-than::before{content:"\F96C"}.mdi-greater-than-or-equal::before{content:"\F96D"}.mdi-grid::before{content:"\F2C1"}.mdi-grid-large::before{content:"\F757"}.mdi-grid-off::before{content:"\F2C2"}.mdi-grill::before{content:"\FE86"}.mdi-grill-outline::before{content:"\F01B5"}.mdi-group::before{content:"\F2C3"}.mdi-guitar-acoustic::before{content:"\F770"}.mdi-guitar-electric::before{content:"\F2C4"}.mdi-guitar-pick::before{content:"\F2C5"}.mdi-guitar-pick-outline::before{content:"\F2C6"}.mdi-guy-fawkes-mask::before{content:"\F824"}.mdi-hackernews::before{content:"\F624"}.mdi-hail::before{content:"\FAC0"}.mdi-hair-dryer::before{content:"\F011A"}.mdi-hair-dryer-outline::before{content:"\F011B"}.mdi-halloween::before{content:"\FB7F"}.mdi-hamburger::before{content:"\F684"}.mdi-hammer::before{content:"\F8E9"}.mdi-hammer-screwdriver::before{content:"\F034D"}.mdi-hammer-wrench::before{content:"\F034E"}.mdi-hand::before{content:"\FA4E"}.mdi-hand-heart::before{content:"\F011C"}.mdi-hand-left::before{content:"\FE87"}.mdi-hand-okay::before{content:"\FA4F"}.mdi-hand-peace::before{content:"\FA50"}.mdi-hand-peace-variant::before{content:"\FA51"}.mdi-hand-pointing-down::before{content:"\FA52"}.mdi-hand-pointing-left::before{content:"\FA53"}.mdi-hand-pointing-right::before{content:"\F2C7"}.mdi-hand-pointing-up::before{content:"\FA54"}.mdi-hand-right::before{content:"\FE88"}.mdi-hand-saw::before{content:"\FE89"}.mdi-handball::before{content:"\FF70"}.mdi-handcuffs::before{content:"\F0169"}.mdi-handshake::before{content:"\F0243"}.mdi-hanger::before{content:"\F2C8"}.mdi-hard-hat::before{content:"\F96E"}.mdi-harddisk::before{content:"\F2CA"}.mdi-harddisk-plus::before{content:"\F006D"}.mdi-harddisk-remove::before{content:"\F006E"}.mdi-hat-fedora::before{content:"\FB80"}.mdi-hazard-lights::before{content:"\FC65"}.mdi-hdr::before{content:"\FD59"}.mdi-hdr-off::before{content:"\FD5A"}.mdi-head::before{content:"\F0389"}.mdi-head-alert::before{content:"\F0363"}.mdi-head-alert-outline::before{content:"\F0364"}.mdi-head-check::before{content:"\F0365"}.mdi-head-check-outline::before{content:"\F0366"}.mdi-head-cog::before{content:"\F0367"}.mdi-head-cog-outline::before{content:"\F0368"}.mdi-head-dots-horizontal::before{content:"\F0369"}.mdi-head-dots-horizontal-outline::before{content:"\F036A"}.mdi-head-flash::before{content:"\F036B"}.mdi-head-flash-outline::before{content:"\F036C"}.mdi-head-heart::before{content:"\F036D"}.mdi-head-heart-outline::before{content:"\F036E"}.mdi-head-lightbulb::before{content:"\F036F"}.mdi-head-lightbulb-outline::before{content:"\F0370"}.mdi-head-minus::before{content:"\F0371"}.mdi-head-minus-outline::before{content:"\F0372"}.mdi-head-outline::before{content:"\F038A"}.mdi-head-plus::before{content:"\F0373"}.mdi-head-plus-outline::before{content:"\F0374"}.mdi-head-question::before{content:"\F0375"}.mdi-head-question-outline::before{content:"\F0376"}.mdi-head-remove::before{content:"\F0377"}.mdi-head-remove-outline::before{content:"\F0378"}.mdi-head-snowflake::before{content:"\F0379"}.mdi-head-snowflake-outline::before{content:"\F037A"}.mdi-head-sync::before{content:"\F037B"}.mdi-head-sync-outline::before{content:"\F037C"}.mdi-headphones::before{content:"\F2CB"}.mdi-headphones-bluetooth::before{content:"\F96F"}.mdi-headphones-box::before{content:"\F2CC"}.mdi-headphones-off::before{content:"\F7CD"}.mdi-headphones-settings::before{content:"\F2CD"}.mdi-headset::before{content:"\F2CE"}.mdi-headset-dock::before{content:"\F2CF"}.mdi-headset-off::before{content:"\F2D0"}.mdi-heart::before{content:"\F2D1"}.mdi-heart-box::before{content:"\F2D2"}.mdi-heart-box-outline::before{content:"\F2D3"}.mdi-heart-broken::before{content:"\F2D4"}.mdi-heart-broken-outline::before{content:"\FCF0"}.mdi-heart-circle::before{content:"\F970"}.mdi-heart-circle-outline::before{content:"\F971"}.mdi-heart-flash::before{content:"\FF16"}.mdi-heart-half::before{content:"\F6DE"}.mdi-heart-half-full::before{content:"\F6DD"}.mdi-heart-half-outline::before{content:"\F6DF"}.mdi-heart-multiple::before{content:"\FA55"}.mdi-heart-multiple-outline::before{content:"\FA56"}.mdi-heart-off::before{content:"\F758"}.mdi-heart-outline::before{content:"\F2D5"}.mdi-heart-pulse::before{content:"\F5F6"}.mdi-helicopter::before{content:"\FAC1"}.mdi-help::before{content:"\F2D6"}.mdi-help-box::before{content:"\F78A"}.mdi-help-circle::before{content:"\F2D7"}.mdi-help-circle-outline::before{content:"\F625"}.mdi-help-network::before{content:"\F6F4"}.mdi-help-network-outline::before{content:"\FC66"}.mdi-help-rhombus::before{content:"\FB81"}.mdi-help-rhombus-outline::before{content:"\FB82"}.mdi-hexadecimal::before{content:"\F02D2"}.mdi-hexagon::before{content:"\F2D8"}.mdi-hexagon-multiple::before{content:"\F6E0"}.mdi-hexagon-multiple-outline::before{content:"\F011D"}.mdi-hexagon-outline::before{content:"\F2D9"}.mdi-hexagon-slice-1::before{content:"\FAC2"}.mdi-hexagon-slice-2::before{content:"\FAC3"}.mdi-hexagon-slice-3::before{content:"\FAC4"}.mdi-hexagon-slice-4::before{content:"\FAC5"}.mdi-hexagon-slice-5::before{content:"\FAC6"}.mdi-hexagon-slice-6::before{content:"\FAC7"}.mdi-hexagram::before{content:"\FAC8"}.mdi-hexagram-outline::before{content:"\FAC9"}.mdi-high-definition::before{content:"\F7CE"}.mdi-high-definition-box::before{content:"\F877"}.mdi-highway::before{content:"\F5F7"}.mdi-hiking::before{content:"\FD5B"}.mdi-hinduism::before{content:"\F972"}.mdi-history::before{content:"\F2DA"}.mdi-hockey-puck::before{content:"\F878"}.mdi-hockey-sticks::before{content:"\F879"}.mdi-hololens::before{content:"\F2DB"}.mdi-home::before{content:"\F2DC"}.mdi-home-account::before{content:"\F825"}.mdi-home-alert::before{content:"\F87A"}.mdi-home-analytics::before{content:"\FED7"}.mdi-home-assistant::before{content:"\F7CF"}.mdi-home-automation::before{content:"\F7D0"}.mdi-home-circle::before{content:"\F7D1"}.mdi-home-circle-outline::before{content:"\F006F"}.mdi-home-city::before{content:"\FCF1"}.mdi-home-city-outline::before{content:"\FCF2"}.mdi-home-currency-usd::before{content:"\F8AE"}.mdi-home-edit::before{content:"\F0184"}.mdi-home-edit-outline::before{content:"\F0185"}.mdi-home-export-outline::before{content:"\FFB8"}.mdi-home-flood::before{content:"\FF17"}.mdi-home-floor-0::before{content:"\FDAE"}.mdi-home-floor-1::before{content:"\FD5C"}.mdi-home-floor-2::before{content:"\FD5D"}.mdi-home-floor-3::before{content:"\FD5E"}.mdi-home-floor-a::before{content:"\FD5F"}.mdi-home-floor-b::before{content:"\FD60"}.mdi-home-floor-g::before{content:"\FD61"}.mdi-home-floor-l::before{content:"\FD62"}.mdi-home-floor-negative-1::before{content:"\FDAF"}.mdi-home-group::before{content:"\FDB0"}.mdi-home-heart::before{content:"\F826"}.mdi-home-import-outline::before{content:"\FFB9"}.mdi-home-lightbulb::before{content:"\F027C"}.mdi-home-lightbulb-outline::before{content:"\F027D"}.mdi-home-lock::before{content:"\F8EA"}.mdi-home-lock-open::before{content:"\F8EB"}.mdi-home-map-marker::before{content:"\F5F8"}.mdi-home-minus::before{content:"\F973"}.mdi-home-modern::before{content:"\F2DD"}.mdi-home-outline::before{content:"\F6A0"}.mdi-home-plus::before{content:"\F974"}.mdi-home-remove::before{content:"\F0272"}.mdi-home-roof::before{content:"\F0156"}.mdi-home-thermometer::before{content:"\FF71"}.mdi-home-thermometer-outline::before{content:"\FF72"}.mdi-home-variant::before{content:"\F2DE"}.mdi-home-variant-outline::before{content:"\FB83"}.mdi-hook::before{content:"\F6E1"}.mdi-hook-off::before{content:"\F6E2"}.mdi-hops::before{content:"\F2DF"}.mdi-horizontal-rotate-clockwise::before{content:"\F011E"}.mdi-horizontal-rotate-counterclockwise::before{content:"\F011F"}.mdi-horseshoe::before{content:"\FA57"}.mdi-hospital::before{content:"\F0017"}.mdi-hospital-box::before{content:"\F2E0"}.mdi-hospital-box-outline::before{content:"\F0018"}.mdi-hospital-building::before{content:"\F2E1"}.mdi-hospital-marker::before{content:"\F2E2"}.mdi-hot-tub::before{content:"\F827"}.mdi-hotel::before{content:"\F2E3"}.mdi-houzz::before{content:"\F2E4"}.mdi-houzz-box::before{content:"\F2E5"}.mdi-hubspot::before{content:"\FCF3"}.mdi-hulu::before{content:"\F828"}.mdi-human::before{content:"\F2E6"}.mdi-human-child::before{content:"\F2E7"}.mdi-human-female::before{content:"\F649"}.mdi-human-female-boy::before{content:"\FA58"}.mdi-human-female-female::before{content:"\FA59"}.mdi-human-female-girl::before{content:"\FA5A"}.mdi-human-greeting::before{content:"\F64A"}.mdi-human-handsdown::before{content:"\F64B"}.mdi-human-handsup::before{content:"\F64C"}.mdi-human-male::before{content:"\F64D"}.mdi-human-male-boy::before{content:"\FA5B"}.mdi-human-male-female::before{content:"\F2E8"}.mdi-human-male-girl::before{content:"\FA5C"}.mdi-human-male-height::before{content:"\FF18"}.mdi-human-male-height-variant::before{content:"\FF19"}.mdi-human-male-male::before{content:"\FA5D"}.mdi-human-pregnant::before{content:"\F5CF"}.mdi-humble-bundle::before{content:"\F743"}.mdi-hvac::before{content:"\F037D"}.mdi-hydraulic-oil-level::before{content:"\F034F"}.mdi-hydraulic-oil-temperature::before{content:"\F0350"}.mdi-hydro-power::before{content:"\F0310"}.mdi-ice-cream::before{content:"\F829"}.mdi-ice-pop::before{content:"\FF1A"}.mdi-id-card::before{content:"\FFE0"}.mdi-identifier::before{content:"\FF1B"}.mdi-ideogram-cjk::before{content:"\F035C"}.mdi-ideogram-cjk-variant::before{content:"\F035D"}.mdi-iframe::before{content:"\FC67"}.mdi-iframe-array::before{content:"\F0120"}.mdi-iframe-array-outline::before{content:"\F0121"}.mdi-iframe-braces::before{content:"\F0122"}.mdi-iframe-braces-outline::before{content:"\F0123"}.mdi-iframe-outline::before{content:"\FC68"}.mdi-iframe-parentheses::before{content:"\F0124"}.mdi-iframe-parentheses-outline::before{content:"\F0125"}.mdi-iframe-variable::before{content:"\F0126"}.mdi-iframe-variable-outline::before{content:"\F0127"}.mdi-image::before{content:"\F2E9"}.mdi-image-album::before{content:"\F2EA"}.mdi-image-area::before{content:"\F2EB"}.mdi-image-area-close::before{content:"\F2EC"}.mdi-image-auto-adjust::before{content:"\FFE1"}.mdi-image-broken::before{content:"\F2ED"}.mdi-image-broken-variant::before{content:"\F2EE"}.mdi-image-edit::before{content:"\F020E"}.mdi-image-edit-outline::before{content:"\F020F"}.mdi-image-filter::before{content:"\F2EF"}.mdi-image-filter-black-white::before{content:"\F2F0"}.mdi-image-filter-center-focus::before{content:"\F2F1"}.mdi-image-filter-center-focus-strong::before{content:"\FF1C"}.mdi-image-filter-center-focus-strong-outline::before{content:"\FF1D"}.mdi-image-filter-center-focus-weak::before{content:"\F2F2"}.mdi-image-filter-drama::before{content:"\F2F3"}.mdi-image-filter-frames::before{content:"\F2F4"}.mdi-image-filter-hdr::before{content:"\F2F5"}.mdi-image-filter-none::before{content:"\F2F6"}.mdi-image-filter-tilt-shift::before{content:"\F2F7"}.mdi-image-filter-vintage::before{content:"\F2F8"}.mdi-image-frame::before{content:"\FE8A"}.mdi-image-move::before{content:"\F9F7"}.mdi-image-multiple::before{content:"\F2F9"}.mdi-image-off::before{content:"\F82A"}.mdi-image-off-outline::before{content:"\F01FC"}.mdi-image-outline::before{content:"\F975"}.mdi-image-plus::before{content:"\F87B"}.mdi-image-search::before{content:"\F976"}.mdi-image-search-outline::before{content:"\F977"}.mdi-image-size-select-actual::before{content:"\FC69"}.mdi-image-size-select-large::before{content:"\FC6A"}.mdi-image-size-select-small::before{content:"\FC6B"}.mdi-import::before{content:"\F2FA"}.mdi-inbox::before{content:"\F686"}.mdi-inbox-arrow-down::before{content:"\F2FB"}.mdi-inbox-arrow-down-outline::before{content:"\F029B"}.mdi-inbox-arrow-up::before{content:"\F3D1"}.mdi-inbox-arrow-up-outline::before{content:"\F029C"}.mdi-inbox-full::before{content:"\F029D"}.mdi-inbox-full-outline::before{content:"\F029E"}.mdi-inbox-multiple::before{content:"\F8AF"}.mdi-inbox-multiple-outline::before{content:"\FB84"}.mdi-inbox-outline::before{content:"\F029F"}.mdi-incognito::before{content:"\F5F9"}.mdi-infinity::before{content:"\F6E3"}.mdi-information::before{content:"\F2FC"}.mdi-information-outline::before{content:"\F2FD"}.mdi-information-variant::before{content:"\F64E"}.mdi-instagram::before{content:"\F2FE"}.mdi-instapaper::before{content:"\F2FF"}.mdi-instrument-triangle::before{content:"\F0070"}.mdi-internet-explorer::before{content:"\F300"}.mdi-invert-colors::before{content:"\F301"}.mdi-invert-colors-off::before{content:"\FE8B"}.mdi-iobroker::before{content:"\F0313"}.mdi-ip::before{content:"\FA5E"}.mdi-ip-network::before{content:"\FA5F"}.mdi-ip-network-outline::before{content:"\FC6C"}.mdi-ipod::before{content:"\FC6D"}.mdi-islam::before{content:"\F978"}.mdi-island::before{content:"\F0071"}.mdi-itunes::before{content:"\F676"}.mdi-iv-bag::before{content:"\F00E4"}.mdi-jabber::before{content:"\FDB1"}.mdi-jeepney::before{content:"\F302"}.mdi-jellyfish::before{content:"\FF1E"}.mdi-jellyfish-outline::before{content:"\FF1F"}.mdi-jira::before{content:"\F303"}.mdi-jquery::before{content:"\F87C"}.mdi-jsfiddle::before{content:"\F304"}.mdi-json::before{content:"\F626"}.mdi-judaism::before{content:"\F979"}.mdi-jump-rope::before{content:"\F032A"}.mdi-kabaddi::before{content:"\FD63"}.mdi-karate::before{content:"\F82B"}.mdi-keg::before{content:"\F305"}.mdi-kettle::before{content:"\F5FA"}.mdi-kettle-alert::before{content:"\F0342"}.mdi-kettle-alert-outline::before{content:"\F0343"}.mdi-kettle-off::before{content:"\F0346"}.mdi-kettle-off-outline::before{content:"\F0347"}.mdi-kettle-outline::before{content:"\FF73"}.mdi-kettle-steam::before{content:"\F0344"}.mdi-kettle-steam-outline::before{content:"\F0345"}.mdi-kettlebell::before{content:"\F032B"}.mdi-key::before{content:"\F306"}.mdi-key-arrow-right::before{content:"\F033D"}.mdi-key-change::before{content:"\F307"}.mdi-key-link::before{content:"\F01CA"}.mdi-key-minus::before{content:"\F308"}.mdi-key-outline::before{content:"\FDB2"}.mdi-key-plus::before{content:"\F309"}.mdi-key-remove::before{content:"\F30A"}.mdi-key-star::before{content:"\F01C9"}.mdi-key-variant::before{content:"\F30B"}.mdi-key-wireless::before{content:"\FFE2"}.mdi-keyboard::before{content:"\F30C"}.mdi-keyboard-backspace::before{content:"\F30D"}.mdi-keyboard-caps::before{content:"\F30E"}.mdi-keyboard-close::before{content:"\F30F"}.mdi-keyboard-esc::before{content:"\F02E2"}.mdi-keyboard-f1::before{content:"\F02D6"}.mdi-keyboard-f10::before{content:"\F02DF"}.mdi-keyboard-f11::before{content:"\F02E0"}.mdi-keyboard-f12::before{content:"\F02E1"}.mdi-keyboard-f2::before{content:"\F02D7"}.mdi-keyboard-f3::before{content:"\F02D8"}.mdi-keyboard-f4::before{content:"\F02D9"}.mdi-keyboard-f5::before{content:"\F02DA"}.mdi-keyboard-f6::before{content:"\F02DB"}.mdi-keyboard-f7::before{content:"\F02DC"}.mdi-keyboard-f8::before{content:"\F02DD"}.mdi-keyboard-f9::before{content:"\F02DE"}.mdi-keyboard-off::before{content:"\F310"}.mdi-keyboard-off-outline::before{content:"\FE8C"}.mdi-keyboard-outline::before{content:"\F97A"}.mdi-keyboard-return::before{content:"\F311"}.mdi-keyboard-settings::before{content:"\F9F8"}.mdi-keyboard-settings-outline::before{content:"\F9F9"}.mdi-keyboard-space::before{content:"\F0072"}.mdi-keyboard-tab::before{content:"\F312"}.mdi-keyboard-variant::before{content:"\F313"}.mdi-khanda::before{content:"\F0128"}.mdi-kickstarter::before{content:"\F744"}.mdi-klingon::before{content:"\F0386"}.mdi-knife::before{content:"\F9FA"}.mdi-knife-military::before{content:"\F9FB"}.mdi-kodi::before{content:"\F314"}.mdi-kotlin::before{content:"\F0244"}.mdi-kubernetes::before{content:"\F0129"}.mdi-label::before{content:"\F315"}.mdi-label-multiple::before{content:"\F03A0"}.mdi-label-multiple-outline::before{content:"\F03A1"}.mdi-label-off::before{content:"\FACA"}.mdi-label-off-outline::before{content:"\FACB"}.mdi-label-outline::before{content:"\F316"}.mdi-label-percent::before{content:"\F0315"}.mdi-label-percent-outline::before{content:"\F0316"}.mdi-label-variant::before{content:"\FACC"}.mdi-label-variant-outline::before{content:"\FACD"}.mdi-ladybug::before{content:"\F82C"}.mdi-lambda::before{content:"\F627"}.mdi-lamp::before{content:"\F6B4"}.mdi-lan::before{content:"\F317"}.mdi-lan-check::before{content:"\F02D5"}.mdi-lan-connect::before{content:"\F318"}.mdi-lan-disconnect::before{content:"\F319"}.mdi-lan-pending::before{content:"\F31A"}.mdi-language-c::before{content:"\F671"}.mdi-language-cpp::before{content:"\F672"}.mdi-language-csharp::before{content:"\F31B"}.mdi-language-css3::before{content:"\F31C"}.mdi-language-fortran::before{content:"\F0245"}.mdi-language-go::before{content:"\F7D2"}.mdi-language-haskell::before{content:"\FC6E"}.mdi-language-html5::before{content:"\F31D"}.mdi-language-java::before{content:"\FB1C"}.mdi-language-javascript::before{content:"\F31E"}.mdi-language-lua::before{content:"\F8B0"}.mdi-language-php::before{content:"\F31F"}.mdi-language-python::before{content:"\F320"}.mdi-language-python-text::before{content:"\F321"}.mdi-language-r::before{content:"\F7D3"}.mdi-language-ruby-on-rails::before{content:"\FACE"}.mdi-language-swift::before{content:"\F6E4"}.mdi-language-typescript::before{content:"\F6E5"}.mdi-laptop::before{content:"\F322"}.mdi-laptop-chromebook::before{content:"\F323"}.mdi-laptop-mac::before{content:"\F324"}.mdi-laptop-off::before{content:"\F6E6"}.mdi-laptop-windows::before{content:"\F325"}.mdi-laravel::before{content:"\FACF"}.mdi-lasso::before{content:"\FF20"}.mdi-lastfm::before{content:"\F326"}.mdi-lastpass::before{content:"\F446"}.mdi-latitude::before{content:"\FF74"}.mdi-launch::before{content:"\F327"}.mdi-lava-lamp::before{content:"\F7D4"}.mdi-layers::before{content:"\F328"}.mdi-layers-minus::before{content:"\FE8D"}.mdi-layers-off::before{content:"\F329"}.mdi-layers-off-outline::before{content:"\F9FC"}.mdi-layers-outline::before{content:"\F9FD"}.mdi-layers-plus::before{content:"\FE30"}.mdi-layers-remove::before{content:"\FE31"}.mdi-layers-search::before{content:"\F0231"}.mdi-layers-search-outline::before{content:"\F0232"}.mdi-layers-triple::before{content:"\FF75"}.mdi-layers-triple-outline::before{content:"\FF76"}.mdi-lead-pencil::before{content:"\F64F"}.mdi-leaf::before{content:"\F32A"}.mdi-leaf-maple::before{content:"\FC6F"}.mdi-leaf-maple-off::before{content:"\F0305"}.mdi-leaf-off::before{content:"\F0304"}.mdi-leak::before{content:"\FDB3"}.mdi-leak-off::before{content:"\FDB4"}.mdi-led-off::before{content:"\F32B"}.mdi-led-on::before{content:"\F32C"}.mdi-led-outline::before{content:"\F32D"}.mdi-led-strip::before{content:"\F7D5"}.mdi-led-strip-variant::before{content:"\F0073"}.mdi-led-variant-off::before{content:"\F32E"}.mdi-led-variant-on::before{content:"\F32F"}.mdi-led-variant-outline::before{content:"\F330"}.mdi-leek::before{content:"\F01A8"}.mdi-less-than::before{content:"\F97B"}.mdi-less-than-or-equal::before{content:"\F97C"}.mdi-library::before{content:"\F331"}.mdi-library-books::before{content:"\F332"}.mdi-library-movie::before{content:"\FCF4"}.mdi-library-music::before{content:"\F333"}.mdi-library-music-outline::before{content:"\FF21"}.mdi-library-shelves::before{content:"\FB85"}.mdi-library-video::before{content:"\FCF5"}.mdi-license::before{content:"\FFE3"}.mdi-lifebuoy::before{content:"\F87D"}.mdi-light-switch::before{content:"\F97D"}.mdi-lightbulb::before{content:"\F335"}.mdi-lightbulb-cfl::before{content:"\F0233"}.mdi-lightbulb-cfl-off::before{content:"\F0234"}.mdi-lightbulb-cfl-spiral::before{content:"\F02A0"}.mdi-lightbulb-cfl-spiral-off::before{content:"\F02EE"}.mdi-lightbulb-group::before{content:"\F027E"}.mdi-lightbulb-group-off::before{content:"\F02F8"}.mdi-lightbulb-group-off-outline::before{content:"\F02F9"}.mdi-lightbulb-group-outline::before{content:"\F027F"}.mdi-lightbulb-multiple::before{content:"\F0280"}.mdi-lightbulb-multiple-off::before{content:"\F02FA"}.mdi-lightbulb-multiple-off-outline::before{content:"\F02FB"}.mdi-lightbulb-multiple-outline::before{content:"\F0281"}.mdi-lightbulb-off::before{content:"\FE32"}.mdi-lightbulb-off-outline::before{content:"\FE33"}.mdi-lightbulb-on::before{content:"\F6E7"}.mdi-lightbulb-on-outline::before{content:"\F6E8"}.mdi-lightbulb-outline::before{content:"\F336"}.mdi-lighthouse::before{content:"\F9FE"}.mdi-lighthouse-on::before{content:"\F9FF"}.mdi-link::before{content:"\F337"}.mdi-link-box::before{content:"\FCF6"}.mdi-link-box-outline::before{content:"\FCF7"}.mdi-link-box-variant::before{content:"\FCF8"}.mdi-link-box-variant-outline::before{content:"\FCF9"}.mdi-link-lock::before{content:"\F00E5"}.mdi-link-off::before{content:"\F338"}.mdi-link-plus::before{content:"\FC70"}.mdi-link-variant::before{content:"\F339"}.mdi-link-variant-minus::before{content:"\F012A"}.mdi-link-variant-off::before{content:"\F33A"}.mdi-link-variant-plus::before{content:"\F012B"}.mdi-link-variant-remove::before{content:"\F012C"}.mdi-linkedin::before{content:"\F33B"}.mdi-linkedin-box::before{content:"\F33C"}.mdi-linux::before{content:"\F33D"}.mdi-linux-mint::before{content:"\F8EC"}.mdi-litecoin::before{content:"\FA60"}.mdi-loading::before{content:"\F771"}.mdi-location-enter::before{content:"\FFE4"}.mdi-location-exit::before{content:"\FFE5"}.mdi-lock::before{content:"\F33E"}.mdi-lock-alert::before{content:"\F8ED"}.mdi-lock-clock::before{content:"\F97E"}.mdi-lock-open::before{content:"\F33F"}.mdi-lock-open-outline::before{content:"\F340"}.mdi-lock-open-variant::before{content:"\FFE6"}.mdi-lock-open-variant-outline::before{content:"\FFE7"}.mdi-lock-outline::before{content:"\F341"}.mdi-lock-pattern::before{content:"\F6E9"}.mdi-lock-plus::before{content:"\F5FB"}.mdi-lock-question::before{content:"\F8EE"}.mdi-lock-reset::before{content:"\F772"}.mdi-lock-smart::before{content:"\F8B1"}.mdi-locker::before{content:"\F7D6"}.mdi-locker-multiple::before{content:"\F7D7"}.mdi-login::before{content:"\F342"}.mdi-login-variant::before{content:"\F5FC"}.mdi-logout::before{content:"\F343"}.mdi-logout-variant::before{content:"\F5FD"}.mdi-longitude::before{content:"\FF77"}.mdi-looks::before{content:"\F344"}.mdi-loupe::before{content:"\F345"}.mdi-lumx::before{content:"\F346"}.mdi-lungs::before{content:"\F00AF"}.mdi-lyft::before{content:"\FB1D"}.mdi-magnet::before{content:"\F347"}.mdi-magnet-on::before{content:"\F348"}.mdi-magnify::before{content:"\F349"}.mdi-magnify-close::before{content:"\F97F"}.mdi-magnify-minus::before{content:"\F34A"}.mdi-magnify-minus-cursor::before{content:"\FA61"}.mdi-magnify-minus-outline::before{content:"\F6EB"}.mdi-magnify-plus::before{content:"\F34B"}.mdi-magnify-plus-cursor::before{content:"\FA62"}.mdi-magnify-plus-outline::before{content:"\F6EC"}.mdi-magnify-remove-cursor::before{content:"\F0237"}.mdi-magnify-remove-outline::before{content:"\F0238"}.mdi-magnify-scan::before{content:"\F02A1"}.mdi-mail::before{content:"\FED8"}.mdi-mail-ru::before{content:"\F34C"}.mdi-mailbox::before{content:"\F6ED"}.mdi-mailbox-open::before{content:"\FD64"}.mdi-mailbox-open-outline::before{content:"\FD65"}.mdi-mailbox-open-up::before{content:"\FD66"}.mdi-mailbox-open-up-outline::before{content:"\FD67"}.mdi-mailbox-outline::before{content:"\FD68"}.mdi-mailbox-up::before{content:"\FD69"}.mdi-mailbox-up-outline::before{content:"\FD6A"}.mdi-map::before{content:"\F34D"}.mdi-map-check::before{content:"\FED9"}.mdi-map-check-outline::before{content:"\FEDA"}.mdi-map-clock::before{content:"\FCFA"}.mdi-map-clock-outline::before{content:"\FCFB"}.mdi-map-legend::before{content:"\FA00"}.mdi-map-marker::before{content:"\F34E"}.mdi-map-marker-alert::before{content:"\FF22"}.mdi-map-marker-alert-outline::before{content:"\FF23"}.mdi-map-marker-check::before{content:"\FC71"}.mdi-map-marker-check-outline::before{content:"\F0326"}.mdi-map-marker-circle::before{content:"\F34F"}.mdi-map-marker-distance::before{content:"\F8EF"}.mdi-map-marker-down::before{content:"\F012D"}.mdi-map-marker-left::before{content:"\F0306"}.mdi-map-marker-left-outline::before{content:"\F0308"}.mdi-map-marker-minus::before{content:"\F650"}.mdi-map-marker-minus-outline::before{content:"\F0324"}.mdi-map-marker-multiple::before{content:"\F350"}.mdi-map-marker-multiple-outline::before{content:"\F02A2"}.mdi-map-marker-off::before{content:"\F351"}.mdi-map-marker-off-outline::before{content:"\F0328"}.mdi-map-marker-outline::before{content:"\F7D8"}.mdi-map-marker-path::before{content:"\FCFC"}.mdi-map-marker-plus::before{content:"\F651"}.mdi-map-marker-plus-outline::before{content:"\F0323"}.mdi-map-marker-question::before{content:"\FF24"}.mdi-map-marker-question-outline::before{content:"\FF25"}.mdi-map-marker-radius::before{content:"\F352"}.mdi-map-marker-radius-outline::before{content:"\F0327"}.mdi-map-marker-remove::before{content:"\FF26"}.mdi-map-marker-remove-outline::before{content:"\F0325"}.mdi-map-marker-remove-variant::before{content:"\FF27"}.mdi-map-marker-right::before{content:"\F0307"}.mdi-map-marker-right-outline::before{content:"\F0309"}.mdi-map-marker-up::before{content:"\F012E"}.mdi-map-minus::before{content:"\F980"}.mdi-map-outline::before{content:"\F981"}.mdi-map-plus::before{content:"\F982"}.mdi-map-search::before{content:"\F983"}.mdi-map-search-outline::before{content:"\F984"}.mdi-mapbox::before{content:"\FB86"}.mdi-margin::before{content:"\F353"}.mdi-markdown::before{content:"\F354"}.mdi-markdown-outline::before{content:"\FF78"}.mdi-marker::before{content:"\F652"}.mdi-marker-cancel::before{content:"\FDB5"}.mdi-marker-check::before{content:"\F355"}.mdi-mastodon::before{content:"\FAD0"}.mdi-mastodon-variant::before{content:"\FAD1"}.mdi-material-design::before{content:"\F985"}.mdi-material-ui::before{content:"\F357"}.mdi-math-compass::before{content:"\F358"}.mdi-math-cos::before{content:"\FC72"}.mdi-math-integral::before{content:"\FFE8"}.mdi-math-integral-box::before{content:"\FFE9"}.mdi-math-log::before{content:"\F00B0"}.mdi-math-norm::before{content:"\FFEA"}.mdi-math-norm-box::before{content:"\FFEB"}.mdi-math-sin::before{content:"\FC73"}.mdi-math-tan::before{content:"\FC74"}.mdi-matrix::before{content:"\F628"}.mdi-medal::before{content:"\F986"}.mdi-medal-outline::before{content:"\F0351"}.mdi-medical-bag::before{content:"\F6EE"}.mdi-meditation::before{content:"\F01A6"}.mdi-medium::before{content:"\F35A"}.mdi-meetup::before{content:"\FAD2"}.mdi-memory::before{content:"\F35B"}.mdi-menu::before{content:"\F35C"}.mdi-menu-down::before{content:"\F35D"}.mdi-menu-down-outline::before{content:"\F6B5"}.mdi-menu-left::before{content:"\F35E"}.mdi-menu-left-outline::before{content:"\FA01"}.mdi-menu-open::before{content:"\FB87"}.mdi-menu-right::before{content:"\F35F"}.mdi-menu-right-outline::before{content:"\FA02"}.mdi-menu-swap::before{content:"\FA63"}.mdi-menu-swap-outline::before{content:"\FA64"}.mdi-menu-up::before{content:"\F360"}.mdi-menu-up-outline::before{content:"\F6B6"}.mdi-merge::before{content:"\FF79"}.mdi-message::before{content:"\F361"}.mdi-message-alert::before{content:"\F362"}.mdi-message-alert-outline::before{content:"\FA03"}.mdi-message-arrow-left::before{content:"\F031D"}.mdi-message-arrow-left-outline::before{content:"\F031E"}.mdi-message-arrow-right::before{content:"\F031F"}.mdi-message-arrow-right-outline::before{content:"\F0320"}.mdi-message-bulleted::before{content:"\F6A1"}.mdi-message-bulleted-off::before{content:"\F6A2"}.mdi-message-draw::before{content:"\F363"}.mdi-message-image::before{content:"\F364"}.mdi-message-image-outline::before{content:"\F0197"}.mdi-message-lock::before{content:"\FFEC"}.mdi-message-lock-outline::before{content:"\F0198"}.mdi-message-minus::before{content:"\F0199"}.mdi-message-minus-outline::before{content:"\F019A"}.mdi-message-outline::before{content:"\F365"}.mdi-message-plus::before{content:"\F653"}.mdi-message-plus-outline::before{content:"\F00E6"}.mdi-message-processing::before{content:"\F366"}.mdi-message-processing-outline::before{content:"\F019B"}.mdi-message-reply::before{content:"\F367"}.mdi-message-reply-text::before{content:"\F368"}.mdi-message-settings::before{content:"\F6EF"}.mdi-message-settings-outline::before{content:"\F019C"}.mdi-message-settings-variant::before{content:"\F6F0"}.mdi-message-settings-variant-outline::before{content:"\F019D"}.mdi-message-text::before{content:"\F369"}.mdi-message-text-clock::before{content:"\F019E"}.mdi-message-text-clock-outline::before{content:"\F019F"}.mdi-message-text-lock::before{content:"\FFED"}.mdi-message-text-lock-outline::before{content:"\F01A0"}.mdi-message-text-outline::before{content:"\F36A"}.mdi-message-video::before{content:"\F36B"}.mdi-meteor::before{content:"\F629"}.mdi-metronome::before{content:"\F7D9"}.mdi-metronome-tick::before{content:"\F7DA"}.mdi-micro-sd::before{content:"\F7DB"}.mdi-microphone::before{content:"\F36C"}.mdi-microphone-minus::before{content:"\F8B2"}.mdi-microphone-off::before{content:"\F36D"}.mdi-microphone-outline::before{content:"\F36E"}.mdi-microphone-plus::before{content:"\F8B3"}.mdi-microphone-settings::before{content:"\F36F"}.mdi-microphone-variant::before{content:"\F370"}.mdi-microphone-variant-off::before{content:"\F371"}.mdi-microscope::before{content:"\F654"}.mdi-microsoft::before{content:"\F372"}.mdi-microsoft-dynamics::before{content:"\F987"}.mdi-microwave::before{content:"\FC75"}.mdi-middleware::before{content:"\FF7A"}.mdi-middleware-outline::before{content:"\FF7B"}.mdi-midi::before{content:"\F8F0"}.mdi-midi-port::before{content:"\F8F1"}.mdi-mine::before{content:"\FDB6"}.mdi-minecraft::before{content:"\F373"}.mdi-mini-sd::before{content:"\FA04"}.mdi-minidisc::before{content:"\FA05"}.mdi-minus::before{content:"\F374"}.mdi-minus-box::before{content:"\F375"}.mdi-minus-box-multiple::before{content:"\F016C"}.mdi-minus-box-multiple-outline::before{content:"\F016D"}.mdi-minus-box-outline::before{content:"\F6F1"}.mdi-minus-circle::before{content:"\F376"}.mdi-minus-circle-outline::before{content:"\F377"}.mdi-minus-network::before{content:"\F378"}.mdi-minus-network-outline::before{content:"\FC76"}.mdi-mirror::before{content:"\F0228"}.mdi-mixcloud::before{content:"\F62A"}.mdi-mixed-martial-arts::before{content:"\FD6B"}.mdi-mixed-reality::before{content:"\F87E"}.mdi-mixer::before{content:"\F7DC"}.mdi-molecule::before{content:"\FB88"}.mdi-monitor::before{content:"\F379"}.mdi-monitor-cellphone::before{content:"\F988"}.mdi-monitor-cellphone-star::before{content:"\F989"}.mdi-monitor-clean::before{content:"\F012F"}.mdi-monitor-dashboard::before{content:"\FA06"}.mdi-monitor-edit::before{content:"\F02F1"}.mdi-monitor-lock::before{content:"\FDB7"}.mdi-monitor-multiple::before{content:"\F37A"}.mdi-monitor-off::before{content:"\FD6C"}.mdi-monitor-screenshot::before{content:"\FE34"}.mdi-monitor-speaker::before{content:"\FF7C"}.mdi-monitor-speaker-off::before{content:"\FF7D"}.mdi-monitor-star::before{content:"\FDB8"}.mdi-moon-first-quarter::before{content:"\FF7E"}.mdi-moon-full::before{content:"\FF7F"}.mdi-moon-last-quarter::before{content:"\FF80"}.mdi-moon-new::before{content:"\FF81"}.mdi-moon-waning-crescent::before{content:"\FF82"}.mdi-moon-waning-gibbous::before{content:"\FF83"}.mdi-moon-waxing-crescent::before{content:"\FF84"}.mdi-moon-waxing-gibbous::before{content:"\FF85"}.mdi-moped::before{content:"\F00B1"}.mdi-more::before{content:"\F37B"}.mdi-mother-heart::before{content:"\F033F"}.mdi-mother-nurse::before{content:"\FCFD"}.mdi-motion-sensor::before{content:"\FD6D"}.mdi-motorbike::before{content:"\F37C"}.mdi-mouse::before{content:"\F37D"}.mdi-mouse-bluetooth::before{content:"\F98A"}.mdi-mouse-off::before{content:"\F37E"}.mdi-mouse-variant::before{content:"\F37F"}.mdi-mouse-variant-off::before{content:"\F380"}.mdi-move-resize::before{content:"\F655"}.mdi-move-resize-variant::before{content:"\F656"}.mdi-movie::before{content:"\F381"}.mdi-movie-edit::before{content:"\F014D"}.mdi-movie-edit-outline::before{content:"\F014E"}.mdi-movie-filter::before{content:"\F014F"}.mdi-movie-filter-outline::before{content:"\F0150"}.mdi-movie-open::before{content:"\FFEE"}.mdi-movie-open-outline::before{content:"\FFEF"}.mdi-movie-outline::before{content:"\FDB9"}.mdi-movie-roll::before{content:"\F7DD"}.mdi-movie-search::before{content:"\F01FD"}.mdi-movie-search-outline::before{content:"\F01FE"}.mdi-muffin::before{content:"\F98B"}.mdi-multiplication::before{content:"\F382"}.mdi-multiplication-box::before{content:"\F383"}.mdi-mushroom::before{content:"\F7DE"}.mdi-mushroom-outline::before{content:"\F7DF"}.mdi-music::before{content:"\F759"}.mdi-music-accidental-double-flat::before{content:"\FF86"}.mdi-music-accidental-double-sharp::before{content:"\FF87"}.mdi-music-accidental-flat::before{content:"\FF88"}.mdi-music-accidental-natural::before{content:"\FF89"}.mdi-music-accidental-sharp::before{content:"\FF8A"}.mdi-music-box::before{content:"\F384"}.mdi-music-box-outline::before{content:"\F385"}.mdi-music-circle::before{content:"\F386"}.mdi-music-circle-outline::before{content:"\FAD3"}.mdi-music-clef-alto::before{content:"\FF8B"}.mdi-music-clef-bass::before{content:"\FF8C"}.mdi-music-clef-treble::before{content:"\FF8D"}.mdi-music-note::before{content:"\F387"}.mdi-music-note-bluetooth::before{content:"\F5FE"}.mdi-music-note-bluetooth-off::before{content:"\F5FF"}.mdi-music-note-eighth::before{content:"\F388"}.mdi-music-note-eighth-dotted::before{content:"\FF8E"}.mdi-music-note-half::before{content:"\F389"}.mdi-music-note-half-dotted::before{content:"\FF8F"}.mdi-music-note-off::before{content:"\F38A"}.mdi-music-note-off-outline::before{content:"\FF90"}.mdi-music-note-outline::before{content:"\FF91"}.mdi-music-note-plus::before{content:"\FDBA"}.mdi-music-note-quarter::before{content:"\F38B"}.mdi-music-note-quarter-dotted::before{content:"\FF92"}.mdi-music-note-sixteenth::before{content:"\F38C"}.mdi-music-note-sixteenth-dotted::before{content:"\FF93"}.mdi-music-note-whole::before{content:"\F38D"}.mdi-music-note-whole-dotted::before{content:"\FF94"}.mdi-music-off::before{content:"\F75A"}.mdi-music-rest-eighth::before{content:"\FF95"}.mdi-music-rest-half::before{content:"\FF96"}.mdi-music-rest-quarter::before{content:"\FF97"}.mdi-music-rest-sixteenth::before{content:"\FF98"}.mdi-music-rest-whole::before{content:"\FF99"}.mdi-nail::before{content:"\FDBB"}.mdi-nas::before{content:"\F8F2"}.mdi-nativescript::before{content:"\F87F"}.mdi-nature::before{content:"\F38E"}.mdi-nature-people::before{content:"\F38F"}.mdi-navigation::before{content:"\F390"}.mdi-near-me::before{content:"\F5CD"}.mdi-necklace::before{content:"\FF28"}.mdi-needle::before{content:"\F391"}.mdi-netflix::before{content:"\F745"}.mdi-network::before{content:"\F6F2"}.mdi-network-off::before{content:"\FC77"}.mdi-network-off-outline::before{content:"\FC78"}.mdi-network-outline::before{content:"\FC79"}.mdi-network-router::before{content:"\F00B2"}.mdi-network-strength-1::before{content:"\F8F3"}.mdi-network-strength-1-alert::before{content:"\F8F4"}.mdi-network-strength-2::before{content:"\F8F5"}.mdi-network-strength-2-alert::before{content:"\F8F6"}.mdi-network-strength-3::before{content:"\F8F7"}.mdi-network-strength-3-alert::before{content:"\F8F8"}.mdi-network-strength-4::before{content:"\F8F9"}.mdi-network-strength-4-alert::before{content:"\F8FA"}.mdi-network-strength-off::before{content:"\F8FB"}.mdi-network-strength-off-outline::before{content:"\F8FC"}.mdi-network-strength-outline::before{content:"\F8FD"}.mdi-new-box::before{content:"\F394"}.mdi-newspaper::before{content:"\F395"}.mdi-newspaper-minus::before{content:"\FF29"}.mdi-newspaper-plus::before{content:"\FF2A"}.mdi-newspaper-variant::before{content:"\F0023"}.mdi-newspaper-variant-multiple::before{content:"\F0024"}.mdi-newspaper-variant-multiple-outline::before{content:"\F0025"}.mdi-newspaper-variant-outline::before{content:"\F0026"}.mdi-nfc::before{content:"\F396"}.mdi-nfc-off::before{content:"\FE35"}.mdi-nfc-search-variant::before{content:"\FE36"}.mdi-nfc-tap::before{content:"\F397"}.mdi-nfc-variant::before{content:"\F398"}.mdi-nfc-variant-off::before{content:"\FE37"}.mdi-ninja::before{content:"\F773"}.mdi-nintendo-switch::before{content:"\F7E0"}.mdi-nix::before{content:"\F0130"}.mdi-nodejs::before{content:"\F399"}.mdi-noodles::before{content:"\F01A9"}.mdi-not-equal::before{content:"\F98C"}.mdi-not-equal-variant::before{content:"\F98D"}.mdi-note::before{content:"\F39A"}.mdi-note-multiple::before{content:"\F6B7"}.mdi-note-multiple-outline::before{content:"\F6B8"}.mdi-note-outline::before{content:"\F39B"}.mdi-note-plus::before{content:"\F39C"}.mdi-note-plus-outline::before{content:"\F39D"}.mdi-note-text::before{content:"\F39E"}.mdi-note-text-outline::before{content:"\F0202"}.mdi-notebook::before{content:"\F82D"}.mdi-notebook-multiple::before{content:"\FE38"}.mdi-notebook-outline::before{content:"\FEDC"}.mdi-notification-clear-all::before{content:"\F39F"}.mdi-npm::before{content:"\F6F6"}.mdi-npm-variant::before{content:"\F98E"}.mdi-npm-variant-outline::before{content:"\F98F"}.mdi-nuke::before{content:"\F6A3"}.mdi-null::before{content:"\F7E1"}.mdi-numeric::before{content:"\F3A0"}.mdi-numeric-0::before{content:"\30"}.mdi-numeric-0-box::before{content:"\F3A1"}.mdi-numeric-0-box-multiple::before{content:"\FF2B"}.mdi-numeric-0-box-multiple-outline::before{content:"\F3A2"}.mdi-numeric-0-box-outline::before{content:"\F3A3"}.mdi-numeric-0-circle::before{content:"\FC7A"}.mdi-numeric-0-circle-outline::before{content:"\FC7B"}.mdi-numeric-1::before{content:"\31"}.mdi-numeric-1-box::before{content:"\F3A4"}.mdi-numeric-1-box-multiple::before{content:"\FF2C"}.mdi-numeric-1-box-multiple-outline::before{content:"\F3A5"}.mdi-numeric-1-box-outline::before{content:"\F3A6"}.mdi-numeric-1-circle::before{content:"\FC7C"}.mdi-numeric-1-circle-outline::before{content:"\FC7D"}.mdi-numeric-10::before{content:"\F000A"}.mdi-numeric-10-box::before{content:"\FF9A"}.mdi-numeric-10-box-multiple::before{content:"\F000B"}.mdi-numeric-10-box-multiple-outline::before{content:"\F000C"}.mdi-numeric-10-box-outline::before{content:"\FF9B"}.mdi-numeric-10-circle::before{content:"\F000D"}.mdi-numeric-10-circle-outline::before{content:"\F000E"}.mdi-numeric-2::before{content:"\32"}.mdi-numeric-2-box::before{content:"\F3A7"}.mdi-numeric-2-box-multiple::before{content:"\FF2D"}.mdi-numeric-2-box-multiple-outline::before{content:"\F3A8"}.mdi-numeric-2-box-outline::before{content:"\F3A9"}.mdi-numeric-2-circle::before{content:"\FC7E"}.mdi-numeric-2-circle-outline::before{content:"\FC7F"}.mdi-numeric-3::before{content:"\33"}.mdi-numeric-3-box::before{content:"\F3AA"}.mdi-numeric-3-box-multiple::before{content:"\FF2E"}.mdi-numeric-3-box-multiple-outline::before{content:"\F3AB"}.mdi-numeric-3-box-outline::before{content:"\F3AC"}.mdi-numeric-3-circle::before{content:"\FC80"}.mdi-numeric-3-circle-outline::before{content:"\FC81"}.mdi-numeric-4::before{content:"\34"}.mdi-numeric-4-box::before{content:"\F3AD"}.mdi-numeric-4-box-multiple::before{content:"\FF2F"}.mdi-numeric-4-box-multiple-outline::before{content:"\F3AE"}.mdi-numeric-4-box-outline::before{content:"\F3AF"}.mdi-numeric-4-circle::before{content:"\FC82"}.mdi-numeric-4-circle-outline::before{content:"\FC83"}.mdi-numeric-5::before{content:"\35"}.mdi-numeric-5-box::before{content:"\F3B0"}.mdi-numeric-5-box-multiple::before{content:"\FF30"}.mdi-numeric-5-box-multiple-outline::before{content:"\F3B1"}.mdi-numeric-5-box-outline::before{content:"\F3B2"}.mdi-numeric-5-circle::before{content:"\FC84"}.mdi-numeric-5-circle-outline::before{content:"\FC85"}.mdi-numeric-6::before{content:"\36"}.mdi-numeric-6-box::before{content:"\F3B3"}.mdi-numeric-6-box-multiple::before{content:"\FF31"}.mdi-numeric-6-box-multiple-outline::before{content:"\F3B4"}.mdi-numeric-6-box-outline::before{content:"\F3B5"}.mdi-numeric-6-circle::before{content:"\FC86"}.mdi-numeric-6-circle-outline::before{content:"\FC87"}.mdi-numeric-7::before{content:"\37"}.mdi-numeric-7-box::before{content:"\F3B6"}.mdi-numeric-7-box-multiple::before{content:"\FF32"}.mdi-numeric-7-box-multiple-outline::before{content:"\F3B7"}.mdi-numeric-7-box-outline::before{content:"\F3B8"}.mdi-numeric-7-circle::before{content:"\FC88"}.mdi-numeric-7-circle-outline::before{content:"\FC89"}.mdi-numeric-8::before{content:"\38"}.mdi-numeric-8-box::before{content:"\F3B9"}.mdi-numeric-8-box-multiple::before{content:"\FF33"}.mdi-numeric-8-box-multiple-outline::before{content:"\F3BA"}.mdi-numeric-8-box-outline::before{content:"\F3BB"}.mdi-numeric-8-circle::before{content:"\FC8A"}.mdi-numeric-8-circle-outline::before{content:"\FC8B"}.mdi-numeric-9::before{content:"\39"}.mdi-numeric-9-box::before{content:"\F3BC"}.mdi-numeric-9-box-multiple::before{content:"\FF34"}.mdi-numeric-9-box-multiple-outline::before{content:"\F3BD"}.mdi-numeric-9-box-outline::before{content:"\F3BE"}.mdi-numeric-9-circle::before{content:"\FC8C"}.mdi-numeric-9-circle-outline::before{content:"\FC8D"}.mdi-numeric-9-plus::before{content:"\F000F"}.mdi-numeric-9-plus-box::before{content:"\F3BF"}.mdi-numeric-9-plus-box-multiple::before{content:"\FF35"}.mdi-numeric-9-plus-box-multiple-outline::before{content:"\F3C0"}.mdi-numeric-9-plus-box-outline::before{content:"\F3C1"}.mdi-numeric-9-plus-circle::before{content:"\FC8E"}.mdi-numeric-9-plus-circle-outline::before{content:"\FC8F"}.mdi-numeric-negative-1::before{content:"\F0074"}.mdi-nut::before{content:"\F6F7"}.mdi-nutrition::before{content:"\F3C2"}.mdi-nuxt::before{content:"\F0131"}.mdi-oar::before{content:"\F67B"}.mdi-ocarina::before{content:"\FDBC"}.mdi-oci::before{content:"\F0314"}.mdi-ocr::before{content:"\F0165"}.mdi-octagon::before{content:"\F3C3"}.mdi-octagon-outline::before{content:"\F3C4"}.mdi-octagram::before{content:"\F6F8"}.mdi-octagram-outline::before{content:"\F774"}.mdi-odnoklassniki::before{content:"\F3C5"}.mdi-offer::before{content:"\F0246"}.mdi-office::before{content:"\F3C6"}.mdi-office-building::before{content:"\F990"}.mdi-oil::before{content:"\F3C7"}.mdi-oil-lamp::before{content:"\FF36"}.mdi-oil-level::before{content:"\F0075"}.mdi-oil-temperature::before{content:"\F0019"}.mdi-omega::before{content:"\F3C9"}.mdi-one-up::before{content:"\FB89"}.mdi-onedrive::before{content:"\F3CA"}.mdi-onenote::before{content:"\F746"}.mdi-onepassword::before{content:"\F880"}.mdi-opacity::before{content:"\F5CC"}.mdi-open-in-app::before{content:"\F3CB"}.mdi-open-in-new::before{content:"\F3CC"}.mdi-open-source-initiative::before{content:"\FB8A"}.mdi-openid::before{content:"\F3CD"}.mdi-opera::before{content:"\F3CE"}.mdi-orbit::before{content:"\F018"}.mdi-origin::before{content:"\FB2B"}.mdi-ornament::before{content:"\F3CF"}.mdi-ornament-variant::before{content:"\F3D0"}.mdi-outdoor-lamp::before{content:"\F0076"}.mdi-outlook::before{content:"\FCFE"}.mdi-overscan::before{content:"\F0027"}.mdi-owl::before{content:"\F3D2"}.mdi-pac-man::before{content:"\FB8B"}.mdi-package::before{content:"\F3D3"}.mdi-package-down::before{content:"\F3D4"}.mdi-package-up::before{content:"\F3D5"}.mdi-package-variant::before{content:"\F3D6"}.mdi-package-variant-closed::before{content:"\F3D7"}.mdi-page-first::before{content:"\F600"}.mdi-page-last::before{content:"\F601"}.mdi-page-layout-body::before{content:"\F6F9"}.mdi-page-layout-footer::before{content:"\F6FA"}.mdi-page-layout-header::before{content:"\F6FB"}.mdi-page-layout-header-footer::before{content:"\FF9C"}.mdi-page-layout-sidebar-left::before{content:"\F6FC"}.mdi-page-layout-sidebar-right::before{content:"\F6FD"}.mdi-page-next::before{content:"\FB8C"}.mdi-page-next-outline::before{content:"\FB8D"}.mdi-page-previous::before{content:"\FB8E"}.mdi-page-previous-outline::before{content:"\FB8F"}.mdi-palette::before{content:"\F3D8"}.mdi-palette-advanced::before{content:"\F3D9"}.mdi-palette-outline::before{content:"\FE6C"}.mdi-palette-swatch::before{content:"\F8B4"}.mdi-palette-swatch-outline::before{content:"\F0387"}.mdi-palm-tree::before{content:"\F0077"}.mdi-pan::before{content:"\FB90"}.mdi-pan-bottom-left::before{content:"\FB91"}.mdi-pan-bottom-right::before{content:"\FB92"}.mdi-pan-down::before{content:"\FB93"}.mdi-pan-horizontal::before{content:"\FB94"}.mdi-pan-left::before{content:"\FB95"}.mdi-pan-right::before{content:"\FB96"}.mdi-pan-top-left::before{content:"\FB97"}.mdi-pan-top-right::before{content:"\FB98"}.mdi-pan-up::before{content:"\FB99"}.mdi-pan-vertical::before{content:"\FB9A"}.mdi-panda::before{content:"\F3DA"}.mdi-pandora::before{content:"\F3DB"}.mdi-panorama::before{content:"\F3DC"}.mdi-panorama-fisheye::before{content:"\F3DD"}.mdi-panorama-horizontal::before{content:"\F3DE"}.mdi-panorama-vertical::before{content:"\F3DF"}.mdi-panorama-wide-angle::before{content:"\F3E0"}.mdi-paper-cut-vertical::before{content:"\F3E1"}.mdi-paper-roll::before{content:"\F0182"}.mdi-paper-roll-outline::before{content:"\F0183"}.mdi-paperclip::before{content:"\F3E2"}.mdi-parachute::before{content:"\FC90"}.mdi-parachute-outline::before{content:"\FC91"}.mdi-parking::before{content:"\F3E3"}.mdi-party-popper::before{content:"\F0078"}.mdi-passport::before{content:"\F7E2"}.mdi-passport-biometric::before{content:"\FDBD"}.mdi-pasta::before{content:"\F018B"}.mdi-patio-heater::before{content:"\FF9D"}.mdi-patreon::before{content:"\F881"}.mdi-pause::before{content:"\F3E4"}.mdi-pause-circle::before{content:"\F3E5"}.mdi-pause-circle-outline::before{content:"\F3E6"}.mdi-pause-octagon::before{content:"\F3E7"}.mdi-pause-octagon-outline::before{content:"\F3E8"}.mdi-paw::before{content:"\F3E9"}.mdi-paw-off::before{content:"\F657"}.mdi-paypal::before{content:"\F882"}.mdi-pdf-box::before{content:"\FE39"}.mdi-peace::before{content:"\F883"}.mdi-peanut::before{content:"\F001E"}.mdi-peanut-off::before{content:"\F001F"}.mdi-peanut-off-outline::before{content:"\F0021"}.mdi-peanut-outline::before{content:"\F0020"}.mdi-pen::before{content:"\F3EA"}.mdi-pen-lock::before{content:"\FDBE"}.mdi-pen-minus::before{content:"\FDBF"}.mdi-pen-off::before{content:"\FDC0"}.mdi-pen-plus::before{content:"\FDC1"}.mdi-pen-remove::before{content:"\FDC2"}.mdi-pencil::before{content:"\F3EB"}.mdi-pencil-box::before{content:"\F3EC"}.mdi-pencil-box-multiple::before{content:"\F016F"}.mdi-pencil-box-multiple-outline::before{content:"\F0170"}.mdi-pencil-box-outline::before{content:"\F3ED"}.mdi-pencil-circle::before{content:"\F6FE"}.mdi-pencil-circle-outline::before{content:"\F775"}.mdi-pencil-lock::before{content:"\F3EE"}.mdi-pencil-lock-outline::before{content:"\FDC3"}.mdi-pencil-minus::before{content:"\FDC4"}.mdi-pencil-minus-outline::before{content:"\FDC5"}.mdi-pencil-off::before{content:"\F3EF"}.mdi-pencil-off-outline::before{content:"\FDC6"}.mdi-pencil-outline::before{content:"\FC92"}.mdi-pencil-plus::before{content:"\FDC7"}.mdi-pencil-plus-outline::before{content:"\FDC8"}.mdi-pencil-remove::before{content:"\FDC9"}.mdi-pencil-remove-outline::before{content:"\FDCA"}.mdi-pencil-ruler::before{content:"\F037E"}.mdi-penguin::before{content:"\FEDD"}.mdi-pentagon::before{content:"\F6FF"}.mdi-pentagon-outline::before{content:"\F700"}.mdi-percent::before{content:"\F3F0"}.mdi-percent-outline::before{content:"\F02A3"}.mdi-periodic-table::before{content:"\F8B5"}.mdi-periodic-table-co::before{content:"\F0329"}.mdi-periodic-table-co2::before{content:"\F7E3"}.mdi-periscope::before{content:"\F747"}.mdi-perspective-less::before{content:"\FCFF"}.mdi-perspective-more::before{content:"\FD00"}.mdi-pharmacy::before{content:"\F3F1"}.mdi-phone::before{content:"\F3F2"}.mdi-phone-alert::before{content:"\FF37"}.mdi-phone-alert-outline::before{content:"\F01B9"}.mdi-phone-bluetooth::before{content:"\F3F3"}.mdi-phone-bluetooth-outline::before{content:"\F01BA"}.mdi-phone-cancel::before{content:"\F00E7"}.mdi-phone-cancel-outline::before{content:"\F01BB"}.mdi-phone-check::before{content:"\F01D4"}.mdi-phone-check-outline::before{content:"\F01D5"}.mdi-phone-classic::before{content:"\F602"}.mdi-phone-classic-off::before{content:"\F02A4"}.mdi-phone-forward::before{content:"\F3F4"}.mdi-phone-forward-outline::before{content:"\F01BC"}.mdi-phone-hangup::before{content:"\F3F5"}.mdi-phone-hangup-outline::before{content:"\F01BD"}.mdi-phone-in-talk::before{content:"\F3F6"}.mdi-phone-in-talk-outline::before{content:"\F01AD"}.mdi-phone-incoming::before{content:"\F3F7"}.mdi-phone-incoming-outline::before{content:"\F01BE"}.mdi-phone-lock::before{content:"\F3F8"}.mdi-phone-lock-outline::before{content:"\F01BF"}.mdi-phone-log::before{content:"\F3F9"}.mdi-phone-log-outline::before{content:"\F01C0"}.mdi-phone-message::before{content:"\F01C1"}.mdi-phone-message-outline::before{content:"\F01C2"}.mdi-phone-minus::before{content:"\F658"}.mdi-phone-minus-outline::before{content:"\F01C3"}.mdi-phone-missed::before{content:"\F3FA"}.mdi-phone-missed-outline::before{content:"\F01D0"}.mdi-phone-off::before{content:"\FDCB"}.mdi-phone-off-outline::before{content:"\F01D1"}.mdi-phone-outgoing::before{content:"\F3FB"}.mdi-phone-outgoing-outline::before{content:"\F01C4"}.mdi-phone-outline::before{content:"\FDCC"}.mdi-phone-paused::before{content:"\F3FC"}.mdi-phone-paused-outline::before{content:"\F01C5"}.mdi-phone-plus::before{content:"\F659"}.mdi-phone-plus-outline::before{content:"\F01C6"}.mdi-phone-return::before{content:"\F82E"}.mdi-phone-return-outline::before{content:"\F01C7"}.mdi-phone-ring::before{content:"\F01D6"}.mdi-phone-ring-outline::before{content:"\F01D7"}.mdi-phone-rotate-landscape::before{content:"\F884"}.mdi-phone-rotate-portrait::before{content:"\F885"}.mdi-phone-settings::before{content:"\F3FD"}.mdi-phone-settings-outline::before{content:"\F01C8"}.mdi-phone-voip::before{content:"\F3FE"}.mdi-pi::before{content:"\F3FF"}.mdi-pi-box::before{content:"\F400"}.mdi-pi-hole::before{content:"\FDCD"}.mdi-piano::before{content:"\F67C"}.mdi-pickaxe::before{content:"\F8B6"}.mdi-picture-in-picture-bottom-right::before{content:"\FE3A"}.mdi-picture-in-picture-bottom-right-outline::before{content:"\FE3B"}.mdi-picture-in-picture-top-right::before{content:"\FE3C"}.mdi-picture-in-picture-top-right-outline::before{content:"\FE3D"}.mdi-pier::before{content:"\F886"}.mdi-pier-crane::before{content:"\F887"}.mdi-pig::before{content:"\F401"}.mdi-pig-variant::before{content:"\F0028"}.mdi-piggy-bank::before{content:"\F0029"}.mdi-pill::before{content:"\F402"}.mdi-pillar::before{content:"\F701"}.mdi-pin::before{content:"\F403"}.mdi-pin-off::before{content:"\F404"}.mdi-pin-off-outline::before{content:"\F92F"}.mdi-pin-outline::before{content:"\F930"}.mdi-pine-tree::before{content:"\F405"}.mdi-pine-tree-box::before{content:"\F406"}.mdi-pinterest::before{content:"\F407"}.mdi-pinterest-box::before{content:"\F408"}.mdi-pinwheel::before{content:"\FAD4"}.mdi-pinwheel-outline::before{content:"\FAD5"}.mdi-pipe::before{content:"\F7E4"}.mdi-pipe-disconnected::before{content:"\F7E5"}.mdi-pipe-leak::before{content:"\F888"}.mdi-pipe-wrench::before{content:"\F037F"}.mdi-pirate::before{content:"\FA07"}.mdi-pistol::before{content:"\F702"}.mdi-piston::before{content:"\F889"}.mdi-pizza::before{content:"\F409"}.mdi-play::before{content:"\F40A"}.mdi-play-box::before{content:"\F02A5"}.mdi-play-box-outline::before{content:"\F40B"}.mdi-play-circle::before{content:"\F40C"}.mdi-play-circle-outline::before{content:"\F40D"}.mdi-play-network::before{content:"\F88A"}.mdi-play-network-outline::before{content:"\FC93"}.mdi-play-outline::before{content:"\FF38"}.mdi-play-pause::before{content:"\F40E"}.mdi-play-protected-content::before{content:"\F40F"}.mdi-play-speed::before{content:"\F8FE"}.mdi-playlist-check::before{content:"\F5C7"}.mdi-playlist-edit::before{content:"\F8FF"}.mdi-playlist-minus::before{content:"\F410"}.mdi-playlist-music::before{content:"\FC94"}.mdi-playlist-music-outline::before{content:"\FC95"}.mdi-playlist-play::before{content:"\F411"}.mdi-playlist-plus::before{content:"\F412"}.mdi-playlist-remove::before{content:"\F413"}.mdi-playlist-star::before{content:"\FDCE"}.mdi-playstation::before{content:"\F414"}.mdi-plex::before{content:"\F6B9"}.mdi-plus::before{content:"\F415"}.mdi-plus-box::before{content:"\F416"}.mdi-plus-box-multiple::before{content:"\F334"}.mdi-plus-box-multiple-outline::before{content:"\F016E"}.mdi-plus-box-outline::before{content:"\F703"}.mdi-plus-circle::before{content:"\F417"}.mdi-plus-circle-multiple-outline::before{content:"\F418"}.mdi-plus-circle-outline::before{content:"\F419"}.mdi-plus-minus::before{content:"\F991"}.mdi-plus-minus-box::before{content:"\F992"}.mdi-plus-network::before{content:"\F41A"}.mdi-plus-network-outline::before{content:"\FC96"}.mdi-plus-one::before{content:"\F41B"}.mdi-plus-outline::before{content:"\F704"}.mdi-plus-thick::before{content:"\F0217"}.mdi-pocket::before{content:"\F41C"}.mdi-podcast::before{content:"\F993"}.mdi-podium::before{content:"\FD01"}.mdi-podium-bronze::before{content:"\FD02"}.mdi-podium-gold::before{content:"\FD03"}.mdi-podium-silver::before{content:"\FD04"}.mdi-point-of-sale::before{content:"\FD6E"}.mdi-pokeball::before{content:"\F41D"}.mdi-pokemon-go::before{content:"\FA08"}.mdi-poker-chip::before{content:"\F82F"}.mdi-polaroid::before{content:"\F41E"}.mdi-police-badge::before{content:"\F0192"}.mdi-police-badge-outline::before{content:"\F0193"}.mdi-poll::before{content:"\F41F"}.mdi-poll-box::before{content:"\F420"}.mdi-poll-box-outline::before{content:"\F02A6"}.mdi-polymer::before{content:"\F421"}.mdi-pool::before{content:"\F606"}.mdi-popcorn::before{content:"\F422"}.mdi-post::before{content:"\F002A"}.mdi-post-outline::before{content:"\F002B"}.mdi-postage-stamp::before{content:"\FC97"}.mdi-pot::before{content:"\F65A"}.mdi-pot-mix::before{content:"\F65B"}.mdi-pound::before{content:"\F423"}.mdi-pound-box::before{content:"\F424"}.mdi-pound-box-outline::before{content:"\F01AA"}.mdi-power::before{content:"\F425"}.mdi-power-cycle::before{content:"\F900"}.mdi-power-off::before{content:"\F901"}.mdi-power-on::before{content:"\F902"}.mdi-power-plug::before{content:"\F6A4"}.mdi-power-plug-off::before{content:"\F6A5"}.mdi-power-settings::before{content:"\F426"}.mdi-power-sleep::before{content:"\F903"}.mdi-power-socket::before{content:"\F427"}.mdi-power-socket-au::before{content:"\F904"}.mdi-power-socket-de::before{content:"\F0132"}.mdi-power-socket-eu::before{content:"\F7E6"}.mdi-power-socket-fr::before{content:"\F0133"}.mdi-power-socket-jp::before{content:"\F0134"}.mdi-power-socket-uk::before{content:"\F7E7"}.mdi-power-socket-us::before{content:"\F7E8"}.mdi-power-standby::before{content:"\F905"}.mdi-powershell::before{content:"\FA09"}.mdi-prescription::before{content:"\F705"}.mdi-presentation::before{content:"\F428"}.mdi-presentation-play::before{content:"\F429"}.mdi-printer::before{content:"\F42A"}.mdi-printer-3d::before{content:"\F42B"}.mdi-printer-3d-nozzle::before{content:"\FE3E"}.mdi-printer-3d-nozzle-alert::before{content:"\F01EB"}.mdi-printer-3d-nozzle-alert-outline::before{content:"\F01EC"}.mdi-printer-3d-nozzle-outline::before{content:"\FE3F"}.mdi-printer-alert::before{content:"\F42C"}.mdi-printer-check::before{content:"\F0171"}.mdi-printer-off::before{content:"\FE40"}.mdi-printer-pos::before{content:"\F0079"}.mdi-printer-settings::before{content:"\F706"}.mdi-printer-wireless::before{content:"\FA0A"}.mdi-priority-high::before{content:"\F603"}.mdi-priority-low::before{content:"\F604"}.mdi-professional-hexagon::before{content:"\F42D"}.mdi-progress-alert::before{content:"\FC98"}.mdi-progress-check::before{content:"\F994"}.mdi-progress-clock::before{content:"\F995"}.mdi-progress-close::before{content:"\F0135"}.mdi-progress-download::before{content:"\F996"}.mdi-progress-upload::before{content:"\F997"}.mdi-progress-wrench::before{content:"\FC99"}.mdi-projector::before{content:"\F42E"}.mdi-projector-screen::before{content:"\F42F"}.mdi-propane-tank::before{content:"\F0382"}.mdi-propane-tank-outline::before{content:"\F0383"}.mdi-protocol::before{content:"\FFF9"}.mdi-publish::before{content:"\F6A6"}.mdi-pulse::before{content:"\F430"}.mdi-pumpkin::before{content:"\FB9B"}.mdi-purse::before{content:"\FF39"}.mdi-purse-outline::before{content:"\FF3A"}.mdi-puzzle::before{content:"\F431"}.mdi-puzzle-outline::before{content:"\FA65"}.mdi-qi::before{content:"\F998"}.mdi-qqchat::before{content:"\F605"}.mdi-qrcode::before{content:"\F432"}.mdi-qrcode-edit::before{content:"\F8B7"}.mdi-qrcode-minus::before{content:"\F01B7"}.mdi-qrcode-plus::before{content:"\F01B6"}.mdi-qrcode-remove::before{content:"\F01B8"}.mdi-qrcode-scan::before{content:"\F433"}.mdi-quadcopter::before{content:"\F434"}.mdi-quality-high::before{content:"\F435"}.mdi-quality-low::before{content:"\FA0B"}.mdi-quality-medium::before{content:"\FA0C"}.mdi-quicktime::before{content:"\F436"}.mdi-quora::before{content:"\FD05"}.mdi-rabbit::before{content:"\F906"}.mdi-racing-helmet::before{content:"\FD6F"}.mdi-racquetball::before{content:"\FD70"}.mdi-radar::before{content:"\F437"}.mdi-radiator::before{content:"\F438"}.mdi-radiator-disabled::before{content:"\FAD6"}.mdi-radiator-off::before{content:"\FAD7"}.mdi-radio::before{content:"\F439"}.mdi-radio-am::before{content:"\FC9A"}.mdi-radio-fm::before{content:"\FC9B"}.mdi-radio-handheld::before{content:"\F43A"}.mdi-radio-off::before{content:"\F0247"}.mdi-radio-tower::before{content:"\F43B"}.mdi-radioactive::before{content:"\F43C"}.mdi-radioactive-off::before{content:"\FEDE"}.mdi-radiobox-blank::before{content:"\F43D"}.mdi-radiobox-marked::before{content:"\F43E"}.mdi-radius::before{content:"\FC9C"}.mdi-radius-outline::before{content:"\FC9D"}.mdi-railroad-light::before{content:"\FF3B"}.mdi-raspberry-pi::before{content:"\F43F"}.mdi-ray-end::before{content:"\F440"}.mdi-ray-end-arrow::before{content:"\F441"}.mdi-ray-start::before{content:"\F442"}.mdi-ray-start-arrow::before{content:"\F443"}.mdi-ray-start-end::before{content:"\F444"}.mdi-ray-vertex::before{content:"\F445"}.mdi-react::before{content:"\F707"}.mdi-read::before{content:"\F447"}.mdi-receipt::before{content:"\F449"}.mdi-record::before{content:"\F44A"}.mdi-record-circle::before{content:"\FEDF"}.mdi-record-circle-outline::before{content:"\FEE0"}.mdi-record-player::before{content:"\F999"}.mdi-record-rec::before{content:"\F44B"}.mdi-rectangle::before{content:"\FE41"}.mdi-rectangle-outline::before{content:"\FE42"}.mdi-recycle::before{content:"\F44C"}.mdi-reddit::before{content:"\F44D"}.mdi-redhat::before{content:"\F0146"}.mdi-redo::before{content:"\F44E"}.mdi-redo-variant::before{content:"\F44F"}.mdi-reflect-horizontal::before{content:"\FA0D"}.mdi-reflect-vertical::before{content:"\FA0E"}.mdi-refresh::before{content:"\F450"}.mdi-refresh-circle::before{content:"\F03A2"}.mdi-regex::before{content:"\F451"}.mdi-registered-trademark::before{content:"\FA66"}.mdi-relative-scale::before{content:"\F452"}.mdi-reload::before{content:"\F453"}.mdi-reload-alert::before{content:"\F0136"}.mdi-reminder::before{content:"\F88B"}.mdi-remote::before{content:"\F454"}.mdi-remote-desktop::before{content:"\F8B8"}.mdi-remote-off::before{content:"\FEE1"}.mdi-remote-tv::before{content:"\FEE2"}.mdi-remote-tv-off::before{content:"\FEE3"}.mdi-rename-box::before{content:"\F455"}.mdi-reorder-horizontal::before{content:"\F687"}.mdi-reorder-vertical::before{content:"\F688"}.mdi-repeat::before{content:"\F456"}.mdi-repeat-off::before{content:"\F457"}.mdi-repeat-once::before{content:"\F458"}.mdi-replay::before{content:"\F459"}.mdi-reply::before{content:"\F45A"}.mdi-reply-all::before{content:"\F45B"}.mdi-reply-all-outline::before{content:"\FF3C"}.mdi-reply-circle::before{content:"\F01D9"}.mdi-reply-outline::before{content:"\FF3D"}.mdi-reproduction::before{content:"\F45C"}.mdi-resistor::before{content:"\FB1F"}.mdi-resistor-nodes::before{content:"\FB20"}.mdi-resize::before{content:"\FA67"}.mdi-resize-bottom-right::before{content:"\F45D"}.mdi-responsive::before{content:"\F45E"}.mdi-restart::before{content:"\F708"}.mdi-restart-alert::before{content:"\F0137"}.mdi-restart-off::before{content:"\FD71"}.mdi-restore::before{content:"\F99A"}.mdi-restore-alert::before{content:"\F0138"}.mdi-rewind::before{content:"\F45F"}.mdi-rewind-10::before{content:"\FD06"}.mdi-rewind-30::before{content:"\FD72"}.mdi-rewind-5::before{content:"\F0224"}.mdi-rewind-outline::before{content:"\F709"}.mdi-rhombus::before{content:"\F70A"}.mdi-rhombus-medium::before{content:"\FA0F"}.mdi-rhombus-outline::before{content:"\F70B"}.mdi-rhombus-split::before{content:"\FA10"}.mdi-ribbon::before{content:"\F460"}.mdi-rice::before{content:"\F7E9"}.mdi-ring::before{content:"\F7EA"}.mdi-rivet::before{content:"\FE43"}.mdi-road::before{content:"\F461"}.mdi-road-variant::before{content:"\F462"}.mdi-robber::before{content:"\F007A"}.mdi-robot::before{content:"\F6A8"}.mdi-robot-industrial::before{content:"\FB21"}.mdi-robot-mower::before{content:"\F0222"}.mdi-robot-mower-outline::before{content:"\F021E"}.mdi-robot-vacuum::before{content:"\F70C"}.mdi-robot-vacuum-variant::before{content:"\F907"}.mdi-rocket::before{content:"\F463"}.mdi-rodent::before{content:"\F0352"}.mdi-roller-skate::before{content:"\FD07"}.mdi-rollerblade::before{content:"\FD08"}.mdi-rollupjs::before{content:"\FB9C"}.mdi-roman-numeral-1::before{content:"\F00B3"}.mdi-roman-numeral-10::before{content:"\F00BC"}.mdi-roman-numeral-2::before{content:"\F00B4"}.mdi-roman-numeral-3::before{content:"\F00B5"}.mdi-roman-numeral-4::before{content:"\F00B6"}.mdi-roman-numeral-5::before{content:"\F00B7"}.mdi-roman-numeral-6::before{content:"\F00B8"}.mdi-roman-numeral-7::before{content:"\F00B9"}.mdi-roman-numeral-8::before{content:"\F00BA"}.mdi-roman-numeral-9::before{content:"\F00BB"}.mdi-room-service::before{content:"\F88C"}.mdi-room-service-outline::before{content:"\FD73"}.mdi-rotate-3d::before{content:"\FEE4"}.mdi-rotate-3d-variant::before{content:"\F464"}.mdi-rotate-left::before{content:"\F465"}.mdi-rotate-left-variant::before{content:"\F466"}.mdi-rotate-orbit::before{content:"\FD74"}.mdi-rotate-right::before{content:"\F467"}.mdi-rotate-right-variant::before{content:"\F468"}.mdi-rounded-corner::before{content:"\F607"}.mdi-router::before{content:"\F020D"}.mdi-router-wireless::before{content:"\F469"}.mdi-router-wireless-settings::before{content:"\FA68"}.mdi-routes::before{content:"\F46A"}.mdi-routes-clock::before{content:"\F007B"}.mdi-rowing::before{content:"\F608"}.mdi-rss::before{content:"\F46B"}.mdi-rss-box::before{content:"\F46C"}.mdi-rss-off::before{content:"\FF3E"}.mdi-ruby::before{content:"\FD09"}.mdi-rugby::before{content:"\FD75"}.mdi-ruler::before{content:"\F46D"}.mdi-ruler-square::before{content:"\FC9E"}.mdi-ruler-square-compass::before{content:"\FEDB"}.mdi-run::before{content:"\F70D"}.mdi-run-fast::before{content:"\F46E"}.mdi-rv-truck::before{content:"\F01FF"}.mdi-sack::before{content:"\FD0A"}.mdi-sack-percent::before{content:"\FD0B"}.mdi-safe::before{content:"\FA69"}.mdi-safe-square::before{content:"\F02A7"}.mdi-safe-square-outline::before{content:"\F02A8"}.mdi-safety-goggles::before{content:"\FD0C"}.mdi-sailing::before{content:"\FEE5"}.mdi-sale::before{content:"\F46F"}.mdi-salesforce::before{content:"\F88D"}.mdi-sass::before{content:"\F7EB"}.mdi-satellite::before{content:"\F470"}.mdi-satellite-uplink::before{content:"\F908"}.mdi-satellite-variant::before{content:"\F471"}.mdi-sausage::before{content:"\F8B9"}.mdi-saw-blade::before{content:"\FE44"}.mdi-saxophone::before{content:"\F609"}.mdi-scale::before{content:"\F472"}.mdi-scale-balance::before{content:"\F5D1"}.mdi-scale-bathroom::before{content:"\F473"}.mdi-scale-off::before{content:"\F007C"}.mdi-scanner::before{content:"\F6AA"}.mdi-scanner-off::before{content:"\F909"}.mdi-scatter-plot::before{content:"\FEE6"}.mdi-scatter-plot-outline::before{content:"\FEE7"}.mdi-school::before{content:"\F474"}.mdi-school-outline::before{content:"\F01AB"}.mdi-scissors-cutting::before{content:"\FA6A"}.mdi-scooter::before{content:"\F0214"}.mdi-scoreboard::before{content:"\F02A9"}.mdi-scoreboard-outline::before{content:"\F02AA"}.mdi-screen-rotation::before{content:"\F475"}.mdi-screen-rotation-lock::before{content:"\F476"}.mdi-screw-flat-top::before{content:"\FDCF"}.mdi-screw-lag::before{content:"\FE54"}.mdi-screw-machine-flat-top::before{content:"\FE55"}.mdi-screw-machine-round-top::before{content:"\FE56"}.mdi-screw-round-top::before{content:"\FE57"}.mdi-screwdriver::before{content:"\F477"}.mdi-script::before{content:"\FB9D"}.mdi-script-outline::before{content:"\F478"}.mdi-script-text::before{content:"\FB9E"}.mdi-script-text-outline::before{content:"\FB9F"}.mdi-sd::before{content:"\F479"}.mdi-seal::before{content:"\F47A"}.mdi-seal-variant::before{content:"\FFFA"}.mdi-search-web::before{content:"\F70E"}.mdi-seat::before{content:"\FC9F"}.mdi-seat-flat::before{content:"\F47B"}.mdi-seat-flat-angled::before{content:"\F47C"}.mdi-seat-individual-suite::before{content:"\F47D"}.mdi-seat-legroom-extra::before{content:"\F47E"}.mdi-seat-legroom-normal::before{content:"\F47F"}.mdi-seat-legroom-reduced::before{content:"\F480"}.mdi-seat-outline::before{content:"\FCA0"}.mdi-seat-passenger::before{content:"\F0274"}.mdi-seat-recline-extra::before{content:"\F481"}.mdi-seat-recline-normal::before{content:"\F482"}.mdi-seatbelt::before{content:"\FCA1"}.mdi-security::before{content:"\F483"}.mdi-security-network::before{content:"\F484"}.mdi-seed::before{content:"\FE45"}.mdi-seed-outline::before{content:"\FE46"}.mdi-segment::before{content:"\FEE8"}.mdi-select::before{content:"\F485"}.mdi-select-all::before{content:"\F486"}.mdi-select-color::before{content:"\FD0D"}.mdi-select-compare::before{content:"\FAD8"}.mdi-select-drag::before{content:"\FA6B"}.mdi-select-group::before{content:"\FF9F"}.mdi-select-inverse::before{content:"\F487"}.mdi-select-marker::before{content:"\F02AB"}.mdi-select-multiple::before{content:"\F02AC"}.mdi-select-multiple-marker::before{content:"\F02AD"}.mdi-select-off::before{content:"\F488"}.mdi-select-place::before{content:"\FFFB"}.mdi-select-search::before{content:"\F022F"}.mdi-selection::before{content:"\F489"}.mdi-selection-drag::before{content:"\FA6C"}.mdi-selection-ellipse::before{content:"\FD0E"}.mdi-selection-ellipse-arrow-inside::before{content:"\FF3F"}.mdi-selection-marker::before{content:"\F02AE"}.mdi-selection-multiple-marker::before{content:"\F02AF"}.mdi-selection-mutliple::before{content:"\F02B0"}.mdi-selection-off::before{content:"\F776"}.mdi-selection-search::before{content:"\F0230"}.mdi-semantic-web::before{content:"\F0341"}.mdi-send::before{content:"\F48A"}.mdi-send-check::before{content:"\F018C"}.mdi-send-check-outline::before{content:"\F018D"}.mdi-send-circle::before{content:"\FE58"}.mdi-send-circle-outline::before{content:"\FE59"}.mdi-send-clock::before{content:"\F018E"}.mdi-send-clock-outline::before{content:"\F018F"}.mdi-send-lock::before{content:"\F7EC"}.mdi-send-lock-outline::before{content:"\F0191"}.mdi-send-outline::before{content:"\F0190"}.mdi-serial-port::before{content:"\F65C"}.mdi-server::before{content:"\F48B"}.mdi-server-minus::before{content:"\F48C"}.mdi-server-network::before{content:"\F48D"}.mdi-server-network-off::before{content:"\F48E"}.mdi-server-off::before{content:"\F48F"}.mdi-server-plus::before{content:"\F490"}.mdi-server-remove::before{content:"\F491"}.mdi-server-security::before{content:"\F492"}.mdi-set-all::before{content:"\F777"}.mdi-set-center::before{content:"\F778"}.mdi-set-center-right::before{content:"\F779"}.mdi-set-left::before{content:"\F77A"}.mdi-set-left-center::before{content:"\F77B"}.mdi-set-left-right::before{content:"\F77C"}.mdi-set-none::before{content:"\F77D"}.mdi-set-right::before{content:"\F77E"}.mdi-set-top-box::before{content:"\F99E"}.mdi-settings::before{content:"\F493"}.mdi-settings-box::before{content:"\F494"}.mdi-settings-helper::before{content:"\FA6D"}.mdi-settings-outline::before{content:"\F8BA"}.mdi-settings-transfer::before{content:"\F007D"}.mdi-settings-transfer-outline::before{content:"\F007E"}.mdi-shaker::before{content:"\F0139"}.mdi-shaker-outline::before{content:"\F013A"}.mdi-shape::before{content:"\F830"}.mdi-shape-circle-plus::before{content:"\F65D"}.mdi-shape-outline::before{content:"\F831"}.mdi-shape-oval-plus::before{content:"\F0225"}.mdi-shape-plus::before{content:"\F495"}.mdi-shape-polygon-plus::before{content:"\F65E"}.mdi-shape-rectangle-plus::before{content:"\F65F"}.mdi-shape-square-plus::before{content:"\F660"}.mdi-share::before{content:"\F496"}.mdi-share-all::before{content:"\F021F"}.mdi-share-all-outline::before{content:"\F0220"}.mdi-share-circle::before{content:"\F01D8"}.mdi-share-off::before{content:"\FF40"}.mdi-share-off-outline::before{content:"\FF41"}.mdi-share-outline::before{content:"\F931"}.mdi-share-variant::before{content:"\F497"}.mdi-sheep::before{content:"\FCA2"}.mdi-shield::before{content:"\F498"}.mdi-shield-account::before{content:"\F88E"}.mdi-shield-account-outline::before{content:"\FA11"}.mdi-shield-airplane::before{content:"\F6BA"}.mdi-shield-airplane-outline::before{content:"\FCA3"}.mdi-shield-alert::before{content:"\FEE9"}.mdi-shield-alert-outline::before{content:"\FEEA"}.mdi-shield-car::before{content:"\FFA0"}.mdi-shield-check::before{content:"\F565"}.mdi-shield-check-outline::before{content:"\FCA4"}.mdi-shield-cross::before{content:"\FCA5"}.mdi-shield-cross-outline::before{content:"\FCA6"}.mdi-shield-edit::before{content:"\F01CB"}.mdi-shield-edit-outline::before{content:"\F01CC"}.mdi-shield-half::before{content:"\F038B"}.mdi-shield-half-full::before{content:"\F77F"}.mdi-shield-home::before{content:"\F689"}.mdi-shield-home-outline::before{content:"\FCA7"}.mdi-shield-key::before{content:"\FBA0"}.mdi-shield-key-outline::before{content:"\FBA1"}.mdi-shield-link-variant::before{content:"\FD0F"}.mdi-shield-link-variant-outline::before{content:"\FD10"}.mdi-shield-lock::before{content:"\F99C"}.mdi-shield-lock-outline::before{content:"\FCA8"}.mdi-shield-off::before{content:"\F99D"}.mdi-shield-off-outline::before{content:"\F99B"}.mdi-shield-outline::before{content:"\F499"}.mdi-shield-plus::before{content:"\FAD9"}.mdi-shield-plus-outline::before{content:"\FADA"}.mdi-shield-refresh::before{content:"\F01CD"}.mdi-shield-refresh-outline::before{content:"\F01CE"}.mdi-shield-remove::before{content:"\FADB"}.mdi-shield-remove-outline::before{content:"\FADC"}.mdi-shield-search::before{content:"\FD76"}.mdi-shield-star::before{content:"\F0166"}.mdi-shield-star-outline::before{content:"\F0167"}.mdi-shield-sun::before{content:"\F007F"}.mdi-shield-sun-outline::before{content:"\F0080"}.mdi-ship-wheel::before{content:"\F832"}.mdi-shoe-formal::before{content:"\FB22"}.mdi-shoe-heel::before{content:"\FB23"}.mdi-shoe-print::before{content:"\FE5A"}.mdi-shopify::before{content:"\FADD"}.mdi-shopping::before{content:"\F49A"}.mdi-shopping-music::before{content:"\F49B"}.mdi-shopping-outline::before{content:"\F0200"}.mdi-shopping-search::before{content:"\FFA1"}.mdi-shovel::before{content:"\F70F"}.mdi-shovel-off::before{content:"\F710"}.mdi-shower::before{content:"\F99F"}.mdi-shower-head::before{content:"\F9A0"}.mdi-shredder::before{content:"\F49C"}.mdi-shuffle::before{content:"\F49D"}.mdi-shuffle-disabled::before{content:"\F49E"}.mdi-shuffle-variant::before{content:"\F49F"}.mdi-shuriken::before{content:"\F03AA"}.mdi-sigma::before{content:"\F4A0"}.mdi-sigma-lower::before{content:"\F62B"}.mdi-sign-caution::before{content:"\F4A1"}.mdi-sign-direction::before{content:"\F780"}.mdi-sign-direction-minus::before{content:"\F0022"}.mdi-sign-direction-plus::before{content:"\FFFD"}.mdi-sign-direction-remove::before{content:"\FFFE"}.mdi-sign-real-estate::before{content:"\F0143"}.mdi-sign-text::before{content:"\F781"}.mdi-signal::before{content:"\F4A2"}.mdi-signal-2g::before{content:"\F711"}.mdi-signal-3g::before{content:"\F712"}.mdi-signal-4g::before{content:"\F713"}.mdi-signal-5g::before{content:"\FA6E"}.mdi-signal-cellular-1::before{content:"\F8BB"}.mdi-signal-cellular-2::before{content:"\F8BC"}.mdi-signal-cellular-3::before{content:"\F8BD"}.mdi-signal-cellular-outline::before{content:"\F8BE"}.mdi-signal-distance-variant::before{content:"\FE47"}.mdi-signal-hspa::before{content:"\F714"}.mdi-signal-hspa-plus::before{content:"\F715"}.mdi-signal-off::before{content:"\F782"}.mdi-signal-variant::before{content:"\F60A"}.mdi-signature::before{content:"\FE5B"}.mdi-signature-freehand::before{content:"\FE5C"}.mdi-signature-image::before{content:"\FE5D"}.mdi-signature-text::before{content:"\FE5E"}.mdi-silo::before{content:"\FB24"}.mdi-silverware::before{content:"\F4A3"}.mdi-silverware-clean::before{content:"\FFFF"}.mdi-silverware-fork::before{content:"\F4A4"}.mdi-silverware-fork-knife::before{content:"\FA6F"}.mdi-silverware-spoon::before{content:"\F4A5"}.mdi-silverware-variant::before{content:"\F4A6"}.mdi-sim::before{content:"\F4A7"}.mdi-sim-alert::before{content:"\F4A8"}.mdi-sim-off::before{content:"\F4A9"}.mdi-simple-icons::before{content:"\F0348"}.mdi-sina-weibo::before{content:"\FADE"}.mdi-sitemap::before{content:"\F4AA"}.mdi-skate::before{content:"\FD11"}.mdi-skew-less::before{content:"\FD12"}.mdi-skew-more::before{content:"\FD13"}.mdi-ski::before{content:"\F032F"}.mdi-ski-cross-country::before{content:"\F0330"}.mdi-ski-water::before{content:"\F0331"}.mdi-skip-backward::before{content:"\F4AB"}.mdi-skip-backward-outline::before{content:"\FF42"}.mdi-skip-forward::before{content:"\F4AC"}.mdi-skip-forward-outline::before{content:"\FF43"}.mdi-skip-next::before{content:"\F4AD"}.mdi-skip-next-circle::before{content:"\F661"}.mdi-skip-next-circle-outline::before{content:"\F662"}.mdi-skip-next-outline::before{content:"\FF44"}.mdi-skip-previous::before{content:"\F4AE"}.mdi-skip-previous-circle::before{content:"\F663"}.mdi-skip-previous-circle-outline::before{content:"\F664"}.mdi-skip-previous-outline::before{content:"\FF45"}.mdi-skull::before{content:"\F68B"}.mdi-skull-crossbones::before{content:"\FBA2"}.mdi-skull-crossbones-outline::before{content:"\FBA3"}.mdi-skull-outline::before{content:"\FBA4"}.mdi-skype::before{content:"\F4AF"}.mdi-skype-business::before{content:"\F4B0"}.mdi-slack::before{content:"\F4B1"}.mdi-slackware::before{content:"\F90A"}.mdi-slash-forward::before{content:"\F0000"}.mdi-slash-forward-box::before{content:"\F0001"}.mdi-sleep::before{content:"\F4B2"}.mdi-sleep-off::before{content:"\F4B3"}.mdi-slope-downhill::before{content:"\FE5F"}.mdi-slope-uphill::before{content:"\FE60"}.mdi-slot-machine::before{content:"\F013F"}.mdi-slot-machine-outline::before{content:"\F0140"}.mdi-smart-card::before{content:"\F00E8"}.mdi-smart-card-outline::before{content:"\F00E9"}.mdi-smart-card-reader::before{content:"\F00EA"}.mdi-smart-card-reader-outline::before{content:"\F00EB"}.mdi-smog::before{content:"\FA70"}.mdi-smoke-detector::before{content:"\F392"}.mdi-smoking::before{content:"\F4B4"}.mdi-smoking-off::before{content:"\F4B5"}.mdi-snapchat::before{content:"\F4B6"}.mdi-snowboard::before{content:"\F0332"}.mdi-snowflake::before{content:"\F716"}.mdi-snowflake-alert::before{content:"\FF46"}.mdi-snowflake-melt::before{content:"\F02F6"}.mdi-snowflake-variant::before{content:"\FF47"}.mdi-snowman::before{content:"\F4B7"}.mdi-soccer::before{content:"\F4B8"}.mdi-soccer-field::before{content:"\F833"}.mdi-sofa::before{content:"\F4B9"}.mdi-solar-panel::before{content:"\FD77"}.mdi-solar-panel-large::before{content:"\FD78"}.mdi-solar-power::before{content:"\FA71"}.mdi-soldering-iron::before{content:"\F00BD"}.mdi-solid::before{content:"\F68C"}.mdi-sort::before{content:"\F4BA"}.mdi-sort-alphabetical::before{content:"\F4BB"}.mdi-sort-alphabetical-ascending::before{content:"\F0173"}.mdi-sort-alphabetical-descending::before{content:"\F0174"}.mdi-sort-ascending::before{content:"\F4BC"}.mdi-sort-descending::before{content:"\F4BD"}.mdi-sort-numeric::before{content:"\F4BE"}.mdi-sort-variant::before{content:"\F4BF"}.mdi-sort-variant-lock::before{content:"\FCA9"}.mdi-sort-variant-lock-open::before{content:"\FCAA"}.mdi-sort-variant-remove::before{content:"\F0172"}.mdi-soundcloud::before{content:"\F4C0"}.mdi-source-branch::before{content:"\F62C"}.mdi-source-commit::before{content:"\F717"}.mdi-source-commit-end::before{content:"\F718"}.mdi-source-commit-end-local::before{content:"\F719"}.mdi-source-commit-local::before{content:"\F71A"}.mdi-source-commit-next-local::before{content:"\F71B"}.mdi-source-commit-start::before{content:"\F71C"}.mdi-source-commit-start-next-local::before{content:"\F71D"}.mdi-source-fork::before{content:"\F4C1"}.mdi-source-merge::before{content:"\F62D"}.mdi-source-pull::before{content:"\F4C2"}.mdi-source-repository::before{content:"\FCAB"}.mdi-source-repository-multiple::before{content:"\FCAC"}.mdi-soy-sauce::before{content:"\F7ED"}.mdi-spa::before{content:"\FCAD"}.mdi-spa-outline::before{content:"\FCAE"}.mdi-space-invaders::before{content:"\FBA5"}.mdi-space-station::before{content:"\F03AE"}.mdi-spade::before{content:"\FE48"}.mdi-speaker::before{content:"\F4C3"}.mdi-speaker-bluetooth::before{content:"\F9A1"}.mdi-speaker-multiple::before{content:"\FD14"}.mdi-speaker-off::before{content:"\F4C4"}.mdi-speaker-wireless::before{content:"\F71E"}.mdi-speedometer::before{content:"\F4C5"}.mdi-speedometer-medium::before{content:"\FFA2"}.mdi-speedometer-slow::before{content:"\FFA3"}.mdi-spellcheck::before{content:"\F4C6"}.mdi-spider::before{content:"\F0215"}.mdi-spider-thread::before{content:"\F0216"}.mdi-spider-web::before{content:"\FBA6"}.mdi-spotify::before{content:"\F4C7"}.mdi-spotlight::before{content:"\F4C8"}.mdi-spotlight-beam::before{content:"\F4C9"}.mdi-spray::before{content:"\F665"}.mdi-spray-bottle::before{content:"\FADF"}.mdi-sprinkler::before{content:"\F0081"}.mdi-sprinkler-variant::before{content:"\F0082"}.mdi-sprout::before{content:"\FE49"}.mdi-sprout-outline::before{content:"\FE4A"}.mdi-square::before{content:"\F763"}.mdi-square-edit-outline::before{content:"\F90B"}.mdi-square-inc::before{content:"\F4CA"}.mdi-square-inc-cash::before{content:"\F4CB"}.mdi-square-medium::before{content:"\FA12"}.mdi-square-medium-outline::before{content:"\FA13"}.mdi-square-off::before{content:"\F0319"}.mdi-square-off-outline::before{content:"\F031A"}.mdi-square-outline::before{content:"\F762"}.mdi-square-root::before{content:"\F783"}.mdi-square-root-box::before{content:"\F9A2"}.mdi-square-small::before{content:"\FA14"}.mdi-squeegee::before{content:"\FAE0"}.mdi-ssh::before{content:"\F8BF"}.mdi-stack-exchange::before{content:"\F60B"}.mdi-stack-overflow::before{content:"\F4CC"}.mdi-stackpath::before{content:"\F359"}.mdi-stadium::before{content:"\F001A"}.mdi-stadium-variant::before{content:"\F71F"}.mdi-stairs::before{content:"\F4CD"}.mdi-stairs-down::before{content:"\F02E9"}.mdi-stairs-up::before{content:"\F02E8"}.mdi-stamper::before{content:"\FD15"}.mdi-standard-definition::before{content:"\F7EE"}.mdi-star::before{content:"\F4CE"}.mdi-star-box::before{content:"\FA72"}.mdi-star-box-multiple::before{content:"\F02B1"}.mdi-star-box-multiple-outline::before{content:"\F02B2"}.mdi-star-box-outline::before{content:"\FA73"}.mdi-star-circle::before{content:"\F4CF"}.mdi-star-circle-outline::before{content:"\F9A3"}.mdi-star-face::before{content:"\F9A4"}.mdi-star-four-points::before{content:"\FAE1"}.mdi-star-four-points-outline::before{content:"\FAE2"}.mdi-star-half::before{content:"\F4D0"}.mdi-star-off::before{content:"\F4D1"}.mdi-star-outline::before{content:"\F4D2"}.mdi-star-three-points::before{content:"\FAE3"}.mdi-star-three-points-outline::before{content:"\FAE4"}.mdi-state-machine::before{content:"\F021A"}.mdi-steam::before{content:"\F4D3"}.mdi-steam-box::before{content:"\F90C"}.mdi-steering::before{content:"\F4D4"}.mdi-steering-off::before{content:"\F90D"}.mdi-step-backward::before{content:"\F4D5"}.mdi-step-backward-2::before{content:"\F4D6"}.mdi-step-forward::before{content:"\F4D7"}.mdi-step-forward-2::before{content:"\F4D8"}.mdi-stethoscope::before{content:"\F4D9"}.mdi-sticker::before{content:"\F038F"}.mdi-sticker-alert::before{content:"\F0390"}.mdi-sticker-alert-outline::before{content:"\F0391"}.mdi-sticker-check::before{content:"\F0392"}.mdi-sticker-check-outline::before{content:"\F0393"}.mdi-sticker-circle-outline::before{content:"\F5D0"}.mdi-sticker-emoji::before{content:"\F784"}.mdi-sticker-minus::before{content:"\F0394"}.mdi-sticker-minus-outline::before{content:"\F0395"}.mdi-sticker-outline::before{content:"\F0396"}.mdi-sticker-plus::before{content:"\F0397"}.mdi-sticker-plus-outline::before{content:"\F0398"}.mdi-sticker-remove::before{content:"\F0399"}.mdi-sticker-remove-outline::before{content:"\F039A"}.mdi-stocking::before{content:"\F4DA"}.mdi-stomach::before{content:"\F00BE"}.mdi-stop::before{content:"\F4DB"}.mdi-stop-circle::before{content:"\F666"}.mdi-stop-circle-outline::before{content:"\F667"}.mdi-store::before{content:"\F4DC"}.mdi-store-24-hour::before{content:"\F4DD"}.mdi-store-outline::before{content:"\F038C"}.mdi-storefront::before{content:"\F00EC"}.mdi-stove::before{content:"\F4DE"}.mdi-strategy::before{content:"\F0201"}.mdi-strava::before{content:"\FB25"}.mdi-stretch-to-page::before{content:"\FF48"}.mdi-stretch-to-page-outline::before{content:"\FF49"}.mdi-string-lights::before{content:"\F02E5"}.mdi-string-lights-off::before{content:"\F02E6"}.mdi-subdirectory-arrow-left::before{content:"\F60C"}.mdi-subdirectory-arrow-right::before{content:"\F60D"}.mdi-subtitles::before{content:"\FA15"}.mdi-subtitles-outline::before{content:"\FA16"}.mdi-subway::before{content:"\F6AB"}.mdi-subway-alert-variant::before{content:"\FD79"}.mdi-subway-variant::before{content:"\F4DF"}.mdi-summit::before{content:"\F785"}.mdi-sunglasses::before{content:"\F4E0"}.mdi-surround-sound::before{content:"\F5C5"}.mdi-surround-sound-2-0::before{content:"\F7EF"}.mdi-surround-sound-3-1::before{content:"\F7F0"}.mdi-surround-sound-5-1::before{content:"\F7F1"}.mdi-surround-sound-7-1::before{content:"\F7F2"}.mdi-svg::before{content:"\F720"}.mdi-swap-horizontal::before{content:"\F4E1"}.mdi-swap-horizontal-bold::before{content:"\FBA9"}.mdi-swap-horizontal-circle::before{content:"\F0002"}.mdi-swap-horizontal-circle-outline::before{content:"\F0003"}.mdi-swap-horizontal-variant::before{content:"\F8C0"}.mdi-swap-vertical::before{content:"\F4E2"}.mdi-swap-vertical-bold::before{content:"\FBAA"}.mdi-swap-vertical-circle::before{content:"\F0004"}.mdi-swap-vertical-circle-outline::before{content:"\F0005"}.mdi-swap-vertical-variant::before{content:"\F8C1"}.mdi-swim::before{content:"\F4E3"}.mdi-switch::before{content:"\F4E4"}.mdi-sword::before{content:"\F4E5"}.mdi-sword-cross::before{content:"\F786"}.mdi-syllabary-hangul::before{content:"\F035E"}.mdi-syllabary-hiragana::before{content:"\F035F"}.mdi-syllabary-katakana::before{content:"\F0360"}.mdi-syllabary-katakana-half-width::before{content:"\F0361"}.mdi-symfony::before{content:"\FAE5"}.mdi-sync::before{content:"\F4E6"}.mdi-sync-alert::before{content:"\F4E7"}.mdi-sync-circle::before{content:"\F03A3"}.mdi-sync-off::before{content:"\F4E8"}.mdi-tab::before{content:"\F4E9"}.mdi-tab-minus::before{content:"\FB26"}.mdi-tab-plus::before{content:"\F75B"}.mdi-tab-remove::before{content:"\FB27"}.mdi-tab-unselected::before{content:"\F4EA"}.mdi-table::before{content:"\F4EB"}.mdi-table-border::before{content:"\FA17"}.mdi-table-chair::before{content:"\F0083"}.mdi-table-column::before{content:"\F834"}.mdi-table-column-plus-after::before{content:"\F4EC"}.mdi-table-column-plus-before::before{content:"\F4ED"}.mdi-table-column-remove::before{content:"\F4EE"}.mdi-table-column-width::before{content:"\F4EF"}.mdi-table-edit::before{content:"\F4F0"}.mdi-table-eye::before{content:"\F00BF"}.mdi-table-headers-eye::before{content:"\F0248"}.mdi-table-headers-eye-off::before{content:"\F0249"}.mdi-table-large::before{content:"\F4F1"}.mdi-table-large-plus::before{content:"\FFA4"}.mdi-table-large-remove::before{content:"\FFA5"}.mdi-table-merge-cells::before{content:"\F9A5"}.mdi-table-of-contents::before{content:"\F835"}.mdi-table-plus::before{content:"\FA74"}.mdi-table-remove::before{content:"\FA75"}.mdi-table-row::before{content:"\F836"}.mdi-table-row-height::before{content:"\F4F2"}.mdi-table-row-plus-after::before{content:"\F4F3"}.mdi-table-row-plus-before::before{content:"\F4F4"}.mdi-table-row-remove::before{content:"\F4F5"}.mdi-table-search::before{content:"\F90E"}.mdi-table-settings::before{content:"\F837"}.mdi-table-tennis::before{content:"\FE4B"}.mdi-tablet::before{content:"\F4F6"}.mdi-tablet-android::before{content:"\F4F7"}.mdi-tablet-cellphone::before{content:"\F9A6"}.mdi-tablet-dashboard::before{content:"\FEEB"}.mdi-tablet-ipad::before{content:"\F4F8"}.mdi-taco::before{content:"\F761"}.mdi-tag::before{content:"\F4F9"}.mdi-tag-faces::before{content:"\F4FA"}.mdi-tag-heart::before{content:"\F68A"}.mdi-tag-heart-outline::before{content:"\FBAB"}.mdi-tag-minus::before{content:"\F90F"}.mdi-tag-minus-outline::before{content:"\F024A"}.mdi-tag-multiple::before{content:"\F4FB"}.mdi-tag-multiple-outline::before{content:"\F0322"}.mdi-tag-off::before{content:"\F024B"}.mdi-tag-off-outline::before{content:"\F024C"}.mdi-tag-outline::before{content:"\F4FC"}.mdi-tag-plus::before{content:"\F721"}.mdi-tag-plus-outline::before{content:"\F024D"}.mdi-tag-remove::before{content:"\F722"}.mdi-tag-remove-outline::before{content:"\F024E"}.mdi-tag-text::before{content:"\F024F"}.mdi-tag-text-outline::before{content:"\F4FD"}.mdi-tank::before{content:"\FD16"}.mdi-tanker-truck::before{content:"\F0006"}.mdi-tape-measure::before{content:"\FB28"}.mdi-target::before{content:"\F4FE"}.mdi-target-account::before{content:"\FBAC"}.mdi-target-variant::before{content:"\FA76"}.mdi-taxi::before{content:"\F4FF"}.mdi-tea::before{content:"\FD7A"}.mdi-tea-outline::before{content:"\FD7B"}.mdi-teach::before{content:"\F88F"}.mdi-teamviewer::before{content:"\F500"}.mdi-telegram::before{content:"\F501"}.mdi-telescope::before{content:"\FB29"}.mdi-television::before{content:"\F502"}.mdi-television-ambient-light::before{content:"\F0381"}.mdi-television-box::before{content:"\F838"}.mdi-television-classic::before{content:"\F7F3"}.mdi-television-classic-off::before{content:"\F839"}.mdi-television-clean::before{content:"\F013B"}.mdi-television-guide::before{content:"\F503"}.mdi-television-off::before{content:"\F83A"}.mdi-television-pause::before{content:"\FFA6"}.mdi-television-play::before{content:"\FEEC"}.mdi-television-stop::before{content:"\FFA7"}.mdi-temperature-celsius::before{content:"\F504"}.mdi-temperature-fahrenheit::before{content:"\F505"}.mdi-temperature-kelvin::before{content:"\F506"}.mdi-tennis::before{content:"\FD7C"}.mdi-tennis-ball::before{content:"\F507"}.mdi-tent::before{content:"\F508"}.mdi-terraform::before{content:"\F0084"}.mdi-terrain::before{content:"\F509"}.mdi-test-tube::before{content:"\F668"}.mdi-test-tube-empty::before{content:"\F910"}.mdi-test-tube-off::before{content:"\F911"}.mdi-text::before{content:"\F9A7"}.mdi-text-recognition::before{content:"\F0168"}.mdi-text-shadow::before{content:"\F669"}.mdi-text-short::before{content:"\F9A8"}.mdi-text-subject::before{content:"\F9A9"}.mdi-text-to-speech::before{content:"\F50A"}.mdi-text-to-speech-off::before{content:"\F50B"}.mdi-textarea::before{content:"\F00C0"}.mdi-textbox::before{content:"\F60E"}.mdi-textbox-lock::before{content:"\F0388"}.mdi-textbox-password::before{content:"\F7F4"}.mdi-texture::before{content:"\F50C"}.mdi-texture-box::before{content:"\F0007"}.mdi-theater::before{content:"\F50D"}.mdi-theme-light-dark::before{content:"\F50E"}.mdi-thermometer::before{content:"\F50F"}.mdi-thermometer-alert::before{content:"\FE61"}.mdi-thermometer-chevron-down::before{content:"\FE62"}.mdi-thermometer-chevron-up::before{content:"\FE63"}.mdi-thermometer-high::before{content:"\F00ED"}.mdi-thermometer-lines::before{content:"\F510"}.mdi-thermometer-low::before{content:"\F00EE"}.mdi-thermometer-minus::before{content:"\FE64"}.mdi-thermometer-plus::before{content:"\FE65"}.mdi-thermostat::before{content:"\F393"}.mdi-thermostat-box::before{content:"\F890"}.mdi-thought-bubble::before{content:"\F7F5"}.mdi-thought-bubble-outline::before{content:"\F7F6"}.mdi-thumb-down::before{content:"\F511"}.mdi-thumb-down-outline::before{content:"\F512"}.mdi-thumb-up::before{content:"\F513"}.mdi-thumb-up-outline::before{content:"\F514"}.mdi-thumbs-up-down::before{content:"\F515"}.mdi-ticket::before{content:"\F516"}.mdi-ticket-account::before{content:"\F517"}.mdi-ticket-confirmation::before{content:"\F518"}.mdi-ticket-outline::before{content:"\F912"}.mdi-ticket-percent::before{content:"\F723"}.mdi-tie::before{content:"\F519"}.mdi-tilde::before{content:"\F724"}.mdi-timelapse::before{content:"\F51A"}.mdi-timeline::before{content:"\FBAD"}.mdi-timeline-alert::before{content:"\FFB2"}.mdi-timeline-alert-outline::before{content:"\FFB5"}.mdi-timeline-clock::before{content:"\F0226"}.mdi-timeline-clock-outline::before{content:"\F0227"}.mdi-timeline-help::before{content:"\FFB6"}.mdi-timeline-help-outline::before{content:"\FFB7"}.mdi-timeline-outline::before{content:"\FBAE"}.mdi-timeline-plus::before{content:"\FFB3"}.mdi-timeline-plus-outline::before{content:"\FFB4"}.mdi-timeline-text::before{content:"\FBAF"}.mdi-timeline-text-outline::before{content:"\FBB0"}.mdi-timer::before{content:"\F51B"}.mdi-timer-10::before{content:"\F51C"}.mdi-timer-3::before{content:"\F51D"}.mdi-timer-off::before{content:"\F51E"}.mdi-timer-sand::before{content:"\F51F"}.mdi-timer-sand-empty::before{content:"\F6AC"}.mdi-timer-sand-full::before{content:"\F78B"}.mdi-timetable::before{content:"\F520"}.mdi-toaster::before{content:"\F0085"}.mdi-toaster-off::before{content:"\F01E2"}.mdi-toaster-oven::before{content:"\FCAF"}.mdi-toggle-switch::before{content:"\F521"}.mdi-toggle-switch-off::before{content:"\F522"}.mdi-toggle-switch-off-outline::before{content:"\FA18"}.mdi-toggle-switch-outline::before{content:"\FA19"}.mdi-toilet::before{content:"\F9AA"}.mdi-toolbox::before{content:"\F9AB"}.mdi-toolbox-outline::before{content:"\F9AC"}.mdi-tools::before{content:"\F0086"}.mdi-tooltip::before{content:"\F523"}.mdi-tooltip-account::before{content:"\F00C"}.mdi-tooltip-edit::before{content:"\F524"}.mdi-tooltip-edit-outline::before{content:"\F02F0"}.mdi-tooltip-image::before{content:"\F525"}.mdi-tooltip-image-outline::before{content:"\FBB1"}.mdi-tooltip-outline::before{content:"\F526"}.mdi-tooltip-plus::before{content:"\FBB2"}.mdi-tooltip-plus-outline::before{content:"\F527"}.mdi-tooltip-text::before{content:"\F528"}.mdi-tooltip-text-outline::before{content:"\FBB3"}.mdi-tooth::before{content:"\F8C2"}.mdi-tooth-outline::before{content:"\F529"}.mdi-toothbrush::before{content:"\F0154"}.mdi-toothbrush-electric::before{content:"\F0157"}.mdi-toothbrush-paste::before{content:"\F0155"}.mdi-tor::before{content:"\F52A"}.mdi-tortoise::before{content:"\FD17"}.mdi-toslink::before{content:"\F02E3"}.mdi-tournament::before{content:"\F9AD"}.mdi-tower-beach::before{content:"\F680"}.mdi-tower-fire::before{content:"\F681"}.mdi-towing::before{content:"\F83B"}.mdi-toy-brick::before{content:"\F02B3"}.mdi-toy-brick-marker::before{content:"\F02B4"}.mdi-toy-brick-marker-outline::before{content:"\F02B5"}.mdi-toy-brick-minus::before{content:"\F02B6"}.mdi-toy-brick-minus-outline::before{content:"\F02B7"}.mdi-toy-brick-outline::before{content:"\F02B8"}.mdi-toy-brick-plus::before{content:"\F02B9"}.mdi-toy-brick-plus-outline::before{content:"\F02BA"}.mdi-toy-brick-remove::before{content:"\F02BB"}.mdi-toy-brick-remove-outline::before{content:"\F02BC"}.mdi-toy-brick-search::before{content:"\F02BD"}.mdi-toy-brick-search-outline::before{content:"\F02BE"}.mdi-track-light::before{content:"\F913"}.mdi-trackpad::before{content:"\F7F7"}.mdi-trackpad-lock::before{content:"\F932"}.mdi-tractor::before{content:"\F891"}.mdi-trademark::before{content:"\FA77"}.mdi-traffic-cone::before{content:"\F03A7"}.mdi-traffic-light::before{content:"\F52B"}.mdi-train::before{content:"\F52C"}.mdi-train-car::before{content:"\FBB4"}.mdi-train-variant::before{content:"\F8C3"}.mdi-tram::before{content:"\F52D"}.mdi-tram-side::before{content:"\F0008"}.mdi-transcribe::before{content:"\F52E"}.mdi-transcribe-close::before{content:"\F52F"}.mdi-transfer::before{content:"\F0087"}.mdi-transfer-down::before{content:"\FD7D"}.mdi-transfer-left::before{content:"\FD7E"}.mdi-transfer-right::before{content:"\F530"}.mdi-transfer-up::before{content:"\FD7F"}.mdi-transit-connection::before{content:"\FD18"}.mdi-transit-connection-variant::before{content:"\FD19"}.mdi-transit-detour::before{content:"\FFA8"}.mdi-transit-transfer::before{content:"\F6AD"}.mdi-transition::before{content:"\F914"}.mdi-transition-masked::before{content:"\F915"}.mdi-translate::before{content:"\F5CA"}.mdi-translate-off::before{content:"\FE66"}.mdi-transmission-tower::before{content:"\FD1A"}.mdi-trash-can::before{content:"\FA78"}.mdi-trash-can-outline::before{content:"\FA79"}.mdi-tray::before{content:"\F02BF"}.mdi-tray-alert::before{content:"\F02C0"}.mdi-tray-full::before{content:"\F02C1"}.mdi-tray-minus::before{content:"\F02C2"}.mdi-tray-plus::before{content:"\F02C3"}.mdi-tray-remove::before{content:"\F02C4"}.mdi-treasure-chest::before{content:"\F725"}.mdi-tree::before{content:"\F531"}.mdi-tree-outline::before{content:"\FE4C"}.mdi-trello::before{content:"\F532"}.mdi-trending-down::before{content:"\F533"}.mdi-trending-neutral::before{content:"\F534"}.mdi-trending-up::before{content:"\F535"}.mdi-triangle::before{content:"\F536"}.mdi-triangle-outline::before{content:"\F537"}.mdi-triforce::before{content:"\FBB5"}.mdi-trophy::before{content:"\F538"}.mdi-trophy-award::before{content:"\F539"}.mdi-trophy-broken::before{content:"\FD80"}.mdi-trophy-outline::before{content:"\F53A"}.mdi-trophy-variant::before{content:"\F53B"}.mdi-trophy-variant-outline::before{content:"\F53C"}.mdi-truck::before{content:"\F53D"}.mdi-truck-check::before{content:"\FCB0"}.mdi-truck-check-outline::before{content:"\F02C5"}.mdi-truck-delivery::before{content:"\F53E"}.mdi-truck-delivery-outline::before{content:"\F02C6"}.mdi-truck-fast::before{content:"\F787"}.mdi-truck-fast-outline::before{content:"\F02C7"}.mdi-truck-outline::before{content:"\F02C8"}.mdi-truck-trailer::before{content:"\F726"}.mdi-trumpet::before{content:"\F00C1"}.mdi-tshirt-crew::before{content:"\FA7A"}.mdi-tshirt-crew-outline::before{content:"\F53F"}.mdi-tshirt-v::before{content:"\FA7B"}.mdi-tshirt-v-outline::before{content:"\F540"}.mdi-tumble-dryer::before{content:"\F916"}.mdi-tumble-dryer-alert::before{content:"\F01E5"}.mdi-tumble-dryer-off::before{content:"\F01E6"}.mdi-tumblr::before{content:"\F541"}.mdi-tumblr-box::before{content:"\F917"}.mdi-tumblr-reblog::before{content:"\F542"}.mdi-tune::before{content:"\F62E"}.mdi-tune-vertical::before{content:"\F66A"}.mdi-turnstile::before{content:"\FCB1"}.mdi-turnstile-outline::before{content:"\FCB2"}.mdi-turtle::before{content:"\FCB3"}.mdi-twitch::before{content:"\F543"}.mdi-twitter::before{content:"\F544"}.mdi-twitter-box::before{content:"\F545"}.mdi-twitter-circle::before{content:"\F546"}.mdi-twitter-retweet::before{content:"\F547"}.mdi-two-factor-authentication::before{content:"\F9AE"}.mdi-typewriter::before{content:"\FF4A"}.mdi-uber::before{content:"\F748"}.mdi-ubisoft::before{content:"\FBB6"}.mdi-ubuntu::before{content:"\F548"}.mdi-ufo::before{content:"\F00EF"}.mdi-ufo-outline::before{content:"\F00F0"}.mdi-ultra-high-definition::before{content:"\F7F8"}.mdi-umbraco::before{content:"\F549"}.mdi-umbrella::before{content:"\F54A"}.mdi-umbrella-closed::before{content:"\F9AF"}.mdi-umbrella-outline::before{content:"\F54B"}.mdi-undo::before{content:"\F54C"}.mdi-undo-variant::before{content:"\F54D"}.mdi-unfold-less-horizontal::before{content:"\F54E"}.mdi-unfold-less-vertical::before{content:"\F75F"}.mdi-unfold-more-horizontal::before{content:"\F54F"}.mdi-unfold-more-vertical::before{content:"\F760"}.mdi-ungroup::before{content:"\F550"}.mdi-unicode::before{content:"\FEED"}.mdi-unity::before{content:"\F6AE"}.mdi-unreal::before{content:"\F9B0"}.mdi-untappd::before{content:"\F551"}.mdi-update::before{content:"\F6AF"}.mdi-upload::before{content:"\F552"}.mdi-upload-lock::before{content:"\F039E"}.mdi-upload-lock-outline::before{content:"\F039F"}.mdi-upload-multiple::before{content:"\F83C"}.mdi-upload-network::before{content:"\F6F5"}.mdi-upload-network-outline::before{content:"\FCB4"}.mdi-upload-off::before{content:"\F00F1"}.mdi-upload-off-outline::before{content:"\F00F2"}.mdi-upload-outline::before{content:"\FE67"}.mdi-usb::before{content:"\F553"}.mdi-usb-flash-drive::before{content:"\F02C9"}.mdi-usb-flash-drive-outline::before{content:"\F02CA"}.mdi-usb-port::before{content:"\F021B"}.mdi-valve::before{content:"\F0088"}.mdi-valve-closed::before{content:"\F0089"}.mdi-valve-open::before{content:"\F008A"}.mdi-van-passenger::before{content:"\F7F9"}.mdi-van-utility::before{content:"\F7FA"}.mdi-vanish::before{content:"\F7FB"}.mdi-vanity-light::before{content:"\F020C"}.mdi-variable::before{content:"\FAE6"}.mdi-variable-box::before{content:"\F013C"}.mdi-vector-arrange-above::before{content:"\F554"}.mdi-vector-arrange-below::before{content:"\F555"}.mdi-vector-bezier::before{content:"\FAE7"}.mdi-vector-circle::before{content:"\F556"}.mdi-vector-circle-variant::before{content:"\F557"}.mdi-vector-combine::before{content:"\F558"}.mdi-vector-curve::before{content:"\F559"}.mdi-vector-difference::before{content:"\F55A"}.mdi-vector-difference-ab::before{content:"\F55B"}.mdi-vector-difference-ba::before{content:"\F55C"}.mdi-vector-ellipse::before{content:"\F892"}.mdi-vector-intersection::before{content:"\F55D"}.mdi-vector-line::before{content:"\F55E"}.mdi-vector-link::before{content:"\F0009"}.mdi-vector-point::before{content:"\F55F"}.mdi-vector-polygon::before{content:"\F560"}.mdi-vector-polyline::before{content:"\F561"}.mdi-vector-polyline-edit::before{content:"\F0250"}.mdi-vector-polyline-minus::before{content:"\F0251"}.mdi-vector-polyline-plus::before{content:"\F0252"}.mdi-vector-polyline-remove::before{content:"\F0253"}.mdi-vector-radius::before{content:"\F749"}.mdi-vector-rectangle::before{content:"\F5C6"}.mdi-vector-selection::before{content:"\F562"}.mdi-vector-square::before{content:"\F001"}.mdi-vector-triangle::before{content:"\F563"}.mdi-vector-union::before{content:"\F564"}.mdi-venmo::before{content:"\F578"}.mdi-vhs::before{content:"\FA1A"}.mdi-vibrate::before{content:"\F566"}.mdi-vibrate-off::before{content:"\FCB5"}.mdi-video::before{content:"\F567"}.mdi-video-3d::before{content:"\F7FC"}.mdi-video-3d-variant::before{content:"\FEEE"}.mdi-video-4k-box::before{content:"\F83D"}.mdi-video-account::before{content:"\F918"}.mdi-video-check::before{content:"\F008B"}.mdi-video-check-outline::before{content:"\F008C"}.mdi-video-image::before{content:"\F919"}.mdi-video-input-antenna::before{content:"\F83E"}.mdi-video-input-component::before{content:"\F83F"}.mdi-video-input-hdmi::before{content:"\F840"}.mdi-video-input-scart::before{content:"\FFA9"}.mdi-video-input-svideo::before{content:"\F841"}.mdi-video-minus::before{content:"\F9B1"}.mdi-video-off::before{content:"\F568"}.mdi-video-off-outline::before{content:"\FBB7"}.mdi-video-outline::before{content:"\FBB8"}.mdi-video-plus::before{content:"\F9B2"}.mdi-video-stabilization::before{content:"\F91A"}.mdi-video-switch::before{content:"\F569"}.mdi-video-vintage::before{content:"\FA1B"}.mdi-video-wireless::before{content:"\FEEF"}.mdi-video-wireless-outline::before{content:"\FEF0"}.mdi-view-agenda::before{content:"\F56A"}.mdi-view-agenda-outline::before{content:"\F0203"}.mdi-view-array::before{content:"\F56B"}.mdi-view-carousel::before{content:"\F56C"}.mdi-view-column::before{content:"\F56D"}.mdi-view-comfy::before{content:"\FE4D"}.mdi-view-compact::before{content:"\FE4E"}.mdi-view-compact-outline::before{content:"\FE4F"}.mdi-view-dashboard::before{content:"\F56E"}.mdi-view-dashboard-outline::before{content:"\FA1C"}.mdi-view-dashboard-variant::before{content:"\F842"}.mdi-view-day::before{content:"\F56F"}.mdi-view-grid::before{content:"\F570"}.mdi-view-grid-outline::before{content:"\F0204"}.mdi-view-grid-plus::before{content:"\FFAA"}.mdi-view-grid-plus-outline::before{content:"\F0205"}.mdi-view-headline::before{content:"\F571"}.mdi-view-list::before{content:"\F572"}.mdi-view-module::before{content:"\F573"}.mdi-view-parallel::before{content:"\F727"}.mdi-view-quilt::before{content:"\F574"}.mdi-view-sequential::before{content:"\F728"}.mdi-view-split-horizontal::before{content:"\FBA7"}.mdi-view-split-vertical::before{content:"\FBA8"}.mdi-view-stream::before{content:"\F575"}.mdi-view-week::before{content:"\F576"}.mdi-vimeo::before{content:"\F577"}.mdi-violin::before{content:"\F60F"}.mdi-virtual-reality::before{content:"\F893"}.mdi-visual-studio::before{content:"\F610"}.mdi-visual-studio-code::before{content:"\FA1D"}.mdi-vk::before{content:"\F579"}.mdi-vk-box::before{content:"\F57A"}.mdi-vk-circle::before{content:"\F57B"}.mdi-vlc::before{content:"\F57C"}.mdi-voice::before{content:"\F5CB"}.mdi-voice-off::before{content:"\FEF1"}.mdi-voicemail::before{content:"\F57D"}.mdi-volleyball::before{content:"\F9B3"}.mdi-volume-high::before{content:"\F57E"}.mdi-volume-low::before{content:"\F57F"}.mdi-volume-medium::before{content:"\F580"}.mdi-volume-minus::before{content:"\F75D"}.mdi-volume-mute::before{content:"\F75E"}.mdi-volume-off::before{content:"\F581"}.mdi-volume-plus::before{content:"\F75C"}.mdi-volume-source::before{content:"\F014B"}.mdi-volume-variant-off::before{content:"\FE68"}.mdi-volume-vibrate::before{content:"\F014C"}.mdi-vote::before{content:"\FA1E"}.mdi-vote-outline::before{content:"\FA1F"}.mdi-vpn::before{content:"\F582"}.mdi-vuejs::before{content:"\F843"}.mdi-vuetify::before{content:"\FE50"}.mdi-walk::before{content:"\F583"}.mdi-wall::before{content:"\F7FD"}.mdi-wall-sconce::before{content:"\F91B"}.mdi-wall-sconce-flat::before{content:"\F91C"}.mdi-wall-sconce-variant::before{content:"\F91D"}.mdi-wallet::before{content:"\F584"}.mdi-wallet-giftcard::before{content:"\F585"}.mdi-wallet-membership::before{content:"\F586"}.mdi-wallet-outline::before{content:"\FBB9"}.mdi-wallet-plus::before{content:"\FFAB"}.mdi-wallet-plus-outline::before{content:"\FFAC"}.mdi-wallet-travel::before{content:"\F587"}.mdi-wallpaper::before{content:"\FE69"}.mdi-wan::before{content:"\F588"}.mdi-wardrobe::before{content:"\FFAD"}.mdi-wardrobe-outline::before{content:"\FFAE"}.mdi-warehouse::before{content:"\FFBB"}.mdi-washing-machine::before{content:"\F729"}.mdi-washing-machine-alert::before{content:"\F01E7"}.mdi-washing-machine-off::before{content:"\F01E8"}.mdi-watch::before{content:"\F589"}.mdi-watch-export::before{content:"\F58A"}.mdi-watch-export-variant::before{content:"\F894"}.mdi-watch-import::before{content:"\F58B"}.mdi-watch-import-variant::before{content:"\F895"}.mdi-watch-variant::before{content:"\F896"}.mdi-watch-vibrate::before{content:"\F6B0"}.mdi-watch-vibrate-off::before{content:"\FCB6"}.mdi-water::before{content:"\F58C"}.mdi-water-boiler::before{content:"\FFAF"}.mdi-water-boiler-alert::before{content:"\F01DE"}.mdi-water-boiler-off::before{content:"\F01DF"}.mdi-water-off::before{content:"\F58D"}.mdi-water-outline::before{content:"\FE6A"}.mdi-water-percent::before{content:"\F58E"}.mdi-water-polo::before{content:"\F02CB"}.mdi-water-pump::before{content:"\F58F"}.mdi-water-pump-off::before{content:"\FFB0"}.mdi-water-well::before{content:"\F008D"}.mdi-water-well-outline::before{content:"\F008E"}.mdi-watermark::before{content:"\F612"}.mdi-wave::before{content:"\FF4B"}.mdi-waves::before{content:"\F78C"}.mdi-waze::before{content:"\FBBA"}.mdi-weather-cloudy::before{content:"\F590"}.mdi-weather-cloudy-alert::before{content:"\FF4C"}.mdi-weather-cloudy-arrow-right::before{content:"\FE51"}.mdi-weather-fog::before{content:"\F591"}.mdi-weather-hail::before{content:"\F592"}.mdi-weather-hazy::before{content:"\FF4D"}.mdi-weather-hurricane::before{content:"\F897"}.mdi-weather-lightning::before{content:"\F593"}.mdi-weather-lightning-rainy::before{content:"\F67D"}.mdi-weather-night::before{content:"\F594"}.mdi-weather-night-partly-cloudy::before{content:"\FF4E"}.mdi-weather-partly-cloudy::before{content:"\F595"}.mdi-weather-partly-lightning::before{content:"\FF4F"}.mdi-weather-partly-rainy::before{content:"\FF50"}.mdi-weather-partly-snowy::before{content:"\FF51"}.mdi-weather-partly-snowy-rainy::before{content:"\FF52"}.mdi-weather-pouring::before{content:"\F596"}.mdi-weather-rainy::before{content:"\F597"}.mdi-weather-snowy::before{content:"\F598"}.mdi-weather-snowy-heavy::before{content:"\FF53"}.mdi-weather-snowy-rainy::before{content:"\F67E"}.mdi-weather-sunny::before{content:"\F599"}.mdi-weather-sunny-alert::before{content:"\FF54"}.mdi-weather-sunset::before{content:"\F59A"}.mdi-weather-sunset-down::before{content:"\F59B"}.mdi-weather-sunset-up::before{content:"\F59C"}.mdi-weather-tornado::before{content:"\FF55"}.mdi-weather-windy::before{content:"\F59D"}.mdi-weather-windy-variant::before{content:"\F59E"}.mdi-web::before{content:"\F59F"}.mdi-web-box::before{content:"\FFB1"}.mdi-web-clock::before{content:"\F0275"}.mdi-webcam::before{content:"\F5A0"}.mdi-webhook::before{content:"\F62F"}.mdi-webpack::before{content:"\F72A"}.mdi-webrtc::before{content:"\F0273"}.mdi-wechat::before{content:"\F611"}.mdi-weight::before{content:"\F5A1"}.mdi-weight-gram::before{content:"\FD1B"}.mdi-weight-kilogram::before{content:"\F5A2"}.mdi-weight-lifter::before{content:"\F0188"}.mdi-weight-pound::before{content:"\F9B4"}.mdi-whatsapp::before{content:"\F5A3"}.mdi-wheelchair-accessibility::before{content:"\F5A4"}.mdi-whistle::before{content:"\F9B5"}.mdi-whistle-outline::before{content:"\F02E7"}.mdi-white-balance-auto::before{content:"\F5A5"}.mdi-white-balance-incandescent::before{content:"\F5A6"}.mdi-white-balance-iridescent::before{content:"\F5A7"}.mdi-white-balance-sunny::before{content:"\F5A8"}.mdi-widgets::before{content:"\F72B"}.mdi-widgets-outline::before{content:"\F0380"}.mdi-wifi::before{content:"\F5A9"}.mdi-wifi-off::before{content:"\F5AA"}.mdi-wifi-star::before{content:"\FE6B"}.mdi-wifi-strength-1::before{content:"\F91E"}.mdi-wifi-strength-1-alert::before{content:"\F91F"}.mdi-wifi-strength-1-lock::before{content:"\F920"}.mdi-wifi-strength-2::before{content:"\F921"}.mdi-wifi-strength-2-alert::before{content:"\F922"}.mdi-wifi-strength-2-lock::before{content:"\F923"}.mdi-wifi-strength-3::before{content:"\F924"}.mdi-wifi-strength-3-alert::before{content:"\F925"}.mdi-wifi-strength-3-lock::before{content:"\F926"}.mdi-wifi-strength-4::before{content:"\F927"}.mdi-wifi-strength-4-alert::before{content:"\F928"}.mdi-wifi-strength-4-lock::before{content:"\F929"}.mdi-wifi-strength-alert-outline::before{content:"\F92A"}.mdi-wifi-strength-lock-outline::before{content:"\F92B"}.mdi-wifi-strength-off::before{content:"\F92C"}.mdi-wifi-strength-off-outline::before{content:"\F92D"}.mdi-wifi-strength-outline::before{content:"\F92E"}.mdi-wii::before{content:"\F5AB"}.mdi-wiiu::before{content:"\F72C"}.mdi-wikipedia::before{content:"\F5AC"}.mdi-wind-turbine::before{content:"\FD81"}.mdi-window-close::before{content:"\F5AD"}.mdi-window-closed::before{content:"\F5AE"}.mdi-window-closed-variant::before{content:"\F0206"}.mdi-window-maximize::before{content:"\F5AF"}.mdi-window-minimize::before{content:"\F5B0"}.mdi-window-open::before{content:"\F5B1"}.mdi-window-open-variant::before{content:"\F0207"}.mdi-window-restore::before{content:"\F5B2"}.mdi-window-shutter::before{content:"\F0147"}.mdi-window-shutter-alert::before{content:"\F0148"}.mdi-window-shutter-open::before{content:"\F0149"}.mdi-windows::before{content:"\F5B3"}.mdi-windows-classic::before{content:"\FA20"}.mdi-wiper::before{content:"\FAE8"}.mdi-wiper-wash::before{content:"\FD82"}.mdi-wordpress::before{content:"\F5B4"}.mdi-worker::before{content:"\F5B5"}.mdi-wrap::before{content:"\F5B6"}.mdi-wrap-disabled::before{content:"\FBBB"}.mdi-wrench::before{content:"\F5B7"}.mdi-wrench-outline::before{content:"\FBBC"}.mdi-wunderlist::before{content:"\F5B8"}.mdi-xamarin::before{content:"\F844"}.mdi-xamarin-outline::before{content:"\F845"}.mdi-xaml::before{content:"\F673"}.mdi-xbox::before{content:"\F5B9"}.mdi-xbox-controller::before{content:"\F5BA"}.mdi-xbox-controller-battery-alert::before{content:"\F74A"}.mdi-xbox-controller-battery-charging::before{content:"\FA21"}.mdi-xbox-controller-battery-empty::before{content:"\F74B"}.mdi-xbox-controller-battery-full::before{content:"\F74C"}.mdi-xbox-controller-battery-low::before{content:"\F74D"}.mdi-xbox-controller-battery-medium::before{content:"\F74E"}.mdi-xbox-controller-battery-unknown::before{content:"\F74F"}.mdi-xbox-controller-menu::before{content:"\FE52"}.mdi-xbox-controller-off::before{content:"\F5BB"}.mdi-xbox-controller-view::before{content:"\FE53"}.mdi-xda::before{content:"\F5BC"}.mdi-xing::before{content:"\F5BD"}.mdi-xing-box::before{content:"\F5BE"}.mdi-xing-circle::before{content:"\F5BF"}.mdi-xml::before{content:"\F5C0"}.mdi-xmpp::before{content:"\F7FE"}.mdi-yahoo::before{content:"\FB2A"}.mdi-yammer::before{content:"\F788"}.mdi-yeast::before{content:"\F5C1"}.mdi-yelp::before{content:"\F5C2"}.mdi-yin-yang::before{content:"\F67F"}.mdi-yoga::before{content:"\F01A7"}.mdi-youtube::before{content:"\F5C3"}.mdi-youtube-creator-studio::before{content:"\F846"}.mdi-youtube-gaming::before{content:"\F847"}.mdi-youtube-subscription::before{content:"\FD1C"}.mdi-youtube-tv::before{content:"\F448"}.mdi-z-wave::before{content:"\FAE9"}.mdi-zend::before{content:"\FAEA"}.mdi-zigbee::before{content:"\FD1D"}.mdi-zip-box::before{content:"\F5C4"}.mdi-zip-box-outline::before{content:"\F001B"}.mdi-zip-disk::before{content:"\FA22"}.mdi-zodiac-aquarius::before{content:"\FA7C"}.mdi-zodiac-aries::before{content:"\FA7D"}.mdi-zodiac-cancer::before{content:"\FA7E"}.mdi-zodiac-capricorn::before{content:"\FA7F"}.mdi-zodiac-gemini::before{content:"\FA80"}.mdi-zodiac-leo::before{content:"\FA81"}.mdi-zodiac-libra::before{content:"\FA82"}.mdi-zodiac-pisces::before{content:"\FA83"}.mdi-zodiac-sagittarius::before{content:"\FA84"}.mdi-zodiac-scorpio::before{content:"\FA85"}.mdi-zodiac-taurus::before{content:"\FA86"}.mdi-zodiac-virgo::before{content:"\FA87"}.mdi-blank::before{content:"\F68C";visibility:hidden}.mdi-18px.mdi-set,.mdi-18px.mdi:before{font-size:18px}.mdi-24px.mdi-set,.mdi-24px.mdi:before{font-size:24px}.mdi-36px.mdi-set,.mdi-36px.mdi:before{font-size:36px}.mdi-48px.mdi-set,.mdi-48px.mdi:before{font-size:48px}.mdi-dark:before{color:rgba(0,0,0,0.54)}.mdi-dark.mdi-inactive:before{color:rgba(0,0,0,0.26)}.mdi-light:before{color:#fff}.mdi-light.mdi-inactive:before{color:rgba(255,255,255,0.3)}.mdi-rotate-45:before{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.mdi-rotate-90:before{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.mdi-rotate-135:before{-webkit-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg)}.mdi-rotate-180:before{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.mdi-rotate-225:before{-webkit-transform:rotate(225deg);-ms-transform:rotate(225deg);transform:rotate(225deg)}.mdi-rotate-270:before{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.mdi-rotate-315:before{-webkit-transform:rotate(315deg);-ms-transform:rotate(315deg);transform:rotate(315deg)}.mdi-flip-h:before{-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.mdi-flip-v:before{-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.mdi-spin:before{-webkit-animation:mdi-spin 2s infinite linear;animation:mdi-spin 2s infinite linear}@-webkit-keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}} + +/*# sourceMappingURL=materialdesignicons.css.map */ diff --git a/packages/demobank-ui/src/scss/libs/_all.scss b/packages/demobank-ui/src/scss/libs/_all.scss new file mode 100644 index 000000000..08bd76cd1 --- /dev/null +++ b/packages/demobank-ui/src/scss/libs/_all.scss @@ -0,0 +1,29 @@ +/* + 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 "node_modules/bulma-radio/bulma-radio"; +// @import "node_modules/bulma-responsive-tables/bulma-responsive-tables"; +@import "node_modules/bulma-checkbox/bulma-checkbox"; +// @import "node_modules/bulma-switch-control/bulma-switch-control"; +// @import "node_modules/bulma-upload-control/bulma-upload-control"; + +/* Bulma */ +@import "node_modules/bulma/bulma"; diff --git a/packages/demobank-ui/src/scss/main.scss b/packages/demobank-ui/src/scss/main.scss new file mode 100644 index 000000000..ebe36b9b4 --- /dev/null +++ b/packages/demobank-ui/src/scss/main.scss @@ -0,0 +1,4 @@ +@import "pure"; +@import "bank"; +@import "demo"; +@import "colors-bank"; diff --git a/packages/demobank-ui/src/scss/pure.scss b/packages/demobank-ui/src/scss/pure.scss new file mode 100644 index 000000000..0d804d6bd --- /dev/null +++ b/packages/demobank-ui/src/scss/pure.scss @@ -0,0 +1,1328 @@ +/*! +Pure v0.6.2 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/yahoo/pure/blob/master/LICENSE.md +*/ +/*! +normalize.css v^3.0 | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS and IE text size adjust after device orientation change, + * without disabling user zoom. + */ +html { + font-family: sans-serif; + /* 1 */ + -ms-text-size-adjust: 100%; + /* 2 */ + -webkit-text-size-adjust: 100%; + /* 2 */ } + +/** + * Remove default margin. + */ +body { + margin: 0; } + +/* HTML5 display definitions + ========================================================================== */ +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; } + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ +audio, +canvas, +progress, +video { + display: inline-block; + /* 1 */ + vertical-align: baseline; + /* 2 */ } + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ +audio:not([controls]) { + display: none; + height: 0; } + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. + */ +[hidden], +template { + display: none; } + +/* Links + ========================================================================== */ +/** + * Remove the gray background color from active links in IE 10. + */ +a { + background-color: transparent; } + +/** + * Improve readability of focused elements when they are also in an + * active/hover state. + */ +a:active, +a:hover { + outline: 0; } + +/* Text-level semantics + ========================================================================== */ +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ +abbr[title] { + border-bottom: 1px dotted; } + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ +b, +strong { + font-weight: bold; } + +/** + * Address styling not present in Safari and Chrome. + */ +dfn { + font-style: italic; } + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ +h1 { + font-size: 2em; + margin: 0.67em 0; } + +/** + * Address styling not present in IE 8/9. + */ +mark { + background: #ff0; + color: #000; } + +/** + * Address inconsistent and variable font size in all browsers. + */ +small { + font-size: 80%; } + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } + +sup { + top: -0.5em; } + +sub { + bottom: -0.25em; } + +/* Embedded content + ========================================================================== */ +/** + * Remove border when inside `a` element in IE 8/9/10. + */ +img { + border: 0; } + +/** + * Correct overflow not hidden in IE 9/10/11. + */ +svg:not(:root) { + overflow: hidden; } + +/* Grouping content + ========================================================================== */ +/** + * Address margin not present in IE 8/9 and Safari. + */ +figure { + margin: 1em 40px; } + +/** + * Address differences between Firefox and other browsers. + */ +hr { + box-sizing: content-box; + height: 0; } + +/** + * Contain overflow in all browsers. + */ +pre { + overflow: auto; } + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; } + +/* Forms + ========================================================================== */ +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ +button, +input, +optgroup, +select, +textarea { + color: inherit; + /* 1 */ + font: inherit; + /* 2 */ + margin: 0; + /* 3 */ } + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ +button { + overflow: visible; } + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ +button, +select { + text-transform: none; } + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + /* 2 */ + cursor: pointer; + /* 3 */ } + +/** + * Re-set default cursor for disabled elements. + */ +button[disabled], +html input[disabled] { + cursor: default; } + +/** + * Remove inner padding and border in Firefox 4+. + */ +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; } + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ +input { + line-height: normal; } + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ } + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; } + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. + */ +input[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + box-sizing: content-box; + /* 2 */ } + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +/** + * Define consistent border, margin, and padding. + */ +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; } + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ +legend { + border: 0; + /* 1 */ + padding: 0; + /* 2 */ } + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ +textarea { + overflow: auto; } + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ +optgroup { + font-weight: bold; } + +/* Tables + ========================================================================== */ +/** + * Remove most spacing between table cells. + */ +table { + border-collapse: collapse; + border-spacing: 0; } + +td, +th { + padding: 0; } + +/*csslint important:false*/ +/* ========================================================================== + Pure Base Extras + ========================================================================== */ +/** + * Extra rules that Pure adds on top of Normalize.css + */ +/** + * Always hide an element when it has the `hidden` HTML attribute. + */ +.hidden, +[hidden] { + display: none !important; } + +/** + * Add this class to an image to make it fit within it's fluid parent wrapper while maintaining + * aspect ratio. + */ +.pure-img { + max-width: 100%; + height: auto; + display: block; } + +/*csslint regex-selectors:false, known-properties:false, duplicate-properties:false*/ +.pure-g { + letter-spacing: -0.31em; + /* Webkit: collapse white-space between units */ + *letter-spacing: normal; + /* reset IE < 8 */ + *word-spacing: -0.43em; + /* IE < 8: collapse white-space between units */ + text-rendering: optimizespeed; + /* Webkit: fixes text-rendering: optimizeLegibility */ + /* + Sets the font stack to fonts known to work properly with the above letter + and word spacings. See: https://github.com/yahoo/pure/issues/41/ + + The following font stack makes Pure Grids work on all known environments. + + * FreeSans: Ships with many Linux distros, including Ubuntu + + * Arimo: Ships with Chrome OS. Arimo has to be defined before Helvetica and + Arial to get picked up by the browser, even though neither is available + in Chrome OS. + + * Droid Sans: Ships with all versions of Android. + + * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows. + */ + font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif; + /* Use flexbox when possible to avoid `letter-spacing` side-effects. */ + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + /* Prevents distributing space between rows */ + -webkit-align-content: flex-start; + -ms-flex-line-pack: start; + align-content: flex-start; } + +/* IE10 display: -ms-flexbox (and display: flex in IE 11) does not work inside a table; fall back to block and rely on font hack */ +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + table .pure-g { + display: block; } } +/* Opera as of 12 on Windows needs word-spacing. + The ".opera-only" selector is used to prevent actual prefocus styling + and is not required in markup. +*/ +.opera-only :-o-prefocus, +.pure-g { + word-spacing: -0.43em; } + +.pure-u { + display: inline-block; + *display: inline; + /* IE < 8: fake inline-block */ + zoom: 1; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; } + +/* +Resets the font family back to the OS/browser's default sans-serif font, +this the same font stack that Normalize.css sets for the `body`. +*/ +.pure-g [class*="pure-u"] { + font-family: sans-serif; } + +.pure-u-1, +.pure-u-1-1, +.pure-u-1-2, +.pure-u-1-3, +.pure-u-2-3, +.pure-u-1-4, +.pure-u-3-4, +.pure-u-1-5, +.pure-u-2-5, +.pure-u-3-5, +.pure-u-4-5, +.pure-u-5-5, +.pure-u-1-6, +.pure-u-5-6, +.pure-u-1-8, +.pure-u-3-8, +.pure-u-5-8, +.pure-u-7-8, +.pure-u-1-12, +.pure-u-5-12, +.pure-u-7-12, +.pure-u-11-12, +.pure-u-1-24, +.pure-u-2-24, +.pure-u-3-24, +.pure-u-4-24, +.pure-u-5-24, +.pure-u-6-24, +.pure-u-7-24, +.pure-u-8-24, +.pure-u-9-24, +.pure-u-10-24, +.pure-u-11-24, +.pure-u-12-24, +.pure-u-13-24, +.pure-u-14-24, +.pure-u-15-24, +.pure-u-16-24, +.pure-u-17-24, +.pure-u-18-24, +.pure-u-19-24, +.pure-u-20-24, +.pure-u-21-24, +.pure-u-22-24, +.pure-u-23-24, +.pure-u-24-24 { + display: inline-block; + *display: inline; + zoom: 1; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; } + +.pure-u-1-24 { + width: 4.1667%; + *width: 4.1357%; } + +.pure-u-1-12, +.pure-u-2-24 { + width: 8.3333%; + *width: 8.3023%; } + +.pure-u-1-8, +.pure-u-3-24 { + width: 12.5000%; + *width: 12.4690%; } + +.pure-u-1-6, +.pure-u-4-24 { + width: 16.6667%; + *width: 16.6357%; } + +.pure-u-1-5 { + width: 20%; + *width: 19.9690%; } + +.pure-u-5-24 { + width: 20.8333%; + *width: 20.8023%; } + +.pure-u-1-4, +.pure-u-6-24 { + width: 25%; + *width: 24.9690%; } + +.pure-u-7-24 { + width: 29.1667%; + *width: 29.1357%; } + +.pure-u-1-3, +.pure-u-8-24 { + width: 33.3333%; + *width: 33.3023%; } + +.pure-u-3-8, +.pure-u-9-24 { + width: 37.5000%; + *width: 37.4690%; } + +.pure-u-2-5 { + width: 40%; + *width: 39.9690%; } + +.pure-u-5-12, +.pure-u-10-24 { + width: 41.6667%; + *width: 41.6357%; } + +.pure-u-11-24 { + width: 45.8333%; + *width: 45.8023%; } + +.pure-u-1-2, +.pure-u-12-24 { + width: 50%; + *width: 49.9690%; } + +.pure-u-13-24 { + width: 54.1667%; + *width: 54.1357%; } + +.pure-u-7-12, +.pure-u-14-24 { + width: 58.3333%; + *width: 58.3023%; } + +.pure-u-3-5 { + width: 60%; + *width: 59.9690%; } + +.pure-u-5-8, +.pure-u-15-24 { + width: 62.5000%; + *width: 62.4690%; } + +.pure-u-2-3, +.pure-u-16-24 { + width: 66.6667%; + *width: 66.6357%; } + +.pure-u-17-24 { + width: 70.8333%; + *width: 70.8023%; } + +.pure-u-3-4, +.pure-u-18-24 { + width: 75%; + *width: 74.9690%; } + +.pure-u-19-24 { + width: 79.1667%; + *width: 79.1357%; } + +.pure-u-4-5 { + width: 80%; + *width: 79.9690%; } + +.pure-u-5-6, +.pure-u-20-24 { + width: 83.3333%; + *width: 83.3023%; } + +.pure-u-7-8, +.pure-u-21-24 { + width: 87.5000%; + *width: 87.4690%; } + +.pure-u-11-12, +.pure-u-22-24 { + width: 91.6667%; + *width: 91.6357%; } + +.pure-u-23-24 { + width: 95.8333%; + *width: 95.8023%; } + +.pure-u-1, +.pure-u-1-1, +.pure-u-5-5, +.pure-u-24-24 { + width: 100%; } + +.pure-button { + /* Structure */ + display: inline-block; + zoom: 1; + line-height: normal; + white-space: nowrap; + vertical-align: middle; + text-align: center; + cursor: pointer; + -webkit-user-drag: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + box-sizing: border-box; } + +/* Firefox: Get rid of the inner focus border */ +.pure-button::-moz-focus-inner { + padding: 0; + border: 0; } + +/* Inherit .pure-g styles */ +.pure-button-group { + letter-spacing: -0.31em; + /* Webkit: collapse white-space between units */ + *letter-spacing: normal; + /* reset IE < 8 */ + *word-spacing: -0.43em; + /* IE < 8: collapse white-space between units */ + text-rendering: optimizespeed; + /* Webkit: fixes text-rendering: optimizeLegibility */ } + +.opera-only :-o-prefocus, +.pure-button-group { + word-spacing: -0.43em; } + +.pure-button-group .pure-button { + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; } + +/*csslint outline-none:false*/ +.pure-button { + font-family: inherit; + font-size: 100%; + padding: 0.5em 1em; + color: #444; + /* rgba not supported (IE 8) */ + color: rgba(0, 0, 0, 0.8); + /* rgba supported */ + border: 1px solid #999; + /*IE 6/7/8*/ + border: none rgba(0, 0, 0, 0); + /*IE9 + everything else*/ + background-color: #E6E6E6; + text-decoration: none; + border-radius: 2px; } + +.pure-button-hover, +.pure-button:hover, +.pure-button:focus { + /* csslint ignore:start */ + filter: alpha(opacity=90); + /* csslint ignore:end */ + background-image: -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.1)); + background-image: linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.1)); } + +.pure-button:focus { + outline: 0; } + +.pure-button-active, +.pure-button:active { + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 0 6px rgba(0, 0, 0, 0.2) inset; + border-color: #000; } + +.pure-button[disabled], +.pure-button-disabled, +.pure-button-disabled:hover, +.pure-button-disabled:focus, +.pure-button-disabled:active { + border: none; + background-image: none; + /* csslint ignore:start */ + filter: alpha(opacity=40); + /* csslint ignore:end */ + opacity: 0.40; + cursor: not-allowed; + box-shadow: none; + pointer-events: none; } + +.pure-button-hidden { + display: none; } + +.pure-button-primary, +.pure-button-selected, +a.pure-button-primary, +a.pure-button-selected { + background-color: #00509b; + color: #fff; } + +/* Button Groups */ +.pure-button-group .pure-button { + margin: 0; + border-radius: 0; + border-right: 1px solid #111; + /* fallback color for rgba() for IE7/8 */ + border-right: 1px solid rgba(0, 0, 0, 0.2); } + +.pure-button-group .pure-button:first-child { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; } + +.pure-button-group .pure-button:last-child { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-right: none; } + +/*csslint box-model:false*/ +/* +Box-model set to false because we're setting a height on select elements, which +also have border and padding. This is done because some browsers don't render +the padding. We explicitly set the box-model for select elements to border-box, +so we can ignore the csslint warning. +*/ +.pure-form input[type="text"], +.pure-form input[type="password"], +.pure-form input[type="email"], +.pure-form input[type="url"], +.pure-form input[type="date"], +.pure-form input[type="month"], +.pure-form input[type="time"], +.pure-form input[type="datetime"], +.pure-form input[type="datetime-local"], +.pure-form input[type="week"], +.pure-form input[type="number"], +.pure-form input[type="search"], +.pure-form input[type="tel"], +.pure-form input[type="color"], +.pure-form select, +.pure-form textarea { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + vertical-align: middle; + box-sizing: border-box; } + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type]) { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + box-sizing: border-box; } + +/* Chrome (as of v.32/34 on OS X) needs additional room for color to display. */ +/* May be able to remove this tweak as color inputs become more standardized across browsers. */ +.pure-form input[type="color"] { + padding: 0.2em 0.5em; } + +.pure-form input[type="text"]:focus, +.pure-form input[type="password"]:focus, +.pure-form input[type="email"]:focus, +.pure-form input[type="url"]:focus, +.pure-form input[type="date"]:focus, +.pure-form input[type="month"]:focus, +.pure-form input[type="time"]:focus, +.pure-form input[type="datetime"]:focus, +.pure-form input[type="datetime-local"]:focus, +.pure-form input[type="week"]:focus, +.pure-form input[type="number"]:focus, +.pure-form input[type="search"]:focus, +.pure-form input[type="tel"]:focus, +.pure-form input[type="color"]:focus, +.pure-form select:focus, +.pure-form textarea:focus { + outline: 0; + border-color: #129FEA; } + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type]):focus { + outline: 0; + border-color: #129FEA; } + +.pure-form input[type="file"]:focus, +.pure-form input[type="radio"]:focus, +.pure-form input[type="checkbox"]:focus { + outline: thin solid #129FEA; + outline: 1px auto #129FEA; } + +.pure-form .pure-checkbox, +.pure-form .pure-radio { + margin: 0.5em 0; + display: block; } + +.pure-form input[type="text"][disabled], +.pure-form input[type="password"][disabled], +.pure-form input[type="email"][disabled], +.pure-form input[type="url"][disabled], +.pure-form input[type="date"][disabled], +.pure-form input[type="month"][disabled], +.pure-form input[type="time"][disabled], +.pure-form input[type="datetime"][disabled], +.pure-form input[type="datetime-local"][disabled], +.pure-form input[type="week"][disabled], +.pure-form input[type="number"][disabled], +.pure-form input[type="search"][disabled], +.pure-form input[type="tel"][disabled], +.pure-form input[type="color"][disabled], +.pure-form select[disabled], +.pure-form textarea[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; } + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form input:not([type])[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; } + +.pure-form input[readonly], +.pure-form select[readonly], +.pure-form textarea[readonly] { + background-color: #eee; + /* menu hover bg color */ + color: #777; + /* menu text color */ + border-color: #ccc; } + +.pure-form input:focus:invalid, +.pure-form textarea:focus:invalid, +.pure-form select:focus:invalid { + color: #b94a48; + border-color: #e9322d; } + +.pure-form input[type="file"]:focus:invalid:focus, +.pure-form input[type="radio"]:focus:invalid:focus, +.pure-form input[type="checkbox"]:focus:invalid:focus { + outline-color: #e9322d; } + +.pure-form select { + /* Normalizes the height; padding is not sufficient. */ + height: 2.25em; + border: 1px solid #ccc; + background-color: white; } + +.pure-form select[multiple] { + height: auto; } + +.pure-form label { + margin: 0.5em 0 0.2em; } + +.pure-form fieldset { + margin: 0; + padding: 0.35em 0 0.75em; + border: 0; } + +.pure-form legend { + display: block; + width: 100%; + padding: 0.3em 0; + margin-bottom: 0.3em; + color: #333; + border-bottom: 1px solid #e5e5e5; } + +.pure-form-stacked input[type="text"], +.pure-form-stacked input[type="password"], +.pure-form-stacked input[type="email"], +.pure-form-stacked input[type="url"], +.pure-form-stacked input[type="date"], +.pure-form-stacked input[type="month"], +.pure-form-stacked input[type="time"], +.pure-form-stacked input[type="datetime"], +.pure-form-stacked input[type="datetime-local"], +.pure-form-stacked input[type="week"], +.pure-form-stacked input[type="number"], +.pure-form-stacked input[type="search"], +.pure-form-stacked input[type="tel"], +.pure-form-stacked input[type="color"], +.pure-form-stacked input[type="file"], +.pure-form-stacked select, +.pure-form-stacked label, +.pure-form-stacked textarea { + display: block; + margin: 0.25em 0; } + +/* +Need to separate out the :not() selector from the rest of the CSS 2.1 selectors +since IE8 won't execute CSS that contains a CSS3 selector. +*/ +.pure-form-stacked input:not([type]) { + display: block; + margin: 0.25em 0; } + +.pure-form-aligned input, +.pure-form-aligned textarea, +.pure-form-aligned select, +.pure-form-aligned .pure-help-inline, +.pure-form-message-inline { + display: inline-block; + *display: inline; + *zoom: 1; + vertical-align: middle; } + +.pure-form-aligned textarea { + vertical-align: top; } + +/* Aligned Forms */ +.pure-form-aligned .pure-control-group { + margin-bottom: 0.5em; } + +.pure-form-aligned .pure-control-group label { + text-align: right; + display: inline-block; + vertical-align: middle; + width: 10em; + margin: 0 1em 0 0; } + +.pure-form-aligned .pure-controls { + margin: 1.5em 0 0 11em; } + +/* Rounded Inputs */ +.pure-form input.pure-input-rounded, +.pure-form .pure-input-rounded { + border-radius: 2em; + padding: 0.5em 1em; } + +/* Grouped Inputs */ +.pure-form .pure-group fieldset { + margin-bottom: 10px; } + +.pure-form .pure-group input, +.pure-form .pure-group textarea { + display: block; + padding: 10px; + margin: 0 0 -1px; + border-radius: 0; + position: relative; + top: -1px; } + +.pure-form .pure-group input:focus, +.pure-form .pure-group textarea:focus { + z-index: 3; } + +.pure-form .pure-group input:first-child, +.pure-form .pure-group textarea:first-child { + top: 1px; + border-radius: 4px 4px 0 0; + margin: 0; } + +.pure-form .pure-group input:first-child:last-child, +.pure-form .pure-group textarea:first-child:last-child { + top: 1px; + border-radius: 4px; + margin: 0; } + +.pure-form .pure-group input:last-child, +.pure-form .pure-group textarea:last-child { + top: -2px; + border-radius: 0 0 4px 4px; + margin: 0; } + +.pure-form .pure-group button { + margin: 0.35em 0; } + +.pure-form .pure-input-1 { + width: 100%; } + +.pure-form .pure-input-3-4 { + width: 75%; } + +.pure-form .pure-input-2-3 { + width: 66%; } + +.pure-form .pure-input-1-2 { + width: 50%; } + +.pure-form .pure-input-1-3 { + width: 33%; } + +.pure-form .pure-input-1-4 { + width: 25%; } + +/* Inline help for forms */ +/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ +.pure-form .pure-help-inline, +.pure-form-message-inline { + display: inline-block; + padding-left: 0.3em; + color: #666; + vertical-align: middle; + font-size: 0.875em; } + +/* Block help for forms */ +.pure-form-message { + display: block; + color: #666; + font-size: 0.875em; } + +@media only screen and (max-width: 480px) { + .pure-form button[type="submit"] { + margin: 0.7em 0 0; } + + .pure-form input:not([type]), + .pure-form input[type="text"], + .pure-form input[type="password"], + .pure-form input[type="email"], + .pure-form input[type="url"], + .pure-form input[type="date"], + .pure-form input[type="month"], + .pure-form input[type="time"], + .pure-form input[type="datetime"], + .pure-form input[type="datetime-local"], + .pure-form input[type="week"], + .pure-form input[type="number"], + .pure-form input[type="search"], + .pure-form input[type="tel"], + .pure-form input[type="color"], + .pure-form label { + margin-bottom: 0.3em; + display: block; } + + .pure-group input:not([type]), + .pure-group input[type="text"], + .pure-group input[type="password"], + .pure-group input[type="email"], + .pure-group input[type="url"], + .pure-group input[type="date"], + .pure-group input[type="month"], + .pure-group input[type="time"], + .pure-group input[type="datetime"], + .pure-group input[type="datetime-local"], + .pure-group input[type="week"], + .pure-group input[type="number"], + .pure-group input[type="search"], + .pure-group input[type="tel"], + .pure-group input[type="color"] { + margin-bottom: 0; } + + .pure-form-aligned .pure-control-group label { + margin-bottom: 0.3em; + text-align: left; + display: block; + width: 100%; } + + .pure-form-aligned .pure-controls { + margin: 1.5em 0 0 0; } + + /* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline instead. */ + .pure-form .pure-help-inline, + .pure-form-message-inline, + .pure-form-message { + display: block; + font-size: 0.75em; + /* Increased bottom padding to make it group with its related input element. */ + padding: 0.2em 0 0.8em; } } +/*csslint adjoining-classes: false, box-model:false*/ +.pure-menu { + box-sizing: border-box; } + +.pure-menu-fixed { + position: fixed; + left: 0; + top: 0; + z-index: 3; } + +.pure-menu-list, +.pure-menu-item { + position: relative; } + +.pure-menu-list { + list-style: none; + margin: 0; + padding: 0; } + +.pure-menu-item { + padding: 0; + margin: 0; + height: 100%; } + +.pure-menu-link, +.pure-menu-heading { + display: block; + text-decoration: none; + white-space: nowrap; } + +/* HORIZONTAL MENU */ +.pure-menu-horizontal { + width: 100%; + white-space: nowrap; } + +.pure-menu-horizontal .pure-menu-list { + display: inline-block; } + +/* Initial menus should be inline-block so that they are horizontal */ +.pure-menu-horizontal .pure-menu-item, +.pure-menu-horizontal .pure-menu-heading, +.pure-menu-horizontal .pure-menu-separator { + display: inline-block; + *display: inline; + zoom: 1; + vertical-align: middle; } + +/* Submenus should still be display: block; */ +.pure-menu-item .pure-menu-item { + display: block; } + +.pure-menu-children { + display: none; + position: absolute; + left: 100%; + top: 0; + margin: 0; + padding: 0; + z-index: 3; } + +.pure-menu-horizontal .pure-menu-children { + left: 0; + top: auto; + width: inherit; } + +.pure-menu-allow-hover:hover > .pure-menu-children, +.pure-menu-active > .pure-menu-children { + display: block; + position: absolute; } + +/* Vertical Menus - show the dropdown arrow */ +.pure-menu-has-children > .pure-menu-link:after { + padding-left: 0.5em; + content: "\25B8"; + font-size: small; } + +/* Horizontal Menus - show the dropdown arrow */ +.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after { + content: "\25BE"; } + +/* scrollable menus */ +.pure-menu-scrollable { + overflow-y: scroll; + overflow-x: hidden; } + +.pure-menu-scrollable .pure-menu-list { + display: block; } + +.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list { + display: inline-block; } + +.pure-menu-horizontal.pure-menu-scrollable { + white-space: nowrap; + overflow-y: hidden; + overflow-x: auto; + -ms-overflow-style: none; + -webkit-overflow-scrolling: touch; + /* a little extra padding for this style to allow for scrollbars */ + padding: .5em 0; } + +.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar { + display: none; } + +/* misc default styling */ +.pure-menu-separator, +.pure-menu-horizontal .pure-menu-children .pure-menu-separator { + background-color: #ccc; + height: 1px; + margin: .3em 0; } + +.pure-menu-horizontal .pure-menu-separator { + width: 1px; + height: 1.3em; + margin: 0 0.3em; } + +/* Need to reset the separator since submenu is vertical */ +.pure-menu-horizontal .pure-menu-children .pure-menu-separator { + display: block; + width: auto; } + +.pure-menu-heading { + text-transform: uppercase; + color: #565d64; } + +.pure-menu-link { + color: #777; } + +.pure-menu-children { + background-color: #fff; } + +.pure-menu-link, +.pure-menu-disabled, +.pure-menu-heading { + padding: .5em 1em; } + +.pure-menu-disabled { + opacity: .5; } + +.pure-menu-disabled .pure-menu-link:hover { + background-color: transparent; } + +.pure-menu-active > .pure-menu-link, +.pure-menu-link:hover, +.pure-menu-link:focus { + background-color: #eee; } + +.pure-menu-selected .pure-menu-link, +.pure-menu-selected .pure-menu-link:visited { + color: #000; } + +.pure-table { + /* Remove spacing between table cells (from Normalize.css) */ + border-collapse: collapse; + border-spacing: 0; + empty-cells: show; + border: 1px solid #cbcbcb; } + +.pure-table caption { + color: #000; + font: italic 85%/1 arial, sans-serif; + padding: 1em 0; + text-align: center; } + +.pure-table td, +.pure-table th { + border-left: 1px solid #cbcbcb; + /* inner column border */ + border-width: 0 0 0 1px; + font-size: inherit; + margin: 0; + overflow: visible; + /*to make ths where the title is really long work*/ + padding: 0.5em 1em; + /* cell padding */ } + +/* Consider removing this next declaration block, as it causes problems when +there's a rowspan on the first cell. Case added to the tests. issue#432 */ +.pure-table td:first-child, +.pure-table th:first-child { + border-left-width: 0; } + +.pure-table thead { + background-color: #e0e0e0; + color: #000; + text-align: left; + vertical-align: bottom; } + +/* +striping: + even - #fff (white) + odd - #f2f2f2 (light gray) +*/ +.pure-table td { + background-color: transparent; } + +.pure-table-odd td { + background-color: #f2f2f2; } + +/* nth-child selector for modern browsers */ +.pure-table-striped tr:nth-child(2n-1) td { + background-color: #f2f2f2; } + +/* BORDERED TABLES */ +.pure-table-bordered td { + border-bottom: 1px solid #cbcbcb; } + +.pure-table-bordered tbody > tr:last-child > td { + border-bottom-width: 0; } + +/* HORIZONTAL BORDERED TABLES */ +.pure-table-horizontal td, +.pure-table-horizontal th { + border-width: 0 0 1px 0; + border-bottom: 1px solid #cbcbcb; } + +.pure-table-horizontal tbody > tr:last-child > td { + border-bottom-width: 0; } + +/*# sourceMappingURL=pure.css.map */ diff --git a/packages/demobank-ui/src/style/index.css b/packages/demobank-ui/src/style/index.css new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/demobank-ui/src/style/index.css diff --git a/packages/demobank-ui/src/template.html b/packages/demobank-ui/src/template.html new file mode 100644 index 000000000..6d8443130 --- /dev/null +++ b/packages/demobank-ui/src/template.html @@ -0,0 +1,52 @@ +<!-- + This file is part of GNU Taler + (C) 2021--2022 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 +--> +<!DOCTYPE html> +<html lang="en" class="has-aside-left has-aside-mobile-transition has-navbar-fixed-top has-aside-expanded"> + <head> + <meta charset="utf-8"> + <title><%= htmlWebpackPlugin.options.title %></title> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="apple-mobile-web-app-capable" content="yes"> + + <link rel="icon" href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" /> + <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" /> + + <% if (htmlWebpackPlugin.options.manifest.theme_color) { %> + <meta name="theme-color" content="<%= htmlWebpackPlugin.options.manifest.theme_color %>"> + <% } %> + + <% for (const index in htmlWebpackPlugin.files.css) { %> + <% const file = htmlWebpackPlugin.files.css[index] %> + <style data-href='<%= file %>' > + <%= compilation.assets[file.substr(htmlWebpackPlugin.files.publicPath.length)].source() %> + </style> + <% } %> + + </head> + <body> + + <script> + <%= compilation.assets[htmlWebpackPlugin.files.chunks["polyfills"].entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %> + </script> + <script> + <%= compilation.assets[htmlWebpackPlugin.files.chunks["bundle"].entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %> + </script> + + </body> +</html> diff --git a/packages/demobank-ui/tests/__mocks__/browserMocks.ts b/packages/demobank-ui/tests/__mocks__/browserMocks.ts new file mode 100644 index 000000000..5be8c3ce6 --- /dev/null +++ b/packages/demobank-ui/tests/__mocks__/browserMocks.ts @@ -0,0 +1,21 @@ +// Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage +/** + * An example how to mock localStorage is given below 👇 + */ + +/* +// Mocks localStorage +const localStorageMock = (function() { + let store = {}; + + return { + getItem: (key) => store[key] || null, + setItem: (key, value) => store[key] = value.toString(), + clear: () => store = {} + }; + +})(); + +Object.defineProperty(window, 'localStorage', { + value: localStorageMock +}); */ diff --git a/packages/demobank-ui/tests/__mocks__/fileMocks.ts b/packages/demobank-ui/tests/__mocks__/fileMocks.ts new file mode 100644 index 000000000..87109e355 --- /dev/null +++ b/packages/demobank-ui/tests/__mocks__/fileMocks.ts @@ -0,0 +1,3 @@ +// This fixed an error related to the CSS and loading gif breaking my Jest test +// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets +export default 'test-file-stub'; diff --git a/packages/demobank-ui/tests/__mocks__/setupTests.ts b/packages/demobank-ui/tests/__mocks__/setupTests.ts new file mode 100644 index 000000000..b0bebb589 --- /dev/null +++ b/packages/demobank-ui/tests/__mocks__/setupTests.ts @@ -0,0 +1,6 @@ +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-preact-pure'; + +configure({ + adapter: new Adapter() as any +}); diff --git a/packages/demobank-ui/tests/__tests__/homepage.js b/packages/demobank-ui/tests/__tests__/homepage.js new file mode 100644 index 000000000..9ea0ed410 --- /dev/null +++ b/packages/demobank-ui/tests/__tests__/homepage.js @@ -0,0 +1,466 @@ +import "core-js/stable"; +import "regenerator-runtime/runtime"; +import "@testing-library/jest-dom"; +import { BankHome } from '../../src/pages/home'; +import { h } from 'preact'; +import { waitFor, cleanup, render, fireEvent, screen } from '@testing-library/preact'; +import expect from 'expect'; +import fetchMock from "jest-fetch-mock"; + +/** + * This mock makes the translator always return the + * english string. It didn't work within the 'beforeAll' + * function... + */ +jest.mock("../../src/i18n") +const i18n = require("../../src/i18n") +i18n.useTranslator.mockImplementation(() => function(arg) {return arg}) + +beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + origin: "http://localhost", + pathname: "/demobanks/default" + } + }) + global.Storage.prototype.setItem = jest.fn((key, value) => {}) +}) + +function fillCredentialsForm() { + const username = Math.random().toString().substring(2); + const u = screen.getByPlaceholderText("username"); + const p = screen.getByPlaceholderText("password"); + fireEvent.input(u, {target: {value: username}}) + fireEvent.input(p, {target: {value: "bar"}}) + const signinButton = screen.getByText("Login"); + return { + username: username, + signinButton: signinButton + }; +} +fetchMock.enableMocks(); + +function mockSuccessLoginOrRegistration() { + fetch.once("{}", { + status: 200 + }).once(JSON.stringify({ + balance: { + amount: "EUR:10", + credit_debit_indicator: "credit" + }, + paytoUri: "payto://iban/123/ABC" + })) +} + +/** + * Render homepage -> navigate to register page -> submit registration. + * 'webMock' is called before submission to mock the server response + */ +function signUp(context, webMock) { + render(<BankHome />); + const registerPage = screen.getByText("Register!"); + fireEvent.click(registerPage); + const username = Math.random().toString().substring(2); + const u = screen.getByPlaceholderText("username"); + const p = screen.getByPlaceholderText("password"); + fireEvent.input(u, {target: {value: username}}) + fireEvent.input(p, {target: {value: "bar"}}) + const registerButton = screen.getByText("Register"); + webMock(); + fireEvent.click(registerButton); + context.username = username; + return context; +} + +describe("wire transfer", () => { + beforeEach(() => { + signUp({}, mockSuccessLoginOrRegistration); // context unused + }) + test("Wire transfer success", async () => { + const transferButton = screen.getByText("Create wire transfer"); + const payto = screen.getByPlaceholderText("payto address"); + fireEvent.input(payto, {target: {value: "payto://only-checked-by-the-backend!"}}) + fetch.once("{}"); // 200 OK + fireEvent.click(transferButton); + await screen.findByText("wire transfer created", {exact: false}) + }) + test("Wire transfer fail", async () => { + const transferButton = screen.getByText("Create wire transfer"); + const payto = screen.getByPlaceholderText("payto address"); + fireEvent.input(payto, {target: {value: "payto://only-checked-by-the-backend!"}}) + fetch.once("{}", {status: 400}); + fireEvent.click(transferButton); + // assert this below does NOT appear. + await waitFor(() => expect( + screen.queryByText("wire transfer created", {exact: false})).not.toBeInTheDocument()); + }) +}) + +describe("withdraw", () => { + afterEach(() => { + fetch.resetMocks(); + cleanup(); + }) + + + let context = {}; + // Register and land on the profile page. + beforeEach(() => { + context = signUp(context, mockSuccessLoginOrRegistration); + }) + + test("network failure before withdrawal creation", async () => { + const a = screen.getAllByPlaceholderText("amount")[0]; + fireEvent.input(a, {target: {value: "10"}}); + let withdrawButton = screen.getByText("Charge Taler wallet"); + // mock network failure. + fetch.mockReject("API is down"); + fireEvent.click(withdrawButton); + await screen.findByText("could not create withdrawal operation", {exact: false}) + }) + + test("HTTP response error upon withdrawal creation", async () => { + const a = screen.getAllByPlaceholderText("amount")[0]; + fireEvent.input(a, {target: {value: "10,0"}}); + let withdrawButton = screen.getByText("Charge Taler wallet"); + fetch.once("{}", {status: 404}); + fireEvent.click(withdrawButton); + await screen.findByText("gave response error", {exact: false}) + }) + + test("Abort withdrawal", async () => { + const a = screen.getAllByPlaceholderText("amount")[0]; + fireEvent.input(a, {target: {value: "10,0"}}); + let withdrawButton = screen.getByText("Charge Taler wallet"); + fetch.once(JSON.stringify({ + taler_withdraw_uri: "taler://withdraw/foo", + withdrawal_id: "foo" + })); + /** + * After triggering a withdrawal, check if the taler://withdraw URI + * rendered, and confirm if so. Lastly, check that a success message + * appeared on the screen. + */ + fireEvent.click(withdrawButton); + const abortButton = await screen.findByText("abort withdrawal", {exact: false}) + fireEvent.click(abortButton); + expect(fetch).toHaveBeenLastCalledWith( + `http://localhost/demobanks/default/access-api/accounts/${context.username}/withdrawals/foo/abort`, + expect.anything() + ) + await waitFor(() => expect( + screen.queryByText("abort withdrawal", {exact: false})).not.toBeInTheDocument()); + }) + + test("Successful withdrawal creation and confirmation", async () => { + const a = screen.getAllByPlaceholderText("amount")[0]; + fireEvent.input(a, {target: {value: "10,0"}}); + let withdrawButton = await screen.findByText("Charge Taler wallet"); + fetch.once(JSON.stringify({ + taler_withdraw_uri: "taler://withdraw/foo", + withdrawal_id: "foo" + })); + /** + * After triggering a withdrawal, check if the taler://withdraw URI + * rendered, and confirm if so. Lastly, check that a success message + * appeared on the screen. */ + fireEvent.click(withdrawButton); + expect(fetch).toHaveBeenCalledWith( + `http://localhost/demobanks/default/access-api/accounts/${context.username}/withdrawals`, + expect.objectContaining({body: JSON.stringify({amount: "EUR:10.0"})}) + ) + // assume wallet POSTed the payment details. + const confirmButton = await screen.findByText("confirm withdrawal", {exact: false}) + /** + * Not expecting a new withdrawal possibility while one is being processed. + */ + await waitFor(() => expect( + screen.queryByText("charge taler wallet", {exact: false})).not.toBeInTheDocument()); + fetch.once("{}") + // Confirm currently processed withdrawal. + fireEvent.click(confirmButton); + /** + * After having confirmed above, wait that the + * pre-withdrawal elements disappears and a success + * message appears. + */ + await waitFor(() => expect( + screen.queryByText( + "confirm withdrawal", + {exact: false})).not.toBeInTheDocument() + ); + await waitFor(() => expect( + screen.queryByText( + "give this address to the taler wallet", + {exact: false})).not.toBeInTheDocument() + ); + expect(fetch).toHaveBeenLastCalledWith( + `http://localhost/demobanks/default/access-api/accounts/${context.username}/withdrawals/foo/confirm`, + expect.anything()) + // success message + await screen.findByText("withdrawal confirmed", {exact: false}) + + /** + * Click on a "return to homepage / close" button, and + * check that the withdrawal confirmation is gone, and + * the option to withdraw again reappeared. + */ + const closeButton = await screen.findByText("close", {exact: false}) + fireEvent.click(closeButton); + + /** + * After closing the operation, the confirmation message is not expected. + */ + await waitFor(() => expect( + screen.queryByText("withdrawal confirmed", {exact: false})).not.toBeInTheDocument() + ); + + /** + * After closing the operation, the possibility to withdraw again should be offered. + */ + await waitFor(() => expect( + screen.queryByText( + "charge taler wallet", + {exact: false})).toBeInTheDocument() + ); + }) +}) + +describe("home page", () => { + afterEach(() => { + fetch.resetMocks(); + cleanup(); + }) + test("public histories", async () => { + render(<BankHome />); + /** + * Mock list of public accounts. 'bar' is + * the shown account, since it occupies the last + * position (and SPA picks it via the 'pop()' method) */ + fetch.once(JSON.stringify({ + "publicAccounts" : [ { + "balance" : "EUR:1", + "iban" : "XXX", + "accountLabel" : "foo" + }, { + "balance" : "EUR:2", + "iban" : "YYY", + "accountLabel" : "bar" + }] + })).once(JSON.stringify({ + transactions: [{ + debtorIban: "XXX", + debtorBic: "YYY", + debtorName: "Foo", + creditorIban: "AAA", + creditorBic: "BBB", + creditorName: "Bar", + direction: "DBIT", + amount: "EUR:5", + subject: "Reimbursement", + date: "1970-01-01" + }, { + debtorIban: "XXX", + debtorBic: "YYY", + debtorName: "Foo", + creditorIban: "AAA", + creditorBic: "BBB", + creditorName: "Bar", + direction: "CRDT", + amount: "EUR:5", + subject: "Bonus", + date: "2000-01-01" + }] + })).once(JSON.stringify({ + transactions: [{ + debtorIban: "XXX", + debtorBic: "YYY", + debtorName: "Foo", + creditorIban: "AAA", + creditorBic: "BBB", + creditorName: "Bar", + direction: "DBIT", + amount: "EUR:5", + subject: "Donation", + date: "1970-01-01" + }, { + debtorIban: "XXX", + debtorBic: "YYY", + debtorName: "Foo", + creditorIban: "AAA", + creditorBic: "BBB", + creditorName: "Bar", + direction: "CRDT", + amount: "EUR:5", + subject: "Refund", + date: "2000-01-01" + }] + })) + + // Navigate to dedicate public histories page. + const publicTxsPage = screen.getByText("transactions"); + fireEvent.click(publicTxsPage); + + /** + * Check that transactions data appears on the page. + */ + await screen.findByText("reimbursement", {exact: false}); + await screen.findByText("bonus", {exact: false}); + /** + * The transactions below should not appear, because only + * one public account renders. + */ + await waitFor(() => expect( + screen.queryByText("refund", {exact: false})).not.toBeInTheDocument()); + await waitFor(() => expect( + screen.queryByText("donation", {exact: false})).not.toBeInTheDocument()); + /** + * First HTTP mock: + */ + await expect(fetch).toHaveBeenCalledWith( + "http://localhost/demobanks/default/access-api/public-accounts" + ) + /** + * Only expecting this request (second mock), as SWR doesn't let + * the unshown history request to the backend: + */ + await expect(fetch).toHaveBeenCalledWith( + "http://localhost/demobanks/default/access-api/accounts/bar/transactions?page=0" + ) + /** + * Switch tab: + */ + let fooTab = await screen.findByText("foo", {exact: false}); + fireEvent.click(fooTab); + /** + * Last two HTTP mocks should render now: + */ + await screen.findByText("refund", {exact: false}); + await screen.findByText("donation", {exact: false}); + + // Expect SWR to have requested 'foo' history + // (consuming the last HTTP mock): + await expect(fetch).toHaveBeenCalledWith( + "http://localhost/demobanks/default/access-api/accounts/foo/transactions?page=0" + ) + let backButton = await screen.findByText("Go back", {exact: false}); + fireEvent.click(backButton); + await waitFor(() => expect( + screen.queryByText("donation", {exact: false})).not.toBeInTheDocument()); + await screen.findByText("welcome to eufin bank", {exact: false}) + }) + + // check page informs about the current balance + // after a successful registration. + + test("new registration response error 404", async () => { + var context = signUp({}, () => fetch.mockResponseOnce("Not found", {status: 404})); + await screen.findByText("has a problem", {exact: false}); + expect(fetch).toHaveBeenCalledWith( + "http://localhost/demobanks/default/access-api/testing/register", + expect.objectContaining( + {body: JSON.stringify({username: context.username, password: "bar"}), method: "POST"}, + )) + }) + + test("registration network failure", async () => { + let context = signUp({}, ()=>fetch.mockReject("API is down")); + await screen.findByText("has a problem", {exact: false}); + expect(fetch).toHaveBeenCalledWith( + "http://localhost/demobanks/default/access-api/testing/register", + expect.objectContaining( + {body: JSON.stringify({username: context.username, password: "bar"}), method: "POST"} + )) + }) + + test("login non existent user", async () => { + render(<BankHome />); + const { username, signinButton } = fillCredentialsForm(); + fetch.once("{}", {status: 404}); + fireEvent.click(signinButton); + await screen.findByText("username or account label not found", {exact: false}) + }) + test("login wrong credentials", async () => { + render(<BankHome />); + const { username, signinButton } = fillCredentialsForm(); + fetch.once("{}", {status: 401}); + fireEvent.click(signinButton); + await screen.findByText("wrong credentials given", {exact: false}) + }) + + /** + * Test that balance and last transactions get shown + * after a successful login. + */ + test("login success", async () => { + render(<BankHome />); + const { username, signinButton } = fillCredentialsForm(); + + // Response to balance request. + fetch.once(JSON.stringify({ + balance: { + amount: "EUR:10", + credit_debit_indicator: "credit" + }, + paytoUri: "payto://iban/123/ABC" + })).once(JSON.stringify({ // Response to history request. + transactions: [{ + debtorIban: "XXX", + debtorBic: "YYY", + debtorName: "Foo", + creditorIban: "AAA", + creditorBic: "BBB", + creditorName: "Bar", + direction: "DBIT", + amount: "EUR:5", + subject: "Donation", + date: "01-01-1970" + }, { + debtorIban: "XXX", + debtorBic: "YYY", + debtorName: "Foo", + creditorIban: "AAA", + creditorBic: "BBB", + creditorName: "Bar", + direction: "CRDT", + amount: "EUR:5", + subject: "Refund", + date: "01-01-2000" + }] + })) + fireEvent.click(signinButton); + expect(fetch).toHaveBeenCalledWith( + `http://localhost/demobanks/default/access-api/accounts/${username}`, + expect.anything() + ) + await screen.findByText("balance is 10 EUR", {exact: false}) + // The two transactions in the history mocked above. + await screen.findByText("refund", {exact: false}) + await screen.findByText("donation", {exact: false}) + expect(fetch).toHaveBeenCalledWith( + `http://localhost/demobanks/default/access-api/accounts/${username}/transactions?page=0`, + expect.anything() + ) + }) + + test("registration success", async () => { + let context = signUp({}, mockSuccessLoginOrRegistration); + /** + * Tests that a balance is shown after the successful + * registration. + */ + await screen.findByText("balance is 10 EUR", {exact: false}) + /** + * The expectation below tests whether the account + * balance was requested after the successful registration. + */ + expect(fetch).toHaveBeenCalledWith( + "http://localhost/demobanks/default/access-api/testing/register", + expect.anything() // no need to match auth headers. + ) + expect(fetch).toHaveBeenCalledWith( + `http://localhost/demobanks/default/access-api/accounts/${context.username}`, + expect.anything() // no need to match auth headers. + ) + }) +}) diff --git a/packages/demobank-ui/tests/declarations.d.ts b/packages/demobank-ui/tests/declarations.d.ts new file mode 100644 index 000000000..67e940277 --- /dev/null +++ b/packages/demobank-ui/tests/declarations.d.ts @@ -0,0 +1,3 @@ +// Enable enzyme adapter's integration with TypeScript +// See: https://github.com/preactjs/enzyme-adapter-preact-pure#usage-with-typescript +/// <reference types="enzyme-adapter-preact-pure" /> diff --git a/packages/demobank-ui/tsconfig.json b/packages/demobank-ui/tsconfig.json new file mode 100644 index 000000000..d04c5b964 --- /dev/null +++ b/packages/demobank-ui/tsconfig.json @@ -0,0 +1,60 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "ES5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */, + "module": "ESNext" /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation: */ + "allowJs": true /* Allow javascript files to be compiled. */, + // "checkJs": true, /* Report errors in .js files. */ + "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, + "jsxFactory": "h" /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */, + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "removeComments": true, /* Do not emit comments to output. */ + "noEmit": true /* Do not emit outputs. */, + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + "esModuleInterop": true /* */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */ + }, + "include": ["src/**/*", "tests/**/*"] +} diff --git a/packages/merchant-backend-ui/.gitignore b/packages/merchant-backend-ui/.gitignore new file mode 100644 index 000000000..a6ee22df2 --- /dev/null +++ b/packages/merchant-backend-ui/.gitignore @@ -0,0 +1,9 @@ +/build +/size-plugin.json +/storybook-static +/docs +/single +/coverage +/dist +/.rollup.cache +/.linaria-cache diff --git a/packages/merchant-backend-ui/.storybook/.babelrc b/packages/merchant-backend-ui/.storybook/.babelrc new file mode 100644 index 000000000..610b6f339 --- /dev/null +++ b/packages/merchant-backend-ui/.storybook/.babelrc @@ -0,0 +1,25 @@ +/* + 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) + */ +{ + "presets": [ + "preact-cli/babel" + ] +}
\ No newline at end of file diff --git a/packages/merchant-backend-ui/.storybook/main.js b/packages/merchant-backend-ui/.storybook/main.js new file mode 100644 index 000000000..5497a6510 --- /dev/null +++ b/packages/merchant-backend-ui/.storybook/main.js @@ -0,0 +1,82 @@ +/* + 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) +*/ + + +module.exports = { + "stories": [ + "../src/**/*.stories.mdx", + "../src/**/*.stories.@(js|jsx|ts|tsx)" + ], + "addons": [ + "@storybook/preset-scss", + "@storybook/addon-a11y", + "@storybook/addon-essentials" //docs, control, actions, viewpot, toolbar, background + ], + // sb does not yet support new jsx transform by default + // https://github.com/storybookjs/storybook/issues/12881 + // https://github.com/storybookjs/storybook/issues/12952 + babel: async (options) => ({ + ...options, + presets: [ + ...options.presets, + [ + '@babel/preset-react', { + runtime: 'automatic', + }, + 'preset-react-jsx-transform' + ], + "@linaria", + ], + }), + webpackFinal: (config) => { + // should be removed after storybook 6.3 + // https://github.com/storybookjs/storybook/issues/12853#issuecomment-821576113 + config.resolve.alias = { + react: "preact/compat", + "react-dom": "preact/compat", + }; + + // we need to add @linaria loader AFTER the babel-loader + // https://github.com/callstack/linaria/blob/master/docs/BUNDLERS_INTEGRATION.md#webpack + config.module.rules[0] = { + ...(config.module.rules[0]), + loader: undefined, // Disable the predefined babel-loader on the rule + use: [ + { + ...(config.module.rules[0].use[0]), + loader: 'babel-loader', + }, + { + loader: '@linaria/webpack-loader', + options: { + sourceMap: true, //always true since this is dev + babelOptions: { + presets: config.module.rules[0].use[0].options.presets, + } + // Pass the current babel options to linaria's babel instance + } + } + ] + }; + + return config; + }, +}
\ No newline at end of file diff --git a/packages/merchant-backend-ui/.storybook/preview.js b/packages/merchant-backend-ui/.storybook/preview.js new file mode 100644 index 000000000..a9cc4c39a --- /dev/null +++ b/packages/merchant-backend-ui/.storybook/preview.js @@ -0,0 +1,73 @@ +/* + 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/> + */ + +import { ConfigContextProvider } from '../src/context/config' +import { InstanceContextProvider } from '../src/context/instance' +import { TranslationProvider } from '../src/context/translation' +import { BackendContextProvider } from '../src/context/backend' +import { h } from 'preact'; + +const mockConfig = { + backendURL: 'http://demo.taler.net', + currency: 'TESTKUDOS' +} + +const mockInstance = { + id: 'instance-id', + token: 'instance-token', + admin: false, +} + +const mockBackend = { + url: 'http://merchant.url', + token: 'default-token', + triedToLog: false, +} + +export const parameters = { + controls: { expanded: true }, + // actions: { argTypesRegex: "^on.*" }, +} + +export const globalTypes = { + locale: { + name: 'Locale', + description: 'Internationalization locale', + defaultValue: 'en', + toolbar: { + icon: 'globe', + items: [ + { value: 'en', right: '🇺🇸', title: 'English' }, + { value: 'es', right: '🇪🇸', title: 'Spanish' }, + ], + }, + }, +}; + +export const decorators = [ + (Story, { globals }) => <TranslationProvider initial='en' forceLang={globals.locale}> + <Story /> + </TranslationProvider>, + (Story) => <ConfigContextProvider value={mockConfig}> + <Story /> + </ConfigContextProvider>, + (Story) => <InstanceContextProvider value={mockInstance}> + <Story /> + </InstanceContextProvider>, + (Story) => <BackendContextProvider defaultUrl={mockBackend.url}> + <Story /> + </BackendContextProvider>, +]; diff --git a/packages/merchant-backend-ui/README.md b/packages/merchant-backend-ui/README.md new file mode 100644 index 000000000..16cbd0b9a --- /dev/null +++ b/packages/merchant-backend-ui/README.md @@ -0,0 +1,30 @@ +Merchant Backend pages + +# Description + +This project generate 5 templates for the merchant backend: + + * DepletedTip + * OfferRefund + * OfferTip + * RequestPayment + * ShowOrderDetails + +This pages are to be serve from the merchant-backend service and will be queried for browser that may or may not have javascript enabled, so we are going to do server side rendering. +The merchant-backend service is currently supporting mustache library for server side rendering. +We also want the be able to create a more interactive design if the browser have javascript enabled, so the pages will be serve with all the infromation in the html but also in javascript. + +In this scenario, we are using jsx to build the template of the page that will be build-time rendered into the mustache template. This template can the be deployed into a merchant-backend that will complete the information before send it to the browser. + +# Building + +The building process can be executed with `pnpm build` + +# Testing + +This project is using a javascript implementation of mustache that can be executed with the command `pnpm render-examples`. +This script will take the pages previously built in `dist/pages` directory and the examples definition in the `src/pages/[exampleName].examples.ts` files and render a to-be-sent-to-the-user page like the merchant would do. +This examples will be saved invidivualy into directory `dist/examples` and should be opened with your testing browser. +Testing should be done with javascript enabled and javascript disabled, both should look ok. + + diff --git a/packages/merchant-backend-ui/contrib/po2ts b/packages/merchant-backend-ui/contrib/po2ts new file mode 100755 index 000000000..a135da61b --- /dev/null +++ b/packages/merchant-backend-ui/contrib/po2ts @@ -0,0 +1,42 @@ +#!/usr/bin/env node +/* + This file is part of GNU Taler + (C) 2020 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/> + */ + +/** + * Convert a <lang>.po file into a JavaScript / TypeScript expression. + */ + +const po2json = require("po2json"); + +const filename = process.argv[2]; + +if (!filename) { + console.error("error: missing filename"); + process.exit(1); +} + +const m = filename.match(/([a-zA-Z0-9-_]+).po/); + +if (!m) { + console.error("error: unexpected filename (expected <lang>.po)"); + process.exit(1); +} + +const lang = m[1]; +const pojson = po2json.parseFileSync(filename, { format: "jed1.x", fuzzy: true }); +const s = + "strings['" + lang + "'] = " + JSON.stringify(pojson, null, " ") + ";\n"; +console.log(s); diff --git a/packages/merchant-backend-ui/copyleft-header.js b/packages/merchant-backend-ui/copyleft-header.js new file mode 100644 index 000000000..0794cb839 --- /dev/null +++ b/packages/merchant-backend-ui/copyleft-header.js @@ -0,0 +1,15 @@ +/* + 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/> + */ diff --git a/packages/merchant-backend-ui/package.json b/packages/merchant-backend-ui/package.json new file mode 100644 index 000000000..4a30def96 --- /dev/null +++ b/packages/merchant-backend-ui/package.json @@ -0,0 +1,144 @@ +{ + "private": true, + "name": "merchant-backend", + "version": "0.0.4", + "license": "MIT", + "scripts": { + "build": "rollup -c", + "dev": "rollup -c -w", + "render-examples": "ts-node -O '{\"module\": \"commonjs\"}' -T render-examples.ts dist/pages dist/examples", + "lint-check": "eslint '{src,tests}/**/*.{js,jsx,ts,tsx}'", + "lint-fix": "eslint --fix '{src,tests}/**/*.{js,jsx,ts,tsx}'", + "test": "jest ./tests", + "dev-test": "jest ./tests --watch", + "typedoc": "typedoc src", + "clean": "rimraf build storybook-static docs single dist", + "serve-dist": "sirv --port ${PORT:=8080} --cors --single dist", + "build-storybook": "build-storybook", + "storybook": "start-storybook -p 6006" + }, + "engines": { + "node": ">=12", + "pnpm": ">=5" + }, + "eslintConfig": { + "parser": "@typescript-eslint/parser", + "extends": [ + "preact", + "plugin:@typescript-eslint/recommended" + ], + "plugins": [ + "header" + ], + "rules": { + "header/header": [ + 2, + "copyleft-header.js" + ] + }, + "ignorePatterns": [ + "build/" + ] + }, + "dependencies": { + "@gnu-taler/taler-util": "workspace:*", + "axios": "^0.21.1", + "date-fns": "^2.21.1", + "history": "4.10.1", + "jed": "^1.1.1", + "preact": "^10.5.13", + "preact-router": "^3.2.1", + "qrcode-generator": "^1.4.4", + "swr": "^0.5.5", + "yup": "^0.32.9" + }, + "devDependencies": { + "@babel/core": "^7.13.16", + "@babel/plugin-transform-react-jsx-source": "^7.12.13", + "@creativebulma/bulma-tooltip": "^1.2.0", + "@gnu-taler/pogen": "^0.0.5", + "@linaria/babel-preset": "^3.0.0-beta.4", + "@linaria/core": "^3.0.0-beta.4", + "@linaria/react": "^3.0.0-beta.7", + "@linaria/rollup": "^3.0.0-beta.7", + "@linaria/shaker": "^3.0.0-beta.7", + "@linaria/webpack-loader": "^3.0.0-beta.7", + "@rollup/plugin-alias": "^3.1.5", + "@rollup/plugin-babel": "^5.3.0", + "@rollup/plugin-commonjs": "^20.0.0", + "@rollup/plugin-html": "^0.2.3", + "@rollup/plugin-image": "^2.1.1", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-replace": "^3.0.0", + "@rollup/plugin-typescript": "^8.2.5", + "@storybook/addon-a11y": "^6.2.9", + "@storybook/addon-actions": "^6.2.9", + "@storybook/addon-essentials": "^6.2.9", + "@storybook/addon-links": "^6.2.9", + "@storybook/preact": "^6.2.9", + "@storybook/preset-scss": "^1.0.3", + "@testing-library/preact": "^2.0.1", + "@testing-library/preact-hooks": "^1.1.0", + "@types/enzyme": "^3.10.8", + "@types/history": "^4.7.8", + "@types/jest": "^26.0.23", + "@types/mocha": "^8.2.2", + "@types/mustache": "^4.1.2", + "@typescript-eslint/eslint-plugin": "^4.22.0", + "@typescript-eslint/parser": "^4.22.0", + "babel-loader": "^8.2.2", + "base64-inline-loader": "^1.1.1", + "bulma": "^0.9.2", + "bulma-checkbox": "^1.1.1", + "bulma-radio": "^1.1.1", + "bulma-responsive-tables": "^1.2.3", + "bulma-switch-control": "^1.1.1", + "bulma-timeline": "^3.0.4", + "bulma-upload-control": "^1.2.0", + "dotenv": "^8.2.0", + "enzyme": "^3.11.0", + "enzyme-adapter-preact-pure": "^3.1.0", + "eslint": "^7.25.0", + "eslint-config-preact": "^1.1.4", + "eslint-plugin-header": "^3.1.1", + "html-webpack-inline-chunk-plugin": "^1.1.1", + "html-webpack-inline-source-plugin": "0.0.10", + "html-webpack-skip-assets-plugin": "^1.0.1", + "inline-chunk-html-plugin": "^1.1.1", + "jest": "^26.6.3", + "jest-preset-preact": "^4.0.2", + "mustache": "^4.2.0", + "po2json": "^0.4.5", + "preact-cli": "^3.0.5", + "preact-render-to-json": "^3.6.6", + "preact-render-to-string": "^5.1.19", + "rimraf": "^3.0.2", + "rollup": "^2.56.3", + "rollup-plugin-bundle-html": "^0.2.2", + "rollup-plugin-css-only": "^3.1.0", + "sass": "^1.32.13", + "sass-loader": "10.1.1", + "script-ext-html-webpack-plugin": "^2.1.5", + "sirv-cli": "^1.0.11", + "tslib": "^2.3.1", + "typedoc": "^0.20.36", + "typescript": "^4.2.4" + }, + "jest": { + "preset": "jest-preset-preact", + "transformIgnorePatterns": [ + "node_modules/.pnpm/(?!(@gnu-taler\\+taler-util))", + "\\.pnp\\.[^\\/]+$" + ], + "setupFiles": [ + "<rootDir>/tests/__mocks__/browserMocks.ts", + "<rootDir>/tests/__mocks__/setupTests.ts" + ], + "moduleNameMapper": { + "\\.(css|less)$": "identity-obj-proxy" + }, + "transform": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|po)$": "<rootDir>/tests/__mocks__/fileTransformer.js" + } + } +} diff --git a/packages/merchant-backend-ui/render-examples.ts b/packages/merchant-backend-ui/render-examples.ts new file mode 100644 index 000000000..47300ab8f --- /dev/null +++ b/packages/merchant-backend-ui/render-examples.ts @@ -0,0 +1,83 @@ +/* + 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 mustache from "mustache"; +import fs from "fs"; +import { format, formatDuration, intervalToDuration } from "date-fns"; + +/** + * This script will emulate what the merchant backend will do when being requested + * +*/ + +const sourceDirectory = process.argv[2] +const destDirectory = process.argv[3] + +if (!sourceDirectory || !destDirectory) { + console.log('usage: render-mustache <source-directory> <dest-directory>') + process.exit(1); +} + +if (!fs.existsSync(destDirectory)) { + fs.mkdirSync(destDirectory); +} + +/** + * Load all the html files + */ +const files = fs.readdirSync(sourceDirectory).filter(f => /.html/.test(f)) + +files.forEach(file => { + const html = fs.readFileSync(`${sourceDirectory}/${file}`, 'utf8') + + const testName = file.replace('.html', '') + if (testName !== 'ShowOrderDetails') return; + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { exampleData } = require(`./src/pages/${testName}.examples`) + + Object.keys(exampleData).forEach(exampleName => { + const example = exampleData[exampleName] + + //enhance the example with more information + example.contract_terms_json = () => JSON.stringify(example.contract_terms); + example.contract_terms.timestamp_str = () => example.contract_terms.timestamp && format(example.contract_terms.timestamp.t_s, 'dd MMM yyyy HH:mm:ss'); + + example.contract_terms.hasProducts = () => example.contract_terms.products?.length > 0; + example.contract_terms.hasAuditors = () => example.contract_terms.auditors?.length > 0; + example.contract_terms.hasExchanges = () => example.contract_terms.exchanges?.length > 0; + + example.contract_terms.products.forEach(p => { + p.delivery_date_str = () => p.delivery_date && format(p.delivery_date.t_s, 'dd MM yyyy HH:mm:ss') + p.hasTaxes = () => p.taxes?.length > 0 + }) + example.contract_terms.has_delivery_info = () => example.contract_terms.delivery_date || example.contract_terms.delivery_location + + example.contract_terms.delivery_date_str = () => example.contract_terms.delivery_date && format(example.contract_terms.delivery_date.t_s, 'dd MM yyyy HH:mm:ss') + example.contract_terms.pay_deadline_str = () => example.contract_terms.pay_deadline && format(example.contract_terms.pay_deadline.t_s, 'dd MM yyyy HH:mm:ss') + example.contract_terms.wire_transfer_deadline_str = () => example.contract_terms.wire_transfer_deadline && format(example.contract_terms.wire_transfer_deadline.t_s, 'dd MM yyyy HH:mm:ss') + example.contract_terms.refund_deadline_str = () => example.contract_terms.refund_deadline && format(example.contract_terms.refund_deadline.t_s, 'dd MM yyyy HH:mm:ss') + example.contract_terms.auto_refund_str = () => example.contract_terms.auto_refund && formatDuration(intervalToDuration({ start: 0, end: example.contract_terms.auto_refund.d_us })) + + const output = mustache.render(html, example); + + fs.writeFileSync(`${destDirectory}/${testName}.${exampleName}.html`, output) + }) +}) diff --git a/packages/merchant-backend-ui/rollup.config.js b/packages/merchant-backend-ui/rollup.config.js new file mode 100644 index 000000000..f5227ba74 --- /dev/null +++ b/packages/merchant-backend-ui/rollup.config.js @@ -0,0 +1,112 @@ +/* + 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/> + */ + +// rollup.config.js +import linaria from '@linaria/rollup'; +import nodeResolve from "@rollup/plugin-node-resolve"; +import alias from "@rollup/plugin-alias"; +import image from '@rollup/plugin-image'; +import json from "@rollup/plugin-json"; +import ts from "@rollup/plugin-typescript"; +import replace from "@rollup/plugin-replace"; +import css from 'rollup-plugin-css-only'; +import html from '@rollup/plugin-html'; +import commonjs from "@rollup/plugin-commonjs"; + + + +const template = async ({ + files, +}) => { + const scripts = (files.js || []).map(({ code }) => `<script>${code}</script>`).join('\n'); + const css = (files.css || []).map(({ source }) => `<style>${source}</style>`).join('\n'); + const ssr = (files.js || []).map(({ code }) => code).join('\n'); + const page = new Function(`${ssr}; return page.buildTimeRendering();`)() + return ` +<!doctype html> +<html> + <head> + ${page.head} + ${css} + </head> + <script id="built_time_data"> + </script> + <body> + ${page.body} + ${scripts} + <script>page.mount()</script> + </body> +</html>`; +}; + +const makePlugins = (name) => [ + alias({ + entries: [ + { find: 'react', replacement: 'preact/compat' }, + { find: 'react-dom', replacement: 'preact/compat' } + ] + }), + + replace({ + "process.env.NODE_ENV": JSON.stringify("production"), + preventAssignment: true, + }), + + commonjs({ + include: [/node_modules/, /dist/], + extensions: [".js"], + ignoreGlobal: true, + sourceMap: true, + }), + + nodeResolve({ + browser: true, + preferBuiltins: true, + }), + + json(), + image(), + + linaria({ + sourceMap: process.env.NODE_ENV !== 'production', + }), + css(), + ts({ + sourceMap: false, + outputToFilesystem: false, + }), + html({ template, fileName: name }), +]; + + +const pageDefinition = (name) => ({ + input: `src/pages/${name}.tsx`, + output: { + file: `dist/pages/${name}.js`, + format: "iife", + exports: 'named', + name: 'page', + }, + plugins: makePlugins(`${name}.html`), +}); + +export default [ + pageDefinition("OfferTip"), + pageDefinition("OfferRefund"), + pageDefinition("DepletedTip"), + pageDefinition("RequestPayment"), + pageDefinition("ShowOrderDetails"), +] diff --git a/packages/merchant-backend-ui/src/assets/empty.png b/packages/merchant-backend-ui/src/assets/empty.png Binary files differnew file mode 100644 index 000000000..5120d3138 --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/empty.png diff --git a/packages/merchant-backend-ui/src/assets/icons/android-chrome-192x192.png b/packages/merchant-backend-ui/src/assets/icons/android-chrome-192x192.png Binary files differnew file mode 100644 index 000000000..93ebe2e2c --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/icons/android-chrome-192x192.png diff --git a/packages/merchant-backend-ui/src/assets/icons/android-chrome-512x512.png b/packages/merchant-backend-ui/src/assets/icons/android-chrome-512x512.png Binary files differnew file mode 100644 index 000000000..52d1623ea --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/icons/android-chrome-512x512.png diff --git a/packages/merchant-backend-ui/src/assets/icons/apple-touch-icon.png b/packages/merchant-backend-ui/src/assets/icons/apple-touch-icon.png Binary files differnew file mode 100644 index 000000000..254e4bb4d --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/icons/apple-touch-icon.png diff --git a/packages/merchant-backend-ui/src/assets/icons/favicon-16x16.png b/packages/merchant-backend-ui/src/assets/icons/favicon-16x16.png Binary files differnew file mode 100644 index 000000000..e81177dcb --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/icons/favicon-16x16.png diff --git a/packages/merchant-backend-ui/src/assets/icons/favicon-32x32.png b/packages/merchant-backend-ui/src/assets/icons/favicon-32x32.png Binary files differnew file mode 100644 index 000000000..40e9b5b47 --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/icons/favicon-32x32.png diff --git a/packages/merchant-backend-ui/src/assets/icons/languageicon.svg b/packages/merchant-backend-ui/src/assets/icons/languageicon.svg new file mode 100644 index 000000000..22d58da65 --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/icons/languageicon.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 2411.2 2794" style="enable-background:new 0 0 2411.2 2794;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+</style>
+<g id="Layer_2">
+</g>
+<g id="Layer_x5F_1_x5F_1">
+ <g>
+ <polygon points="1204.6,359.2 271.8,30 271.8,2060.1 1204.6,1758.3 "/>
+ <polygon class="st0" points="1182.2,358.1 2150.6,29 2150.6,2059 1182.2,1757.3 "/>
+ <polygon class="st0" points="30,2415.4 1182.2,2031.4 1182.2,357.9 30,742 "/>
+ <polygon points="1707.2,2440.7 1870.5,2709.4 1956.6,2459.8 "/>
+ <g>
+ <path d="M421.7,934.8c-6.1-6,8,49.1,27.6,68.9c34.8,35.1,61.9,39.6,76.4,40.2c32,1.3,71.5-8,94.9-17.8
+ c22.7-9.7,62.4-30,77.5-59.6c3.2-6.3,11.9-17,6.4-43.2c-4.2-20.2-17-27.3-32.7-26.2c-15.7,1.1-63.2,13.7-86.1,20.8
+ c-23,7-70.3,21.4-90.9,25.8C474.3,948.2,429,941.7,421.7,934.8z"/>
+ <path d="M1003.1,1593.7c-9.1-3.3-196.9-81.1-223.6-93.9c-21.8-10.5-75.2-33.1-100.4-43.3c70.8-109.2,115.5-191.6,121.5-204.1
+ c11-23,86-169.6,87.7-178.7c1.7-9.1,3.8-42.9,2.2-51c-1.7-8.2-29.1,7.6-66.4,20.2c-37.4,12.6-108.4,58.8-135.8,64.6
+ c-27.5,5.7-115.5,39.1-160.5,54c-45,14.9-130.2,40.9-165.2,50.4c-35.1,9.5-65.7,10.2-85.3,16.2c0,0,2.6,27.5,7.8,35.7
+ c5.2,8.2,23.7,28.4,45.3,34.1c21.6,5.7,57.3,3.4,73.6-0.3c16.3-3.8,44.4-17.5,48.2-23.6c3.8-6.1-2-24.9,4.5-30.6
+ c6.5-5.6,92.2-25.7,124.6-35.4c32.4-10,156.3-52.6,173.1-50.5c-5.3,17.7-105,215.1-137.1,274c-32.1,58.9-218.6,318-258.3,363.6
+ c-30.1,34.7-103.2,123.5-128.5,143.6c6.4,1.8,51.6-2.1,59.9-7.2c51.3-31.6,136.9-138.1,164.4-170.5
+ c81.9-96,153.8-196.8,210.8-283.4h0.1c11.1,4.6,100.9,77.8,124.4,94c23.4,16.2,115.9,67.8,136,76.4c20,8.7,97.1,44.2,100.3,32.2
+ C1029.4,1668,1012.2,1597.1,1003.1,1593.7z"/>
+ </g>
+ <path class="st1" d="M569,2572c18,11,35,20,54,29c38,19,81,39,122,54c56,21,112,38,168,51c31,7,65,13,98,18c3,0,92,11,110,11h90
+ c35-3,68-5,103-10c28-4,59-9,89-16c22-5,45-10,67-17c21-6,45-14,68-22c15-5,31-12,47-18c13-6,29-13,44-19c18-8,39-19,59-29
+ c16-8,34-18,51-28c13-7,43-30,59-30c18,0,30,16,30,30c0,29-39,38-57,51c-19,13-42,23-62,34c-40,21-81,39-120,54
+ c-51,19-107,37-157,49c-19,4-38,9-57,12c-10,2-114,18-143,18h-132c-35-3-72-7-107-12c-31-5-64-11-95-18c-24-5-50-12-73-19
+ c-40-11-79-25-117-40c-69-26-141-60-209-105c-12-8-13-16-13-25c0-15,11-29,29-29C531,2546,563,2569,569,2572z"/>
+ <path class="st1" d="M1151,2009L61,2372V764l1090-363V2009z M1212,354v1680c-1,5-3,10-7,15c-2,3-6,7-9,8c-25,10-1151,388-1166,388
+ c-12,0-23-8-29-21c0-1-1-2-1-4V739c2-5,3-12,7-16c8-11,22-13,31-16c17-6,1126-378,1142-378C1190,329,1212,336,1212,354z"/>
+ <path class="st1" d="M2120,2017l-907-282V380l907-308V2017z M2181,32v2023c-1,23-17,33-32,33c-13,0-107-32-123-37
+ c-126-39-253-78-378-117c-28-9-57-18-84-27c-24-7-50-15-74-23c-107-33-216-66-323-102c-4-1-14-15-14-18V351c2-5,4-11,9-15
+ c8-9,351-123,486-168c36-13,487-168,501-168C2167,0,2181,13,2181,32z"/>
+ <polygon points="2411.2,2440.7 1199.5,2054.5 1204.6,373.2 2411.2,757.2 "/>
+ <g>
+ <path class="st2" d="M1800.3,1124.6L1681.4,1412l218.6,66.3L1800.3,1124.6z M1729,853.2l156.1,47.3l284.4,1025l-160.3-48.7
+ l-57.6-210.4L1620.2,1566l-71.3,171.4l-160.4-48.7L1729,853.2z"/>
+ </g>
+ </g>
+</g>
+</svg>
diff --git a/packages/merchant-backend-ui/src/assets/icons/mstile-150x150.png b/packages/merchant-backend-ui/src/assets/icons/mstile-150x150.png Binary files differnew file mode 100644 index 000000000..9cfb889be --- /dev/null +++ b/packages/merchant-backend-ui/src/assets/icons/mstile-150x150.png diff --git a/packages/merchant-backend-ui/src/components/Footer.tsx b/packages/merchant-backend-ui/src/components/Footer.tsx new file mode 100644 index 000000000..5f2957800 --- /dev/null +++ b/packages/merchant-backend-ui/src/components/Footer.tsx @@ -0,0 +1,32 @@ +/* + 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 { FooterBar } from '../styled'; + +export function Footer(): VNode { + return <FooterBar> + <p> + <a href="https://taler.net/">Learn more about GNU Taler on our website.</a> + <p>Copyright © 2014—2021 Taler Systems SA</p> + </p> + </FooterBar> +} + diff --git a/packages/merchant-backend-ui/src/components/QR.tsx b/packages/merchant-backend-ui/src/components/QR.tsx new file mode 100644 index 000000000..29c9920bf --- /dev/null +++ b/packages/merchant-backend-ui/src/components/QR.tsx @@ -0,0 +1,41 @@ +/* + 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/> + */ + + import { h, VNode } from "preact"; + import { useEffect, useRef } from "preact/hooks"; + import qrcode from "qrcode-generator"; + +export function createSVG(text:string):string { + const qr = qrcode(0, 'L'); + qr.addData(text); + qr.make(); + return qr.createSvgTag({ + scalable: true, + margin: 0 + }); +} + + export function QR({ text }: { text: string; }):VNode { + const divRef = useRef<HTMLDivElement>(null); + useEffect(() => { + divRef.current.innerHTML = createSVG(text) + }); + + return <div style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }}> + <div style={{ width: '50%', minWidth: 200, maxWidth: 300 }} ref={divRef} /> + </div>; + } +
\ No newline at end of file diff --git a/packages/merchant-backend-ui/src/context/backend.ts b/packages/merchant-backend-ui/src/context/backend.ts new file mode 100644 index 000000000..a920d6ffc --- /dev/null +++ b/packages/merchant-backend-ui/src/context/backend.ts @@ -0,0 +1,82 @@ +/* + 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 { createContext, h, VNode } from 'preact' +import { useCallback, useContext, useState } from 'preact/hooks' +import { useBackendDefaultToken, useBackendURL } from '../hooks'; + +interface BackendContextType { + url: string; + token?: string; + triedToLog: boolean; + resetBackend: () => void; + clearAllTokens: () => void; + addTokenCleaner: (c: () => void) => void; + updateLoginStatus: (url: string, token?: string) => void; +} + +const BackendContext = createContext<BackendContextType>({ + url: '', + token: undefined, + triedToLog: false, + resetBackend: () => null, + clearAllTokens: () => null, + addTokenCleaner: () => null, + updateLoginStatus: () => null, +}) + +function useBackendContextState(defaultUrl?: string): BackendContextType { + const [url, triedToLog, changeBackend, resetBackend] = useBackendURL(defaultUrl); + const [token, _updateToken] = useBackendDefaultToken(); + const updateToken = (t?: string) => { + _updateToken(t) + } + + const tokenCleaner = useCallback(() => { updateToken(undefined) }, []) + const [cleaners, setCleaners] = useState([tokenCleaner]) + const addTokenCleaner = (c: () => void) => setCleaners(cs => [...cs, c]) + const addTokenCleanerMemo = useCallback((c: () => void) => { addTokenCleaner(c) }, [tokenCleaner]) + + const clearAllTokens = () => { + cleaners.forEach(c => c()) + for (let i = 0; i < localStorage.length; i++) { + const k = localStorage.key(i) + if (k && /^backend-token/.test(k)) localStorage.removeItem(k) + } + resetBackend() + } + + const updateLoginStatus = (url: string, token?: string) => { + changeBackend(url); + if (token) updateToken(token); + }; + + + return { url, token, triedToLog, updateLoginStatus, resetBackend, clearAllTokens, addTokenCleaner: addTokenCleanerMemo } +} + +export const BackendContextProvider = ({ children, defaultUrl }: { children: any, defaultUrl?: string }): VNode => { + const value = useBackendContextState(defaultUrl) + + return h(BackendContext.Provider, { value, children }); +} + +export const useBackendContext = (): BackendContextType => useContext(BackendContext); diff --git a/packages/merchant-backend-ui/src/context/config.ts b/packages/merchant-backend-ui/src/context/config.ts new file mode 100644 index 000000000..5cd772380 --- /dev/null +++ b/packages/merchant-backend-ui/src/context/config.ts @@ -0,0 +1,32 @@ +/* + 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 { createContext } from 'preact' +import { useContext } from 'preact/hooks' + +interface Type { + currency: string; + version: string; +} +const Context = createContext<Type>(null!) + +export const ConfigContextProvider = Context.Provider +export const useConfigContext = (): Type => useContext(Context); diff --git a/packages/merchant-backend-ui/src/context/fetch.ts b/packages/merchant-backend-ui/src/context/fetch.ts new file mode 100644 index 000000000..52a4f9c8d --- /dev/null +++ b/packages/merchant-backend-ui/src/context/fetch.ts @@ -0,0 +1,40 @@ +/* + 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, createContext, VNode, ComponentChildren } from 'preact' +import { useContext } from 'preact/hooks' +import useSWR, { trigger, useSWRInfinite, cache, mutate } from 'swr'; + +interface Type { + useSWR: typeof useSWR, + useSWRInfinite: typeof useSWRInfinite, +} + +const Context = createContext<Type>({} as any) + +export const useFetchContext = (): Type => useContext(Context); +export const FetchContextProvider = ({ children }: { children: ComponentChildren }): VNode => { + return h(Context.Provider, { value: { useSWR, useSWRInfinite }, children }); +} + +export const FetchContextProviderTesting = ({ children, data }: { children: ComponentChildren, data:any }): VNode => { + return h(Context.Provider, { value: { useSWR: () => data, useSWRInfinite }, children }); +} diff --git a/packages/merchant-backend-ui/src/context/instance.ts b/packages/merchant-backend-ui/src/context/instance.ts new file mode 100644 index 000000000..fecf36426 --- /dev/null +++ b/packages/merchant-backend-ui/src/context/instance.ts @@ -0,0 +1,35 @@ +/* + 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 { createContext } from 'preact' +import { useContext } from 'preact/hooks' + +interface Type { + id: string; + token?: string; + admin?: boolean; + changeToken: (t?:string) => void; +} + +const Context = createContext<Type>({} as any) + +export const InstanceContextProvider = Context.Provider +export const useInstanceContext = (): Type => useContext(Context); diff --git a/packages/merchant-backend-ui/src/context/listener.ts b/packages/merchant-backend-ui/src/context/listener.ts new file mode 100644 index 000000000..659db0a03 --- /dev/null +++ b/packages/merchant-backend-ui/src/context/listener.ts @@ -0,0 +1,35 @@ +/* + 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 { createContext } from 'preact' +import { useContext } from 'preact/hooks' + +interface Type { + id: string; + token?: string; + admin?: boolean; + changeToken: (t?:string) => void; +} + +const Context = createContext<Type>({} as any) + +export const ListenerContextProvider = Context.Provider +export const useListenerContext = (): Type => useContext(Context); diff --git a/packages/merchant-backend-ui/src/context/translation.ts b/packages/merchant-backend-ui/src/context/translation.ts new file mode 100644 index 000000000..952a1e325 --- /dev/null +++ b/packages/merchant-backend-ui/src/context/translation.ts @@ -0,0 +1,59 @@ +/* + 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 { createContext, h, VNode } from 'preact' +import { useContext, useEffect } from 'preact/hooks' +import { useLang } from '../hooks' +import * as jedLib from "jed"; +import { strings } from "../i18n/strings"; + +interface Type { + lang: string; + handler: any; + changeLanguage: (l: string) => void; +} +const initial = { + lang: 'en', + handler: null, + changeLanguage: () => { + // do not change anything + } +} +const Context = createContext<Type>(initial) + +interface Props { + initial?: string, + children: any, + forceLang?: string +} + +export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => { + const [lang, changeLanguage] = useLang(initial) + useEffect(() => { + if (forceLang) { + changeLanguage(forceLang) + } + }) + const handler = new jedLib.Jed(strings[lang]); + return h(Context.Provider, { value: { lang, handler, changeLanguage }, children }); +} + +export const useTranslationContext = (): Type => useContext(Context);
\ No newline at end of file diff --git a/packages/merchant-backend-ui/src/css/pure-min.css b/packages/merchant-backend-ui/src/css/pure-min.css new file mode 100644 index 000000000..77217b520 --- /dev/null +++ b/packages/merchant-backend-ui/src/css/pure-min.css @@ -0,0 +1,973 @@ +/*! + Pure v2.0.3 + Copyright 2013 Yahoo! + Licensed under the BSD License. + https://github.com/pure-cs s/pure/blob/master/LICENSE.md +*/ +/*! + normalize.cs s v | MIT License | git.io/normalize + Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.cs s v8.0.1 | MIT License | github.com/necolas/normalize.cs s */ + +.talerbar { + text-align: center; +} + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +main { + display: block; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +hr { + -webkit-box-sizing: content-box; + box-sizing: content-box; + height: 0; + overflow: visible; +} +pre { + font-family: monospace, monospace; + font-size: 1em; +} +a { + background-color: transparent; +} +abbr[title] { + border-bottom: none; + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} +b, +strong { + font-weight: bolder; +} +code, +kbd, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} +img { + border-style: none; +} +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; +} +button, +input { + overflow: visible; +} +button, +select { + text-transform: none; +} +[type="button"], +[type="reset"], +[type="submit"], +button { + -webkit-appearance: button; +} +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner, +button::-moz-focus-inner { + border-style: none; + padding: 0; +} +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring, +button:-moz-focusring { + outline: 1px dotted ButtonText; +} +fieldset { + padding: 0.35em 0.75em 0.625em; +} +legend { + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal; +} +progress { + vertical-align: baseline; +} +textarea { + overflow: auto; +} +[type="checkbox"], +[type="radio"] { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; +} +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} +details { + display: block; +} +summary { + display: list-item; +} +template { + display: none; +} +[hidden] { + display: none; +} +html { + font-family: sans-serif; +} +.hidden, +[hidden] { + display: none !important; +} +.pure-img { + max-width: 100%; + height: auto; + display: block; +} +.pure-g { + letter-spacing: -0.31em; + text-rendering: optimizespeed; + font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -ms-flex-line-pack: start; + align-content: flex-start; +} +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + table .pure-g { + display: block; + } +} +.opera-only :-o-prefocus, +.pure-g { + word-spacing: -0.43em; +} +.pure-u { + display: inline-block; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} +.pure-g [class*="pure-u"] { + font-family: sans-serif; +} +.pure-u-1, +.pure-u-1-1, +.pure-u-1-12, +.pure-u-1-2, +.pure-u-1-24, +.pure-u-1-3, +.pure-u-1-4, +.pure-u-1-5, +.pure-u-1-6, +.pure-u-1-8, +.pure-u-10-24, +.pure-u-11-12, +.pure-u-11-24, +.pure-u-12-24, +.pure-u-13-24, +.pure-u-14-24, +.pure-u-15-24, +.pure-u-16-24, +.pure-u-17-24, +.pure-u-18-24, +.pure-u-19-24, +.pure-u-2-24, +.pure-u-2-3, +.pure-u-2-5, +.pure-u-20-24, +.pure-u-21-24, +.pure-u-22-24, +.pure-u-23-24, +.pure-u-24-24, +.pure-u-3-24, +.pure-u-3-4, +.pure-u-3-5, +.pure-u-3-8, +.pure-u-4-24, +.pure-u-4-5, +.pure-u-5-12, +.pure-u-5-24, +.pure-u-5-5, +.pure-u-5-6, +.pure-u-5-8, +.pure-u-6-24, +.pure-u-7-12, +.pure-u-7-24, +.pure-u-7-8, +.pure-u-8-24, +.pure-u-9-24 { + display: inline-block; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} +.pure-u-1-24 { + width: 4.1667%; +} +.pure-u-1-12, +.pure-u-2-24 { + width: 8.3333%; +} +.pure-u-1-8, +.pure-u-3-24 { + width: 12.5%; +} +.pure-u-1-6, +.pure-u-4-24 { + width: 16.6667%; +} +.pure-u-1-5 { + width: 20%; +} +.pure-u-5-24 { + width: 20.8333%; +} +.pure-u-1-4, +.pure-u-6-24 { + width: 25%; +} +.pure-u-7-24 { + width: 29.1667%; +} +.pure-u-1-3, +.pure-u-8-24 { + width: 33.3333%; +} +.pure-u-3-8, +.pure-u-9-24 { + width: 37.5%; +} +.pure-u-2-5 { + width: 40%; +} +.pure-u-10-24, +.pure-u-5-12 { + width: 41.6667%; +} +.pure-u-11-24 { + width: 45.8333%; +} +.pure-u-1-2, +.pure-u-12-24 { + width: 50%; +} +.pure-u-13-24 { + width: 54.1667%; +} +.pure-u-14-24, +.pure-u-7-12 { + width: 58.3333%; +} +.pure-u-3-5 { + width: 60%; +} +.pure-u-15-24, +.pure-u-5-8 { + width: 62.5%; +} +.pure-u-16-24, +.pure-u-2-3 { + width: 66.6667%; +} +.pure-u-17-24 { + width: 70.8333%; +} +.pure-u-18-24, +.pure-u-3-4 { + width: 75%; +} +.pure-u-19-24 { + width: 79.1667%; +} +.pure-u-4-5 { + width: 80%; +} +.pure-u-20-24, +.pure-u-5-6 { + width: 83.3333%; +} +.pure-u-21-24, +.pure-u-7-8 { + width: 87.5%; +} +.pure-u-11-12, +.pure-u-22-24 { + width: 91.6667%; +} +.pure-u-23-24 { + width: 95.8333%; +} +.pure-u-1, +.pure-u-1-1, +.pure-u-24-24, +.pure-u-5-5 { + width: 100%; +} +.pure-button { + display: inline-block; + line-height: normal; + white-space: nowrap; + vertical-align: middle; + text-align: center; + cursor: pointer; + -webkit-user-drag: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.pure-button::-moz-focus-inner { + padding: 0; + border: 0; +} +.pure-button-group { + letter-spacing: -0.31em; + text-rendering: optimizespeed; +} +.opera-only :-o-prefocus, +.pure-button-group { + word-spacing: -0.43em; +} +.pure-button-group .pure-button { + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} +.pure-button { + font-family: inherit; + font-size: 100%; + padding: 0.5em 1em; + color: rgba(0, 0, 0, 0.8); + border: none transparent; + background-color: #e6e6e6; + text-decoration: none; + border-radius: 2px; +} +.pure-button-hover, +.pure-button:focus, +.pure-button:hover { + background-image: -webkit-gradient( + linear, + left top, + left bottom, + from(transparent), + color-stop(40%, rgba(0, 0, 0, 0.05)), + to(rgba(0, 0, 0, 0.1)) + ); + background-image: linear-gradient( + transparent, + rgba(0, 0, 0, 0.05) 40%, + rgba(0, 0, 0, 0.1) + ); +} +.pure-button:focus { + outline: 0; +} +.pure-button-active, +.pure-button:active { + -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, + 0 0 6px rgba(0, 0, 0, 0.2) inset; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, + 0 0 6px rgba(0, 0, 0, 0.2) inset; + border-color: #000; +} +.pure-button-disabled, +.pure-button-disabled:active, +.pure-button-disabled:focus, +.pure-button-disabled:hover, +.pure-button[disabled] { + border: none; + background-image: none; + opacity: 0.4; + cursor: not-allowed; + -webkit-box-shadow: none; + box-shadow: none; + pointer-events: none; +} +.pure-button-hidden { + display: none; +} +.pure-button-primary, +.pure-button-selected, +a.pure-button-primary, +a.pure-button-selected { + background-color: #0078e7; + color: #fff; +} +.pure-button-group .pure-button { + margin: 0; + border-radius: 0; + border-right: 1px solid rgba(0, 0, 0, 0.2); +} +.pure-button-group .pure-button:first-child { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} +.pure-button-group .pure-button:last-child { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-right: none; +} +.pure-form input[type="color"], +.pure-form input[type="date"], +.pure-form input[type="datetime-local"], +.pure-form input[type="datetime"], +.pure-form input[type="email"], +.pure-form input[type="month"], +.pure-form input[type="number"], +.pure-form input[type="password"], +.pure-form input[type="search"], +.pure-form input[type="tel"], +.pure-form input[type="text"], +.pure-form input[type="time"], +.pure-form input[type="url"], +.pure-form input[type="week"], +.pure-form select, +.pure-form textarea { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + -webkit-box-shadow: inset 0 1px 3px #ddd; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + vertical-align: middle; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.pure-form input:not([type]) { + padding: 0.5em 0.6em; + display: inline-block; + border: 1px solid #ccc; + -webkit-box-shadow: inset 0 1px 3px #ddd; + box-shadow: inset 0 1px 3px #ddd; + border-radius: 4px; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.pure-form input[type="color"] { + padding: 0.2em 0.5em; +} +.pure-form input[type="color"]:focus, +.pure-form input[type="date"]:focus, +.pure-form input[type="datetime-local"]:focus, +.pure-form input[type="datetime"]:focus, +.pure-form input[type="email"]:focus, +.pure-form input[type="month"]:focus, +.pure-form input[type="number"]:focus, +.pure-form input[type="password"]:focus, +.pure-form input[type="search"]:focus, +.pure-form input[type="tel"]:focus, +.pure-form input[type="text"]:focus, +.pure-form input[type="time"]:focus, +.pure-form input[type="url"]:focus, +.pure-form input[type="week"]:focus, +.pure-form select:focus, +.pure-form textarea:focus { + outline: 0; + border-color: #129fea; +} +.pure-form input:not([type]):focus { + outline: 0; + border-color: #129fea; +} +.pure-form input[type="checkbox"]:focus, +.pure-form input[type="file"]:focus, +.pure-form input[type="radio"]:focus { + outline: thin solid #129fea; + outline: 1px auto #129fea; +} +.pure-form .pure-checkbox, +.pure-form .pure-radio { + margin: 0.5em 0; + display: block; +} +.pure-form input[type="color"][disabled], +.pure-form input[type="date"][disabled], +.pure-form input[type="datetime-local"][disabled], +.pure-form input[type="datetime"][disabled], +.pure-form input[type="email"][disabled], +.pure-form input[type="month"][disabled], +.pure-form input[type="number"][disabled], +.pure-form input[type="password"][disabled], +.pure-form input[type="search"][disabled], +.pure-form input[type="tel"][disabled], +.pure-form input[type="text"][disabled], +.pure-form input[type="time"][disabled], +.pure-form input[type="url"][disabled], +.pure-form input[type="week"][disabled], +.pure-form select[disabled], +.pure-form textarea[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; +} +.pure-form input:not([type])[disabled] { + cursor: not-allowed; + background-color: #eaeded; + color: #cad2d3; +} +.pure-form input[readonly], +.pure-form select[readonly], +.pure-form textarea[readonly] { + background-color: #eee; + color: #777; + border-color: #ccc; +} +.pure-form input:focus:invalid, +.pure-form select:focus:invalid, +.pure-form textarea:focus:invalid { + color: #b94a48; + border-color: #e9322d; +} +.pure-form input[type="checkbox"]:focus:invalid:focus, +.pure-form input[type="file"]:focus:invalid:focus, +.pure-form input[type="radio"]:focus:invalid:focus { + outline-color: #e9322d; +} +.pure-form select { + height: 2.25em; + border: 1px solid #ccc; + background-color: #fff; +} +.pure-form select[multiple] { + height: auto; +} +.pure-form label { + margin: 0.5em 0 0.2em; +} +.pure-form fieldset { + margin: 0; + padding: 0.35em 0 0.75em; + border: 0; +} +.pure-form legend { + display: block; + width: 100%; + padding: 0.3em 0; + margin-bottom: 0.3em; + color: #333; + border-bottom: 1px solid #e5e5e5; +} +.pure-form-stacked input[type="color"], +.pure-form-stacked input[type="date"], +.pure-form-stacked input[type="datetime-local"], +.pure-form-stacked input[type="datetime"], +.pure-form-stacked input[type="email"], +.pure-form-stacked input[type="file"], +.pure-form-stacked input[type="month"], +.pure-form-stacked input[type="number"], +.pure-form-stacked input[type="password"], +.pure-form-stacked input[type="search"], +.pure-form-stacked input[type="tel"], +.pure-form-stacked input[type="text"], +.pure-form-stacked input[type="time"], +.pure-form-stacked input[type="url"], +.pure-form-stacked input[type="week"], +.pure-form-stacked label, +.pure-form-stacked select, +.pure-form-stacked textarea { + display: block; + margin: 0.25em 0; +} +.pure-form-stacked input:not([type]) { + display: block; + margin: 0.25em 0; +} +.pure-form-aligned input, +.pure-form-aligned select, +.pure-form-aligned textarea, +.pure-form-message-inline { + display: inline-block; + vertical-align: middle; +} +.pure-form-aligned textarea { + vertical-align: top; +} +.pure-form-aligned .pure-control-group { + margin-bottom: 0.5em; +} +.pure-form-aligned .pure-control-group label { + text-align: right; + display: inline-block; + vertical-align: middle; + width: 10em; + margin: 0 1em 0 0; +} +.pure-form-aligned .pure-controls { + margin: 1.5em 0 0 11em; +} +.pure-form .pure-input-rounded, +.pure-form input.pure-input-rounded { + border-radius: 2em; + padding: 0.5em 1em; +} +.pure-form .pure-group fieldset { + margin-bottom: 10px; +} +.pure-form .pure-group input, +.pure-form .pure-group textarea { + display: block; + padding: 10px; + margin: 0 0 -1px; + border-radius: 0; + position: relative; + top: -1px; +} +.pure-form .pure-group input:focus, +.pure-form .pure-group textarea:focus { + z-index: 3; +} +.pure-form .pure-group input:first-child, +.pure-form .pure-group textarea:first-child { + top: 1px; + border-radius: 4px 4px 0 0; + margin: 0; +} +.pure-form .pure-group input:first-child:last-child, +.pure-form .pure-group textarea:first-child:last-child { + top: 1px; + border-radius: 4px; + margin: 0; +} +.pure-form .pure-group input:last-child, +.pure-form .pure-group textarea:last-child { + top: -2px; + border-radius: 0 0 4px 4px; + margin: 0; +} +.pure-form .pure-group button { + margin: 0.35em 0; +} +.pure-form .pure-input-1 { + width: 100%; +} +.pure-form .pure-input-3-4 { + width: 75%; +} +.pure-form .pure-input-2-3 { + width: 66%; +} +.pure-form .pure-input-1-2 { + width: 50%; +} +.pure-form .pure-input-1-3 { + width: 33%; +} +.pure-form .pure-input-1-4 { + width: 25%; +} +.pure-form-message-inline { + display: inline-block; + padding-left: 0.3em; + color: #666; + vertical-align: middle; + font-size: 0.875em; +} +.pure-form-message { + display: block; + color: #666; + font-size: 0.875em; +} +@media only screen and (max-width: 480px) { + .pure-form button[type="submit"] { + margin: 0.7em 0 0; + } + .pure-form input:not([type]), + .pure-form input[type="color"], + .pure-form input[type="date"], + .pure-form input[type="datetime-local"], + .pure-form input[type="datetime"], + .pure-form input[type="email"], + .pure-form input[type="month"], + .pure-form input[type="number"], + .pure-form input[type="password"], + .pure-form input[type="search"], + .pure-form input[type="tel"], + .pure-form input[type="text"], + .pure-form input[type="time"], + .pure-form input[type="url"], + .pure-form input[type="week"], + .pure-form label { + margin-bottom: 0.3em; + display: block; + } + .pure-group input:not([type]), + .pure-group input[type="color"], + .pure-group input[type="date"], + .pure-group input[type="datetime-local"], + .pure-group input[type="datetime"], + .pure-group input[type="email"], + .pure-group input[type="month"], + .pure-group input[type="number"], + .pure-group input[type="password"], + .pure-group input[type="search"], + .pure-group input[type="tel"], + .pure-group input[type="text"], + .pure-group input[type="time"], + .pure-group input[type="url"], + .pure-group input[type="week"] { + margin-bottom: 0; + } + .pure-form-aligned .pure-control-group label { + margin-bottom: 0.3em; + text-align: left; + display: block; + width: 100%; + } + .pure-form-aligned .pure-controls { + margin: 1.5em 0 0 0; + } + .pure-form-message, + .pure-form-message-inline { + display: block; + font-size: 0.75em; + padding: 0.2em 0 0.8em; + } +} +.pure-menu { + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.pure-menu-fixed { + position: fixed; + left: 0; + top: 0; + z-index: 3; +} +.pure-menu-item, +.pure-menu-list { + position: relative; +} +.pure-menu-list { + list-style: none; + margin: 0; + padding: 0; +} +.pure-menu-item { + padding: 0; + margin: 0; + height: 100%; +} +.pure-menu-heading, +.pure-menu-link { + display: block; + text-decoration: none; + white-space: nowrap; +} +.pure-menu-horizontal { + width: 100%; + white-space: nowrap; +} +.pure-menu-horizontal .pure-menu-list { + display: inline-block; +} +.pure-menu-horizontal .pure-menu-heading, +.pure-menu-horizontal .pure-menu-item, +.pure-menu-horizontal .pure-menu-separator { + display: inline-block; + vertical-align: middle; +} +.pure-menu-item .pure-menu-item { + display: block; +} +.pure-menu-children { + display: none; + position: absolute; + left: 100%; + top: 0; + margin: 0; + padding: 0; + z-index: 3; +} +.pure-menu-horizontal .pure-menu-children { + left: 0; + top: auto; + width: inherit; +} +.pure-menu-active > .pure-menu-children, +.pure-menu-allow-hover:hover > .pure-menu-children { + display: block; + position: absolute; +} +.pure-menu-has-children > .pure-menu-link:after { + padding-left: 0.5em; + content: "\25B8"; + font-size: small; +} +.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after { + content: "\25BE"; +} +.pure-menu-scrollable { + overflow-y: scroll; + overflow-x: hidden; +} +.pure-menu-scrollable .pure-menu-list { + display: block; +} +.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list { + display: inline-block; +} +.pure-menu-horizontal.pure-menu-scrollable { + white-space: nowrap; + overflow-y: hidden; + overflow-x: auto; + padding: 0.5em 0; +} +.pure-menu-horizontal .pure-menu-children .pure-menu-separator, +.pure-menu-separator { + background-color: #ccc; + height: 1px; + margin: 0.3em 0; +} +.pure-menu-horizontal .pure-menu-separator { + width: 1px; + height: 1.3em; + margin: 0 0.3em; +} +.pure-menu-horizontal .pure-menu-children .pure-menu-separator { + display: block; + width: auto; +} +.pure-menu-heading { + text-transform: uppercase; + color: #565d64; +} +.pure-menu-link { + color: #777; +} +.pure-menu-children { + background-color: #fff; +} +.pure-menu-disabled, +.pure-menu-heading, +.pure-menu-link { + padding: 0.5em 1em; +} +.pure-menu-disabled { + opacity: 0.5; +} +.pure-menu-disabled .pure-menu-link:hover { + background-color: transparent; +} +.pure-menu-active > .pure-menu-link, +.pure-menu-link:focus, +.pure-menu-link:hover { + background-color: #eee; +} +.pure-menu-selected > .pure-menu-link, +.pure-menu-selected > .pure-menu-link:visited { + color: #000; +} +.pure-table { + border-collapse: collapse; + border-spacing: 0; + empty-cells: show; + border: 1px solid #cbcbcb; +} +.pure-table caption { + color: #000; + font: italic 85%/1 arial, sans-serif; + padding: 1em 0; + text-align: center; +} +.pure-table td, +.pure-table th { + border-left: 1px solid #cbcbcb; + border-width: 0 0 0 1px; + font-size: inherit; + margin: 0; + overflow: visible; + padding: 0.5em 1em; +} +.pure-table thead { + background-color: #e0e0e0; + color: #000; + text-align: left; + vertical-align: bottom; +} +.pure-table td { + background-color: transparent; +} +.pure-table-odd td { + background-color: #f2f2f2; +} +.pure-table-striped tr:nth-child(2n-1) td { + background-color: #f2f2f2; +} +.pure-table-bordered td { + border-bottom: 1px solid #cbcbcb; +} +.pure-table-bordered tbody > tr:last-child > td { + border-bottom-width: 0; +} +.pure-table-horizontal td, +.pure-table-horizontal th { + border-width: 0 0 1px 0; + border-bottom: 1px solid #cbcbcb; +} +.pure-table-horizontal tbody > tr:last-child > td { + border-bottom-width: 0; +} diff --git a/packages/merchant-backend-ui/src/css/style.css b/packages/merchant-backend-ui/src/css/style.css new file mode 100644 index 000000000..f24dbaa87 --- /dev/null +++ b/packages/merchant-backend-ui/src/css/style.css @@ -0,0 +1,61 @@ +/*! + Pure v2.0.3 + Copyright 2013 Yahoo! + Licensed under the BSD License. + https://github.com/pure-ss/pure/blob/master/LICENSE.md +*/ +/*! + normalize.cs v | MIT License | git.io/normalize + Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.ss v8.0.1 | MIT License | github.com/necolas/normalize.cs */ + +.talerbar { + text-align: center; +} +.tt { + font-family: "Lucida Console", Monaco, monospace; +} +.content { + overflow-x: auto; + padding-left: 15%; + padding-right: 15%; +} +.qr { + margin: auto; + text-align: center; +} +.qrtext { + width: max-content; + margin: auto; + transition: font-size 0.2s; + font-family: "Lucida Console", Monaco, monospace; + font-size: 0.5em; +} +.qrtext:hover { + font-size: 1em; +} +.talerbar { + margin: 0; + bottom: 0; + background-color: #033; + color: white; + width: 100%; + padding: 1em; + overflow: auto; +} +body { + overflow-y: scroll; +} +@media (min-width: 500px) { + .content { + padding-bottom: 2em; + overflow-y: auto; + } +} +#main a:link, +#main a:visited, +#main a:hover, +#main a:active { + color: black; +}
\ No newline at end of file diff --git a/packages/merchant-backend-ui/src/custom.d.ts b/packages/merchant-backend-ui/src/custom.d.ts new file mode 100644 index 000000000..d2705003b --- /dev/null +++ b/packages/merchant-backend-ui/src/custom.d.ts @@ -0,0 +1,40 @@ +/* + 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/> + */ +declare module '*.po' { + const content: any; + export default content; +} +declare module 'jed' { + const x: any; + export = x; +} +declare module "*.jpeg" { + const content: any; + export default content; +} +declare module "*.png" { + const content: any; + export default content; +} +declare module '*.svg' { + const content: any; + export default content; +} + +declare module '*.scss' { + const content: Record<string, string>; + export default content; +} diff --git a/packages/merchant-backend-ui/src/declaration.d.ts b/packages/merchant-backend-ui/src/declaration.d.ts new file mode 100644 index 000000000..74b0a5011 --- /dev/null +++ b/packages/merchant-backend-ui/src/declaration.d.ts @@ -0,0 +1,1384 @@ +/* + 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) +*/ + + +type HashCode = string; +type EddsaPublicKey = string; +type EddsaSignature = string; +type WireTransferIdentifierRawP = string; +type RelativeTime = Duration; +type ImageDataUrl = string; + +export interface WithId { + id: string +} + +interface Timestamp { + // Milliseconds since epoch, or the special + // value "forever" to represent an event that will + // never happen. + t_s: number | "never"; +} +interface Duration { + // Duration in milliseconds or "forever" + // to represent an infinite duration. + d_us: number | "forever"; +} + +interface WithId { + id: string; +} + +type Amount = string; +type UUID = string; +type Integer = number; + +export namespace ExchangeBackend { + interface WireResponse { + + // 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 { + // 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 { + // 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: TalerProtocolTimestamp; + + // 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: TalerProtocolTimestamp; + + // Signature of TALER_MasterWireFeePS with + // purpose TALER_SIGNATURE_MASTER_WIRE_FEES. + sig: EddsaSignature; + } + +} +export namespace MerchantBackend { + interface ErrorDetail { + + // Numeric error code unique to the condition. + // The other arguments are specific to the error value reported here. + code: number; + + // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ... + // Should give a human-readable hint about the error's nature. Optional, may change without notice! + hint?: string; + + // Optional detail about the specific input value that failed. May change without notice! + detail?: string; + + // Name of the parameter that was bogus (if applicable). + parameter?: string; + + // Path to the argument that was bogus (if applicable). + path?: string; + + // Offset of the argument that was bogus (if applicable). + offset?: string; + + // Index of the argument that was bogus (if applicable). + index?: string; + + // Name of the object that was bogus (if applicable). + object?: string; + + // Name of the currency than was problematic (if applicable). + currency?: string; + + // Expected type (if applicable). + type_expected?: string; + + // Type that was provided instead (if applicable). + type_actual?: string; + } + + + // Delivery location, loosely modeled as a subset of + // ISO20022's PostalAddress25. + interface Tax { + // the name of the tax + name: string; + + // amount paid in tax + tax: Amount; + } + + interface Auditor { + // official name + name: string; + + // Auditor's public key + auditor_pub: EddsaPublicKey; + + // Base URL of the auditor + url: string; + } + interface Exchange { + // the exchange's base URL + url: string; + + // master public key of the exchange + master_pub: EddsaPublicKey; + } + + interface Product { + // merchant-internal identifier for the product. + product_id?: string; + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n?: { [lang_tag: string]: string }; + + // The number of units of the product to deliver to the customer. + quantity: Integer; + + // The unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price of the product; this is the total price for quantity times unit of this product. + price: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for this product. Can be empty. + taxes: Tax[]; + + // time indicating when this product should be delivered + delivery_date?: Timestamp; + } + interface Merchant { + // label for a location with the business address of the merchant + address: Location; + + // the merchant's legal name of business + name: string; + + // label for a location that denotes the jurisdiction for disputes. + // Some of the typical fields for a location (such as a street address) may be absent. + jurisdiction: Location; + } + + interface VersionResponse { + // libtool-style representation of the Merchant protocol version, see + // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning + // The format is "current:revision:age". + version: string; + + // Name of the protocol. + name: "taler-merchant"; + + // Currency supported by this backend. + currency: string; + + } + interface Location { + // Nation with its own government. + country?: string; + + // Identifies a subdivision of a country such as state, region, county. + country_subdivision?: string; + + // Identifies a subdivision within a country sub-division. + district?: string; + + // Name of a built-up area, with defined boundaries, and a local government. + town?: string; + + // Specific location name within the town. + town_location?: string; + + // Identifier consisting of a group of letters and/or numbers that + // is added to a postal address to assist the sorting of mail. + post_code?: string; + + // Name of a street or thoroughfare. + street?: string; + + // Name of the building or house. + building_name?: string; + + // Number that identifies the position of a building on a street. + building_number?: string; + + // Free-form address lines, should not exceed 7 elements. + address_lines?: string[]; + } + namespace Instances { + + //POST /private/instances/$INSTANCE/auth + interface InstanceAuthConfigurationMessage { + // Type of authentication. + // "external": The mechant backend does not do + // any authentication checks. Instead an API + // gateway must do the authentication. + // "token": The merchant checks an auth token. + // See "token" for details. + method: "external" | "token"; + + // For method "external", this field is mandatory. + // The token MUST begin with the string "secret-token:". + // After the auth token has been set (with method "token"), + // the value must be provided in a "Authorization: Bearer $token" + // header. + token?: string; + + } + //POST /private/instances + interface InstanceConfigurationMessage { + // The URI where the wallet will send coins. A merchant may have + // multiple accounts, thus this is an array. Note that by + // removing URIs from this list the respective account is set to + // inactive and thus unavailable for new contracts, but preserved + // in the database as existing offers and contracts may still refer + // to it. + payto_uris: string[]; + + // Name of the merchant instance to create (will become $INSTANCE). + id: string; + + // Merchant name corresponding to this instance. + name: string; + + // "Authentication" header required to authorize management access the instance. + // Optional, if not given authentication will be disabled for + // this instance (hopefully authentication checks are still + // done by some reverse proxy). + auth: InstanceAuthConfigurationMessage; + + // The merchant's physical address (to be put into contracts). + address: Location; + + // The jurisdiction under which the merchant conducts its business + // (to be put into contracts). + jurisdiction: Location; + + // Maximum wire fee this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_wire_fee: Amount; + + // Default factor for wire fee amortization calculations. + // Can be overridden by the frontend on a per-order basis. + default_wire_fee_amortization: Integer; + + // Maximum deposit fee (sum over all coins) this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_deposit_fee: Amount; + + // If the frontend does NOT specify an execution date, how long should + // we tell the exchange to wait to aggregate transactions before + // executing the wire transfer? This delay is added to the current + // time when we generate the advisory execution time for the exchange. + default_wire_transfer_delay: RelativeTime; + + // If the frontend does NOT specify a payment deadline, how long should + // offers we make be valid by default? + default_pay_delay: RelativeTime; + + } + + // PATCH /private/instances/$INSTANCE + interface InstanceReconfigurationMessage { + // The URI where the wallet will send coins. A merchant may have + // multiple accounts, thus this is an array. Note that by + // removing URIs from this list + payto_uris: string[]; + + // Merchant name corresponding to this instance. + name: string; + + // The merchant's physical address (to be put into contracts). + address: Location; + + // The jurisdiction under which the merchant conducts its business + // (to be put into contracts). + jurisdiction: Location; + + // Maximum wire fee this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_wire_fee: Amount; + + // Default factor for wire fee amortization calculations. + // Can be overridden by the frontend on a per-order basis. + default_wire_fee_amortization: Integer; + + // Maximum deposit fee (sum over all coins) this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_deposit_fee: Amount; + + // If the frontend does NOT specify an execution date, how long should + // we tell the exchange to wait to aggregate transactions before + // executing the wire transfer? This delay is added to the current + // time when we generate the advisory execution time for the exchange. + default_wire_transfer_delay: RelativeTime; + + // If the frontend does NOT specify a payment deadline, how long should + // offers we make be valid by default? + default_pay_delay: RelativeTime; + + } + + // GET /private/instances + interface InstancesResponse { + // List of instances that are present in the backend (see Instance) + instances: Instance[]; + } + + interface Instance { + // Merchant name corresponding to this instance. + name: string; + + deleted?: boolean; + + // Merchant instance this response is about ($INSTANCE) + id: string; + + // Public key of the merchant/instance, in Crockford Base32 encoding. + merchant_pub: EddsaPublicKey; + + // List of the payment targets supported by this instance. Clients can + // specify the desired payment target in /order requests. Note that + // front-ends do not have to support wallets selecting payment targets. + payment_targets: string[]; + + } + + //GET /private/instances/$INSTANCE + interface QueryInstancesResponse { + // The URI where the wallet will send coins. A merchant may have + // multiple accounts, thus this is an array. + accounts: MerchantAccount[]; + + // Merchant name corresponding to this instance. + name: string; + + // Public key of the merchant/instance, in Crockford Base32 encoding. + merchant_pub: EddsaPublicKey; + + // The merchant's physical address (to be put into contracts). + address: Location; + + // The jurisdiction under which the merchant conducts its business + // (to be put into contracts). + jurisdiction: Location; + + // Maximum wire fee this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_wire_fee: Amount; + + // Default factor for wire fee amortization calculations. + // Can be overridden by the frontend on a per-order basis. + default_wire_fee_amortization: Integer; + + // Maximum deposit fee (sum over all coins) this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_deposit_fee: Amount; + + // If the frontend does NOT specify an execution date, how long should + // we tell the exchange to wait to aggregate transactions before + // executing the wire transfer? This delay is added to the current + // time when we generate the advisory execution time for the exchange. + default_wire_transfer_delay: RelativeTime; + + // If the frontend does NOT specify a payment deadline, how long should + // offers we make be valid by default? + default_pay_delay: RelativeTime; + + // Authentication configuration. + // Does not contain the token when token auth is configured. + auth: { + method: "external" | "token"; + }; + } + + interface MerchantAccount { + + // payto:// URI of the account. + payto_uri: string; + + // Hash over the wire details (including over the salt) + h_wire: HashCode; + + // salt used to compute h_wire + salt: HashCode; + + // true if this account is active, + // false if it is historic. + active: boolean; + } + + // DELETE /private/instances/$INSTANCE + + + } + + namespace Products { + // POST /private/products + interface ProductAddDetail { + + // product ID to use. + product_id: string; + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n: { [lang_tag: string]: string }; + + // unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price for one unit of the product. Zero is used + // to imply that this product is not sold separately, or + // that the price is not fixed, and must be supplied by the + // front-end. If non-zero, this price MUST include applicable + // taxes. + price: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for one unit of this product + taxes: Tax[]; + + // Number of units of the product in stock in sum in total, + // including all existing sales ever. Given in product-specific + // units. + // A value of -1 indicates "infinite" (i.e. for "electronic" books). + total_stock: Integer; + + // Identifies where the product is in stock. + address: Location; + + // Identifies when we expect the next restocking to happen. + next_restock?: Timestamp; + + } + // PATCH /private/products/$PRODUCT_ID + interface ProductPatchDetail { + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n: { [lang_tag: string]: string }; + + // unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price for one unit of the product. Zero is used + // to imply that this product is not sold separately, or + // that the price is not fixed, and must be supplied by the + // front-end. If non-zero, this price MUST include applicable + // taxes. + price: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for one unit of this product + taxes: Tax[]; + + // Number of units of the product in stock in sum in total, + // including all existing sales ever. Given in product-specific + // units. + // A value of -1 indicates "infinite" (i.e. for "electronic" books). + total_stock: Integer; + + // Number of units of the product that were lost (spoiled, stolen, etc.) + total_lost: Integer; + + // Identifies where the product is in stock. + address: Location; + + // Identifies when we expect the next restocking to happen. + next_restock?: Timestamp; + + } + + // GET /private/products + interface InventorySummaryResponse { + // List of products that are present in the inventory + products: InventoryEntry[]; + } + interface InventoryEntry { + // Product identifier, as found in the product. + product_id: string; + + } + + // GET /private/products/$PRODUCT_ID + interface ProductDetail { + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n: { [lang_tag: string]: string }; + + // unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price for one unit of the product. Zero is used + // to imply that this product is not sold separately, or + // that the price is not fixed, and must be supplied by the + // front-end. If non-zero, this price MUST include applicable + // taxes. + price: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for one unit of this product + taxes: Tax[]; + + // Number of units of the product in stock in sum in total, + // including all existing sales ever. Given in product-specific + // units. + // A value of -1 indicates "infinite" (i.e. for "electronic" books). + total_stock: Integer; + + // Number of units of the product that have already been sold. + total_sold: Integer; + + // Number of units of the product that were lost (spoiled, stolen, etc.) + total_lost: Integer; + + // Identifies where the product is in stock. + address: Location; + + // Identifies when we expect the next restocking to happen. + next_restock?: Timestamp; + + } + + // POST /private/products/$PRODUCT_ID/lock + interface LockRequest { + + // UUID that identifies the frontend performing the lock + // It is suggested that clients use a timeflake for this, + // see https://github.com/anthonynsimon/timeflake + lock_uuid: UUID; + + // How long does the frontend intend to hold the lock + duration: RelativeTime; + + // How many units should be locked? + quantity: Integer; + + } + + // DELETE /private/products/$PRODUCT_ID + + } + + namespace Orders { + + type MerchantOrderStatusResponse = CheckPaymentPaidResponse | + CheckPaymentClaimedResponse | + CheckPaymentUnpaidResponse; + interface CheckPaymentPaidResponse { + // The customer paid for this contract. + order_status: "paid"; + + // Was the payment refunded (even partially)? + refunded: boolean; + + // True if there are any approved refunds that the wallet has + // not yet obtained. + refund_pending: boolean; + + // Did the exchange wire us the funds? + wired: boolean; + + // Total amount the exchange deposited into our bank account + // for this contract, excluding fees. + deposit_total: Amount; + + // Numeric error code indicating errors the exchange + // encountered tracking the wire transfer for this purchase (before + // we even got to specific coin issues). + // 0 if there were no issues. + exchange_ec: number; + + // HTTP status code returned by the exchange when we asked for + // information to track the wire transfer for this purchase. + // 0 if there were no issues. + exchange_hc: number; + + // Total amount that was refunded, 0 if refunded is false. + refund_amount: Amount; + + // Contract terms. + contract_terms: ContractTerms; + + // The wire transfer status from the exchange for this order if + // available, otherwise empty array. + wire_details: TransactionWireTransfer[]; + + // Reports about trouble obtaining wire transfer details, + // empty array if no trouble were encountered. + wire_reports: TransactionWireReport[]; + + // The refund details for this order. One entry per + // refunded coin; empty array if there are no refunds. + refund_details: RefundDetails[]; + + // Status URL, can be used as a redirect target for the browser + // to show the order QR code / trigger the wallet. + order_status_url: string; + } + interface CheckPaymentClaimedResponse { + // A wallet claimed the order, but did not yet pay for the contract. + order_status: "claimed"; + + // Contract terms. + contract_terms: ContractTerms; + + } + interface CheckPaymentUnpaidResponse { + // The order was neither claimed nor paid. + order_status: "unpaid"; + + // when was the order created + creation_time: Timestamp; + + // Order summary text. + summary: string; + + // Total amount of the order (to be paid by the customer). + total_amount: Amount; + + // URI that the wallet must process to complete the payment. + taler_pay_uri: string; + + // Alternative order ID which was paid for already in the same session. + // Only given if the same product was purchased before in the same session. + already_paid_order_id?: string; + + // Fulfillment URL of an already paid order. Only given if under this + // session an already paid order with a fulfillment URL exists. + already_paid_fulfillment_url?: string; + + // Status URL, can be used as a redirect target for the browser + // to show the order QR code / trigger the wallet. + order_status_url: string; + + // We do we NOT return the contract terms here because they may not + // exist in case the wallet did not yet claim them. + } + interface RefundDetails { + // Reason given for the refund. + reason: string; + + // When was the refund approved. + timestamp: Timestamp; + + // Total amount that was refunded (minus a refund fee). + amount: Amount; + } + interface TransactionWireTransfer { + // Responsible exchange. + exchange_url: string; + + // 32-byte wire transfer identifier. + wtid: Base32; + + // Execution time of the wire transfer. + execution_time: Timestamp; + + // Total amount that has been wire transferred + // to the merchant. + amount: Amount; + + // Was this transfer confirmed by the merchant via the + // POST /transfers API, or is it merely claimed by the exchange? + confirmed: boolean; + } + interface TransactionWireReport { + // Numerical error code. + code: number; + + // Human-readable error description. + hint: string; + + // Numerical error code from the exchange. + exchange_ec: number; + + // HTTP status code received from the exchange. + exchange_hc: number; + + // Public key of the coin for which we got the exchange error. + coin_pub: CoinPublicKey; + } + + interface OrderHistory { + // timestamp-sorted array of all orders matching the query. + // The order of the sorting depends on the sign of delta. + orders: OrderHistoryEntry[]; + } + interface OrderHistoryEntry { + + // order ID of the transaction related to this entry. + order_id: string; + + // row ID of the order in the database + row_id: number; + + // when the order was created + timestamp: Timestamp; + + // the amount of money the order is for + amount: Amount; + + // the summary of the order + summary: string; + + // whether some part of the order is refundable, + // that is the refund deadline has not yet expired + // and the total amount refunded so far is below + // the value of the original transaction. + refundable: boolean; + + // whether the order has been paid or not + paid: boolean; + } + + interface PostOrderRequest { + // The order must at least contain the minimal + // order detail, but can override all + order: Order; + + // if set, the backend will then set the refund deadline to the current + // time plus the specified delay. If it's not set, refunds will not be + // possible. + refund_delay?: RelativeTime; + + // specifies the payment target preferred by the client. Can be used + // to select among the various (active) wire methods supported by the instance. + payment_target?: string; + + // specifies that some products are to be included in the + // order from the inventory. For these inventory management + // is performed (so the products must be in stock) and + // details are completed from the product data of the backend. + inventory_products?: MinimalInventoryProduct[]; + + // Specifies a lock identifier that was used to + // lock a product in the inventory. Only useful if + // manage_inventory is set. Used in case a frontend + // reserved quantities of the individual products while + // the shopping card was being built. Multiple UUIDs can + // be used in case different UUIDs were used for different + // products (i.e. in case the user started with multiple + // shopping sessions that were combined during checkout). + lock_uuids?: UUID[]; + + // Should a token for claiming the order be generated? + // False can make sense if the ORDER_ID is sufficiently + // high entropy to prevent adversarial claims (like it is + // if the backend auto-generates one). Default is 'true'. + create_token?: boolean; + + } + type Order = MinimalOrderDetail | ContractTerms; + + interface MinimalOrderDetail { + // Amount to be paid by the customer + amount: Amount; + + // Short summary of the order + summary: string; + + // URL that will show that the order was successful after + // it has been paid for. Optional. When POSTing to the + // merchant, the placeholder "${ORDER_ID}" will be + // replaced with the actual order ID (useful if the + // order ID is generated server-side and needs to be + // in the URL). + fulfillment_url?: string; + } + + interface MinimalInventoryProduct { + // Which product is requested (here mandatory!) + product_id: string; + + // How many units of the product are requested + quantity: Integer; + } + interface PostOrderResponse { + // Order ID of the response that was just created + order_id: string; + + // Token that authorizes the wallet to claim the order. + // Provided only if "create_token" was set to 'true' + // in the request. + token?: ClaimToken; + } + interface OutOfStockResponse { + + // Product ID of an out-of-stock item + product_id: string; + + // Requested quantity + requested_quantity: Integer; + + // Available quantity (must be below requested_quanitity) + available_quantity: Integer; + + // When do we expect the product to be again in stock? + // Optional, not given if unknown. + restock_expected?: Timestamp; + } + + interface ForgetRequest { + + // Array of valid JSON paths to forgettable fields in the order's + // contract terms. + fields: string[]; + } + interface RefundRequest { + // Amount to be refunded + refund: Amount; + + // Human-readable refund justification + reason: string; + } + interface MerchantRefundResponse { + + // URL (handled by the backend) that the wallet should access to + // trigger refund processing. + // taler://refund/... + taler_refund_uri: string; + + // Contract hash that a client may need to authenticate an + // HTTP request to obtain the above URI in a wallet-friendly way. + h_contract: HashCode; + } + + } + + namespace Tips { + + // GET /private/reserves + interface TippingReserveStatus { + // Array of all known reserves (possibly empty!) + reserves: ReserveStatusEntry[]; + } + interface ReserveStatusEntry { + // Public key of the reserve + reserve_pub: EddsaPublicKey; + + // Timestamp when it was established + creation_time: Timestamp; + + // Timestamp when it expires + expiration_time: Timestamp; + + // Initial amount as per reserve creation call + merchant_initial_amount: Amount; + + // Initial amount as per exchange, 0 if exchange did + // not confirm reserve creation yet. + exchange_initial_amount: Amount; + + // Amount picked up so far. + pickup_amount: Amount; + + // Amount approved for tips that exceeds the pickup_amount. + committed_amount: Amount; + + // Is this reserve active (false if it was deleted but not purged) + active: boolean; + } + + interface ReserveCreateRequest { + // Amount that the merchant promises to put into the reserve + initial_balance: Amount; + + // Exchange the merchant intends to use for tipping + exchange_url: string; + + // Desired wire method, for example "iban" or "x-taler-bank" + wire_method: string; + } + interface ReserveCreateConfirmation { + // Public key identifying the reserve + reserve_pub: EddsaPublicKey; + + // Wire account of the exchange where to transfer the funds + payto_uri: string; + } + interface TipCreateRequest { + // Amount that the customer should be tipped + amount: Amount; + + // Justification for giving the tip + justification: string; + + // URL that the user should be directed to after tipping, + // will be included in the tip_token. + next_url: string; + } + interface TipCreateConfirmation { + // Unique tip identifier for the tip that was created. + tip_id: HashCode; + + // taler://tip URI for the tip + taler_tip_uri: string; + + // URL that will directly trigger processing + // the tip when the browser is redirected to it + tip_status_url: string; + + // when does the tip expire + tip_expiration: Timestamp; + } + + interface ReserveDetail { + // Timestamp when it was established. + creation_time: Timestamp; + + // Timestamp when it expires. + expiration_time: Timestamp; + + // Initial amount as per reserve creation call. + merchant_initial_amount: Amount; + + // Initial amount as per exchange, 0 if exchange did + // not confirm reserve creation yet. + exchange_initial_amount: Amount; + + // Amount picked up so far. + pickup_amount: Amount; + + // Amount approved for tips that exceeds the pickup_amount. + committed_amount: Amount; + + // Array of all tips created by this reserves (possibly empty!). + // Only present if asked for explicitly. + tips?: TipStatusEntry[]; + + // 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. + tip_id: HashCode; + + // Total amount of the tip that can be withdrawn. + total_amount: Amount; + + // Human-readable reason for why the tip was granted. + reason: string; + } + + interface TipDetails { + // Amount that we authorized for this tip. + total_authorized: Amount; + + // Amount that was picked up by the user already. + total_picked_up: Amount; + + // Human-readable reason given when authorizing the tip. + reason: string; + + // Timestamp indicating when the tip is set to expire (may be in the past). + expiration: TalerProtocolTimestamp; + + // Reserve public key from which the tip is funded. + reserve_pub: EddsaPublicKey; + + // Array showing the pickup operations of the wallet (possibly empty!). + // Only present if asked for explicitly. + pickups?: PickupDetail[]; + } + interface PickupDetail { + // Unique identifier for the pickup operation. + pickup_id: HashCode; + + // Number of planchets involved. + num_planchets: Integer; + + // Total amount requested for this pickup_id. + requested_amount: Amount; + } + + } + + namespace Transfers { + + interface TransferList { + // list of all the transfers that fit the filter that we know + transfers: TransferDetails[]; + } + interface TransferDetails { + // how much was wired to the merchant (minus fees) + credit_amount: Amount; + + // raw wire transfer identifier identifying the wire transfer (a base32-encoded value) + wtid: string; + + // target account that received the wire transfer + payto_uri: string; + + // base URL of the exchange that made the wire transfer + exchange_url: string; + + // Serial number identifying the transfer in the merchant backend. + // Used for filgering via offset. + transfer_serial_id: number; + + // Time of the execution of the wire transfer by the exchange, according to the exchange + // Only provided if we did get an answer from the exchange. + execution_time?: Timestamp; + + // True if we checked the exchange's answer and are happy with it. + // False if we have an answer and are unhappy, missing if we + // do not have an answer from the exchange. + verified?: boolean; + + // True if the merchant uses the POST /transfers API to confirm + // that this wire transfer took place (and it is thus not + // something merely claimed by the exchange). + confirmed?: boolean; + } + + interface TransferInformation { + // how much was wired to the merchant (minus fees) + credit_amount: Amount; + + // raw wire transfer identifier identifying the wire transfer (a base32-encoded value) + wtid: WireTransferIdentifierRawP; + + // target account that received the wire transfer + payto_uri: string; + + // base URL of the exchange that made the wire transfer + exchange_url: string; + } + interface MerchantTrackTransferResponse { + // Total amount transferred + total: Amount; + + // Applicable wire fee that was charged + wire_fee: Amount; + + // Time of the execution of the wire transfer by the exchange, according to the exchange + execution_time: Timestamp; + + // details about the deposits + deposits_sums: MerchantTrackTransferDetail[]; + } + interface MerchantTrackTransferDetail { + // Business activity associated with the wire transferred amount + // deposit_value. + order_id: string; + + // The total amount the exchange paid back for order_id. + deposit_value: Amount; + + // applicable fees for the deposit + deposit_fee: Amount; + } + + type ExchangeConflictDetails = WireFeeConflictDetails | TrackTransferConflictDetails + // Note: this is not the full 'proof' of missbehavior, as + // the bogus message from the exchange with a signature + // over the 'different' wire fee is missing. + // + // This information is NOT provided by the current implementation, + // because this would be quite expensive to generate and is + // hardly needed _here_. Once we add automated reports for + // the Taler auditor, we need to generate this data anyway + // and should probably return it here as well. + interface WireFeeConflictDetails { + // Numerical error code: + code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE"; + + // Text describing the issue for humans. + hint: string; + + + // Wire fee (wrongly) charged by the exchange, breaking the + // contract affirmed by the exchange_sig. + wire_fee: Amount; + + // Timestamp of the wire transfer + execution_time: Timestamp; + + // The expected wire fee (as signed by the exchange) + expected_wire_fee: Amount; + + // Expected closing fee (needed to verify signature) + expected_closing_fee: Amount; + + // Start date of the expected fee structure + start_date: Timestamp; + + // End date of the expected fee structure + end_date: Timestamp; + + // Signature of the exchange affirming the expected fee structure + master_sig: EddsaSignature; + + // Master public key of the exchange + master_pub: EddsaPublicKey; + } + interface TrackTransferConflictDetails { + // Numerical error code + code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS"; + + // Text describing the issue for humans. + hint: string; + + // Offset in the exchange_transfer where the + // exchange's response fails to match the exchange_deposit_proof. + conflict_offset: number; + + // The response from the exchange which tells us when the + // coin was returned to us, except that it does not match + // the expected value of the coin. + // + // This field is NOT provided by the current implementation, + // because this would be quite expensive to generate and is + // hardly needed _here_. Once we add automated reports for + // the Taler auditor, we need to generate this data anyway + // and should probably return it here as well. + // exchange_transfer?: TrackTransferResponse; + + // Public key of the exchange used to sign the response to + // our deposit request. + deposit_exchange_pub: EddsaPublicKey; + + // Signature of the exchange signing the (conflicting) response. + // Signs over a struct TALER_DepositConfirmationPS. + deposit_exchange_sig: EddsaSignature; + + // Hash of the merchant's bank account the wire transfer went to + h_wire: HashCode; + + // Hash of the contract terms with the conflicting deposit. + h_contract_terms: HashCode; + + // At what time the exchange received the deposit. Needed + // to verify the \exchange_sig\. + deposit_timestamp: Timestamp; + + // At what time the refund possibility expired (needed to verify exchange_sig). + refund_deadline: Timestamp; + + // Public key of the coin for which we have conflicting information. + coin_pub: EddsaPublicKey; + + // Amount the exchange counted the coin for in the transfer. + amount_with_fee: Amount; + + // Expected value of the coin. + coin_value: Amount; + + // Expected deposit fee of the coin. + coin_fee: Amount; + + // Expected deposit fee of the coin. + deposit_fee: Amount; + + } + + // interface TrackTransferProof { + // // signature from the exchange made with purpose + // // TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT + // exchange_sig: EddsaSignature; + + // // public EdDSA key of the exchange that was used to generate the signature. + // // Should match one of the exchange's signing keys from /keys. Again given + // // explicitly as the client might otherwise be confused by clock skew as to + // // which signing key was used. + // exchange_pub: EddsaSignature; + + // // hash of the wire details (identical for all deposits) + // // Needed to check the exchange_sig + // h_wire: HashCode; + // } + + } + + + interface ContractTerms { + // Human-readable description of the whole purchase + summary: string; + + // Map from IETF BCP 47 language tags to localized summaries + summary_i18n?: { [lang_tag: string]: string }; + + // Unique, free-form identifier for the proposal. + // Must be unique within a merchant instance. + // For merchants that do not store proposals in their DB + // before the customer paid for them, the order_id can be used + // by the frontend to restore a proposal from the information + // encoded in it (such as a short product identifier and timestamp). + order_id: string; + + // Total price for the transaction. + // The exchange will subtract deposit fees from that amount + // before transferring it to the merchant. + amount: Amount; + + // The URL for this purchase. Every time is is visited, the merchant + // will send back to the customer the same proposal. Clearly, this URL + // can be bookmarked and shared by users. + fulfillment_url?: string; + + // Maximum total deposit fee accepted by the merchant for this contract + max_fee: Amount; + + // Maximum wire fee accepted by the merchant (customer share to be + // divided by the 'wire_fee_amortization' factor, and further reduced + // if deposit fees are below 'max_fee'). Default if missing is zero. + max_wire_fee: Amount; + + // Over how many customer transactions does the merchant expect to + // amortize wire fees on average? If the exchange's wire fee is + // above 'max_wire_fee', the difference is divided by this number + // to compute the expected customer's contribution to the wire fee. + // The customer's contribution may further be reduced by the difference + // between the 'max_fee' and the sum of the actual deposit fees. + // Optional, default value if missing is 1. 0 and negative values are + // invalid and also interpreted as 1. + wire_fee_amortization: number; + + // List of products that are part of the purchase (see Product). + products: Product[]; + + // Time when this contract was generated + timestamp: TalerProtocolTimestamp; + + // After this deadline has passed, no refunds will be accepted. + refund_deadline: TalerProtocolTimestamp; + + // After this deadline, the merchant won't accept payments for the contact + pay_deadline: TalerProtocolTimestamp; + + // Transfer deadline for the exchange. Must be in the + // deposit permissions of coins used to pay for this order. + wire_transfer_deadline: TalerProtocolTimestamp; + + // Merchant's public key used to sign this proposal; this information + // is typically added by the backend Note that this can be an ephemeral key. + merchant_pub: EddsaPublicKey; + + // Base URL of the (public!) merchant backend API. + // Must be an absolute URL that ends with a slash. + merchant_base_url: string; + + // More info about the merchant, see below + merchant: Merchant; + + // The hash of the merchant instance's wire details. + h_wire: HashCode; + + // Wire transfer method identifier for the wire method associated with h_wire. + // The wallet may only select exchanges via a matching auditor if the + // exchange also supports this wire method. + // The wire transfer fees must be added based on this wire transfer method. + wire_method: string; + + // Any exchanges audited by these auditors are accepted by the merchant. + auditors: Auditor[]; + + // Exchanges that the merchant accepts even if it does not accept any auditors that audit them. + exchanges: Exchange[]; + + // Delivery location for (all!) products. + delivery_location?: Location; + + // Time indicating when the order should be delivered. + // May be overwritten by individual products. + delivery_date?: TalerProtocolTimestamp; + + // Nonce generated by the wallet and echoed by the merchant + // in this field when the proposal is generated. + nonce: string; + + // Specifies for how long the wallet should try to get an + // automatic refund for the purchase. If this field is + // present, the wallet should wait for a few seconds after + // the purchase and then automatically attempt to obtain + // a refund. The wallet should probe until "delay" + // after the payment was successful (i.e. via long polling + // or via explicit requests with exponential back-off). + // + // In particular, if the wallet is offline + // at that time, it MUST repeat the request until it gets + // one response from the merchant after the delay has expired. + // If the refund is granted, the wallet MUST automatically + // recover the payment. This is used in case a merchant + // knows that it might be unable to satisfy the contract and + // desires for the wallet to attempt to get the refund without any + // customer interaction. Note that it is NOT an error if the + // merchant does not grant a refund. + auto_refund?: RelativeTime; + + // Extra data that is only interpreted by the merchant frontend. + // Useful when the merchant needs to store extra information on a + // contract without storing it separately in their database. + extra?: any; + } + +} diff --git a/packages/merchant-backend-ui/src/hooks/async.ts b/packages/merchant-backend-ui/src/hooks/async.ts new file mode 100644 index 000000000..fd550043b --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/async.ts @@ -0,0 +1,76 @@ +/* + 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 { useState } from "preact/hooks"; +import { cancelPendingRequest } from "./backend"; + +export interface Options { + slowTolerance: number, +} + +export interface AsyncOperationApi<T> { + request: (...a: any) => void, + cancel: () => void, + data: T | undefined, + isSlow: boolean, + isLoading: boolean, + error: string | undefined +} + +export function useAsync<T>(fn?: (...args: any) => Promise<T>, { slowTolerance: tooLong }: Options = { slowTolerance: 1000 }): AsyncOperationApi<T> { + const [data, setData] = useState<T | undefined>(undefined); + const [isLoading, setLoading] = useState<boolean>(false); + const [error, setError] = useState<any>(undefined); + const [isSlow, setSlow] = useState(false) + + const request = async (...args: any) => { + if (!fn) return; + setLoading(true); + + const handler = setTimeout(() => { + setSlow(true) + }, tooLong) + + try { + const result = await fn(...args); + setData(result); + } catch (error) { + setError(error); + } + setLoading(false); + setSlow(false) + clearTimeout(handler) + }; + + function cancel() { + cancelPendingRequest() + setLoading(false); + setSlow(false) + } + + return { + request, + cancel, + data, + isSlow, + isLoading, + error + }; +} diff --git a/packages/merchant-backend-ui/src/hooks/backend.ts b/packages/merchant-backend-ui/src/hooks/backend.ts new file mode 100644 index 000000000..96b8f7139 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/backend.ts @@ -0,0 +1,262 @@ +/* + 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 { mutate, cache } from 'swr'; +import axios, { AxiosError, AxiosResponse } from 'axios' +import { MerchantBackend } from '../declaration'; +import { useBackendContext } from '../context/backend'; +import { useEffect, useState } from 'preact/hooks'; +import { DEFAULT_REQUEST_TIMEOUT } from '../utils/constants'; + +export function mutateAll(re: RegExp, value?: unknown): Array<Promise<any>> { + return cache.keys().filter(key => { + return re.test(key) + }).map(key => { + return mutate(key, value) + }) +} + +export type HttpResponse<T> = HttpResponseOk<T> | HttpResponseLoading<T> | HttpError; +export type HttpResponsePaginated<T> = HttpResponseOkPaginated<T> | HttpResponseLoading<T> | HttpError; + +export interface RequestInfo { + url: string; + hasToken: boolean; + params: unknown; + data: unknown; +} + +interface HttpResponseLoading<T> { + ok?: false; + loading: true; + clientError?: false; + serverError?: false; + + data?: T; +} +export interface HttpResponseOk<T> { + ok: true; + loading?: false; + clientError?: false; + serverError?: false; + + data: T; + info?: RequestInfo; +} + +export type HttpResponseOkPaginated<T> = HttpResponseOk<T> & WithPagination + +export interface WithPagination { + loadMore: () => void; + loadMorePrev: () => void; + isReachingEnd?: boolean; + isReachingStart?: boolean; +} + +export type HttpError = HttpResponseClientError | HttpResponseServerError | HttpResponseUnexpectedError; +export interface SwrError { + info: unknown, + status: number, + message: string, +} +export interface HttpResponseServerError { + ok?: false; + loading?: false; + clientError?: false; + serverError: true; + + error?: MerchantBackend.ErrorDetail; + status: number; + message: string; + info?: RequestInfo; +} +interface HttpResponseClientError { + ok?: false; + loading?: false; + clientError: true; + serverError?: false; + + info?: RequestInfo; + isUnauthorized: boolean; + isNotfound: boolean; + status: number; + error?: MerchantBackend.ErrorDetail; + message: string; + +} + +interface HttpResponseUnexpectedError { + ok?: false; + loading?: false; + clientError?: false; + serverError?: false; + + info?: RequestInfo; + status?: number; + error: unknown; + message: string; +} + +type Methods = 'get' | 'post' | 'patch' | 'delete' | 'put'; + +interface RequestOptions { + method?: Methods; + token?: string; + data?: unknown; + params?: unknown; +} + +function buildRequestOk<T>(res: AxiosResponse<T>, url: string, hasToken: boolean): HttpResponseOk<T> { + return { + ok: true, data: res.data, info: { + params: res.config.params, + data: res.config.data, + url, + hasToken, + } + } +} + +// function buildResponse<T>(data?: T, error?: MerchantBackend.ErrorDetail, isValidating?: boolean): HttpResponse<T> { +// if (isValidating) return {loading: true} +// if (error) return buildRequestFailed() +// } + +function buildRequestFailed(ex: AxiosError<MerchantBackend.ErrorDetail>, url: string, hasToken: boolean): HttpResponseClientError | HttpResponseServerError | HttpResponseUnexpectedError { + const status = ex.response?.status + + const info: RequestInfo = { + data: ex.request?.data, + params: ex.request?.params, + url, + hasToken, + }; + + if (status && status >= 400 && status < 500) { + const error: HttpResponseClientError = { + clientError: true, + isNotfound: status === 404, + isUnauthorized: status === 401, + status, + info, + message: ex.response?.data?.hint || ex.message, + error: ex.response?.data + } + return error + } + if (status && status >= 500 && status < 600) { + const error: HttpResponseServerError = { + serverError: true, + status, + info, + message: `${ex.response?.data?.hint} (code ${ex.response?.data?.code})` || ex.message, + error: ex.response?.data + } + return error; + } + + const error: HttpResponseUnexpectedError = { + info, + status, + error: ex, + message: ex.message + } + + return error +} + + +const CancelToken = axios.CancelToken; +let source = CancelToken.source(); + +export function cancelPendingRequest() { + source.cancel('canceled by the user') + source = CancelToken.source() +} + +let removeAxiosCancelToken = false +/** + * Jest mocking seems to break when using the cancelToken property. + * Using this workaround when testing while finding the correct solution + */ +export function setAxiosRequestAsTestingEnvironment() { + removeAxiosCancelToken = true +} + +export async function request<T>(url: string, options: RequestOptions = {}): Promise<HttpResponseOk<T>> { + const headers = options.token ? { Authorization: `Bearer ${options.token}` } : undefined + + try { + const res = await axios({ + url, + responseType: 'json', + headers, + cancelToken: !removeAxiosCancelToken? source.token : undefined, + method: options.method || 'get', + data: options.data, + params: options.params, + timeout: DEFAULT_REQUEST_TIMEOUT * 1000, + }) + return buildRequestOk<T>(res, url, !!options.token) + } catch (e) { + const error = buildRequestFailed(e, url, !!options.token) + throw error + } + +} + +export function fetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> { + return request<T>(`${backend}${url}`, { token }) +} + +export function useBackendInstancesTestForAdmin(): HttpResponse<MerchantBackend.Instances.InstancesResponse> { + const { url, token } = useBackendContext() + + type Type = MerchantBackend.Instances.InstancesResponse; + + const [result, setResult] = useState<HttpResponse<Type>>({ loading: true }) + + useEffect(() => { + request<Type>(`${url}/management/instances`, { token }) + .then(data => setResult(data)) + .catch(error => setResult(error)) + }, [url, token]) + + + return result +} + + +export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse> { + const { url, token } = useBackendContext() + + type Type = MerchantBackend.VersionResponse; + + const [result, setResult] = useState<HttpResponse<Type>>({ loading: true }) + + useEffect(() => { + request<Type>(`${url}/config`, { token }) + .then(data => setResult(data)) + .catch(error => setResult(error)) + }, [url, token]) + + return result +} diff --git a/packages/merchant-backend-ui/src/hooks/index.ts b/packages/merchant-backend-ui/src/hooks/index.ts new file mode 100644 index 000000000..19d672ad3 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/index.ts @@ -0,0 +1,110 @@ +/* + 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 { StateUpdater, useCallback, useState } from "preact/hooks"; +import { ValueOrFunction } from '../utils/types'; + + +const calculateRootPath = () => { + const rootPath = typeof window !== undefined ? window.location.origin + window.location.pathname : '/' + return rootPath +} + +export function useBackendURL(url?: string): [string, boolean, StateUpdater<string>, () => void] { + const [value, setter] = useNotNullLocalStorage('backend-url', url || calculateRootPath()) + const [triedToLog, setTriedToLog] = useLocalStorage('tried-login') + + const checkedSetter = (v: ValueOrFunction<string>) => { + setTriedToLog('yes') + return setter(p => (v instanceof Function ? v(p) : v).replace(/\/$/, '')) + } + + const resetBackend = () => { + setTriedToLog(undefined) + } + return [value, !!triedToLog, checkedSetter, resetBackend] +} + +export function useBackendDefaultToken(): [string | undefined, StateUpdater<string | undefined>] { + return useLocalStorage('backend-token') +} + +export function useBackendInstanceToken(id: string): [string | undefined, StateUpdater<string | undefined>] { + const [token, setToken] = useLocalStorage(`backend-token-${id}`) + const [defaultToken, defaultSetToken] = useBackendDefaultToken() + + // instance named 'default' use the default token + if (id === 'default') { + return [defaultToken, defaultSetToken] + } + + return [token, setToken] +} + +export function useLang(initial?: string): [string, StateUpdater<string>] { + const browserLang = typeof window !== "undefined" ? navigator.language || (navigator as any).userLanguage : undefined; + const defaultLang = (browserLang || initial || 'en').substring(0, 2) + return useNotNullLocalStorage('lang-preference', defaultLang) +} + +export function useLocalStorage(key: string, initialValue?: string): [string | undefined, StateUpdater<string | undefined>] { + const [storedValue, setStoredValue] = useState<string | undefined>((): string | undefined => { + return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue; + }); + + const setValue = (value?: string | ((val?: string) => string | undefined)) => { + setStoredValue(p => { + const toStore = value instanceof Function ? value(p) : value + if (typeof window !== "undefined") { + if (!toStore) { + window.localStorage.removeItem(key) + } else { + window.localStorage.setItem(key, toStore); + } + } + return toStore + }) + }; + + return [storedValue, setValue]; +} + +export function useNotNullLocalStorage(key: string, initialValue: string): [string, StateUpdater<string>] { + const [storedValue, setStoredValue] = useState<string>((): string => { + return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue; + }); + + const setValue = (value: string | ((val: string) => string)) => { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + if (typeof window !== "undefined") { + if (!valueToStore) { + window.localStorage.removeItem(key) + } else { + window.localStorage.setItem(key, valueToStore); + } + } + }; + + return [storedValue, setValue]; +} + + diff --git a/packages/merchant-backend-ui/src/hooks/instance.ts b/packages/merchant-backend-ui/src/hooks/instance.ts new file mode 100644 index 000000000..14ab8de9c --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/instance.ts @@ -0,0 +1,187 @@ +/* + 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/> + */ +import { MerchantBackend } from '../declaration'; +import { useBackendContext } from '../context/backend'; +import { fetcher, HttpError, HttpResponse, HttpResponseOk, request, SwrError } from './backend'; +import useSWR, { mutate } from 'swr'; +import { useInstanceContext } from '../context/instance'; + + +interface InstanceAPI { + updateInstance: (data: MerchantBackend.Instances.InstanceReconfigurationMessage) => Promise<void>; + deleteInstance: () => Promise<void>; + clearToken: () => Promise<void>; + setNewToken: (token: string) => Promise<void>; +} + +export function useManagementAPI(instanceId: string) : InstanceAPI { + const { url, token } = useBackendContext() + + const updateInstance = async (instance: MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => { + await request(`${url}/management/instances/${instanceId}`, { + method: 'patch', + token, + data: instance + }) + + mutate([`/private/`, token, url], null) + }; + + const deleteInstance = async (): Promise<void> => { + await request(`${url}/management/instances/${instanceId}`, { + method: 'delete', + token, + }) + + mutate([`/private/`, token, url], null) + } + + const clearToken = async (): Promise<void> => { + await request(`${url}/management/instances/${instanceId}/auth`, { + method: 'post', + token, + data: { method: 'external' } + }) + + mutate([`/private/`, token, url], null) + } + + const setNewToken = async (newToken: string): Promise<void> => { + await request(`${url}/management/instances/${instanceId}/auth`, { + method: 'post', + token, + data: { method: 'token', token: newToken } + }) + + mutate([`/private/`, token, url], null) + } + + return { updateInstance, deleteInstance, setNewToken, clearToken } +} + +export function useInstanceAPI(): InstanceAPI { + const { url: baseUrl, token: adminToken } = useBackendContext() + const { token: instanceToken, id, admin } = useInstanceContext() + + const { url, token } = !admin ? { + url: baseUrl, token: adminToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + }; + + const updateInstance = async (instance: MerchantBackend.Instances.InstanceReconfigurationMessage): Promise<void> => { + await request(`${url}/private/`, { + method: 'patch', + token, + data: instance + }) + + if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null) + mutate([`/private/`, token, url], null) + }; + + const deleteInstance = async (): Promise<void> => { + await request(`${url}/private/`, { + method: 'delete', + token: adminToken, + }) + + if (adminToken) mutate(['/private/instances', adminToken, baseUrl], null) + mutate([`/private/`, token, url], null) + } + + const clearToken = async (): Promise<void> => { + await request(`${url}/private/auth`, { + method: 'post', + token, + data: { method: 'external' } + }) + + mutate([`/private/`, token, url], null) + } + + const setNewToken = async (newToken: string): Promise<void> => { + await request(`${url}/private/auth`, { + method: 'post', + token, + data: { method: 'token', token: newToken } + }) + + mutate([`/private/`, token, url], null) + } + + return { updateInstance, deleteInstance, setNewToken, clearToken } +} + + +export function useInstanceDetails(): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin ? { + url: baseUrl, token: baseToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + } + + const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpError>([`/private/`, token, url], fetcher, { + refreshInterval:0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + errorRetryCount: 0, + errorRetryInterval: 1, + shouldRetryOnError: false, + }) + + if (isValidating) return {loading:true, data: data?.data} + if (data) return data + if (error) return error + return {loading: true} +} + +export function useManagedInstanceDetails(instanceId: string): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> { + const { url, token } = useBackendContext(); + + const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpError>([`/management/instances/${instanceId}`, token, url], fetcher, { + refreshInterval:0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + errorRetryCount: 0, + errorRetryInterval: 1, + shouldRetryOnError: false, + }) + + if (isValidating) return {loading:true, data: data?.data} + if (data) return data + if (error) return error + return {loading: true} +} + +export function useBackendInstances(): HttpResponse<MerchantBackend.Instances.InstancesResponse> { + const { url } = useBackendContext() + const { token } = useInstanceContext(); + + const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Instances.InstancesResponse>, HttpError>(['/management/instances', token, url], fetcher) + + if (isValidating) return {loading:true, data: data?.data} + if (data) return data + if (error) return error + return {loading: true} +} diff --git a/packages/merchant-backend-ui/src/hooks/listener.ts b/packages/merchant-backend-ui/src/hooks/listener.ts new file mode 100644 index 000000000..231ed6c87 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/listener.ts @@ -0,0 +1,68 @@ +/* + 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 { useState } from "preact/hooks"; + +/** + * returns subscriber and activator + * subscriber will receive a method (listener) that will be call when the activator runs. + * the result of calling the listener will be sent to @action + * + * @param action from <T> to <R> + * @returns activator and subscriber, undefined activator means that there is not subscriber + */ + +export function useListener<T, R = any>(action: (r: T) => Promise<R>): [undefined | (() => Promise<R>), (listener?: () => T) => void] { + type RunnerHandler = { toBeRan?: () => Promise<R>; }; + const [state, setState] = useState<RunnerHandler>({}); + + /** + * subscriber will receive a method that will be call when the activator runs + * + * @param listener function to be run when the activator runs + */ + const subscriber = (listener?: () => T) => { + if (listener) { + setState({ + toBeRan: () => { + const whatWeGetFromTheListener = listener(); + return action(whatWeGetFromTheListener); + } + }); + } else { + setState({ + toBeRan: undefined + }) + } + }; + + /** + * activator will call runner if there is someone subscribed + */ + const activator = state.toBeRan ? async () => { + if (state.toBeRan) { + return state.toBeRan(); + } + return Promise.reject(); + } : undefined; + + return [activator, subscriber]; +} diff --git a/packages/merchant-backend-ui/src/hooks/notification.ts b/packages/merchant-backend-ui/src/hooks/notification.ts new file mode 100644 index 000000000..d1dfbff2c --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/notification.ts @@ -0,0 +1,43 @@ +/* + 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 { useCallback, useState } from "preact/hooks"; +import { Notification } from '../utils/types'; + +interface Result { + notification?: Notification; + pushNotification: (n: Notification) => void; + removeNotification: () => void; +} + +export function useNotification(): Result { + const [notification, setNotifications] = useState<Notification|undefined>(undefined) + + const pushNotification = useCallback((n: Notification): void => { + setNotifications(n) + },[]) + + const removeNotification = useCallback(() => { + setNotifications(undefined) + },[]) + + return { notification, pushNotification, removeNotification } +} diff --git a/packages/merchant-backend-ui/src/hooks/notifications.ts b/packages/merchant-backend-ui/src/hooks/notifications.ts new file mode 100644 index 000000000..1c0c37308 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/notifications.ts @@ -0,0 +1,48 @@ +/* + 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 { useState } from "preact/hooks"; +import { Notification } from '../utils/types'; + +interface Result { + notifications: Notification[]; + pushNotification: (n: Notification) => void; + removeNotification: (n: Notification) => void; +} + +type NotificationWithDate = Notification & { since: Date } + +export function useNotifications(initial: Notification[] = [], timeout = 3000): Result { + const [notifications, setNotifications] = useState<(NotificationWithDate)[]>(initial.map(i => ({...i, since: new Date() }))) + + const pushNotification = (n: Notification): void => { + const entry = { ...n, since: new Date() } + setNotifications(ns => [...ns, entry]) + if (n.type !== 'ERROR') setTimeout(() => { + setNotifications(ns => ns.filter(x => x.since !== entry.since)) + }, timeout) + } + + const removeNotification = (notif: Notification) => { + setNotifications((ns: NotificationWithDate[]) => ns.filter(n => n !== notif)) + } + return { notifications, pushNotification, removeNotification } +} diff --git a/packages/merchant-backend-ui/src/hooks/order.ts b/packages/merchant-backend-ui/src/hooks/order.ts new file mode 100644 index 000000000..4a17eac30 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/order.ts @@ -0,0 +1,217 @@ +/* + 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/> + */ +import { useEffect, useState } from 'preact/hooks'; +import useSWR from 'swr'; +import { useBackendContext } from '../context/backend'; +import { useInstanceContext } from '../context/instance'; +import { MerchantBackend } from '../declaration'; +import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants'; +import { fetcher, HttpError, HttpResponse, HttpResponseOk, HttpResponsePaginated, mutateAll, request } from './backend'; + +export interface OrderAPI { + //FIXME: add OutOfStockResponse on 410 + createOrder: (data: MerchantBackend.Orders.PostOrderRequest) => Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>>; + forgetOrder: (id: string, data: MerchantBackend.Orders.ForgetRequest) => Promise<HttpResponseOk<void>>; + refundOrder: (id: string, data: MerchantBackend.Orders.RefundRequest) => Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>>; + deleteOrder: (id: string) => Promise<HttpResponseOk<void>>; + getPaymentURL: (id: string) => Promise<HttpResponseOk<string>>; +} + +type YesOrNo = 'yes' | 'no'; + + +export function orderFetcher<T>(url: string, token: string, backend: string, paid?: YesOrNo, refunded?: YesOrNo, wired?: YesOrNo, searchDate?: Date, delta?: number): Promise<HttpResponseOk<T>> { + const date_ms = delta && delta < 0 && searchDate ? searchDate.getTime() + 1 : searchDate?.getTime() + const params: any = {} + if (paid !== undefined) params.paid = paid + if (delta !== undefined) params.delta = delta + if (refunded !== undefined) params.refunded = refunded + if (wired !== undefined) params.wired = wired + if (date_ms !== undefined) params.date_ms = date_ms + return request<T>(`${backend}${url}`, { token, params }) +} + + +export function useOrderAPI(): OrderAPI { + const { url: baseUrl, token: adminToken } = useBackendContext() + const { token: instanceToken, id, admin } = useInstanceContext() + + const { url, token } = !admin ? { + url: baseUrl, token: adminToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + } + + const createOrder = async (data: MerchantBackend.Orders.PostOrderRequest): Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>> => { + const res = await request<MerchantBackend.Orders.PostOrderResponse>(`${url}/private/orders`, { + method: 'post', + token, + data + }) + await mutateAll(/@"\/private\/orders"@/) + return res + } + const refundOrder = async (orderId: string, data: MerchantBackend.Orders.RefundRequest): Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>> => { + mutateAll(/@"\/private\/orders"@/) + return request<MerchantBackend.Orders.MerchantRefundResponse>(`${url}/private/orders/${orderId}/refund`, { + method: 'post', + token, + data + }) + + // return res + } + + const forgetOrder = async (orderId: string, data: MerchantBackend.Orders.ForgetRequest): Promise<HttpResponseOk<void>> => { + mutateAll(/@"\/private\/orders"@/) + return request(`${url}/private/orders/${orderId}/forget`, { + method: 'patch', + token, + data + }) + + } + const deleteOrder = async (orderId: string): Promise<HttpResponseOk<void>> => { + mutateAll(/@"\/private\/orders"@/) + return request(`${url}/private/orders/${orderId}`, { + method: 'delete', + token + }) + } + + const getPaymentURL = async (orderId: string): Promise<HttpResponseOk<string>> => { + return request<MerchantBackend.Orders.MerchantOrderStatusResponse>(`${url}/private/orders/${orderId}`, { + method: 'get', + token + }).then((res) => { + const url = res.data.order_status === "unpaid" ? res.data.taler_pay_uri : res.data.contract_terms.fulfillment_url + const response: HttpResponseOk<string> = res as any + response.data = url || '' + return response + }) + } + + return { createOrder, forgetOrder, deleteOrder, refundOrder, getPaymentURL } +} + +export function useOrderDetails(oderId: string): HttpResponse<MerchantBackend.Orders.MerchantOrderStatusResponse> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin ? { + url: baseUrl, token: baseToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + }; + + const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Orders.MerchantOrderStatusResponse>, HttpError>([`/private/orders/${oderId}`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }) + + if (isValidating) return { loading: true, data: data?.data } + if (data) return data + if (error) return error + return { loading: true } +} + +export interface InstanceOrderFilter { + paid?: YesOrNo; + refunded?: YesOrNo; + wired?: YesOrNo; + date?: Date; +} + +export function useInstanceOrders(args?: InstanceOrderFilter, updateFilter?: (d: Date) => void): HttpResponsePaginated<MerchantBackend.Orders.OrderHistory> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin ? { + url: baseUrl, token: baseToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + } + + const [pageBefore, setPageBefore] = useState(1) + const [pageAfter, setPageAfter] = useState(1) + + const totalAfter = pageAfter * PAGE_SIZE; + const totalBefore = args?.date ? pageBefore * PAGE_SIZE : 0; + + /** + * FIXME: this can be cleaned up a little + * + * the logic of double query should be inside the orderFetch so from the hook perspective and cache + * is just one query and one error status + */ + const { data: beforeData, error: beforeError, isValidating: loadingBefore } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>( + [`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired, args?.date, totalBefore], + orderFetcher, + ) + const { data: afterData, error: afterError, isValidating: loadingAfter } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>( + [`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired, args?.date, -totalAfter], + orderFetcher, + ) + + //this will save last result + const [lastBefore, setLastBefore] = useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true }) + const [lastAfter, setLastAfter] = useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true }) + useEffect(() => { + if (afterData) setLastAfter(afterData) + if (beforeData) setLastBefore(beforeData) + }, [afterData, beforeData]) + + // this has problems when there are some ids missing + + if (beforeError) return beforeError + if (afterError) return afterError + + + const pagination = { + isReachingEnd: afterData && afterData.data.orders.length < totalAfter, + isReachingStart: (!args?.date) || (beforeData && beforeData.data.orders.length < totalBefore), + loadMore: () => { + if (!afterData) return + if (afterData.data.orders.length < MAX_RESULT_SIZE) { + setPageAfter(pageAfter + 1) + } else { + const from = afterData.data.orders[afterData.data.orders.length - 1].timestamp.t_s + if (from && updateFilter) updateFilter(new Date(from)) + } + }, + loadMorePrev: () => { + if (!beforeData) return + if (beforeData.data.orders.length < MAX_RESULT_SIZE) { + setPageBefore(pageBefore + 1) + } else if (beforeData) { + const from = beforeData.data.orders[beforeData.data.orders.length - 1].timestamp.t_s + if (from && updateFilter) updateFilter(new Date(from)) + } + }, + } + + const orders = !beforeData || !afterData ? [] : (beforeData || lastBefore).data.orders.slice().reverse().concat((afterData || lastAfter).data.orders) + if (loadingAfter || loadingBefore) return { loading: true, data: { orders } } + if (beforeData && afterData) { + return { ok: true, data: { orders }, ...pagination } + } + return { loading: true } + +} + diff --git a/packages/merchant-backend-ui/src/hooks/product.ts b/packages/merchant-backend-ui/src/hooks/product.ts new file mode 100644 index 000000000..4fc8bccb7 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/product.ts @@ -0,0 +1,223 @@ +/* + 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/> + */ +import { useEffect } from "preact/hooks"; +import useSWR, { trigger, useSWRInfinite, cache, mutate } from "swr"; +import { useBackendContext } from "../context/backend"; +// import { useFetchContext } from '../context/fetch'; +import { useInstanceContext } from "../context/instance"; +import { MerchantBackend, WithId } from "../declaration"; +import { + fetcher, + HttpError, + HttpResponse, + HttpResponseOk, + mutateAll, + request, +} from "./backend"; + +export interface ProductAPI { + createProduct: ( + data: MerchantBackend.Products.ProductAddDetail + ) => Promise<void>; + updateProduct: ( + id: string, + data: MerchantBackend.Products.ProductPatchDetail + ) => Promise<void>; + deleteProduct: (id: string) => Promise<void>; + lockProduct: ( + id: string, + data: MerchantBackend.Products.LockRequest + ) => Promise<void>; +} + +export function useProductAPI(): ProductAPI { + const { url: baseUrl, token: adminToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { + url: baseUrl, + token: adminToken, + } + : { + url: `${baseUrl}/instances/${id}`, + token: instanceToken, + }; + + const createProduct = async ( + data: MerchantBackend.Products.ProductAddDetail + ): Promise<void> => { + await request(`${url}/private/products`, { + method: "post", + token, + data, + }); + + await mutateAll(/@"\/private\/products"@/, null); + }; + + const updateProduct = async ( + productId: string, + data: MerchantBackend.Products.ProductPatchDetail + ): Promise<void> => { + const r = await request(`${url}/private/products/${productId}`, { + method: "patch", + token, + data, + }); + + await mutateAll(/@"\/private\/products\/.*"@/); + return Promise.resolve(); + }; + + const deleteProduct = async (productId: string): Promise<void> => { + await request(`${url}/private/products/${productId}`, { + method: "delete", + token, + }); + + await mutateAll(/@"\/private\/products"@/); + }; + + const lockProduct = async ( + productId: string, + data: MerchantBackend.Products.LockRequest + ): Promise<void> => { + await request(`${url}/private/products/${productId}/lock`, { + method: "post", + token, + data, + }); + + await mutateAll(/@"\/private\/products"@/); + }; + + return { createProduct, updateProduct, deleteProduct, lockProduct }; +} + +export function useInstanceProducts(): HttpResponse< + (MerchantBackend.Products.ProductDetail & WithId)[] +> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + // const { useSWR, useSWRInfinite } = useFetchContext(); + + const { url, token } = !admin + ? { + url: baseUrl, + token: baseToken, + } + : { + url: `${baseUrl}/instances/${id}`, + token: instanceToken, + }; + + const { + data: list, + error: listError, + isValidating: listLoading, + } = useSWR< + HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>, + HttpError + >([`/private/products`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + const { + data: products, + error: productError, + setSize, + size, + } = useSWRInfinite< + HttpResponseOk<MerchantBackend.Products.ProductDetail>, + HttpError + >( + (pageIndex: number) => { + if (!list?.data || !list.data.products.length || listError || listLoading) + return null; + return [ + `/private/products/${list.data.products[pageIndex].product_id}`, + token, + url, + ]; + }, + fetcher, + { + revalidateAll: true, + } + ); + + useEffect(() => { + if (list?.data && list.data.products.length > 0) { + setSize(list.data.products.length); + } + }, [list?.data.products.length, listLoading]); + + if (listLoading) return { loading: true, data: [] }; + if (listError) return listError; + if (productError) return productError; + if (list?.data && list.data.products.length === 0) { + return { ok: true, data: [] }; + } + if (products) { + const dataWithId = products.map((d) => { + //take the id from the queried url + return { + ...d.data, + id: d.info?.url.replace(/.*\/private\/products\//, "") || "", + }; + }); + return { ok: true, data: dataWithId }; + } + return { loading: true }; +} + +export function useProductDetails( + productId: string +): HttpResponse<MerchantBackend.Products.ProductDetail> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { + url: baseUrl, + token: baseToken, + } + : { + url: `${baseUrl}/instances/${id}`, + token: instanceToken, + }; + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Products.ProductDetail>, + HttpError + >([`/private/products/${productId}`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} diff --git a/packages/merchant-backend-ui/src/hooks/tips.ts b/packages/merchant-backend-ui/src/hooks/tips.ts new file mode 100644 index 000000000..345e1faa5 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/tips.ts @@ -0,0 +1,159 @@ +/* + 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/> + */ +import useSWR from 'swr'; +import { useBackendContext } from '../context/backend'; +import { useInstanceContext } from '../context/instance'; +import { MerchantBackend } from '../declaration'; +import { fetcher, HttpError, HttpResponse, HttpResponseOk, mutateAll, request } from './backend'; + + +export function useReservesAPI(): ReserveMutateAPI { + const { url: baseUrl, token: adminToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin ? { + url: baseUrl, token: adminToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + }; + + const createReserve = async (data: MerchantBackend.Tips.ReserveCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>> => { + const res = await request<MerchantBackend.Tips.ReserveCreateConfirmation>(`${url}/private/reserves`, { + method: 'post', + token, + data + }); + + await mutateAll(/@"\/private\/reserves"@/); + + return res + }; + + const authorizeTipReserve = async (pub: string, data: MerchantBackend.Tips.TipCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => { + const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/reserves/${pub}/authorize-tip`, { + method: 'post', + token, + data + }); + await mutateAll(/@"\/private\/reserves"@/); + + return res + }; + + const authorizeTip = async (data: MerchantBackend.Tips.TipCreateRequest): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => { + const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/tips`, { + method: 'post', + token, + data + }); + + await mutateAll(/@"\/private\/reserves"@/); + + return res + }; + + const deleteReserve = async (pub: string): Promise<HttpResponse<void>> => { + const res = await request<void>(`${url}/private/reserves/${pub}`, { + method: 'delete', + token, + }); + + await mutateAll(/@"\/private\/reserves"@/); + + return res + }; + + + return { createReserve, authorizeTip, authorizeTipReserve, deleteReserve }; +} + +export interface ReserveMutateAPI { + createReserve: (data: MerchantBackend.Tips.ReserveCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>>; + authorizeTipReserve: (id: string, data: MerchantBackend.Tips.TipCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>; + authorizeTip: (data: MerchantBackend.Tips.TipCreateRequest) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>; + deleteReserve: (id: string) => Promise<HttpResponse<void>>; +} + +export function useInstanceTips(): HttpResponse<MerchantBackend.Tips.TippingReserveStatus> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin ? { + url: baseUrl, token: baseToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + } + + const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>, HttpError>([`/private/reserves`, token, url], fetcher) + + if (isValidating) return { loading: true, data: data?.data } + if (data) return data + if (error) return error + return { loading: true } +} + + +export function useReserveDetails(reserveId: string): HttpResponse<MerchantBackend.Tips.ReserveDetail> { + const { url: baseUrl } = useBackendContext(); + const { token, id: instanceId, admin } = useInstanceContext(); + + const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}` + + const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.ReserveDetail>, HttpError>([`/private/reserves/${reserveId}`, token, url], reserveDetailFetcher, { + refreshInterval:0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }) + + if (isValidating) return { loading: true, data: data?.data } + if (data) return data + if (error) return error + return { loading: true } +} + +export function useTipDetails(tipId: string): HttpResponse<MerchantBackend.Tips.TipDetails> { + const { url: baseUrl } = useBackendContext(); + const { token, id: instanceId, admin } = useInstanceContext(); + + const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}` + + const { data, error, isValidating } = useSWR<HttpResponseOk<MerchantBackend.Tips.TipDetails>, HttpError>([`/private/tips/${tipId}`, token, url], tipsDetailFetcher, { + refreshInterval:0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }) + + if (isValidating) return { loading: true, data: data?.data } + if (data) return data + if (error) return error + return { loading: true } +} + +export function reserveDetailFetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> { + return request<T>(`${backend}${url}`, { token, params: { + tips: 'yes' + } }) +} + +export function tipsDetailFetcher<T>(url: string, token: string, backend: string): Promise<HttpResponseOk<T>> { + return request<T>(`${backend}${url}`, { token, params: { + pickups: 'yes' + } }) +} diff --git a/packages/merchant-backend-ui/src/hooks/transfer.ts b/packages/merchant-backend-ui/src/hooks/transfer.ts new file mode 100644 index 000000000..482f00dc5 --- /dev/null +++ b/packages/merchant-backend-ui/src/hooks/transfer.ts @@ -0,0 +1,150 @@ +/* + 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/> + */ +import { MerchantBackend } from '../declaration'; +import { useBackendContext } from '../context/backend'; +import { request, mutateAll, HttpResponse, HttpError, HttpResponseOk, HttpResponsePaginated } from './backend'; +import useSWR from 'swr'; +import { useInstanceContext } from '../context/instance'; +import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants'; +import { useEffect, useState } from 'preact/hooks'; + +async function transferFetcher<T>(url: string, token: string, backend: string, payto_uri?: string, verified?: string, position?: string, delta?: number): Promise<HttpResponseOk<T>> { + const params: any = {} + if (payto_uri !== undefined) params.payto_uri = payto_uri + if (verified !== undefined) params.verified = verified + if (delta !== undefined) { + // if (delta > 0) { + // params.after = searchDate?.getTime() + // } else { + // params.before = searchDate?.getTime() + // } + params.limit = delta + } + if (position !== undefined) params.offset = position + + return request<T>(`${backend}${url}`, { token, params }) +} + +export function useTransferAPI(): TransferAPI { + const { url: baseUrl, token: adminToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin ? { + url: baseUrl, token: adminToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + }; + + const informTransfer = async (data: MerchantBackend.Transfers.TransferInformation): Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>> => { + mutateAll(/@"\/private\/transfers"@/); + + return request<MerchantBackend.Transfers.MerchantTrackTransferResponse>(`${url}/private/transfers`, { + method: 'post', + token, + data + }); + }; + + return { informTransfer }; +} + +export interface TransferAPI { + informTransfer: (data: MerchantBackend.Transfers.TransferInformation) => Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>>; +} + +export interface InstanceTransferFilter { + payto_uri?: string; + verified?: 'yes' | 'no'; + position?: string; +} + + +export function useInstanceTransfers(args?: InstanceTransferFilter, updatePosition?: (id: string) => void): HttpResponsePaginated<MerchantBackend.Transfers.TransferList> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin ? { + url: baseUrl, token: baseToken + } : { + url: `${baseUrl}/instances/${id}`, token: instanceToken + } + + const [pageBefore, setPageBefore] = useState(1) + const [pageAfter, setPageAfter] = useState(1) + + const totalAfter = pageAfter * PAGE_SIZE; + const totalBefore = args?.position !== undefined ? pageBefore * PAGE_SIZE : 0; + + /** + * FIXME: this can be cleaned up a little + * + * the logic of double query should be inside the orderFetch so from the hook perspective and cache + * is just one query and one error status + */ + const { data: beforeData, error: beforeError, isValidating: loadingBefore } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>( + [`/private/transfers`, token, url, args?.payto_uri, args?.verified, args?.position, totalBefore], + transferFetcher, + ) + const { data: afterData, error: afterError, isValidating: loadingAfter } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>( + [`/private/transfers`, token, url, args?.payto_uri, args?.verified, args?.position, -totalAfter], + transferFetcher, + ) + + //this will save last result + const [lastBefore, setLastBefore] = useState<HttpResponse<MerchantBackend.Transfers.TransferList>>({ loading: true }) + const [lastAfter, setLastAfter] = useState<HttpResponse<MerchantBackend.Transfers.TransferList>>({ loading: true }) + useEffect(() => { + if (afterData) setLastAfter(afterData) + if (beforeData) setLastBefore(beforeData) + }, [afterData, beforeData]) + + // this has problems when there are some ids missing + + if (beforeError) return beforeError + if (afterError) return afterError + + const pagination = { + isReachingEnd: afterData && afterData.data.transfers.length < totalAfter, + isReachingStart: (!args?.position) || (beforeData && beforeData.data.transfers.length < totalBefore), + loadMore: () => { + if (!afterData) return + if (afterData.data.transfers.length < MAX_RESULT_SIZE) { + setPageAfter(pageAfter + 1) + } else { + const from = `${afterData.data.transfers[afterData.data.transfers.length - 1].transfer_serial_id}` + if (from && updatePosition) updatePosition(from) + } + }, + loadMorePrev: () => { + if (!beforeData) return + if (beforeData.data.transfers.length < MAX_RESULT_SIZE) { + setPageBefore(pageBefore + 1) + } else if (beforeData) { + const from = `${beforeData.data.transfers[beforeData.data.transfers.length - 1].transfer_serial_id}` + if (from && updatePosition) updatePosition(from) + } + }, + } + + const transfers = !beforeData || !afterData ? [] : (beforeData || lastBefore).data.transfers.slice().reverse().concat((afterData || lastAfter).data.transfers) + if (loadingAfter || loadingBefore) return { loading: true, data: { transfers } } + if (beforeData && afterData) { + return { ok: true, data: { transfers }, ...pagination } + } + return { loading: true } +} + + diff --git a/packages/merchant-backend-ui/src/i18n/de.po b/packages/merchant-backend-ui/src/i18n/de.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/de.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backend-ui/src/i18n/en.po b/packages/merchant-backend-ui/src/i18n/en.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/en.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backend-ui/src/i18n/es.po b/packages/merchant-backend-ui/src/i18n/es.po new file mode 100644 index 000000000..9075d4656 --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/es.po @@ -0,0 +1,1065 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "Acceso denegado" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "Verifica que el token sea valido" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "No se pudo acceder al servidor" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "No se pudo inferir el id de la instancia con la url %1$s" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "HTTP status #%1$s: Servidor reporto un problema" + +#: src/InstanceRoutes.tsx:110 +#, fuzzy, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "Recivimos el mensaje %1$s desde %2$s" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "Sin instancia default" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "para usar el merchant backoffice, deberÃa crear la instancia default" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "Servidir reporto un problema: HTTP status #%1$s" + +#: src/InstanceRoutes.tsx:289 +#, fuzzy, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "Recivimos el mensaje %1$s desde %2$s" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "Login necesario" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" +"Por favor ingrese su token de autorización. El token debe tener \"secret-" +"token\" y comenzar con Bearer o ApiKey" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "Confirmar" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "El valor %1$s es invalido para una URL de pago" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "elegir una fecha" + +#: src/components/form/InputDate.tsx:81 +#, fuzzy, c-format +msgid "clear" +msgstr "Limpiar" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "nunca" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "La imagen debe ser mas chica que 1 MB" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "PaÃs" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "Dirección" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "Número de edificio" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "Nombre de edificio" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "Calle" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "Código postal" + +#: src/components/form/InputLocation.tsx:38 +#, fuzzy, c-format +msgid "Town location" +msgstr "Ubicación de ciudad" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "Ciudad" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "Distrito" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "Provincia" + +#: src/components/form/InputSearchProduct.tsx:59 +#, fuzzy, c-format +msgid "Product id" +msgstr "Id de producto" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "Descripcion" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "Nombre" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "Cargando..." + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "No se encontraron productos" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "Sin resultados" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "Borrando" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "Cambiando" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "Administrar token" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "Actualizar" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "Eliminar" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "Cancelar" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "Administrar stock" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "Inifinito" + +#: src/components/form/InputStock.tsx:105 +#, fuzzy, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "no puede ser mayor al stock actual %1$s" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "stock actual cambiará desde %1$s a %2$s" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "stock actual seguirá en %1$s" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "Ingresando" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "Perdido" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "Actual" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "sin stock" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "Próximo reabastecimiento" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "Dirección de entrega" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "este producto no tiene impuestos" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "Monto" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "Moneda y valor separado por dos puntos" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "Agregar" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "Instancia" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "Configuración" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, fuzzy, c-format +msgid "Orders" +msgstr "Ordenes" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "Productos" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "Transferencias" + +#: src/components/menu/SideBar.tsx:87 +#, fuzzy, c-format +msgid "Connection" +msgstr "Conexión" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "Instancias" + +#: src/components/menu/SideBar.tsx:116 +#, fuzzy, c-format +msgid "New" +msgstr "Nuevo" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "Lista" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "Salir" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "Limpiar" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "deberÃan ser iguales" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "no puede ser igual al anterior" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" +"Está actualizando el token de autorización para la instancia %1$s con id %2$s" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "Viejo token" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "Nuevo token" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" +"Limpiar el token de autorización significa acceso publico a la instancia" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "ID" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "Imagen" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "Unidad" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "Precio" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "Stock" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "Impuesto" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "Servidor no encontrado" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "No se pudo aceder al servidor" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "Recivimos el mensaje %1$s desde %2$s" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "Error inesperado" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "Token de autorización" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "Dirección de cuenta" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "Impuesto máximo de deposito por omisión" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "Impuesto máximo de transferencia por omisión" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "Amortización de impuesto de transferencia por omisión" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "Jurisdicción" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "Retrazo de pago por omisión" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "Retrazo de transferencia por omisión" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "no se pudo crear la instancia" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, fuzzy, c-format +msgid "Delete" +msgstr "Borrando" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "No hay instancias todavÃan, agregue mas presionando el signo +" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "Productos de inventario" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "Precio total" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "Impuesto total" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "Precio de la orden" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, fuzzy, c-format +msgid "Net" +msgstr "Neto" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "Resumen" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "Opciones de pago" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "Plazo de reembolso automático" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "Plazo de reembolso" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "Plazo de pago" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "Fecha de entrega" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, fuzzy, c-format +msgid "Location" +msgstr "Ubicación" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "Impuesto máximo" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "Impuesto de transferencia máximo" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "Amortización de impuesto de transferencia" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "URL de completitud" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "Información extra" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "seleccione un producto primero" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, fuzzy, c-format +msgid "should be greater than 0" +msgstr "La imagen debe ser mas chica que 1 MB" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" +"no puede ser mayor al stock actual y la cantidad previamente agregada. " +"máximo: %1$s" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "no puede ser mayor al stock actual %1$s" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "Cantidad" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "Orden" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "reclamado" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "copiar url" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "pagar en" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "creado" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "CronologÃa" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "Detalles de pago" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, fuzzy, c-format +msgid "Order status" +msgstr "Estado de orden" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, fuzzy, c-format +msgid "Product list" +msgstr "Lista de producto" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "pagados" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "transferido" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "reembolzado" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "reembolzar" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "Monto reembolzado" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "Total depositado" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "impago" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "URL de estado de orden" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "URI de pago" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" +"Estado de orden desconocido. Esto es un error, por favor contacte a su " +"administrador" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "reembolzo creado satisfactoriamente" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, fuzzy, c-format +msgid "could not create the refund" +msgstr "No se pudo aceder al servidor" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "cargar nuevas ordenes" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "Fecha" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "Reembolzar" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "cargar viejas ordenes" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "No se enconraron ordenes" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "fecha" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "monto" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "razón" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "Máximo reembolzable:" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "Razón" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "duplicado" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "pedido por el consumidor" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "otro" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "ir a id de orden" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "Pagado" + +#: src/paths/instance/orders/list/index.tsx:108 +#, fuzzy, c-format +msgid "Refunded" +msgstr "Reembolzado" + +#: src/paths/instance/orders/list/index.tsx:109 +#, fuzzy, c-format +msgid "Not wired" +msgstr "No transferido" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "Todo" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "no se pudo crear el producto" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "Venta" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "Ganancia" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "Vendido" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "producto actualizado correctamente" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "no se pudo actualizar el producto" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "producto fue eliminado correctamente" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "no se pudo eliminar el producto" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "Propinas" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "No hay propinas todavÃa, agregar mas presionando el signo +" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "no puede ser vacÃo" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "verificar el id, no parece válido" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "deberÃa tener 52 caracteres, actualmente %1$s" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "La URL no tiene el formato correcto" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, fuzzy, c-format +msgid "Transfer ID" +msgstr "Transferencias" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, fuzzy, c-format +msgid "Account Address" +msgstr "Dirección de cuenta" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "URL del Exchange" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, fuzzy, c-format +msgid "could not inform transfer" +msgstr "no se pudo crear la instancia" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, fuzzy, c-format +msgid "load newer transfers" +msgstr "cargar nuevas ordenes" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "Crédito" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, fuzzy, c-format +msgid "Confirmed" +msgstr "Confirmar" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "Verificado" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, fuzzy, c-format +msgid "Executed at" +msgstr "creado" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "si" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "no" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "desconocido" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, fuzzy, c-format +msgid "load older transfers" +msgstr "cargar viejas transferencias" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "No hay transferencias todavÃa, agregar mas presionando el signo +" diff --git a/packages/merchant-backend-ui/src/i18n/fr.po b/packages/merchant-backend-ui/src/i18n/fr.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/fr.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backend-ui/src/i18n/index.tsx b/packages/merchant-backend-ui/src/i18n/index.tsx new file mode 100644 index 000000000..63c8e1934 --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/index.tsx @@ -0,0 +1,203 @@ +/* + 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/> + */ + +/** + * Translation helpers for React components and template literals. + */ + +/** + * Imports + */ +import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact"; + +import { useTranslationContext } from "../context/translation"; + +export function useTranslator() { + const ctx = useTranslationContext(); + const jed = ctx.handler + return function str(stringSeq: TemplateStringsArray, ...values: any[]): string { + const s = toI18nString(stringSeq); + if (!s) return s + const tr = jed + .translate(s) + .ifPlural(1, s) + .fetch(...values); + return tr; + } +} + + +/** + * Convert template strings to a msgid + */ + function toI18nString(stringSeq: ReadonlyArray<string>): string { + let s = ""; + for (let i = 0; i < stringSeq.length; i++) { + s += stringSeq[i]; + if (i < stringSeq.length - 1) { + s += `%${i + 1}$s`; + } + } + return s; +} + + +interface TranslateSwitchProps { + target: number; + children: ComponentChildren; +} + +function stringifyChildren(children: ComponentChildren): string { + let n = 1; + const ss = (children instanceof Array ? children : [children]).map((c) => { + if (typeof c === "string") { + return c; + } + return `%${n++}$s`; + }); + const s = ss.join("").replace(/ +/g, " ").trim(); + return s; +} + +interface TranslateProps { + children: ComponentChildren; + /** + * Component that the translated element should be wrapped in. + * Defaults to "div". + */ + wrap?: any; + + /** + * Props to give to the wrapped component. + */ + wrapProps?: any; +} + +function getTranslatedChildren( + translation: string, + children: ComponentChildren, +): ComponentChild[] { + const tr = translation.split(/%(\d+)\$s/); + const childArray = children instanceof Array ? children : [children]; + // Merge consecutive string children. + const placeholderChildren = Array<ComponentChild>(); + for (let i = 0; i < childArray.length; i++) { + const x = childArray[i]; + if (x === undefined) { + continue; + } else if (typeof x === "string") { + continue; + } else { + placeholderChildren.push(x); + } + } + const result = Array<ComponentChild>(); + for (let i = 0; i < tr.length; i++) { + if (i % 2 == 0) { + // Text + result.push(tr[i]); + } else { + const childIdx = Number.parseInt(tr[i],10) - 1; + result.push(placeholderChildren[childIdx]); + } + } + return result; +} + +/** + * Translate text node children of this component. + * If a child component might produce a text node, it must be wrapped + * in a another non-text element. + * + * Example: + * ``` + * <Translate> + * Hello. Your score is <span><PlayerScore player={player} /></span> + * </Translate> + * ``` + */ +export function Translate({ children }: TranslateProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext() + const translation: string = ctx.handler.ngettext(s, s, 1); + const result = getTranslatedChildren(translation, children) + return <Fragment>{result}</Fragment>; +} + +/** + * Switch translation based on singular or plural based on the target prop. + * Should only contain TranslateSingular and TransplatePlural as children. + * + * Example: + * ``` + * <TranslateSwitch target={n}> + * <TranslateSingular>I have {n} apple.</TranslateSingular> + * <TranslatePlural>I have {n} apples.</TranslatePlural> + * </TranslateSwitch> + * ``` + */ +export function TranslateSwitch({ children, target }: TranslateSwitchProps) { + let singular: VNode<TranslationPluralProps> | undefined; + let plural: VNode<TranslationPluralProps> | undefined; + // const children = this.props.children; + if (children) { + (children instanceof Array ? children : [children]).forEach((child: any) => { + if (child.type === TranslatePlural) { + plural = child; + } + if (child.type === TranslateSingular) { + singular = child; + } + }); + } + if (!singular || !plural) { + console.error("translation not found"); + return h("span", {}, ["translation not found"]); + } + singular.props.target = target; + plural.props.target = target; + // We're looking up the translation based on the + // singular, even if we must use the plural form. + return singular; +} + +interface TranslationPluralProps { + children: ComponentChildren; + target: number; +} + +/** + * See [[TranslateSwitch]]. + */ +export function TranslatePlural({ children, target }: TranslationPluralProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext() + const translation = ctx.handler.ngettext(s, s, 1); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; +} + +/** + * See [[TranslateSwitch]]. + */ +export function TranslateSingular({ children, target }: TranslationPluralProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext() + const translation = ctx.handler.ngettext(s, s, target); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; + +} diff --git a/packages/merchant-backend-ui/src/i18n/it.po b/packages/merchant-backend-ui/src/i18n/it.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/it.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backend-ui/src/i18n/poheader b/packages/merchant-backend-ui/src/i18n/poheader new file mode 100644 index 000000000..ee3fcd7be --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/poheader @@ -0,0 +1,27 @@ +# 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/> + +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: taler@gnu.org\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" diff --git a/packages/merchant-backend-ui/src/i18n/strings-prelude b/packages/merchant-backend-ui/src/i18n/strings-prelude new file mode 100644 index 000000000..cca13afad --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/strings-prelude @@ -0,0 +1,19 @@ +/* + 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/> + */ + +/*eslint quote-props: ["error", "consistent"]*/ +export const strings: {[s: string]: any} = {}; + diff --git a/packages/merchant-backend-ui/src/i18n/strings.ts b/packages/merchant-backend-ui/src/i18n/strings.ts new file mode 100644 index 000000000..63e96949a --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/strings.ts @@ -0,0 +1,3445 @@ +/* + 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/> + */ + +/*eslint quote-props: ["error", "consistent"]*/ +export const strings: {[s: string]: any} = {}; + +strings['de'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['en'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['es'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "Acceso denegado" + ], + "Check your token is valid": [ + "Verifica que el token sea valido" + ], + "Couldn't access the server.": [ + "No se pudo acceder al servidor" + ], + "Could not infer instance id from url %1$s": [ + "No se pudo inferir el id de la instancia con la url %1$s" + ], + "HTTP status #%1$s: Server reported a problem": [ + "HTTP status #%1$s: Servidor reporto un problema" + ], + "Got message: \"%1$s\" from: %2$s": [ + "Recivimos el mensaje %1$s desde %2$s" + ], + "No default instance": [ + "Sin instancia default" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "para usar el merchant backoffice, deberÃa crear la instancia default" + ], + "Server reported a problem: HTTP status #%1$s": [ + "Servidir reporto un problema: HTTP status #%1$s" + ], + "Got message: %1$s from: %2$s": [ + "Recivimos el mensaje %1$s desde %2$s" + ], + "Login required": [ + "Login necesario" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "Por favor ingrese su token de autorización. El token debe tener \"secret-token\" y comenzar con Bearer o ApiKey" + ], + "Confirm": [ + "Confirmar" + ], + "The value %1$s is invalid for a payment url": [ + "El valor %1$s es invalido para una URL de pago" + ], + "pick a date": [ + "elegir una fecha" + ], + "clear": [ + "Limpiar" + ], + "never": [ + "nunca" + ], + "Image should be smaller than 1 MB": [ + "La imagen debe ser mas chica que 1 MB" + ], + "Country": [ + "PaÃs" + ], + "Address": [ + "Dirección" + ], + "Building number": [ + "Número de edificio" + ], + "Building name": [ + "Nombre de edificio" + ], + "Street": [ + "Calle" + ], + "Post code": [ + "Código postal" + ], + "Town location": [ + "Ubicación de ciudad" + ], + "Town": [ + "Ciudad" + ], + "District": [ + "Distrito" + ], + "Country subdivision": [ + "Provincia" + ], + "Product id": [ + "Id de producto" + ], + "Description": [ + "Descripcion" + ], + "Name": [ + "Nombre" + ], + "loading...": [ + "Cargando..." + ], + "no products found": [ + "No se encontraron productos" + ], + "no results": [ + "Sin resultados" + ], + "Deleting": [ + "Borrando" + ], + "Changing": [ + "Cambiando" + ], + "Manage token": [ + "Administrar token" + ], + "Update": [ + "Actualizar" + ], + "Remove": [ + "Eliminar" + ], + "Cancel": [ + "Cancelar" + ], + "Manage stock": [ + "Administrar stock" + ], + "Infinite": [ + "Inifinito" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "no puede ser mayor al stock actual %1$s" + ], + "current stock will change from %1$s to %2$s": [ + "stock actual cambiará desde %1$s a %2$s" + ], + "current stock will stay at %1$s": [ + "stock actual seguirá en %1$s" + ], + "Incoming": [ + "Ingresando" + ], + "Lost": [ + "Perdido" + ], + "Current": [ + "Actual" + ], + "without stock": [ + "sin stock" + ], + "Next restock": [ + "Próximo reabastecimiento" + ], + "Delivery address": [ + "Dirección de entrega" + ], + "this product has no taxes": [ + "este producto no tiene impuestos" + ], + "Amount": [ + "Monto" + ], + "currency and value separated with colon": [ + "Moneda y valor separado por dos puntos" + ], + "Add": [ + "Agregar" + ], + "Instance": [ + "Instancia" + ], + "Settings": [ + "Configuración" + ], + "Orders": [ + "Ordenes" + ], + "Products": [ + "Productos" + ], + "Transfers": [ + "Transferencias" + ], + "Connection": [ + "Conexión" + ], + "Instances": [ + "Instancias" + ], + "New": [ + "Nuevo" + ], + "List": [ + "Lista" + ], + "Log out": [ + "Salir" + ], + "Clear": [ + "Limpiar" + ], + "should be the same": [ + "deberÃan ser iguales" + ], + "cannot be the same as before": [ + "no puede ser igual al anterior" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "Está actualizando el token de autorización para la instancia %1$s con id %2$s" + ], + "Old token": [ + "Viejo token" + ], + "New token": [ + "Nuevo token" + ], + "Clearing the auth token will mean public access to the instance": [ + "Limpiar el token de autorización significa acceso publico a la instancia" + ], + "ID": [ + "ID" + ], + "Image": [ + "Imagen" + ], + "Unit": [ + "Unidad" + ], + "Price": [ + "Precio" + ], + "Stock": [ + "Stock" + ], + "Taxes": [ + "Impuesto" + ], + "Server not found": [ + "Servidor no encontrado" + ], + "Couldn't access the server": [ + "No se pudo aceder al servidor" + ], + "Got message %1$s from %2$s": [ + "Recivimos el mensaje %1$s desde %2$s" + ], + "Unexpected Error": [ + "Error inesperado" + ], + "Auth token": [ + "Token de autorización" + ], + "Account address": [ + "Dirección de cuenta" + ], + "Default max deposit fee": [ + "Impuesto máximo de deposito por omisión" + ], + "Default max wire fee": [ + "Impuesto máximo de transferencia por omisión" + ], + "Default wire fee amortization": [ + "Amortización de impuesto de transferencia por omisión" + ], + "Jurisdiction": [ + "Jurisdicción" + ], + "Default pay delay": [ + "Retrazo de pago por omisión" + ], + "Default wire transfer delay": [ + "Retrazo de transferencia por omisión" + ], + "could not create instance": [ + "no se pudo crear la instancia" + ], + "Delete": [ + "Borrando" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "No hay instancias todavÃan, agregue mas presionando el signo +" + ], + "Inventory products": [ + "Productos de inventario" + ], + "Total price": [ + "Precio total" + ], + "Total tax": [ + "Impuesto total" + ], + "Order price": [ + "Precio de la orden" + ], + "Net": [ + "Neto" + ], + "Summary": [ + "Resumen" + ], + "Payments options": [ + "Opciones de pago" + ], + "Auto refund deadline": [ + "Plazo de reembolso automático" + ], + "Refund deadline": [ + "Plazo de reembolso" + ], + "Pay deadline": [ + "Plazo de pago" + ], + "Delivery date": [ + "Fecha de entrega" + ], + "Location": [ + "Ubicación" + ], + "Max fee": [ + "Impuesto máximo" + ], + "Max wire fee": [ + "Impuesto de transferencia máximo" + ], + "Wire fee amortization": [ + "Amortización de impuesto de transferencia" + ], + "Fullfilment url": [ + "URL de completitud" + ], + "Extra information": [ + "Información extra" + ], + "select a product first": [ + "seleccione un producto primero" + ], + "should be greater than 0": [ + "La imagen debe ser mas chica que 1 MB" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "no puede ser mayor al stock actual y la cantidad previamente agregada. máximo: %1$s" + ], + "cannot be greater than current stock %1$s": [ + "no puede ser mayor al stock actual %1$s" + ], + "Quantity": [ + "Cantidad" + ], + "Order": [ + "Orden" + ], + "claimed": [ + "reclamado" + ], + "copy url": [ + "copiar url" + ], + "pay at": [ + "pagar en" + ], + "created at": [ + "creado" + ], + "Timeline": [ + "CronologÃa" + ], + "Payment details": [ + "Detalles de pago" + ], + "Order status": [ + "Estado de orden" + ], + "Product list": [ + "Lista de producto" + ], + "paid": [ + "pagados" + ], + "wired": [ + "transferido" + ], + "refunded": [ + "reembolzado" + ], + "refund": [ + "reembolzar" + ], + "Refunded amount": [ + "Monto reembolzado" + ], + "Deposit total": [ + "Total depositado" + ], + "unpaid": [ + "impago" + ], + "Order status URL": [ + "URL de estado de orden" + ], + "Pay URI": [ + "URI de pago" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "Estado de orden desconocido. Esto es un error, por favor contacte a su administrador" + ], + "refund created successfully": [ + "reembolzo creado satisfactoriamente" + ], + "could not create the refund": [ + "No se pudo aceder al servidor" + ], + "load newer orders": [ + "cargar nuevas ordenes" + ], + "Date": [ + "Fecha" + ], + "Refund": [ + "Reembolzar" + ], + "load older orders": [ + "cargar viejas ordenes" + ], + "No orders has been found": [ + "No se enconraron ordenes" + ], + "date": [ + "fecha" + ], + "amount": [ + "monto" + ], + "reason": [ + "razón" + ], + "Max refundable:": [ + "Máximo reembolzable:" + ], + "Reason": [ + "Razón" + ], + "duplicated": [ + "duplicado" + ], + "requested by the customer": [ + "pedido por el consumidor" + ], + "other": [ + "otro" + ], + "go to order id": [ + "ir a id de orden" + ], + "Paid": [ + "Pagado" + ], + "Refunded": [ + "Reembolzado" + ], + "Not wired": [ + "No transferido" + ], + "All": [ + "Todo" + ], + "could not create product": [ + "no se pudo crear el producto" + ], + "Sell": [ + "Venta" + ], + "Profit": [ + "Ganancia" + ], + "Sold": [ + "Vendido" + ], + "product updated successfully": [ + "producto actualizado correctamente" + ], + "could not update the product": [ + "no se pudo actualizar el producto" + ], + "product delete successfully": [ + "producto fue eliminado correctamente" + ], + "could not delete the product": [ + "no se pudo eliminar el producto" + ], + "Tips": [ + "Propinas" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "No hay propinas todavÃa, agregar mas presionando el signo +" + ], + "cannot be empty": [ + "no puede ser vacÃo" + ], + "check the id, doest look valid": [ + "verificar el id, no parece válido" + ], + "should have 52 characters, current %1$s": [ + "deberÃa tener 52 caracteres, actualmente %1$s" + ], + "URL doesn't have the right format": [ + "La URL no tiene el formato correcto" + ], + "Transfer ID": [ + "Transferencias" + ], + "Account Address": [ + "Dirección de cuenta" + ], + "Exchange URL": [ + "URL del Exchange" + ], + "could not inform transfer": [ + "no se pudo crear la instancia" + ], + "load newer transfers": [ + "cargar nuevas ordenes" + ], + "Credit": [ + "Crédito" + ], + "Confirmed": [ + "Confirmar" + ], + "Verified": [ + "Verificado" + ], + "Executed at": [ + "creado" + ], + "yes": [ + "si" + ], + "no": [ + "no" + ], + "unknown": [ + "desconocido" + ], + "load older transfers": [ + "cargar viejas transferencias" + ], + "There is no transfer yet, add more pressing the + sign": [ + "No hay transferencias todavÃa, agregar mas presionando el signo +" + ] + } + } +}; + +strings['fr'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['it'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['sv'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + diff --git a/packages/merchant-backend-ui/src/i18n/sv.po b/packages/merchant-backend-ui/src/i18n/sv.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/sv.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backend-ui/src/i18n/taler-merchant-backoffice.pot b/packages/merchant-backend-ui/src/i18n/taler-merchant-backoffice.pot new file mode 100644 index 000000000..21fd863b0 --- /dev/null +++ b/packages/merchant-backend-ui/src/i18n/taler-merchant-backoffice.pot @@ -0,0 +1,1054 @@ +# 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/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backend-ui/src/index.tsx b/packages/merchant-backend-ui/src/index.tsx new file mode 100644 index 000000000..275f63371 --- /dev/null +++ b/packages/merchant-backend-ui/src/index.tsx @@ -0,0 +1,61 @@ +/* + 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, Fragment } from 'preact'; +import { BackendContextProvider } from './context/backend'; +import { TranslationProvider } from './context/translation'; +// import { Page as RequestPayment } from './RequestPayment'; +import "./css/pure-min.css" +import { Route, Router } from 'preact-router'; +import { Footer } from './components/Footer'; +// import OfferTip from './pages/OfferTip'; +// import {OfferRefund} from './pages/OfferRefund'; +// import DepletedTip from './pages/DepletedTip'; +// import RequestPayment from './pages/RequestPayment'; +// import ShowOrderDetails from './pages/ShowOrderDetails'; + +export default function Application(): VNode { + return ( + // <FetchContextProvider> + <BackendContextProvider> + <TranslationProvider> + <ApplicationStatusRoutes /> + </TranslationProvider> + </BackendContextProvider> + // </FetchContextProvider> + ); +} + +function ApplicationStatusRoutes(): VNode { + return <Fragment> + <Router> + {/* <Route path="offer_tip" component={OfferTip} /> + <Route path="offer_refund" component={OfferRefund} /> + <Route path="depleted_tip" component={DepletedTip} /> + <Route path="request_payment" component={RequestPayment} /> + <Route path="show_order_details" component={ShowOrderDetails} /> */} + <Route default component={() => <div> + hello! + </div>} /> + </Router> + <Footer /> + </Fragment> +} diff --git a/packages/merchant-backend-ui/src/pages/DepletedTip.stories.tsx b/packages/merchant-backend-ui/src/pages/DepletedTip.stories.tsx new file mode 100644 index 000000000..c20f6dc18 --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/DepletedTip.stories.tsx @@ -0,0 +1,40 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { DepletedTip as TestedComponent } from './DepletedTip'; + + +export default { + title: 'DepletedTip', + component: TestedComponent, + argTypes: { + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Example = createExample(TestedComponent, { +}); diff --git a/packages/merchant-backend-ui/src/pages/DepletedTip.tsx b/packages/merchant-backend-ui/src/pages/DepletedTip.tsx new file mode 100644 index 000000000..756b08d6a --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/DepletedTip.tsx @@ -0,0 +1,60 @@ +/* + 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 { Fragment, h, render, VNode } from 'preact'; +import { render as renderToString } from 'preact-render-to-string'; +import { Footer } from '../components/Footer'; +import "../css/pure-min.css"; +import "../css/style.css"; +import { Page } from '../styled'; + +function Head(): VNode { + return <title>Status of your tip</title> +} + +export function DepletedTip(): VNode { + return <Page> + <section> + <h1>Tip already collected</h1> + <div> + You have already collected this tip. + </div> + </section> + <Footer /> + </Page> +} + +export function mount(): void { + try { + render(<DepletedTip />, document.body); + } catch (e) { + console.error("got error", e); + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } + } +} + +export function buildTimeRendering(): { head: string, body: string } { + return { + head: renderToString(<Head />), + body: renderToString(<DepletedTip />) + } +} diff --git a/packages/merchant-backend-ui/src/pages/OfferRefund.stories.tsx b/packages/merchant-backend-ui/src/pages/OfferRefund.stories.tsx new file mode 100644 index 000000000..92694f867 --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/OfferRefund.stories.tsx @@ -0,0 +1,45 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { createSVG } from '../components/QR'; +import { OfferRefund as TestedComponent } from './OfferRefund'; + + +export default { + title: 'OfferRefund', + component: TestedComponent, + argTypes: { + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +const REFUND_URI_EXAMPLE = 'taler://pay/backend.demo.taler.net/instances/blog/2021.249-022NW2KG88QGA/def537eb-00c2-4a8b-8a17-0be034d118d3?c=2Y4N4PMST7KYAPS83428GTPCD4' + +export const Example = createExample(TestedComponent, { + refundURI: REFUND_URI_EXAMPLE, + qr_code: createSVG(REFUND_URI_EXAMPLE) +}); diff --git a/packages/merchant-backend-ui/src/pages/OfferRefund.tsx b/packages/merchant-backend-ui/src/pages/OfferRefund.tsx new file mode 100644 index 000000000..14c9372c2 --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/OfferRefund.tsx @@ -0,0 +1,154 @@ +/* + 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 { Fragment, h, render, VNode } from 'preact'; +import { render as renderToString } from 'preact-render-to-string'; +import { useEffect } from 'preact/hooks'; +import { Footer } from '../components/Footer'; +import { QR } from '../components/QR'; +import "../css/pure-min.css"; +import "../css/style.css"; +import { Page, QRPlaceholder, WalletLink } from '../styled'; + +/** + * This page creates a refund offer QR code + * + * It will build into a mustache html template for server side rendering + * + * server side rendering params: + * - order_status_url + * - taler_refund_qrcode_svg + * - taler_refund_uri + * + * request params: + * - refund_uri + * - order_status_url + */ + +interface Props { + refundURI?: string; + order_status_url?: string; + qr_code?: string; +} + +function Head({ order_summary }: { order_summary?: string }): VNode { + return <Fragment> + <meta charSet="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <noscript> + <meta http-equiv="refresh" content="1" /> + </noscript> + <title>Refund available for {order_summary ? order_summary : `{{ order_summary }}`}</title> + </Fragment> +} + +export function OfferRefund({ refundURI, qr_code, order_status_url }: Props): VNode { + useEffect(() => { + let checkUrl: URL; + try { + checkUrl = new URL(order_status_url ? order_status_url : "{{& order_status_url }}"); + } catch (e) { + return; + } + checkUrl.searchParams.set("await_refund_obtained", "yes"); + const delayMs = 500; + function check() { + let retried = false; + function retryOnce() { + if (!retried) { + retried = true; + check(); + } + } + const req = new XMLHttpRequest(); + req.onreadystatechange = function () { + if (req.readyState === XMLHttpRequest.DONE) { + if (req.status === 200) { + try { + const resp = JSON.parse(req.responseText); + if (!resp.refund_pending) { + window.location.reload(); + } + } catch (e) { + console.error("could not parse response:", e); + } + } + setTimeout(retryOnce, delayMs); + } + }; + req.onerror = function () { + setTimeout(retryOnce, delayMs); + } + req.open("GET", checkUrl.href); + req.send(); + } + + setTimeout(check, delayMs); + }) + return <Page> + <section> + <h1>Collect Taler refund</h1> + <p> + Scan this QR code with your Taler mobile wallet: + </p> + <QRPlaceholder dangerouslySetInnerHTML={{ __html: qr_code ? qr_code : `{{{ taler_refund_qrcode_svg }}}` }} /> + <p> + <WalletLink href={refundURI ? refundURI : `{{ taler_refund_uri }}`}> + Or open your Taller wallet + </WalletLink> + </p> + <p> + <a href="https://wallet.taler.net/">Don't have a Taler wallet yet? Install it!</a> + </p> + </section> + <Footer /> + </Page> +} + +export function mount(): void { + try { + const fromLocation = new URL(window.location.href).searchParams + const os = fromLocation.get('order_summary') || undefined; + if (os) { + render(<Head order_summary={os} />, document.head); + } + + const uri = fromLocation.get('refund_uri') || undefined; + const osu = fromLocation.get('order_status_url') || undefined; + const qr_code = uri ? renderToString(<QR text={uri} />) : undefined; + + render(<OfferRefund + refundURI={uri} order_status_url={osu} + qr_code={qr_code} + />, document.body); + } catch (e) { + console.error("got error", e); + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } + } +} + +export function buildTimeRendering(): { head: string, body: string } { + return { + head: renderToString(<Head />), + body: renderToString(<OfferRefund />) + } +} diff --git a/packages/merchant-backend-ui/src/pages/OfferTip.stories.tsx b/packages/merchant-backend-ui/src/pages/OfferTip.stories.tsx new file mode 100644 index 000000000..dfbf71fff --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/OfferTip.stories.tsx @@ -0,0 +1,45 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { createSVG } from '../components/QR'; +import { OfferTip as TestedComponent } from './OfferTip'; + + +export default { + title: 'OfferTip', + component: TestedComponent, + argTypes: { + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +const TIP_URI_EXAMPLE = 'taler+http://tip/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0' + +export const Example = createExample(TestedComponent, { + tipURI: TIP_URI_EXAMPLE, + qr_code: createSVG(TIP_URI_EXAMPLE) +}); diff --git a/packages/merchant-backend-ui/src/pages/OfferTip.tsx b/packages/merchant-backend-ui/src/pages/OfferTip.tsx new file mode 100644 index 000000000..ace1059ca --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/OfferTip.tsx @@ -0,0 +1,141 @@ +/* + 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 { Fragment, h, render, VNode } from 'preact'; +import { render as renderToString } from 'preact-render-to-string'; +import { useEffect } from 'preact/hooks'; +import { Footer } from '../components/Footer'; +import { QR } from '../components/QR'; +import "../css/pure-min.css"; +import "../css/style.css"; +import { Page, QRPlaceholder, WalletLink } from '../styled'; +import { ShowOrderDetails } from './ShowOrderDetails'; + + +/** + * This page creates a tip offer QR code + * + * It will build into a mustache html template for server side rendering + * + * server side rendering params: + * - tip_status_url + * - taler_tip_qrcode_svg + * - taler_tip_uri + * + * request params: + * - tip_uri + * - tip_status_url + */ + +interface Props { + tipURI?: string, + tip_status_url?: string, + qr_code?: string, +} + +export function Head(): VNode { + return <Fragment> + <meta charSet="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <noscript> + <meta http-equiv="refresh" content="1" /> + </noscript> + <title>Tip available</title> + </Fragment> +} + +export function OfferTip({ tipURI, qr_code, tip_status_url }: Props): VNode { + useEffect(() => { + let checkUrl: URL; + try { + checkUrl = new URL(tip_status_url ? tip_status_url : "{{& tip_status_url }}"); + } catch (e) { + return; + } + + const delayMs = 500; + function check() { + let retried = false; + function retryOnce() { + if (!retried) { + retried = true; + check(); + } + } + const req = new XMLHttpRequest(); + req.onreadystatechange = function () { + if (req.readyState === XMLHttpRequest.DONE) { + if (req.status === 410) { + window.location.reload(); + } + setTimeout(retryOnce, delayMs); + } + }; + req.onerror = function () { + setTimeout(retryOnce, delayMs); + } + req.open("GET", checkUrl.href); + req.send(); + } + + setTimeout(check, delayMs); + }) + return <Page> + <section> + <h1 >Collect Taler tip</h1> + <p> + Scan this QR code with your Taler mobile wallet: + </p> + <QRPlaceholder dangerouslySetInnerHTML={{ __html: qr_code ? qr_code : `{{{ taler_tip_qrcode_svg }}}` }} /> + <p> + <WalletLink href={tipURI ? tipURI : `{{ taler_tip_uri }}`}> + Or open your Taller wallet + </WalletLink> + </p> + <p> + <a href="https://wallet.taler.net/">Don't have a Taler wallet yet? Install it!</a> + </p> + </section> + <Footer /> + </Page> +} + +export function mount(): void { + try { + const fromLocation = new URL(window.location.href).searchParams + + const uri = fromLocation.get('tip_uri') || undefined + const tsu = fromLocation.get('tip_status_url') || undefined + + render(<OfferTip tipURI={uri} tip_status_url={tsu} />, document.body); + } catch (e) { + console.error("got error", e); + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } + } +} + +export function buildTimeRendering(): { head: string, body: string } { + return { + head: renderToString(<Head />), + body: renderToString(<ShowOrderDetails />) + } +} diff --git a/packages/merchant-backend-ui/src/pages/RequestPayment.stories.tsx b/packages/merchant-backend-ui/src/pages/RequestPayment.stories.tsx new file mode 100644 index 000000000..5d6d79adf --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/RequestPayment.stories.tsx @@ -0,0 +1,45 @@ +/* + 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 { FunctionalComponent, h } from 'preact'; +import { createSVG } from '../components/QR'; +import { RequestPayment as TestedComponent } from './RequestPayment'; + + +export default { + title: 'RequestPayment', + component: TestedComponent, + argTypes: { + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +const PAYTO_URI_EXAMPLE = 'taler+http://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0' + +export const Example = createExample(TestedComponent, { + payURI: 'taler+http://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0', + qr_code: createSVG(PAYTO_URI_EXAMPLE) +}); diff --git a/packages/merchant-backend-ui/src/pages/RequestPayment.tsx b/packages/merchant-backend-ui/src/pages/RequestPayment.tsx new file mode 100644 index 000000000..050755dfb --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/RequestPayment.tsx @@ -0,0 +1,196 @@ +/* + 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 { Fragment, h, render, VNode } from "preact"; +import { render as renderToString } from "preact-render-to-string"; +import { useEffect } from "preact/hooks"; +import { Footer } from "../components/Footer"; +import "../css/pure-min.css"; +import "../css/style.css"; +import { QR } from "../components/QR"; +import { Page, QRPlaceholder, WalletLink } from "../styled"; + +/** + * This page creates a payment request QR code + * + * It will build into a mustache html template for server side rendering + * + * server side rendering params: + * - order_status_url + * - taler_pay_qrcode_svg + * - taler_pay_uri + * - order_summary + * + * request params: + * - pay_uri + * - order_summary + * - order_status_url + */ + +interface Props { + payURI?: string; + order_status_url?: string; + qr_code?: string; +} + +function Head({ order_summary }: { order_summary?: string }): VNode { + return ( + <Fragment> + <meta charSet="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <noscript> + <meta http-equiv="refresh" content="1" /> + </noscript> + <title> + Payment requested for{" "} + {order_summary ? order_summary : `{{ order_summary }}`} + </title> + </Fragment> + ); +} + +export function RequestPayment({ + payURI, + qr_code, + order_status_url, +}: Props): VNode { + useEffect(() => { + const longpollDelayMs = 60 * 1000; + let checkUrl: URL; + try { + checkUrl = new URL( + order_status_url ? order_status_url : "{{& order_status_url }}" + ); + } catch (e) { + return; + } + checkUrl.searchParams.set("timeout_s", longpollDelayMs.toString()); + function check() { + let retried = false; + function retryOnce() { + if (!retried) { + retried = true; + check(); + } + } + const req = new XMLHttpRequest(); + req.onreadystatechange = function () { + if (req.readyState === XMLHttpRequest.DONE) { + if (req.status === 200) { + try { + const resp = JSON.parse(req.responseText); + if (resp.fulfillment_url) { + window.location.replace(resp.fulfillment_url); + } + } catch (e) { + console.error("could not parse response:", e); + } + } + if (req.status === 202) { + try { + const resp = JSON.parse(req.responseText); + if (resp.fulfillment_url) { + window.location.replace(resp.fulfillment_url); + } + } catch (e) { + console.error("could not parse response:", e); + } + } + if (req.status === 402) { + try { + const resp = JSON.parse(req.responseText); + if (resp.already_paid_order_id && resp.fulfillment_url) { + window.location.replace(resp.fulfillment_url); + } + } catch (e) { + console.error("could not parse response:", e); + } + } + setTimeout(retryOnce, 500); + } + }; + req.onerror = function () { + setTimeout(retryOnce, 500); + }; + req.ontimeout = function () { + setTimeout(retryOnce, 500); + }; + req.timeout = longpollDelayMs; + req.open("GET", checkUrl.href); + req.send(); + } + setTimeout(check, 500); + }); + return ( + <Page> + <section> + <h1>Pay with Taler</h1> + <p>Scan this QR code with your mobile wallet:</p> + <QRPlaceholder + dangerouslySetInnerHTML={{ + __html: qr_code ? qr_code : `{{{ taler_pay_qrcode_svg }}}`, + }} + /> + <p> + <WalletLink href={payURI ? payURI : `{{ taler_pay_uri }}`}> + Or open your Taller wallet + </WalletLink> + </p> + <p> + <a href="https://wallet.taler.net/"> + Don't have a Taler wallet yet? Install it! + </a> + </p> + </section> + <Footer /> + </Page> + ); +} + +export function mount(): void { + try { + const fromLocation = new URL(window.location.href).searchParams; + const os = fromLocation.get("order_summary") || undefined; + if (os) { + render(<Head order_summary={os} />, document.head); + } + + const uri = fromLocation.get("pay_uri") || undefined; + const osu = fromLocation.get("order_status_url") || undefined; + const qr_code = uri ? renderToString(<QR text={uri} />) : undefined; + + render( + <RequestPayment payURI={uri} order_status_url={osu} qr_code={qr_code} />, + document.body + ); + } catch (e) { + console.error("got error", e); + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } + } +} + +export function buildTimeRendering(): { head: string; body: string } { + return { + head: renderToString(<Head />), + body: renderToString(<RequestPayment />), + }; +} diff --git a/packages/merchant-backend-ui/src/pages/ShowOrderDetails.examples.ts b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.examples.ts new file mode 100644 index 000000000..ba68397ee --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.examples.ts @@ -0,0 +1,219 @@ +/* + 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 { MerchantBackend } from '../declaration'; +import { Props } from './ShowOrderDetails'; + + +const defaultContractTerms: MerchantBackend.ContractTerms = { + order_id: 'XRS8876388373', + amount: 'USD:10', + summary: 'this is a short summary', + pay_deadline: { + t_s: new Date().getTime() + 6 * 24 * 60 * 60 * 1000 + }, + merchant: { + name: 'the merchant (inc)', + address: { + country_subdivision: 'Buenos Aires', + town: 'CABA', + country: 'Argentina' + }, + jurisdiction: { + country_subdivision: 'Cordoba', + town: 'Capital', + country: 'Argentina' + }, + }, + max_fee: 'USD:0.1', + max_wire_fee: 'USD:0.2', + wire_fee_amortization: 1, + products: [], + timestamp: { + t_s: new Date().getTime() + }, + auditors: [], + exchanges: [], + h_wire: '', + merchant_base_url: 'http://merchant.base.url/', + merchant_pub: 'QWEASDQWEASD', + nonce: 'NONCE', + refund_deadline: { + t_s: new Date().getTime() + 6 * 24 * 60 * 60 * 1000 + }, + wire_method: 'x-taler-bank', + wire_transfer_deadline: { + t_s: new Date().getTime() + 3 * 24 * 60 * 60 * 1000 + }, +}; + +const inSixDays = new Date().getTime() + 6 * 24 * 60 * 60 * 1000 +const in10Minutes = new Date().getTime() + 10 * 60 * 1000 +const in15Minutes = new Date().getTime() + 15 * 60 * 1000 +const in20Minutes = new Date().getTime() + 20 * 60 * 1000 + +export const exampleData: { [name: string]: Props } = { + Simplest: { + order_summary: 'here goes the order summary', + contract_terms: defaultContractTerms, + }, + WithRefundAmount: { + order_summary: 'here goes the order summary', + refund_amount: 'USD:10', + contract_terms: defaultContractTerms, + }, + WithDeliveryDate: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + delivery_date: { + t_s: inSixDays + }, + }, + }, + WithDeliveryLocation: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + delivery_location: { + address_lines: ['addr line 1', 'addr line 2', 'addr line 3', 'addr line 4', 'addr line 5', 'addr line 6', 'addr line 7'], + building_name: 'building-name', + building_number: 'building-number', + country: 'country', + country_subdivision: 'country sub', + district: 'district', + post_code: 'post-code', + street: 'street', + town: 'town', + town_location: 'town loc', + }, + }, + }, + WithDeliveryLocationAndDate: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + delivery_location: { + address_lines: ['addr1', 'addr2', 'addr3', 'addr4', 'addr5', 'addr6', 'addr7'], + building_name: 'building-name', + building_number: 'building-number', + country: 'country', + country_subdivision: 'country sub', + district: 'district', + post_code: 'post-code', + street: 'street', + town: 'town', + town_location: 'town loc', + }, + delivery_date: { + t_s: inSixDays + }, + }, + }, + WithThreeProducts: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + products: [{ + description: 'description of the first product', + price: '5:USD', + quantity: 1, + delivery_date: { t_s: in10Minutes }, + product_id: '12333', + }, { + description: 'another description', + price: '10:USD', + quantity: 5, + unit: 't-shirt', + }, { + description: 'one last description', + price: '10:USD', + quantity: 5 + }] + } as MerchantBackend.ContractTerms + }, + WithProductWithTaxes: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + products: [{ + description: 'description of the first product', + price: '5:USD', + quantity: 1, + unit: 'beer', + delivery_date: { t_s: in10Minutes }, + product_id: '456', + taxes: [{ + name: 'VAT', tax: 'USD:1' + }], + }, { + description: 'one last description', + price: '10:USD', + quantity: 5, + product_id: '123', + unit: 'beer', + taxes: [{ + name: 'VAT', tax: 'USD:1' + }], + }] + } as MerchantBackend.ContractTerms + }, + WithExchangeList: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + exchanges: [{ + master_pub: 'ABCDEFGHIJKLMNO', + url: 'http://exchange0.taler.net' + }, { + master_pub: 'AAAAAAAAAAAAAAA', + url: 'http://exchange1.taler.net' + }, { + master_pub: 'BBBBBBBBBBBBBBB', + url: 'http://exchange2.taler.net' + }] + }, + }, + WithAuditorList: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + auditors: [{ + auditor_pub: 'ABCDEFGHIJKLMNO', + name: 'the USD auditor', + url: 'http://auditor-usd.taler.net' + }, { + auditor_pub: 'OPQRSTUVWXYZABCD', + name: 'the EUR auditor', + url: 'http://auditor-eur.taler.net' + }] + }, + }, + WithAutoRefund: { + order_summary: 'here goes the order summary', + contract_terms: { + ...defaultContractTerms, + auto_refund: { + d_us: 1000 * 60 * 60 * 26 + 1000 * 60 * 30 + } + }, + }, +} diff --git a/packages/merchant-backend-ui/src/pages/ShowOrderDetails.stories.tsx b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.stories.tsx new file mode 100644 index 000000000..6a902cc9e --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.stories.tsx @@ -0,0 +1,49 @@ +/* + 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 { FunctionalComponent, h } from 'preact'; +import { ShowOrderDetails as TestedComponent } from './ShowOrderDetails'; +import { exampleData } from './ShowOrderDetails.examples'; + +export default { + title: 'ShowOrderDetails', + component: TestedComponent, + argTypes: { + }, + excludeStories: /.*Data$/, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Simplest = createExample(TestedComponent, exampleData.Simplest); +export const WithRefundAmount = createExample(TestedComponent, exampleData.WithRefundAmount); +export const WithDeliveryDate = createExample(TestedComponent, exampleData.WithDeliveryDate); +export const WithDeliveryLocation = createExample(TestedComponent, exampleData.WithDeliveryLocation); +export const WithDeliveryLocationAndDate = createExample(TestedComponent, exampleData.WithDeliveryLocationAndDate); +export const WithThreeProducts = createExample(TestedComponent, exampleData.WithThreeProducts); +export const WithAuditorList = createExample(TestedComponent, exampleData.WithAuditorList); +export const WithExchangeList = createExample(TestedComponent, exampleData.WithExchangeList); +export const WithAutoRefund = createExample(TestedComponent, exampleData.WithAutoRefund); +export const WithProductWithTaxes = createExample(TestedComponent, exampleData.WithProductWithTaxes); diff --git a/packages/merchant-backend-ui/src/pages/ShowOrderDetails.tsx b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.tsx new file mode 100644 index 000000000..aa62c2932 --- /dev/null +++ b/packages/merchant-backend-ui/src/pages/ShowOrderDetails.tsx @@ -0,0 +1,551 @@ +/* + 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 { format, formatDuration } from "date-fns"; +import { intervalToDuration } from "date-fns/esm"; +import { Fragment, h, render, VNode } from "preact"; +import { render as renderToString } from "preact-render-to-string"; +import { Footer } from "../components/Footer"; +import "../css/pure-min.css"; +import "../css/style.css"; +import { MerchantBackend } from "../declaration"; +import { Page, InfoBox, TableExpanded, TableSimple } from "../styled"; + +/** + * This page creates a payment request QR code + * + * It will build into a mustache html template for server side rendering + * + * server side rendering params: + * - order_summary + * - contract_terms + * - refund_amount + * + * request params: + * - refund_amount + * - contract_terms + * - order_summary + */ + +export interface Props { + btr?: boolean; // build time rendering flag + order_summary?: string; + refund_amount?: string; + contract_terms?: MerchantBackend.ContractTerms; +} + +function Head({ order_summary }: { order_summary?: string }): VNode { + return ( + <Fragment> + <meta charSet="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <noscript> + <meta http-equiv="refresh" content="1" /> + </noscript> + <title> + Status of your order for{" "} + {order_summary ? order_summary : `{{ order_summary }}`} + </title> + <script>{` + var contractTermsStr = '{{{contract_terms_json}}}'; + `}</script> + </Fragment> + ); +} + +function Location({ + templateName, + location, + btr, +}: { + templateName: string; + location: MerchantBackend.Location | undefined; + btr?: boolean; +}) { + //FIXME: mustache strings show be constructed in a way that ends in the final output of the html but is not present in the + // javascript code, otherwise when mustache render engine run over the html it will also replace string in the javascript code + // that is made to run when the browser has javascript enable leading into undefined behavior. + // that's why in the next fields we are using concatenations to build the mustache placeholder. + return ( + <Fragment> + {btr && `{{` + `#${templateName}.building_name}}`} + <dd> + {location?.building_name || + (btr && `{{ ${templateName}.building_name }}`)}{" "} + {location?.building_number || + (btr && `{{ ${templateName}.building_number }}`)} + </dd> + {btr && `{{` + `/${templateName}.building_name}}`} + + {btr && `{{` + `#${templateName}.country}}`} + <dd> + {location?.country || (btr && `{{ ${templateName}.country }}`)}{" "} + {location?.country_subdivision || + (btr && `{{ ${templateName}.country_subdivision }}`)} + </dd> + {btr && `{{` + `/${templateName}.country}}`} + + {btr && `{{` + `#${templateName}.district}}`} + <dd>{location?.district || (btr && `{{ ${templateName}.district }}`)}</dd> + {btr && `{{` + `/${templateName}.district}}`} + + {btr && `{{` + `#${templateName}.post_code}}`} + <dd> + {location?.post_code || (btr && `{{ ${templateName}.post_code }}`)} + </dd> + {btr && `{{` + `/${templateName}.post_code}}`} + + {btr && `{{` + `#${templateName}.street}}`} + <dd>{location?.street || (btr && `{{ ${templateName}.street }}`)}</dd> + {btr && `{{` + `/${templateName}.street}}`} + + {btr && `{{` + `#${templateName}.town}}`} + <dd>{location?.town || (btr && `{{ ${templateName}.town }}`)}</dd> + {btr && `{{` + `/${templateName}.town}}`} + + {btr && `{{` + `#${templateName}.town_location}}`} + <dd> + {location?.town_location || + (btr && `{{ ${templateName}.town_location }}`)} + </dd> + {btr && `{{` + `/${templateName}.town_location}}`} + </Fragment> + ); +} + +export function ShowOrderDetails({ + order_summary, + refund_amount, + contract_terms, + btr, +}: Props): VNode { + const productList = btr + ? [{} as MerchantBackend.Product] + : contract_terms?.products || []; + const auditorsList = btr + ? [{} as MerchantBackend.Auditor] + : contract_terms?.auditors || []; + const exchangesList = btr + ? [{} as MerchantBackend.Exchange] + : contract_terms?.exchanges || []; + const hasDeliveryInfo = + btr || + !!contract_terms?.delivery_date || + !!contract_terms?.delivery_location; + + return ( + <Page> + <header> + <h1> + Details of order{" "} + {contract_terms?.order_id || `{{ contract_terms.order_id }}`} + </h1> + </header> + + <section> + {btr && `{{#refund_amount}}`} + {(btr || refund_amount) && ( + <section> + <InfoBox> + <b>Refunded:</b> The merchant refunded you{" "} + <b>{refund_amount || `{{ refund_amount }}`}</b>. + </InfoBox> + </section> + )} + {btr && `{{/refund_amount}}`} + + <section> + <TableExpanded> + <dt>Order summary:</dt> + <dd>{contract_terms?.summary || `{{ contract_terms.summary }}`}</dd> + <dt>Amount paid:</dt> + <dd>{contract_terms?.amount || `{{ contract_terms.amount }}`}</dd> + <dt>Order date:</dt> + <dd> + {contract_terms?.timestamp + ? contract_terms?.timestamp.t_s != "never" + ? format( + contract_terms?.timestamp.t_s, + "dd MMM yyyy HH:mm:ss" + ) + : "never" + : `{{ contract_terms.timestamp_str }}`}{" "} + </dd> + <dt>Merchant name:</dt> + <dd> + {contract_terms?.merchant.name || + `{{ contract_terms.merchant.name }}`} + </dd> + </TableExpanded> + </section> + + {btr && `{{#contract_terms.hasProducts}}`} + {!productList.length ? null : ( + <section> + <h2>Products purchased</h2> + <TableSimple> + {btr && "{{" + "#contract_terms.products" + "}}"} + {productList.map((p, i) => { + const taxList = btr + ? [{} as MerchantBackend.Tax] + : p.taxes || []; + + return ( + <Fragment key={i}> + <p>{p.description || `{{description}}`}</p> + <dl> + <dt>Quantity:</dt> + <dd>{p.quantity || `{{quantity}}`}</dd> + + <dt>Price:</dt> + <dd>{p.price || `{{price}}`}</dd> + + {btr && `{{#hasTaxes}}`} + {!taxList.length ? null : ( + <Fragment> + {btr && "{{" + "#taxes" + "}}"} + {taxList.map((t, i) => { + return ( + <Fragment key={i}> + <dt>{t.name || `{{name}}`}</dt> + <dd>{t.tax || `{{tax}}`}</dd> + </Fragment> + ); + })} + {btr && "{{" + "/taxes" + "}}"} + </Fragment> + )} + {btr && `{{/hasTaxes}}`} + + {btr && `{{#delivery_date}}`} + {(btr || p.delivery_date) && ( + <Fragment> + <dt>Delivered on:</dt> + <dd> + {p.delivery_date + ? p.delivery_date.t_s != "never" + ? format( + p.delivery_date.t_s, + "dd MMM yyyy HH:mm:ss" + ) + : "never" + : `{{ delivery_date_str }}`}{" "} + </dd> + </Fragment> + )} + {btr && `{{/delivery_date}}`} + + {btr && `{{#unit}}`} + {(btr || p.unit) && ( + <Fragment> + <dt>Product unit:</dt> + <dd>{p.unit || `{{.}}`}</dd> + </Fragment> + )} + {btr && `{{/unit}}`} + + {btr && `{{#product_id}}`} + {(btr || p.product_id) && ( + <Fragment> + <dt>Product ID:</dt> + <dd>{p.product_id || `{{.}}`}</dd> + </Fragment> + )} + {btr && `{{/product_id}}`} + </dl> + </Fragment> + ); + })} + {btr && "{{" + "/contract_terms.products" + "}}"} + </TableSimple> + </section> + )} + {btr && `{{/contract_terms.hasProducts}}`} + + {btr && `{{#contract_terms.has_delivery_info}}`} + {!hasDeliveryInfo ? null : ( + <section> + <h2>Delivery information</h2> + <TableExpanded> + {btr && `{{#contract_terms.delivery_date}}`} + {(btr || contract_terms?.delivery_date) && ( + <Fragment> + <dt>Delivery date:</dt> + <dd> + {contract_terms?.delivery_date + ? contract_terms?.delivery_date.t_s != "never" + ? format( + contract_terms?.delivery_date.t_s, + "dd MMM yyyy HH:mm:ss" + ) + : "never" + : `{{ contract_terms.delivery_date_str }}`}{" "} + </dd> + </Fragment> + )} + {btr && `{{/contract_terms.delivery_date}}`} + + {btr && `{{#contract_terms.delivery_location}}`} + {(btr || contract_terms?.delivery_location) && ( + <Fragment> + <dt>Delivery address:</dt> + <Location + btr={btr} + location={contract_terms?.delivery_location} + templateName="contract_terms.delivery_location" + /> + </Fragment> + )} + {btr && `{{/contract_terms.delivery_location}}`} + </TableExpanded> + </section> + )} + {btr && `{{/contract_terms.has_delivery_info}}`} + + <section> + <h2>Full payment information</h2> + <TableExpanded> + <dt>Amount paid:</dt> + <dd>{contract_terms?.amount || `{{ contract_terms.amount }}`}</dd> + <dt>Wire transfer method:</dt> + <dd> + {contract_terms?.wire_method || + `{{ contract_terms.wire_method }}`} + </dd> + <dt>Payment deadline:</dt> + <dd> + {contract_terms?.pay_deadline + ? contract_terms?.pay_deadline.t_s != "never" + ? format( + contract_terms?.pay_deadline.t_s, + "dd MMM yyyy HH:mm:ss" + ) + : "never" + : `{{ contract_terms.pay_deadline_str }}`}{" "} + </dd> + <dt>Exchange transfer deadline:</dt> + <dd> + {contract_terms?.wire_transfer_deadline + ? contract_terms?.wire_transfer_deadline.t_s != "never" + ? format( + contract_terms?.wire_transfer_deadline.t_s, + "dd MMM yyyy HH:mm:ss" + ) + : "never" + : `{{ contract_terms.wire_transfer_deadline_str }}`}{" "} + </dd> + <dt>Maximum deposit fee:</dt> + <dd>{contract_terms?.max_fee || `{{ contract_terms.max_fee }}`}</dd> + <dt>Maximum wire fee:</dt> + <dd> + {contract_terms?.max_wire_fee || + `{{ contract_terms.max_wire_fee }}`} + </dd> + <dt>Wire fee amortization:</dt> + <dd> + {contract_terms?.wire_fee_amortization || + `{{ contract_terms.wire_fee_amortization }}`}{" "} + transactions + </dd> + </TableExpanded> + </section> + + <section> + <h2>Refund information</h2> + <TableExpanded> + <dt>Refund deadline:</dt> + <dd> + {contract_terms?.refund_deadline + ? contract_terms?.refund_deadline.t_s != "never" + ? format( + contract_terms?.refund_deadline.t_s, + "dd MMM yyyy HH:mm:ss" + ) + : "never" + : `{{ contract_terms.refund_deadline_str }}`}{" "} + </dd> + + {btr && `{{#contract_terms.auto_refund}}`} + {(btr || contract_terms?.auto_refund) && ( + <Fragment> + <dt>Attempt autorefund for:</dt> + <dd> + {contract_terms?.auto_refund + ? contract_terms?.auto_refund.d_us != "forever" + ? formatDuration( + intervalToDuration({ + start: 0, + end: contract_terms?.auto_refund.d_us, + }) + ) + : "forever" + : `{{ contract_terms.auto_refund_str }}`}{" "} + </dd> + </Fragment> + )} + {btr && `{{/contract_terms.auto_refund}}`} + </TableExpanded> + </section> + + <section> + <h2>Additional order details</h2> + <TableExpanded> + <dt>Public reorder URL:</dt> + <dd> -- not defined yet -- </dd> + {btr && `{{#contract_terms.fulfillment_url}}`} + {(btr || contract_terms?.fulfillment_url) && ( + <Fragment> + <dt>Fulfillment URL:</dt> + <dd> + {contract_terms?.fulfillment_url || + (btr && `{{ contract_terms.fulfillment_url }}`)} + </dd> + </Fragment> + )} + {btr && `{{/contract_terms.fulfillment_url}}`} + {/* <dt>Fulfillment message:</dt> + <dd> -- not defined yet -- </dd> */} + </TableExpanded> + </section> + + <section> + <h2>Full merchant information</h2> + <TableExpanded> + <dt>Merchant name:</dt> + <dd> + {contract_terms?.merchant.name || + `{{ contract_terms.merchant.name }}`} + </dd> + <dt>Merchant address:</dt> + <Location + btr={btr} + location={contract_terms?.merchant.address} + templateName="contract_terms.merchant.address" + /> + <dt>Merchant's jurisdiction:</dt> + <Location + btr={btr} + location={contract_terms?.merchant.jurisdiction} + templateName="contract_terms.merchant.jurisdiction" + /> + <dt>Merchant URI:</dt> + <dd> + {contract_terms?.merchant_base_url || + `{{ contract_terms.merchant_base_url }}`} + </dd> + <dt>Merchant's public key:</dt> + <dd> + {contract_terms?.merchant_pub || + `{{ contract_terms.merchant_pub }}`} + </dd> + {/* <dt>Merchant's hash:</dt> + <dd> -- not defined yet -- </dd> */} + </TableExpanded> + </section> + + {btr && `{{#contract_terms.hasAuditors}}`} + {!auditorsList.length ? null : ( + <section> + <h2>Auditors accepted by the merchant</h2> + <TableExpanded> + {btr && "{{" + "#contract_terms.auditors" + "}}"} + {auditorsList.map((p, i) => { + return ( + <Fragment key={i}> + <p>{p.name || `{{name}}`}</p> + <dt>Auditor's public key:</dt> + <dd>{p.auditor_pub || `{{auditor_pub}}`}</dd> + <dt>Auditor's URL:</dt> + <dd>{p.url || `{{url}}`}</dd> + </Fragment> + ); + })} + {btr && "{{" + "/contract_terms.auditors" + "}}"} + </TableExpanded> + </section> + )} + {btr && `{{/contract_terms.hasAuditors}}`} + + {btr && `{{#contract_terms.hasExchanges}}`} + {!exchangesList.length ? null : ( + <section> + <h2>Exchanges accepted by the merchant</h2> + <TableExpanded> + {btr && "{{" + "#contract_terms.exchanges" + "}}"} + {exchangesList.map((p, i) => { + return ( + <Fragment key={i}> + <dt>Exchange's URL:</dt> + <dd>{p.url || `{{url}}`}</dd> + <dt>Public key:</dt> + <dd>{p.master_pub || `{{master_pub}}`}</dd> + </Fragment> + ); + })} + {btr && "{{" + "/contract_terms.exchanges" + "}}"} + </TableExpanded> + </section> + )} + {btr && `{{/contract_terms.hasExchanges}}`} + </section> + + <Footer /> + </Page> + ); +} + +export function mount(): void { + try { + const fromLocation = new URL(window.location.href).searchParams; + const os = fromLocation.get("order_summary") || undefined; + if (os) { + render(<Head order_summary={os} />, document.head); + } + + const ra = fromLocation.get("refund_amount") || undefined; + const ct = fromLocation.get("contract_terms") || undefined; + + let contractTerms: MerchantBackend.ContractTerms | undefined; + try { + contractTerms = JSON.parse((window as any).contractTermsStr); + } catch {} + + render( + <ShowOrderDetails + contract_terms={contractTerms} + order_summary={os} + refund_amount={ra} + />, + document.body + ); + } catch (e) { + console.error("got error", e); + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } + } +} + +export function buildTimeRendering(): { head: string; body: string } { + return { + head: renderToString(<Head />), + body: renderToString(<ShowOrderDetails btr />), + }; +} diff --git a/packages/merchant-backend-ui/src/styled/index.tsx b/packages/merchant-backend-ui/src/styled/index.tsx new file mode 100644 index 000000000..55803b9cd --- /dev/null +++ b/packages/merchant-backend-ui/src/styled/index.tsx @@ -0,0 +1,178 @@ +/* + 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 { styled } from '@linaria/react' + +export const QRPlaceholder = styled.div` + margin: auto; + text-align: center; + width: 340px; +` + +export const FooterBar = styled.footer` + text-align: center; + background-color: #033; + color: white; + padding: 1em; + overflow: auto; + + & > p > a:link, + & > p > a:visited, + & > p > a:hover, + & > p > a:active { + color: white; + } +` + +export const Page = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + min-height: 100vh; + align-items: center; + + a:link, + a:visited, + a:hover, + a:active { + color: black; + } + + section { + text-align: center; + width: 600px; + /* margin: auto; */ + /* margin-top: 0px; */ + margin-bottom: auto; + /* overflow: auto; */ + } + section:not(:first-of-type) { + margin-top: 2em; + } + & > header { + display: flex; + flex-direction: row; + justify-content: space-between; + text-align: center; + } + & > footer { + display: flex; + flex-direction: row; + justify-content: space-around; + width: 100%; + margin-bottom: 0px; + } +` +export const Center = styled.div` + display: flex; + justify-content: center; +` + +export const WalletLink = styled.a<{ upperCased?: boolean }>` + display: inline-block; + zoom: 1; + line-height: normal; + white-space: nowrap; + vertical-align: middle; + text-align: center; + cursor: pointer; + user-select: none; + box-sizing: border-box; + text-transform: ${({ upperCased }) => upperCased ? 'uppercase' : 'none'}; + + font-family: inherit; + font-size: 100%; + padding: 0.5em 1em; + color: #444; /* rgba not supported (IE 8) */ + color: rgba(0, 0, 0, 0.8); /* rgba supported */ + border: 1px solid #999; /*IE 6/7/8*/ + border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/ + background-color: '#e6e6e6'; + text-decoration: none; + border-radius: 2px; + + :focus { + outline: 0; + } + + &:disabled { + border: none; + background-image: none; + /* csslint ignore:start */ + filter: alpha(opacity=40); + /* csslint ignore:end */ + opacity: 0.4; + cursor: not-allowed; + box-shadow: none; + pointer-events: none; + } + + :hover { + filter: alpha(opacity=90); + background-image: linear-gradient( + transparent, + rgba(0, 0, 0, 0.05) 40%, + rgba(0, 0, 0, 0.1) + ); + } + + background-color: #e6e6e6; + border-radius: 4px; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, + 0 0 6px rgba(0, 0, 0, 0.2) inset; + border-color: #000; +`; + +export const InfoBox = styled.div` + border-radius: 0.25em; + flex-direction: column; + /* margin: 0.5em; */ + padding: 1em; + /* width: 100%; */ + border:solid 1px #b8daff; + background-color:#cce5ff; + color:#004085; +` + +export const TableExpanded = styled.dl` + text-align: left; + dt { + font-weight: bold; + margin-top: 1em; + } + dd { + margin-inline-start: 0px; + } +` + +export const TableSimple = styled.dl` + text-align: left; + dt { + font-weight: bold; + display: inline-block; + width:30%; + } + dd { + margin-inline-start: 0px; + display: inline-block; + width:70%; + } +`
\ No newline at end of file diff --git a/packages/merchant-backend-ui/src/utils/amount.ts b/packages/merchant-backend-ui/src/utils/amount.ts new file mode 100644 index 000000000..85f230427 --- /dev/null +++ b/packages/merchant-backend-ui/src/utils/amount.ts @@ -0,0 +1,69 @@ +/* + 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/> + */ +import { amountFractionalBase, AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { MerchantBackend } from "../declaration"; + +/** + * sums two prices, + * @param one + * @param two + * @returns + */ +const sumPrices = (one: string, two: string) => { + const [currency, valueOne] = one.split(':') + const [, valueTwo] = two.split(':') + return `${currency}:${parseInt(valueOne, 10) + parseInt(valueTwo, 10)}` +} + +/** + * merge refund with the same description and a difference less than one minute + * @param prev list of refunds that will hold the merged refunds + * @param cur new refund to add to the list + * @returns list with the new refund, may be merged with the last + */ +export function mergeRefunds(prev: MerchantBackend.Orders.RefundDetails[], cur: MerchantBackend.Orders.RefundDetails) { + let tail; + + if (prev.length === 0 || //empty list + cur.timestamp.t_s === 'never' || //current doesnt have timestamp + (tail = prev[prev.length - 1]).timestamp.t_s === 'never' || // last doesnt have timestamp + cur.reason !== tail.reason || //different reason + Math.abs(cur.timestamp.t_s - tail.timestamp.t_s) > 1000 * 60) {//more than 1 minute difference + + prev.push(cur) + return prev + } + + prev[prev.length - 1] = { + ...tail, + amount: sumPrices(tail.amount, cur.amount) + } + + return prev +} + +export const rate = (one: string, two: string) => { + const a = Amounts.parseOrThrow(one) + const b = Amounts.parseOrThrow(two) + 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); +} diff --git a/packages/merchant-backend-ui/src/utils/constants.ts b/packages/merchant-backend-ui/src/utils/constants.ts new file mode 100644 index 000000000..37c46e4c2 --- /dev/null +++ b/packages/merchant-backend-ui/src/utils/constants.ts @@ -0,0 +1,47 @@ +/* + 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) +*/ + +//https://tools.ietf.org/html/rfc8905 +export const PAYTO_REGEX = /^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]+(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/ +export const PAYTO_WIRE_METHOD_LOOKUP = /payto:\/\/([a-zA-Z][a-zA-Z0-9-.]+)\/.*/ + +export const AMOUNT_REGEX = /^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/ + +export const INSTANCE_ID_LOOKUP = /^\/instances\/([^/]*)\/?$/ + +export const AMOUNT_ZERO_REGEX = /^[a-zA-Z][a-zA-Z]*:0$/ + +export const CROCKFORD_BASE32_REGEX = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]+[*~$=U]*$/ + +export const URL_REGEX = /^((https?:)(\/\/\/?)([\w]*(?::[\w]*)?@)?([\d\w\.-]+)(?::(\d+))?)\/$/ + +// how much rows we add every time user hit load more +export const PAGE_SIZE = 20 +// how bigger can be the result set +// after this threshold, load more with move the cursor +export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1; + +// how much we will wait for all request, in seconds +export const DEFAULT_REQUEST_TIMEOUT = 10; + +export const MAX_IMAGE_SIZE = 1024 * 1024; + +export const INSTANCE_ID_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_.@-]+$/ diff --git a/packages/merchant-backend-ui/src/utils/table.ts b/packages/merchant-backend-ui/src/utils/table.ts new file mode 100644 index 000000000..3d713a6f7 --- /dev/null +++ b/packages/merchant-backend-ui/src/utils/table.ts @@ -0,0 +1,37 @@ +/* + 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/> + */ + +import { WithId } from "../declaration"; + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +export interface Actions<T extends WithId> { + element: T; + type: 'DELETE' | 'UPDATE'; +} + +function notEmpty<TValue>(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; +} + +export function buildActions<T extends WithId>(intances: T[], selected: string[], action: 'DELETE'): Actions<T>[] { + return selected.map(id => intances.find(i => i.id === id)) + .filter(notEmpty) + .map(id => ({ element: id, type: action })) +} diff --git a/packages/merchant-backend-ui/src/utils/types.ts b/packages/merchant-backend-ui/src/utils/types.ts new file mode 100644 index 000000000..9e49d39e1 --- /dev/null +++ b/packages/merchant-backend-ui/src/utils/types.ts @@ -0,0 +1,31 @@ +/* + 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/> + */ + +import { VNode } from "preact" + +export interface KeyValue { + [key: string]: string; +} + +export interface Notification { + message: string; + description?: string | VNode; + type: MessageType; +} + +export type ValueOrFunction<T> = T | ((p: T) => T) +export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS' + diff --git a/packages/merchant-backend-ui/tests/__mocks__/browserMocks.ts b/packages/merchant-backend-ui/tests/__mocks__/browserMocks.ts new file mode 100644 index 000000000..ee6bba505 --- /dev/null +++ b/packages/merchant-backend-ui/tests/__mocks__/browserMocks.ts @@ -0,0 +1,42 @@ +/* + 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) + */ + +// Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage +/** + * An example how to mock localStorage is given below 👇 + */ + +/* +// Mocks localStorage +const localStorageMock = (function() { + let store = {}; + + return { + getItem: (key) => store[key] || null, + setItem: (key, value) => store[key] = value.toString(), + clear: () => store = {} + }; + +})(); + +Object.defineProperty(window, 'localStorage', { + value: localStorageMock +}); */ diff --git a/packages/merchant-backend-ui/tests/__mocks__/fileMocks.ts b/packages/merchant-backend-ui/tests/__mocks__/fileMocks.ts new file mode 100644 index 000000000..0c045e9d1 --- /dev/null +++ b/packages/merchant-backend-ui/tests/__mocks__/fileMocks.ts @@ -0,0 +1,24 @@ +/* + 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) + */ + +// This fixed an error related to the CSS and loading gif breaking my Jest test +// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets +export default 'test-file-stub'; diff --git a/packages/merchant-backend-ui/tests/__mocks__/fileTransformer.js b/packages/merchant-backend-ui/tests/__mocks__/fileTransformer.js new file mode 100644 index 000000000..51ebbfa62 --- /dev/null +++ b/packages/merchant-backend-ui/tests/__mocks__/fileTransformer.js @@ -0,0 +1,31 @@ +/* + 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) +*/ +// fileTransformer.js + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +module.exports = { + process(src, filename, config, options) { + return `module.exports = ${ JSON.stringify(path.basename(filename)) };`; + }, +}; + diff --git a/packages/merchant-backend-ui/tests/__mocks__/setupTests.ts b/packages/merchant-backend-ui/tests/__mocks__/setupTests.ts new file mode 100644 index 000000000..ab0f08b2f --- /dev/null +++ b/packages/merchant-backend-ui/tests/__mocks__/setupTests.ts @@ -0,0 +1,28 @@ +/* + 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 'regenerator-runtime/runtime' +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-preact-pure'; + +configure({ + adapter: new Adapter() +}); diff --git a/packages/merchant-backend-ui/tests/funcitons/regex.test.ts b/packages/merchant-backend-ui/tests/funcitons/regex.test.ts new file mode 100644 index 000000000..fc8a6a42f --- /dev/null +++ b/packages/merchant-backend-ui/tests/funcitons/regex.test.ts @@ -0,0 +1,87 @@ +/* + 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 { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/utils/constants"; + +describe('payto uri format', () => { + const valids = [ + 'payto://iban/DE75512108001245126199?amount=EUR:200.0&message=hello', + 'payto://ach/122000661/1234', + 'payto://upi/alice@example.com?receiver-name=Alice&amount=INR:200', + 'payto://void/?amount=EUR:10.5', + 'payto://ilp/g.acme.bob' + ] + + test('should be valid', () => { + valids.forEach(v => expect(v).toMatch(PAYTO_REGEX)) + }); + + const invalids = [ + // has two question marks + 'payto://iban/DE75?512108001245126199?amount=EUR:200.0&message=hello', + // has a space + 'payto://ach /122000661/1234', + // has a space + 'payto://upi/alice@ example.com?receiver-name=Alice&amount=INR:200', + // invalid field name (mount instead of amount) + 'payto://void/?mount=EUR:10.5', + // payto:// is incomplete + 'payto: //ilp/g.acme.bob' + ] + + test('should not be valid', () => { + invalids.forEach(v => expect(v).not.toMatch(PAYTO_REGEX)) + }); +}) + +describe('amount format', () => { + const valids = [ + 'ARS:10', + 'COL:10.2', + 'UY:1,000.2', + 'ARS:10.123,123', + 'ARS:1,000,000', + 'ARSCOL:10', + 'THISISTHEMOTHERCOIN:1,000,000.123,123', + ] + + test('should be valid', () => { + valids.forEach(v => expect(v).toMatch(AMOUNT_REGEX)) + }); + + const invalids = [ + //no currency name + ':10', + //use . instead of , + 'ARS:1.000.000', + //currency name with numbers + '1ARS:10', + //currency name with numbers + 'AR5:10', + //missing value + 'USD:', + ] + + test('should not be valid', () => { + invalids.forEach(v => expect(v).not.toMatch(AMOUNT_REGEX)) + }); + +})
\ No newline at end of file diff --git a/packages/merchant-backend-ui/tests/util.ts b/packages/merchant-backend-ui/tests/util.ts new file mode 100644 index 000000000..14b82b51c --- /dev/null +++ b/packages/merchant-backend-ui/tests/util.ts @@ -0,0 +1,62 @@ +/* + 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 * as axios from 'axios'; + +type Query<Req, Res> = (GetQuery | PostQuery | DeleteQuery | PatchQuery) & RequestResponse<Req, Res> + +interface RequestResponse<Req, Res> { + request?: Req, + params?: any, + response?: Res, +} +interface GetQuery { get: string } +interface PostQuery { post: string } +interface DeleteQuery { delete: string } +interface PatchQuery { patch: string } + +export function simulateBackendResponse<R, T>(query: Query<R, T>): void { + (axios.default as jest.MockedFunction<axios.AxiosStatic>).mockImplementationOnce(function (opt?: axios.AxiosRequestConfig): axios.AxiosPromise { + // console.log(opt, JSON.stringify(query,undefined,2)) + expect(opt).toBeDefined(); + if (!opt) + return Promise.reject(); + + // expect(query.request).toStrictEqual(opt.data); + // expect(query.params).toStrictEqual(opt.params); + if ('get' in query) { + expect(opt.method).toBe('get'); + expect(opt.url).toBe(query.get); + } + if ('post' in query) { + expect(opt.method).toBe('post'); + expect(opt.url).toBe(query.post); + } + if ('delete' in query) { + expect(opt.method).toBe('delete'); + expect(opt.url).toBe(query.delete); + } + if ('patch' in query) { + expect(opt.method).toBe('patch'); + expect(opt.url).toBe(query.patch); + } + return ({ data: query.response, config: {} } as any); + } as any) +} diff --git a/packages/merchant-backend-ui/tsconfig.back.json b/packages/merchant-backend-ui/tsconfig.back.json new file mode 100644 index 000000000..9ac5a3c25 --- /dev/null +++ b/packages/merchant-backend-ui/tsconfig.back.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "composite": true, + "lib": ["es6", "DOM"], + "jsx": "react", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment", + "moduleResolution": "Node", + "module": "ESNext", + "target": "ES6", + "noImplicitAny": true, + "noEmitOnError": true, + "strict": true, + "incremental": true, + "sourceMap": true, + "esModuleInterop": true, + "importHelpers": true, + "rootDir": "./src", + "typeRoots": ["./node_modules/@types"] + }, + "include": ["src/**/*"] + } +
\ No newline at end of file diff --git a/packages/merchant-backend-ui/tsconfig.json b/packages/merchant-backend-ui/tsconfig.json new file mode 100644 index 000000000..7a4d70a17 --- /dev/null +++ b/packages/merchant-backend-ui/tsconfig.json @@ -0,0 +1,61 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "ES6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ + "module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation: */ + "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */ + "jsxFragmentFactory": "Fragment", // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#custom-jsx-factories + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "removeComments": true, /* Do not emit comments to output. */ + "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "esModuleInterop": true, /* */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */ + }, + "include": ["src/**/*", "tests/**/*"] +} diff --git a/packages/merchant-backoffice-ui/.gitignore b/packages/merchant-backoffice-ui/.gitignore new file mode 100644 index 000000000..df149101c --- /dev/null +++ b/packages/merchant-backoffice-ui/.gitignore @@ -0,0 +1,6 @@ +/build +/size-plugin.json +/storybook-static +/docs +/single +/coverage diff --git a/packages/merchant-backoffice-ui/.storybook/.babelrc b/packages/merchant-backoffice-ui/.storybook/.babelrc new file mode 100644 index 000000000..610b6f339 --- /dev/null +++ b/packages/merchant-backoffice-ui/.storybook/.babelrc @@ -0,0 +1,25 @@ +/* + 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) + */ +{ + "presets": [ + "preact-cli/babel" + ] +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/.storybook/main.js b/packages/merchant-backoffice-ui/.storybook/main.js new file mode 100644 index 000000000..f8e4bbcc7 --- /dev/null +++ b/packages/merchant-backoffice-ui/.storybook/main.js @@ -0,0 +1,57 @@ +/* + 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) +*/ + + +module.exports = { + "stories": [ + "../src/**/*.stories.mdx", + "../src/**/*.stories.@(js|jsx|ts|tsx)" + ], + "addons": [ + "@storybook/preset-scss", + "@storybook/addon-a11y", + "@storybook/addon-essentials" //docs, control, actions, viewpot, toolbar, background + ], + // sb does not yet support new jsx transform by default + // https://github.com/storybookjs/storybook/issues/12881 + // https://github.com/storybookjs/storybook/issues/12952 + babel: async (options) => ({ + ...options, + presets: [ + ...options.presets, + [ + '@babel/preset-react', { + runtime: 'automatic', + }, + 'preset-react-jsx-transform' + ], + ], + }), + webpackFinal: (config) => { + // should be removed after storybook 6.3 + // https://github.com/storybookjs/storybook/issues/12853#issuecomment-821576113 + config.resolve.alias = { + react: "preact/compat", + "react-dom": "preact/compat", + }; + return config; + }, +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/.storybook/preview.js b/packages/merchant-backoffice-ui/.storybook/preview.js new file mode 100644 index 000000000..d13103ac9 --- /dev/null +++ b/packages/merchant-backoffice-ui/.storybook/preview.js @@ -0,0 +1,74 @@ +/* + 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/> + */ + +import "../src/scss/main.scss" +import { ConfigContextProvider } from '../src/context/config' +import { InstanceContextProvider } from '../src/context/instance' +import { TranslationProvider } from '../src/context/translation' +import { BackendContextProvider } from '../src/context/backend' +import { h } from 'preact'; + +const mockConfig = { + backendURL: 'http://demo.taler.net', + currency: 'TESTKUDOS' +} + +const mockInstance = { + id: 'instance-id', + token: 'instance-token', + admin: false, +} + +const mockBackend = { + url: 'http://merchant.url', + token: 'default-token', + triedToLog: false, +} + +export const parameters = { + controls: { expanded: true }, + // actions: { argTypesRegex: "^on.*" }, +} + +export const globalTypes = { + locale: { + name: 'Locale', + description: 'Internationalization locale', + defaultValue: 'en', + toolbar: { + icon: 'globe', + items: [ + { value: 'en', right: '🇺🇸', title: 'English' }, + { value: 'es', right: '🇪🇸', title: 'Spanish' }, + ], + }, + }, +}; + +export const decorators = [ + (Story, { globals }) => <TranslationProvider initial='en' forceLang={globals.locale}> + <Story /> + </TranslationProvider>, + (Story) => <ConfigContextProvider value={mockConfig}> + <Story /> + </ConfigContextProvider>, + (Story) => <InstanceContextProvider value={mockInstance}> + <Story /> + </InstanceContextProvider>, + (Story) => <BackendContextProvider defaultUrl={mockBackend.url}> + <Story /> + </BackendContextProvider>, +]; diff --git a/packages/merchant-backoffice-ui/contrib/po2ts b/packages/merchant-backoffice-ui/contrib/po2ts new file mode 100755 index 000000000..a135da61b --- /dev/null +++ b/packages/merchant-backoffice-ui/contrib/po2ts @@ -0,0 +1,42 @@ +#!/usr/bin/env node +/* + This file is part of GNU Taler + (C) 2020 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/> + */ + +/** + * Convert a <lang>.po file into a JavaScript / TypeScript expression. + */ + +const po2json = require("po2json"); + +const filename = process.argv[2]; + +if (!filename) { + console.error("error: missing filename"); + process.exit(1); +} + +const m = filename.match(/([a-zA-Z0-9-_]+).po/); + +if (!m) { + console.error("error: unexpected filename (expected <lang>.po)"); + process.exit(1); +} + +const lang = m[1]; +const pojson = po2json.parseFileSync(filename, { format: "jed1.x", fuzzy: true }); +const s = + "strings['" + lang + "'] = " + JSON.stringify(pojson, null, " ") + ";\n"; +console.log(s); diff --git a/packages/merchant-backoffice-ui/copyleft-header.js b/packages/merchant-backoffice-ui/copyleft-header.js new file mode 100644 index 000000000..0794cb839 --- /dev/null +++ b/packages/merchant-backoffice-ui/copyleft-header.js @@ -0,0 +1,15 @@ +/* + 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/> + */ diff --git a/packages/merchant-backoffice-ui/package.json b/packages/merchant-backoffice-ui/package.json new file mode 100644 index 000000000..ac90dd5cb --- /dev/null +++ b/packages/merchant-backoffice-ui/package.json @@ -0,0 +1,123 @@ +{ + "private": true, + "name": "merchant-backoffice", + "version": "0.0.4", + "license": "MIT", + "scripts": { + "build": "preact build --no-sw --no-esm", + "compile": "tsc", + "serve": "sirv build --port ${PORT:=8080} --cors --single", + "dev": "preact watch --port ${PORT:=8080} --no-sw --no-esm", + "lint-check": "eslint '{src,tests}/**/*.{js,jsx,ts,tsx}'", + "lint-fix": "eslint --fix '{src,tests}/**/*.{js,jsx,ts,tsx}'", + "test": "jest ./tests", + "dev-test": "jest ./tests --watch", + "typedoc": "typedoc src", + "clean": "rimraf build storybook-static docs single", + "build-single": "preact build --no-sw --no-esm -c preact.single-config.js --dest single && sh remove-link-stylesheet.sh", + "serve-single": "sirv single --port ${PORT:=8080} --cors --single", + "build-storybook": "build-storybook", + "storybook": "start-storybook -p 6006" + }, + "engines": { + "node": ">=12", + "pnpm": ">=5" + }, + "eslintConfig": { + "parser": "@typescript-eslint/parser", + "extends": [ + "preact", + "plugin:@typescript-eslint/recommended" + ], + "plugins": [ + "header" + ], + "rules": { + "header/header": [ + 2, + "copyleft-header.js" + ] + }, + "ignorePatterns": [ + "build/" + ] + }, + "dependencies": { + "@gnu-taler/taler-util": "workspace:*", + "axios": "^0.21.1", + "date-fns": "^2.21.1", + "history": "4.10.1", + "jed": "^1.1.1", + "preact": "10.6.1", + "preact-router": "^3.2.1", + "qrcode-generator": "^1.4.4", + "swr": "1.1.0", + "yup": "^0.32.9" + }, + "devDependencies": { + "@babel/core": "^7.13.16", + "@babel/plugin-transform-react-jsx-source": "^7.12.13", + "@creativebulma/bulma-tooltip": "^1.2.0", + "@gnu-taler/pogen": "^0.0.5", + "@storybook/addon-a11y": "^6.2.9", + "@storybook/addon-actions": "^6.2.9", + "@storybook/addon-essentials": "^6.2.9", + "@storybook/addon-links": "^6.2.9", + "@storybook/preact": "^6.2.9", + "@storybook/preset-scss": "^1.0.3", + "@testing-library/preact": "^2.0.1", + "@testing-library/preact-hooks": "^1.1.0", + "@types/history": "^4.7.8", + "@types/jest": "^26.0.23", + "@types/mocha": "^8.2.2", + "@typescript-eslint/eslint-plugin": "^4.22.0", + "@typescript-eslint/parser": "^4.22.0", + "babel-loader": "^8.2.2", + "base64-inline-loader": "^1.1.1", + "bulma": "^0.9.2", + "bulma-checkbox": "^1.1.1", + "bulma-radio": "^1.1.1", + "bulma-responsive-tables": "^1.2.3", + "bulma-switch-control": "^1.1.1", + "bulma-timeline": "^3.0.4", + "bulma-upload-control": "^1.2.0", + "dotenv": "^8.2.0", + "eslint": "^7.25.0", + "eslint-config-preact": "^1.1.4", + "eslint-plugin-header": "^3.1.1", + "html-webpack-inline-chunk-plugin": "^1.1.1", + "html-webpack-inline-source-plugin": "0.0.10", + "html-webpack-skip-assets-plugin": "^1.0.1", + "inline-chunk-html-plugin": "^1.1.1", + "jest": "^26.6.3", + "jest-preset-preact": "^4.0.2", + "po2json": "^0.4.5", + "preact-cli": "^3.0.5", + "preact-render-to-json": "^3.6.6", + "preact-render-to-string": "^5.1.19", + "rimraf": "^3.0.2", + "sass": "^1.32.13", + "sass-loader": "10.1.1", + "script-ext-html-webpack-plugin": "^2.1.5", + "sirv-cli": "^1.0.11", + "typedoc": "^0.20.36", + "typescript": "^4.2.4" + }, + "jest": { + "preset": "jest-preset-preact", + "transformIgnorePatterns": [ + "node_modules/.pnpm/(?!(@gnu-taler\\+taler-util))", + "\\.pnp\\.[^\\/]+$" + ], + "setupFiles": [ + "<rootDir>/tests/__mocks__/browserMocks.ts", + "<rootDir>/tests/__mocks__/setupTests.ts" + ], + "moduleNameMapper": { + "\\.(css|less)$": "identity-obj-proxy" + }, + "transform": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|po)$": "<rootDir>/tests/__mocks__/fileTransformer.js" + } + } +} diff --git a/packages/merchant-backoffice-ui/preact.config.js b/packages/merchant-backoffice-ui/preact.config.js new file mode 100644 index 000000000..8e640f3ff --- /dev/null +++ b/packages/merchant-backoffice-ui/preact.config.js @@ -0,0 +1,70 @@ +/* + 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 { DefinePlugin } from 'webpack'; + +import pack from './package.json'; +import * as cp from 'child_process'; + +const commitHash = cp.execSync('git rev-parse --short HEAD').toString(); + +export default { + webpack(config, env, helpers) { + // ensure that process.env will not be undefined on runtime + config.node.process = 'mock' + + // add __VERSION__ to be use in the html + config.plugins.push( + new DefinePlugin({ + 'process.env.__VERSION__': JSON.stringify(env.isProd ? pack.version : `dev-${commitHash}`) , + }), + ); + + // suddenly getting out of memory error from build process, error below [1] + // FIXME: remove preact-cli, use rollup + let { index } = helpers.getPluginsByName(config, 'WebpackFixStyleOnlyEntriesPlugin')[0] + config.plugins.splice(index, 1) + } +} + + + +/* [1] from this error decided to remove plugin 'webpack-fix-style-only-entries + leaving this error for future reference + + +<--- Last few GCs ---> + +[32479:0x2e01870] 19969 ms: Mark-sweep 1869.4 (1950.2) -> 1443.1 (1504.1) MB, 497.5 / 0.0 ms (average mu = 0.631, current mu = 0.455) allocation failure scavenge might not succeed +[32479:0x2e01870] 21907 ms: Mark-sweep 2016.9 (2077.9) -> 1628.6 (1681.4) MB, 1596.0 / 0.0 ms (average mu = 0.354, current mu = 0.176) allocation failure scavenge might not succeed + +<--- JS stacktrace ---> + +==== JS stack trace ========================================= + + 0: ExitFrame [pc: 0x13cf099] +Security context: 0x2f4ca66c08d1 <JSObject> + 1: /* anonymous * / [0x35d05555b4b9] [...path/merchant-backoffice/node_modules/.pnpm/webpack-fix-style-only-entries@0.5.2/node_modules/webpack-fix-style-only-entries/index.js:~80] [pc=0x2145e699d1a4](this=0x1149465410e9 <GlobalObject Object map = 0xff481b5b5f9>,0x047e52e36a49 <Dependency map = 0x1ed1fe41cd19>) + 2: arguments adaptor frame: 3... + +FATAL ERROR: invalid array length Allocation failed - JavaScript heap out of memory + +*/
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/preact.single-config.js b/packages/merchant-backoffice-ui/preact.single-config.js new file mode 100644 index 000000000..61a79bb8a --- /dev/null +++ b/packages/merchant-backoffice-ui/preact.single-config.js @@ -0,0 +1,62 @@ +/* + 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 defaultConfig from './preact.config' + +export default { + webpack(config, env, helpers, options) { + defaultConfig.webpack(config, env, helpers, options) + + //1. check no file is under /routers or /component/{routers,async} to prevent async components + // https://github.com/preactjs/preact-cli#route-based-code-splitting + + //2. remove devtools to prevent sourcemaps + config.devtool = false + + //3. change assetLoader to load assets inline + const loaders = helpers.getLoaders(config) + const assetsLoader = loaders.find(lo => lo.rule.test.test('something.woff')) + if (assetsLoader) { + assetsLoader.rule.use = 'base64-inline-loader' + assetsLoader.rule.loader = undefined + } + + //4. remove critters + //critters remove the css bundle from htmlWebpackPlugin.files.css + //for now, pushing all the content into the html is enough + const crittersWrapper = helpers.getPluginsByName(config, 'Critters') + if (crittersWrapper && crittersWrapper.length > 0) { + const [{ index }] = crittersWrapper + config.plugins.splice(index, 1) + } + + //5. remove favicon from src/assets + + //6. remove performance hints since we now that this is going to be big + if (config.performance) { + config.performance.hints = false + } + + //7. template.html should have a favicon and add js/css content + + //last, after building remove the mysterious link to stylesheet with remove-link-stylesheet.sh + } +} diff --git a/packages/merchant-backoffice-ui/remove-link-stylesheet.sh b/packages/merchant-backoffice-ui/remove-link-stylesheet.sh new file mode 100644 index 000000000..fdf8f241c --- /dev/null +++ b/packages/merchant-backoffice-ui/remove-link-stylesheet.sh @@ -0,0 +1,8 @@ +# This script has been placed in the public domain. + +FILE=$(ls single/bundle.*.css) +BUNDLE=${FILE#single} +grep -q '<link href="'$BUNDLE'" rel="stylesheet">' single/index.html || { echo bundle $BUNDLE not found in index.html; exit 1; } +echo -n Removing link from index.html ... +sed 's_<link href="'$BUNDLE'" rel="stylesheet">__' -i single/index.html +echo done diff --git a/packages/merchant-backoffice-ui/src/.babelrc b/packages/merchant-backoffice-ui/src/.babelrc new file mode 100644 index 000000000..3ec8a6291 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/.babelrc @@ -0,0 +1,26 @@ +/* + 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) + */ + +{ + "presets": [ + "preact-cli/babel" + ] +} diff --git a/packages/merchant-backoffice-ui/src/AdminRoutes.tsx b/packages/merchant-backoffice-ui/src/AdminRoutes.tsx new file mode 100644 index 000000000..a3ffbe2e6 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/AdminRoutes.tsx @@ -0,0 +1,58 @@ +/* + 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/> + */ +import { h, VNode } from "preact"; +import Router, { route, Route } from "preact-router"; +import InstanceCreatePage from "./paths/admin/create"; +import InstanceListPage from './paths/admin/list'; + + +export enum AdminPaths { + list_instances = '/instances', + new_instance = '/instance/new', +} + +export function AdminRoutes(): VNode { + + return <Router> + + <Route path={AdminPaths.list_instances} component={InstanceListPage} + + onCreate={() => { + route(AdminPaths.new_instance); + }} + + onUpdate={(id: string): void => { + route(`/instance/${id}/update`); + }} + + /> + + <Route path={AdminPaths.new_instance} component={InstanceCreatePage} + + onBack={() => route(AdminPaths.list_instances)} + + onConfirm={() => { + // route(AdminPaths.list_instances); + }} + + // onError={(error: any) => { + // }} + + /> + + + </Router> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx new file mode 100644 index 000000000..ebc3d1d95 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx @@ -0,0 +1,120 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import Router, { Route, route } from "preact-router"; +import { useBackendContext } from "./context/backend"; +import { useBackendInstancesTestForAdmin } from "./hooks/backend"; +import { InstanceRoutes } from "./InstanceRoutes"; +import LoginPage from "./paths/login"; +import { INSTANCE_ID_LOOKUP } from "./utils/constants"; +import { NotYetReadyAppMenu, Menu, NotificationCard } from "./components/menu"; +import { useTranslator } from "./i18n"; +import { createHashHistory } from "history"; +import { useState } from "preact/hooks"; + +export function ApplicationReadyRoutes(): VNode { + const i18n = useTranslator(); + const { + url: backendURL, + updateLoginStatus, + clearAllTokens, + } = useBackendContext(); + + const result = useBackendInstancesTestForAdmin(); + + const clearTokenAndGoToRoot = () => { + clearAllTokens(); + route("/"); + }; + + if (result.clientError && result.isUnauthorized) { + return ( + <Fragment> + <NotYetReadyAppMenu title="Login" onLogout={clearTokenAndGoToRoot} /> + <NotificationCard + notification={{ + message: i18n`Access denied`, + description: i18n`Check your token is valid`, + type: "ERROR", + }} + /> + <LoginPage onConfirm={updateLoginStatus} /> + </Fragment> + ); + } + + if (result.loading) return <NotYetReadyAppMenu title="Loading..." />; + + let admin = true; + let instanceNameByBackendURL; + + if (!result.ok) { + const path = new URL(backendURL).pathname; + const match = INSTANCE_ID_LOOKUP.exec(path); + if (!match || !match[1]) { + // this should be rare because + // query to /config is ok but the URL + // does not match our pattern + return ( + <Fragment> + <NotYetReadyAppMenu title="Error" onLogout={clearTokenAndGoToRoot} /> + <NotificationCard + notification={{ + message: i18n`Couldn't access the server.`, + description: i18n`Could not infer instance id from url ${backendURL}`, + type: "ERROR", + }} + /> + <LoginPage onConfirm={updateLoginStatus} /> + </Fragment> + ); + } + + admin = false; + instanceNameByBackendURL = match[1]; + } + + const history = createHashHistory(); + return ( + <Router history={history}> + <Route + default + component={DefaultMainRoute} + admin={admin} + instanceNameByBackendURL={instanceNameByBackendURL} + /> + </Router> + ); +} + +function DefaultMainRoute({ instance, admin, instanceNameByBackendURL }: any) { + const [instanceName, setInstanceName] = useState( + instanceNameByBackendURL || instance || "default" + ); + + return ( + <InstanceRoutes + admin={admin} + id={instanceName} + setInstanceName={setInstanceName} + /> + ); +} diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx new file mode 100644 index 000000000..06f1db17c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx @@ -0,0 +1,528 @@ +/* + 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 { Fragment, FunctionComponent, h, VNode } from "preact"; +import { Route, route, Router } from "preact-router"; +import { useCallback, useEffect, useMemo, useState } from "preact/hooks"; +import { Loading } from "./components/exception/loading"; +import { Menu, NotificationCard } from "./components/menu"; +import { useBackendContext } from "./context/backend"; +import { InstanceContextProvider } from "./context/instance"; +import { + useBackendDefaultToken, + useBackendInstanceToken, + useLocalStorage, +} from "./hooks"; +import { HttpError } from "./hooks/backend"; +import { Translate, useTranslator } from "./i18n"; +import InstanceCreatePage from "./paths/admin/create"; +import InstanceListPage from "./paths/admin/list"; +import OrderCreatePage from "./paths/instance/orders/create"; +import OrderDetailsPage from "./paths/instance/orders/details"; +import OrderListPage from "./paths/instance/orders/list"; +import ProductCreatePage from "./paths/instance/products/create"; +import ProductListPage from "./paths/instance/products/list"; +import ProductUpdatePage from "./paths/instance/products/update"; +import TransferListPage from "./paths/instance/transfers/list"; +import TransferCreatePage from "./paths/instance/transfers/create"; +import ReservesCreatePage from "./paths/instance/reserves/create"; +import ReservesDetailsPage from "./paths/instance/reserves/details"; +import ReservesListPage from "./paths/instance/reserves/list"; +import ListKYCPage from "./paths/instance/kyc/list"; +import InstanceUpdatePage, { + Props as InstanceUpdatePageProps, + AdminUpdate as InstanceAdminUpdatePage, +} from "./paths/instance/update"; +import LoginPage from "./paths/login"; +import NotFoundPage from "./paths/notfound"; +import { Notification } from "./utils/types"; +import { useInstanceKYCDetails } from "./hooks/instance"; +import { format } from "date-fns"; + +export enum InstancePaths { + // details = '/', + error = "/error", + update = "/update", + + product_list = "/products", + product_update = "/product/:pid/update", + product_new = "/product/new", + + order_list = "/orders", + order_new = "/order/new", + order_details = "/order/:oid/details", + + reserves_list = "/reserves", + reserves_details = "/reserves/:rid/details", + reserves_new = "/reserves/new", + + kyc = "/kyc", + + transfers_list = "/transfers", + transfers_new = "/transfer/new", +} + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noop = () => {}; + +export enum AdminPaths { + list_instances = "/instances", + new_instance = "/instance/new", + update_instance = "/instance/:id/update", +} + +export interface Props { + id: string; + admin?: boolean; + setInstanceName: (s: string) => void; +} + +export function InstanceRoutes({ id, admin, setInstanceName }: Props): VNode { + const [_, updateDefaultToken] = useBackendDefaultToken(); + const [token, updateToken] = useBackendInstanceToken(id); + const { + updateLoginStatus: changeBackend, + addTokenCleaner, + clearAllTokens, + } = useBackendContext(); + const cleaner = useCallback(() => { + updateToken(undefined); + }, [id]); + const i18n = useTranslator(); + + type GlobalNotifState = (Notification & { to: string }) | undefined; + const [globalNotification, setGlobalNotification] = + useState<GlobalNotifState>(undefined); + + useEffect(() => { + addTokenCleaner(cleaner); + }, [addTokenCleaner, cleaner]); + + const changeToken = (token?: string) => { + if (admin) { + updateToken(token); + } else { + updateDefaultToken(token); + } + }; + const updateLoginStatus = (url: string, token?: string) => { + changeBackend(url); + if (!token) return; + changeToken(token); + }; + + const value = useMemo( + () => ({ id, token, admin, changeToken }), + [id, token, admin] + ); + + function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) { + return function ServerErrorRedirectToImpl(error: HttpError) { + setGlobalNotification({ + message: i18n`The backend reported a problem: HTTP status #${error.status}`, + description: i18n`Diagnostic from ${error.info?.url} is "${error.message}"`, + details: + error.clientError || error.serverError + ? error.error?.detail + : undefined, + type: "ERROR", + to, + }); + return <Redirect to={to} />; + }; + } + + const LoginPageAccessDenied = () => ( + <Fragment> + <NotificationCard + notification={{ + message: i18n`Access denied`, + description: i18n`The access token provided is invalid.`, + type: "ERROR", + }} + /> + <LoginPage onConfirm={updateLoginStatus} /> + </Fragment> + ); + + function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<any>) { + return function IfAdminCreateDefaultOrImpl(props?: T) { + if (admin && id === "default") { + return ( + <Fragment> + <NotificationCard + notification={{ + message: i18n`No 'default' instance configured yet.`, + description: i18n`Create a 'default' instance to begin using the merchant backoffice.`, + type: "INFO", + }} + /> + <InstanceCreatePage + forceId="default" + onConfirm={() => { + route(AdminPaths.list_instances); + }} + /> + </Fragment> + ); + } + if (props) { + return <Next {...props} />; + } + return <Next />; + }; + } + + const clearTokenAndGoToRoot = () => { + clearAllTokens(); + route("/"); + }; + + return ( + <InstanceContextProvider value={value}> + <Menu + instance={id} + admin={admin} + onLogout={clearTokenAndGoToRoot} + setInstanceName={setInstanceName} + /> + <KycBanner /> + <NotificationCard notification={globalNotification} /> + + <Router + onChange={(e) => { + const movingOutFromNotification = + globalNotification && e.url !== globalNotification.to; + if (movingOutFromNotification) { + setGlobalNotification(undefined); + } + }} + > + <Route path="/" component={Redirect} to={InstancePaths.order_list} /> + + {/** + * Admin pages + */} + {admin && ( + <Route + path={AdminPaths.list_instances} + component={InstanceListPage} + onCreate={() => { + route(AdminPaths.new_instance); + }} + onUpdate={(id: string): void => { + route(`/instance/${id}/update`); + }} + setInstanceName={setInstanceName} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.error)} + /> + )} + + {admin && ( + <Route + path={AdminPaths.new_instance} + component={InstanceCreatePage} + onBack={() => route(AdminPaths.list_instances)} + onConfirm={() => { + route(AdminPaths.list_instances); + }} + /> + )} + + {admin && ( + <Route + path={AdminPaths.update_instance} + component={AdminInstanceUpdatePage} + onBack={() => route(AdminPaths.list_instances)} + onConfirm={() => { + route(AdminPaths.list_instances); + }} + onUpdateError={ServerErrorRedirectTo(AdminPaths.list_instances)} + onLoadError={ServerErrorRedirectTo(AdminPaths.list_instances)} + onNotFound={NotFoundPage} + /> + )} + + {/** + * Update instance page + */} + <Route + path={InstancePaths.update} + component={InstanceUpdatePage} + onBack={() => { + route(`/`); + }} + onConfirm={() => { + route(`/`); + }} + onUpdateError={noop} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.error)} + /> + + {/** + * Product pages + */} + <Route + path={InstancePaths.product_list} + component={ProductListPage} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.update)} + onCreate={() => { + route(InstancePaths.product_new); + }} + onSelect={(id: string) => { + route(InstancePaths.product_update.replace(":pid", id)); + }} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + /> + <Route + path={InstancePaths.product_update} + component={ProductUpdatePage} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.product_list)} + onConfirm={() => { + route(InstancePaths.product_list); + }} + onBack={() => { + route(InstancePaths.product_list); + }} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + /> + <Route + path={InstancePaths.product_new} + component={ProductCreatePage} + onConfirm={() => { + route(InstancePaths.product_list); + }} + onBack={() => { + route(InstancePaths.product_list); + }} + /> + + {/** + * Order pages + */} + <Route + path={InstancePaths.order_list} + component={OrderListPage} + onCreate={() => { + route(InstancePaths.order_new); + }} + onSelect={(id: string) => { + route(InstancePaths.order_details.replace(":oid", id)); + }} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.update)} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + /> + <Route + path={InstancePaths.order_details} + component={OrderDetailsPage} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.order_list)} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + onBack={() => { + route(InstancePaths.order_list); + }} + /> + <Route + path={InstancePaths.order_new} + component={OrderCreatePage} + onConfirm={() => { + route(InstancePaths.order_list); + }} + onBack={() => { + route(InstancePaths.order_list); + }} + /> + + {/** + * Transfer pages + */} + <Route + path={InstancePaths.transfers_list} + component={TransferListPage} + onUnauthorized={LoginPageAccessDenied} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + onLoadError={ServerErrorRedirectTo(InstancePaths.update)} + onCreate={() => { + route(InstancePaths.transfers_new); + }} + /> + + <Route + path={InstancePaths.transfers_new} + component={TransferCreatePage} + onConfirm={() => { + route(InstancePaths.transfers_list); + }} + onBack={() => { + route(InstancePaths.transfers_list); + }} + /> + + {/** + * reserves pages + */} + <Route + path={InstancePaths.reserves_list} + component={ReservesListPage} + onUnauthorized={LoginPageAccessDenied} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + onLoadError={ServerErrorRedirectTo(InstancePaths.update)} + onSelect={(id: string) => { + route(InstancePaths.reserves_details.replace(":rid", id)); + }} + onCreate={() => { + route(InstancePaths.reserves_new); + }} + /> + + <Route + path={InstancePaths.reserves_details} + component={ReservesDetailsPage} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.reserves_list)} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + onBack={() => { + route(InstancePaths.reserves_list); + }} + /> + + <Route + path={InstancePaths.reserves_new} + component={ReservesCreatePage} + onConfirm={() => { + route(InstancePaths.reserves_list); + }} + onBack={() => { + route(InstancePaths.reserves_list); + }} + /> + + <Route path={InstancePaths.kyc} component={ListKYCPage} /> + {/** + * Example pages + */} + <Route path="/loading" component={Loading} /> + <Route default component={NotFoundPage} /> + </Router> + </InstanceContextProvider> + ); +} + +export function Redirect({ to }: { to: string }): null { + useEffect(() => { + route(to, true); + }); + return null; +} + +function AdminInstanceUpdatePage({ + id, + ...rest +}: { id: string } & InstanceUpdatePageProps) { + const [token, changeToken] = useBackendInstanceToken(id); + const { updateLoginStatus: changeBackend } = useBackendContext(); + const updateLoginStatus = (url: string, token?: string) => { + changeBackend(url); + if (token) changeToken(token); + }; + const value = useMemo( + () => ({ id, token, admin: true, changeToken }), + [id, token] + ); + const i18n = useTranslator(); + + return ( + <InstanceContextProvider value={value}> + <InstanceAdminUpdatePage + {...rest} + instanceId={id} + onLoadError={(error: HttpError) => { + return ( + <Fragment> + <NotificationCard + notification={{ + message: i18n`The backend reported a problem: HTTP status #${error.status}`, + description: i18n`Diagnostic from ${error.info?.url} is "${error.message}"`, + details: + error.clientError || error.serverError + ? error.error?.detail + : undefined, + type: "ERROR", + }} + /> + <LoginPage onConfirm={updateLoginStatus} /> + </Fragment> + ); + }} + onUnauthorized={() => { + return ( + <Fragment> + <NotificationCard + notification={{ + message: i18n`Access denied`, + description: i18n`The access token provided is invalid`, + type: "ERROR", + }} + /> + <LoginPage onConfirm={updateLoginStatus} /> + </Fragment> + ); + }} + /> + </InstanceContextProvider> + ); +} + +function KycBanner(): VNode { + const kycStatus = useInstanceKYCDetails(); + const today = format(new Date(), "yyyy-MM-dd"); + const [lastHide, setLastHide] = useLocalStorage("kyc-last-hide"); + const hasBeenHidden = today === lastHide; + const needsToBeShown = kycStatus.ok && kycStatus.data.type === "redirect"; + if (hasBeenHidden || !needsToBeShown) return <Fragment />; + return ( + <NotificationCard + notification={{ + type: "WARN", + message: "KYC verification needed", + description: ( + <div> + <p> + Some transfer are on hold until a KYC process is completed. Go to + the KYC section in the left panel for more information + </p> + <div class="buttons is-right"> + <button class="button" onClick={() => setLastHide(today)}> + <Translate>Hide for today</Translate> + </button> + </div> + </div> + ), + }} + /> + ); +} diff --git a/packages/merchant-backoffice-ui/src/assets/empty.png b/packages/merchant-backoffice-ui/src/assets/empty.png Binary files differnew file mode 100644 index 000000000..5120d3138 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/empty.png diff --git a/packages/merchant-backoffice-ui/src/assets/icons/android-chrome-192x192.png b/packages/merchant-backoffice-ui/src/assets/icons/android-chrome-192x192.png Binary files differnew file mode 100644 index 000000000..93ebe2e2c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/icons/android-chrome-192x192.png diff --git a/packages/merchant-backoffice-ui/src/assets/icons/android-chrome-512x512.png b/packages/merchant-backoffice-ui/src/assets/icons/android-chrome-512x512.png Binary files differnew file mode 100644 index 000000000..52d1623ea --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/icons/android-chrome-512x512.png diff --git a/packages/merchant-backoffice-ui/src/assets/icons/apple-touch-icon.png b/packages/merchant-backoffice-ui/src/assets/icons/apple-touch-icon.png Binary files differnew file mode 100644 index 000000000..254e4bb4d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/icons/apple-touch-icon.png diff --git a/packages/merchant-backoffice-ui/src/assets/icons/favicon-16x16.png b/packages/merchant-backoffice-ui/src/assets/icons/favicon-16x16.png Binary files differnew file mode 100644 index 000000000..e81177dcb --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/icons/favicon-16x16.png diff --git a/packages/merchant-backoffice-ui/src/assets/icons/favicon-32x32.png b/packages/merchant-backoffice-ui/src/assets/icons/favicon-32x32.png Binary files differnew file mode 100644 index 000000000..40e9b5b47 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/icons/favicon-32x32.png diff --git a/packages/merchant-backoffice-ui/src/assets/icons/languageicon.svg b/packages/merchant-backoffice-ui/src/assets/icons/languageicon.svg new file mode 100644 index 000000000..22d58da65 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/icons/languageicon.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 2411.2 2794" style="enable-background:new 0 0 2411.2 2794;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#FFFFFF;}
+ .st1{fill-rule:evenodd;clip-rule:evenodd;}
+ .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+</style>
+<g id="Layer_2">
+</g>
+<g id="Layer_x5F_1_x5F_1">
+ <g>
+ <polygon points="1204.6,359.2 271.8,30 271.8,2060.1 1204.6,1758.3 "/>
+ <polygon class="st0" points="1182.2,358.1 2150.6,29 2150.6,2059 1182.2,1757.3 "/>
+ <polygon class="st0" points="30,2415.4 1182.2,2031.4 1182.2,357.9 30,742 "/>
+ <polygon points="1707.2,2440.7 1870.5,2709.4 1956.6,2459.8 "/>
+ <g>
+ <path d="M421.7,934.8c-6.1-6,8,49.1,27.6,68.9c34.8,35.1,61.9,39.6,76.4,40.2c32,1.3,71.5-8,94.9-17.8
+ c22.7-9.7,62.4-30,77.5-59.6c3.2-6.3,11.9-17,6.4-43.2c-4.2-20.2-17-27.3-32.7-26.2c-15.7,1.1-63.2,13.7-86.1,20.8
+ c-23,7-70.3,21.4-90.9,25.8C474.3,948.2,429,941.7,421.7,934.8z"/>
+ <path d="M1003.1,1593.7c-9.1-3.3-196.9-81.1-223.6-93.9c-21.8-10.5-75.2-33.1-100.4-43.3c70.8-109.2,115.5-191.6,121.5-204.1
+ c11-23,86-169.6,87.7-178.7c1.7-9.1,3.8-42.9,2.2-51c-1.7-8.2-29.1,7.6-66.4,20.2c-37.4,12.6-108.4,58.8-135.8,64.6
+ c-27.5,5.7-115.5,39.1-160.5,54c-45,14.9-130.2,40.9-165.2,50.4c-35.1,9.5-65.7,10.2-85.3,16.2c0,0,2.6,27.5,7.8,35.7
+ c5.2,8.2,23.7,28.4,45.3,34.1c21.6,5.7,57.3,3.4,73.6-0.3c16.3-3.8,44.4-17.5,48.2-23.6c3.8-6.1-2-24.9,4.5-30.6
+ c6.5-5.6,92.2-25.7,124.6-35.4c32.4-10,156.3-52.6,173.1-50.5c-5.3,17.7-105,215.1-137.1,274c-32.1,58.9-218.6,318-258.3,363.6
+ c-30.1,34.7-103.2,123.5-128.5,143.6c6.4,1.8,51.6-2.1,59.9-7.2c51.3-31.6,136.9-138.1,164.4-170.5
+ c81.9-96,153.8-196.8,210.8-283.4h0.1c11.1,4.6,100.9,77.8,124.4,94c23.4,16.2,115.9,67.8,136,76.4c20,8.7,97.1,44.2,100.3,32.2
+ C1029.4,1668,1012.2,1597.1,1003.1,1593.7z"/>
+ </g>
+ <path class="st1" d="M569,2572c18,11,35,20,54,29c38,19,81,39,122,54c56,21,112,38,168,51c31,7,65,13,98,18c3,0,92,11,110,11h90
+ c35-3,68-5,103-10c28-4,59-9,89-16c22-5,45-10,67-17c21-6,45-14,68-22c15-5,31-12,47-18c13-6,29-13,44-19c18-8,39-19,59-29
+ c16-8,34-18,51-28c13-7,43-30,59-30c18,0,30,16,30,30c0,29-39,38-57,51c-19,13-42,23-62,34c-40,21-81,39-120,54
+ c-51,19-107,37-157,49c-19,4-38,9-57,12c-10,2-114,18-143,18h-132c-35-3-72-7-107-12c-31-5-64-11-95-18c-24-5-50-12-73-19
+ c-40-11-79-25-117-40c-69-26-141-60-209-105c-12-8-13-16-13-25c0-15,11-29,29-29C531,2546,563,2569,569,2572z"/>
+ <path class="st1" d="M1151,2009L61,2372V764l1090-363V2009z M1212,354v1680c-1,5-3,10-7,15c-2,3-6,7-9,8c-25,10-1151,388-1166,388
+ c-12,0-23-8-29-21c0-1-1-2-1-4V739c2-5,3-12,7-16c8-11,22-13,31-16c17-6,1126-378,1142-378C1190,329,1212,336,1212,354z"/>
+ <path class="st1" d="M2120,2017l-907-282V380l907-308V2017z M2181,32v2023c-1,23-17,33-32,33c-13,0-107-32-123-37
+ c-126-39-253-78-378-117c-28-9-57-18-84-27c-24-7-50-15-74-23c-107-33-216-66-323-102c-4-1-14-15-14-18V351c2-5,4-11,9-15
+ c8-9,351-123,486-168c36-13,487-168,501-168C2167,0,2181,13,2181,32z"/>
+ <polygon points="2411.2,2440.7 1199.5,2054.5 1204.6,373.2 2411.2,757.2 "/>
+ <g>
+ <path class="st2" d="M1800.3,1124.6L1681.4,1412l218.6,66.3L1800.3,1124.6z M1729,853.2l156.1,47.3l284.4,1025l-160.3-48.7
+ l-57.6-210.4L1620.2,1566l-71.3,171.4l-160.4-48.7L1729,853.2z"/>
+ </g>
+ </g>
+</g>
+</svg>
diff --git a/packages/merchant-backoffice-ui/src/assets/icons/mstile-150x150.png b/packages/merchant-backoffice-ui/src/assets/icons/mstile-150x150.png Binary files differnew file mode 100644 index 000000000..9cfb889be --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/icons/mstile-150x150.png diff --git a/packages/merchant-backoffice-ui/src/assets/logo.jpeg b/packages/merchant-backoffice-ui/src/assets/logo.jpeg Binary files differnew file mode 100644 index 000000000..489832f7c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/assets/logo.jpeg diff --git a/packages/merchant-backoffice-ui/src/components/exception/AsyncButton.tsx b/packages/merchant-backoffice-ui/src/components/exception/AsyncButton.tsx new file mode 100644 index 000000000..92bab4bfb --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/exception/AsyncButton.tsx @@ -0,0 +1,49 @@ +/* + 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 { ComponentChildren, h } from "preact"; +import { LoadingModal } from "../modal"; +import { useAsync } from "../../hooks/async"; +import { Translate } from "../../i18n"; + +type Props = { + children: ComponentChildren, + disabled: boolean; + onClick?: () => Promise<void>; + [rest:string]: any, +}; + +export function AsyncButton({ onClick, disabled, children, ...rest }: Props) { + const { isSlow, isLoading, request, cancel } = useAsync(onClick); + + if (isSlow) { + return <LoadingModal onCancel={cancel} />; + } + if (isLoading) { + return <button class="button"><Translate>Loading...</Translate></button>; + } + + return <span {...rest}> + <button class="button is-success" onClick={request} disabled={disabled}> + {children} + </button> + </span>; +} diff --git a/packages/merchant-backoffice-ui/src/components/exception/QR.tsx b/packages/merchant-backoffice-ui/src/components/exception/QR.tsx new file mode 100644 index 000000000..bcb9964a5 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/exception/QR.tsx @@ -0,0 +1,49 @@ +/* + 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/> + */ + +import { h, VNode } from "preact"; +import { useEffect, useRef } from "preact/hooks"; +import qrcode from "qrcode-generator"; + +export function QR({ text }: { text: string }): VNode { + const divRef = useRef<HTMLDivElement>(null); + useEffect(() => { + const qr = qrcode(0, "L"); + qr.addData(text); + qr.make(); + if (divRef.current) { + divRef.current.innerHTML = qr.createSvgTag({ + scalable: true, + }); + } + }); + + return ( + <div + style={{ + width: "100%", + display: "flex", + flexDirection: "column", + alignItems: "center", + }} + > + <div + style={{ width: "50%", minWidth: 200, maxWidth: 300 }} + ref={divRef} + /> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/exception/loading.tsx b/packages/merchant-backoffice-ui/src/components/exception/loading.tsx new file mode 100644 index 000000000..f2139a17e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/exception/loading.tsx @@ -0,0 +1,32 @@ +/* + 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"; + +export function Loading(): VNode { + return <div class="columns is-centered is-vcentered" style={{ height: 'calc(100% - 3rem)', position: 'absolute', width: '100%' }}> + <Spinner /> + </div> +} + +export function Spinner(): VNode { + return <div class="lds-ring"><div /><div /><div /><div /></div> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/components/exception/login.tsx b/packages/merchant-backoffice-ui/src/components/exception/login.tsx new file mode 100644 index 000000000..498d994ed --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/exception/login.tsx @@ -0,0 +1,143 @@ +/* + 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 { useState } from "preact/hooks"; +import { useBackendContext } from "../../context/backend"; +import { useInstanceContext } from "../../context/instance"; +import { Translate, useTranslator } from "../../i18n"; +import { Notification } from "../../utils/types"; + +interface Props { + withMessage?: Notification; + onConfirm: (backend: string, token?: string) => void; +} + +function getTokenValuePart(t?: string): string | undefined { + if (!t) return t; + const match = /secret-token:(.*)/.exec(t); + if (!match || !match[1]) return undefined; + return match[1]; +} + +function normalizeToken(r: string | undefined): string | undefined { + return r ? `secret-token:${encodeURIComponent(r)}` : undefined; +} + +export function LoginModal({ onConfirm, withMessage }: Props): VNode { + const { url: backendUrl, token: baseToken } = useBackendContext(); + const { admin, token: instanceToken } = useInstanceContext(); + const currentToken = getTokenValuePart( + !admin ? baseToken : instanceToken || "" + ); + const [token, setToken] = useState(currentToken); + + const [url, setURL] = useState(backendUrl); + const i18n = useTranslator(); + + return ( + <div class="columns is-centered"> + <div class="column is-two-thirds "> + <div class="modal-card" style={{ width: "100%", margin: 0 }}> + <header + class="modal-card-head" + style={{ border: "1px solid", borderBottom: 0 }} + > + <p class="modal-card-title">{i18n`Login required`}</p> + </header> + <section + class="modal-card-body" + style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} + > + <Translate>Please enter your access token.</Translate> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">URL</label> + </div> + <div class="field-body"> + <div class="field"> + <p class="control is-expanded"> + <input + class="input" + type="text" + placeholder="set new url" + name="id" + value={url} + onKeyPress={(e) => + e.keyCode === 13 + ? onConfirm(url, normalizeToken(token)) + : null + } + onInput={(e): void => setURL(e?.currentTarget.value)} + /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Access Token</Translate> + </label> + </div> + <div class="field-body"> + <div class="field"> + <p class="control is-expanded"> + <input + class="input" + type="password" + placeholder={"set new access token"} + name="token" + onKeyPress={(e) => + e.keyCode === 13 + ? onConfirm(url, normalizeToken(token)) + : null + } + value={token} + onInput={(e): void => setToken(e?.currentTarget.value)} + /> + </p> + </div> + </div> + </div> + </section> + <footer + class="modal-card-foot " + style={{ + justifyContent: "flex-end", + border: "1px solid", + borderTop: 0, + }} + > + <button + class="button is-info" + onClick={(): void => { + onConfirm(url, normalizeToken(token)); + }} + > + <Translate>Confirm</Translate> + </button> + </footer> + </div> + </div> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx b/packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx new file mode 100644 index 000000000..aef410ce7 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/FormProvider.tsx @@ -0,0 +1,81 @@ +/* + 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 { ComponentChildren, createContext, h, VNode } from "preact"; +import { useContext, useMemo } from "preact/hooks"; + +type Updater<S> = (value: ((prevState: S) => S) ) => void; + +export interface Props<T> { + object?: Partial<T>; + errors?: FormErrors<T>; + name?: string; + valueHandler: Updater<Partial<T>> | null; + children: ComponentChildren +} + +const noUpdater: Updater<Partial<unknown>> = () => (s: unknown) => s + +export function FormProvider<T>({ object = {}, errors = {}, name = '', valueHandler, children }: Props<T>): VNode { + const initialObject = useMemo(() => object, []); + const value = useMemo<FormType<T>>(() => ({ errors, object, initialObject, valueHandler: valueHandler ? valueHandler : noUpdater, name, toStr: {}, fromStr: {} }), [errors, object, valueHandler]); + + return <FormContext.Provider value={value}> + <form class="field" onSubmit={(e) => { + e.preventDefault(); + // if (valueHandler) valueHandler(object); + }}> + {children} + </form> + </FormContext.Provider>; +} + +export interface FormType<T> { + object: Partial<T>; + initialObject: Partial<T>; + errors: FormErrors<T>; + toStr: FormtoStr<T>; + name: string; + fromStr: FormfromStr<T>; + valueHandler: Updater<Partial<T>>; +} + +const FormContext = createContext<FormType<unknown>>(null!) + +export function useFormContext<T>() { + return useContext<FormType<T>>(FormContext) +} + +export type FormErrors<T> = { + [P in keyof T]?: string | FormErrors<T[P]> +} + +export type FormtoStr<T> = { + [P in keyof T]?: ((f?: T[P]) => string) +} + +export type FormfromStr<T> = { + [P in keyof T]?: ((f: string) => T[P]) +} + +export type FormUpdater<T> = { + [P in keyof T]?: (f: keyof T) => (v: T[P]) => void +} diff --git a/packages/merchant-backoffice-ui/src/components/form/Input.tsx b/packages/merchant-backoffice-ui/src/components/form/Input.tsx new file mode 100644 index 000000000..9a9691e9b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/Input.tsx @@ -0,0 +1,71 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; +import { useField, InputProps } from "./useField"; + +interface Props<T> extends InputProps<T> { + inputType?: 'text' | 'number' | 'multiline' | 'password'; + expand?: boolean; + toStr?: (v?: any) => string; + fromStr?: (s: string) => any; + inputExtra?: any, + side?: ComponentChildren; + children?: ComponentChildren; +} + +const defaultToString = (f?: any): string => f || '' +const defaultFromString = (v: string): any => v as any + +const TextInput = ({ inputType, error, ...rest }: any) => inputType === 'multiline' ? + <textarea {...rest} class={error ? "textarea is-danger" : "textarea"} rows="3" /> : + <input {...rest} class={error ? "input is-danger" : "input"} type={inputType} />; + +export function Input<T>({ name, readonly, placeholder, tooltip, label, expand, help, children, inputType, inputExtra, side, fromStr = defaultFromString, toStr = defaultToString }: Props<keyof T>): VNode { + const { error, value, onChange, required } = useField<T>(name); + return <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class={expand ? "control is-expanded has-icons-right" : "control has-icons-right"}> + <TextInput error={error} {...inputExtra} + inputType={inputType} + placeholder={placeholder} readonly={readonly} + name={String(name)} value={toStr(value)} + onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void => onChange(fromStr(e.currentTarget.value))} /> + {help} + {children} + { required && <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> + </span> } + </p> + {error && <p class="help is-danger">{error}</p>} + </div> + {side} + </div> + </div>; +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx new file mode 100644 index 000000000..984c6dc49 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputArray.tsx @@ -0,0 +1,97 @@ +/* + 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 { useState } from "preact/hooks"; +import { Translate, useTranslator } from "../../i18n"; +import { InputProps, useField } from "./useField"; + +export interface Props<T> extends InputProps<T> { + isValid?: (e: any) => boolean; + addonBefore?: string; + toStr?: (v?: any) => string; + fromStr?: (s: string) => any; +} + +const defaultToString = (f?: any): string => f || '' +const defaultFromString = (v: string): any => v as any + +export function InputArray<T>({ name, readonly, placeholder, tooltip, label, help, addonBefore, isValid = () => true, fromStr = defaultFromString, toStr = defaultToString }: Props<keyof T>): VNode { + const { error: formError, value, onChange, required } = useField<T>(name); + const [localError, setLocalError] = useState<string | null>(null) + + const error = localError || formError + + const array: any[] = (value ? value! : []) as any; + const [currentValue, setCurrentValue] = useState(''); + const i18n = useTranslator(); + + return <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <div class="field has-addons"> + {addonBefore && <div class="control"> + <a class="button is-static">{addonBefore}</a> + </div>} + <p class="control is-expanded has-icons-right"> + <input class={error ? "input is-danger" : "input"} type="text" + placeholder={placeholder} readonly={readonly} disabled={readonly} + name={String(name)} value={currentValue} + onChange={(e): void => setCurrentValue(e.currentTarget.value)} /> + {required && <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> + </span>} + </p> + <p class="control"> + <button class="button is-info has-tooltip-left" disabled={!currentValue} onClick={(): void => { + const v = fromStr(currentValue) + if (!isValid(v)) { + setLocalError(i18n`The value ${v} is invalid for a payment url`) + return; + } + setLocalError(null) + onChange([v, ...array] as any); + setCurrentValue(''); + }} data-tooltip={i18n`add element to the list`}><Translate>add</Translate></button> + </p> + </div> + {help} + {error && <p class="help is-danger"> {error} </p>} + {array.map((v, i) => <div key={i} class="tags has-addons mt-3 mb-0"> + <span class="tag is-medium is-info mb-0" style={{ maxWidth: '90%' }}>{v}</span> + <a class="tag is-medium is-danger is-delete mb-0" onClick={() => { + onChange(array.filter(f => f !== v) as any); + setCurrentValue(toStr(v)); + }} /> + </div> + )} + </div> + + </div> + </div>; +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx b/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx new file mode 100644 index 000000000..2771fe483 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputBoolean.tsx @@ -0,0 +1,72 @@ +/* + 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 { InputProps, useField } from "./useField"; + +interface Props<T> extends InputProps<T> { + name: T; + readonly?: boolean; + expand?: boolean; + threeState?: boolean; + toBoolean?: (v?: any) => boolean | undefined; + fromBoolean?: (s: boolean | undefined) => any; +} + +const defaultToBoolean = (f?: any): boolean | undefined => f || '' +const defaultFromBoolean = (v: boolean | undefined): any => v as any + + +export function InputBoolean<T>({ name, readonly, placeholder, tooltip, label, help, threeState, expand, fromBoolean = defaultFromBoolean, toBoolean = defaultToBoolean }: Props<keyof T>): VNode { + const { error, value, onChange } = useField<T>(name); + + const onCheckboxClick = (): void => { + const c = toBoolean(value) + if (c === false && threeState) return onChange(undefined as any) + return onChange(fromBoolean(!c)) + } + + return <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class={expand ? "control is-expanded" : "control"}> + <label class="b-checkbox checkbox"> + <input type="checkbox" class={toBoolean(value) === undefined ? "is-indeterminate" : ""} + checked={toBoolean(value)} + placeholder={placeholder} readonly={readonly} + name={String(name)} disabled={readonly} + onChange={onCheckboxClick} /> + <span class="check" /> + </label> + {help} + </p> + {error && <p class="help is-danger">{error}</p>} + </div> + </div> + </div>; +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx new file mode 100644 index 000000000..d3a46f483 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputCurrency.tsx @@ -0,0 +1,47 @@ +/* + 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 { ComponentChildren, h } from "preact"; +import { useConfigContext } from "../../context/config"; +import { Amount } from "../../declaration"; +import { InputWithAddon } from "./InputWithAddon"; +import { InputProps } from "./useField"; + +export interface Props<T> extends InputProps<T> { + expand?: boolean; + addonAfter?: ComponentChildren; + children?: ComponentChildren; + side?: ComponentChildren; +} + +export function InputCurrency<T>({ name, readonly, label, placeholder, help, tooltip, expand, addonAfter, children, side }: Props<keyof T>) { + const config = useConfigContext() + return <InputWithAddon<T> name={name} readonly={readonly} addonBefore={config.currency} + side={side} + label={label} placeholder={placeholder} help={help} tooltip={tooltip} + addonAfter={addonAfter} + inputType='number' expand={expand} + toStr={(v?: Amount) => v?.split(':')[1] || ''} + fromStr={(v: string) => !v ? '' : `${config.currency}:${v}`} + inputExtra={{ min: 0 }} + children={children} + /> +} + diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx new file mode 100644 index 000000000..77199527f --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx @@ -0,0 +1,159 @@ +/* + 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 { format } from "date-fns"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Translate, useTranslator } from "../../i18n"; +import { DatePicker } from "../picker/DatePicker"; +import { InputProps, useField } from "./useField"; + +export interface Props<T> extends InputProps<T> { + readonly?: boolean; + expand?: boolean; + //FIXME: create separated components InputDate and InputTimestamp + withTimestampSupport?: boolean; +} + +export function InputDate<T>({ + name, + readonly, + label, + placeholder, + help, + tooltip, + expand, + withTimestampSupport, +}: Props<keyof T>): VNode { + const [opened, setOpened] = useState(false); + const i18n = useTranslator(); + + const { error, required, value, onChange } = useField<T>(name); + + let strValue = ""; + if (!value) { + strValue = withTimestampSupport ? "unknown" : ""; + } else if (value instanceof Date) { + strValue = format(value, "yyyy/MM/dd"); + } else if (value.t_s) { + strValue = + value.t_s === "never" + ? withTimestampSupport + ? "never" + : "" + : format(new Date(value.t_s * 1000), "yyyy/MM/dd"); + } + + return ( + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <div class="field has-addons"> + <p + class={ + expand + ? "control is-expanded has-icons-right" + : "control has-icons-right" + } + > + <input + class="input" + type="text" + readonly + value={strValue} + placeholder={placeholder} + onClick={() => { + if (!readonly) setOpened(true); + }} + /> + {required && ( + <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> + </span> + )} + {help} + </p> + <div + class="control" + onClick={() => { + if (!readonly) setOpened(true); + }} + > + <a class="button is-static"> + <span class="icon"> + <i class="mdi mdi-calendar" /> + </span> + </a> + </div> + </div> + {error && <p class="help is-danger">{error}</p>} + </div> + + {!readonly && ( + <span + data-tooltip={ + withTimestampSupport + ? i18n`change value to unknown date` + : i18n`change value to empty` + } + > + <button + class="button is-info mr-3" + onClick={() => onChange(undefined as any)} + > + <Translate>clear</Translate> + </button> + </span> + )} + {withTimestampSupport && ( + <span data-tooltip={i18n`change value to never`}> + <button + class="button is-info" + onClick={() => onChange({ t_s: "never" } as any)} + > + <Translate>never</Translate> + </button> + </span> + )} + </div> + <DatePicker + opened={opened} + closeFunction={() => setOpened(false)} + dateReceiver={(d) => { + if (withTimestampSupport) { + onChange({ t_s: d.getTime() / 1000 } as any); + } else { + onChange(d as any); + } + }} + /> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx new file mode 100644 index 000000000..d5c208e25 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx @@ -0,0 +1,172 @@ +/* + 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 { intervalToDuration, formatDuration } from "date-fns"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Translate, useTranslator } from "../../i18n"; +import { SimpleModal } from "../modal"; +import { DurationPicker } from "../picker/DurationPicker"; +import { InputProps, useField } from "./useField"; + +export interface Props<T> extends InputProps<T> { + expand?: boolean; + readonly?: boolean; + withForever?: boolean; +} + +export function InputDuration<T>({ + name, + expand, + placeholder, + tooltip, + label, + help, + readonly, + withForever, +}: Props<keyof T>): VNode { + const [opened, setOpened] = useState(false); + const i18n = useTranslator(); + + const { error, required, value, onChange } = useField<T>(name); + let strValue = ""; + if (!value) { + strValue = ""; + } else if (value.d_us === "forever") { + strValue = i18n`forever`; + } else { + strValue = formatDuration( + intervalToDuration({ start: 0, end: value.d_us / 1000 }), + { + locale: { + formatDistance: (name, value) => { + switch (name) { + case "xMonths": + return i18n`${value}M`; + case "xYears": + return i18n`${value}Y`; + case "xDays": + return i18n`${value}d`; + case "xHours": + return i18n`${value}h`; + case "xMinutes": + return i18n`${value}min`; + case "xSeconds": + return i18n`${value}sec`; + } + }, + localize: { + day: () => "s", + month: () => "m", + ordinalNumber: () => "th", + dayPeriod: () => "p", + quarter: () => "w", + era: () => "e", + }, + }, + } + ); + } + + return ( + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && ( + <span class="icon" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <div class="field has-addons"> + <p class={expand ? "control is-expanded " : "control "}> + <input + class="input" + type="text" + readonly + value={strValue} + placeholder={placeholder} + onClick={() => { + if (!readonly) setOpened(true); + }} + /> + {required && ( + <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> + </span> + )} + {help} + </p> + <div + class="control" + onClick={() => { + if (!readonly) setOpened(true); + }} + > + <a class="button is-static"> + <span class="icon"> + <i class="mdi mdi-clock" /> + </span> + </a> + </div> + </div> + {error && <p class="help is-danger">{error}</p>} + </div> + {withForever && ( + <span data-tooltip={i18n`change value to never`}> + <button + class="button is-info mr-3" + onClick={() => onChange({ d_us: "forever" } as any)} + > + <Translate>forever</Translate> + </button> + </span> + )} + {!readonly && ( + <span data-tooltip={i18n`change value to empty`}> + <button + class="button is-info " + onClick={() => onChange(undefined as any)} + > + <Translate>clear</Translate> + </button> + </span> + )} + </div> + {opened && ( + <SimpleModal onCancel={() => setOpened(false)}> + <DurationPicker + days + hours + minutes + value={!value || value.d_us === "forever" ? 0 : value.d_us} + onChange={(v) => { + onChange({ d_us: v } as any); + }} + /> + </SimpleModal> + )} + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx b/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx new file mode 100644 index 000000000..8af9c7d96 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputGroup.tsx @@ -0,0 +1,66 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { useGroupField } from "./useGroupField"; + +export interface Props<T> { + name: T; + children: ComponentChildren; + label: ComponentChildren; + tooltip?: ComponentChildren; + alternative?: ComponentChildren; + fixed?: boolean; + initialActive?: boolean; +} + +export function InputGroup<T>({ name, label, children, tooltip, alternative, fixed, initialActive }: Props<keyof T>): VNode { + const [active, setActive] = useState(initialActive || fixed); + const group = useGroupField<T>(name); + + return <div class="card"> + <header class="card-header"> + <p class="card-header-title"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + {group?.hasError && <span class="icon has-text-danger" data-tooltip={tooltip}> + <i class="mdi mdi-alert" /> + </span>} + </p> + { !fixed && <button class="card-header-icon" aria-label="more options" onClick={(): void => setActive(!active)}> + <span class="icon"> + {active ? + <i class="mdi mdi-arrow-up" /> : + <i class="mdi mdi-arrow-down" />} + </span> + </button> } + </header> + {active ? <div class="card-content"> + {children} + </div> : ( + alternative ? <div class="card-content"> + {alternative} + </div> : undefined + )} + </div>; +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx new file mode 100644 index 000000000..6cc9b9dcc --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputImage.tsx @@ -0,0 +1,95 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; +import { useRef, useState } from "preact/hooks"; +import emptyImage from "../../assets/empty.png"; +import { Translate } from "../../i18n"; +import { MAX_IMAGE_SIZE as MAX_IMAGE_UPLOAD_SIZE } from "../../utils/constants"; +import { InputProps, useField } from "./useField"; + +export interface Props<T> extends InputProps<T> { + expand?: boolean; + addonAfter?: ComponentChildren; + children?: ComponentChildren; +} + +export function InputImage<T>({ name, readonly, placeholder, tooltip, label, help, children, expand }: Props<keyof T>): VNode { + const { error, value, onChange } = useField<T>(name); + + const image = useRef<HTMLInputElement>(null) + + const [sizeError, setSizeError] = useState(false) + + return <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class={expand ? "control is-expanded" : "control"}> + {value && + <img src={value} style={{ width: 200, height: 200 }} onClick={() => image.current?.click()} /> + } + <input + ref={image} style={{ display: 'none' }} + type="file" name={String(name)} + placeholder={placeholder} readonly={readonly} + onChange={e => { + const f: FileList | null = e.currentTarget.files + if (!f || f.length != 1) { + return onChange(undefined!) + } + if (f[0].size > MAX_IMAGE_UPLOAD_SIZE) { + setSizeError(true) + return onChange(undefined!) + } + setSizeError(false) + return f[0].arrayBuffer().then(b => { + const b64 = btoa( + new Uint8Array(b) + .reduce((data, byte) => data + String.fromCharCode(byte), '') + ) + return onChange(`data:${f[0].type};base64,${b64}` as any) + }) + }} /> + {help} + {children} + </p> + {error && <p class="help is-danger">{error}</p>} + {sizeError && <p class="help is-danger"> + <Translate>Image should be smaller than 1 MB</Translate> + </p>} + {!value && + <button class="button" onClick={() => image.current?.click()} ><Translate>Add</Translate></button> + } + {value && + <button class="button" onClick={() => onChange(undefined!)} ><Translate>Remove</Translate></button> + } + </div> + </div> + </div> +} + diff --git a/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx b/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx new file mode 100644 index 000000000..12755f47a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputLocation.tsx @@ -0,0 +1,43 @@ +/* + 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 { Fragment, h } from "preact"; +import { useTranslator } from "../../i18n"; +import { Input } from "./Input"; + +export function InputLocation({name}:{name:string}) { + const i18n = useTranslator() + return <> + <Input name={`${name}.country`} label={i18n`Country`} /> + <Input name={`${name}.address_lines`} inputType="multiline" + label={i18n`Address`} + toStr={(v: string[] | undefined) => !v ? '' : v.join('\n')} + fromStr={(v: string) => v.split('\n')} + /> + <Input name={`${name}.building_number`} label={i18n`Building number`} /> + <Input name={`${name}.building_name`} label={i18n`Building name`} /> + <Input name={`${name}.street`} label={i18n`Street`} /> + <Input name={`${name}.post_code`} label={i18n`Post code`} /> + <Input name={`${name}.town_location`} label={i18n`Town location`} /> + <Input name={`${name}.town`} label={i18n`Town`} /> + <Input name={`${name}.district`} label={i18n`District`} /> + <Input name={`${name}.country_subdivision`} label={i18n`Country subdivision`} /> + </> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx b/packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx new file mode 100644 index 000000000..046cda59e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputNumber.tsx @@ -0,0 +1,42 @@ +/* + 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 { ComponentChildren, h } from "preact"; +import { InputWithAddon } from "./InputWithAddon"; +import { InputProps } from "./useField"; + +export interface Props<T> extends InputProps<T> { + readonly?: boolean; + expand?: boolean; + side?: ComponentChildren; + children?: ComponentChildren; +} + +export function InputNumber<T>({ name, readonly, placeholder, tooltip, label, help, expand, children, side }: Props<keyof T>) { + return <InputWithAddon<T> name={name} readonly={readonly} + fromStr={(v) => !v ? undefined : parseInt(v, 10) } toStr={(v) => `${v}`} + inputType='number' expand={expand} + label={label} placeholder={placeholder} help={help} tooltip={tooltip} + inputExtra={{ min: 0 }} + children={children} + side={side} + /> +} + diff --git a/packages/merchant-backoffice-ui/src/components/form/InputPayto.tsx b/packages/merchant-backoffice-ui/src/components/form/InputPayto.tsx new file mode 100644 index 000000000..44252317e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputPayto.tsx @@ -0,0 +1,39 @@ +/* + 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 { InputArray } from "./InputArray"; +import { PAYTO_REGEX } from "../../utils/constants"; +import { InputProps } from "./useField"; + +export type Props<T> = InputProps<T>; + +const PAYTO_START_REGEX = /^payto:\/\// + +export function InputPayto<T>({ name, readonly, placeholder, tooltip, label, help }: Props<keyof T>): VNode { + return <InputArray<T> name={name} readonly={readonly} + addonBefore="payto://" + label={label} placeholder={placeholder} help={help} tooltip={tooltip} + isValid={(v) => v && PAYTO_REGEX.test(v) } + toStr={(v?: string) => !v ? '': v.replace(PAYTO_START_REGEX, '')} + fromStr={(v: string) => `payto://${v}` } + /> +} + diff --git a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx new file mode 100644 index 000000000..9cfef07cf --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx @@ -0,0 +1,392 @@ +/* + 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, Fragment } from "preact"; +import { useCallback, useState } from "preact/hooks"; +import { Translate, Translator, useTranslator } from "../../i18n"; +import { COUNTRY_TABLE } from "../../utils/constants"; +import { FormErrors, FormProvider } from "./FormProvider"; +import { Input } from "./Input"; +import { InputGroup } from "./InputGroup"; +import { InputSelector } from "./InputSelector"; +import { InputProps, useField } from "./useField"; + +export interface Props<T> extends InputProps<T> { + isValid?: (e: any) => boolean; +} + +// https://datatracker.ietf.org/doc/html/rfc8905 +type Entity = { + // iban, bitcoin, x-taler-bank. it defined the format + target: string; + // path1 if the first field to be used + path1: string; + // path2 if the second field to be used, optional + path2?: string; + // options of the payto uri + options: { + "receiver-name"?: string; + sender?: string; + message?: string; + amount?: string; + instruction?: string; + [name: string]: string | undefined; + }; +}; + +function isEthereumAddress(address: string) { + if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) { + return false; + } else if ( + /^(0x|0X)?[0-9a-f]{40}$/.test(address) || + /^(0x|0X)?[0-9A-F]{40}$/.test(address) + ) { + return true; + } + return checkAddressChecksum(address); +} + +function checkAddressChecksum(address: string) { + //TODO implement ethereum checksum + return true; +} + +function validateBitcoin(addr: string, i18n: Translator): string | undefined { + try { + const valid = /^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$/.test(addr); + if (valid) return undefined; + } catch (e) { + console.log(e); + } + return i18n`This is not a valid bitcoin address.`; +} + +function validateEthereum(addr: string, i18n: Translator): string | undefined { + try { + const valid = isEthereumAddress(addr); + if (valid) return undefined; + } catch (e) { + console.log(e); + } + return i18n`This is not a valid Ethereum address.`; +} + +/** + * An IBAN is validated by converting it into an integer and performing a + * basic mod-97 operation (as described in ISO 7064) on it. + * If the IBAN is valid, the remainder equals 1. + * + * The algorithm of IBAN validation is as follows: + * 1.- Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid + * 2.- Move the four initial characters to the end of the string + * 3.- Replace each letter in the string with two digits, thereby expanding the string, where A = 10, B = 11, ..., Z = 35 + * 4.- Interpret the string as a decimal integer and compute the remainder of that number on division by 97 + * + * If the remainder is 1, the check digit test is passed and the IBAN might be valid. + * + */ +function validateIBAN(iban: string, i18n: Translator): string | undefined { + // Check total length + if (iban.length < 4) + return i18n`IBAN numbers usually have more that 4 digits`; + if (iban.length > 34) + return i18n`IBAN numbers usually have less that 34 digits`; + + const A_code = "A".charCodeAt(0); + const Z_code = "Z".charCodeAt(0); + const IBAN = iban.toUpperCase(); + // check supported country + const code = IBAN.substr(0, 2); + const found = code in COUNTRY_TABLE; + if (!found) return i18n`IBAN country code not found`; + + // 2.- Move the four initial characters to the end of the string + const step2 = IBAN.substr(4) + iban.substr(0, 4); + const step3 = Array.from(step2) + .map((letter) => { + const code = letter.charCodeAt(0); + if (code < A_code || code > Z_code) return letter; + return `${letter.charCodeAt(0) - "A".charCodeAt(0) + 10}`; + }) + .join(""); + + function calculate_iban_checksum(str: string): number { + const numberStr = str.substr(0, 5); + const rest = str.substr(5); + const number = parseInt(numberStr, 10); + const result = number % 97; + if (rest.length > 0) { + return calculate_iban_checksum(`${result}${rest}`); + } + return result; + } + + const checksum = calculate_iban_checksum(step3); + if (checksum !== 1) return i18n`IBAN number is not valid, checksum is wrong`; + return undefined; +} + +// const targets = ['ach', 'bic', 'iban', 'upi', 'bitcoin', 'ilp', 'void', 'x-taler-bank'] +const targets = [ + "Choose one...", + "iban", + "x-taler-bank", + "bitcoin", + "ethereum", +]; +const noTargetValue = targets[0]; +const defaultTarget = { target: noTargetValue, options: {} }; + +function undefinedIfEmpty<T>(obj: T): T | undefined { + return Object.keys(obj).some((k) => (obj as any)[k] !== undefined) + ? obj + : undefined; +} + +export function InputPaytoForm<T>({ + name, + readonly, + label, + tooltip, +}: Props<keyof T>): VNode { + const { value: paytos, onChange } = useField<T>(name); + + const [value, valueHandler] = useState<Partial<Entity>>(defaultTarget); + + let payToPath; + if (value.target === "iban" && value.path1) { + payToPath = `/${value.path1.toUpperCase()}`; + } else if (value.path1) { + if (value.path2) { + payToPath = `/${value.path1}/${value.path2}`; + } else { + payToPath = `/${value.path1}`; + } + } + const i18n = useTranslator(); + + const ops = value.options!; + const url = tryUrl(`payto://${value.target}${payToPath}`); + if (url) { + Object.keys(ops).forEach((opt_key) => { + const opt_value = ops[opt_key]; + if (opt_value) url.searchParams.set(opt_key, opt_value); + }); + } + const paytoURL = !url ? "" : url.toString(); + + const errors: FormErrors<Entity> = { + target: value.target === noTargetValue ? i18n`required` : undefined, + path1: !value.path1 + ? i18n`required` + : value.target === "iban" + ? validateIBAN(value.path1, i18n) + : value.target === "bitcoin" + ? validateBitcoin(value.path1, i18n) + : value.target === "ethereum" + ? validateEthereum(value.path1, i18n) + : undefined, + path2: + value.target === "x-taler-bank" + ? !value.path2 + ? i18n`required` + : undefined + : undefined, + options: undefinedIfEmpty({ + "receiver-name": !value.options?.["receiver-name"] + ? i18n`required` + : undefined, + }), + }; + + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined + ); + + const submit = useCallback((): void => { + const alreadyExists = + paytos.findIndex((x: string) => x === paytoURL) !== -1; + if (!alreadyExists) { + onChange([paytoURL, ...paytos] as any); + } + valueHandler(defaultTarget); + }, [value]); + + //FIXME: translating plural singular + return ( + <InputGroup name="payto" label={label} fixed tooltip={tooltip}> + <FormProvider<Entity> + name="tax" + errors={errors} + object={value} + valueHandler={valueHandler} + > + <InputSelector<Entity> + name="target" + label={i18n`Target type`} + tooltip={i18n`Method to use for wire transfer`} + values={targets} + toStr={(v) => (v === noTargetValue ? i18n`Choose one...` : v)} + /> + + {value.target === "ach" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Routing`} + tooltip={i18n`Routing number.`} + /> + <Input<Entity> + name="path2" + label={i18n`Account`} + tooltip={i18n`Account number.`} + /> + </Fragment> + )} + {value.target === "bic" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Code`} + tooltip={i18n`Business Identifier Code.`} + /> + </Fragment> + )} + {value.target === "iban" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Account`} + tooltip={i18n`Bank Account Number.`} + inputExtra={{ style: { textTransform: "uppercase" } }} + /> + </Fragment> + )} + {value.target === "upi" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Account`} + tooltip={i18n`Unified Payment Interface.`} + /> + </Fragment> + )} + {value.target === "bitcoin" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Address`} + tooltip={i18n`Bitcoin protocol.`} + /> + </Fragment> + )} + {value.target === "ethereum" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Address`} + tooltip={i18n`Ethereum protocol.`} + /> + </Fragment> + )} + {value.target === "ilp" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Address`} + tooltip={i18n`Interledger protocol.`} + /> + </Fragment> + )} + {value.target === "void" && <Fragment />} + {value.target === "x-taler-bank" && ( + <Fragment> + <Input<Entity> + name="path1" + label={i18n`Host`} + tooltip={i18n`Bank host.`} + /> + <Input<Entity> + name="path2" + label={i18n`Account`} + tooltip={i18n`Bank account.`} + /> + </Fragment> + )} + + {value.target !== noTargetValue && ( + <Input + name="options.receiver-name" + label={i18n`Name`} + tooltip={i18n`Bank account owner's name.`} + /> + )} + + <div class="field is-horizontal"> + <div class="field-label is-normal" /> + <div class="field-body" style={{ display: "block" }}> + {paytos.map((v: any, i: number) => ( + <div + key={i} + class="tags has-addons mt-3 mb-0 mr-3" + style={{ flexWrap: "nowrap" }} + > + <span + class="tag is-medium is-info mb-0" + style={{ maxWidth: "90%" }} + > + {v} + </span> + <a + class="tag is-medium is-danger is-delete mb-0" + onClick={() => { + onChange(paytos.filter((f: any) => f !== v) as any); + }} + /> + </div> + ))} + {!paytos.length && i18n`No accounts yet.`} + </div> + </div> + + {value.target !== noTargetValue && ( + <div class="buttons is-right mt-5"> + <button + class="button is-info" + data-tooltip={i18n`add tax to the tax list`} + disabled={hasErrors} + onClick={submit} + > + <Translate>Add</Translate> + </button> + </div> + )} + </FormProvider> + </InputGroup> + ); +} + +function tryUrl(s: string): URL | undefined { + try { + return new URL(s); + } catch (e) { + return undefined; + } +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx new file mode 100644 index 000000000..51f84fd12 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx @@ -0,0 +1,139 @@ +/* + 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 { useState } from "preact/hooks"; +import emptyImage from "../../assets/empty.png"; +import { MerchantBackend, WithId } from "../../declaration"; +import { useInstanceProducts } from "../../hooks/product"; +import { Translate, useTranslator } from "../../i18n"; +import { FormErrors, FormProvider } from "./FormProvider"; +import { InputWithAddon } from "./InputWithAddon"; + +type Entity = MerchantBackend.Products.ProductDetail & WithId + +export interface Props { + selected?: Entity; + onChange: (p?: Entity) => void; + products: (MerchantBackend.Products.ProductDetail & WithId)[], +} + +interface ProductSearch { + name: string; +} + +export function InputSearchProduct({ selected, onChange, products }: Props): VNode { + const [prodForm, setProdName] = useState<Partial<ProductSearch>>({ name: '' }) + + const errors: FormErrors<ProductSearch> = { + name: undefined + } + const i18n = useTranslator() + + + if (selected) { + return <article class="media"> + <figure class="media-left"> + <p class="image is-128x128"> + <img src={selected.image ? selected.image : emptyImage} /> + </p> + </figure> + <div class="media-content"> + <div class="content"> + <p class="media-meta"><Translate>Product id</Translate>: <b>{selected.id}</b></p> + <p><Translate>Description</Translate>: {selected.description}</p> + <div class="buttons is-right mt-5"> + <button class="button is-info" onClick={() => onChange(undefined)}>clear</button> + </div> + </div> + </div> + </article> + } + + return <FormProvider<ProductSearch> errors={errors} object={prodForm} valueHandler={setProdName} > + + <InputWithAddon<ProductSearch> + name="name" + label={i18n`Product`} + tooltip={i18n`search products by it's description or id`} + addonAfter={<span class="icon" ><i class="mdi mdi-magnify" /></span>} + > + <div> + <ProductList + name={prodForm.name} + list={products} + onSelect={(p) => { + setProdName({ name: '' }) + onChange(p) + }} + /> + </div> + </InputWithAddon> + + </FormProvider> + +} + +interface ProductListProps { + name?: string; + onSelect: (p: MerchantBackend.Products.ProductDetail & WithId) => void; + list: (MerchantBackend.Products.ProductDetail & WithId)[] +} + +function ProductList({ name, onSelect, list }: ProductListProps) { + if (!name) { + /* FIXME + this BR is added to occupy the space that will be added when the + dropdown appears + */ + return <div ><br /></div> + } + const filtered = list.filter(p => p.id.includes(name) || p.description.includes(name)) + + return <div class="dropdown is-active"> + <div class="dropdown-menu" id="dropdown-menu" role="menu" style={{ minWidth: '20rem' }}> + <div class="dropdown-content"> + {!filtered.length ? + <div class="dropdown-item" > + <Translate>no products found with that description</Translate> + </div> : + filtered.map(p => ( + <div key={p.id} class="dropdown-item" onClick={() => onSelect(p)} style={{ cursor: 'pointer' }}> + <article class="media"> + <div class="media-left"> + <div class="image" style={{ minWidth: 64 }}><img src={p.image ? p.image : emptyImage} style={{ width: 64, height: 64 }} /></div> + </div> + <div class="media-content"> + <div class="content"> + <p> + <strong>{p.id}</strong> <small>{p.price}</small> + <br /> + {p.description} + </p> + </div> + </div> + </article> + </div> + )) + } + </div> + </div> + </div> +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx new file mode 100644 index 000000000..1990eeeae --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputSecured.stories.tsx @@ -0,0 +1,55 @@ +/* + 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 { useState } from 'preact/hooks'; +import { FormProvider } from "./FormProvider"; +import { InputSecured } from './InputSecured'; + +export default { + title: 'Components/Form/InputSecured', + component: InputSecured, +}; + +type T = { auth_token: string | null } + +export const InitialValueEmpty = (): VNode => { + const [state, setState] = useState<Partial<T>>({ auth_token: '' }) + return <FormProvider<T> object={state} errors={{}} valueHandler={setState}> + Initial value: '' + <InputSecured<T> name="auth_token" label="Access token" /> + </FormProvider> +} + +export const InitialValueToken = (): VNode => { + const [state, setState] = useState<Partial<T>>({ auth_token: 'token' }) + return <FormProvider<T> object={state} errors={{}} valueHandler={setState}> + <InputSecured<T> name="auth_token" label="Access token" /> + </FormProvider> +} + +export const InitialValueNull = (): VNode => { + const [state, setState] = useState<Partial<T>>({ auth_token: null }) + return <FormProvider<T> object={state} errors={{}} valueHandler={setState}> + Initial value: '' + <InputSecured<T> name="auth_token" label="Access token" /> + </FormProvider> +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx new file mode 100644 index 000000000..c9b0f43b9 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputSecured.tsx @@ -0,0 +1,119 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Translate, useTranslator } from "../../i18n"; +import { InputProps, useField } from "./useField"; + +export type Props<T> = InputProps<T>; + +const TokenStatus = ({ prev, post }: any) => { + if ((prev === undefined || prev === null) && (post === undefined || post === null)) + return null + return (prev === post) ? null : ( + post === null ? + <span class="tag is-danger is-align-self-center ml-2"><Translate>Deleting</Translate></span> : + <span class="tag is-warning is-align-self-center ml-2"><Translate>Changing</Translate></span> + ) +} + +export function InputSecured<T>({ name, readonly, placeholder, tooltip, label, help }: Props<keyof T>): VNode { + const { error, value, initial, onChange, toStr, fromStr } = useField<T>(name); + + const [active, setActive] = useState(false); + const [newValue, setNuewValue] = useState("") + + const i18n = useTranslator() + + return <Fragment> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + {!active ? + <Fragment> + <div class="field has-addons"> + <button class="button" + onClick={(): void => { setActive(!active); }} > + <div class="icon is-left"><i class="mdi mdi-lock-reset" /></div> + <span><Translate>Manage access token</Translate></span> + </button> + <TokenStatus prev={initial} post={value} /> + </div> + </Fragment> : + <Fragment> + <div class="field has-addons"> + <div class="control"> + <a class="button is-static">secret-token:</a> + </div> + <div class="control is-expanded"> + <input class="input" type="text" + placeholder={placeholder} readonly={readonly || !active} + disabled={readonly || !active} + name={String(name)} value={newValue} + onInput={(e): void => { + setNuewValue(e.currentTarget.value) + }} /> + {help} + </div> + <div class="control"> + <button class="button is-info" disabled={fromStr(newValue) === value} onClick={(): void => { onChange(fromStr(newValue)); setActive(!active); setNuewValue(""); }} > + <div class="icon is-left"><i class="mdi mdi-lock-outline" /></div> + <span><Translate>Update</Translate></span> + </button> + </div> + </div> + </Fragment> + } + {error ? <p class="help is-danger">{error}</p> : null} + </div> + </div> + {active && + <div class="field is-horizontal"> + <div class="field-body is-flex-grow-3"> + <div class="level" style={{ width: '100%' }}> + <div class="level-right is-flex-grow-1"> + <div class="level-item"> + <button class="button is-danger" disabled={null === value || undefined === value} onClick={(): void => { onChange(null!); setActive(!active); setNuewValue(""); }} > + <div class="icon is-left"><i class="mdi mdi-lock-open-variant" /></div> + <span><Translate>Remove</Translate></span> + </button> + </div> + <div class="level-item"> + <button class="button " onClick={(): void => { onChange(initial!); setActive(!active); setNuewValue(""); }} > + <div class="icon is-left"><i class="mdi mdi-lock-open-variant" /></div> + <span><Translate>Cancel</Translate></span> + </button> + </div> + </div> + + </div> + </div> + </div> + } + </Fragment >; +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx new file mode 100644 index 000000000..86f4de756 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputSelector.tsx @@ -0,0 +1,86 @@ +/* + 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 { InputProps, useField } from "./useField"; + +interface Props<T> extends InputProps<T> { + readonly?: boolean; + expand?: boolean; + values: string[]; + toStr?: (v?: any) => string; + fromStr?: (s: string) => any; +} + +const defaultToString = (f?: any): string => f || ""; +const defaultFromString = (v: string): any => v as any; + +export function InputSelector<T>({ + name, + readonly, + expand, + placeholder, + tooltip, + label, + help, + values, + toStr = defaultToString, +}: Props<keyof T>): VNode { + const { error, value, onChange } = useField<T>(name); + + return ( + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && ( + <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span> + )} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class={expand ? "control is-expanded select" : "control select"}> + <select + class={error ? "select is-danger" : "select"} + name={String(name)} + disabled={readonly} + readonly={readonly} + onChange={(e) => { + onChange(e.currentTarget.value as any); + }} + > + {placeholder && <option>{placeholder}</option>} + {values.map((v, i) => ( + <option key={i} value={v} selected={value === v}> + {toStr(v)} + </option> + ))} + </select> + {help} + </p> + {error && <p class="help is-danger">{error}</p>} + </div> + </div> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputStock.stories.tsx b/packages/merchant-backoffice-ui/src/components/form/InputStock.stories.tsx new file mode 100644 index 000000000..63c7e4131 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputStock.stories.tsx @@ -0,0 +1,162 @@ +/* + 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 { addDays } from "date-fns"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { FormProvider } from "./FormProvider"; +import { InputStock, Stock } from "./InputStock"; + +export default { + title: "Components/Form/InputStock", + component: InputStock, +}; + +type T = { stock?: Stock }; + +export const CreateStockEmpty = () => { + const [state, setState] = useState<Partial<T>>({}); + return ( + <FormProvider<T> + name="product" + object={state} + errors={{}} + valueHandler={setState} + > + <InputStock<T> name="stock" label="Stock" /> + <div> + <pre>{JSON.stringify(state, undefined, 2)}</pre> + </div> + </FormProvider> + ); +}; + +export const CreateStockUnknownRestock = () => { + const [state, setState] = useState<Partial<T>>({ + stock: { + current: 10, + lost: 0, + sold: 0, + }, + }); + return ( + <FormProvider<T> + name="product" + object={state} + errors={{}} + valueHandler={setState} + > + <InputStock<T> name="stock" label="Stock" /> + <div> + <pre>{JSON.stringify(state, undefined, 2)}</pre> + </div> + </FormProvider> + ); +}; + +export const CreateStockNoRestock = () => { + const [state, setState] = useState<Partial<T>>({ + stock: { + current: 10, + lost: 0, + sold: 0, + nextRestock: { t_s: "never" }, + }, + }); + return ( + <FormProvider<T> + name="product" + object={state} + errors={{}} + valueHandler={setState} + > + <InputStock<T> name="stock" label="Stock" /> + <div> + <pre>{JSON.stringify(state, undefined, 2)}</pre> + </div> + </FormProvider> + ); +}; + +export const CreateStockWithRestock = () => { + const [state, setState] = useState<Partial<T>>({ + stock: { + current: 15, + lost: 0, + sold: 0, + nextRestock: { t_s: addDays(new Date(), 1).getTime() / 1000 }, + }, + }); + return ( + <FormProvider<T> + name="product" + object={state} + errors={{}} + valueHandler={setState} + > + <InputStock<T> name="stock" label="Stock" /> + <div> + <pre>{JSON.stringify(state, undefined, 2)}</pre> + </div> + </FormProvider> + ); +}; + +export const UpdatingProductWithManagedStock = () => { + const [state, setState] = useState<Partial<T>>({ + stock: { + current: 100, + lost: 0, + sold: 0, + nextRestock: { t_s: addDays(new Date(), 1).getTime() / 1000 }, + }, + }); + return ( + <FormProvider<T> + name="product" + object={state} + errors={{}} + valueHandler={setState} + > + <InputStock<T> name="stock" label="Stock" alreadyExist /> + <div> + <pre>{JSON.stringify(state, undefined, 2)}</pre> + </div> + </FormProvider> + ); +}; + +export const UpdatingProductWithInfiniteStock = () => { + const [state, setState] = useState<Partial<T>>({}); + return ( + <FormProvider<T> + name="product" + object={state} + errors={{}} + valueHandler={setState} + > + <InputStock<T> name="stock" label="Stock" alreadyExist /> + <div> + <pre>{JSON.stringify(state, undefined, 2)}</pre> + </div> + </FormProvider> + ); +}; diff --git a/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx new file mode 100644 index 000000000..158f44192 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputStock.tsx @@ -0,0 +1,171 @@ +/* + 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 { Fragment, h } from "preact"; +import { MerchantBackend, Timestamp } from "../../declaration"; +import { InputProps, useField } from "./useField"; +import { FormProvider, FormErrors } from "./FormProvider"; +import { useLayoutEffect, useState } from "preact/hooks"; +import { Input } from "./Input"; +import { InputGroup } from "./InputGroup"; +import { InputNumber } from "./InputNumber"; +import { InputDate } from "./InputDate"; +import { Translate, useTranslator } from "../../i18n"; +import { InputLocation } from "./InputLocation"; + +export interface Props<T> extends InputProps<T> { + alreadyExist?: boolean; +} + + +type Entity = Stock + +export interface Stock { + current: number; + lost: number; + sold: number; + address?: MerchantBackend.Location; + nextRestock?: Timestamp; +} + +interface StockDelta { + incoming: number; + lost: number; +} + + +export function InputStock<T>({ name, tooltip, label, alreadyExist }: Props<keyof T>) { + const { error, value, onChange } = useField<T>(name); + + const [errors, setErrors] = useState<FormErrors<Entity>>({}) + + const [formValue, valueHandler] = useState<Partial<Entity>>(value) + const [addedStock, setAddedStock] = useState<StockDelta>({ incoming: 0, lost: 0 }) + const i18n = useTranslator() + + + useLayoutEffect(() => { + if (!formValue) { + onChange(undefined as any) + } else { + onChange({ + ...formValue, + current: (formValue?.current || 0) + addedStock.incoming, + lost: (formValue?.lost || 0) + addedStock.lost + } as any) + } + }, [formValue, addedStock]) + + if (!formValue) { + return <Fragment> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field has-addons"> + {!alreadyExist ? + <button class="button" + data-tooltip={i18n`click here to configure the stock of the product, leave it as is and the backend will not control stock`} + onClick={(): void => { valueHandler({ current: 0, lost: 0, sold: 0 } as Stock as any); }} > + <span><Translate>Manage stock</Translate></span> + </button> : <button class="button" + data-tooltip={i18n`this product has been configured without stock control`} + disabled > + <span><Translate>Infinite</Translate></span> + </button> + } + </div> + </div> + </div> + </Fragment > + } + + const currentStock = (formValue.current || 0) - (formValue.lost || 0) - (formValue.sold || 0) + + const stockAddedErrors: FormErrors<typeof addedStock> = { + lost: currentStock + addedStock.incoming < addedStock.lost ? + i18n`lost cannot be greater than current and incoming (max ${currentStock + addedStock.incoming})` + : undefined + } + + // const stockUpdateDescription = stockAddedErrors.lost ? '' : ( + // !!addedStock.incoming || !!addedStock.lost ? + // i18n`current stock will change from ${currentStock} to ${currentStock + addedStock.incoming - addedStock.lost}` : + // i18n`current stock will stay at ${currentStock}` + // ) + + return <Fragment> + <div class="card"> + <header class="card-header"> + <p class="card-header-title"> + {label} + {tooltip && <span class="icon" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </p> + </header> + <div class="card-content"> + <FormProvider<Entity> name="stock" errors={errors} object={formValue} valueHandler={valueHandler}> + {alreadyExist ? <Fragment> + + <FormProvider name="added" errors={stockAddedErrors} object={addedStock} valueHandler={setAddedStock as any}> + <InputNumber name="incoming" label={i18n`Incoming`} /> + <InputNumber name="lost" label={i18n`Lost`} /> + </FormProvider> + + {/* <div class="field is-horizontal"> + <div class="field-label is-normal" /> + <div class="field-body is-flex-grow-3"> + <div class="field"> + {stockUpdateDescription} + </div> + </div> + </div> */} + + </Fragment> : <InputNumber<Entity> name="current" + label={i18n`Current`} + side={ + <button class="button is-danger" + data-tooltip={i18n`remove stock control for this product`} + onClick={(): void => { valueHandler(undefined as any) }} > + <span><Translate>without stock</Translate></span> + </button> + } + />} + + <InputDate<Entity> name="nextRestock" label={i18n`Next restock`} withTimestampSupport /> + + <InputGroup<Entity> name="address" label={i18n`Delivery address`}> + <InputLocation name="address" /> + </InputGroup> + </FormProvider> + </div> + </div> + </Fragment> +} + // ( + + diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx new file mode 100644 index 000000000..507a61242 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx @@ -0,0 +1,97 @@ +/* + 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 { useCallback, useState } from "preact/hooks"; +import * as yup from 'yup'; +import { MerchantBackend } from "../../declaration"; +import { Translate, useTranslator } from "../../i18n"; +import { TaxSchema as schema } from '../../schemas'; +import { FormErrors, FormProvider } from "./FormProvider"; +import { Input } from "./Input"; +import { InputGroup } from "./InputGroup"; +import { InputProps, useField } from "./useField"; + +export interface Props<T> extends InputProps<T> { + isValid?: (e: any) => boolean; +} + +type Entity = MerchantBackend.Tax +export function InputTaxes<T>({ name, readonly, label }: Props<keyof T>): VNode { + const { value: taxes, onChange, } = useField<T>(name); + + const [value, valueHandler] = useState<Partial<Entity>>({}) + // const [errors, setErrors] = useState<FormErrors<Entity>>({}) + + let errors: FormErrors<Entity> = {} + + try { + schema.validateSync(value, { abortEarly: false }) + } catch (err) { + if (err instanceof yup.ValidationError) { + const yupErrors = err.inner as yup.ValidationError[] + errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {}) + } + } + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + + const submit = useCallback((): void => { + onChange([value as any, ...taxes] as any) + valueHandler({}) + }, [value]) + + const i18n = useTranslator() + + //FIXME: translating plural singular + return ( + <InputGroup name="tax" label={label} alternative={taxes.length > 0 && <p>This product has {taxes.length} applicable taxes configured.</p>}> + <FormProvider<Entity> name="tax" errors={errors} object={value} valueHandler={valueHandler} > + + <div class="field is-horizontal"> + <div class="field-label is-normal" /> + <div class="field-body" style={{ display: 'block' }}> + {taxes.map((v: any, i: number) => <div key={i} class="tags has-addons mt-3 mb-0 mr-3" style={{ flexWrap: 'nowrap' }}> + <span class="tag is-medium is-info mb-0" style={{ maxWidth: '90%' }}><b>{v.tax}</b>: {v.name}</span> + <a class="tag is-medium is-danger is-delete mb-0" onClick={() => { + onChange(taxes.filter((f: any) => f !== v) as any); + valueHandler(v); + }} /> + </div> + )} + {!taxes.length && i18n`No taxes configured for this product.`} + </div> + </div> + + <Input<Entity> name="tax" label={i18n`Amount`} tooltip={i18n`Taxes can be in currencies that differ from the main currency used by the merchant.`}> + <Translate>Enter currency and value separated with a colon, e.g. "USD:2.3".</Translate> + </Input> + + <Input<Entity> name="name" label={i18n`Description`} tooltip={i18n`Legal name of the tax, e.g. VAT or import duties.`} /> + + <div class="buttons is-right mt-5"> + <button class="button is-info" + data-tooltip={i18n`add tax to the tax list`} + disabled={hasErrors} + onClick={submit}><Translate>Add</Translate></button> + </div> + </FormProvider> + </InputGroup> + ) +} diff --git a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx new file mode 100644 index 000000000..a16ebc2e9 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx @@ -0,0 +1,77 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; +import { InputProps, useField } from "./useField"; + +export interface Props<T> extends InputProps<T> { + expand?: boolean; + inputType?: 'text' | 'number'; + addonBefore?: ComponentChildren; + addonAfter?: ComponentChildren; + toStr?: (v?: any) => string; + fromStr?: (s: string) => any; + inputExtra?: any, + children?: ComponentChildren, + side?: ComponentChildren; +} + +const defaultToString = (f?: any): string => f || '' +const defaultFromString = (v: string): any => v as any + +export function InputWithAddon<T>({ name, readonly, addonBefore, children, expand, label, placeholder, help, tooltip, inputType, inputExtra, side, addonAfter, toStr = defaultToString, fromStr = defaultFromString }: Props<keyof T>): VNode { + const { error, value, onChange, required } = useField<T>(name); + + return <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <div class="field has-addons"> + {addonBefore && <div class="control"> + <a class="button is-static">{addonBefore}</a> + </div>} + <p class={`control${expand ? " is-expanded" :""}${required ? " has-icons-right" : ''}`}> + <input {...(inputExtra || {})} class={error ? "input is-danger" : "input"} type={inputType} + placeholder={placeholder} readonly={readonly} + name={String(name)} value={toStr(value)} + onChange={(e): void => onChange(fromStr(e.currentTarget.value))} /> + {required && <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> + </span>} + {help} + {children} + </p> + {addonAfter && <div class="control"> + <a class="button is-static">{addonAfter}</a> + </div>} + </div> + {error && <p class="help is-danger">{error}</p>} + </div> + {side} + </div> + </div>; +} diff --git a/packages/merchant-backoffice-ui/src/components/form/TextField.tsx b/packages/merchant-backoffice-ui/src/components/form/TextField.tsx new file mode 100644 index 000000000..2579a27b2 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/TextField.tsx @@ -0,0 +1,53 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; +import { useField, InputProps } from "./useField"; + +interface Props<T> extends InputProps<T> { + inputType?: 'text' | 'number' | 'multiline' | 'password'; + expand?: boolean; + side?: ComponentChildren; + children: ComponentChildren; +} + +export function TextField<T>({ name, tooltip, label, expand, help, children, side}: Props<keyof T>): VNode { + const { error } = useField<T>(name); + return <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + {label} + {tooltip && <span class="icon has-tooltip-right" data-tooltip={tooltip}> + <i class="mdi mdi-information" /> + </span>} + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class={expand ? "control is-expanded has-icons-right" : "control has-icons-right"}> + {children} + {help} + </p> + {error && <p class="help is-danger">{error}</p>} + </div> + {side} + </div> + </div>; +} diff --git a/packages/merchant-backoffice-ui/src/components/form/useField.tsx b/packages/merchant-backoffice-ui/src/components/form/useField.tsx new file mode 100644 index 000000000..8479d7ad8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/useField.tsx @@ -0,0 +1,86 @@ +/* + 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 { ComponentChildren, VNode } from "preact"; +import { useFormContext } from "./FormProvider"; + +interface Use<V> { + error?: string; + required: boolean; + value: any; + initial: any; + onChange: (v: V) => void; + toStr: (f: V | undefined) => string; + fromStr: (v: string) => V +} + +export function useField<T>(name: keyof T): Use<T[typeof name]> { + const { errors, object, initialObject, toStr, fromStr, valueHandler } = useFormContext<T>() + type P = typeof name + type V = T[P] + + const updateField = (field: P) => (value: V): void => { + return valueHandler((prev) => { + return setValueDeeper(prev, String(field).split('.'), value) + }) + } + + const defaultToString = ((f?: V): string => String(!f ? '' : f)) + const defaultFromString = ((v: string): V => v as any) + const value = readField(object, String(name)) + const initial = readField(initialObject, String(name)) + const isDirty = value !== initial + const hasError = readField(errors, String(name)) + return { + error: isDirty ? hasError : undefined, + required: !isDirty && hasError, + value, + initial, + onChange: updateField(name) as any, + toStr: toStr[name] ? toStr[name]! : defaultToString, + fromStr: fromStr[name] ? fromStr[name]! : defaultFromString, + } +} +/** + * read the field of an object an support accessing it using '.' + * + * @param object + * @param name + * @returns + */ +const readField = (object: any, name: string) => { + return name.split('.').reduce((prev, current) => prev && prev[current], object) +} + +const setValueDeeper = (object: any, names: string[], value: any): any => { + if (names.length === 0) return value + const [head, ...rest] = names + return { ...object, [head]: setValueDeeper(object[head] || {}, rest, value) } +} + +export interface InputProps<T> { + name: T; + label: ComponentChildren; + placeholder?: string; + tooltip?: ComponentChildren; + readonly?: boolean; + help?: ComponentChildren; +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx b/packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx new file mode 100644 index 000000000..a73f464a1 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/form/useGroupField.tsx @@ -0,0 +1,40 @@ +/* + 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 { useFormContext } from "./FormProvider"; + +interface Use { + hasError?: boolean; +} + +export function useGroupField<T>(name: keyof T): Use { + const f = useFormContext<T>(); + if (!f) + return {}; + + return { + hasError: readField(f.errors, String(name)) + }; +} + +const readField = (object: any, name: string) => { + return name.split('.').reduce((prev, current) => prev && prev[current], object) +} diff --git a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx new file mode 100644 index 000000000..d80c65cc2 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx @@ -0,0 +1,135 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { useBackendContext } from "../../context/backend"; +import { useTranslator } from "../../i18n"; +import { Entity } from "../../paths/admin/create/CreatePage"; +import { Input } from "../form/Input"; +import { InputCurrency } from "../form/InputCurrency"; +import { InputDuration } from "../form/InputDuration"; +import { InputGroup } from "../form/InputGroup"; +import { InputImage } from "../form/InputImage"; +import { InputLocation } from "../form/InputLocation"; +import { InputPaytoForm } from "../form/InputPaytoForm"; +import { InputWithAddon } from "../form/InputWithAddon"; + +export function DefaultInstanceFormFields({ + readonlyId, + showId, +}: { + readonlyId?: boolean; + showId: boolean; +}): VNode { + const i18n = useTranslator(); + const backend = useBackendContext(); + return ( + <Fragment> + {showId && ( + <InputWithAddon<Entity> + name="id" + addonBefore={`${backend.url}/instances/`} + readonly={readonlyId} + 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.`} + /> + )} + + <Input<Entity> + name="name" + label={i18n`Business name`} + tooltip={i18n`Legal name of the business represented by this instance.`} + /> + + <Input<Entity> + name="email" + label={i18n`Email`} + tooltip={i18n`Contact email`} + /> + + <Input<Entity> + name="website" + label={i18n`Website URL`} + tooltip={i18n`URL.`} + /> + + <InputImage<Entity> + name="logo" + label={i18n`Logo`} + tooltip={i18n`Logo image.`} + /> + + <InputPaytoForm<Entity> + name="payto_uris" + label={i18n`Bank account`} + tooltip={i18n`URI specifying bank account for crediting revenue.`} + /> + + <InputCurrency<Entity> + name="default_max_deposit_fee" + label={i18n`Default max deposit fee`} + tooltip={i18n`Maximum deposit fees this merchant is willing to pay per order by default.`} + /> + + <InputCurrency<Entity> + name="default_max_wire_fee" + label={i18n`Default max wire fee`} + tooltip={i18n`Maximum wire fees this merchant is willing to pay per wire transfer by default.`} + /> + + <Input<Entity> + name="default_wire_fee_amortization" + label={i18n`Default wire fee amortization`} + tooltip={i18n`Number of orders excess wire transfer fees will be divided by to compute per order surcharge.`} + /> + + <InputGroup + name="address" + label={i18n`Address`} + tooltip={i18n`Physical location of the merchant.`} + > + <InputLocation name="address" /> + </InputGroup> + + <InputGroup + name="jurisdiction" + label={i18n`Jurisdiction`} + tooltip={i18n`Jurisdiction for legal disputes with the merchant.`} + > + <InputLocation name="jurisdiction" /> + </InputGroup> + + <InputDuration<Entity> + name="default_pay_delay" + label={i18n`Default payment delay`} + withForever + tooltip={i18n`Time customers have to pay an order before the offer expires by default.`} + /> + + <InputDuration<Entity> + name="default_wire_transfer_delay" + label={i18n`Default wire transfer delay`} + tooltip={i18n`Maximum time an exchange is allowed to delay wiring funds to the merchant, enabling it to aggregate smaller payments into larger wire transfers and reducing wire fees.`} + withForever + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx b/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx new file mode 100644 index 000000000..41d08a58b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx @@ -0,0 +1,73 @@ +/* + 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 { useState } from "preact/hooks"; +import langIcon from '../../assets/icons/languageicon.svg'; +import { useTranslationContext } from "../../context/translation"; +import { strings as messages } from '../../i18n/strings' + +type LangsNames = { + [P in keyof typeof messages]: string +} + +const names: LangsNames = { + es: 'Español [es]', + en: 'English [en]', + fr: 'Français [fr]', + de: 'Deutsch [de]', + sv: 'Svenska [sv]', + it: 'Italiano [it]', +} + +function getLangName(s: keyof LangsNames | string) { + if (names[s]) return names[s] + return s +} + +export function LangSelector(): VNode { + const [updatingLang, setUpdatingLang] = useState(false) + const { lang, changeLanguage } = useTranslationContext() + + return <div class="dropdown is-active "> + <div class="dropdown-trigger"> + <button class="button has-tooltip-left" + data-tooltip="change language selection" + aria-haspopup="true" + aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)}> + <div class="icon is-small is-left"> + <img src={langIcon} /> + </div> + <span>{getLangName(lang)}</span> + <div class="icon is-right"> + <i class="mdi mdi-chevron-down" /> + </div> + </button> + </div> + {updatingLang && <div class="dropdown-menu" id="dropdown-menu" role="menu"> + <div class="dropdown-content"> + {Object.keys(messages) + .filter((l) => l !== lang) + .map(l => <a key={l} class="dropdown-item" value={l} onClick={() => { changeLanguage(l); setUpdatingLang(false) }}>{getLangName(l)}</a>)} + </div> + </div>} + </div> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx new file mode 100644 index 000000000..e1bb4c7c0 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx @@ -0,0 +1,58 @@ +/* + 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 logo from '../../assets/logo.jpeg'; +import { LangSelector } from './LangSelector'; + +interface Props { + onMobileMenu: () => void; + title: string; +} + +export function NavigationBar({ onMobileMenu, title }: Props): VNode { + return (<nav class="navbar is-fixed-top" role="navigation" aria-label="main navigation"> + <div class="navbar-brand"> + <span class="navbar-item" style={{ fontSize: 24, fontWeight: 900 }}>{title}</span> + + <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" onClick={(e) => { + onMobileMenu() + e.stopPropagation() + }}> + <span aria-hidden="true" /> + <span aria-hidden="true" /> + <span aria-hidden="true" /> + </a> + </div> + + <div class="navbar-menu "> + <a class="navbar-start is-justify-content-center is-flex-grow-1" href="https://taler.net"> + <img src={logo} style={{ height: 50, maxHeight: 50 }} /> + </a> + <div class="navbar-end"> + <div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}> + <LangSelector /> + </div> + </div> + </div> + </nav> + ); +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx new file mode 100644 index 000000000..e9c5ef8ae --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx @@ -0,0 +1,227 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { useCallback } from "preact/hooks"; +import { useBackendContext } from "../../context/backend"; +import { useConfigContext } from "../../context/config"; +import { useInstanceContext } from "../../context/instance"; +import { useInstanceKYCDetails } from "../../hooks/instance"; +import { Translate } from "../../i18n"; +import { LangSelector } from "./LangSelector"; + +interface Props { + onLogout: () => void; + mobile?: boolean; + instance: string; + admin?: boolean; + mimic?: boolean; +} + +export function Sidebar({ + mobile, + instance, + onLogout, + admin, + mimic, +}: Props): VNode { + const config = useConfigContext(); + const backend = useBackendContext(); + + const kycStatus = useInstanceKYCDetails(); + const needKYC = kycStatus.ok && kycStatus.data.type === "redirect"; + // const withInstanceIdIfNeeded = useCallback(function (path: string) { + // if (mimic) { + // return path + '?instance=' + instance + // } + // return path + // },[instance]) + + return ( + <aside class="aside is-placed-left is-expanded"> + {mobile && ( + <div + class="footer" + onClick={(e) => { + return e.stopImmediatePropagation(); + }} + > + <LangSelector /> + </div> + )} + <div class="aside-tools"> + <div class="aside-tools-label"> + <div> + <b>Taler</b> Backoffice + </div> + <div + class="is-size-7 has-text-right" + style={{ lineHeight: 0, marginTop: -10 }} + > + {process.env.__VERSION__} ({config.version}) + </div> + </div> + </div> + <div class="menu is-menu-main"> + {instance ? ( + <Fragment> + <p class="menu-label"> + <Translate>Instance</Translate> + </p> + <ul class="menu-list"> + <li> + <a href={"/update"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-square-edit-outline" /> + </span> + <span class="menu-item-label"> + <Translate>Settings</Translate> + </span> + </a> + </li> + <li> + <a href={"/orders"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-cash-register" /> + </span> + <span class="menu-item-label"> + <Translate>Orders</Translate> + </span> + </a> + </li> + <li> + <a href={"/products"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-shopping" /> + </span> + <span class="menu-item-label"> + <Translate>Products</Translate> + </span> + </a> + </li> + <li> + <a href={"/transfers"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-bank" /> + </span> + <span class="menu-item-label"> + <Translate>Transfers</Translate> + </span> + </a> + </li> + <li> + <a href={"/reserves"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-cash" /> + </span> + <span class="menu-item-label">Reserves</span> + </a> + </li> + {needKYC && ( + <li> + <a href={"/kyc"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-account-check" /> + </span> + <span class="menu-item-label">KYC Status</span> + </a> + </li> + )} + </ul> + </Fragment> + ) : undefined} + <p class="menu-label"> + <Translate>Connection</Translate> + </p> + <ul class="menu-list"> + <li> + <div> + <span style={{ width: "3rem" }} class="icon"> + <i class="mdi mdi-currency-eur" /> + </span> + <span class="menu-item-label">{config.currency}</span> + </div> + </li> + <li> + <div> + <span style={{ width: "3rem" }} class="icon"> + <i class="mdi mdi-web" /> + </span> + <span class="menu-item-label"> + {new URL(backend.url).hostname} + </span> + </div> + </li> + <li> + <div> + <span style={{ width: "3rem" }} class="icon"> + ID + </span> + <span class="menu-item-label"> + {!instance ? "default" : instance} + </span> + </div> + </li> + {admin && !mimic && ( + <Fragment> + <p class="menu-label"> + <Translate>Instances</Translate> + </p> + <li> + <a href={"/instance/new"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-plus" /> + </span> + <span class="menu-item-label"> + <Translate>New</Translate> + </span> + </a> + </li> + <li> + <a href={"/instances"} class="has-icon"> + <span class="icon"> + <i class="mdi mdi-format-list-bulleted" /> + </span> + <span class="menu-item-label"> + <Translate>List</Translate> + </span> + </a> + </li> + </Fragment> + )} + <li> + <a + class="has-icon is-state-info is-hoverable" + onClick={(): void => onLogout()} + > + <span class="icon"> + <i class="mdi mdi-logout default" /> + </span> + <span class="menu-item-label"> + <Translate>Log out</Translate> + </span> + </a> + </li> + </ul> + </div> + </aside> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx b/packages/merchant-backoffice-ui/src/components/menu/index.tsx new file mode 100644 index 000000000..0a621af56 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx @@ -0,0 +1,210 @@ +/* + 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/> + */ + +import { ComponentChildren, Fragment, h, VNode } from "preact"; +import Match from "preact-router/match"; +import { useEffect, useState } from "preact/hooks"; +import { AdminPaths } from "../../AdminRoutes"; +import { InstancePaths } from "../../InstanceRoutes"; +import { Notification } from "../../utils/types"; +import { NavigationBar } from "./NavigationBar"; +import { Sidebar } from "./SideBar"; + +function getInstanceTitle(path: string, id: string): string { + switch (path) { + case InstancePaths.update: + return `${id}: Settings`; + case InstancePaths.order_list: + return `${id}: Orders`; + case InstancePaths.order_new: + return `${id}: New order`; + case InstancePaths.product_list: + return `${id}: Products`; + case InstancePaths.product_new: + return `${id}: New product`; + case InstancePaths.product_update: + return `${id}: Update product`; + case InstancePaths.reserves_new: + return `${id}: New reserve`; + case InstancePaths.reserves_list: + return `${id}: Reserves`; + case InstancePaths.transfers_list: + return `${id}: Transfers`; + case InstancePaths.transfers_new: + return `${id}: New transfer`; + default: + return ""; + } +} + +function getAdminTitle(path: string, instance: string) { + if (path === AdminPaths.new_instance) return `New instance`; + if (path === AdminPaths.list_instances) return `Instances`; + return getInstanceTitle(path, instance); +} + +interface MenuProps { + title?: string; + instance: string; + admin?: boolean; + onLogout?: () => void; + setInstanceName: (s: string) => void; +} + +function WithTitle({ + title, + children, +}: { + title: string; + children: ComponentChildren; +}): VNode { + useEffect(() => { + document.title = `Taler Backoffice: ${title}`; + }, [title]); + return <Fragment>{children}</Fragment>; +} + +export function Menu({ + onLogout, + title, + instance, + admin, + setInstanceName, +}: MenuProps): VNode { + const [mobileOpen, setMobileOpen] = useState(false); + + return ( + <Match> + {({ path }: any) => { + const titleWithSubtitle = title + ? title + : !admin + ? getInstanceTitle(path, instance) + : getAdminTitle(path, instance); + const adminInstance = instance === "default"; + const mimic = admin && !adminInstance; + return ( + <WithTitle title={titleWithSubtitle}> + <div + class={mobileOpen ? "has-aside-mobile-expanded" : ""} + onClick={() => setMobileOpen(false)} + > + <NavigationBar + onMobileMenu={() => setMobileOpen(!mobileOpen)} + title={titleWithSubtitle} + /> + + {onLogout && ( + <Sidebar + onLogout={onLogout} + admin={admin} + mimic={mimic} + instance={instance} + mobile={mobileOpen} + /> + )} + + {mimic && ( + <nav class="level"> + <div class="level-item has-text-centered has-background-warning"> + <p class="is-size-5"> + You are viewing the instance <b>"{instance}"</b>.{" "} + <a + href="#/instances" + onClick={(e) => { + setInstanceName("default"); + }} + > + go back + </a> + </p> + </div> + </nav> + )} + </div> + </WithTitle> + ); + }} + </Match> + ); +} + +interface NotYetReadyAppMenuProps { + title: string; + onLogout?: () => void; +} + +interface NotifProps { + notification?: Notification; +} +export function NotificationCard({ + notification: n, +}: NotifProps): VNode | null { + if (!n) return null; + return ( + <div class="notification"> + <div class="columns is-vcentered"> + <div class="column is-12"> + <article + class={ + n.type === "ERROR" + ? "message is-danger" + : n.type === "WARN" + ? "message is-warning" + : "message is-info" + } + > + <div class="message-header"> + <p>{n.message}</p> + </div> + {n.description && ( + <div class="message-body"> + <div>{n.description}</div> + {n.details && <pre>{n.details}</pre>} + </div> + )} + </article> + </div> + </div> + </div> + ); +} + +export function NotYetReadyAppMenu({ + onLogout, + title, +}: NotYetReadyAppMenuProps): VNode { + const [mobileOpen, setMobileOpen] = useState(false); + + useEffect(() => { + document.title = `Taler Backoffice: ${title}`; + }, [title]); + + return ( + <div + class={mobileOpen ? "has-aside-mobile-expanded" : ""} + onClick={() => setMobileOpen(false)} + > + <NavigationBar + onMobileMenu={() => setMobileOpen(!mobileOpen)} + title={title} + /> + {onLogout && ( + <Sidebar onLogout={onLogout} instance="" mobile={mobileOpen} /> + )} + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/modal/index.tsx b/packages/merchant-backoffice-ui/src/components/modal/index.tsx new file mode 100644 index 000000000..a7edb9e48 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/modal/index.tsx @@ -0,0 +1,262 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { useInstanceContext } from "../../context/instance"; +import { Translate, useTranslator } from "../../i18n"; +import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants"; +import { Loading, Spinner } from "../exception/loading"; +import { FormProvider } from "../form/FormProvider"; +import { Input } from "../form/Input"; + +interface Props { + active?: boolean; + description?: string; + onCancel?: () => void; + onConfirm?: () => void; + label?: string; + children?: ComponentChildren; + danger?: boolean; + disabled?: boolean; +} + +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" style={{maxWidth: 700}}> + <header class="modal-card-head"> + {!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"> + {children} + </section> + <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>{label}</Translate></button> + </div> + </footer> + </div> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> +} + +export function ContinueModal({ active, description, onCancel, onConfirm, children, disabled }: Props): VNode { + return <div class={active ? "modal is-active" : "modal"}> + <div class="modal-background " onClick={onCancel} /> + <div class="modal-card"> + <header class="modal-card-head has-background-success"> + {!description ? null : <p class="modal-card-title">{description}</p>} + <button class="delete " aria-label="close" onClick={onCancel} /> + </header> + <section class="modal-card-body"> + {children} + </section> + <footer class="modal-card-foot"> + <div class="buttons is-right" style={{ width: '100%' }}> + <button class="button is-success " disabled={disabled} onClick={onConfirm} ><Translate>Continue</Translate></button> + </div> + </footer> + </div> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> +} + +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> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> +} + +export function ClearConfirmModal({ description, onCancel, onClear, onConfirm, children }: Props & { onClear?: () => void }): VNode { + return <div class="modal is-active"> + <div class="modal-background " onClick={onCancel} /> + <div class="modal-card"> + <header class="modal-card-head"> + {!description ? null : <p class="modal-card-title">{description}</p>} + <button class="delete " aria-label="close" onClick={onCancel} /> + </header> + <section class="modal-card-body is-main-section"> + {children} + </section> + <footer class="modal-card-foot"> + {onClear && <button class="button is-danger" onClick={onClear} disabled={onClear === undefined} ><Translate>Clear</Translate></button>} + <div class="buttons is-right" style={{ width: '100%' }}> + <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button> + <button class="button is-info" onClick={onConfirm} disabled={onConfirm === undefined} ><Translate>Confirm</Translate></button> + </div> + </footer> + </div> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> +} + +interface DeleteModalProps { + element: { id: string, name: string }; + onCancel: () => void; + onConfirm: (id: string) => void; +} + +export function DeleteModal({ element, onCancel, onConfirm }: DeleteModalProps): VNode { + 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 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> +} + +interface UpdateTokenModalProps { + oldToken?: string; + onCancel: () => void; + onConfirm: (value: string) => void; + onClear: () => void; +} + +//FIXME: merge UpdateTokenModal with SetTokenNewInstanceModal +export function UpdateTokenModal({ onCancel, onClear, onConfirm, oldToken }: UpdateTokenModalProps): VNode { + type State = { old_token: string, new_token: string, repeat_token: string } + const [form, setValue] = useState<Partial<State>>({ + old_token: '', new_token: '', repeat_token: '', + }) + const i18n = useTranslator() + + const hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token + const errors = { + old_token: hasInputTheCorrectOldToken ? i18n`is not the same as the current access token` : undefined, + new_token: !form.new_token ? i18n`cannot be empty` : (form.new_token === form.old_token ? i18n`cannot be the same as the old token` : undefined), + repeat_token: form.new_token !== form.repeat_token ? i18n`is not the same` : undefined + } + + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + + const instance = useInstanceContext() + + const text = i18n`You are updating the access token from instance with id ${instance.id}` + + return <ClearConfirmModal description={text} + onCancel={onCancel} + onConfirm={!hasErrors ? () => onConfirm(form.new_token!) : undefined} + onClear={!hasInputTheCorrectOldToken && oldToken ? onClear : undefined} + > + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths" > + <FormProvider errors={errors} object={form} valueHandler={setValue}> + {oldToken && <Input<State> name="old_token" label={i18n`Old access token`} tooltip={i18n`access token currently in use`} inputType="password" />} + <Input<State> name="new_token" label={i18n`New access token`} tooltip={i18n`next access token to be used`} inputType="password" /> + <Input<State> name="repeat_token" label={i18n`Repeat access token`} tooltip={i18n`confirm the same access token`} inputType="password" /> + </FormProvider> + <p><Translate>Clearing the access token will mean public access to the instance</Translate></p> + </div> + <div class="column" /> + </div> + </ClearConfirmModal> +} + +export function SetTokenNewInstanceModal({ onCancel, onClear, onConfirm }: UpdateTokenModalProps): VNode { + type State = { old_token: string, new_token: string, repeat_token: string } + const [form, setValue] = useState<Partial<State>>({ + new_token: '', repeat_token: '', + }) + const i18n = useTranslator() + + const errors = { + new_token: !form.new_token ? i18n`cannot be empty` : (form.new_token === form.old_token ? i18n`cannot be the same as the old access token` : undefined), + repeat_token: form.new_token !== form.repeat_token ? i18n`is not the same` : undefined + } + + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + + + return <div class="modal is-active"> + <div class="modal-background " onClick={onCancel} /> + <div class="modal-card"> + <header class="modal-card-head"> + <p class="modal-card-title">{i18n`You are setting the access token for the new instance`}</p> + <button class="delete " aria-label="close" onClick={onCancel} /> + </header> + <section class="modal-card-body is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths" > + <FormProvider errors={errors} object={form} valueHandler={setValue}> + <Input<State> name="new_token" label={i18n`New access token`} tooltip={i18n`next access token to be used`} inputType="password" /> + <Input<State> name="repeat_token" label={i18n`Repeat access token`} tooltip={i18n`confirm the same access token`} inputType="password" /> + </FormProvider> + <p><Translate>With external authorization method no check will be done by the merchant backend</Translate></p> + </div> + <div class="column" /> + </div> + </section> + <footer class="modal-card-foot"> + {onClear && <button class="button is-danger" onClick={onClear} disabled={onClear === undefined} ><Translate>Set external authorization</Translate></button>} + <div class="buttons is-right" style={{ width: '100%' }}> + <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button> + <button class="button is-info" onClick={() => onConfirm(form.new_token!)} disabled={hasErrors} ><Translate>Set access token</Translate></button> + </div> + </footer> + </div> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> +} + +export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode { + const i18n = useTranslator() + return <div class="modal is-active"> + <div class="modal-background " onClick={onCancel} /> + <div class="modal-card"> + <header class="modal-card-head"> + <p class="modal-card-title"><Translate>Operation in progress...</Translate></p> + </header> + <section class="modal-card-body"> + <div class="columns"> + <div class="column" /> + <Spinner /> + <div class="column" /> + </div> + <p>{i18n`The operation will be automatically canceled after ${DEFAULT_REQUEST_TIMEOUT} seconds`}</p> + </section> + <footer class="modal-card-foot"> + <div class="buttons is-right" style={{ width: '100%' }}> + <button class="button " onClick={onCancel} ><Translate>Cancel</Translate></button> + </div> + </footer> + </div> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> +} diff --git a/packages/merchant-backoffice-ui/src/components/notifications/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/components/notifications/CreatedSuccessfully.tsx new file mode 100644 index 000000000..e0b355c2e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/notifications/CreatedSuccessfully.tsx @@ -0,0 +1,49 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; + +interface Props { + onCreateAnother?: () => void; + onConfirm: () => void; + children: ComponentChildren; +} + +export function CreatedSuccessfully({ children, onConfirm, onCreateAnother }: Props): VNode { + return <div class="columns is-fullwidth is-vcentered mt-3"> + <div class="column" /> + <div class="column is-four-fifths"> + <div class="card"> + <header class="card-header has-background-success"> + <p class="card-header-title has-text-white-ter"> + Success. + </p> + </header> + <div class="card-content"> + {children} + </div> + </div> + <div class="buttons is-right"> + {onCreateAnother && <button class="button is-info" onClick={onCreateAnother}>Create another</button>} + <button class="button is-info" onClick={onConfirm}>Continue</button> + </div> + </div> + <div class="column" /> + </div> +} diff --git a/packages/merchant-backoffice-ui/src/components/notifications/Notifications.stories.tsx b/packages/merchant-backoffice-ui/src/components/notifications/Notifications.stories.tsx new file mode 100644 index 000000000..3b95295fe --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/notifications/Notifications.stories.tsx @@ -0,0 +1,57 @@ +/* + 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 } from 'preact'; +import { Notifications } from './index'; + + +export default { + title: 'Components/Notification', + component: Notifications, + argTypes: { + removeNotification: { action: 'removeNotification' }, + }, +}; + +export const Info = (a: any) => <Notifications {...a} />; +Info.args = { + notifications: [{ + message: 'Title', + description: 'Some large description', + type: 'INFO', + }] +} +export const Warn = (a: any) => <Notifications {...a} />; +Warn.args = { + notifications: [{ + message: 'Title', + description: 'Some large description', + type: 'WARN', + }] +} +export const Error = (a: any) => <Notifications {...a} />; +Error.args = { + notifications: [{ + message: 'Title', + description: 'Some large description', + type: 'ERROR', + }] +} diff --git a/packages/merchant-backoffice-ui/src/components/notifications/index.tsx b/packages/merchant-backoffice-ui/src/components/notifications/index.tsx new file mode 100644 index 000000000..34bd40ec6 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/notifications/index.tsx @@ -0,0 +1,52 @@ +/* + 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 { MessageType, Notification } from "../../utils/types"; + +interface Props { + notifications: Notification[]; + removeNotification?: (n: Notification) => void; +} + +function messageStyle(type: MessageType): string { + switch (type) { + case "INFO": return "message is-info"; + case "WARN": return "message is-warning"; + case "ERROR": return "message is-danger"; + case "SUCCESS": return "message is-success"; + default: return "message" + } +} + +export function Notifications({ notifications, removeNotification }: Props): VNode { + return <div class="toast"> + {notifications.map((n,i) => <article key={i} class={messageStyle(n.type)}> + <div class="message-header"> + <p>{n.message}</p> + <button class="delete" onClick={() => removeNotification && removeNotification(n)} /> + </div> + {n.description && <div class="message-body"> + {n.description} + </div>} + </article>)} + </div> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx b/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx new file mode 100644 index 000000000..084b7b00a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx @@ -0,0 +1,324 @@ +/* + 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, Component } from "preact"; + +interface Props { + closeFunction?: () => void; + dateReceiver?: (d: Date) => void; + opened?: boolean; +} +interface State { + displayedMonth: number; + displayedYear: number; + selectYearMode: boolean; + currentDate: Date; +} + +// inspired by https://codepen.io/m4r1vs/pen/MOOxyE +export class DatePicker extends Component<Props, State> { + + closeDatePicker() { + this.props.closeFunction && this.props.closeFunction(); // Function gets passed by parent + } + + /** + * Gets fired when a day gets clicked. + * @param {object} e The event thrown by the <span /> element clicked + */ + dayClicked(e: any) { + + const element = e.target; // the actual element clicked + + if (element.innerHTML === '') return false; // don't continue if <span /> empty + + // get date from clicked element (gets attached when rendered) + const date = new Date(element.getAttribute('data-value')); + + // update the state + this.setState({ currentDate: date }); + this.passDateToParent(date) + } + + /** + * returns days in month as array + * @param {number} month the month to display + * @param {number} year the year to display + */ + getDaysByMonth(month: number, year: number) { + + const calendar = []; + + const date = new Date(year, month, 1); // month to display + + const firstDay = new Date(year, month, 1).getDay(); // first weekday of month + const lastDate = new Date(year, month + 1, 0).getDate(); // last date of month + + let day: number | null = 0; + + // the calendar is 7*6 fields big, so 42 loops + for (let i = 0; i < 42; i++) { + + if (i >= firstDay && day !== null) day = day + 1; + if (day !== null && day > lastDate) day = null; + + // append the calendar Array + calendar.push({ + day: (day === 0 || day === null) ? null : day, // null or number + date: (day === 0 || day === null) ? null : new Date(year, month, day), // null or Date() + today: (day === now.getDate() && month === now.getMonth() && year === now.getFullYear()) // boolean + }); + } + + return calendar; + } + + /** + * Display previous month by updating state + */ + displayPrevMonth() { + if (this.state.displayedMonth <= 0) { + this.setState({ + displayedMonth: 11, + displayedYear: this.state.displayedYear - 1 + }); + } + else { + this.setState({ + displayedMonth: this.state.displayedMonth - 1 + }); + } + } + + /** + * Display next month by updating state + */ + displayNextMonth() { + if (this.state.displayedMonth >= 11) { + this.setState({ + displayedMonth: 0, + displayedYear: this.state.displayedYear + 1 + }); + } + else { + this.setState({ + displayedMonth: this.state.displayedMonth + 1 + }); + } + } + + /** + * Display the selected month (gets fired when clicking on the date string) + */ + displaySelectedMonth() { + if (this.state.selectYearMode) { + this.toggleYearSelector(); + } + else { + if (!this.state.currentDate) return false; + this.setState({ + displayedMonth: this.state.currentDate.getMonth(), + displayedYear: this.state.currentDate.getFullYear() + }); + } + } + + toggleYearSelector() { + this.setState({ selectYearMode: !this.state.selectYearMode }); + } + + changeDisplayedYear(e: any) { + const element = e.target; + this.toggleYearSelector(); + this.setState({ displayedYear: parseInt(element.innerHTML, 10), displayedMonth: 0 }); + } + + /** + * Pass the selected date to parent when 'OK' is clicked + */ + passSavedDateDateToParent() { + this.passDateToParent(this.state.currentDate) + } + passDateToParent(date: Date) { + if (typeof this.props.dateReceiver === 'function') this.props.dateReceiver(date); + this.closeDatePicker(); + } + + componentDidUpdate() { + if (this.state.selectYearMode) { + document.getElementsByClassName('selected')[0].scrollIntoView(); // works in every browser incl. IE, replace with scrollIntoViewIfNeeded when browsers support it + } + } + + constructor() { + super(); + + this.closeDatePicker = this.closeDatePicker.bind(this); + this.dayClicked = this.dayClicked.bind(this); + this.displayNextMonth = this.displayNextMonth.bind(this); + this.displayPrevMonth = this.displayPrevMonth.bind(this); + this.getDaysByMonth = this.getDaysByMonth.bind(this); + this.changeDisplayedYear = this.changeDisplayedYear.bind(this); + this.passDateToParent = this.passDateToParent.bind(this); + this.toggleYearSelector = this.toggleYearSelector.bind(this); + this.displaySelectedMonth = this.displaySelectedMonth.bind(this); + + + this.state = { + currentDate: now, + displayedMonth: now.getMonth(), + displayedYear: now.getFullYear(), + selectYearMode: false + } + } + + render() { + + const { currentDate, displayedMonth, displayedYear, selectYearMode } = this.state; + + return ( + <div> + <div class={`datePicker ${ this.props.opened && "datePicker--opened"}`} > + + <div class="datePicker--titles"> + <h3 style={{ + color: selectYearMode ? 'rgba(255,255,255,.87)' : 'rgba(255,255,255,.57)' + }} onClick={this.toggleYearSelector}>{currentDate.getFullYear()}</h3> + <h2 style={{ + color: !selectYearMode ? 'rgba(255,255,255,.87)' : 'rgba(255,255,255,.57)' + }} onClick={this.displaySelectedMonth}> + {dayArr[currentDate.getDay()]}, {monthArrShort[currentDate.getMonth()]} {currentDate.getDate()} + </h2> + </div> + + {!selectYearMode && <nav> + <span onClick={this.displayPrevMonth} class="icon"><i style={{ transform: 'rotate(180deg)' }} class="mdi mdi-forward" /></span> + <h4>{monthArrShortFull[displayedMonth]} {displayedYear}</h4> + <span onClick={this.displayNextMonth} class="icon"><i class="mdi mdi-forward" /></span> + </nav>} + + <div class="datePicker--scroll"> + + {!selectYearMode && <div class="datePicker--calendar" > + + <div class="datePicker--dayNames"> + {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day,i) => <span key={i}>{day}</span>)} + </div> + + <div onClick={this.dayClicked} class="datePicker--days"> + + {/* + Loop through the calendar object returned by getDaysByMonth(). + */} + + {this.getDaysByMonth(this.state.displayedMonth, this.state.displayedYear) + .map( + day => { + let selected = false; + + if (currentDate && day.date) selected = (currentDate.toLocaleDateString() === day.date.toLocaleDateString()); + + return (<span key={day.day} + class={(day.today ? 'datePicker--today ' : '') + (selected ? 'datePicker--selected' : '')} + disabled={!day.date} + data-value={day.date} + > + {day.day} + </span>) + } + ) + } + + </div> + + </div>} + + {selectYearMode && <div class="datePicker--selectYear"> + + {yearArr.map(year => ( + <span key={year} class={(year === displayedYear) ? 'selected' : ''} onClick={this.changeDisplayedYear}> + {year} + </span> + ))} + + </div>} + + </div> + </div> + + <div class="datePicker--background" onClick={this.closeDatePicker} style={{ + display: this.props.opened ? 'block' : 'none' + }} + /> + + </div> + ) + } +} + + +const monthArrShortFull = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' +] + +const monthArrShort = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec' +] + +const dayArr = [ + 'Sun', + 'Mon', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + 'Sat' +] + +const now = new Date() + +const yearArr: number[] = [] + +for (let i = 2010; i <= now.getFullYear() + 10; i++) { + yearArr.push(i); +} diff --git a/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.stories.tsx b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.stories.tsx new file mode 100644 index 000000000..275c80fa6 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.stories.tsx @@ -0,0 +1,50 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { useState } from 'preact/hooks'; +import { DurationPicker as TestedComponent } from './DurationPicker'; + + +export default { + title: 'Components/Picker/Duration', + component: TestedComponent, + argTypes: { + onCreate: { action: 'onCreate' }, + goBack: { action: 'goBack' }, + } +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Example = createExample(TestedComponent, { + days: true, minutes: true, hours: true, seconds: true, + value: 10000000 +}); + +export const WithState = () => { + const [v,s] = useState<number>(1000000) + return <TestedComponent value={v} onChange={s} days minutes hours seconds /> +} diff --git a/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx new file mode 100644 index 000000000..f32a48fd4 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx @@ -0,0 +1,211 @@ +/* + 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 { useState } from "preact/hooks"; +import { useTranslator } from "../../i18n"; +import "../../scss/DurationPicker.scss"; + +export interface Props { + hours?: boolean; + minutes?: boolean; + seconds?: boolean; + days?: boolean; + onChange: (value: number) => void; + value: number; +} + +// inspiration taken from https://github.com/flurmbo/react-duration-picker +export function DurationPicker({ + days, + hours, + minutes, + seconds, + onChange, + value, +}: Props): VNode { + const ss = 1000 * 1000; + const ms = ss * 60; + const hs = ms * 60; + const ds = hs * 24; + const i18n = useTranslator(); + + return ( + <div class="rdp-picker"> + {days && ( + <DurationColumn + unit={i18n`days`} + max={99} + value={Math.floor(value / ds)} + onDecrease={value >= ds ? () => onChange(value - ds) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ds) : undefined} + onChange={(diff) => onChange(value + diff * ds)} + /> + )} + {hours && ( + <DurationColumn + unit={i18n`hours`} + max={23} + min={1} + value={Math.floor(value / hs) % 24} + onDecrease={value >= hs ? () => onChange(value - hs) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + hs) : undefined} + onChange={(diff) => onChange(value + diff * hs)} + /> + )} + {minutes && ( + <DurationColumn + unit={i18n`minutes`} + max={59} + min={1} + value={Math.floor(value / ms) % 60} + onDecrease={value >= ms ? () => onChange(value - ms) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ms) : undefined} + onChange={(diff) => onChange(value + diff * ms)} + /> + )} + {seconds && ( + <DurationColumn + unit={i18n`seconds`} + max={59} + value={Math.floor(value / ss) % 60} + onDecrease={value >= ss ? () => onChange(value - ss) : undefined} + onIncrease={value < 99 * ds ? () => onChange(value + ss) : undefined} + onChange={(diff) => onChange(value + diff * ss)} + /> + )} + </div> + ); +} + +interface ColProps { + unit: string; + min?: number; + max: number; + value: number; + onIncrease?: () => void; + onDecrease?: () => void; + onChange?: (diff: number) => void; +} + +function InputNumber({ + initial, + onChange, +}: { + initial: number; + onChange: (n: number) => void; +}) { + const [value, handler] = useState<{ v: string }>({ + v: toTwoDigitString(initial), + }); + + return ( + <input + value={value.v} + onBlur={(e) => onChange(parseInt(value.v, 10))} + onInput={(e) => { + e.preventDefault(); + const n = Number.parseInt(e.currentTarget.value, 10); + if (isNaN(n)) return handler({ v: toTwoDigitString(initial) }); + return handler({ v: toTwoDigitString(n) }); + }} + style={{ + width: 50, + border: "none", + fontSize: "inherit", + background: "inherit", + }} + /> + ); +} + +function DurationColumn({ + unit, + min = 0, + max, + value, + onIncrease, + onDecrease, + onChange, +}: ColProps): VNode { + const cellHeight = 35; + return ( + <div class="rdp-column-container"> + <div class="rdp-masked-div"> + <hr class="rdp-reticule" style={{ top: cellHeight * 2 - 1 }} /> + <hr class="rdp-reticule" style={{ top: cellHeight * 3 - 1 }} /> + + <div class="rdp-column" style={{ top: 0 }}> + <div class="rdp-cell" key={value - 2}> + {onDecrease && ( + <button + style={{ width: "100%", textAlign: "center", margin: 5 }} + onClick={onDecrease} + > + <span class="icon"> + <i class="mdi mdi-chevron-up" /> + </span> + </button> + )} + </div> + <div class="rdp-cell" key={value - 1}> + {value > min ? toTwoDigitString(value - 1) : ""} + </div> + <div class="rdp-cell rdp-center" key={value}> + {onChange ? ( + <InputNumber + initial={value} + onChange={(n) => onChange(n - value)} + /> + ) : ( + toTwoDigitString(value) + )} + <div>{unit}</div> + </div> + + <div class="rdp-cell" key={value + 1}> + {value < max ? toTwoDigitString(value + 1) : ""} + </div> + + <div class="rdp-cell" key={value + 2}> + {onIncrease && ( + <button + style={{ width: "100%", textAlign: "center", margin: 5 }} + onClick={onIncrease} + > + <span class="icon"> + <i class="mdi mdi-chevron-down" /> + </span> + </button> + )} + </div> + </div> + </div> + </div> + ); +} + +function toTwoDigitString(n: number) { + if (n < 10) { + return `0${n}`; + } + return `${n}`; +} diff --git a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.stories.tsx b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.stories.tsx new file mode 100644 index 000000000..6504d85ba --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.stories.tsx @@ -0,0 +1,58 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { InventoryProductForm as TestedComponent } from './InventoryProductForm'; + + +export default { + title: 'Components/Product/Add', + component: TestedComponent, + argTypes: { + onAddProduct: { action: 'onAddProduct' }, + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const WithASimpleList = createExample(TestedComponent, { + inventory:[{ + id: 'this id', + description: 'this is the description', + } as any] +}); + +export const WithAProductSelected = createExample(TestedComponent, { + inventory:[], + currentProducts: { + thisid: { + quantity: 1, + product: { + id: 'asd', + description: 'asdsadsad', + } as any + } + } +}); diff --git a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx new file mode 100644 index 000000000..8f05c9736 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx @@ -0,0 +1,95 @@ +/* + 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/> + */ +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { FormProvider, FormErrors } from "../form/FormProvider"; +import { InputNumber } from "../form/InputNumber"; +import { InputSearchProduct } from "../form/InputSearchProduct"; +import { MerchantBackend, WithId } from "../../declaration"; +import { Translate, useTranslator } from "../../i18n"; +import { ProductMap } from "../../paths/instance/orders/create/CreatePage"; + +type Form = { + product: MerchantBackend.Products.ProductDetail & WithId, + quantity: number; +} + +interface Props { + currentProducts: ProductMap, + onAddProduct: (product: MerchantBackend.Products.ProductDetail & WithId, quantity: number) => void, + inventory: (MerchantBackend.Products.ProductDetail & WithId)[], +} + +export function InventoryProductForm({ currentProducts, onAddProduct, inventory }: Props): VNode { + const initialState = { quantity: 1 } + const [state, setState] = useState<Partial<Form>>(initialState) + const [errors, setErrors] = useState<FormErrors<Form>>({}) + + const i18n = useTranslator() + + const productWithInfiniteStock = state.product && state.product.total_stock === -1 + + const submit = (): void => { + if (!state.product) { + setErrors({ product: i18n`You must enter a valid product identifier.` }); + return; + } + if (productWithInfiniteStock) { + onAddProduct(state.product, 1) + } else { + if (!state.quantity || state.quantity <= 0) { + setErrors({ quantity: i18n`Quantity must be greater than 0!` }); + return; + } + const currentStock = state.product.total_stock - state.product.total_lost - state.product.total_sold + const p = currentProducts[state.product.id] + if (p) { + if (state.quantity + p.quantity > currentStock) { + const left = currentStock - p.quantity; + setErrors({ quantity: i18n`This quantity exceeds remaining stock. Currently, only ${left} units remain unreserved in stock.` }); + return; + } + onAddProduct(state.product, state.quantity + p.quantity) + } else { + if (state.quantity > currentStock) { + const left = currentStock; + setErrors({ quantity: i18n`This quantity exceeds remaining stock. Currently, only ${left} units remain unreserved in stock.` }); + return; + } + onAddProduct(state.product, state.quantity) + } + } + + setState(initialState) + } + + return <FormProvider<Form> errors={errors} object={state} valueHandler={setState}> + <InputSearchProduct selected={state.product} onChange={(p) => setState(v => ({ ...v, product: p }))} products={inventory} /> + { state.product && <div class="columns mt-5"> + <div class="column is-two-thirds"> + {!productWithInfiniteStock && + <InputNumber<Form> name="quantity" label={i18n`Quantity`} tooltip={i18n`how many products will be added`} /> + } + </div> + <div class="column"> + <div class="buttons is-right"> + <button class="button is-success" onClick={submit}><Translate>Add from inventory</Translate></button> + </div> + </div> + </div> } + + </FormProvider> +} diff --git a/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx b/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx new file mode 100644 index 000000000..397efe616 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/product/NonInventoryProductForm.tsx @@ -0,0 +1,146 @@ +/* + 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/> + */ +import { Fragment, h, VNode } from "preact"; +import { useCallback, useEffect, useState } from "preact/hooks"; +import * as yup from 'yup'; +import { FormErrors, FormProvider } from "../form/FormProvider"; +import { Input } from "../form/Input"; +import { InputCurrency } from "../form/InputCurrency"; +import { InputImage } from "../form/InputImage"; +import { InputNumber } from "../form/InputNumber"; +import { InputTaxes } from "../form/InputTaxes"; +import { MerchantBackend } from "../../declaration"; +import { useListener } from "../../hooks/listener"; +import { Translate, useTranslator } from "../../i18n"; +import { + NonInventoryProductSchema as schema +} from '../../schemas'; + + +type Entity = MerchantBackend.Product + +interface Props { + onAddProduct: (p: Entity) => Promise<void>; + productToEdit?: Entity; +} +export function NonInventoryProductFrom({ productToEdit, onAddProduct }: Props): VNode { + const [showCreateProduct, setShowCreateProduct] = useState(false) + + const isEditing = !!productToEdit + + useEffect(() => { + setShowCreateProduct(isEditing) + }, [isEditing]) + + const [submitForm, addFormSubmitter] = useListener<Partial<MerchantBackend.Product> | undefined>((result) => { + if (result) { + setShowCreateProduct(false) + return onAddProduct({ + quantity: result.quantity || 0, + taxes: result.taxes || [], + description: result.description || '', + image: result.image || '', + price: result.price || '', + unit: result.unit || '' + }) + } + return Promise.resolve() + }) + + const i18n = useTranslator() + + return <Fragment> + <div class="buttons"> + <button class="button is-success" data-tooltip={i18n`describe and add a product that is not in the inventory list`} onClick={() => setShowCreateProduct(true)} ><Translate>Add custom product</Translate></button> + </div> + {showCreateProduct && <div class="modal is-active"> + <div class="modal-background " onClick={() => setShowCreateProduct(false)} /> + <div class="modal-card"> + <header class="modal-card-head"> + <p class="modal-card-title">{i18n`Complete information of the product`}</p> + <button class="delete " aria-label="close" onClick={() => setShowCreateProduct(false)} /> + </header> + <section class="modal-card-body"> + <ProductForm initial={productToEdit} onSubscribe={addFormSubmitter} /> + </section> + <footer class="modal-card-foot"> + <div class="buttons is-right" style={{ width: '100%' }}> + <button class="button " onClick={() => setShowCreateProduct(false)} ><Translate>Cancel</Translate></button> + <button class="button is-info " disabled={!submitForm} onClick={submitForm} ><Translate>Confirm</Translate></button> + </div> + </footer> + </div> + <button class="modal-close is-large " aria-label="close" onClick={() => setShowCreateProduct(false)} /> + </div>} + </Fragment> +} + +interface ProductProps { + onSubscribe: (c?: () => Entity | undefined) => void; + initial?: Partial<Entity>; +} + +interface NonInventoryProduct { + quantity: number; + description: string; + unit: string; + price: string; + image: string; + taxes: MerchantBackend.Tax[]; +} + +export function ProductForm({ onSubscribe, initial }: ProductProps): VNode { + const [value, valueHandler] = useState<Partial<NonInventoryProduct>>({ + taxes: [], + ...initial, + }) + let errors: FormErrors<Entity> = {} + try { + schema.validateSync(value, { abortEarly: false }) + } catch (err) { + if (err instanceof yup.ValidationError) { + const yupErrors = err.inner as yup.ValidationError[] + errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {}) + } + } + + const submit = useCallback((): Entity | undefined => { + return value as MerchantBackend.Product + }, [value]) + + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + + useEffect(() => { + onSubscribe(hasErrors ? undefined : submit) + }, [submit, hasErrors]) + + const i18n = useTranslator() + + return <div> + <FormProvider<NonInventoryProduct> name="product" errors={errors} object={value} valueHandler={valueHandler} > + + <InputImage<NonInventoryProduct> name="image" label={i18n`Image`} tooltip={i18n`photo of the product`} /> + <Input<NonInventoryProduct> name="description" inputType="multiline" label={i18n`Description`} tooltip={i18n`full product description`} /> + <Input<NonInventoryProduct> name="unit" label={i18n`Unit`} tooltip={i18n`name of the product unit`} /> + <InputCurrency<NonInventoryProduct> name="price" label={i18n`Price`} tooltip={i18n`amount in the current currency`} /> + + <InputNumber<NonInventoryProduct> name="quantity" label={i18n`Quantity`} tooltip={i18n`how many products will be added`} /> + + <InputTaxes<NonInventoryProduct> name="taxes" label={i18n`Taxes`} /> + + </FormProvider> + </div> +} diff --git a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx new file mode 100644 index 000000000..9434d3de8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx @@ -0,0 +1,176 @@ +/* + 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 } from "preact"; +import { useCallback, useEffect, useState } from "preact/hooks"; +import * as yup from "yup"; +import { useBackendContext } from "../../context/backend"; +import { MerchantBackend } from "../../declaration"; +import { useTranslator } from "../../i18n"; +import { + ProductCreateSchema as createSchema, + ProductUpdateSchema as updateSchema, +} from "../../schemas"; +import { FormProvider, FormErrors } from "../form/FormProvider"; +import { Input } from "../form/Input"; +import { InputCurrency } from "../form/InputCurrency"; +import { InputImage } from "../form/InputImage"; +import { InputNumber } from "../form/InputNumber"; +import { InputStock, Stock } from "../form/InputStock"; +import { InputTaxes } from "../form/InputTaxes"; +import { InputWithAddon } from "../form/InputWithAddon"; + +type Entity = MerchantBackend.Products.ProductDetail & { product_id: string }; + +interface Props { + onSubscribe: (c?: () => Entity | undefined) => void; + initial?: Partial<Entity>; + alreadyExist?: boolean; +} + +export function ProductForm({ onSubscribe, initial, alreadyExist }: Props) { + const [value, valueHandler] = useState<Partial<Entity & { stock: Stock }>>({ + address: {}, + description_i18n: {}, + taxes: [], + next_restock: { t_s: "never" }, + price: ":0", + ...initial, + stock: + !initial || initial.total_stock === -1 + ? undefined + : { + current: initial.total_stock || 0, + lost: initial.total_lost || 0, + sold: initial.total_sold || 0, + address: initial.address, + nextRestock: initial.next_restock, + }, + }); + let errors: FormErrors<Entity> = {}; + + try { + (alreadyExist ? updateSchema : createSchema).validateSync(value, { + abortEarly: false, + }); + } catch (err) { + if (err instanceof yup.ValidationError) { + const yupErrors = err.inner as yup.ValidationError[]; + errors = yupErrors.reduce( + (prev, cur) => + !cur.path ? prev : { ...prev, [cur.path]: cur.message }, + {} + ); + } + } + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined + ); + + const submit = useCallback((): Entity | undefined => { + const stock: Stock = (value as any).stock; + + if (!stock) { + value.total_stock = -1; + } else { + value.total_stock = stock.current; + value.total_lost = stock.lost; + value.next_restock = + stock.nextRestock instanceof Date + ? { t_s: stock.nextRestock.getTime() / 1000 } + : stock.nextRestock; + value.address = stock.address; + } + delete (value as any).stock; + + if (typeof value.minimum_age !== "undefined" && value.minimum_age < 1) { + delete value.minimum_age; + } + + return value as MerchantBackend.Products.ProductDetail & { + product_id: string; + }; + }, [value]); + + useEffect(() => { + onSubscribe(hasErrors ? undefined : submit); + }, [submit, hasErrors]); + + const backend = useBackendContext(); + const i18n = useTranslator(); + + return ( + <div> + <FormProvider<Entity> + name="product" + errors={errors} + object={value} + valueHandler={valueHandler} + > + {alreadyExist ? undefined : ( + <InputWithAddon<Entity> + name="product_id" + addonBefore={`${backend.url}/product/`} + label={i18n`ID`} + tooltip={i18n`product identification to use in URLs (for internal use only)`} + /> + )} + <InputImage<Entity> + name="image" + label={i18n`Image`} + tooltip={i18n`illustration of the product for customers`} + /> + <Input<Entity> + name="description" + inputType="multiline" + label={i18n`Description`} + tooltip={i18n`product description for customers`} + /> + <InputNumber<Entity> + name="minimum_age" + label={i18n`Age restricted`} + tooltip={i18n`is this product restricted for customer below certain age?`} + /> + <Input<Entity> + name="unit" + label={i18n`Unit`} + tooltip={i18n`unit describing quantity of product sold (e.g. 2 kilograms, 5 liters, 3 items, 5 meters) for customers`} + /> + <InputCurrency<Entity> + name="price" + label={i18n`Price`} + tooltip={i18n`sale price for customers, including taxes, for above units of the product`} + /> + <InputStock + name="stock" + label={i18n`Stock`} + alreadyExist={alreadyExist} + tooltip={i18n`product inventory for products with finite supply (for internal use only)`} + /> + <InputTaxes<Entity> + name="taxes" + label={i18n`Taxes`} + tooltip={i18n`taxes included in the product price, exposed to customers`} + /> + </FormProvider> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx b/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx new file mode 100644 index 000000000..ff141bb39 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/components/product/ProductList.tsx @@ -0,0 +1,105 @@ +/* + 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/> + */ +import { Amounts } from "@gnu-taler/taler-util"; +import { h, VNode } from "preact"; +import emptyImage from "../../assets/empty.png"; +import { MerchantBackend } from "../../declaration"; +import { Translate } from "../../i18n"; + +interface Props { + list: MerchantBackend.Product[]; + actions?: { + name: string; + tooltip: string; + handler: (d: MerchantBackend.Product, index: number) => void; + }[]; +} +export function ProductList({ list, actions = [] }: Props): VNode { + return ( + <div class="table-container"> + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>image</Translate> + </th> + <th> + <Translate>description</Translate> + </th> + <th> + <Translate>quantity</Translate> + </th> + <th> + <Translate>unit price</Translate> + </th> + <th> + <Translate>total price</Translate> + </th> + <th /> + </tr> + </thead> + <tbody> + {list.map((entry, index) => { + const unitPrice = !entry.price ? "0" : entry.price; + const totalPrice = !entry.price + ? "0" + : Amounts.stringify( + Amounts.mult( + Amounts.parseOrThrow(entry.price), + entry.quantity + ).amount + ); + + return ( + <tr key={index}> + <td> + <img + style={{ height: 32, width: 32 }} + src={entry.image ? entry.image : emptyImage} + /> + </td> + <td>{entry.description}</td> + <td> + {entry.quantity === 0 + ? "--" + : `${entry.quantity} ${entry.unit}`} + </td> + <td>{unitPrice}</td> + <td>{totalPrice}</td> + <td class="is-actions-cell right-sticky"> + {actions.map((a, i) => { + return ( + <div key={i} class="buttons is-right"> + <button + class="button is-small is-danger has-tooltip-left" + data-tooltip={a.tooltip} + type="button" + onClick={() => a.handler(entry, index)} + > + {a.name} + </button> + </div> + ); + })} + </td> + </tr> + ); + })} + </tbody> + </table> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/context/backend.ts b/packages/merchant-backoffice-ui/src/context/backend.ts new file mode 100644 index 000000000..9ef7bfdea --- /dev/null +++ b/packages/merchant-backoffice-ui/src/context/backend.ts @@ -0,0 +1,82 @@ +/* + 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 { createContext, h, VNode } from 'preact' +import { useCallback, useContext, useState } from 'preact/hooks' +import { useBackendDefaultToken, useBackendURL } from '../hooks'; + +interface BackendContextType { + url: string; + token?: string; + triedToLog: boolean; + resetBackend: () => void; + clearAllTokens: () => void; + addTokenCleaner: (c: () => void) => void; + updateLoginStatus: (url: string, token?: string) => void; +} + +const BackendContext = createContext<BackendContextType>({ + url: '', + token: undefined, + triedToLog: false, + resetBackend: () => null, + clearAllTokens: () => null, + addTokenCleaner: () => null, + updateLoginStatus: () => null, +}) + +function useBackendContextState(defaultUrl?: string, initialToken?: string): BackendContextType { + const [url, triedToLog, changeBackend, resetBackend] = useBackendURL(defaultUrl); + const [token, _updateToken] = useBackendDefaultToken(initialToken); + const updateToken = (t?: string) => { + _updateToken(t) + } + + const tokenCleaner = useCallback(() => { updateToken(undefined) }, []) + const [cleaners, setCleaners] = useState([tokenCleaner]) + const addTokenCleaner = (c: () => void) => setCleaners(cs => [...cs, c]) + const addTokenCleanerMemo = useCallback((c: () => void) => { addTokenCleaner(c) }, [tokenCleaner]) + + const clearAllTokens = () => { + cleaners.forEach(c => c()) + for (let i = 0; i < localStorage.length; i++) { + const k = localStorage.key(i) + if (k && /^backend-token/.test(k)) localStorage.removeItem(k) + } + resetBackend() + } + + const updateLoginStatus = (url: string, token?: string) => { + changeBackend(url); + if (token) updateToken(token); + }; + + + return { url, token, triedToLog, updateLoginStatus, resetBackend, clearAllTokens, addTokenCleaner: addTokenCleanerMemo } +} + +export const BackendContextProvider = ({ children, defaultUrl, initialToken }: { children: any, defaultUrl?: string, initialToken?: string }): VNode => { + const value = useBackendContextState(defaultUrl, initialToken) + + return h(BackendContext.Provider, { value, children }); +} + +export const useBackendContext = (): BackendContextType => useContext(BackendContext); diff --git a/packages/merchant-backoffice-ui/src/context/config.ts b/packages/merchant-backoffice-ui/src/context/config.ts new file mode 100644 index 000000000..5cd772380 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/context/config.ts @@ -0,0 +1,32 @@ +/* + 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 { createContext } from 'preact' +import { useContext } from 'preact/hooks' + +interface Type { + currency: string; + version: string; +} +const Context = createContext<Type>(null!) + +export const ConfigContextProvider = Context.Provider +export const useConfigContext = (): Type => useContext(Context); diff --git a/packages/merchant-backoffice-ui/src/context/fetch.ts b/packages/merchant-backoffice-ui/src/context/fetch.ts new file mode 100644 index 000000000..ef4dfb7ae --- /dev/null +++ b/packages/merchant-backoffice-ui/src/context/fetch.ts @@ -0,0 +1,54 @@ +/* + 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, createContext, VNode, ComponentChildren } from "preact"; +import { useContext } from "preact/hooks"; +import useSWR from "swr"; +import useSWRInfinite from "swr/infinite"; + +interface Type { + useSWR: typeof useSWR; + useSWRInfinite: typeof useSWRInfinite; +} + +const Context = createContext<Type>({} as any); + +export const useFetchContext = (): Type => useContext(Context); +export const FetchContextProvider = ({ + children, +}: { + children: ComponentChildren; +}): VNode => { + return h(Context.Provider, { value: { useSWR, useSWRInfinite }, children }); +}; + +export const FetchContextProviderTesting = ({ + children, + data, +}: { + children: ComponentChildren; + data: any; +}): VNode => { + return h(Context.Provider, { + value: { useSWR: () => data, useSWRInfinite }, + children, + }); +}; diff --git a/packages/merchant-backoffice-ui/src/context/instance.ts b/packages/merchant-backoffice-ui/src/context/instance.ts new file mode 100644 index 000000000..fecf36426 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/context/instance.ts @@ -0,0 +1,35 @@ +/* + 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 { createContext } from 'preact' +import { useContext } from 'preact/hooks' + +interface Type { + id: string; + token?: string; + admin?: boolean; + changeToken: (t?:string) => void; +} + +const Context = createContext<Type>({} as any) + +export const InstanceContextProvider = Context.Provider +export const useInstanceContext = (): Type => useContext(Context); diff --git a/packages/merchant-backoffice-ui/src/context/listener.ts b/packages/merchant-backoffice-ui/src/context/listener.ts new file mode 100644 index 000000000..659db0a03 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/context/listener.ts @@ -0,0 +1,35 @@ +/* + 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 { createContext } from 'preact' +import { useContext } from 'preact/hooks' + +interface Type { + id: string; + token?: string; + admin?: boolean; + changeToken: (t?:string) => void; +} + +const Context = createContext<Type>({} as any) + +export const ListenerContextProvider = Context.Provider +export const useListenerContext = (): Type => useContext(Context); diff --git a/packages/merchant-backoffice-ui/src/context/translation.ts b/packages/merchant-backoffice-ui/src/context/translation.ts new file mode 100644 index 000000000..952a1e325 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/context/translation.ts @@ -0,0 +1,59 @@ +/* + 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 { createContext, h, VNode } from 'preact' +import { useContext, useEffect } from 'preact/hooks' +import { useLang } from '../hooks' +import * as jedLib from "jed"; +import { strings } from "../i18n/strings"; + +interface Type { + lang: string; + handler: any; + changeLanguage: (l: string) => void; +} +const initial = { + lang: 'en', + handler: null, + changeLanguage: () => { + // do not change anything + } +} +const Context = createContext<Type>(initial) + +interface Props { + initial?: string, + children: any, + forceLang?: string +} + +export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => { + const [lang, changeLanguage] = useLang(initial) + useEffect(() => { + if (forceLang) { + changeLanguage(forceLang) + } + }) + const handler = new jedLib.Jed(strings[lang]); + return h(Context.Provider, { value: { lang, handler, changeLanguage }, children }); +} + +export const useTranslationContext = (): Type => useContext(Context);
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/custom.d.ts b/packages/merchant-backoffice-ui/src/custom.d.ts new file mode 100644 index 000000000..d2705003b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/custom.d.ts @@ -0,0 +1,40 @@ +/* + 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/> + */ +declare module '*.po' { + const content: any; + export default content; +} +declare module 'jed' { + const x: any; + export = x; +} +declare module "*.jpeg" { + const content: any; + export default content; +} +declare module "*.png" { + const content: any; + export default content; +} +declare module '*.svg' { + const content: any; + export default content; +} + +declare module '*.scss' { + const content: Record<string, string>; + export default content; +} diff --git a/packages/merchant-backoffice-ui/src/declaration.d.ts b/packages/merchant-backoffice-ui/src/declaration.d.ts new file mode 100644 index 000000000..f0d257d3c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/declaration.d.ts @@ -0,0 +1,1443 @@ +/* + 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) +*/ + + +type HashCode = string; +type EddsaPublicKey = string; +type EddsaSignature = string; +type WireTransferIdentifierRawP = string; +type RelativeTime = Duration; +type ImageDataUrl = string; + +export interface WithId { + id: string +} + +interface Timestamp { + // Milliseconds since epoch, or the special + // value "forever" to represent an event that will + // never happen. + t_s: number | "never"; +} +interface Duration { + d_us: number | "forever"; +} + +interface WithId { + id: string; +} + +type Amount = string; +type UUID = string; +type Integer = number; + +export namespace ExchangeBackend { + interface WireResponse { + + // 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 { + // 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 { + // 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 { + + // Numeric error code unique to the condition. + // The other arguments are specific to the error value reported here. + code: number; + + // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ... + // Should give a human-readable hint about the error's nature. Optional, may change without notice! + hint?: string; + + // Optional detail about the specific input value that failed. May change without notice! + detail?: string; + + // Name of the parameter that was bogus (if applicable). + parameter?: string; + + // Path to the argument that was bogus (if applicable). + path?: string; + + // Offset of the argument that was bogus (if applicable). + offset?: string; + + // Index of the argument that was bogus (if applicable). + index?: string; + + // Name of the object that was bogus (if applicable). + object?: string; + + // Name of the currency than was problematic (if applicable). + currency?: string; + + // Expected type (if applicable). + type_expected?: string; + + // Type that was provided instead (if applicable). + type_actual?: string; + } + + + // Delivery location, loosely modeled as a subset of + // ISO20022's PostalAddress25. + interface Tax { + // the name of the tax + name: string; + + // amount paid in tax + tax: Amount; + } + + interface Auditor { + // official name + name: string; + + // Auditor's public key + auditor_pub: EddsaPublicKey; + + // Base URL of the auditor + url: string; + } + interface Exchange { + // the exchange's base URL + url: string; + + // master public key of the exchange + master_pub: EddsaPublicKey; + } + + interface Product { + // merchant-internal identifier for the product. + product_id?: string; + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n?: { [lang_tag: string]: string }; + + // The number of units of the product to deliver to the customer. + quantity: Integer; + + // The unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price of the product; this is the total price for quantity times unit of this product. + price?: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for this product. Can be empty. + taxes: Tax[]; + + // time indicating when this product should be delivered + delivery_date?: TalerProtocolTimestamp; + + // Minimum age buyer must have (in years). Default is 0. + minimum_age?: Integer; + } + interface Merchant { + // label for a location with the business address of the merchant + address: Location; + + // the merchant's legal name of business + name: string; + + // label for a location that denotes the jurisdiction for disputes. + // Some of the typical fields for a location (such as a street address) may be absent. + jurisdiction: Location; + } + + interface VersionResponse { + // libtool-style representation of the Merchant protocol version, see + // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning + // The format is "current:revision:age". + version: string; + + // Name of the protocol. + name: "taler-merchant"; + + // Currency supported by this backend. + currency: string; + + } + interface Location { + // Nation with its own government. + country?: string; + + // Identifies a subdivision of a country such as state, region, county. + country_subdivision?: string; + + // Identifies a subdivision within a country sub-division. + district?: string; + + // Name of a built-up area, with defined boundaries, and a local government. + town?: string; + + // Specific location name within the town. + town_location?: string; + + // Identifier consisting of a group of letters and/or numbers that + // is added to a postal address to assist the sorting of mail. + post_code?: string; + + // Name of a street or thoroughfare. + street?: string; + + // Name of the building or house. + building_name?: string; + + // Number that identifies the position of a building on a street. + building_number?: string; + + // Free-form address lines, should not exceed 7 elements. + address_lines?: string[]; + } + namespace Instances { + + //POST /private/instances/$INSTANCE/auth + interface InstanceAuthConfigurationMessage { + // Type of authentication. + // "external": The mechant backend does not do + // any authentication checks. Instead an API + // gateway must do the authentication. + // "token": The merchant checks an auth token. + // See "token" for details. + method: "external" | "token"; + + // For method "external", this field is mandatory. + // The token MUST begin with the string "secret-token:". + // After the auth token has been set (with method "token"), + // the value must be provided in a "Authorization: Bearer $token" + // header. + token?: string; + + } + //POST /private/instances + interface InstanceConfigurationMessage { + // The URI where the wallet will send coins. A merchant may have + // multiple accounts, thus this is an array. Note that by + // removing URIs from this list the respective account is set to + // inactive and thus unavailable for new contracts, but preserved + // in the database as existing offers and contracts may still refer + // to it. + payto_uris: string[]; + + // Name of the merchant instance to create (will become $INSTANCE). + id: string; + + // Merchant name corresponding to this instance. + name: string; + + email: string; + website: string; + // An optional base64-encoded logo image + logo: ImageDataUrl; + + + // "Authentication" header required to authorize management access the instance. + // Optional, if not given authentication will be disabled for + // this instance (hopefully authentication checks are still + // done by some reverse proxy). + auth: InstanceAuthConfigurationMessage; + + // The merchant's physical address (to be put into contracts). + address: Location; + + // The jurisdiction under which the merchant conducts its business + // (to be put into contracts). + jurisdiction: Location; + + // Maximum wire fee this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_wire_fee: Amount; + + // Default factor for wire fee amortization calculations. + // Can be overridden by the frontend on a per-order basis. + default_wire_fee_amortization: Integer; + + // Maximum deposit fee (sum over all coins) this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_deposit_fee: Amount; + + // If the frontend does NOT specify an execution date, how long should + // we tell the exchange to wait to aggregate transactions before + // executing the wire transfer? This delay is added to the current + // time when we generate the advisory execution time for the exchange. + default_wire_transfer_delay: RelativeTime; + + // If the frontend does NOT specify a payment deadline, how long should + // offers we make be valid by default? + default_pay_delay: RelativeTime; + + } + + // PATCH /private/instances/$INSTANCE + interface InstanceReconfigurationMessage { + // The URI where the wallet will send coins. A merchant may have + // multiple accounts, thus this is an array. Note that by + // removing URIs from this list + payto_uris: string[]; + + // Merchant name corresponding to this instance. + name: string; + + // The merchant's physical address (to be put into contracts). + address: Location; + + // The jurisdiction under which the merchant conducts its business + // (to be put into contracts). + jurisdiction: Location; + + // Maximum wire fee this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_wire_fee: Amount; + + // Default factor for wire fee amortization calculations. + // Can be overridden by the frontend on a per-order basis. + default_wire_fee_amortization: Integer; + + // Maximum deposit fee (sum over all coins) this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_deposit_fee: Amount; + + // If the frontend does NOT specify an execution date, how long should + // we tell the exchange to wait to aggregate transactions before + // executing the wire transfer? This delay is added to the current + // time when we generate the advisory execution time for the exchange. + default_wire_transfer_delay: RelativeTime; + + // If the frontend does NOT specify a payment deadline, how long should + // offers we make be valid by default? + default_pay_delay: RelativeTime; + + } + + // GET /private/instances + interface InstancesResponse { + // List of instances that are present in the backend (see Instance) + instances: Instance[]; + } + + interface Instance { + // Merchant name corresponding to this instance. + name: string; + + deleted?: boolean; + + // Merchant instance this response is about ($INSTANCE) + id: string; + + // Public key of the merchant/instance, in Crockford Base32 encoding. + merchant_pub: EddsaPublicKey; + + // List of the payment targets supported by this instance. Clients can + // specify the desired payment target in /order requests. Note that + // front-ends do not have to support wallets selecting payment targets. + payment_targets: string[]; + + } + + //GET /private/instances/$INSTANCE/kyc + interface AccountKycRedirects { + // Array of pending KYCs. + pending_kycs: MerchantAccountKycRedirect[]; + + // Array of exchanges with no reply. + timeout_kycs: ExchangeKycTimeout[]; + + } + interface MerchantAccountKycRedirect { + + // URL that the user should open in a browser to + // proceed with the KYC process (as returned + // by the exchange's /kyc-check/ endpoint). + kyc_url: string; + + // Base URL of the exchange this is about. + exchange_url: string; + + // Our bank wire account this is about. + payto_uri: string; + + } + interface ExchangeKycTimeout { + + // Base URL of the exchange this is about. + exchange_url: string; + + // Numeric error code indicating errors the exchange + // returned, or TALER_EC_INVALID for none. + exchange_code: number; + + // HTTP status code returned by the exchange when we asked for + // information about the KYC status. + // 0 if there was no response at all. + exchange_http_status: number; + + } + + //GET /private/instances/$INSTANCE + interface QueryInstancesResponse { + // The URI where the wallet will send coins. A merchant may have + // multiple accounts, thus this is an array. + accounts: MerchantAccount[]; + + // Merchant name corresponding to this instance. + name: string; + + // Public key of the merchant/instance, in Crockford Base32 encoding. + merchant_pub: EddsaPublicKey; + + // The merchant's physical address (to be put into contracts). + address: Location; + + // The jurisdiction under which the merchant conducts its business + // (to be put into contracts). + jurisdiction: Location; + + // Maximum wire fee this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_wire_fee: Amount; + + // Default factor for wire fee amortization calculations. + // Can be overridden by the frontend on a per-order basis. + default_wire_fee_amortization: Integer; + + // Maximum deposit fee (sum over all coins) this instance is willing to pay. + // Can be overridden by the frontend on a per-order basis. + default_max_deposit_fee: Amount; + + // If the frontend does NOT specify an execution date, how long should + // we tell the exchange to wait to aggregate transactions before + // executing the wire transfer? This delay is added to the current + // time when we generate the advisory execution time for the exchange. + default_wire_transfer_delay: RelativeTime; + + // If the frontend does NOT specify a payment deadline, how long should + // offers we make be valid by default? + default_pay_delay: RelativeTime; + + // Authentication configuration. + // Does not contain the token when token auth is configured. + auth: { + method: "external" | "token"; + token?: string; + }; + } + + interface MerchantAccount { + + // payto:// URI of the account. + payto_uri: string; + + // Hash over the wire details (including over the salt) + h_wire: HashCode; + + // salt used to compute h_wire + salt: HashCode; + + // true if this account is active, + // false if it is historic. + active: boolean; + } + + // DELETE /private/instances/$INSTANCE + + + } + + namespace Products { + // POST /private/products + interface ProductAddDetail { + + // product ID to use. + product_id: string; + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n: { [lang_tag: string]: string }; + + // unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price for one unit of the product. Zero is used + // to imply that this product is not sold separately, or + // that the price is not fixed, and must be supplied by the + // front-end. If non-zero, this price MUST include applicable + // taxes. + price: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for one unit of this product + taxes: Tax[]; + + // Number of units of the product in stock in sum in total, + // including all existing sales ever. Given in product-specific + // units. + // A value of -1 indicates "infinite" (i.e. for "electronic" books). + total_stock: Integer; + + // Identifies where the product is in stock. + address: Location; + + // Identifies when we expect the next restocking to happen. + next_restock?: Timestamp; + + // Minimum age buyer must have (in years). Default is 0. + minimum_age?: Integer; + } + // PATCH /private/products/$PRODUCT_ID + interface ProductPatchDetail { + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n: { [lang_tag: string]: string }; + + // unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price for one unit of the product. Zero is used + // to imply that this product is not sold separately, or + // that the price is not fixed, and must be supplied by the + // front-end. If non-zero, this price MUST include applicable + // taxes. + price: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for one unit of this product + taxes: Tax[]; + + // Number of units of the product in stock in sum in total, + // including all existing sales ever. Given in product-specific + // units. + // A value of -1 indicates "infinite" (i.e. for "electronic" books). + total_stock: Integer; + + // Number of units of the product that were lost (spoiled, stolen, etc.) + total_lost: Integer; + + // Identifies where the product is in stock. + address: Location; + + // Identifies when we expect the next restocking to happen. + next_restock?: Timestamp; + + // Minimum age buyer must have (in years). Default is 0. + minimum_age?: Integer; + } + + // GET /private/products + interface InventorySummaryResponse { + // List of products that are present in the inventory + products: InventoryEntry[]; + } + interface InventoryEntry { + // Product identifier, as found in the product. + product_id: string; + + } + + // GET /private/products/$PRODUCT_ID + interface ProductDetail { + + // Human-readable product description. + description: string; + + // Map from IETF BCP 47 language tags to localized descriptions + description_i18n: { [lang_tag: string]: string }; + + // unit in which the product is measured (liters, kilograms, packages, etc.) + unit: string; + + // The price for one unit of the product. Zero is used + // to imply that this product is not sold separately, or + // that the price is not fixed, and must be supplied by the + // front-end. If non-zero, this price MUST include applicable + // taxes. + price: Amount; + + // An optional base64-encoded product image + image: ImageDataUrl; + + // a list of taxes paid by the merchant for one unit of this product + taxes: Tax[]; + + // Number of units of the product in stock in sum in total, + // including all existing sales ever. Given in product-specific + // units. + // A value of -1 indicates "infinite" (i.e. for "electronic" books). + total_stock: Integer; + + // Number of units of the product that have already been sold. + total_sold: Integer; + + // Number of units of the product that were lost (spoiled, stolen, etc.) + total_lost: Integer; + + // Identifies where the product is in stock. + address: Location; + + // Identifies when we expect the next restocking to happen. + next_restock?: Timestamp; + + // Minimum age buyer must have (in years). Default is 0. + minimum_age?: Integer; + } + + // POST /private/products/$PRODUCT_ID/lock + interface LockRequest { + + // UUID that identifies the frontend performing the lock + // It is suggested that clients use a timeflake for this, + // see https://github.com/anthonynsimon/timeflake + lock_uuid: UUID; + + // How long does the frontend intend to hold the lock + duration: RelativeTime; + + // How many units should be locked? + quantity: Integer; + + } + + // DELETE /private/products/$PRODUCT_ID + + } + + namespace Orders { + + type MerchantOrderStatusResponse = CheckPaymentPaidResponse | + CheckPaymentClaimedResponse | + CheckPaymentUnpaidResponse; + interface CheckPaymentPaidResponse { + // The customer paid for this contract. + order_status: "paid"; + + // Was the payment refunded (even partially)? + refunded: boolean; + + // True if there are any approved refunds that the wallet has + // not yet obtained. + refund_pending: boolean; + + // Did the exchange wire us the funds? + wired: boolean; + + // Total amount the exchange deposited into our bank account + // for this contract, excluding fees. + deposit_total: Amount; + + // Numeric error code indicating errors the exchange + // encountered tracking the wire transfer for this purchase (before + // we even got to specific coin issues). + // 0 if there were no issues. + exchange_ec: number; + + // HTTP status code returned by the exchange when we asked for + // information to track the wire transfer for this purchase. + // 0 if there were no issues. + exchange_hc: number; + + // Total amount that was refunded, 0 if refunded is false. + refund_amount: Amount; + + // Contract terms. + contract_terms: ContractTerms; + + // The wire transfer status from the exchange for this order if + // available, otherwise empty array. + wire_details: TransactionWireTransfer[]; + + // Reports about trouble obtaining wire transfer details, + // empty array if no trouble were encountered. + wire_reports: TransactionWireReport[]; + + // The refund details for this order. One entry per + // refunded coin; empty array if there are no refunds. + refund_details: RefundDetails[]; + + // Status URL, can be used as a redirect target for the browser + // to show the order QR code / trigger the wallet. + order_status_url: string; + } + interface CheckPaymentClaimedResponse { + // A wallet claimed the order, but did not yet pay for the contract. + order_status: "claimed"; + + // Contract terms. + contract_terms: ContractTerms; + + } + interface CheckPaymentUnpaidResponse { + // The order was neither claimed nor paid. + order_status: "unpaid"; + + // when was the order created + creation_time: Timestamp; + + // Order summary text. + summary: string; + + // Total amount of the order (to be paid by the customer). + total_amount: Amount; + + // URI that the wallet must process to complete the payment. + taler_pay_uri: string; + + // Alternative order ID which was paid for already in the same session. + // Only given if the same product was purchased before in the same session. + already_paid_order_id?: string; + + // Fulfillment URL of an already paid order. Only given if under this + // session an already paid order with a fulfillment URL exists. + already_paid_fulfillment_url?: string; + + // Status URL, can be used as a redirect target for the browser + // to show the order QR code / trigger the wallet. + order_status_url: string; + + // We do we NOT return the contract terms here because they may not + // exist in case the wallet did not yet claim them. + } + interface RefundDetails { + // Reason given for the refund. + reason: string; + + // When was the refund approved. + timestamp: Timestamp; + + // Set to true if a refund is still available for the wallet for this payment. + pending: boolean; + + // Total amount that was refunded (minus a refund fee). + amount: Amount; + } + interface TransactionWireTransfer { + // Responsible exchange. + exchange_url: string; + + // 32-byte wire transfer identifier. + wtid: Base32; + + // Execution time of the wire transfer. + execution_time: Timestamp; + + // Total amount that has been wire transferred + // to the merchant. + amount: Amount; + + // Was this transfer confirmed by the merchant via the + // POST /transfers API, or is it merely claimed by the exchange? + confirmed: boolean; + } + interface TransactionWireReport { + // Numerical error code. + code: number; + + // Human-readable error description. + hint: string; + + // Numerical error code from the exchange. + exchange_ec: number; + + // HTTP status code received from the exchange. + exchange_hc: number; + + // Public key of the coin for which we got the exchange error. + coin_pub: CoinPublicKey; + } + + interface OrderHistory { + // timestamp-sorted array of all orders matching the query. + // The order of the sorting depends on the sign of delta. + orders: OrderHistoryEntry[]; + } + interface OrderHistoryEntry { + + // order ID of the transaction related to this entry. + order_id: string; + + // row ID of the order in the database + row_id: number; + + // when the order was created + timestamp: Timestamp; + + // the amount of money the order is for + amount: Amount; + + // the summary of the order + summary: string; + + // whether some part of the order is refundable, + // that is the refund deadline has not yet expired + // and the total amount refunded so far is below + // the value of the original transaction. + refundable: boolean; + + // whether the order has been paid or not + paid: boolean; + } + + interface PostOrderRequest { + // The order must at least contain the minimal + // order detail, but can override all + order: Order; + + // if set, the backend will then set the refund deadline to the current + // time plus the specified delay. If it's not set, refunds will not be + // possible. + refund_delay?: RelativeTime; + + // specifies the payment target preferred by the client. Can be used + // to select among the various (active) wire methods supported by the instance. + payment_target?: string; + + // specifies that some products are to be included in the + // order from the inventory. For these inventory management + // is performed (so the products must be in stock) and + // details are completed from the product data of the backend. + inventory_products?: MinimalInventoryProduct[]; + + // Specifies a lock identifier that was used to + // lock a product in the inventory. Only useful if + // manage_inventory is set. Used in case a frontend + // reserved quantities of the individual products while + // the shopping card was being built. Multiple UUIDs can + // be used in case different UUIDs were used for different + // products (i.e. in case the user started with multiple + // shopping sessions that were combined during checkout). + lock_uuids?: UUID[]; + + // Should a token for claiming the order be generated? + // False can make sense if the ORDER_ID is sufficiently + // high entropy to prevent adversarial claims (like it is + // if the backend auto-generates one). Default is 'true'. + create_token?: boolean; + + } + type Order = MinimalOrderDetail | ContractTerms; + + interface MinimalOrderDetail { + // Amount to be paid by the customer + amount: Amount; + + // Short summary of the order + summary: string; + + // URL that will show that the order was successful after + // it has been paid for. Optional. When POSTing to the + // merchant, the placeholder "${ORDER_ID}" will be + // replaced with the actual order ID (useful if the + // order ID is generated server-side and needs to be + // in the URL). + fulfillment_url?: string; + } + + interface MinimalInventoryProduct { + // Which product is requested (here mandatory!) + product_id: string; + + // How many units of the product are requested + quantity: Integer; + } + interface PostOrderResponse { + // Order ID of the response that was just created + order_id: string; + + // Token that authorizes the wallet to claim the order. + // Provided only if "create_token" was set to 'true' + // in the request. + token?: ClaimToken; + } + interface OutOfStockResponse { + + // Product ID of an out-of-stock item + product_id: string; + + // Requested quantity + requested_quantity: Integer; + + // Available quantity (must be below requested_quanitity) + available_quantity: Integer; + + // When do we expect the product to be again in stock? + // Optional, not given if unknown. + restock_expected?: Timestamp; + } + + interface ForgetRequest { + + // Array of valid JSON paths to forgettable fields in the order's + // contract terms. + fields: string[]; + } + interface RefundRequest { + // Amount to be refunded + refund: Amount; + + // Human-readable refund justification + reason: string; + } + interface MerchantRefundResponse { + + // URL (handled by the backend) that the wallet should access to + // trigger refund processing. + // taler://refund/... + taler_refund_uri: string; + + // Contract hash that a client may need to authenticate an + // HTTP request to obtain the above URI in a wallet-friendly way. + h_contract: HashCode; + } + + } + + namespace Tips { + + // GET /private/reserves + interface TippingReserveStatus { + // Array of all known reserves (possibly empty!) + reserves: ReserveStatusEntry[]; + } + interface ReserveStatusEntry { + // Public key of the reserve + reserve_pub: EddsaPublicKey; + + // Timestamp when it was established + creation_time: Timestamp; + + // Timestamp when it expires + expiration_time: Timestamp; + + // Initial amount as per reserve creation call + merchant_initial_amount: Amount; + + // Initial amount as per exchange, 0 if exchange did + // not confirm reserve creation yet. + exchange_initial_amount: Amount; + + // Amount picked up so far. + pickup_amount: Amount; + + // Amount approved for tips that exceeds the pickup_amount. + committed_amount: Amount; + + // Is this reserve active (false if it was deleted but not purged) + active: boolean; + } + + interface ReserveCreateRequest { + // Amount that the merchant promises to put into the reserve + initial_balance: Amount; + + // Exchange the merchant intends to use for tipping + exchange_url: string; + + // Desired wire method, for example "iban" or "x-taler-bank" + wire_method: string; + } + interface ReserveCreateConfirmation { + // Public key identifying the reserve + reserve_pub: EddsaPublicKey; + + // Wire account of the exchange where to transfer the funds + payto_uri: string; + } + interface TipCreateRequest { + // Amount that the customer should be tipped + amount: Amount; + + // Justification for giving the tip + justification: string; + + // URL that the user should be directed to after tipping, + // will be included in the tip_token. + next_url: string; + } + interface TipCreateConfirmation { + // Unique tip identifier for the tip that was created. + tip_id: HashCode; + + // taler://tip URI for the tip + taler_tip_uri: string; + + // URL that will directly trigger processing + // the tip when the browser is redirected to it + tip_status_url: string; + + // when does the tip expire + tip_expiration: Timestamp; + } + + interface ReserveDetail { + // Timestamp when it was established. + creation_time: Timestamp; + + // Timestamp when it expires. + expiration_time: Timestamp; + + // Initial amount as per reserve creation call. + merchant_initial_amount: Amount; + + // Initial amount as per exchange, 0 if exchange did + // not confirm reserve creation yet. + exchange_initial_amount: Amount; + + // Amount picked up so far. + pickup_amount: Amount; + + // Amount approved for tips that exceeds the pickup_amount. + committed_amount: Amount; + + // Array of all tips created by this reserves (possibly empty!). + // Only present if asked for explicitly. + tips?: TipStatusEntry[]; + + // 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. + tip_id: HashCode; + + // Total amount of the tip that can be withdrawn. + total_amount: Amount; + + // Human-readable reason for why the tip was granted. + reason: string; + } + + interface TipDetails { + // Amount that we authorized for this tip. + total_authorized: Amount; + + // Amount that was picked up by the user already. + total_picked_up: Amount; + + // Human-readable reason given when authorizing the tip. + reason: string; + + // Timestamp indicating when the tip is set to expire (may be in the past). + expiration: Timestamp; + + // Reserve public key from which the tip is funded. + reserve_pub: EddsaPublicKey; + + // Array showing the pickup operations of the wallet (possibly empty!). + // Only present if asked for explicitly. + pickups?: PickupDetail[]; + } + interface PickupDetail { + // Unique identifier for the pickup operation. + pickup_id: HashCode; + + // Number of planchets involved. + num_planchets: Integer; + + // Total amount requested for this pickup_id. + requested_amount: Amount; + } + + } + + namespace Transfers { + + interface TransferList { + // list of all the transfers that fit the filter that we know + transfers: TransferDetails[]; + } + interface TransferDetails { + // how much was wired to the merchant (minus fees) + credit_amount: Amount; + + // raw wire transfer identifier identifying the wire transfer (a base32-encoded value) + wtid: string; + + // target account that received the wire transfer + payto_uri: string; + + // base URL of the exchange that made the wire transfer + exchange_url: string; + + // Serial number identifying the transfer in the merchant backend. + // Used for filgering via offset. + transfer_serial_id: number; + + // Time of the execution of the wire transfer by the exchange, according to the exchange + // Only provided if we did get an answer from the exchange. + execution_time?: Timestamp; + + // True if we checked the exchange's answer and are happy with it. + // False if we have an answer and are unhappy, missing if we + // do not have an answer from the exchange. + verified?: boolean; + + // True if the merchant uses the POST /transfers API to confirm + // that this wire transfer took place (and it is thus not + // something merely claimed by the exchange). + confirmed?: boolean; + } + + interface TransferInformation { + // how much was wired to the merchant (minus fees) + credit_amount: Amount; + + // raw wire transfer identifier identifying the wire transfer (a base32-encoded value) + wtid: WireTransferIdentifierRawP; + + // target account that received the wire transfer + payto_uri: string; + + // base URL of the exchange that made the wire transfer + exchange_url: string; + } + interface MerchantTrackTransferResponse { + // Total amount transferred + total: Amount; + + // Applicable wire fee that was charged + wire_fee: Amount; + + // Time of the execution of the wire transfer by the exchange, according to the exchange + execution_time: Timestamp; + + // details about the deposits + deposits_sums: MerchantTrackTransferDetail[]; + } + interface MerchantTrackTransferDetail { + // Business activity associated with the wire transferred amount + // deposit_value. + order_id: string; + + // The total amount the exchange paid back for order_id. + deposit_value: Amount; + + // applicable fees for the deposit + deposit_fee: Amount; + } + + type ExchangeConflictDetails = WireFeeConflictDetails | TrackTransferConflictDetails + // Note: this is not the full 'proof' of missbehavior, as + // the bogus message from the exchange with a signature + // over the 'different' wire fee is missing. + // + // This information is NOT provided by the current implementation, + // because this would be quite expensive to generate and is + // hardly needed _here_. Once we add automated reports for + // the Taler auditor, we need to generate this data anyway + // and should probably return it here as well. + interface WireFeeConflictDetails { + // Numerical error code: + code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE"; + + // Text describing the issue for humans. + hint: string; + + + // Wire fee (wrongly) charged by the exchange, breaking the + // contract affirmed by the exchange_sig. + wire_fee: Amount; + + // Timestamp of the wire transfer + execution_time: Timestamp; + + // The expected wire fee (as signed by the exchange) + expected_wire_fee: Amount; + + // Expected closing fee (needed to verify signature) + expected_closing_fee: Amount; + + // Start date of the expected fee structure + start_date: Timestamp; + + // End date of the expected fee structure + end_date: Timestamp; + + // Signature of the exchange affirming the expected fee structure + master_sig: EddsaSignature; + + // Master public key of the exchange + master_pub: EddsaPublicKey; + } + interface TrackTransferConflictDetails { + // Numerical error code + code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS"; + + // Text describing the issue for humans. + hint: string; + + // Offset in the exchange_transfer where the + // exchange's response fails to match the exchange_deposit_proof. + conflict_offset: number; + + // The response from the exchange which tells us when the + // coin was returned to us, except that it does not match + // the expected value of the coin. + // + // This field is NOT provided by the current implementation, + // because this would be quite expensive to generate and is + // hardly needed _here_. Once we add automated reports for + // the Taler auditor, we need to generate this data anyway + // and should probably return it here as well. + // exchange_transfer?: TrackTransferResponse; + + // Public key of the exchange used to sign the response to + // our deposit request. + deposit_exchange_pub: EddsaPublicKey; + + // Signature of the exchange signing the (conflicting) response. + // Signs over a struct TALER_DepositConfirmationPS. + deposit_exchange_sig: EddsaSignature; + + // Hash of the merchant's bank account the wire transfer went to + h_wire: HashCode; + + // Hash of the contract terms with the conflicting deposit. + h_contract_terms: HashCode; + + // At what time the exchange received the deposit. Needed + // to verify the \exchange_sig\. + deposit_timestamp: Timestamp; + + // At what time the refund possibility expired (needed to verify exchange_sig). + refund_deadline: Timestamp; + + // Public key of the coin for which we have conflicting information. + coin_pub: EddsaPublicKey; + + // Amount the exchange counted the coin for in the transfer. + amount_with_fee: Amount; + + // Expected value of the coin. + coin_value: Amount; + + // Expected deposit fee of the coin. + coin_fee: Amount; + + // Expected deposit fee of the coin. + deposit_fee: Amount; + + } + + // interface TrackTransferProof { + // // signature from the exchange made with purpose + // // TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT + // exchange_sig: EddsaSignature; + + // // public EdDSA key of the exchange that was used to generate the signature. + // // Should match one of the exchange's signing keys from /keys. Again given + // // explicitly as the client might otherwise be confused by clock skew as to + // // which signing key was used. + // exchange_pub: EddsaSignature; + + // // hash of the wire details (identical for all deposits) + // // Needed to check the exchange_sig + // h_wire: HashCode; + // } + + } + + + interface ContractTerms { + // Human-readable description of the whole purchase + summary: string; + + // Map from IETF BCP 47 language tags to localized summaries + summary_i18n?: { [lang_tag: string]: string }; + + // Unique, free-form identifier for the proposal. + // Must be unique within a merchant instance. + // For merchants that do not store proposals in their DB + // before the customer paid for them, the order_id can be used + // by the frontend to restore a proposal from the information + // encoded in it (such as a short product identifier and timestamp). + order_id: string; + + // Total price for the transaction. + // The exchange will subtract deposit fees from that amount + // before transferring it to the merchant. + amount: Amount; + + // The URL for this purchase. Every time is is visited, the merchant + // will send back to the customer the same proposal. Clearly, this URL + // can be bookmarked and shared by users. + fulfillment_url?: string; + + // Maximum total deposit fee accepted by the merchant for this contract + max_fee: Amount; + + // Maximum wire fee accepted by the merchant (customer share to be + // divided by the 'wire_fee_amortization' factor, and further reduced + // if deposit fees are below 'max_fee'). Default if missing is zero. + max_wire_fee: Amount; + + // Over how many customer transactions does the merchant expect to + // amortize wire fees on average? If the exchange's wire fee is + // above 'max_wire_fee', the difference is divided by this number + // to compute the expected customer's contribution to the wire fee. + // The customer's contribution may further be reduced by the difference + // between the 'max_fee' and the sum of the actual deposit fees. + // Optional, default value if missing is 1. 0 and negative values are + // invalid and also interpreted as 1. + wire_fee_amortization: number; + + // List of products that are part of the purchase (see Product). + products: Product[]; + + // Time when this contract was generated + timestamp: TalerProtocolTimestamp; + + // After this deadline has passed, no refunds will be accepted. + refund_deadline: TalerProtocolTimestamp; + + // After this deadline, the merchant won't accept payments for the contact + pay_deadline: TalerProtocolTimestamp; + + // Transfer deadline for the exchange. Must be in the + // deposit permissions of coins used to pay for this order. + wire_transfer_deadline: TalerProtocolTimestamp; + + // Merchant's public key used to sign this proposal; this information + // is typically added by the backend Note that this can be an ephemeral key. + merchant_pub: EddsaPublicKey; + + // Base URL of the (public!) merchant backend API. + // Must be an absolute URL that ends with a slash. + merchant_base_url: string; + + // More info about the merchant, see below + merchant: Merchant; + + // The hash of the merchant instance's wire details. + h_wire: HashCode; + + // Wire transfer method identifier for the wire method associated with h_wire. + // The wallet may only select exchanges via a matching auditor if the + // exchange also supports this wire method. + // The wire transfer fees must be added based on this wire transfer method. + wire_method: string; + + // Any exchanges audited by these auditors are accepted by the merchant. + auditors: Auditor[]; + + // Exchanges that the merchant accepts even if it does not accept any auditors that audit them. + exchanges: Exchange[]; + + // Delivery location for (all!) products. + delivery_location?: Location; + + // Time indicating when the order should be delivered. + // May be overwritten by individual products. + delivery_date?: TalerProtocolTimestamp; + + // Nonce generated by the wallet and echoed by the merchant + // in this field when the proposal is generated. + nonce: string; + + // Specifies for how long the wallet should try to get an + // automatic refund for the purchase. If this field is + // present, the wallet should wait for a few seconds after + // the purchase and then automatically attempt to obtain + // a refund. The wallet should probe until "delay" + // after the payment was successful (i.e. via long polling + // or via explicit requests with exponential back-off). + // + // In particular, if the wallet is offline + // at that time, it MUST repeat the request until it gets + // one response from the merchant after the delay has expired. + // If the refund is granted, the wallet MUST automatically + // recover the payment. This is used in case a merchant + // knows that it might be unable to satisfy the contract and + // desires for the wallet to attempt to get the refund without any + // customer interaction. Note that it is NOT an error if the + // merchant does not grant a refund. + auto_refund?: RelativeTime; + + // Extra data that is only interpreted by the merchant frontend. + // Useful when the merchant needs to store extra information on a + // contract without storing it separately in their database. + extra?: any; + + // Minimum age buyer must have (in years). Default is 0. + minimum_age?: Integer; + } + +} diff --git a/packages/merchant-backoffice-ui/src/hooks/async.ts b/packages/merchant-backoffice-ui/src/hooks/async.ts new file mode 100644 index 000000000..fd550043b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/async.ts @@ -0,0 +1,76 @@ +/* + 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 { useState } from "preact/hooks"; +import { cancelPendingRequest } from "./backend"; + +export interface Options { + slowTolerance: number, +} + +export interface AsyncOperationApi<T> { + request: (...a: any) => void, + cancel: () => void, + data: T | undefined, + isSlow: boolean, + isLoading: boolean, + error: string | undefined +} + +export function useAsync<T>(fn?: (...args: any) => Promise<T>, { slowTolerance: tooLong }: Options = { slowTolerance: 1000 }): AsyncOperationApi<T> { + const [data, setData] = useState<T | undefined>(undefined); + const [isLoading, setLoading] = useState<boolean>(false); + const [error, setError] = useState<any>(undefined); + const [isSlow, setSlow] = useState(false) + + const request = async (...args: any) => { + if (!fn) return; + setLoading(true); + + const handler = setTimeout(() => { + setSlow(true) + }, tooLong) + + try { + const result = await fn(...args); + setData(result); + } catch (error) { + setError(error); + } + setLoading(false); + setSlow(false) + clearTimeout(handler) + }; + + function cancel() { + cancelPendingRequest() + setLoading(false); + setSlow(false) + } + + return { + request, + cancel, + data, + isSlow, + isLoading, + error + }; +} diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts new file mode 100644 index 000000000..789cfc81c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts @@ -0,0 +1,319 @@ +/* + 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 { useSWRConfig } from "swr"; +import axios, { AxiosError, AxiosResponse } from "axios"; +import { MerchantBackend } from "../declaration"; +import { useBackendContext } from "../context/backend"; +import { useEffect, useState } from "preact/hooks"; +import { DEFAULT_REQUEST_TIMEOUT } from "../utils/constants"; +import { axiosHandler, removeAxiosCancelToken } from "../utils/switchableAxios"; + +export function useMatchMutate(): ( + re: RegExp, + value?: unknown +) => Promise<any> { + const { cache, mutate } = useSWRConfig(); + + if (!(cache instanceof Map)) { + throw new Error( + "matchMutate requires the cache provider to be a Map instance" + ); + } + + return function matchRegexMutate(re: RegExp, value?: unknown) { + const allKeys = Array.from(cache.keys()); + // console.log(allKeys) + const keys = allKeys.filter((key) => re.test(key)); + // console.log(allKeys.length, keys.length) + const mutations = keys.map((key) => { + // console.log(key) + mutate(key, value, true); + }); + return Promise.all(mutations); + }; +} + +export type HttpResponse<T> = + | HttpResponseOk<T> + | HttpResponseLoading<T> + | HttpError; +export type HttpResponsePaginated<T> = + | HttpResponseOkPaginated<T> + | HttpResponseLoading<T> + | HttpError; + +export interface RequestInfo { + url: string; + hasToken: boolean; + params: unknown; + data: unknown; + status: number; +} + +interface HttpResponseLoading<T> { + ok?: false; + loading: true; + clientError?: false; + serverError?: false; + + data?: T; +} +export interface HttpResponseOk<T> { + ok: true; + loading?: false; + clientError?: false; + serverError?: false; + + data: T; + info?: RequestInfo; +} + +export type HttpResponseOkPaginated<T> = HttpResponseOk<T> & WithPagination; + +export interface WithPagination { + loadMore: () => void; + loadMorePrev: () => void; + isReachingEnd?: boolean; + isReachingStart?: boolean; +} + +export type HttpError = + | HttpResponseClientError + | HttpResponseServerError + | HttpResponseUnexpectedError; +export interface SwrError { + info: unknown; + status: number; + message: string; +} +export interface HttpResponseServerError { + ok?: false; + loading?: false; + clientError?: false; + serverError: true; + + error?: MerchantBackend.ErrorDetail; + status: number; + message: string; + info?: RequestInfo; +} +interface HttpResponseClientError { + ok?: false; + loading?: false; + clientError: true; + serverError?: false; + + info?: RequestInfo; + isUnauthorized: boolean; + isNotfound: boolean; + status: number; + error?: MerchantBackend.ErrorDetail; + message: string; +} + +interface HttpResponseUnexpectedError { + ok?: false; + loading?: false; + clientError?: false; + serverError?: false; + + info?: RequestInfo; + status?: number; + error: unknown; + message: string; +} + +type Methods = "get" | "post" | "patch" | "delete" | "put"; + +interface RequestOptions { + method?: Methods; + token?: string; + data?: unknown; + params?: unknown; +} + +function buildRequestOk<T>( + res: AxiosResponse<T>, + url: string, + hasToken: boolean +): HttpResponseOk<T> { + return { + ok: true, + data: res.data, + info: { + params: res.config.params, + data: res.config.data, + url, + hasToken, + status: res.status, + }, + }; +} + +// function buildResponse<T>(data?: T, error?: MerchantBackend.ErrorDetail, isValidating?: boolean): HttpResponse<T> { +// if (isValidating) return {loading: true} +// if (error) return buildRequestFailed() +// } + +function buildRequestFailed( + ex: AxiosError<MerchantBackend.ErrorDetail>, + url: string, + hasToken: boolean +): + | HttpResponseClientError + | HttpResponseServerError + | HttpResponseUnexpectedError { + const status = ex.response?.status; + + const info: RequestInfo = { + data: ex.request?.data, + params: ex.request?.params, + url, + hasToken, + status: status || 0, + }; + + if (status && status >= 400 && status < 500) { + const error: HttpResponseClientError = { + clientError: true, + isNotfound: status === 404, + isUnauthorized: status === 401, + status, + info, + message: ex.response?.data?.hint || ex.message, + error: ex.response?.data, + }; + return error; + } + if (status && status >= 500 && status < 600) { + const error: HttpResponseServerError = { + serverError: true, + status, + info, + message: + `${ex.response?.data?.hint} (code ${ex.response?.data?.code})` || + ex.message, + error: ex.response?.data, + }; + return error; + } + + const error: HttpResponseUnexpectedError = { + info, + status, + error: ex, + message: ex.message, + }; + + return error; +} + +const CancelToken = axios.CancelToken; +let source = CancelToken.source(); + +export function cancelPendingRequest(): void { + source.cancel("canceled by the user"); + source = CancelToken.source(); +} + +export function isAxiosError<T>( + error: AxiosError | any +): error is AxiosError<T> { + return error && error.isAxiosError; +} + +export async function request<T>( + url: string, + options: RequestOptions = {} +): Promise<HttpResponseOk<T>> { + const headers = options.token + ? { Authorization: `Bearer ${options.token}` } + : undefined; + + try { + const res = await axiosHandler({ + url, + responseType: "json", + headers, + cancelToken: !removeAxiosCancelToken ? source.token : undefined, + method: options.method || "get", + data: options.data, + params: options.params, + timeout: DEFAULT_REQUEST_TIMEOUT * 1000, + }); + return buildRequestOk<T>(res, url, !!options.token); + } catch (e) { + if (isAxiosError<MerchantBackend.ErrorDetail>(e)) { + const error = buildRequestFailed(e, url, !!options.token); + throw error; + } + throw e; + } +} + +export function multiFetcher<T>( + urls: string[], + token: string, + backend: string +): Promise<HttpResponseOk<T>[]> { + return Promise.all(urls.map((url) => fetcher<T>(url, token, backend))); +} + +export function fetcher<T>( + url: string, + token: string, + backend: string +): Promise<HttpResponseOk<T>> { + return request<T>(`${backend}${url}`, { token }); +} + +export function useBackendInstancesTestForAdmin(): HttpResponse<MerchantBackend.Instances.InstancesResponse> { + const { url, token } = useBackendContext(); + + type Type = MerchantBackend.Instances.InstancesResponse; + + const [result, setResult] = useState<HttpResponse<Type>>({ loading: true }); + + useEffect(() => { + request<Type>(`${url}/management/instances`, { token }) + .then((data) => setResult(data)) + .catch((error) => setResult(error)); + }, [url, token]); + + return result; +} + +export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse> { + const { url, token } = useBackendContext(); + + type Type = MerchantBackend.VersionResponse; + + const [result, setResult] = useState<HttpResponse<Type>>({ loading: true }); + + useEffect(() => { + request<Type>(`${url}/config`, { token }) + .then((data) => setResult(data)) + .catch((error) => setResult(error)); + }, [url, token]); + + return result; +} diff --git a/packages/merchant-backoffice-ui/src/hooks/index.ts b/packages/merchant-backoffice-ui/src/hooks/index.ts new file mode 100644 index 000000000..a647e3e6c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/index.ts @@ -0,0 +1,110 @@ +/* + 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 { StateUpdater, useCallback, useState } from "preact/hooks"; +import { ValueOrFunction } from '../utils/types'; + + +const calculateRootPath = () => { + const rootPath = typeof window !== undefined ? window.location.origin + window.location.pathname : '/' + return rootPath +} + +export function useBackendURL(url?: string): [string, boolean, StateUpdater<string>, () => void] { + const [value, setter] = useNotNullLocalStorage('backend-url', url || calculateRootPath()) + const [triedToLog, setTriedToLog] = useLocalStorage('tried-login') + + const checkedSetter = (v: ValueOrFunction<string>) => { + setTriedToLog('yes') + return setter(p => (v instanceof Function ? v(p) : v).replace(/\/$/, '')) + } + + const resetBackend = () => { + setTriedToLog(undefined) + } + return [value, !!triedToLog, checkedSetter, resetBackend] +} + +export function useBackendDefaultToken(initialValue?: string): [string | undefined, StateUpdater<string | undefined>] { + return useLocalStorage('backend-token', initialValue) +} + +export function useBackendInstanceToken(id: string): [string | undefined, StateUpdater<string | undefined>] { + const [token, setToken] = useLocalStorage(`backend-token-${id}`) + const [defaultToken, defaultSetToken] = useBackendDefaultToken() + + // instance named 'default' use the default token + if (id === 'default') { + return [defaultToken, defaultSetToken] + } + + return [token, setToken] +} + +export function useLang(initial?: string): [string, StateUpdater<string>] { + const browserLang = typeof window !== "undefined" ? navigator.language || (navigator as any).userLanguage : undefined; + const defaultLang = (browserLang || initial || 'en').substring(0, 2) + return useNotNullLocalStorage('lang-preference', defaultLang) +} + +export function useLocalStorage(key: string, initialValue?: string): [string | undefined, StateUpdater<string | undefined>] { + const [storedValue, setStoredValue] = useState<string | undefined>((): string | undefined => { + return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue; + }); + + const setValue = (value?: string | ((val?: string) => string | undefined)) => { + setStoredValue(p => { + const toStore = value instanceof Function ? value(p) : value + if (typeof window !== "undefined") { + if (!toStore) { + window.localStorage.removeItem(key) + } else { + window.localStorage.setItem(key, toStore); + } + } + return toStore + }) + }; + + return [storedValue, setValue]; +} + +export function useNotNullLocalStorage(key: string, initialValue: string): [string, StateUpdater<string>] { + const [storedValue, setStoredValue] = useState<string>((): string => { + return typeof window !== "undefined" ? window.localStorage.getItem(key) || initialValue : initialValue; + }); + + const setValue = (value: string | ((val: string) => string)) => { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + if (typeof window !== "undefined") { + if (!valueToStore) { + window.localStorage.removeItem(key) + } else { + window.localStorage.setItem(key, valueToStore); + } + } + }; + + return [storedValue, setValue]; +} + + diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts b/packages/merchant-backoffice-ui/src/hooks/instance.ts new file mode 100644 index 000000000..748bb82af --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/instance.ts @@ -0,0 +1,292 @@ +/* + 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/> + */ +import useSWR, { useSWRConfig } from "swr"; +import { useBackendContext } from "../context/backend"; +import { useInstanceContext } from "../context/instance"; +import { MerchantBackend } from "../declaration"; +import { + fetcher, + HttpError, + HttpResponse, + HttpResponseOk, + request, + useMatchMutate, +} from "./backend"; + +interface InstanceAPI { + updateInstance: ( + data: MerchantBackend.Instances.InstanceReconfigurationMessage + ) => Promise<void>; + deleteInstance: () => Promise<void>; + clearToken: () => Promise<void>; + setNewToken: (token: string) => Promise<void>; +} + +export function useAdminAPI(): AdminAPI { + const { url, token } = useBackendContext(); + const mutateAll = useMatchMutate(); + + const createInstance = async ( + instance: MerchantBackend.Instances.InstanceConfigurationMessage + ): Promise<void> => { + await request(`${url}/management/instances`, { + method: "post", + token, + data: instance, + }); + + mutateAll(/\/management\/instances/); + }; + + const deleteInstance = async (id: string): Promise<void> => { + await request(`${url}/management/instances/${id}`, { + method: "delete", + token, + }); + + mutateAll(/\/management\/instances/); + }; + + const purgeInstance = async (id: string): Promise<void> => { + await request(`${url}/management/instances/${id}`, { + method: "delete", + token, + params: { + purge: "YES", + }, + }); + + mutateAll(/\/management\/instances/); + }; + + return { createInstance, deleteInstance, purgeInstance }; +} + +export interface AdminAPI { + createInstance: ( + data: MerchantBackend.Instances.InstanceConfigurationMessage + ) => Promise<void>; + deleteInstance: (id: string) => Promise<void>; + purgeInstance: (id: string) => Promise<void>; +} + +export function useManagementAPI(instanceId: string): InstanceAPI { + const mutateAll = useMatchMutate(); + const { url, token, updateLoginStatus } = useBackendContext(); + + const updateInstance = async ( + instance: MerchantBackend.Instances.InstanceReconfigurationMessage + ): Promise<void> => { + await request(`${url}/management/instances/${instanceId}`, { + method: "patch", + token, + data: instance, + }); + + mutateAll(/\/management\/instances/); + }; + + const deleteInstance = async (): Promise<void> => { + await request(`${url}/management/instances/${instanceId}`, { + method: "delete", + token, + }); + + mutateAll(/\/management\/instances/); + }; + + const clearToken = async (): Promise<void> => { + await request(`${url}/management/instances/${instanceId}/auth`, { + method: "post", + token, + data: { method: "external" }, + }); + + mutateAll(/\/management\/instances/); + }; + + const setNewToken = async (newToken: string): Promise<void> => { + await request(`${url}/management/instances/${instanceId}/auth`, { + method: "post", + token, + data: { method: "token", token: newToken }, + }); + + updateLoginStatus(url, newToken) + mutateAll(/\/management\/instances/); + }; + + return { updateInstance, deleteInstance, setNewToken, clearToken }; +} + +export function useInstanceAPI(): InstanceAPI { + const { mutate } = useSWRConfig(); + const { url: baseUrl, token: adminToken, updateLoginStatus } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: adminToken } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken }; + + const updateInstance = async ( + instance: MerchantBackend.Instances.InstanceReconfigurationMessage + ): Promise<void> => { + await request(`${url}/private/`, { + method: "patch", + token, + data: instance, + }); + + if (adminToken) mutate(["/private/instances", adminToken, baseUrl], null); + mutate([`/private/`, token, url], null); + }; + + const deleteInstance = async (): Promise<void> => { + await request(`${url}/private/`, { + method: "delete", + token: adminToken, + }); + + if (adminToken) mutate(["/private/instances", adminToken, baseUrl], null); + mutate([`/private/`, token, url], null); + }; + + const clearToken = async (): Promise<void> => { + await request(`${url}/private/auth`, { + method: "post", + token, + data: { method: "external" }, + }); + + mutate([`/private/`, token, url], null); + }; + + const setNewToken = async (newToken: string): Promise<void> => { + await request(`${url}/private/auth`, { + method: "post", + token, + data: { method: "token", token: newToken }, + }); + + updateLoginStatus(baseUrl, newToken) + mutate([`/private/`, token, url], null); + }; + + return { updateInstance, deleteInstance, setNewToken, clearToken }; +} + +export function useInstanceDetails(): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: baseToken } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken }; + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, + HttpError + >([`/private/`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + errorRetryCount: 0, + errorRetryInterval: 1, + shouldRetryOnError: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} + +type KYCStatus = + | { type: "ok" } + | { type: "redirect"; status: MerchantBackend.Instances.AccountKycRedirects }; + +export function useInstanceKYCDetails(): HttpResponse<KYCStatus> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: baseToken } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken }; + + const { data, error } = useSWR< + HttpResponseOk<MerchantBackend.Instances.AccountKycRedirects>, + HttpError + >([`/private/kyc`, token, url], fetcher, { + refreshInterval: 5000, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + errorRetryCount: 0, + errorRetryInterval: 1, + shouldRetryOnError: false, + }); + + if (data) { + if (data.info?.status === 202) + return { ok: true, data: { type: "redirect", status: data.data } }; + return { ok: true, data: { type: "ok" } }; + } + if (error) return error; + return { loading: true }; +} + +export function useManagedInstanceDetails( + instanceId: string +): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> { + const { url, token } = useBackendContext(); + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, + HttpError + >([`/management/instances/${instanceId}`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + errorRetryCount: 0, + errorRetryInterval: 1, + shouldRetryOnError: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} + +export function useBackendInstances(): HttpResponse<MerchantBackend.Instances.InstancesResponse> { + const { url } = useBackendContext(); + const { token } = useInstanceContext(); + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Instances.InstancesResponse>, + HttpError + >(["/management/instances", token, url], fetcher); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} diff --git a/packages/merchant-backoffice-ui/src/hooks/listener.ts b/packages/merchant-backoffice-ui/src/hooks/listener.ts new file mode 100644 index 000000000..e7e3327b7 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/listener.ts @@ -0,0 +1,81 @@ +/* + 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 { useState } from "preact/hooks"; + +/** + * This component is used when a component wants one child to have a trigger for + * an action (a button) and other child have the action implemented (like + * gathering information with a form). The difference with other approaches is + * that in this case the parent component is not holding the state. + * + * It will return a subscriber and activator. + * + * The activator may be undefined, if it is undefined it is indicating that the + * subscriber is not ready to be called. + * + * The subscriber will receive a function (the listener) that will be call when the + * activator runs. The listener must return the collected information. + * + * As a result, when the activator is triggered by a child component, the + * @action function is called receives the information from the listener defined by other + * child component + * + * @param action from <T> to <R> + * @returns activator and subscriber, undefined activator means that there is not subscriber + */ + +export function useListener<T, R = any>(action: (r: T) => Promise<R>): [undefined | (() => Promise<R>), (listener?: () => T) => void] { + type RunnerHandler = { toBeRan?: () => Promise<R>; }; + const [state, setState] = useState<RunnerHandler>({}); + + /** + * subscriber will receive a method that will be call when the activator runs + * + * @param listener function to be run when the activator runs + */ + const subscriber = (listener?: () => T) => { + if (listener) { + setState({ + toBeRan: () => { + const whatWeGetFromTheListener = listener(); + return action(whatWeGetFromTheListener); + } + }); + } else { + setState({ + toBeRan: undefined + }) + } + }; + + /** + * activator will call runner if there is someone subscribed + */ + const activator = state.toBeRan ? async () => { + if (state.toBeRan) { + return state.toBeRan(); + } + return Promise.reject(); + } : undefined; + + return [activator, subscriber]; +} diff --git a/packages/merchant-backoffice-ui/src/hooks/notifications.ts b/packages/merchant-backoffice-ui/src/hooks/notifications.ts new file mode 100644 index 000000000..1c0c37308 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/notifications.ts @@ -0,0 +1,48 @@ +/* + 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 { useState } from "preact/hooks"; +import { Notification } from '../utils/types'; + +interface Result { + notifications: Notification[]; + pushNotification: (n: Notification) => void; + removeNotification: (n: Notification) => void; +} + +type NotificationWithDate = Notification & { since: Date } + +export function useNotifications(initial: Notification[] = [], timeout = 3000): Result { + const [notifications, setNotifications] = useState<(NotificationWithDate)[]>(initial.map(i => ({...i, since: new Date() }))) + + const pushNotification = (n: Notification): void => { + const entry = { ...n, since: new Date() } + setNotifications(ns => [...ns, entry]) + if (n.type !== 'ERROR') setTimeout(() => { + setNotifications(ns => ns.filter(x => x.since !== entry.since)) + }, timeout) + } + + const removeNotification = (notif: Notification) => { + setNotifications((ns: NotificationWithDate[]) => ns.filter(n => n !== notif)) + } + return { notifications, pushNotification, removeNotification } +} diff --git a/packages/merchant-backoffice-ui/src/hooks/order.ts b/packages/merchant-backoffice-ui/src/hooks/order.ts new file mode 100644 index 000000000..d0829683d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/order.ts @@ -0,0 +1,323 @@ +/* + 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/> + */ +import { useEffect, useState } from "preact/hooks"; +import useSWR, { useSWRConfig } from "swr"; +import { useBackendContext } from "../context/backend"; +import { useInstanceContext } from "../context/instance"; +import { MerchantBackend } from "../declaration"; +import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants"; +import { + fetcher, + HttpError, + HttpResponse, + HttpResponseOk, + HttpResponsePaginated, + request, + useMatchMutate, +} from "./backend"; + +export interface OrderAPI { + //FIXME: add OutOfStockResponse on 410 + createOrder: ( + data: MerchantBackend.Orders.PostOrderRequest + ) => Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>>; + forgetOrder: ( + id: string, + data: MerchantBackend.Orders.ForgetRequest + ) => Promise<HttpResponseOk<void>>; + refundOrder: ( + id: string, + data: MerchantBackend.Orders.RefundRequest + ) => Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>>; + deleteOrder: (id: string) => Promise<HttpResponseOk<void>>; + getPaymentURL: (id: string) => Promise<HttpResponseOk<string>>; +} + +type YesOrNo = "yes" | "no"; + +export function orderFetcher<T>( + url: string, + token: string, + backend: string, + paid?: YesOrNo, + refunded?: YesOrNo, + wired?: YesOrNo, + searchDate?: Date, + delta?: number +): Promise<HttpResponseOk<T>> { + const date_ms = + delta && delta < 0 && searchDate + ? searchDate.getTime() + 1 + : searchDate?.getTime(); + const params: any = {}; + if (paid !== undefined) params.paid = paid; + if (delta !== undefined) params.delta = delta; + if (refunded !== undefined) params.refunded = refunded; + if (wired !== undefined) params.wired = wired; + if (date_ms !== undefined) params.date_ms = date_ms; + return request<T>(`${backend}${url}`, { token, params }); +} + +export function useOrderAPI(): OrderAPI { + const mutateAll = useMatchMutate(); + const { url: baseUrl, token: adminToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { + url: baseUrl, + token: adminToken, + } + : { + url: `${baseUrl}/instances/${id}`, + token: instanceToken, + }; + + const createOrder = async ( + data: MerchantBackend.Orders.PostOrderRequest + ): Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>> => { + const res = await request<MerchantBackend.Orders.PostOrderResponse>( + `${url}/private/orders`, + { + method: "post", + token, + data, + } + ); + await mutateAll(/.*private\/orders.*/); + // mutate('') + return res; + }; + const refundOrder = async ( + orderId: string, + data: MerchantBackend.Orders.RefundRequest + ): Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>> => { + mutateAll(/@"\/private\/orders"@/); + const res = request<MerchantBackend.Orders.MerchantRefundResponse>( + `${url}/private/orders/${orderId}/refund`, + { + method: "post", + token, + data, + } + ); + + // order list returns refundable information, so we must evict everything + await mutateAll(/.*private\/orders.*/); + return res + }; + + const forgetOrder = async ( + orderId: string, + data: MerchantBackend.Orders.ForgetRequest + ): Promise<HttpResponseOk<void>> => { + mutateAll(/@"\/private\/orders"@/); + const res = request<void>(`${url}/private/orders/${orderId}/forget`, { + method: "patch", + token, + data, + }); + // we may be forgetting some fields that are pare of the listing, so we must evict everything + await mutateAll(/.*private\/orders.*/); + return res + }; + const deleteOrder = async ( + orderId: string + ): Promise<HttpResponseOk<void>> => { + mutateAll(/@"\/private\/orders"@/); + const res = request<void>(`${url}/private/orders/${orderId}`, { + method: "delete", + token, + }); + await mutateAll(/.*private\/orders.*/); + return res + }; + + const getPaymentURL = async ( + orderId: string + ): Promise<HttpResponseOk<string>> => { + return request<MerchantBackend.Orders.MerchantOrderStatusResponse>( + `${url}/private/orders/${orderId}`, + { + method: "get", + token, + } + ).then((res) => { + const url = + res.data.order_status === "unpaid" + ? res.data.taler_pay_uri + : res.data.contract_terms.fulfillment_url; + const response: HttpResponseOk<string> = res as any; + response.data = url || ""; + return response; + }); + }; + + return { createOrder, forgetOrder, deleteOrder, refundOrder, getPaymentURL }; +} + +export function useOrderDetails( + oderId: string +): HttpResponse<MerchantBackend.Orders.MerchantOrderStatusResponse> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: baseToken, } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken, }; + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Orders.MerchantOrderStatusResponse>, + HttpError + >([`/private/orders/${oderId}`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} + +export interface InstanceOrderFilter { + paid?: YesOrNo; + refunded?: YesOrNo; + wired?: YesOrNo; + date?: Date; +} + +export function useInstanceOrders( + args?: InstanceOrderFilter, + updateFilter?: (d: Date) => void +): HttpResponsePaginated<MerchantBackend.Orders.OrderHistory> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: baseToken, } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken, }; + + const [pageBefore, setPageBefore] = useState(1); + const [pageAfter, setPageAfter] = useState(1); + + const totalAfter = pageAfter * PAGE_SIZE; + const totalBefore = args?.date ? pageBefore * PAGE_SIZE : 0; + + /** + * FIXME: this can be cleaned up a little + * + * the logic of double query should be inside the orderFetch so from the hook perspective and cache + * is just one query and one error status + */ + const { + data: beforeData, + error: beforeError, + isValidating: loadingBefore, + } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>( + [ + `/private/orders`, + token, + url, + args?.paid, + args?.refunded, + args?.wired, + args?.date, + totalBefore, + ], + orderFetcher + ); + const { + data: afterData, + error: afterError, + isValidating: loadingAfter, + } = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>( + [ + `/private/orders`, + token, + url, + args?.paid, + args?.refunded, + args?.wired, + args?.date, + -totalAfter, + ], + orderFetcher + ); + + //this will save last result + const [lastBefore, setLastBefore] = useState< + HttpResponse<MerchantBackend.Orders.OrderHistory> + >({ loading: true }); + const [lastAfter, setLastAfter] = useState< + HttpResponse<MerchantBackend.Orders.OrderHistory> + >({ loading: true }); + useEffect(() => { + if (afterData) setLastAfter(afterData); + if (beforeData) setLastBefore(beforeData); + }, [afterData, beforeData]); + + if (beforeError) return beforeError; + if (afterError) return afterError; + + // if the query returns less that we ask, then we have reach the end or beginning + const isReachingEnd = afterData && afterData.data.orders.length < totalAfter; + const isReachingStart = args?.date === undefined || + (beforeData && beforeData.data.orders.length < totalBefore); + + const pagination = { + isReachingEnd, + isReachingStart, + loadMore: () => { + if (!afterData || isReachingEnd) return; + if (afterData.data.orders.length < MAX_RESULT_SIZE) { + setPageAfter(pageAfter + 1); + } else { + const from = + afterData.data.orders[afterData.data.orders.length - 1].timestamp + .t_s; + if (from && from !== "never" && updateFilter) updateFilter(new Date(from * 1000)); + } + }, + loadMorePrev: () => { + if (!beforeData || isReachingStart) return; + if (beforeData.data.orders.length < MAX_RESULT_SIZE) { + setPageBefore(pageBefore + 1); + } else if (beforeData) { + const from = + beforeData.data.orders[beforeData.data.orders.length - 1].timestamp + .t_s; + if (from && from !== "never" && updateFilter) updateFilter(new Date(from * 1000)); + } + }, + }; + + const orders = + !beforeData || !afterData + ? [] + : (beforeData || lastBefore).data.orders + .slice() + .reverse() + .concat((afterData || lastAfter).data.orders); + if (loadingAfter || loadingBefore) return { loading: true, data: { orders } }; + if (beforeData && afterData) { + return { ok: true, data: { orders }, ...pagination }; + } + return { loading: true }; +} diff --git a/packages/merchant-backoffice-ui/src/hooks/product.ts b/packages/merchant-backoffice-ui/src/hooks/product.ts new file mode 100644 index 000000000..c99542bc9 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/product.ts @@ -0,0 +1,187 @@ +/* + 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/> + */ +import useSWR, { useSWRConfig } from "swr"; +import { useBackendContext } from "../context/backend"; +import { useInstanceContext } from "../context/instance"; +import { MerchantBackend, WithId } from "../declaration"; +import { + fetcher, + HttpError, + HttpResponse, + HttpResponseOk, + multiFetcher, + request, + useMatchMutate +} from "./backend"; + +export interface ProductAPI { + createProduct: ( + data: MerchantBackend.Products.ProductAddDetail + ) => Promise<void>; + updateProduct: ( + id: string, + data: MerchantBackend.Products.ProductPatchDetail + ) => Promise<void>; + deleteProduct: (id: string) => Promise<void>; + lockProduct: ( + id: string, + data: MerchantBackend.Products.LockRequest + ) => Promise<void>; +} + +export function useProductAPI(): ProductAPI { + const mutateAll = useMatchMutate(); + const { mutate } = useSWRConfig(); + const { url: baseUrl, token: adminToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: adminToken, } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken, }; + + const createProduct = async ( + data: MerchantBackend.Products.ProductAddDetail + ): Promise<void> => { + const res = await request(`${url}/private/products`, { + method: "post", + token, + data, + }); + + return await mutateAll(/.*"\/private\/products.*/); + }; + + const updateProduct = async ( + productId: string, + data: MerchantBackend.Products.ProductPatchDetail + ): Promise<void> => { + const r = await request(`${url}/private/products/${productId}`, { + method: "patch", + token, + data, + }); + + return await mutateAll(/.*"\/private\/products.*/); + }; + + const deleteProduct = async (productId: string): Promise<void> => { + await request(`${url}/private/products/${productId}`, { + method: "delete", + token, + }); + await mutate([`/private/products`, token, url]); + }; + + const lockProduct = async ( + productId: string, + data: MerchantBackend.Products.LockRequest + ): Promise<void> => { + await request(`${url}/private/products/${productId}/lock`, { + method: "post", + token, + data, + }); + + return await mutateAll(/.*"\/private\/products.*/); + }; + + return { createProduct, updateProduct, deleteProduct, lockProduct }; +} + +export function useInstanceProducts(): HttpResponse< + (MerchantBackend.Products.ProductDetail & WithId)[] +> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: baseToken, } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken, }; + + const { data: list, error: listError } = useSWR< + HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>, + HttpError + >([`/private/products`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + const paths = (list?.data.products || []).map( + (p) => `/private/products/${p.product_id}` + ); + const { data: products, error: productError } = useSWR< + HttpResponseOk<MerchantBackend.Products.ProductDetail>[], + HttpError + >([paths, token, url], multiFetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + + if (listError) return listError; + if (productError) return productError; + + if (products) { + const dataWithId = products.map((d) => { + //take the id from the queried url + return { + ...d.data, + id: d.info?.url.replace(/.*\/private\/products\//, "") || "", + }; + }); + return { ok: true, data: dataWithId }; + } + return { loading: true }; +} + +export function useProductDetails( + productId: string +): HttpResponse<MerchantBackend.Products.ProductDetail> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { + url: baseUrl, + token: baseToken, + } + : { + url: `${baseUrl}/instances/${id}`, + token: instanceToken, + }; + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Products.ProductDetail>, + HttpError + >([`/private/products/${productId}`, token, url], fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} diff --git a/packages/merchant-backoffice-ui/src/hooks/reserves.ts b/packages/merchant-backoffice-ui/src/hooks/reserves.ts new file mode 100644 index 000000000..7a662dfbc --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/reserves.ts @@ -0,0 +1,218 @@ +/* + 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/> + */ +import useSWR, { useSWRConfig } from "swr"; +import { useBackendContext } from "../context/backend"; +import { useInstanceContext } from "../context/instance"; +import { MerchantBackend } from "../declaration"; +import { + fetcher, + HttpError, + HttpResponse, + HttpResponseOk, + request, + useMatchMutate, +} from "./backend"; + +export function useReservesAPI(): ReserveMutateAPI { + const mutateAll = useMatchMutate(); + const { mutate } = useSWRConfig(); + const { url: baseUrl, token: adminToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: adminToken, } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken, }; + + const createReserve = async ( + data: MerchantBackend.Tips.ReserveCreateRequest + ): Promise< + HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation> + > => { + const res = await request<MerchantBackend.Tips.ReserveCreateConfirmation>( + `${url}/private/reserves`, + { + method: "post", + token, + data, + } + ); + + //evict reserve list query + await mutateAll(/.*private\/reserves.*/); + + return res; + }; + + const authorizeTipReserve = async ( + pub: string, + data: MerchantBackend.Tips.TipCreateRequest + ): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => { + const res = await request<MerchantBackend.Tips.TipCreateConfirmation>( + `${url}/private/reserves/${pub}/authorize-tip`, + { + method: "post", + token, + data, + } + ); + + //evict reserve details query + await mutate([`/private/reserves/${pub}`, token, url]); + + return res; + }; + + const authorizeTip = async ( + data: MerchantBackend.Tips.TipCreateRequest + ): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => { + const res = await request<MerchantBackend.Tips.TipCreateConfirmation>( + `${url}/private/tips`, + { + method: "post", + token, + data, + } + ); + + //evict all details query + await mutateAll(/.*private\/reserves\/.*/); + + return res; + }; + + const deleteReserve = async (pub: string): Promise<HttpResponse<void>> => { + const res = await request<void>(`${url}/private/reserves/${pub}`, { + method: "delete", + token, + }); + + //evict reserve list query + await mutateAll(/.*private\/reserves.*/); + + return res; + }; + + return { createReserve, authorizeTip, authorizeTipReserve, deleteReserve }; +} + +export interface ReserveMutateAPI { + createReserve: ( + data: MerchantBackend.Tips.ReserveCreateRequest + ) => Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>>; + authorizeTipReserve: ( + id: string, + data: MerchantBackend.Tips.TipCreateRequest + ) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>; + authorizeTip: ( + data: MerchantBackend.Tips.TipCreateRequest + ) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>; + deleteReserve: (id: string) => Promise<HttpResponse<void>>; +} + +export function useInstanceReserves(): HttpResponse<MerchantBackend.Tips.TippingReserveStatus> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: baseToken, } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken, }; + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>, + HttpError + >([`/private/reserves`, token, url], fetcher); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} + +export function useReserveDetails( + reserveId: string +): HttpResponse<MerchantBackend.Tips.ReserveDetail> { + const { url: baseUrl } = useBackendContext(); + const { token, id: instanceId, admin } = useInstanceContext(); + + const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}`; + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Tips.ReserveDetail>, + HttpError + >([`/private/reserves/${reserveId}`, token, url], reserveDetailFetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} + +export function useTipDetails( + tipId: string +): HttpResponse<MerchantBackend.Tips.TipDetails> { + const { url: baseUrl } = useBackendContext(); + const { token, id: instanceId, admin } = useInstanceContext(); + + const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}`; + + const { data, error, isValidating } = useSWR< + HttpResponseOk<MerchantBackend.Tips.TipDetails>, + HttpError + >([`/private/tips/${tipId}`, token, url], tipsDetailFetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} + +function reserveDetailFetcher<T>( + url: string, + token: string, + backend: string +): Promise<HttpResponseOk<T>> { + return request<T>(`${backend}${url}`, { + token, + params: { + tips: "yes", + }, + }); +} + +function tipsDetailFetcher<T>( + url: string, + token: string, + backend: string +): Promise<HttpResponseOk<T>> { + return request<T>(`${backend}${url}`, { + token, + params: { + pickups: "yes", + }, + }); +} diff --git a/packages/merchant-backoffice-ui/src/hooks/transfer.ts b/packages/merchant-backoffice-ui/src/hooks/transfer.ts new file mode 100644 index 000000000..0c12d6d4d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/transfer.ts @@ -0,0 +1,217 @@ +/* + 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/> + */ +import { MerchantBackend } from "../declaration"; +import { useBackendContext } from "../context/backend"; +import { + request, + HttpResponse, + HttpError, + HttpResponseOk, + HttpResponsePaginated, + useMatchMutate, +} from "./backend"; +import useSWR from "swr"; +import { useInstanceContext } from "../context/instance"; +import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants"; +import { useEffect, useState } from "preact/hooks"; + +async function transferFetcher<T>( + url: string, + token: string, + backend: string, + payto_uri?: string, + verified?: string, + position?: string, + delta?: number +): Promise<HttpResponseOk<T>> { + const params: any = {}; + if (payto_uri !== undefined) params.payto_uri = payto_uri; + if (verified !== undefined) params.verified = verified; + if (delta !== undefined) { + params.limit = delta; + } + if (position !== undefined) params.offset = position; + + return request<T>(`${backend}${url}`, { token, params }); +} + +export function useTransferAPI(): TransferAPI { + const mutateAll = useMatchMutate(); + const { url: baseUrl, token: adminToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { + url: baseUrl, + token: adminToken, + } + : { + url: `${baseUrl}/instances/${id}`, + token: instanceToken, + }; + + const informTransfer = async ( + data: MerchantBackend.Transfers.TransferInformation + ): Promise< + HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse> + > => { + const res = await request<MerchantBackend.Transfers.MerchantTrackTransferResponse>( + `${url}/private/transfers`, { + method: "post", + token, + data, + }); + + await mutateAll(/.*private\/transfers.*/); + return res + }; + + return { informTransfer }; +} + +export interface TransferAPI { + informTransfer: ( + data: MerchantBackend.Transfers.TransferInformation + ) => Promise< + HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse> + >; +} + +export interface InstanceTransferFilter { + payto_uri?: string; + verified?: "yes" | "no"; + position?: string; +} + +export function useInstanceTransfers( + args?: InstanceTransferFilter, + updatePosition?: (id: string) => void +): HttpResponsePaginated<MerchantBackend.Transfers.TransferList> { + const { url: baseUrl, token: baseToken } = useBackendContext(); + const { token: instanceToken, id, admin } = useInstanceContext(); + + const { url, token } = !admin + ? { url: baseUrl, token: baseToken } + : { url: `${baseUrl}/instances/${id}`, token: instanceToken }; + + const [pageBefore, setPageBefore] = useState(1); + const [pageAfter, setPageAfter] = useState(1); + + const totalAfter = pageAfter * PAGE_SIZE; + const totalBefore = args?.position !== undefined ? pageBefore * PAGE_SIZE : 0; + + /** + * FIXME: this can be cleaned up a little + * + * the logic of double query should be inside the orderFetch so from the hook perspective and cache + * is just one query and one error status + */ + const { + data: beforeData, + error: beforeError, + isValidating: loadingBefore, + } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>( + [ + `/private/transfers`, + token, + url, + args?.payto_uri, + args?.verified, + args?.position, + totalBefore, + ], + transferFetcher + ); + const { + data: afterData, + error: afterError, + isValidating: loadingAfter, + } = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>( + [ + `/private/transfers`, + token, + url, + args?.payto_uri, + args?.verified, + args?.position, + -totalAfter, + ], + transferFetcher + ); + + //this will save last result + const [lastBefore, setLastBefore] = useState< + HttpResponse<MerchantBackend.Transfers.TransferList> + >({ loading: true }); + const [lastAfter, setLastAfter] = useState< + HttpResponse<MerchantBackend.Transfers.TransferList> + >({ loading: true }); + useEffect(() => { + if (afterData) setLastAfter(afterData); + if (beforeData) setLastBefore(beforeData); + }, [afterData, beforeData]); + + if (beforeError) return beforeError; + if (afterError) return afterError; + + // if the query returns less that we ask, then we have reach the end or beginning + const isReachingEnd = afterData && afterData.data.transfers.length < totalAfter; + const isReachingStart = args?.position === undefined || + (beforeData && beforeData.data.transfers.length < totalBefore); + + const pagination = { + isReachingEnd, + isReachingStart, + loadMore: () => { + if (!afterData || isReachingEnd) return; + if (afterData.data.transfers.length < MAX_RESULT_SIZE) { + setPageAfter(pageAfter + 1); + } else { + const from = + `${afterData.data + .transfers[afterData.data.transfers.length - 1] + .transfer_serial_id}`; + if (from && updatePosition) updatePosition(from); + } + }, + loadMorePrev: () => { + if (!beforeData || isReachingStart) return; + if (beforeData.data.transfers.length < MAX_RESULT_SIZE) { + setPageBefore(pageBefore + 1); + } else if (beforeData) { + const from = + `${beforeData.data + .transfers[beforeData.data.transfers.length - 1] + .transfer_serial_id}`; + if (from && updatePosition) updatePosition(from); + } + }, + }; + + const transfers = + !beforeData || !afterData + ? [] + : (beforeData || lastBefore).data.transfers + .slice() + .reverse() + .concat((afterData || lastAfter).data.transfers); + if (loadingAfter || loadingBefore) + return { loading: true, data: { transfers } }; + if (beforeData && afterData) { + return { ok: true, data: { transfers }, ...pagination }; + } + return { loading: true }; +} diff --git a/packages/merchant-backoffice-ui/src/i18n/de.po b/packages/merchant-backoffice-ui/src/i18n/de.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/de.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backoffice-ui/src/i18n/en.po b/packages/merchant-backoffice-ui/src/i18n/en.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/en.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backoffice-ui/src/i18n/es.po b/packages/merchant-backoffice-ui/src/i18n/es.po new file mode 100644 index 000000000..9075d4656 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/es.po @@ -0,0 +1,1065 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "Acceso denegado" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "Verifica que el token sea valido" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "No se pudo acceder al servidor" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "No se pudo inferir el id de la instancia con la url %1$s" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "HTTP status #%1$s: Servidor reporto un problema" + +#: src/InstanceRoutes.tsx:110 +#, fuzzy, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "Recivimos el mensaje %1$s desde %2$s" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "Sin instancia default" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "para usar el merchant backoffice, deberÃa crear la instancia default" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "Servidir reporto un problema: HTTP status #%1$s" + +#: src/InstanceRoutes.tsx:289 +#, fuzzy, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "Recivimos el mensaje %1$s desde %2$s" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "Login necesario" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" +"Por favor ingrese su token de autorización. El token debe tener \"secret-" +"token\" y comenzar con Bearer o ApiKey" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "Confirmar" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "El valor %1$s es invalido para una URL de pago" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "elegir una fecha" + +#: src/components/form/InputDate.tsx:81 +#, fuzzy, c-format +msgid "clear" +msgstr "Limpiar" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "nunca" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "La imagen debe ser mas chica que 1 MB" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "PaÃs" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "Dirección" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "Número de edificio" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "Nombre de edificio" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "Calle" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "Código postal" + +#: src/components/form/InputLocation.tsx:38 +#, fuzzy, c-format +msgid "Town location" +msgstr "Ubicación de ciudad" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "Ciudad" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "Distrito" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "Provincia" + +#: src/components/form/InputSearchProduct.tsx:59 +#, fuzzy, c-format +msgid "Product id" +msgstr "Id de producto" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "Descripcion" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "Nombre" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "Cargando..." + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "No se encontraron productos" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "Sin resultados" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "Borrando" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "Cambiando" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "Administrar token" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "Actualizar" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "Eliminar" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "Cancelar" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "Administrar stock" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "Inifinito" + +#: src/components/form/InputStock.tsx:105 +#, fuzzy, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "no puede ser mayor al stock actual %1$s" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "stock actual cambiará desde %1$s a %2$s" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "stock actual seguirá en %1$s" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "Ingresando" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "Perdido" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "Actual" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "sin stock" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "Próximo reabastecimiento" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "Dirección de entrega" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "este producto no tiene impuestos" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "Monto" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "Moneda y valor separado por dos puntos" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "Agregar" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "Instancia" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "Configuración" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, fuzzy, c-format +msgid "Orders" +msgstr "Ordenes" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "Productos" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "Transferencias" + +#: src/components/menu/SideBar.tsx:87 +#, fuzzy, c-format +msgid "Connection" +msgstr "Conexión" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "Instancias" + +#: src/components/menu/SideBar.tsx:116 +#, fuzzy, c-format +msgid "New" +msgstr "Nuevo" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "Lista" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "Salir" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "Limpiar" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "deberÃan ser iguales" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "no puede ser igual al anterior" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" +"Está actualizando el token de autorización para la instancia %1$s con id %2$s" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "Viejo token" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "Nuevo token" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" +"Limpiar el token de autorización significa acceso publico a la instancia" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "ID" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "Imagen" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "Unidad" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "Precio" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "Stock" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "Impuesto" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "Servidor no encontrado" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "No se pudo aceder al servidor" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "Recivimos el mensaje %1$s desde %2$s" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "Error inesperado" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "Token de autorización" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "Dirección de cuenta" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "Impuesto máximo de deposito por omisión" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "Impuesto máximo de transferencia por omisión" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "Amortización de impuesto de transferencia por omisión" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "Jurisdicción" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "Retrazo de pago por omisión" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "Retrazo de transferencia por omisión" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "no se pudo crear la instancia" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, fuzzy, c-format +msgid "Delete" +msgstr "Borrando" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "No hay instancias todavÃan, agregue mas presionando el signo +" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "Productos de inventario" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "Precio total" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "Impuesto total" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "Precio de la orden" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, fuzzy, c-format +msgid "Net" +msgstr "Neto" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "Resumen" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "Opciones de pago" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "Plazo de reembolso automático" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "Plazo de reembolso" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "Plazo de pago" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "Fecha de entrega" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, fuzzy, c-format +msgid "Location" +msgstr "Ubicación" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "Impuesto máximo" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "Impuesto de transferencia máximo" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "Amortización de impuesto de transferencia" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "URL de completitud" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "Información extra" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "seleccione un producto primero" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, fuzzy, c-format +msgid "should be greater than 0" +msgstr "La imagen debe ser mas chica que 1 MB" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" +"no puede ser mayor al stock actual y la cantidad previamente agregada. " +"máximo: %1$s" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "no puede ser mayor al stock actual %1$s" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "Cantidad" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "Orden" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "reclamado" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "copiar url" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "pagar en" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "creado" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "CronologÃa" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "Detalles de pago" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, fuzzy, c-format +msgid "Order status" +msgstr "Estado de orden" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, fuzzy, c-format +msgid "Product list" +msgstr "Lista de producto" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "pagados" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "transferido" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "reembolzado" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "reembolzar" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "Monto reembolzado" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "Total depositado" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "impago" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "URL de estado de orden" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "URI de pago" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" +"Estado de orden desconocido. Esto es un error, por favor contacte a su " +"administrador" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "reembolzo creado satisfactoriamente" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, fuzzy, c-format +msgid "could not create the refund" +msgstr "No se pudo aceder al servidor" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "cargar nuevas ordenes" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "Fecha" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "Reembolzar" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "cargar viejas ordenes" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "No se enconraron ordenes" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "fecha" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "monto" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "razón" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "Máximo reembolzable:" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "Razón" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "duplicado" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "pedido por el consumidor" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "otro" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "ir a id de orden" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "Pagado" + +#: src/paths/instance/orders/list/index.tsx:108 +#, fuzzy, c-format +msgid "Refunded" +msgstr "Reembolzado" + +#: src/paths/instance/orders/list/index.tsx:109 +#, fuzzy, c-format +msgid "Not wired" +msgstr "No transferido" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "Todo" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "no se pudo crear el producto" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "Venta" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "Ganancia" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "Vendido" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "producto actualizado correctamente" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "no se pudo actualizar el producto" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "producto fue eliminado correctamente" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "no se pudo eliminar el producto" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "Propinas" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "No hay propinas todavÃa, agregar mas presionando el signo +" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "no puede ser vacÃo" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "verificar el id, no parece válido" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "deberÃa tener 52 caracteres, actualmente %1$s" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "La URL no tiene el formato correcto" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, fuzzy, c-format +msgid "Transfer ID" +msgstr "Transferencias" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, fuzzy, c-format +msgid "Account Address" +msgstr "Dirección de cuenta" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "URL del Exchange" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, fuzzy, c-format +msgid "could not inform transfer" +msgstr "no se pudo crear la instancia" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, fuzzy, c-format +msgid "load newer transfers" +msgstr "cargar nuevas ordenes" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "Crédito" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, fuzzy, c-format +msgid "Confirmed" +msgstr "Confirmar" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "Verificado" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, fuzzy, c-format +msgid "Executed at" +msgstr "creado" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "si" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "no" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "desconocido" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, fuzzy, c-format +msgid "load older transfers" +msgstr "cargar viejas transferencias" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "No hay transferencias todavÃa, agregar mas presionando el signo +" diff --git a/packages/merchant-backoffice-ui/src/i18n/fr.po b/packages/merchant-backoffice-ui/src/i18n/fr.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/fr.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backoffice-ui/src/i18n/index.tsx b/packages/merchant-backoffice-ui/src/i18n/index.tsx new file mode 100644 index 000000000..9403de1f5 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/index.tsx @@ -0,0 +1,215 @@ +/* + 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/> + */ + +/** + * Translation helpers for React components and template literals. + */ + +/** + * Imports + */ +import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact"; + +import { useTranslationContext } from "../context/translation"; + +export type Translator = ( + stringSeq: TemplateStringsArray, + ...values: any[] +) => string; +export function useTranslator(): Translator { + const ctx = useTranslationContext(); + const jed = ctx.handler; + return function str( + stringSeq: TemplateStringsArray, + ...values: any[] + ): string { + const s = toI18nString(stringSeq); + if (!s) return s; + const tr = jed + .translate(s) + .ifPlural(1, s) + .fetch(...values); + return tr; + }; +} + +/** + * Convert template strings to a msgid + */ +function toI18nString(stringSeq: ReadonlyArray<string>): string { + let s = ""; + for (let i = 0; i < stringSeq.length; i++) { + s += stringSeq[i]; + if (i < stringSeq.length - 1) { + s += `%${i + 1}$s`; + } + } + return s; +} + +interface TranslateSwitchProps { + target: number; + children: ComponentChildren; +} + +function stringifyChildren(children: ComponentChildren): string { + let n = 1; + const ss = (children instanceof Array ? children : [children]).map((c) => { + if (typeof c === "string") { + return c; + } + return `%${n++}$s`; + }); + const s = ss.join("").replace(/ +/g, " ").trim(); + return s; +} + +interface TranslateProps { + children: ComponentChildren; + /** + * Component that the translated element should be wrapped in. + * Defaults to "div". + */ + wrap?: any; + + /** + * Props to give to the wrapped component. + */ + wrapProps?: any; +} + +function getTranslatedChildren( + translation: string, + children: ComponentChildren +): ComponentChild[] { + const tr = translation.split(/%(\d+)\$s/); + const childArray = children instanceof Array ? children : [children]; + // Merge consecutive string children. + const placeholderChildren = Array<ComponentChild>(); + for (let i = 0; i < childArray.length; i++) { + const x = childArray[i]; + if (x === undefined) { + continue; + } else if (typeof x === "string") { + continue; + } else { + placeholderChildren.push(x); + } + } + const result = Array<ComponentChild>(); + for (let i = 0; i < tr.length; i++) { + if (i % 2 == 0) { + // Text + result.push(tr[i]); + } else { + const childIdx = Number.parseInt(tr[i], 10) - 1; + result.push(placeholderChildren[childIdx]); + } + } + return result; +} + +/** + * Translate text node children of this component. + * If a child component might produce a text node, it must be wrapped + * in a another non-text element. + * + * Example: + * ``` + * <Translate> + * Hello. Your score is <span><PlayerScore player={player} /></span> + * </Translate> + * ``` + */ +export function Translate({ children }: TranslateProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext(); + const translation: string = ctx.handler.ngettext(s, s, 1); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; +} + +/** + * Switch translation based on singular or plural based on the target prop. + * Should only contain TranslateSingular and TransplatePlural as children. + * + * Example: + * ``` + * <TranslateSwitch target={n}> + * <TranslateSingular>I have {n} apple.</TranslateSingular> + * <TranslatePlural>I have {n} apples.</TranslatePlural> + * </TranslateSwitch> + * ``` + */ +export function TranslateSwitch({ children, target }: TranslateSwitchProps) { + let singular: VNode<TranslationPluralProps> | undefined; + let plural: VNode<TranslationPluralProps> | undefined; + // const children = this.props.children; + if (children) { + (children instanceof Array ? children : [children]).forEach( + (child: any) => { + if (child.type === TranslatePlural) { + plural = child; + } + if (child.type === TranslateSingular) { + singular = child; + } + } + ); + } + if (!singular || !plural) { + console.error("translation not found"); + return h("span", {}, ["translation not found"]); + } + singular.props.target = target; + plural.props.target = target; + // We're looking up the translation based on the + // singular, even if we must use the plural form. + return singular; +} + +interface TranslationPluralProps { + children: ComponentChildren; + target: number; +} + +/** + * See [[TranslateSwitch]]. + */ +export function TranslatePlural({ + children, + target, +}: TranslationPluralProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext(); + const translation = ctx.handler.ngettext(s, s, 1); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; +} + +/** + * See [[TranslateSwitch]]. + */ +export function TranslateSingular({ + children, + target, +}: TranslationPluralProps): VNode { + const s = stringifyChildren(children); + const ctx = useTranslationContext(); + const translation = ctx.handler.ngettext(s, s, target); + const result = getTranslatedChildren(translation, children); + return <Fragment>{result}</Fragment>; +} diff --git a/packages/merchant-backoffice-ui/src/i18n/it.po b/packages/merchant-backoffice-ui/src/i18n/it.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/it.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backoffice-ui/src/i18n/poheader b/packages/merchant-backoffice-ui/src/i18n/poheader new file mode 100644 index 000000000..ee3fcd7be --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/poheader @@ -0,0 +1,27 @@ +# 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/> + +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: taler@gnu.org\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" diff --git a/packages/merchant-backoffice-ui/src/i18n/strings-prelude b/packages/merchant-backoffice-ui/src/i18n/strings-prelude new file mode 100644 index 000000000..cca13afad --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/strings-prelude @@ -0,0 +1,19 @@ +/* + 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/> + */ + +/*eslint quote-props: ["error", "consistent"]*/ +export const strings: {[s: string]: any} = {}; + diff --git a/packages/merchant-backoffice-ui/src/i18n/strings.ts b/packages/merchant-backoffice-ui/src/i18n/strings.ts new file mode 100644 index 000000000..63e96949a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/strings.ts @@ -0,0 +1,3445 @@ +/* + 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/> + */ + +/*eslint quote-props: ["error", "consistent"]*/ +export const strings: {[s: string]: any} = {}; + +strings['de'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['en'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['es'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "Acceso denegado" + ], + "Check your token is valid": [ + "Verifica que el token sea valido" + ], + "Couldn't access the server.": [ + "No se pudo acceder al servidor" + ], + "Could not infer instance id from url %1$s": [ + "No se pudo inferir el id de la instancia con la url %1$s" + ], + "HTTP status #%1$s: Server reported a problem": [ + "HTTP status #%1$s: Servidor reporto un problema" + ], + "Got message: \"%1$s\" from: %2$s": [ + "Recivimos el mensaje %1$s desde %2$s" + ], + "No default instance": [ + "Sin instancia default" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "para usar el merchant backoffice, deberÃa crear la instancia default" + ], + "Server reported a problem: HTTP status #%1$s": [ + "Servidir reporto un problema: HTTP status #%1$s" + ], + "Got message: %1$s from: %2$s": [ + "Recivimos el mensaje %1$s desde %2$s" + ], + "Login required": [ + "Login necesario" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "Por favor ingrese su token de autorización. El token debe tener \"secret-token\" y comenzar con Bearer o ApiKey" + ], + "Confirm": [ + "Confirmar" + ], + "The value %1$s is invalid for a payment url": [ + "El valor %1$s es invalido para una URL de pago" + ], + "pick a date": [ + "elegir una fecha" + ], + "clear": [ + "Limpiar" + ], + "never": [ + "nunca" + ], + "Image should be smaller than 1 MB": [ + "La imagen debe ser mas chica que 1 MB" + ], + "Country": [ + "PaÃs" + ], + "Address": [ + "Dirección" + ], + "Building number": [ + "Número de edificio" + ], + "Building name": [ + "Nombre de edificio" + ], + "Street": [ + "Calle" + ], + "Post code": [ + "Código postal" + ], + "Town location": [ + "Ubicación de ciudad" + ], + "Town": [ + "Ciudad" + ], + "District": [ + "Distrito" + ], + "Country subdivision": [ + "Provincia" + ], + "Product id": [ + "Id de producto" + ], + "Description": [ + "Descripcion" + ], + "Name": [ + "Nombre" + ], + "loading...": [ + "Cargando..." + ], + "no products found": [ + "No se encontraron productos" + ], + "no results": [ + "Sin resultados" + ], + "Deleting": [ + "Borrando" + ], + "Changing": [ + "Cambiando" + ], + "Manage token": [ + "Administrar token" + ], + "Update": [ + "Actualizar" + ], + "Remove": [ + "Eliminar" + ], + "Cancel": [ + "Cancelar" + ], + "Manage stock": [ + "Administrar stock" + ], + "Infinite": [ + "Inifinito" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "no puede ser mayor al stock actual %1$s" + ], + "current stock will change from %1$s to %2$s": [ + "stock actual cambiará desde %1$s a %2$s" + ], + "current stock will stay at %1$s": [ + "stock actual seguirá en %1$s" + ], + "Incoming": [ + "Ingresando" + ], + "Lost": [ + "Perdido" + ], + "Current": [ + "Actual" + ], + "without stock": [ + "sin stock" + ], + "Next restock": [ + "Próximo reabastecimiento" + ], + "Delivery address": [ + "Dirección de entrega" + ], + "this product has no taxes": [ + "este producto no tiene impuestos" + ], + "Amount": [ + "Monto" + ], + "currency and value separated with colon": [ + "Moneda y valor separado por dos puntos" + ], + "Add": [ + "Agregar" + ], + "Instance": [ + "Instancia" + ], + "Settings": [ + "Configuración" + ], + "Orders": [ + "Ordenes" + ], + "Products": [ + "Productos" + ], + "Transfers": [ + "Transferencias" + ], + "Connection": [ + "Conexión" + ], + "Instances": [ + "Instancias" + ], + "New": [ + "Nuevo" + ], + "List": [ + "Lista" + ], + "Log out": [ + "Salir" + ], + "Clear": [ + "Limpiar" + ], + "should be the same": [ + "deberÃan ser iguales" + ], + "cannot be the same as before": [ + "no puede ser igual al anterior" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "Está actualizando el token de autorización para la instancia %1$s con id %2$s" + ], + "Old token": [ + "Viejo token" + ], + "New token": [ + "Nuevo token" + ], + "Clearing the auth token will mean public access to the instance": [ + "Limpiar el token de autorización significa acceso publico a la instancia" + ], + "ID": [ + "ID" + ], + "Image": [ + "Imagen" + ], + "Unit": [ + "Unidad" + ], + "Price": [ + "Precio" + ], + "Stock": [ + "Stock" + ], + "Taxes": [ + "Impuesto" + ], + "Server not found": [ + "Servidor no encontrado" + ], + "Couldn't access the server": [ + "No se pudo aceder al servidor" + ], + "Got message %1$s from %2$s": [ + "Recivimos el mensaje %1$s desde %2$s" + ], + "Unexpected Error": [ + "Error inesperado" + ], + "Auth token": [ + "Token de autorización" + ], + "Account address": [ + "Dirección de cuenta" + ], + "Default max deposit fee": [ + "Impuesto máximo de deposito por omisión" + ], + "Default max wire fee": [ + "Impuesto máximo de transferencia por omisión" + ], + "Default wire fee amortization": [ + "Amortización de impuesto de transferencia por omisión" + ], + "Jurisdiction": [ + "Jurisdicción" + ], + "Default pay delay": [ + "Retrazo de pago por omisión" + ], + "Default wire transfer delay": [ + "Retrazo de transferencia por omisión" + ], + "could not create instance": [ + "no se pudo crear la instancia" + ], + "Delete": [ + "Borrando" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "No hay instancias todavÃan, agregue mas presionando el signo +" + ], + "Inventory products": [ + "Productos de inventario" + ], + "Total price": [ + "Precio total" + ], + "Total tax": [ + "Impuesto total" + ], + "Order price": [ + "Precio de la orden" + ], + "Net": [ + "Neto" + ], + "Summary": [ + "Resumen" + ], + "Payments options": [ + "Opciones de pago" + ], + "Auto refund deadline": [ + "Plazo de reembolso automático" + ], + "Refund deadline": [ + "Plazo de reembolso" + ], + "Pay deadline": [ + "Plazo de pago" + ], + "Delivery date": [ + "Fecha de entrega" + ], + "Location": [ + "Ubicación" + ], + "Max fee": [ + "Impuesto máximo" + ], + "Max wire fee": [ + "Impuesto de transferencia máximo" + ], + "Wire fee amortization": [ + "Amortización de impuesto de transferencia" + ], + "Fullfilment url": [ + "URL de completitud" + ], + "Extra information": [ + "Información extra" + ], + "select a product first": [ + "seleccione un producto primero" + ], + "should be greater than 0": [ + "La imagen debe ser mas chica que 1 MB" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "no puede ser mayor al stock actual y la cantidad previamente agregada. máximo: %1$s" + ], + "cannot be greater than current stock %1$s": [ + "no puede ser mayor al stock actual %1$s" + ], + "Quantity": [ + "Cantidad" + ], + "Order": [ + "Orden" + ], + "claimed": [ + "reclamado" + ], + "copy url": [ + "copiar url" + ], + "pay at": [ + "pagar en" + ], + "created at": [ + "creado" + ], + "Timeline": [ + "CronologÃa" + ], + "Payment details": [ + "Detalles de pago" + ], + "Order status": [ + "Estado de orden" + ], + "Product list": [ + "Lista de producto" + ], + "paid": [ + "pagados" + ], + "wired": [ + "transferido" + ], + "refunded": [ + "reembolzado" + ], + "refund": [ + "reembolzar" + ], + "Refunded amount": [ + "Monto reembolzado" + ], + "Deposit total": [ + "Total depositado" + ], + "unpaid": [ + "impago" + ], + "Order status URL": [ + "URL de estado de orden" + ], + "Pay URI": [ + "URI de pago" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "Estado de orden desconocido. Esto es un error, por favor contacte a su administrador" + ], + "refund created successfully": [ + "reembolzo creado satisfactoriamente" + ], + "could not create the refund": [ + "No se pudo aceder al servidor" + ], + "load newer orders": [ + "cargar nuevas ordenes" + ], + "Date": [ + "Fecha" + ], + "Refund": [ + "Reembolzar" + ], + "load older orders": [ + "cargar viejas ordenes" + ], + "No orders has been found": [ + "No se enconraron ordenes" + ], + "date": [ + "fecha" + ], + "amount": [ + "monto" + ], + "reason": [ + "razón" + ], + "Max refundable:": [ + "Máximo reembolzable:" + ], + "Reason": [ + "Razón" + ], + "duplicated": [ + "duplicado" + ], + "requested by the customer": [ + "pedido por el consumidor" + ], + "other": [ + "otro" + ], + "go to order id": [ + "ir a id de orden" + ], + "Paid": [ + "Pagado" + ], + "Refunded": [ + "Reembolzado" + ], + "Not wired": [ + "No transferido" + ], + "All": [ + "Todo" + ], + "could not create product": [ + "no se pudo crear el producto" + ], + "Sell": [ + "Venta" + ], + "Profit": [ + "Ganancia" + ], + "Sold": [ + "Vendido" + ], + "product updated successfully": [ + "producto actualizado correctamente" + ], + "could not update the product": [ + "no se pudo actualizar el producto" + ], + "product delete successfully": [ + "producto fue eliminado correctamente" + ], + "could not delete the product": [ + "no se pudo eliminar el producto" + ], + "Tips": [ + "Propinas" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "No hay propinas todavÃa, agregar mas presionando el signo +" + ], + "cannot be empty": [ + "no puede ser vacÃo" + ], + "check the id, doest look valid": [ + "verificar el id, no parece válido" + ], + "should have 52 characters, current %1$s": [ + "deberÃa tener 52 caracteres, actualmente %1$s" + ], + "URL doesn't have the right format": [ + "La URL no tiene el formato correcto" + ], + "Transfer ID": [ + "Transferencias" + ], + "Account Address": [ + "Dirección de cuenta" + ], + "Exchange URL": [ + "URL del Exchange" + ], + "could not inform transfer": [ + "no se pudo crear la instancia" + ], + "load newer transfers": [ + "cargar nuevas ordenes" + ], + "Credit": [ + "Crédito" + ], + "Confirmed": [ + "Confirmar" + ], + "Verified": [ + "Verificado" + ], + "Executed at": [ + "creado" + ], + "yes": [ + "si" + ], + "no": [ + "no" + ], + "unknown": [ + "desconocido" + ], + "load older transfers": [ + "cargar viejas transferencias" + ], + "There is no transfer yet, add more pressing the + sign": [ + "No hay transferencias todavÃa, agregar mas presionando el signo +" + ] + } + } +}; + +strings['fr'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['it'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + +strings['sv'] = { + "domain": "messages", + "locale_data": { + "messages": { + "": { + "domain": "messages", + "plural_forms": "nplurals=2; plural=(n != 1);", + "lang": "" + }, + "Access denied": [ + "" + ], + "Check your token is valid": [ + "" + ], + "Couldn't access the server.": [ + "" + ], + "Could not infer instance id from url %1$s": [ + "" + ], + "HTTP status #%1$s: Server reported a problem": [ + "" + ], + "Got message: \"%1$s\" from: %2$s": [ + "" + ], + "No default instance": [ + "" + ], + "in order to use merchant backoffice, you should create the default instance": [ + "" + ], + "Server reported a problem: HTTP status #%1$s": [ + "" + ], + "Got message: %1$s from: %2$s": [ + "" + ], + "Login required": [ + "" + ], + "Please enter your auth token. Token should have \"secret-token:\" and start with Bearer or ApiKey": [ + "" + ], + "Confirm": [ + "" + ], + "The value %1$s is invalid for a payment url": [ + "" + ], + "pick a date": [ + "" + ], + "clear": [ + "" + ], + "never": [ + "" + ], + "Image should be smaller than 1 MB": [ + "" + ], + "Country": [ + "" + ], + "Address": [ + "" + ], + "Building number": [ + "" + ], + "Building name": [ + "" + ], + "Street": [ + "" + ], + "Post code": [ + "" + ], + "Town location": [ + "" + ], + "Town": [ + "" + ], + "District": [ + "" + ], + "Country subdivision": [ + "" + ], + "Product id": [ + "" + ], + "Description": [ + "" + ], + "Name": [ + "" + ], + "loading...": [ + "" + ], + "no products found": [ + "" + ], + "no results": [ + "" + ], + "Deleting": [ + "" + ], + "Changing": [ + "" + ], + "Manage token": [ + "" + ], + "Update": [ + "" + ], + "Remove": [ + "" + ], + "Cancel": [ + "" + ], + "Manage stock": [ + "" + ], + "Infinite": [ + "" + ], + "lost cannot be greater that current + incoming (max %1$s)": [ + "" + ], + "current stock will change from %1$s to %2$s": [ + "" + ], + "current stock will stay at %1$s": [ + "" + ], + "Incoming": [ + "" + ], + "Lost": [ + "" + ], + "Current": [ + "" + ], + "without stock": [ + "" + ], + "Next restock": [ + "" + ], + "Delivery address": [ + "" + ], + "this product has no taxes": [ + "" + ], + "Amount": [ + "" + ], + "currency and value separated with colon": [ + "" + ], + "Add": [ + "" + ], + "Instance": [ + "" + ], + "Settings": [ + "" + ], + "Orders": [ + "" + ], + "Products": [ + "" + ], + "Transfers": [ + "" + ], + "Connection": [ + "" + ], + "Instances": [ + "" + ], + "New": [ + "" + ], + "List": [ + "" + ], + "Log out": [ + "" + ], + "Clear": [ + "" + ], + "should be the same": [ + "" + ], + "cannot be the same as before": [ + "" + ], + "You are updating the authorization token from instance %1$s with id %2$s": [ + "" + ], + "Old token": [ + "" + ], + "New token": [ + "" + ], + "Clearing the auth token will mean public access to the instance": [ + "" + ], + "ID": [ + "" + ], + "Image": [ + "" + ], + "Unit": [ + "" + ], + "Price": [ + "" + ], + "Stock": [ + "" + ], + "Taxes": [ + "" + ], + "Server not found": [ + "" + ], + "Couldn't access the server": [ + "" + ], + "Got message %1$s from %2$s": [ + "" + ], + "Unexpected Error": [ + "" + ], + "Auth token": [ + "" + ], + "Account address": [ + "" + ], + "Default max deposit fee": [ + "" + ], + "Default max wire fee": [ + "" + ], + "Default wire fee amortization": [ + "" + ], + "Jurisdiction": [ + "" + ], + "Default pay delay": [ + "" + ], + "Default wire transfer delay": [ + "" + ], + "could not create instance": [ + "" + ], + "Delete": [ + "" + ], + "Edit": [ + "" + ], + "There is no instances yet, add more pressing the + sign": [ + "" + ], + "Inventory products": [ + "" + ], + "Total price": [ + "" + ], + "Total tax": [ + "" + ], + "Order price": [ + "" + ], + "Net": [ + "" + ], + "Summary": [ + "" + ], + "Payments options": [ + "" + ], + "Auto refund deadline": [ + "" + ], + "Refund deadline": [ + "" + ], + "Pay deadline": [ + "" + ], + "Delivery date": [ + "" + ], + "Location": [ + "" + ], + "Max fee": [ + "" + ], + "Max wire fee": [ + "" + ], + "Wire fee amortization": [ + "" + ], + "Fullfilment url": [ + "" + ], + "Extra information": [ + "" + ], + "select a product first": [ + "" + ], + "should be greater than 0": [ + "" + ], + "cannot be greater than current stock and quantity previously added. max: %1$s": [ + "" + ], + "cannot be greater than current stock %1$s": [ + "" + ], + "Quantity": [ + "" + ], + "Order": [ + "" + ], + "claimed": [ + "" + ], + "copy url": [ + "" + ], + "pay at": [ + "" + ], + "created at": [ + "" + ], + "Timeline": [ + "" + ], + "Payment details": [ + "" + ], + "Order status": [ + "" + ], + "Product list": [ + "" + ], + "paid": [ + "" + ], + "wired": [ + "" + ], + "refunded": [ + "" + ], + "refund": [ + "" + ], + "Refunded amount": [ + "" + ], + "Deposit total": [ + "" + ], + "unpaid": [ + "" + ], + "Order status URL": [ + "" + ], + "Pay URI": [ + "" + ], + "Unknown order status. This is an error, please contact the administrator.": [ + "" + ], + "refund created successfully": [ + "" + ], + "could not create the refund": [ + "" + ], + "load newer orders": [ + "" + ], + "Date": [ + "" + ], + "Refund": [ + "" + ], + "load older orders": [ + "" + ], + "No orders has been found": [ + "" + ], + "date": [ + "" + ], + "amount": [ + "" + ], + "reason": [ + "" + ], + "Max refundable:": [ + "" + ], + "Reason": [ + "" + ], + "duplicated": [ + "" + ], + "requested by the customer": [ + "" + ], + "other": [ + "" + ], + "go to order id": [ + "" + ], + "Paid": [ + "" + ], + "Refunded": [ + "" + ], + "Not wired": [ + "" + ], + "All": [ + "" + ], + "could not create product": [ + "" + ], + "Sell": [ + "" + ], + "Profit": [ + "" + ], + "Sold": [ + "" + ], + "product updated successfully": [ + "" + ], + "could not update the product": [ + "" + ], + "product delete successfully": [ + "" + ], + "could not delete the product": [ + "" + ], + "Tips": [ + "" + ], + "Committed amount": [ + "" + ], + "Exchange initial amount": [ + "" + ], + "Merchant initial amount": [ + "" + ], + "There is no tips yet, add more pressing the + sign": [ + "" + ], + "cannot be empty": [ + "" + ], + "check the id, doest look valid": [ + "" + ], + "should have 52 characters, current %1$s": [ + "" + ], + "URL doesn't have the right format": [ + "" + ], + "Transfer ID": [ + "" + ], + "Account Address": [ + "" + ], + "Exchange URL": [ + "" + ], + "could not inform transfer": [ + "" + ], + "load newer transfers": [ + "" + ], + "Credit": [ + "" + ], + "Confirmed": [ + "" + ], + "Verified": [ + "" + ], + "Executed at": [ + "" + ], + "yes": [ + "" + ], + "no": [ + "" + ], + "unknown": [ + "" + ], + "load older transfers": [ + "" + ], + "There is no transfer yet, add more pressing the + sign": [ + "" + ] + } + } +}; + diff --git a/packages/merchant-backoffice-ui/src/i18n/sv.po b/packages/merchant-backoffice-ui/src/i18n/sv.po new file mode 100644 index 000000000..6b35bd0ce --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/sv.po @@ -0,0 +1,1057 @@ +# This file is part of TALER +# (C) 2016 GNUnet e.V. +# +# 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. +# +# 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 +# TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backoffice-ui/src/i18n/taler-merchant-backoffice.pot b/packages/merchant-backoffice-ui/src/i18n/taler-merchant-backoffice.pot new file mode 100644 index 000000000..21fd863b0 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/i18n/taler-merchant-backoffice.pot @@ -0,0 +1,1054 @@ +# 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/> +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Taler Wallet\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-23 00:00+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/ApplicationReadyRoutes.tsx:50 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:299 +#, c-format +msgid "Access denied" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:51 src/InstanceRoutes.tsx:118 +#: src/InstanceRoutes.tsx:300 +#, c-format +msgid "Check your token is valid" +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:72 +#, c-format +msgid "Couldn't access the server." +msgstr "" + +#: src/ApplicationReadyRoutes.tsx:73 +#, c-format +msgid "Could not infer instance id from url %1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:109 +#, c-format +msgid "HTTP status #%1$s: Server reported a problem" +msgstr "" + +#: src/InstanceRoutes.tsx:110 +#, c-format +msgid "Got message: \"%1$s\" from: %2$s" +msgstr "" + +#: src/InstanceRoutes.tsx:127 +#, c-format +msgid "No default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:128 +#, c-format +msgid "" +"in order to use merchant backoffice, you should create the default instance" +msgstr "" + +#: src/InstanceRoutes.tsx:288 +#, c-format +msgid "Server reported a problem: HTTP status #%1$s" +msgstr "" + +#: src/InstanceRoutes.tsx:289 +#, c-format +msgid "Got message: %1$s from: %2$s" +msgstr "" + +#: src/components/exception/login.tsx:46 +#, c-format +msgid "Login required" +msgstr "" + +#: src/components/exception/login.tsx:49 +#, c-format +msgid "" +"Please enter your auth token. Token should have \"secret-token:\" and start " +"with Bearer or ApiKey" +msgstr "" + +#: src/components/exception/login.tsx:86 src/components/modal/index.tsx:53 +#: src/components/modal/index.tsx:75 src/paths/admin/create/CreatePage.tsx:115 +#: src/paths/instance/orders/create/CreatePage.tsx:325 +#: src/paths/instance/products/create/CreatePage.tsx:51 +#: src/paths/instance/products/list/Table.tsx:174 +#: src/paths/instance/products/list/Table.tsx:228 +#: src/paths/instance/products/update/UpdatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:134 +#, c-format +msgid "Confirm" +msgstr "" + +#: src/components/form/InputArray.tsx:72 +#, c-format +msgid "The value %1$s is invalid for a payment url" +msgstr "" + +#: src/components/form/InputDate.tsx:67 +#: src/paths/instance/orders/list/index.tsx:123 +#, c-format +msgid "pick a date" +msgstr "" + +#: src/components/form/InputDate.tsx:81 +#, c-format +msgid "clear" +msgstr "" + +#: src/components/form/InputDate.tsx:83 +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "never" +msgstr "" + +#: src/components/form/InputImage.tsx:80 +#, c-format +msgid "Image should be smaller than 1 MB" +msgstr "" + +#: src/components/form/InputLocation.tsx:28 +#, c-format +msgid "Country" +msgstr "" + +#: src/components/form/InputLocation.tsx:30 +#: src/paths/admin/create/CreatePage.tsx:99 +#: src/paths/instance/transfers/list/Table.tsx:124 +#: src/paths/instance/update/UpdatePage.tsx:118 +#, c-format +msgid "Address" +msgstr "" + +#: src/components/form/InputLocation.tsx:34 +#, c-format +msgid "Building number" +msgstr "" + +#: src/components/form/InputLocation.tsx:35 +#, c-format +msgid "Building name" +msgstr "" + +#: src/components/form/InputLocation.tsx:36 +#, c-format +msgid "Street" +msgstr "" + +#: src/components/form/InputLocation.tsx:37 +#, c-format +msgid "Post code" +msgstr "" + +#: src/components/form/InputLocation.tsx:38 +#, c-format +msgid "Town location" +msgstr "" + +#: src/components/form/InputLocation.tsx:39 +#, c-format +msgid "Town" +msgstr "" + +#: src/components/form/InputLocation.tsx:40 +#, c-format +msgid "District" +msgstr "" + +#: src/components/form/InputLocation.tsx:41 +#, c-format +msgid "Country subdivision" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:59 +#, c-format +msgid "Product id" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:60 +#: src/components/product/ProductForm.tsx:99 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:122 +#: src/paths/instance/orders/list/Table.tsx:227 +#: src/paths/instance/products/list/Table.tsx:86 +#, c-format +msgid "Description" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:73 +#: src/components/form/InputTaxes.tsx:81 +#: src/paths/admin/create/CreatePage.tsx:87 src/paths/admin/list/Table.tsx:110 +#: src/paths/instance/details/DetailPage.tsx:76 +#: src/paths/instance/update/UpdatePage.tsx:106 +#, c-format +msgid "Name" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:102 +#, c-format +msgid "loading..." +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:108 +#, c-format +msgid "no products found" +msgstr "" + +#: src/components/form/InputSearchProduct.tsx:116 +#, c-format +msgid "no results" +msgstr "" + +#: src/components/form/InputSecured.tsx:33 +#, c-format +msgid "Deleting" +msgstr "" + +#: src/components/form/InputSecured.tsx:34 +#, c-format +msgid "Changing" +msgstr "" + +#: src/components/form/InputSecured.tsx:60 +#, c-format +msgid "Manage token" +msgstr "" + +#: src/components/form/InputSecured.tsx:83 +#, c-format +msgid "Update" +msgstr "" + +#: src/components/form/InputSecured.tsx:100 +#: src/paths/instance/orders/create/CreatePage.tsx:252 +#: src/paths/instance/orders/create/CreatePage.tsx:273 +#, c-format +msgid "Remove" +msgstr "" + +#: src/components/form/InputSecured.tsx:106 src/components/modal/index.tsx:52 +#: src/components/modal/index.tsx:73 src/paths/admin/create/CreatePage.tsx:114 +#: src/paths/instance/orders/create/CreatePage.tsx:324 +#: src/paths/instance/products/create/CreatePage.tsx:50 +#: src/paths/instance/products/list/Table.tsx:166 +#: src/paths/instance/products/list/Table.tsx:218 +#: src/paths/instance/products/update/UpdatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:88 +#: src/paths/instance/update/UpdatePage.tsx:133 +#, c-format +msgid "Cancel" +msgstr "" + +#: src/components/form/InputStock.tsx:91 +#, c-format +msgid "Manage stock" +msgstr "" + +#: src/components/form/InputStock.tsx:93 +#, c-format +msgid "Infinite" +msgstr "" + +#: src/components/form/InputStock.tsx:105 +#, c-format +msgid "lost cannot be greater that current + incoming (max %1$s)" +msgstr "" + +#: src/components/form/InputStock.tsx:111 +#, c-format +msgid "current stock will change from %1$s to %2$s" +msgstr "" + +#: src/components/form/InputStock.tsx:112 +#, c-format +msgid "current stock will stay at %1$s" +msgstr "" + +#: src/components/form/InputStock.tsx:129 +#: src/paths/instance/products/list/Table.tsx:204 +#, c-format +msgid "Incoming" +msgstr "" + +#: src/components/form/InputStock.tsx:130 +#: src/paths/instance/products/list/Table.tsx:205 +#, c-format +msgid "Lost" +msgstr "" + +#: src/components/form/InputStock.tsx:142 +#, c-format +msgid "Current" +msgstr "" + +#: src/components/form/InputStock.tsx:145 +#, c-format +msgid "without stock" +msgstr "" + +#: src/components/form/InputStock.tsx:150 +#, c-format +msgid "Next restock" +msgstr "" + +#: src/components/form/InputStock.tsx:152 +#, c-format +msgid "Delivery address" +msgstr "" + +#: src/components/form/InputTaxes.tsx:73 +#, c-format +msgid "this product has no taxes" +msgstr "" + +#: src/components/form/InputTaxes.tsx:77 +#: src/paths/instance/orders/details/DetailPage.tsx:145 +#: src/paths/instance/orders/details/DetailPage.tsx:296 +#: src/paths/instance/orders/list/Table.tsx:116 +#: src/paths/instance/transfers/create/CreatePage.tsx:84 +#, c-format +msgid "Amount" +msgstr "" + +#: src/components/form/InputTaxes.tsx:78 +#, c-format +msgid "currency and value separated with colon" +msgstr "" + +#: src/components/form/InputTaxes.tsx:84 +#: src/paths/instance/orders/create/InventoryProductForm.tsx:78 +#, c-format +msgid "Add" +msgstr "" + +#: src/components/menu/SideBar.tsx:53 +#, c-format +msgid "Instance" +msgstr "" + +#: src/components/menu/SideBar.tsx:59 +#, c-format +msgid "Settings" +msgstr "" + +#: src/components/menu/SideBar.tsx:65 +#: src/paths/instance/orders/list/Table.tsx:60 +#, c-format +msgid "Orders" +msgstr "" + +#: src/components/menu/SideBar.tsx:71 +#: src/paths/instance/orders/create/CreatePage.tsx:258 +#: src/paths/instance/products/list/Table.tsx:48 +#, c-format +msgid "Products" +msgstr "" + +#: src/components/menu/SideBar.tsx:77 +#: src/paths/instance/transfers/list/Table.tsx:65 +#, c-format +msgid "Transfers" +msgstr "" + +#: src/components/menu/SideBar.tsx:87 +#, c-format +msgid "Connection" +msgstr "" + +#: src/components/menu/SideBar.tsx:112 src/paths/admin/list/Table.tsx:57 +#, c-format +msgid "Instances" +msgstr "" + +#: src/components/menu/SideBar.tsx:116 +#, c-format +msgid "New" +msgstr "" + +#: src/components/menu/SideBar.tsx:122 +#, c-format +msgid "List" +msgstr "" + +#: src/components/menu/SideBar.tsx:129 +#, c-format +msgid "Log out" +msgstr "" + +#: src/components/modal/index.tsx:74 +#, c-format +msgid "Clear" +msgstr "" + +#: src/components/modal/index.tsx:110 src/components/modal/index.tsx:111 +#, c-format +msgid "should be the same" +msgstr "" + +#: src/components/modal/index.tsx:111 +#, c-format +msgid "cannot be the same as before" +msgstr "" + +#: src/components/modal/index.tsx:114 +#, c-format +msgid "" +"You are updating the authorization token from instance %1$s with id %2$s" +msgstr "" + +#: src/components/modal/index.tsx:124 +#, c-format +msgid "Old token" +msgstr "" + +#: src/components/modal/index.tsx:125 +#, c-format +msgid "New token" +msgstr "" + +#: src/components/modal/index.tsx:127 +#, c-format +msgid "Clearing the auth token will mean public access to the instance" +msgstr "" + +#: src/components/product/ProductForm.tsx:96 +#: src/paths/admin/create/CreatePage.tsx:85 src/paths/admin/list/Table.tsx:109 +#: src/paths/instance/transfers/list/Table.tsx:122 +#, c-format +msgid "ID" +msgstr "" + +#: src/components/product/ProductForm.tsx:98 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:121 +#: src/paths/instance/products/list/Table.tsx:85 +#, c-format +msgid "Image" +msgstr "" + +#: src/components/product/ProductForm.tsx:100 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:123 +#, c-format +msgid "Unit" +msgstr "" + +#: src/components/product/ProductForm.tsx:101 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:124 +#: src/paths/instance/products/list/Table.tsx:162 +#: src/paths/instance/products/list/Table.tsx:214 +#, c-format +msgid "Price" +msgstr "" + +#: src/components/product/ProductForm.tsx:103 +#: src/paths/instance/products/list/Table.tsx:90 +#, c-format +msgid "Stock" +msgstr "" + +#: src/components/product/ProductForm.tsx:105 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:128 +#: src/paths/instance/products/list/Table.tsx:88 +#, c-format +msgid "Taxes" +msgstr "" + +#: src/index.tsx:75 +#, c-format +msgid "Server not found" +msgstr "" + +#: src/index.tsx:85 +#, c-format +msgid "Couldn't access the server" +msgstr "" + +#: src/index.tsx:87 src/index.tsx:99 +#, c-format +msgid "Got message %1$s from %2$s" +msgstr "" + +#: src/index.tsx:97 +#, c-format +msgid "Unexpected Error" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:89 +#: src/paths/instance/update/UpdatePage.tsx:108 +#, c-format +msgid "Auth token" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:91 +#: src/paths/instance/details/DetailPage.tsx:77 +#: src/paths/instance/update/UpdatePage.tsx:110 +#, c-format +msgid "Account address" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:93 +#: src/paths/instance/update/UpdatePage.tsx:112 +#, c-format +msgid "Default max deposit fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:95 +#: src/paths/instance/update/UpdatePage.tsx:114 +#, c-format +msgid "Default max wire fee" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:97 +#: src/paths/instance/update/UpdatePage.tsx:116 +#, c-format +msgid "Default wire fee amortization" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:103 +#: src/paths/instance/update/UpdatePage.tsx:122 +#, c-format +msgid "Jurisdiction" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:107 +#: src/paths/instance/update/UpdatePage.tsx:126 +#, c-format +msgid "Default pay delay" +msgstr "" + +#: src/paths/admin/create/CreatePage.tsx:109 +#: src/paths/instance/update/UpdatePage.tsx:128 +#, c-format +msgid "Default wire transfer delay" +msgstr "" + +#: src/paths/admin/create/index.tsx:58 +#, c-format +msgid "could not create instance" +msgstr "" + +#: src/paths/admin/list/Table.tsx:63 src/paths/admin/list/Table.tsx:131 +#: src/paths/instance/transfers/list/Table.tsx:71 +#, c-format +msgid "Delete" +msgstr "" + +#: src/paths/admin/list/Table.tsx:128 +#, c-format +msgid "Edit" +msgstr "" + +#: src/paths/admin/list/Table.tsx:149 +#: src/paths/instance/products/list/Table.tsx:245 +#, c-format +msgid "There is no instances yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:237 +#, c-format +msgid "Inventory products" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:286 +#, c-format +msgid "Total price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:287 +#, c-format +msgid "Total tax" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:289 +#: src/paths/instance/orders/create/CreatePage.tsx:297 +#, c-format +msgid "Order price" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:295 +#, c-format +msgid "Net" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:300 +#: src/paths/instance/orders/details/DetailPage.tsx:144 +#: src/paths/instance/orders/details/DetailPage.tsx:295 +#: src/paths/instance/orders/list/Table.tsx:117 +#, c-format +msgid "Summary" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:302 +#, c-format +msgid "Payments options" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:303 +#, c-format +msgid "Auto refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:304 +#, c-format +msgid "Refund deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:305 +#, c-format +msgid "Pay deadline" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:307 +#, c-format +msgid "Delivery date" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:308 +#, c-format +msgid "Location" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:312 +#, c-format +msgid "Max fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:313 +#, c-format +msgid "Max wire fee" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:314 +#, c-format +msgid "Wire fee amortization" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:315 +#, c-format +msgid "Fullfilment url" +msgstr "" + +#: src/paths/instance/orders/create/CreatePage.tsx:318 +#, c-format +msgid "Extra information" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:44 +#, c-format +msgid "select a product first" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:51 +#, c-format +msgid "should be greater than 0" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:58 +#, c-format +msgid "" +"cannot be greater than current stock and quantity previously added. max: %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:64 +#, c-format +msgid "cannot be greater than current stock %1$s" +msgstr "" + +#: src/paths/instance/orders/create/InventoryProductForm.tsx:76 +#: src/paths/instance/orders/create/NonInventoryProductForm.tsx:126 +#, c-format +msgid "Quantity" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:92 +#: src/paths/instance/orders/details/DetailPage.tsx:235 +#: src/paths/instance/orders/details/DetailPage.tsx:333 +#, c-format +msgid "Order" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:93 +#, c-format +msgid "claimed" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:110 +#: src/paths/instance/orders/details/DetailPage.tsx:261 +#: src/paths/instance/orders/list/Table.tsx:136 +#, c-format +msgid "copy url" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:126 +#: src/paths/instance/orders/details/DetailPage.tsx:349 +#, c-format +msgid "pay at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:127 +#: src/paths/instance/orders/details/DetailPage.tsx:350 +#, c-format +msgid "created at" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:138 +#: src/paths/instance/orders/details/DetailPage.tsx:289 +#, c-format +msgid "Timeline" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:142 +#: src/paths/instance/orders/details/DetailPage.tsx:293 +#, c-format +msgid "Payment details" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:146 +#: src/paths/instance/orders/details/DetailPage.tsx:299 +#: src/paths/instance/orders/details/DetailPage.tsx:363 +#, c-format +msgid "Order status" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:156 +#: src/paths/instance/orders/details/DetailPage.tsx:308 +#, c-format +msgid "Product list" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:236 +#, c-format +msgid "paid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:238 +#, c-format +msgid "wired" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:241 +#, c-format +msgid "refunded" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:258 +#, c-format +msgid "refund" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:297 +#, c-format +msgid "Refunded amount" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:298 +#, c-format +msgid "Deposit total" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:336 +#, c-format +msgid "unpaid" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:364 +#, c-format +msgid "Order status URL" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:365 +#, c-format +msgid "Pay URI" +msgstr "" + +#: src/paths/instance/orders/details/DetailPage.tsx:383 +#, c-format +msgid "" +"Unknown order status. This is an error, please contact the administrator." +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:56 +#: src/paths/instance/orders/list/index.tsx:147 +#, c-format +msgid "refund created successfully" +msgstr "" + +#: src/paths/instance/orders/details/index.tsx:59 +#: src/paths/instance/orders/list/index.tsx:150 +#, c-format +msgid "could not create the refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:111 +#, c-format +msgid "load newer orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:115 +#, c-format +msgid "Date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:131 +#: src/paths/instance/orders/list/Table.tsx:223 +#, c-format +msgid "Refund" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:145 +#, c-format +msgid "load older orders" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:154 +#, c-format +msgid "No orders has been found" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:202 +#, c-format +msgid "date" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:203 +#, c-format +msgid "amount" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:204 +#, c-format +msgid "reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:224 +#, c-format +msgid "Max refundable:" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "Reason" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "duplicated" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "requested by the customer" +msgstr "" + +#: src/paths/instance/orders/list/Table.tsx:226 +#, c-format +msgid "other" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:91 +#, c-format +msgid "go to order id" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:107 +#, c-format +msgid "Paid" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:108 +#, c-format +msgid "Refunded" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:109 +#, c-format +msgid "Not wired" +msgstr "" + +#: src/paths/instance/orders/list/index.tsx:110 +#, c-format +msgid "All" +msgstr "" + +#: src/paths/instance/products/create/index.tsx:48 +#: src/paths/instance/products/update/index.tsx:64 +#, c-format +msgid "could not create product" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:87 +#, c-format +msgid "Sell" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:89 +#, c-format +msgid "Profit" +msgstr "" + +#: src/paths/instance/products/list/Table.tsx:91 +#, c-format +msgid "Sold" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:59 +#, c-format +msgid "product updated successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:62 +#, c-format +msgid "could not update the product" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:70 +#, c-format +msgid "product delete successfully" +msgstr "" + +#: src/paths/instance/products/list/index.tsx:73 +#, c-format +msgid "could not delete the product" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:59 +#, c-format +msgid "Tips" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:111 +#, c-format +msgid "Committed amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:112 +#, c-format +msgid "Exchange initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:113 +#, c-format +msgid "Merchant initial amount" +msgstr "" + +#: src/paths/instance/tips/list/Table.tsx:148 +#, c-format +msgid "There is no tips yet, add more pressing the + sign" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:50 +#: src/paths/instance/transfers/create/CreatePage.tsx:54 +#: src/paths/instance/transfers/create/CreatePage.tsx:55 +#: src/paths/instance/transfers/create/CreatePage.tsx:56 +#, c-format +msgid "cannot be empty" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:51 +#, c-format +msgid "check the id, doest look valid" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:52 +#, c-format +msgid "should have 52 characters, current %1$s" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:57 +#, c-format +msgid "URL doesn't have the right format" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:74 +#, c-format +msgid "Transfer ID" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:76 +#, c-format +msgid "Account Address" +msgstr "" + +#: src/paths/instance/transfers/create/CreatePage.tsx:82 +#: src/paths/instance/transfers/list/Table.tsx:125 +#, c-format +msgid "Exchange URL" +msgstr "" + +#: src/paths/instance/transfers/create/index.tsx:49 +#, c-format +msgid "could not inform transfer" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:118 +#, c-format +msgid "load newer transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:123 +#, c-format +msgid "Credit" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:126 +#, c-format +msgid "Confirmed" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:127 +#: src/paths/instance/transfers/list/index.tsx:60 +#, c-format +msgid "Verified" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:128 +#, c-format +msgid "Executed at" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "yes" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:138 +#: src/paths/instance/transfers/list/Table.tsx:139 +#, c-format +msgid "no" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:140 +#, c-format +msgid "unknown" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:145 +#, c-format +msgid "load older transfers" +msgstr "" + +#: src/paths/instance/transfers/list/Table.tsx:154 +#, c-format +msgid "There is no transfer yet, add more pressing the + sign" +msgstr "" diff --git a/packages/merchant-backoffice-ui/src/index.tsx b/packages/merchant-backoffice-ui/src/index.tsx new file mode 100644 index 000000000..26dcc3eb8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/index.tsx @@ -0,0 +1,111 @@ +/* + 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 { route } from 'preact-router'; +import { useMemo } from "preact/hooks"; +import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes"; +import { Loading } from "./components/exception/loading"; +import { NotificationCard, NotYetReadyAppMenu } from "./components/menu"; +import { BackendContextProvider, useBackendContext } from './context/backend'; +import { ConfigContextProvider } from './context/config'; +import { FetchContextProvider } from './context/fetch'; +import { TranslationProvider } from './context/translation'; +import { useBackendConfig } from "./hooks/backend"; +import { useTranslator } from './i18n'; +import LoginPage from './paths/login'; +import "./scss/main.scss"; + +export default function Application(): VNode { + return ( + // <FetchContextProvider> + <BackendContextProvider> + <TranslationProvider> + <ApplicationStatusRoutes /> + </TranslationProvider> + </BackendContextProvider> + // </FetchContextProvider> + ); +} + +function ApplicationStatusRoutes(): VNode { + const { updateLoginStatus, triedToLog } = useBackendContext() + const result = useBackendConfig(); + const i18n = useTranslator() + + const updateLoginInfoAndGoToRoot = (url: string, token?: string) => { + updateLoginStatus(url, token) + route('/') + } + + const { currency, version } = result.ok ? result.data : { currency: 'unknown', version: 'unknown' } + const ctx = useMemo(() => ({ currency, version }), [currency, version]) + + if (!triedToLog) { + return <div id="app"> + <NotYetReadyAppMenu title="Welcome!" /> + <LoginPage onConfirm={updateLoginInfoAndGoToRoot} /> + </div> + } + + if (result.clientError && result.isUnauthorized) return <div id="app"> + <NotYetReadyAppMenu title="Login" /> + <LoginPage onConfirm={updateLoginInfoAndGoToRoot} /> + </div> + + if (result.clientError && result.isNotfound) return <div id="app"> + <NotYetReadyAppMenu title="Error" /> + <NotificationCard notification={{ + message: i18n`Server not found`, + type: 'ERROR', + description: `Check your url`, + }} /> + <LoginPage onConfirm={updateLoginInfoAndGoToRoot} /> + </div> + + if (result.serverError) return <div id="app"> + <NotYetReadyAppMenu title="Error" /> + <NotificationCard notification={{ + message: i18n`Couldn't access the server`, + type: 'ERROR', + description: i18n`Got message ${result.message} from ${result.info?.url}`, + }} /> + <LoginPage onConfirm={updateLoginInfoAndGoToRoot} /> + </div> + + if (result.loading) return <Loading /> + + if (!result.ok) return <div id="app"> + <NotYetReadyAppMenu title="Error" /> + <NotificationCard notification={{ + message: i18n`Unexpected Error`, + type: 'ERROR', + description: i18n`Got message ${result.message} from ${result.info?.url}`, + }} /> + <LoginPage onConfirm={updateLoginInfoAndGoToRoot} /> + </div> + + return <div id="app" class="has-navbar-fixed-top"> + <ConfigContextProvider value={ctx}> + <ApplicationReadyRoutes /> + </ConfigContextProvider> + </div> +} diff --git a/packages/merchant-backoffice-ui/src/manifest.json b/packages/merchant-backoffice-ui/src/manifest.json new file mode 100644 index 000000000..d0d3ea463 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "backoffice-preact", + "short_name": "backoffice-preact", + "start_url": "/", + "display": "standalone", + "orientation": "portrait", + "background_color": "#fff", + "theme_color": "#673ab8", + "icons": [ + { + "src": "/assets/icons/android-chrome-192x192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "/assets/icons/android-chrome-512x512.png", + "type": "image/png", + "sizes": "512x512" + } + ] +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx new file mode 100644 index 000000000..c1287557d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/Create.stories.tsx @@ -0,0 +1,46 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { CreatePage as TestedComponent } from './CreatePage'; + + +export default { + title: 'Pages/Instance/Create', + component: TestedComponent, + argTypes: { + onCreate: { action: 'onCreate' }, + goBack: { action: 'goBack' }, + } +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Example = createExample(TestedComponent, { +}); +// export const Example = (a: any): VNode => <CreatePage {...a} />; +// Example.args = { +// isLoading: false +// } diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx new file mode 100644 index 000000000..1851e52f1 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx @@ -0,0 +1,234 @@ +/* + 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 { useState } from "preact/hooks"; +import * as yup from "yup"; +import { AsyncButton } from "../../../components/exception/AsyncButton"; +import { + FormErrors, + FormProvider, +} from "../../../components/form/FormProvider"; +import { SetTokenNewInstanceModal } from "../../../components/modal"; +import { MerchantBackend } from "../../../declaration"; +import { Translate, useTranslator } from "../../../i18n"; +import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields"; +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; +}; + +interface Props { + onCreate: (d: Entity) => Promise<void>; + onBack?: () => void; + forceId?: string; +} + +function with_defaults(id?: string): Partial<Entity> { + return { + id, + payto_uris: [], + default_pay_delay: { d_us: 2 * 1000 * 60 * 60 * 1000 }, // two hours + default_wire_fee_amortization: 1, + default_wire_transfer_delay: { d_us: 1000 * 2 * 60 * 60 * 24 * 1000 }, // two days + }; +} + +function undefinedIfEmpty<T>(obj: T): T | undefined { + return Object.keys(obj).some((k) => (obj as any)[k] !== undefined) + ? obj + : undefined; +} + +export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { + const [value, valueHandler] = useState(with_defaults(forceId)); + const [isTokenSet, updateIsTokenSet] = useState<boolean>(false); + const [isTokenDialogActive, updateIsTokenDialogActive] = + useState<boolean>(false); + + const i18n = useTranslator(); + + const errors: FormErrors<Entity> = { + 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) => { + return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined; + }) + ), + default_max_deposit_fee: !value.default_max_deposit_fee + ? i18n`required` + : !Amounts.parse(value.default_max_deposit_fee) + ? i18n`invalid format` + : undefined, + default_max_wire_fee: !value.default_max_wire_fee + ? i18n`required` + : !Amounts.parse(value.default_max_wire_fee) + ? i18n`invalid format` + : undefined, + default_wire_fee_amortization: + value.default_wire_fee_amortization === undefined + ? i18n`required` + : isNaN(value.default_wire_fee_amortization) + ? i18n`is not a number` + : value.default_wire_fee_amortization < 1 + ? i18n`must be 1 or greater` + : undefined, + default_pay_delay: !value.default_pay_delay ? i18n`required` : undefined, + default_wire_transfer_delay: !value.default_wire_transfer_delay + ? i18n`required` + : undefined, + address: undefinedIfEmpty({ + address_lines: + value.address?.address_lines && value.address?.address_lines.length > 7 + ? i18n`max 7 lines` + : undefined, + }), + jurisdiction: undefinedIfEmpty({ + address_lines: + value.address?.address_lines && value.address?.address_lines.length > 7 + ? i18n`max 7 lines` + : undefined, + }), + }; + + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined + ); + + const submit = (): Promise<void> => { + // use conversion instead of this + 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 = {}; + // remove above use conversion + // schema.validateSync(value, { abortEarly: false }) + return onCreate(value as Entity); + }; + + function updateToken(token: string | null) { + valueHandler((old) => ({ + ...old, + auth_token: token === null ? undefined : token, + })); + } + + return ( + <div> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + {isTokenDialogActive && ( + <SetTokenNewInstanceModal + onCancel={() => { + updateIsTokenDialogActive(false); + updateIsTokenSet(false); + }} + onClear={() => { + updateToken(null); + updateIsTokenDialogActive(false); + updateIsTokenSet(true); + }} + onConfirm={(newToken) => { + updateToken(newToken); + updateIsTokenDialogActive(false); + updateIsTokenSet(true); + }} + /> + )} + </div> + <div class="column" /> + </div> + + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-item has-text-centered"> + <h1 class="title"> + <button + class="button is-danger has-tooltip-bottom" + data-tooltip={i18n`change authorization configuration`} + onClick={() => updateIsTokenDialogActive(true)} + > + <div class="icon is-centered"> + <i class="mdi mdi-lock-reset" /> + </div> + <span> + <Translate>Set access token</Translate> + </span> + </button> + </h1> + </div> + </div> + </div> + </section> + + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <FormProvider<Entity> + errors={errors} + object={value} + valueHandler={valueHandler} + > + <DefaultInstanceFormFields readonlyId={!!forceId} showId={true} /> + </FormProvider> + + <div class="buttons is-right mt-5"> + {onBack && ( + <button class="button" onClick={onBack}> + <Translate>Cancel</Translate> + </button> + )} + <AsyncButton + onClick={submit} + disabled={!isTokenSet || hasErrors} + data-tooltip={ + hasErrors + ? i18n`Need to complete marked fields and choose authorization method` + : "confirm operation" + } + > + <Translate>Confirm</Translate> + </AsyncButton> + </div> + </div> + <div class="column" /> + </div> + </section> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx new file mode 100644 index 000000000..00b3f20fc --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx @@ -0,0 +1,65 @@ +/* + 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 { CreatedSuccessfully } from "../../../components/notifications/CreatedSuccessfully"; +import { Entity } from "./index"; + +export function InstanceCreatedSuccessfully({ entity, onConfirm }: { entity: Entity; onConfirm: () => void; }): VNode { + return <CreatedSuccessfully onConfirm={onConfirm}> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">ID</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.id} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Business Name</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.name} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Access token</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + {entity.auth.method === 'external' && 'external'} + {entity.auth.method === 'token' && + <input class="input" readonly value={entity.auth.token} />} + </p> + </div> + </div> + </div> + </CreatedSuccessfully>; +} diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx new file mode 100644 index 000000000..aaed6d666 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx @@ -0,0 +1,74 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { NotificationCard } from "../../../components/menu"; +import { MerchantBackend } from "../../../declaration"; +import { useAdminAPI } from "../../../hooks/instance"; +import { useTranslator } from "../../../i18n"; +import { Notification } from "../../../utils/types"; +import { CreatePage } from "./CreatePage"; +import { InstanceCreatedSuccessfully } from "./InstanceCreatedSuccessfully"; + +interface Props { + onBack?: () => void; + onConfirm: () => void; + forceId?: string; +} +export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage; + +export default function Create({ onBack, onConfirm, forceId }: Props): VNode { + const { createInstance } = useAdminAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); + const [createdOk, setCreatedOk] = useState<Entity | undefined>(undefined); + const i18n = useTranslator(); + + if (createdOk) { + return ( + <InstanceCreatedSuccessfully entity={createdOk} onConfirm={onConfirm} /> + ); + } + + return ( + <Fragment> + <NotificationCard notification={notif} /> + + <CreatePage + onBack={onBack} + forceId={forceId} + onCreate={( + d: MerchantBackend.Instances.InstanceConfigurationMessage + ) => { + return createInstance(d) + .then(() => { + setCreatedOk(d); + }) + .catch((error) => { + setNotif({ + message: i18n`Failed to create instance`, + type: "ERROR", + description: error.message, + }); + }); + }} + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx new file mode 100644 index 000000000..928658910 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/TableActive.tsx @@ -0,0 +1,184 @@ +/* + 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[]; + 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, onPurge, setInstanceName, onDelete, selected }: Props): VNode { + const [actionQueue, actionQueueHandler] = useState<Actions[]>([]); + const [rowSelection, rowSelectionHandler] = useState<string[]>([]) + + useEffect(() => { + if (actionQueue.length > 0 && !selected && actionQueue[0].type == 'DELETE') { + onDelete(actionQueue[0].element) + actionQueueHandler(actionQueue.slice(1)) + } + }, [actionQueue, selected, onDelete]) + + useEffect(() => { + if (actionQueue.length > 0 && !selected && actionQueue[0].type == 'UPDATE') { + onUpdate(actionQueue[0].element.id) + actionQueueHandler(actionQueue.slice(1)) + } + }, [actionQueue, selected, onUpdate]) + + 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>Instances</Translate></p> + + <div class="card-header-icon" aria-label="more options"> + + <button class={rowSelection.length > 0 ? "button is-danger" : "is-hidden"} + type="button" onClick={(): void => actionQueueHandler(buildActions(instances, rowSelection, 'DELETE'))} > + <Translate>Delete</Translate> + </button> + </div> + <div class="card-header-icon" aria-label="more options"> + <span class="has-tooltip-left" data-tooltip={i18n`add new instance`}> + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small" ><i class="mdi mdi-plus mdi-36px" /></span> + </button> + </span> + </div> + + </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} onUpdate={onUpdate} setInstanceName={setInstanceName} onDelete={onDelete} rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler} /> : + <EmptyTable /> + } + </div> + </div> + </div> + </div> +} +interface TableProps { + rowSelection: string[]; + 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; +} + +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, onPurge }: 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"> + <label class="b-checkbox checkbox"> + <input type="checkbox" checked={rowSelection.length === instances.length} onClick={(): void => rowSelectionHandler(rowSelection.length === instances.length ? [] : instances.map(i => i.id))} /> + <span class="check" /> + </label> + </th> + <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"> + <label class="b-checkbox checkbox"> + <input type="checkbox" checked={rowSelection.indexOf(i.id) != -1} onClick={(): void => rowSelectionHandler(toggleSelected(i.id))} /> + <span class="check" /> + </label> + </td> + <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-success jb-modal" type="button" onClick={(): void => onUpdate(i.id)}> + <Translate>Edit</Translate> + </button> + {!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> + })} + + </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/merchant-backoffice-ui/src/paths/admin/list/View.stories.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/View.stories.tsx new file mode 100644 index 000000000..3da8c2e50 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.stories.tsx @@ -0,0 +1,82 @@ +/* + 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 } from 'preact'; +import { View } from './View'; + + +export default { + title: 'Pages/Instance/List', + component: View, + argTypes: { + onSelect: { action: 'onSelect' }, + }, +}; + +export const Empty = (a: any) => <View {...a} />; +Empty.args = { + instances: [] +} + +export const WithDefaultInstance = (a: any) => <View {...a} />; +WithDefaultInstance.args = { + instances: [{ + id: 'default', + name: 'the default instance', + merchant_pub: 'abcdef', + payment_targets: [] + }] +} + +export const WithFiveInstance = (a: any) => <View {...a} />; +WithFiveInstance.args = { + instances: [{ + id: 'first', + name: 'the first instance', + merchant_pub: 'abcdefgh', + payment_targets: ['asd'] + }, { + id: 'second', + name: 'the second instance', + merchant_pub: 'zxczxcz', + payment_targets: ['asd'] + }, { + id: 'third', + name: 'the third instance', + merchant_pub: 'QWEQWEWQE', + payment_targets: ['asd'] + }, { + id: 'other', + name: 'the other instance', + merchant_pub: 'FHJHGJGHJ', + payment_targets: ['asd'] + }, { + id: 'another', + name: 'the another instance', + merchant_pub: 'abcd3423423efgh', + payment_targets: ['asd'] + }, { + id: 'last', + name: 'last instance', + merchant_pub: 'zxcvvbnm', + payment_targets: ['pay-to', 'asd'] + }] +} diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx new file mode 100644 index 000000000..a77a5a1bf --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx @@ -0,0 +1,80 @@ +/* + 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 { MerchantBackend } from "../../../declaration"; +import { CardTable as CardTableActive } from './TableActive'; +import { useState } from 'preact/hooks'; +import { Translate, useTranslator } from "../../../i18n"; + +interface Props { + instances: MerchantBackend.Instances.Instance[]; + onCreate: () => void; + onUpdate: (id: string) => void; + onDelete: (id: MerchantBackend.Instances.Instance) => void; + onPurge: (id: MerchantBackend.Instances.Instance) => void; + selected?: boolean; + setInstanceName: (s: string) => void; +} + +export function View({ instances, onCreate, onDelete, onPurge, onUpdate, setInstanceName, selected }: Props): VNode { + const [show, setShow] = useState<"active" | "deleted" | null>("active"); + const showIsActive = show === 'active' ? "is-active" : '' + const showIsDeleted = show === 'deleted' ? "is-active" : '' + const showAll = show === null ? "is-active" : '' + const i18n = useTranslator() + + const showingInstances = showIsDeleted ? + instances.filter(i => i.deleted) : (showIsActive ? + instances.filter(i => !i.deleted) : + instances) + + return <div id="app"> + + <section class="section is-main-section"> + <div class="columns"> + <div class="column is-two-thirds"> + <div class="tabs" style={{ overflow: 'inherit' }}> + <ul> + <li class={showIsActive}> + <div class="has-tooltip-right" data-tooltip={i18n`Only show active instances`}> + <a onClick={() => setShow("active")}><Translate>Active</Translate></a> + </div> + </li> + <li class={showIsDeleted}> + <div class="has-tooltip-right" data-tooltip={i18n`Only show deleted instances`}> + <a onClick={() => setShow("deleted")}><Translate>Deleted</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> + + </div > +} diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx new file mode 100644 index 000000000..c5609fd10 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx @@ -0,0 +1,126 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Loading } from "../../../components/exception/loading"; +import { NotificationCard } from "../../../components/menu"; +import { DeleteModal, PurgeModal } from "../../../components/modal"; +import { MerchantBackend } from "../../../declaration"; +import { HttpError } from "../../../hooks/backend"; +import { useAdminAPI, useBackendInstances } from "../../../hooks/instance"; +import { useTranslator } from "../../../i18n"; +import { Notification } from "../../../utils/types"; +import { View } from "./View"; + +interface Props { + onCreate: () => void; + onUpdate: (id: string) => void; + instances: MerchantBackend.Instances.Instance[]; + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onLoadError: (error: HttpError) => VNode; + setInstanceName: (s: string) => void; +} + +export default function Instances({ + onUnauthorized, + onLoadError, + onNotFound, + onCreate, + onUpdate, + setInstanceName, +}: Props): VNode { + const result = useBackendInstances(); + const [deleting, setDeleting] = + useState<MerchantBackend.Instances.Instance | null>(null); + const [purging, setPurging] = + useState<MerchantBackend.Instances.Instance | null>(null); + const { deleteInstance, purgeInstance } = useAdminAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); + const i18n = useTranslator(); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + + return ( + <Fragment> + <NotificationCard notification={notif} /> + <View + instances={result.data.instances} + onDelete={setDeleting} + onCreate={onCreate} + onPurge={setPurging} + onUpdate={onUpdate} + setInstanceName={setInstanceName} + selected={!!deleting} + /> + {deleting && ( + <DeleteModal + element={deleting} + onCancel={() => setDeleting(null)} + onConfirm={async (): Promise<void> => { + try { + await deleteInstance(deleting.id); + // pushNotification({ message: 'delete_success', type: 'SUCCESS' }) + setNotif({ + message: i18n`Instance "${deleting.name}" (ID: ${deleting.id}) has been deleted`, + type: "SUCCESS", + }); + } catch (error) { + setNotif({ + message: i18n`Failed to delete instance`, + type: "ERROR", + description: error instanceof Error ? error.message : undefined, + }); + // pushNotification({ message: 'delete_error', type: 'ERROR' }) + } + setDeleting(null); + }} + /> + )} + {purging && ( + <PurgeModal + element={purging} + onCancel={() => setPurging(null)} + onConfirm={async (): Promise<void> => { + try { + await purgeInstance(purging.id); + setNotif({ + message: i18n`Instance "${purging.name}" (ID: ${purging.id}) has been disabled`, + type: "SUCCESS", + }); + } catch (error) { + setNotif({ + message: i18n`Failed to purge instance`, + type: "ERROR", + description: error instanceof Error ? error.message : undefined, + }); + } + setPurging(null); + }} + /> + )} + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx new file mode 100644 index 000000000..2561f5842 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx @@ -0,0 +1,87 @@ +/* + 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 { useState } from "preact/hooks"; +import { FormProvider } from "../../../components/form/FormProvider"; +import { Input } from "../../../components/form/Input"; +import { MerchantBackend } from "../../../declaration"; +import { useTranslator } from "../../../i18n"; + +type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage; +interface Props { + onUpdate: () => void; + onDelete: () => void; + selected: MerchantBackend.Instances.QueryInstancesResponse; +} + +function convert(from: MerchantBackend.Instances.QueryInstancesResponse): Entity { + const { accounts, ...rest } = from + const payto_uris = accounts.filter(a => a.active).map(a => a.payto_uri) + const defaults = { + default_wire_fee_amortization: 1, + default_pay_delay: { d_us: 1000 * 60 * 60 }, //one hour + default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 }, //two hours + } + return { ...defaults, ...rest, payto_uris }; +} + +export function DetailPage({ selected }: Props): VNode { + const [value, valueHandler] = useState<Partial<Entity>>(convert(selected)) + + const i18n = useTranslator() + + return <div> + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <h1 class="title"> + Here goes the instance description + </h1> + </div> + </div> + <div class="level-right" style="display: none;"> + <div class="level-item" /> + </div> + </div> + </div> + </section> + + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-6"> + <FormProvider<Entity> object={value} valueHandler={valueHandler} > + + <Input<Entity> name="name" readonly label={i18n`Name`} /> + <Input<Entity> name="payto_uris" readonly label={i18n`Account address`} /> + + </FormProvider> + </div> + <div class="column" /> + </div> + </section> + + </div> + +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/Details.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/Details.stories.tsx new file mode 100644 index 000000000..fb7c9144c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/details/Details.stories.tsx @@ -0,0 +1,61 @@ +/* + 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, FunctionalComponent } from "preact"; +import { DetailPage as TestedComponent } from "./DetailPage"; + +export default { + title: "Pages/Instance/Detail", + component: TestedComponent, + argTypes: { + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const Example = createExample(TestedComponent, { + selected: { + accounts: [], + name: "name", + auth: { method: "external" }, + address: {}, + jurisdiction: {}, + default_max_deposit_fee: "TESTKUDOS:2", + default_max_wire_fee: "TESTKUDOS:1", + default_pay_delay: { + d_us: 1000000, + }, + default_wire_fee_amortization: 1, + default_wire_transfer_delay: { + d_us: 100000, + }, + merchant_pub: "ASDWQEKASJDKSADJ", + }, +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/index.tsx new file mode 100644 index 000000000..15675891e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/details/index.tsx @@ -0,0 +1,65 @@ +/* + 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/> + */ +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Loading } from "../../../components/exception/loading"; +import { DeleteModal } from "../../../components/modal"; +import { useInstanceContext } from "../../../context/instance"; +import { HttpError } from "../../../hooks/backend"; +import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance"; +import { DetailPage } from "./DetailPage"; + +interface Props { + onUnauthorized: () => VNode; + onLoadError: (error: HttpError) => VNode; + onUpdate: () => void; + onNotFound: () => VNode; + onDelete: () => void; +} + +export default function Detail({ onUpdate, onLoadError, onUnauthorized, onDelete, onNotFound }: Props): VNode { + const { id } = useInstanceContext() + const result = useInstanceDetails() + const [deleting, setDeleting] = useState<boolean>(false) + + const { deleteInstance } = useInstanceAPI() + + if (result.clientError && result.isUnauthorized) return onUnauthorized() + if (result.clientError && result.isNotfound) return onNotFound() + if (result.loading) return <Loading /> + if (!result.ok) return onLoadError(result) + + return <Fragment> + <DetailPage + selected={result.data} + onUpdate={onUpdate} + onDelete={() => setDeleting(true)} + /> + {deleting && <DeleteModal + element={{ name: result.data.name, id }} + onCancel={() => setDeleting(false)} + onConfirm={async (): Promise<void> => { + try { + await deleteInstance() + onDelete() + } catch (error) { + } + setDeleting(false) + }} + />} + + </Fragment> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx new file mode 100644 index 000000000..52363d3cd --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx @@ -0,0 +1,178 @@ +/* + 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 { MerchantBackend } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; + +export interface Props { + status: MerchantBackend.Instances.AccountKycRedirects; +} + +export function ListPage({ status }: Props): VNode { + const i18n = useTranslator(); + + return ( + <section class="section is-main-section"> + <div class="card has-table"> + <header class="card-header"> + <p class="card-header-title"> + <span class="icon"> + <i class="mdi mdi-clock" /> + </span> + <Translate>Pending KYC verification</Translate> + </p> + + <div class="card-header-icon" aria-label="more options" /> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {status.pending_kycs.length > 0 ? ( + <PendingTable entries={status.pending_kycs} /> + ) : ( + <EmptyTable /> + )} + </div> + </div> + </div> + </div> + + {status.timeout_kycs.length > 0 ? ( + <div class="card has-table"> + <header class="card-header"> + <p class="card-header-title"> + <span class="icon"> + <i class="mdi mdi-clock" /> + </span> + <Translate>Timed out</Translate> + </p> + + <div class="card-header-icon" aria-label="more options" /> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {status.timeout_kycs.length > 0 ? ( + <TimedOutTable entries={status.timeout_kycs} /> + ) : ( + <EmptyTable /> + )} + </div> + </div> + </div> + </div> + ) : undefined} + </section> + ); +} +interface PendingTableProps { + entries: MerchantBackend.Instances.MerchantAccountKycRedirect[]; +} + +interface TimedOutTableProps { + entries: MerchantBackend.Instances.ExchangeKycTimeout[]; +} + +function PendingTable({ entries }: PendingTableProps): VNode { + return ( + <div class="table-container"> + <table class="table is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>Exchange</Translate> + </th> + <th> + <Translate>Target account</Translate> + </th> + <th> + <Translate>KYC URL</Translate> + </th> + </tr> + </thead> + <tbody> + {entries.map((e, i) => { + return ( + <tr key={i}> + <td>{e.exchange_url}</td> + <td>{e.payto_uri}</td> + <td> + <a href={e.kyc_url} target="_black" rel="noreferrer"> + {e.kyc_url} + </a> + </td> + </tr> + ); + })} + </tbody> + </table> + </div> + ); +} + +function TimedOutTable({ entries }: TimedOutTableProps): VNode { + return ( + <div class="table-container"> + <table class="table is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>Exchange</Translate> + </th> + <th> + <Translate>Code</Translate> + </th> + <th> + <Translate>Http Status</Translate> + </th> + </tr> + </thead> + <tbody> + {entries.map((e, i) => { + return ( + <tr key={i}> + <td>{e.exchange_url}</td> + <td>{e.exchange_code}</td> + <td>{e.exchange_http_status}</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-happy mdi-48px" /> + </span> + </p> + <p> + <Translate>No pending kyc verification!</Translate> + </p> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx new file mode 100644 index 000000000..5dff01994 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx @@ -0,0 +1,51 @@ +/* + 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 { Loading } from "../../../../components/exception/loading"; +import { HttpError } from "../../../../hooks/backend"; +import { useInstanceKYCDetails } from "../../../../hooks/instance"; +import { ListPage } from "./ListPage"; + +interface Props { + onUnauthorized: () => VNode; + onLoadError: (error: HttpError) => VNode; + onNotFound: () => VNode; +} + +export default function ListKYC({ + onUnauthorized, + onLoadError, + onNotFound, +}: Props): VNode { + const result = useInstanceKYCDetails(); + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + + const status = result.data.type === "ok" ? undefined : result.data.status; + + if (!status) { + return <div>no kyc required</div>; + } + return <ListPage status={status} />; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx new file mode 100644 index 000000000..43df8484a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx @@ -0,0 +1,70 @@ +/* + 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, FunctionalComponent } from "preact"; +import { CreatePage as TestedComponent } from "./CreatePage"; + +export default { + title: "Pages/Order/Create", + component: TestedComponent, + argTypes: { + onCreate: { action: "onCreate" }, + goBack: { action: "goBack" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const Example = createExample(TestedComponent, { + instanceConfig: { + default_max_deposit_fee: "", + default_max_wire_fee: "", + default_pay_delay: { + d_us: 1000 * 60 * 60, + }, + default_wire_fee_amortization: 1, + }, + instanceInventory: [ + { + id: "t-shirt-1", + description: "a m size t-shirt", + price: "TESTKUDOS:1", + total_stock: -1, + }, + { + id: "t-shirt-2", + price: "TESTKUDOS:1", + description: "a xl size t-shirt", + } as any, + { + id: "t-shirt-3", + price: "TESTKUDOS:1", + description: "a s size t-shirt", + } as any, + ], +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx new file mode 100644 index 000000000..c08d8ee1d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx @@ -0,0 +1,576 @@ +/* + 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 { add, isAfter, isBefore, isFuture } from "date-fns"; +import { Amounts } from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; +import { + FormProvider, + FormErrors, +} from "../../../../components/form/FormProvider"; +import { Input } from "../../../../components/form/Input"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { InputDate } from "../../../../components/form/InputDate"; +import { InputGroup } from "../../../../components/form/InputGroup"; +import { InputLocation } from "../../../../components/form/InputLocation"; +import { ProductList } from "../../../../components/product/ProductList"; +import { useConfigContext } from "../../../../context/config"; +import { Duration, MerchantBackend, WithId } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; +import { OrderCreateSchema as schema } from "../../../../schemas/index"; +import { rate } from "../../../../utils/amount"; +import { InventoryProductForm } from "../../../../components/product/InventoryProductForm"; +import { NonInventoryProductFrom } from "../../../../components/product/NonInventoryProductForm"; +import { InputNumber } from "../../../../components/form/InputNumber"; +import { InputBoolean } from "../../../../components/form/InputBoolean"; + +interface Props { + onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void; + onBack?: () => void; + instanceConfig: InstanceConfig; + instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[]; +} +interface InstanceConfig { + default_max_wire_fee: string; + default_max_deposit_fee: string; + default_wire_fee_amortization: number; + default_pay_delay: Duration; +} + +function with_defaults(config: InstanceConfig): Partial<Entity> { + const defaultPayDeadline = + !config.default_pay_delay || config.default_pay_delay.d_us === "forever" + ? undefined + : add(new Date(), { seconds: config.default_pay_delay.d_us / 1000 }); + + return { + inventoryProducts: {}, + products: [], + pricing: {}, + payments: { + max_wire_fee: config.default_max_wire_fee, + max_fee: config.default_max_deposit_fee, + wire_fee_amortization: config.default_wire_fee_amortization, + pay_deadline: defaultPayDeadline, + refund_deadline: defaultPayDeadline, + createToken: true, + }, + shipping: {}, + extra: "", + }; +} + +interface ProductAndQuantity { + product: MerchantBackend.Products.ProductDetail & WithId; + quantity: number; +} +export interface ProductMap { + [id: string]: ProductAndQuantity; +} + +interface Pricing { + products_price: string; + order_price: string; + summary: string; +} +interface Shipping { + delivery_date?: Date; + delivery_location?: MerchantBackend.Location; + fullfilment_url?: string; +} +interface Payments { + refund_deadline?: Date; + pay_deadline?: Date; + wire_transfer_deadline?: Date; + auto_refund_deadline?: Date; + max_fee?: string; + max_wire_fee?: string; + wire_fee_amortization?: number; + createToken: boolean; + minimum_age?: number; +} +interface Entity { + inventoryProducts: ProductMap; + products: MerchantBackend.Product[]; + pricing: Partial<Pricing>; + payments: Partial<Payments>; + shipping: Partial<Shipping>; + extra: string; +} + +const stringIsValidJSON = (value: string) => { + try { + JSON.parse(value.trim()); + return true; + } catch { + return false; + } +}; + +function undefinedIfEmpty<T>(obj: T): T | undefined { + return Object.keys(obj).some((k) => (obj as any)[k] !== undefined) + ? obj + : undefined; +} + +export function CreatePage({ + onCreate, + onBack, + instanceConfig, + instanceInventory, +}: Props): VNode { + const [value, valueHandler] = useState(with_defaults(instanceConfig)); + const config = useConfigContext(); + const zero = Amounts.getZero(config.currency); + + const inventoryList = Object.values(value.inventoryProducts || {}); + const productList = Object.values(value.products || {}); + + const i18n = useTranslator(); + + const errors: FormErrors<Entity> = { + pricing: undefinedIfEmpty({ + summary: !value.pricing?.summary ? i18n`required` : undefined, + order_price: !value.pricing?.order_price + ? i18n`required` + : Amounts.isZero(value.pricing.order_price) + ? i18n`must be greater than 0` + : undefined, + }), + extra: + value.extra && !stringIsValidJSON(value.extra) + ? i18n`not a valid json` + : undefined, + payments: undefinedIfEmpty({ + refund_deadline: !value.payments?.refund_deadline + ? undefined + : !isFuture(value.payments.refund_deadline) + ? i18n`should be in the future` + : value.payments.pay_deadline && + isBefore(value.payments.refund_deadline, value.payments.pay_deadline) + ? i18n`refund deadline cannot be before pay deadline` + : value.payments.wire_transfer_deadline && + isBefore( + value.payments.wire_transfer_deadline, + value.payments.refund_deadline + ) + ? i18n`wire transfer deadline cannot be before refund deadline` + : undefined, + pay_deadline: !value.payments?.pay_deadline + ? undefined + : !isFuture(value.payments.pay_deadline) + ? i18n`should be in the future` + : value.payments.wire_transfer_deadline && + isBefore( + value.payments.wire_transfer_deadline, + value.payments.pay_deadline + ) + ? i18n`wire transfer deadline cannot be before pay deadline` + : undefined, + auto_refund_deadline: !value.payments?.auto_refund_deadline + ? undefined + : !isFuture(value.payments.auto_refund_deadline) + ? i18n`should be in the future` + : !value.payments?.refund_deadline + ? i18n`should have a refund deadline` + : !isAfter( + value.payments.refund_deadline, + value.payments.auto_refund_deadline + ) + ? i18n`auto refund cannot be after refund deadline` + : undefined, + }), + shipping: undefinedIfEmpty({ + delivery_date: !value.shipping?.delivery_date + ? undefined + : !isFuture(value.shipping.delivery_date) + ? i18n`should be in the future` + : undefined, + }), + }; + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined + ); + + const submit = (): void => { + const order = schema.cast(value); + if (!value.payments) return; + if (!value.shipping) return; + + const request: MerchantBackend.Orders.PostOrderRequest = { + order: { + amount: order.pricing.order_price, + summary: order.pricing.summary, + products: productList, + extra: value.extra, + pay_deadline: value.payments.pay_deadline + ? { + t_s: Math.floor(value.payments.pay_deadline.getTime() / 1000), + } + : undefined, + wire_transfer_deadline: value.payments.wire_transfer_deadline + ? { + t_s: Math.floor( + value.payments.wire_transfer_deadline.getTime() / 1000 + ), + } + : undefined, + refund_deadline: value.payments.refund_deadline + ? { + t_s: Math.floor(value.payments.refund_deadline.getTime() / 1000), + } + : undefined, + auto_refund: value.payments.auto_refund_deadline + ? { + d_us: Math.floor( + value.payments.auto_refund_deadline.getTime() * 1000 + ), + } + : undefined, + wire_fee_amortization: value.payments.wire_fee_amortization as number, + max_fee: value.payments.max_fee as string, + max_wire_fee: value.payments.max_wire_fee as string, + + delivery_date: value.shipping.delivery_date + ? { t_s: value.shipping.delivery_date.getTime() / 1000 } + : undefined, + delivery_location: value.shipping.delivery_location, + fulfillment_url: value.shipping.fullfilment_url, + minimum_age: value.payments.minimum_age, + }, + inventory_products: inventoryList.map((p) => ({ + product_id: p.product.id, + quantity: p.quantity, + })), + create_token: value.payments.createToken, + }; + + onCreate(request); + }; + + const addProductToTheInventoryList = ( + product: MerchantBackend.Products.ProductDetail & WithId, + quantity: number + ) => { + valueHandler((v) => { + const inventoryProducts = { ...v.inventoryProducts }; + inventoryProducts[product.id] = { product, quantity }; + return { ...v, inventoryProducts }; + }); + }; + + const removeProductFromTheInventoryList = (id: string) => { + valueHandler((v) => { + const inventoryProducts = { ...v.inventoryProducts }; + delete inventoryProducts[id]; + return { ...v, inventoryProducts }; + }); + }; + + const addNewProduct = async (product: MerchantBackend.Product) => { + return valueHandler((v) => { + const products = v.products ? [...v.products, product] : []; + return { ...v, products }; + }); + }; + + const removeFromNewProduct = (index: number) => { + valueHandler((v) => { + const products = v.products ? [...v.products] : []; + products.splice(index, 1); + return { ...v, products }; + }); + }; + + const [editingProduct, setEditingProduct] = useState< + MerchantBackend.Product | undefined + >(undefined); + + const totalPriceInventory = inventoryList.reduce((prev, cur) => { + const p = Amounts.parseOrThrow(cur.product.price); + return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount; + }, zero); + + const totalPriceProducts = productList.reduce((prev, cur) => { + if (!cur.price) return zero; + const p = Amounts.parseOrThrow(cur.price); + return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount; + }, zero); + + const hasProducts = inventoryList.length > 0 || productList.length > 0; + const totalPrice = Amounts.add(totalPriceInventory, totalPriceProducts); + + const totalAsString = Amounts.stringify(totalPrice.amount); + const allProducts = productList.concat(inventoryList.map(asProduct)); + + useEffect(() => { + valueHandler((v) => { + return { + ...v, + pricing: { + ...v.pricing, + products_price: hasProducts ? totalAsString : undefined, + order_price: hasProducts ? totalAsString : undefined, + }, + }; + }); + }, [hasProducts, totalAsString]); + + const discountOrRise = rate( + value.pricing?.order_price || `${config.currency}:0`, + totalAsString + ); + + const minAgeByProducts = allProducts.reduce( + (cur, prev) => + !prev.minimum_age || cur > prev.minimum_age ? cur : prev.minimum_age, + 0 + ); + return ( + <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + {/* // FIXME: translating plural singular */} + <InputGroup + name="inventory_products" + label={i18n`Manage products in order`} + alternative={ + allProducts.length > 0 && ( + <p> + {allProducts.length} products with a total price of{" "} + {totalAsString}. + </p> + ) + } + tooltip={i18n`Manage list of products in the order.`} + > + <InventoryProductForm + currentProducts={value.inventoryProducts || {}} + onAddProduct={addProductToTheInventoryList} + inventory={instanceInventory} + /> + + <NonInventoryProductFrom + productToEdit={editingProduct} + onAddProduct={(p) => { + setEditingProduct(undefined); + return addNewProduct(p); + }} + /> + + {allProducts.length > 0 && ( + <ProductList + list={allProducts} + actions={[ + { + name: i18n`Remove`, + tooltip: i18n`Remove this product from the order.`, + handler: (e, index) => { + if (e.product_id) { + removeProductFromTheInventoryList(e.product_id); + } else { + removeFromNewProduct(index); + setEditingProduct(e); + } + }, + }, + ]} + /> + )} + </InputGroup> + + <FormProvider<Entity> + errors={errors} + object={value} + valueHandler={valueHandler as any} + > + {hasProducts ? ( + <Fragment> + <InputCurrency + name="pricing.products_price" + label={i18n`Total price`} + readonly + tooltip={i18n`total product price added up`} + /> + <InputCurrency + name="pricing.order_price" + label={i18n`Total price`} + addonAfter={ + discountOrRise > 0 && + (discountOrRise < 1 + ? `discount of %${Math.round( + (1 - discountOrRise) * 100 + )}` + : `rise of %${Math.round((discountOrRise - 1) * 100)}`) + } + tooltip={i18n`Amount to be paid by the customer`} + /> + </Fragment> + ) : ( + <InputCurrency + name="pricing.order_price" + label={i18n`Order price`} + tooltip={i18n`final order price`} + /> + )} + + <Input + name="pricing.summary" + inputType="multiline" + label={i18n`Summary`} + tooltip={i18n`Title of the order to be shown to the customer`} + /> + + <InputGroup + name="shipping" + label={i18n`Shipping and Fulfillment`} + initialActive + > + <InputDate + name="shipping.delivery_date" + label={i18n`Delivery date`} + tooltip={i18n`Deadline for physical delivery assured by the merchant.`} + /> + {value.shipping?.delivery_date && ( + <InputGroup + name="shipping.delivery_location" + label={i18n`Location`} + tooltip={i18n`address where the products will be delivered`} + > + <InputLocation name="shipping.delivery_location" /> + </InputGroup> + )} + <Input + name="shipping.fullfilment_url" + label={i18n`Fulfillment URL`} + tooltip={i18n`URL to which the user will be redirected after successful payment.`} + /> + </InputGroup> + + <InputGroup + name="payments" + label={i18n`Taler payment options`} + tooltip={i18n`Override default Taler payment settings for this order`} + > + <InputDate + name="payments.pay_deadline" + label={i18n`Payment deadline`} + tooltip={i18n`Deadline for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline.`} + /> + <InputDate + name="payments.refund_deadline" + label={i18n`Refund deadline`} + tooltip={i18n`Time until which the order can be refunded by the merchant.`} + /> + <InputDate + name="payments.wire_transfer_deadline" + label={i18n`Wire transfer deadline`} + tooltip={i18n`Deadline for the exchange to make the wire transfer.`} + /> + <InputDate + name="payments.auto_refund_deadline" + label={i18n`Auto-refund deadline`} + tooltip={i18n`Time until which the wallet will automatically check for refunds without user interaction.`} + /> + + <InputCurrency + name="payments.max_fee" + label={i18n`Maximum deposit fee`} + tooltip={i18n`Maximum deposit fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`} + /> + <InputCurrency + name="payments.max_wire_fee" + label={i18n`Maximum wire fee`} + tooltip={i18n`Maximum aggregate wire fees the merchant is willing to cover for this order. Wire fees exceeding this amount are to be covered by the customers.`} + /> + <InputNumber + name="payments.wire_fee_amortization" + label={i18n`Wire fee amortization`} + tooltip={i18n`Factor by which wire fees exceeding the above threshold are divided to determine the share of excess wire fees to be paid explicitly by the consumer.`} + /> + <InputBoolean + name="payments.createToken" + label={i18n`Create token`} + tooltip={i18n`Uncheck this option if the merchant backend generated an order ID with enough entropy to prevent adversarial claims.`} + /> + <InputNumber + name="payments.minimum_age" + label={i18n`Minimum age required`} + tooltip={i18n`Any value greater than 0 will limit the coins able be used to pay this contract. If empty the age restriction will be defined by the products`} + help={ + minAgeByProducts > 0 + ? i18n`Min age defined by the producs is ${minAgeByProducts}` + : undefined + } + /> + </InputGroup> + + <InputGroup + name="extra" + label={i18n`Additional information`} + tooltip={i18n`Custom information to be included in the contract for this order.`} + > + <Input + name="extra" + inputType="multiline" + label={`Value`} + tooltip={i18n`You must enter a value in JavaScript Object Notation (JSON).`} + /> + </InputGroup> + </FormProvider> + + <div class="buttons is-right mt-5"> + {onBack && ( + <button class="button" onClick={onBack}> + <Translate>Cancel</Translate> + </button> + )} + <button + class="button is-success" + onClick={submit} + disabled={hasErrors} + > + <Translate>Confirm</Translate> + </button> + </div> + </div> + <div class="column" /> + </div> + </section> + </div> + ); +} + +function asProduct(p: ProductAndQuantity): MerchantBackend.Product { + return { + product_id: p.product.id, + image: p.product.image, + price: p.product.price, + unit: p.product.unit, + quantity: p.quantity, + description: p.product.description, + taxes: p.product.taxes, + minimum_age: p.product.minimum_age, + }; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx new file mode 100644 index 000000000..14c5d68c3 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx @@ -0,0 +1,89 @@ +/* + 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/> + */ +import { h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; +import { CreatedSuccessfully } from "../../../../components/notifications/CreatedSuccessfully"; +import { useOrderAPI } from "../../../../hooks/order"; +import { Translate } from "../../../../i18n"; +import { Entity } from "./index"; + +interface Props { + entity: Entity; + onConfirm: () => void; + onCreateAnother?: () => void; +} + +export function OrderCreatedSuccessfully({ entity, onConfirm, onCreateAnother }: Props): VNode { + const { getPaymentURL } = useOrderAPI() + const [url, setURL] = useState<string | undefined>(undefined) + + useEffect(() => { + getPaymentURL(entity.response.order_id).then(response => { + setURL(response.data) + }) + }, [getPaymentURL, entity.response.order_id]) + + return <CreatedSuccessfully onConfirm={onConfirm} onCreateAnother={onCreateAnother}> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"><Translate>Amount</Translate></label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.request.order.amount} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"><Translate>Summary</Translate></label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.request.order.summary} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"><Translate>Order ID</Translate></label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.response.order_id} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"><Translate>Payment URL</Translate></label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={url} /> + </p> + </div> + </div> + </div> + </CreatedSuccessfully>; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx new file mode 100644 index 000000000..c447c4b04 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx @@ -0,0 +1,82 @@ +/* + 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 { Fragment, h, VNode } from 'preact'; +import { useState } from 'preact/hooks'; +import { Loading } from '../../../../components/exception/loading'; +import { NotificationCard } from '../../../../components/menu'; +import { MerchantBackend } from '../../../../declaration'; +import { HttpError } from '../../../../hooks/backend'; +import { useInstanceDetails } from '../../../../hooks/instance'; +import { useOrderAPI } from '../../../../hooks/order'; +import { useInstanceProducts } from '../../../../hooks/product'; +import { Notification } from '../../../../utils/types'; +import { CreatePage } from './CreatePage'; +import { OrderCreatedSuccessfully } from './OrderCreatedSuccessfully'; + +export type Entity = { + request: MerchantBackend.Orders.PostOrderRequest, + response: MerchantBackend.Orders.PostOrderResponse +} +interface Props { + onBack?: () => void; + onConfirm: () => void; + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onLoadError: (error: HttpError) => VNode; +} +export default function OrderCreate({ onConfirm, onBack, onLoadError, onNotFound, onUnauthorized }: Props): VNode { + const { createOrder } = useOrderAPI() + const [notif, setNotif] = useState<Notification | undefined>(undefined) + + const detailsResult = useInstanceDetails() + const inventoryResult = useInstanceProducts() + + if (detailsResult.clientError && detailsResult.isUnauthorized) return onUnauthorized() + if (detailsResult.clientError && detailsResult.isNotfound) return onNotFound() + if (detailsResult.loading) return <Loading /> + if (!detailsResult.ok) return onLoadError(detailsResult) + + if (inventoryResult.clientError && inventoryResult.isUnauthorized) return onUnauthorized() + if (inventoryResult.clientError && inventoryResult.isNotfound) return onNotFound() + if (inventoryResult.loading) return <Loading /> + if (!inventoryResult.ok) return onLoadError(inventoryResult) + + return <Fragment> + + <NotificationCard notification={notif} /> + + <CreatePage + onBack={onBack} + onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => { + createOrder(request).then(onConfirm).catch((error) => { + setNotif({ + message: 'could not create order', + type: "ERROR", + description: error.message + }) + }) + }} + instanceConfig={detailsResult.data} + instanceInventory={inventoryResult.data} + /> + </Fragment> +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx new file mode 100644 index 000000000..812b11a76 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx @@ -0,0 +1,137 @@ +/* + 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 { addDays } from "date-fns"; +import { h, VNode, FunctionalComponent } from "preact"; +import { MerchantBackend } from "../../../../declaration"; +import { DetailPage as TestedComponent } from "./DetailPage"; + +export default { + title: "Pages/Order/Detail", + component: TestedComponent, + argTypes: { + onRefund: { action: "onRefund" }, + onBack: { action: "onBack" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +const defaultContractTerm = { + amount: "TESTKUDOS:10", + timestamp: { + t_s: new Date().getTime() / 1000, + }, + auditors: [], + exchanges: [], + max_fee: "TESTKUDOS:1", + max_wire_fee: "TESTKUDOS:1", + merchant: {} as any, + merchant_base_url: "http://merchant.url/", + order_id: "2021.165-03GDFC26Y1NNG", + products: [], + summary: "text summary", + wire_fee_amortization: 1, + wire_transfer_deadline: { + t_s: "never", + }, + refund_deadline: { t_s: "never" }, + merchant_pub: "ASDASDASDSd", + nonce: "QWEQWEQWE", + pay_deadline: { + t_s: "never", + }, + wire_method: "x-taler-bank", + h_wire: "asd", +} as MerchantBackend.ContractTerms; + +// contract_terms: defaultContracTerm, +export const Claimed = createExample(TestedComponent, { + id: "2021.165-03GDFC26Y1NNG", + selected: { + order_status: "claimed", + contract_terms: defaultContractTerm, + }, +}); + +export const PaidNotRefundable = createExample(TestedComponent, { + id: "2021.165-03GDFC26Y1NNG", + selected: { + order_status: "paid", + contract_terms: defaultContractTerm, + refunded: false, + deposit_total: "TESTKUDOS:10", + exchange_ec: 0, + order_status_url: "http://merchant.backend/status", + exchange_hc: 0, + refund_amount: "TESTKUDOS:0", + refund_details: [], + refund_pending: false, + wire_details: [], + wire_reports: [], + wired: false, + }, +}); + +export const PaidRefundable = createExample(TestedComponent, { + id: "2021.165-03GDFC26Y1NNG", + selected: { + order_status: "paid", + contract_terms: { + ...defaultContractTerm, + refund_deadline: { + t_s: addDays(new Date(), 2).getTime() / 1000, + }, + }, + refunded: false, + deposit_total: "TESTKUDOS:10", + exchange_ec: 0, + order_status_url: "http://merchant.backend/status", + exchange_hc: 0, + refund_amount: "TESTKUDOS:0", + refund_details: [], + refund_pending: false, + wire_details: [], + wire_reports: [], + wired: false, + }, +}); + +export const Unpaid = createExample(TestedComponent, { + id: "2021.165-03GDFC26Y1NNG", + selected: { + order_status: "unpaid", + order_status_url: "http://merchant.backend/status", + creation_time: { + t_s: new Date().getTime() / 1000, + }, + summary: "text summary", + taler_pay_uri: "pay uri", + total_amount: "TESTKUDOS:10", + }, +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx new file mode 100644 index 000000000..4bb7051d6 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx @@ -0,0 +1,776 @@ +/* + 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 { AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { format } from "date-fns"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { FormProvider } from "../../../../components/form/FormProvider"; +import { Input } from "../../../../components/form/Input"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { InputDate } from "../../../../components/form/InputDate"; +import { InputDuration } from "../../../../components/form/InputDuration"; +import { InputGroup } from "../../../../components/form/InputGroup"; +import { InputLocation } from "../../../../components/form/InputLocation"; +import { TextField } from "../../../../components/form/TextField"; +import { ProductList } from "../../../../components/product/ProductList"; +import { useBackendContext } from "../../../../context/backend"; +import { MerchantBackend } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; +import { mergeRefunds } from "../../../../utils/amount"; +import { RefundModal } from "../list/Table"; +import { Event, Timeline } from "./Timeline"; + +type Entity = MerchantBackend.Orders.MerchantOrderStatusResponse; +type CT = MerchantBackend.ContractTerms; + +interface Props { + onBack: () => void; + selected: Entity; + id: string; + onRefund: (id: string, value: MerchantBackend.Orders.RefundRequest) => void; +} + +type Paid = MerchantBackend.Orders.CheckPaymentPaidResponse & { + refund_taken: string; +}; +type Unpaid = MerchantBackend.Orders.CheckPaymentUnpaidResponse; +type Claimed = MerchantBackend.Orders.CheckPaymentClaimedResponse; + +function ContractTerms({ value }: { value: CT }) { + const i18n = useTranslator(); + + return ( + <InputGroup name="contract_terms" label={i18n`Contract Terms`}> + <FormProvider<CT> object={value} valueHandler={null}> + <Input<CT> + readonly + name="summary" + label={i18n`Summary`} + tooltip={i18n`human-readable description of the whole purchase`} + /> + <InputCurrency<CT> + readonly + name="amount" + label={i18n`Amount`} + tooltip={i18n`total price for the transaction`} + /> + {value.fulfillment_url && ( + <Input<CT> + readonly + name="fulfillment_url" + label={i18n`Fulfillment URL`} + tooltip={i18n`URL for this purchase`} + /> + )} + <Input<CT> + readonly + name="max_fee" + label={i18n`Max fee`} + tooltip={i18n`maximum total deposit fee accepted by the merchant for this contract`} + /> + <Input<CT> + readonly + name="max_wire_fee" + label={i18n`Max wire fee`} + tooltip={i18n`maximum wire fee accepted by the merchant`} + /> + <Input<CT> + readonly + name="wire_fee_amortization" + label={i18n`Wire fee amortization`} + tooltip={i18n`over how many customer transactions does the merchant expect to amortize wire fees on average`} + /> + <InputDate<CT> + readonly + name="timestamp" + label={i18n`Created at`} + tooltip={i18n`time when this contract was generated`} + /> + <InputDate<CT> + readonly + name="refund_deadline" + label={i18n`Refund deadline`} + tooltip={i18n`after this deadline has passed no refunds will be accepted`} + /> + <InputDate<CT> + readonly + name="pay_deadline" + label={i18n`Payment deadline`} + tooltip={i18n`after this deadline, the merchant won't accept payments for the contract`} + /> + <InputDate<CT> + readonly + name="wire_transfer_deadline" + label={i18n`Wire transfer deadline`} + tooltip={i18n`transfer deadline for the exchange`} + /> + <InputDate<CT> + readonly + name="delivery_date" + label={i18n`Delivery date`} + tooltip={i18n`time indicating when the order should be delivered`} + /> + {value.delivery_date && ( + <InputGroup + name="delivery_location" + label={i18n`Location`} + tooltip={i18n`where the order will be delivered`} + > + <InputLocation name="payments.delivery_location" /> + </InputGroup> + )} + <InputDuration<CT> + readonly + name="auto_refund" + label={i18n`Auto-refund delay`} + tooltip={i18n`how long the wallet should try to get an automatic refund for the purchase`} + /> + <Input<CT> + readonly + name="extra" + label={i18n`Extra info`} + tooltip={i18n`extra data that is only interpreted by the merchant frontend`} + /> + </FormProvider> + </InputGroup> + ); +} + +function ClaimedPage({ + id, + order, +}: { + id: string; + order: MerchantBackend.Orders.CheckPaymentClaimedResponse; +}) { + const events: Event[] = []; + if (order.contract_terms.timestamp.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.timestamp.t_s * 1000), + description: "order created", + type: "start", + }); + } + if (order.contract_terms.pay_deadline.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.pay_deadline.t_s * 1000), + description: "pay deadline", + type: "deadline", + }); + } + if (order.contract_terms.refund_deadline.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.refund_deadline.t_s * 1000), + description: "refund deadline", + type: "deadline", + }); + } + if (order.contract_terms.wire_transfer_deadline.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000), + description: "wire deadline", + type: "deadline", + }); + } + if ( + order.contract_terms.delivery_date && + order.contract_terms.delivery_date.t_s !== "never" + ) { + events.push({ + when: new Date(order.contract_terms.delivery_date?.t_s * 1000), + description: "delivery", + type: "delivery", + }); + } + + const [value, valueHandler] = useState<Partial<Claimed>>(order); + const i18n = useTranslator(); + + return ( + <div> + <section class="section"> + <div class="columns"> + <div class="column" /> + <div class="column is-10"> + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <Translate>Order</Translate> #{id} + <div class="tag is-info ml-4"> + <Translate>claimed</Translate> + </div> + </div> + </div> + </div> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <h1 class="title">{order.contract_terms.amount}</h1> + </div> + </div> + </div> + + <div class="level"> + <div class="level-left" style={{ maxWidth: "100%" }}> + <div class="level-item" style={{ maxWidth: "100%" }}> + <div + class="content" + style={{ + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + }} + > + <p> + <b> + <Translate>claimed at</Translate>: + </b>{" "} + {format( + new Date(order.contract_terms.timestamp.t_s * 1000), + "yyyy-MM-dd HH:mm:ss" + )} + </p> + </div> + </div> + </div> + </div> + </div> + </section> + + <section class="section"> + <div class="columns"> + <div class="column is-4"> + <div class="title"> + <Translate>Timeline</Translate> + </div> + <Timeline events={events} /> + </div> + <div class="column is-8"> + <div class="title"> + <Translate>Payment details</Translate> + </div> + <FormProvider<Claimed> + object={value} + valueHandler={valueHandler} + > + <Input + name="contract_terms.summary" + readonly + inputType="multiline" + label={i18n`Summary`} + /> + <InputCurrency + name="contract_terms.amount" + readonly + label={i18n`Amount`} + /> + <Input<Claimed> + name="order_status" + readonly + label={i18n`Order status`} + /> + </FormProvider> + </div> + </div> + </section> + + {order.contract_terms.products.length ? ( + <Fragment> + <div class="title"> + <Translate>Product list</Translate> + </div> + <ProductList list={order.contract_terms.products} /> + </Fragment> + ) : undefined} + + {value.contract_terms && ( + <ContractTerms value={value.contract_terms} /> + )} + </div> + <div class="column" /> + </div> + </section> + </div> + ); +} +function PaidPage({ + id, + order, + onRefund, +}: { + id: string; + order: MerchantBackend.Orders.CheckPaymentPaidResponse; + onRefund: (id: string) => void; +}) { + const events: Event[] = []; + if (order.contract_terms.timestamp.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.timestamp.t_s * 1000), + description: "order created", + type: "start", + }); + } + if (order.contract_terms.pay_deadline.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.pay_deadline.t_s * 1000), + description: "pay deadline", + type: "deadline", + }); + } + if (order.contract_terms.refund_deadline.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.refund_deadline.t_s * 1000), + description: "refund deadline", + type: "deadline", + }); + } + if (order.contract_terms.wire_transfer_deadline.t_s !== "never") { + events.push({ + when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000), + description: "wire deadline", + type: "deadline", + }); + } + if ( + order.contract_terms.delivery_date && + order.contract_terms.delivery_date.t_s !== "never" + ) { + if (order.contract_terms.delivery_date) + events.push({ + when: new Date(order.contract_terms.delivery_date?.t_s * 1000), + description: "delivery", + type: "delivery", + }); + } + order.refund_details.reduce(mergeRefunds, []).forEach((e) => { + if (e.timestamp.t_s !== "never") { + events.push({ + when: new Date(e.timestamp.t_s * 1000), + description: `refund: ${e.amount}: ${e.reason}`, + type: e.pending ? "refund" : "refund-taken", + }); + } + }); + if (order.wire_details && order.wire_details.length) { + if (order.wire_details.length > 1) { + let last: MerchantBackend.Orders.TransactionWireTransfer | null = null; + let first: MerchantBackend.Orders.TransactionWireTransfer | null = null; + let total: AmountJson | null = null; + + order.wire_details.forEach((w) => { + if (last === null || last.execution_time.t_s < w.execution_time.t_s) { + last = w; + } + if (first === null || first.execution_time.t_s > w.execution_time.t_s) { + first = w; + } + total = + total === null + ? Amounts.parseOrThrow(w.amount) + : Amounts.add(total, Amounts.parseOrThrow(w.amount)).amount; + }); + const last_time = last!.execution_time.t_s; + if (last_time !== "never") { + events.push({ + when: new Date(last_time * 1000), + description: `wired ${Amounts.stringify(total!)}`, + type: "wired-range", + }); + } + const first_time = first!.execution_time.t_s; + if (first_time !== "never") { + events.push({ + when: new Date(first_time * 1000), + description: `wire transfer started...`, + type: "wired-range", + }); + } + } else { + order.wire_details.forEach((e) => { + if (e.execution_time.t_s !== "never") { + events.push({ + when: new Date(e.execution_time.t_s * 1000), + description: `wired ${e.amount}`, + type: "wired", + }); + } + }); + } + } + + const [value, valueHandler] = useState<Partial<Paid>>(order); + const { url } = useBackendContext(); + const refundHost = url.replace(/.*:\/\//, ""); // remove protocol part + const proto = url.startsWith("http://") ? "taler+http" : "taler"; + const refundurl = `${proto}://refund/${refundHost}/${order.contract_terms.order_id}/`; + const refundable = + new Date().getTime() < order.contract_terms.refund_deadline.t_s * 1000; + const i18n = useTranslator(); + + const amount = Amounts.parseOrThrow(order.contract_terms.amount); + const refund_taken = order.refund_details.reduce((prev, cur) => { + if (cur.pending) return prev; + return Amounts.add(prev, Amounts.parseOrThrow(cur.amount)).amount; + }, Amounts.getZero(amount.currency)); + value.refund_taken = Amounts.stringify(refund_taken); + + return ( + <div> + <section class="section"> + <div class="columns"> + <div class="column" /> + <div class="column is-10"> + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <Translate>Order</Translate> #{id} + <div class="tag is-success ml-4"> + <Translate>paid</Translate> + </div> + {order.wired ? ( + <div class="tag is-success ml-4"> + <Translate>wired</Translate> + </div> + ) : null} + {order.refunded ? ( + <div class="tag is-danger ml-4"> + <Translate>refunded</Translate> + </div> + ) : null} + </div> + </div> + </div> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <h1 class="title">{order.contract_terms.amount}</h1> + </div> + </div> + <div class="level-right"> + <div class="level-item"> + <h1 class="title"> + <div class="buttons"> + <span + class="has-tooltip-left" + data-tooltip={ + refundable + ? i18n`refund order` + : i18n`not refundable` + } + > + <button + class="button is-danger" + disabled={!refundable} + onClick={() => onRefund(id)} + > + <Translate>refund</Translate> + </button> + </span> + </div> + </h1> + </div> + </div> + </div> + + <div class="level"> + <div class="level-left" style={{ maxWidth: "100%" }}> + <div class="level-item" style={{ maxWidth: "100%" }}> + <div + class="content" + style={{ + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + // maxWidth: '100%', + }} + > + <p> + <a + href={order.contract_terms.fulfillment_url} + rel="nofollow" + target="new" + > + {order.contract_terms.fulfillment_url} + </a> + </p> + <p> + {format( + new Date(order.contract_terms.timestamp.t_s * 1000), + "yyyy/MM/dd HH:mm:ss" + )} + </p> + </div> + </div> + </div> + </div> + </div> + </section> + + <section class="section"> + <div class="columns"> + <div class="column is-4"> + <div class="title"> + <Translate>Timeline</Translate> + </div> + <Timeline events={events} /> + </div> + <div class="column is-8"> + <div class="title"> + <Translate>Payment details</Translate> + </div> + <FormProvider<Paid> + object={value} + valueHandler={valueHandler} + > + {/* <InputCurrency<Paid> name="deposit_total" readonly label={i18n`Deposit total`} /> */} + {order.refunded && ( + <InputCurrency<Paid> + name="refund_amount" + readonly + label={i18n`Refunded amount`} + /> + )} + {order.refunded && ( + <InputCurrency<Paid> + name="refund_taken" + readonly + label={i18n`Refund taken`} + /> + )} + <Input<Paid> + name="order_status" + readonly + label={i18n`Order status`} + /> + <TextField<Paid> + name="order_status_url" + label={i18n`Status URL`} + > + <a + target="_blank" + rel="noreferrer" + href={order.order_status_url} + > + {order.order_status_url} + </a> + </TextField> + {order.refunded && ( + <TextField<Paid> + name="order_status_url" + label={i18n`Refund URI`} + > + <a target="_blank" rel="noreferrer" href={refundurl}> + {refundurl} + </a> + </TextField> + )} + </FormProvider> + </div> + </div> + </section> + + {order.contract_terms.products.length ? ( + <Fragment> + <div class="title"> + <Translate>Product list</Translate> + </div> + <ProductList list={order.contract_terms.products} /> + </Fragment> + ) : undefined} + + {value.contract_terms && ( + <ContractTerms value={value.contract_terms} /> + )} + </div> + <div class="column" /> + </div> + </section> + </div> + ); +} + +function UnpaidPage({ + id, + order, +}: { + id: string; + order: MerchantBackend.Orders.CheckPaymentUnpaidResponse; +}) { + const [value, valueHandler] = useState<Partial<Unpaid>>(order); + const i18n = useTranslator(); + return ( + <div> + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <h1 class="title"> + <Translate>Order</Translate> #{id} + </h1> + </div> + <div class="tag is-dark"> + <Translate>unpaid</Translate> + </div> + </div> + </div> + + <div class="level"> + <div class="level-left" style={{ maxWidth: "100%" }}> + <div class="level-item" style={{ maxWidth: "100%" }}> + <div + class="content" + style={{ + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + }} + > + <p> + <b> + <Translate>pay at</Translate>: + </b>{" "} + <a + href={order.order_status_url} + rel="nofollow" + target="new" + > + {order.order_status_url} + </a> + </p> + <p> + <b> + <Translate>created at</Translate>: + </b>{" "} + {order.creation_time.t_s === "never" + ? "never" + : format( + new Date(order.creation_time.t_s * 1000), + "yyyy-MM-dd HH:mm:ss" + )} + </p> + </div> + </div> + </div> + </div> + </div> + </section> + + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <FormProvider<Unpaid> object={value} valueHandler={valueHandler}> + <Input<Unpaid> + readonly + name="summary" + label={i18n`Summary`} + tooltip={i18n`human-readable description of the whole purchase`} + /> + <InputCurrency<Unpaid> + readonly + name="total_amount" + label={i18n`Amount`} + tooltip={i18n`total price for the transaction`} + /> + <Input<Unpaid> + name="order_status" + readonly + label={i18n`Order status`} + /> + <Input<Unpaid> + name="order_status_url" + readonly + label={i18n`Order status URL`} + /> + <TextField<Unpaid> name="taler_pay_uri" label={i18n`Payment URI`}> + <a target="_blank" rel="noreferrer" href={value.taler_pay_uri}> + {value.taler_pay_uri} + </a> + </TextField> + </FormProvider> + </div> + <div class="column" /> + </div> + </section> + </div> + ); +} + +export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode { + const [showRefund, setShowRefund] = useState<string | undefined>(undefined); + + const DetailByStatus = function () { + switch (selected.order_status) { + case "claimed": + return <ClaimedPage id={id} order={selected} />; + case "paid": + return <PaidPage id={id} order={selected} onRefund={setShowRefund} />; + case "unpaid": + return <UnpaidPage id={id} order={selected} />; + default: + return ( + <div> + <Translate> + Unknown order status. This is an error, please contact the + administrator. + </Translate> + </div> + ); + } + }; + + return ( + <Fragment> + {DetailByStatus()} + {showRefund && ( + <RefundModal + order={selected} + onCancel={() => setShowRefund(undefined)} + onConfirm={(value) => { + onRefund(showRefund, value); + setShowRefund(undefined); + }} + /> + )} + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <div class="buttons is-right mt-5"> + <button class="button" onClick={onBack}> + <Translate>Back</Translate> + </button> + </div> + </div> + <div class="column" /> + </div> + </Fragment> + ); +} + +async function copyToClipboard(text: string) { + return navigator.clipboard.writeText(text); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx new file mode 100644 index 000000000..bea65607a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx @@ -0,0 +1,128 @@ +/* + 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/> + */ +import { format } from "date-fns"; +import { h } from "preact"; +import { useEffect, useState } from "preact/hooks"; + +interface Props { + events: Event[]; +} + +export function Timeline({ events: e }: Props) { + const events = [...e]; + events.push({ + when: new Date(), + description: "now", + type: "now", + }); + + events.sort((a, b) => a.when.getTime() - b.when.getTime()); + + const [state, setState] = useState(events); + useEffect(() => { + const handle = setTimeout(() => { + const eventsWithoutNow = state.filter((e) => e.type !== "now"); + eventsWithoutNow.push({ + when: new Date(), + description: "now", + type: "now", + }); + setState(eventsWithoutNow); + }, 1000); + return () => { + clearTimeout(handle); + }; + }); + return ( + <div class="timeline"> + {events.map((e, i) => { + return ( + <div key={i} class="timeline-item"> + {(() => { + switch (e.type) { + case "deadline": + return ( + <div class="timeline-marker is-icon "> + <i class="mdi mdi-flag" /> + </div> + ); + case "delivery": + return ( + <div class="timeline-marker is-icon "> + <i class="mdi mdi-delivery" /> + </div> + ); + case "start": + return ( + <div class="timeline-marker is-icon is-success"> + <i class="mdi mdi-flag " /> + </div> + ); + case "wired": + return ( + <div class="timeline-marker is-icon is-success"> + <i class="mdi mdi-cash" /> + </div> + ); + case "wired-range": + return ( + <div class="timeline-marker is-icon is-success"> + <i class="mdi mdi-cash" /> + </div> + ); + case "refund": + return ( + <div class="timeline-marker is-icon is-danger"> + <i class="mdi mdi-cash" /> + </div> + ); + case "refund-taken": + return ( + <div class="timeline-marker is-icon is-success"> + <i class="mdi mdi-cash" /> + </div> + ); + case "now": + return ( + <div class="timeline-marker is-icon is-info"> + <i class="mdi mdi-clock" /> + </div> + ); + } + })()} + <div class="timeline-content"> + <p class="heading">{format(e.when, "yyyy/MM/dd HH:mm:ss")}</p> + <p>{e.description}</p> + </div> + </div> + ); + })} + </div> + ); +} +export interface Event { + when: Date; + description: string; + type: + | "start" + | "refund" + | "refund-taken" + | "wired" + | "wired-range" + | "deadline" + | "delivery" + | "now"; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx new file mode 100644 index 000000000..cd4e163eb --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx @@ -0,0 +1,67 @@ +/* + 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/> + */ +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Loading } from "../../../../components/exception/loading"; +import { NotificationCard } from "../../../../components/menu"; +import { HttpError } from "../../../../hooks/backend"; +import { useOrderDetails, useOrderAPI } from "../../../../hooks/order"; +import { useTranslator } from "../../../../i18n"; +import { Notification } from "../../../../utils/types"; +import { DetailPage } from "./DetailPage"; + +export interface Props { + oid: string; + + onBack: () => void; + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onLoadError: (error: HttpError) => VNode; +} + +export default function Update({ oid, onBack, onLoadError, onNotFound, onUnauthorized }: Props): VNode { + const { refundOrder } = useOrderAPI(); + const result = useOrderDetails(oid) + const [notif, setNotif] = useState<Notification | undefined>(undefined) + + const i18n = useTranslator() + + if (result.clientError && result.isUnauthorized) return onUnauthorized() + if (result.clientError && result.isNotfound) return onNotFound() + if (result.loading) return <Loading /> + if (!result.ok) return onLoadError(result) + + return <Fragment> + + <NotificationCard notification={notif} /> + + <DetailPage + onBack={onBack} + id={oid} + onRefund={(id, value) => refundOrder(id, value) + .then(() => setNotif({ + message: i18n`refund created successfully`, + type: "SUCCESS" + })).catch((error) => setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message + })) + } + selected={result.data} + /> + </Fragment> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx new file mode 100644 index 000000000..1dbb3f20d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx @@ -0,0 +1,107 @@ +/* + 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, FunctionalComponent } from "preact"; +import { ListPage as TestedComponent } from "./ListPage"; + +export default { + title: "Pages/Order/List", + component: TestedComponent, + argTypes: { + onShowAll: { action: "onShowAll" }, + onShowPaid: { action: "onShowPaid" }, + onShowRefunded: { action: "onShowRefunded" }, + onShowNotWired: { action: "onShowNotWired" }, + onCopyURL: { action: "onCopyURL" }, + onSelectDate: { action: "onSelectDate" }, + onLoadMoreBefore: { action: "onLoadMoreBefore" }, + onLoadMoreAfter: { action: "onLoadMoreAfter" }, + onSelectOrder: { action: "onSelectOrder" }, + onRefundOrder: { action: "onRefundOrder" }, + onSearchOrderById: { action: "onSearchOrderById" }, + onCreate: { action: "onCreate" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const Example = createExample(TestedComponent, { + orders: [ + { + id: "123", + amount: "TESTKUDOS:10", + paid: false, + refundable: true, + row_id: 1, + summary: "summary", + timestamp: { + t_s: new Date().getTime() / 1000, + }, + order_id: "123", + }, + { + id: "234", + amount: "TESTKUDOS:12", + paid: true, + refundable: true, + row_id: 2, + summary: + "summary with long text, very very long text that someone want to add as a description of the order", + timestamp: { + t_s: new Date().getTime() / 1000, + }, + order_id: "234", + }, + { + id: "456", + amount: "TESTKUDOS:1", + paid: false, + refundable: false, + row_id: 3, + summary: + "summary with long text, very very long text that someone want to add as a description of the order", + timestamp: { + t_s: new Date().getTime() / 1000, + }, + order_id: "456", + }, + { + id: "234", + amount: "TESTKUDOS:12", + paid: false, + refundable: false, + row_id: 4, + summary: + "summary with long text, very very long text that someone want to add as a description of the order", + timestamp: { + t_s: new Date().getTime() / 1000, + }, + order_id: "234", + }, + ], +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx new file mode 100644 index 000000000..032801bde --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx @@ -0,0 +1,146 @@ +/* + 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 { format } from 'date-fns'; +import { h, VNode } from 'preact'; +import { useState } from 'preact/hooks'; +import { DatePicker } from '../../../../components/picker/DatePicker'; +import { MerchantBackend, WithId } from '../../../../declaration'; +import { Translate, useTranslator } from '../../../../i18n'; +import { CardTable } from './Table'; + +export interface ListPageProps { + errorOrderId: string | undefined, + + onShowAll: () => void, + onShowPaid: () => void, + onShowRefunded: () => void, + onShowNotWired: () => void, + onCopyURL: (id: string) => void; + isAllActive: string, + isPaidActive: string, + isRefundedActive: string, + isNotWiredActive: string, + + jumpToDate?: Date, + onSelectDate: (date?: Date) => void, + + orders: (MerchantBackend.Orders.OrderHistoryEntry & WithId)[]; + onLoadMoreBefore?: () => void; + hasMoreBefore?: boolean; + hasMoreAfter?: boolean; + onLoadMoreAfter?: () => void; + + onSelectOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void; + onRefundOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void; + onSearchOrderById: (id: string) => void; + onCreate: () => void; +} + +export function ListPage({ orders, errorOrderId, isAllActive, onSelectOrder, onRefundOrder, onSearchOrderById, jumpToDate, onCopyURL, onShowAll, onShowPaid, onShowRefunded, onShowNotWired, onSelectDate, isPaidActive, isRefundedActive, isNotWiredActive, onCreate }: ListPageProps): VNode { + const i18n = useTranslator(); + const dateTooltip = i18n`select date to show nearby orders`; + const [pickDate, setPickDate] = useState(false); + const [orderId, setOrderId] = useState<string>(''); + + return <section class="section is-main-section"> + + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <div class="field has-addons"> + <div class="control"> + <input class={errorOrderId ? "input is-danger" : "input"} type="text" value={orderId} onChange={e => setOrderId(e.currentTarget.value)} placeholder={i18n`order id`} /> + {errorOrderId && <p class="help is-danger">{errorOrderId}</p>} + </div> + <span class="has-tooltip-bottom" data-tooltip={i18n`jump to order with the given order ID`}> + <button class="button" onClick={(e) => onSearchOrderById(orderId)}> + <span class="icon"><i class="mdi mdi-arrow-right" /></span> + </button> + </span> + </div> + </div> + </div> + </div> + <div class="columns"> + <div class="column is-two-thirds"> + <div class="tabs" style={{overflow:'inherit'}}> + <ul> + <li class={isAllActive}> + <div class="has-tooltip-right" data-tooltip={i18n`remove all filters`}> + <a onClick={onShowAll}><Translate>All</Translate></a> + </div> + </li> + <li class={isPaidActive}> + <div class="has-tooltip-right" data-tooltip={i18n`only show paid orders`}> + <a onClick={onShowPaid}><Translate>Paid</Translate></a> + </div> + </li> + <li class={isRefundedActive}> + <div class="has-tooltip-right" data-tooltip={i18n`only show orders with refunds`}> + <a onClick={onShowRefunded}><Translate>Refunded</Translate></a> + </div> + </li> + <li class={isNotWiredActive}> + <div class="has-tooltip-left" data-tooltip={i18n`only show orders where customers paid, but wire payments from payment provider are still pending`}> + <a onClick={onShowNotWired}><Translate>Not wired</Translate></a> + </div> + </li> + </ul> + </div> + </div> + <div class="column "> + <div class="buttons is-right"> + <div class="field has-addons"> + {jumpToDate && <div class="control"> + <a class="button" onClick={() => onSelectDate(undefined)}> + <span class="icon" data-tooltip={i18n`clear date filter`}><i class="mdi mdi-close" /></span> + </a> + </div>} + <div class="control"> + <span class="has-tooltip-top" data-tooltip={dateTooltip}> + <input class="input" type="text" readonly value={!jumpToDate ? '' : format(jumpToDate, 'yyyy/MM/dd')} placeholder={i18n`date (YYYY/MM/DD)`} onClick={() => { setPickDate(true); }} /> + </span> + </div> + <div class="control"> + <span class="has-tooltip-left" data-tooltip={dateTooltip}> + <a class="button" onClick={() => { setPickDate(true); }}> + <span class="icon"><i class="mdi mdi-calendar" /></span> + </a> + </span> + </div> + </div> + </div> + </div> + </div> + + <DatePicker + opened={pickDate} + closeFunction={() => setPickDate(false)} + dateReceiver={onSelectDate} /> + + <CardTable orders={orders} + onCreate={onCreate} + onCopyURL={onCopyURL} + onSelect={onSelectOrder} + onRefund={onRefundOrder} /> + </section>; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx new file mode 100644 index 000000000..60d5fae59 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx @@ -0,0 +1,412 @@ +/* + 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 { Amounts } from "@gnu-taler/taler-util"; +import { format } from "date-fns"; +import { h, VNode } from "preact"; +import { StateUpdater, useState } from "preact/hooks"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider"; +import { Input } from "../../../../components/form/Input"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { InputGroup } from "../../../../components/form/InputGroup"; +import { InputSelector } from "../../../../components/form/InputSelector"; +import { ConfirmModal } from "../../../../components/modal"; +import { useConfigContext } from "../../../../context/config"; +import { MerchantBackend, WithId } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; +import { mergeRefunds } from "../../../../utils/amount"; + +type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId; +interface Props { + orders: Entity[]; + onRefund: (value: Entity) => void; + onCopyURL: (id: string) => void; + onCreate: () => void; + onSelect: (order: Entity) => void; + onLoadMoreBefore?: () => void; + hasMoreBefore?: boolean; + hasMoreAfter?: boolean; + onLoadMoreAfter?: () => void; +} + +export function CardTable({ + orders, + onCreate, + onRefund, + onCopyURL, + onSelect, + onLoadMoreAfter, + onLoadMoreBefore, + hasMoreAfter, + hasMoreBefore, +}: Props): VNode { + const [rowSelection, rowSelectionHandler] = useState<string[]>([]); + + 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-cash-register" /> + </span> + <Translate>Orders</Translate> + </p> + + <div class="card-header-icon" aria-label="more options" /> + + <div class="card-header-icon" aria-label="more options"> + <span class="has-tooltip-left" data-tooltip={i18n`create order`}> + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small"> + <i class="mdi mdi-plus mdi-36px" /> + </span> + </button> + </span> + </div> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {orders.length > 0 ? ( + <Table + instances={orders} + onSelect={onSelect} + onRefund={onRefund} + onCopyURL={(o) => onCopyURL(o.id)} + rowSelection={rowSelection} + rowSelectionHandler={rowSelectionHandler} + onLoadMoreAfter={onLoadMoreAfter} + onLoadMoreBefore={onLoadMoreBefore} + hasMoreAfter={hasMoreAfter} + hasMoreBefore={hasMoreBefore} + /> + ) : ( + <EmptyTable /> + )} + </div> + </div> + </div> + </div> + ); +} +interface TableProps { + rowSelection: string[]; + instances: Entity[]; + onRefund: (id: Entity) => void; + onCopyURL: (id: Entity) => void; + onSelect: (id: Entity) => void; + rowSelectionHandler: StateUpdater<string[]>; + onLoadMoreBefore?: () => void; + hasMoreBefore?: boolean; + hasMoreAfter?: boolean; + onLoadMoreAfter?: () => void; +} + +function Table({ + instances, + onSelect, + onRefund, + onCopyURL, + onLoadMoreAfter, + onLoadMoreBefore, + hasMoreAfter, + hasMoreBefore, +}: TableProps): VNode { + return ( + <div class="table-container"> + {onLoadMoreBefore && ( + <button + class="button is-fullwidth" + disabled={!hasMoreBefore} + onClick={onLoadMoreBefore} + > + <Translate>load newer orders</Translate> + </button> + )} + <table class="table is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th style={{ minWidth: 100 }}> + <Translate>Date</Translate> + </th> + <th style={{ minWidth: 100 }}> + <Translate>Amount</Translate> + </th> + <th style={{ minWidth: 400 }}> + <Translate>Summary</Translate> + </th> + <th style={{ minWidth: 50 }} /> + </tr> + </thead> + <tbody> + {instances.map((i) => { + return ( + <tr key={i.id}> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.timestamp.t_s === "never" + ? "never" + : format( + new Date(i.timestamp.t_s * 1000), + "yyyy/MM/dd HH:mm:ss" + )} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.amount} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.summary} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + {i.refundable && ( + <button + class="button is-small is-danger jb-modal" + type="button" + onClick={(): void => onRefund(i)} + > + <Translate>Refund</Translate> + </button> + )} + {!i.paid && ( + <button + class="button is-small is-info jb-modal" + type="button" + onClick={(): void => onCopyURL(i)} + > + <Translate>copy url</Translate> + </button> + )} + </div> + </td> + </tr> + ); + })} + </tbody> + </table> + {onLoadMoreAfter && ( + <button + class="button is-fullwidth" + disabled={!hasMoreAfter} + onClick={onLoadMoreAfter} + > + <Translate>load older orders</Translate> + </button> + )} + </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 orders have been found matching your query!</Translate> + </p> + </div> + ); +} + +interface RefundModalProps { + onCancel: () => void; + onConfirm: (value: MerchantBackend.Orders.RefundRequest) => void; + order: MerchantBackend.Orders.MerchantOrderStatusResponse; +} + +export function RefundModal({ + order, + onCancel, + onConfirm, +}: RefundModalProps): VNode { + type State = { mainReason?: string; description?: string; refund?: string }; + const [form, setValue] = useState<State>({}); + const i18n = useTranslator(); + // const [errors, setErrors] = useState<FormErrors<State>>({}); + + const refunds = ( + order.order_status === "paid" ? order.refund_details : [] + ).reduce(mergeRefunds, []); + + const config = useConfigContext(); + const totalRefunded = refunds + .map((r) => r.amount) + .reduce( + (p, c) => Amounts.add(p, Amounts.parseOrThrow(c)).amount, + Amounts.getZero(config.currency) + ); + const orderPrice = + order.order_status === "paid" + ? Amounts.parseOrThrow(order.contract_terms.amount) + : undefined; + const totalRefundable = !orderPrice + ? Amounts.getZero(totalRefunded.currency) + : refunds.length + ? Amounts.sub(orderPrice, totalRefunded).amount + : orderPrice; + + const isRefundable = Amounts.isNonZero(totalRefundable); + const duplicatedText = i18n`duplicated`; + + const errors: FormErrors<State> = { + mainReason: !form.mainReason ? i18n`required` : undefined, + description: + !form.description && form.mainReason !== duplicatedText + ? i18n`required` + : undefined, + refund: !form.refund + ? i18n`required` + : !Amounts.parse(form.refund) + ? i18n`invalid format` + : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1 + ? i18n`this value exceed the refundable amount` + : undefined, + }; + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined + ); + + const validateAndConfirm = () => { + try { + if (!form.refund) return; + onConfirm({ + refund: Amounts.stringify( + Amounts.add(Amounts.parse(form.refund)!, totalRefunded).amount + ), + reason: + form.description === undefined + ? form.mainReason || "" + : `${form.mainReason}: ${form.description}`, + }); + } catch (err) { + console.log(err); + } + }; + + //FIXME: parameters in the translation + return ( + <ConfirmModal + description="refund" + danger + active + disabled={!isRefundable || hasErrors} + onCancel={onCancel} + onConfirm={validateAndConfirm} + > + {refunds.length > 0 && ( + <div class="columns"> + <div class="column is-12"> + <InputGroup + name="asd" + label={`${Amounts.stringify(totalRefunded)} was already refunded`} + > + <table class="table is-fullwidth"> + <thead> + <tr> + <th> + <Translate>date</Translate> + </th> + <th> + <Translate>amount</Translate> + </th> + <th> + <Translate>reason</Translate> + </th> + </tr> + </thead> + <tbody> + {refunds.map((r) => { + return ( + <tr key={r.timestamp.t_s}> + <td> + {r.timestamp.t_s === "never" + ? "never" + : format( + new Date(r.timestamp.t_s * 1000), + "yyyy-MM-dd HH:mm:ss" + )} + </td> + <td>{r.amount}</td> + <td>{r.reason}</td> + </tr> + ); + })} + </tbody> + </table> + </InputGroup> + </div> + </div> + )} + + {isRefundable && ( + <FormProvider<State> + errors={errors} + object={form} + valueHandler={(d) => setValue(d as any)} + > + <InputCurrency<State> + name="refund" + label={i18n`Refund`} + tooltip={i18n`amount to be refunded`} + > + <Translate>Max refundable:</Translate>{" "} + {Amounts.stringify(totalRefundable)} + </InputCurrency> + <InputSelector + name="mainReason" + label={i18n`Reason`} + values={[ + i18n`Choose one...`, + duplicatedText, + i18n`requested by the customer`, + i18n`other`, + ]} + tooltip={i18n`why this order is being refunded`} + /> + {form.mainReason && form.mainReason !== duplicatedText ? ( + <Input<State> + label={i18n`Description`} + name="description" + tooltip={i18n`more information to give context`} + /> + ) : undefined} + </FormProvider> + )} + </ConfirmModal> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx new file mode 100644 index 000000000..47e143fb7 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx @@ -0,0 +1,171 @@ +/* + 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, Fragment } from 'preact'; +import { useState } from 'preact/hooks'; +import { Loading } from '../../../../components/exception/loading'; +import { NotificationCard } from '../../../../components/menu'; +import { MerchantBackend, WithId } from '../../../../declaration'; +import { HttpError } from '../../../../hooks/backend'; +import { InstanceOrderFilter, useInstanceOrders, useOrderAPI, useOrderDetails } from '../../../../hooks/order'; +import { useTranslator } from '../../../../i18n'; +import { Notification } from '../../../../utils/types'; +import { RefundModal } from './Table'; +import { ListPage } from './ListPage'; + +interface Props { + onUnauthorized: () => VNode; + onLoadError: (error: HttpError) => VNode; + onNotFound: () => VNode; + onSelect: (id: string) => void; + onCreate: () => void; +} + +export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNotFound }: Props): VNode { + const [filter, setFilter] = useState<InstanceOrderFilter>({}) + const [orderToBeRefunded, setOrderToBeRefunded] = useState<MerchantBackend.Orders.OrderHistoryEntry | undefined>(undefined) + + const setNewDate = (date?: Date) => setFilter(prev => ({ ...prev, date })) + + const result = useInstanceOrders(filter, setNewDate) + const { refundOrder, getPaymentURL } = useOrderAPI() + + const [notif, setNotif] = useState<Notification | undefined>(undefined) + + if (result.clientError && result.isUnauthorized) return onUnauthorized() + if (result.clientError && result.isNotfound) return onNotFound() + if (result.loading) return <Loading /> + if (!result.ok) return onLoadError(result) + + const isPaidActive = filter.paid === 'yes' ? "is-active" : '' + const isRefundedActive = filter.refunded === 'yes' ? "is-active" : '' + const isNotWiredActive = filter.wired === 'no' ? "is-active" : '' + const isAllActive = filter.paid === undefined && filter.refunded === undefined && filter.wired === undefined ? 'is-active' : '' + + const i18n = useTranslator() + const [errorOrderId, setErrorOrderId] = useState<string | undefined>(undefined) + + async function testIfOrderExistAndSelect(orderId: string) { + if (!orderId) { + setErrorOrderId(i18n`Enter an order id`) + return; + } + try { + await getPaymentURL(orderId) + onSelect(orderId) + setErrorOrderId(undefined) + } catch { + setErrorOrderId(i18n`order not found`) + } + } + + return <Fragment> + <NotificationCard notification={notif} /> + + <ListPage + orders={result.data.orders.map(o => ({ ...o, id: o.order_id }))} + onLoadMoreBefore={result.loadMorePrev} hasMoreBefore={!result.isReachingStart} + onLoadMoreAfter={result.loadMore} hasMoreAfter={!result.isReachingEnd} + + onSelectOrder={(order) => onSelect(order.id)} + onRefundOrder={(value) => setOrderToBeRefunded(value)} + + errorOrderId={errorOrderId} + isAllActive={isAllActive} + isNotWiredActive={isNotWiredActive} + isPaidActive={isPaidActive} + isRefundedActive={isRefundedActive} + jumpToDate={filter.date} + onCopyURL={(id) => getPaymentURL(id).then((resp) => copyToClipboard(resp.data))} + + onCreate={onCreate} + onSearchOrderById={testIfOrderExistAndSelect} + onSelectDate={setNewDate} + onShowAll={() => setFilter({})} + onShowPaid={() => setFilter({ paid: 'yes' })} + onShowRefunded={() => setFilter({ refunded: 'yes' })} + onShowNotWired={() => setFilter({ wired: 'no' })} + + /> + + {orderToBeRefunded && <RefundModalForTable + id={orderToBeRefunded.order_id} + onCancel={() => setOrderToBeRefunded(undefined)} + onConfirm={(value) => refundOrder(orderToBeRefunded.order_id, value) + .then(() => setNotif({ + message: i18n`refund created successfully`, + type: "SUCCESS" + })) + .catch((error) => setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message + })) + .then(() => setOrderToBeRefunded(undefined))} + onLoadError={(error) => { + setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message + }); + setOrderToBeRefunded(undefined); + return <div />; + }} + onUnauthorized={onUnauthorized} + onNotFound={() => { + setNotif({ + message: i18n`could not get the order to refund`, + type: "ERROR", + // description: error.message + }); + setOrderToBeRefunded(undefined); + return <div />; + }} />} + </Fragment> +} + +interface RefundProps { + id: string; + onUnauthorized: () => VNode; + onLoadError: (error: HttpError) => VNode; + onNotFound: () => VNode; + onCancel: () => void; + onConfirm: (m: MerchantBackend.Orders.RefundRequest) => void; +} + +function RefundModalForTable({ id, onUnauthorized, onLoadError, onNotFound, onConfirm, onCancel }: RefundProps) { + const result = useOrderDetails(id); + + if (result.clientError && result.isUnauthorized) return onUnauthorized() + if (result.clientError && result.isNotfound) return onNotFound() + if (result.loading) return <Loading /> + if (!result.ok) return onLoadError(result) + + return <RefundModal + order={result.data} + onCancel={onCancel} + onConfirm={onConfirm} + /> +} + +async function copyToClipboard(text: string) { + return navigator.clipboard.writeText(text) +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx new file mode 100644 index 000000000..1d9ea53f6 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx @@ -0,0 +1,42 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { CreatePage as TestedComponent } from './CreatePage'; + + +export default { + title: 'Pages/Product/Create', + component: TestedComponent, + argTypes: { + onCreate: { action: 'onCreate' }, + onBack: { action: 'onBack' }, + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Example = createExample(TestedComponent, { +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx new file mode 100644 index 000000000..ed669f67f --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx @@ -0,0 +1,65 @@ +/* + 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 { AsyncButton } from "../../../../components/exception/AsyncButton"; +import { ProductForm } from "../../../../components/product/ProductForm"; +import { MerchantBackend } from "../../../../declaration"; +import { useListener } from "../../../../hooks/listener"; +import { Translate, useTranslator } from "../../../../i18n"; + +type Entity = MerchantBackend.Products.ProductAddDetail & { product_id: string} + +interface Props { + onCreate: (d: Entity) => Promise<void>; + onBack?: () => void; +} + + +export function CreatePage({ onCreate, onBack }: Props): VNode { + + const [submitForm, addFormSubmitter] = useListener<Entity | undefined>((result) => { + if (result) return onCreate(result) + return Promise.reject() + }) + + const i18n = useTranslator() + + return <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <ProductForm onSubscribe={addFormSubmitter} /> + + <div class="buttons is-right mt-5"> + {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} + <AsyncButton onClick={submitForm} data-tooltip={ + !submitForm ? i18n`Need to complete marked fields` : 'confirm operation' + } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> + </div> + + </div> + <div class="column" /> + </div> + </section> + </div> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx new file mode 100644 index 000000000..b56750405 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx @@ -0,0 +1,67 @@ +/* + 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/> + */ +import { h, VNode } from "preact"; +import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully"; +import { Entity } from "./index"; +import emptyImage from "../../assets/empty.png"; + +interface Props { + entity: Entity; + onConfirm: () => void; + onCreateAnother?: () => void; +} + +export function CreatedSuccessfully({ entity, onConfirm, onCreateAnother }: Props): VNode { + + return <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Image</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <img src={entity.image} style={{ width: 200, height: 200 }} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Description</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <textarea class="input" readonly value={entity.description} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Price</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.price} /> + </p> + </div> + </div> + </div> + </Template>; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx new file mode 100644 index 000000000..46454582a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/create/index.tsx @@ -0,0 +1,55 @@ +/* + 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 { Fragment, h, VNode } from 'preact'; +import { useState } from 'preact/hooks'; +import { NotificationCard } from '../../../../components/menu'; +import { MerchantBackend } from '../../../../declaration'; +import { useProductAPI } from '../../../../hooks/product'; +import { useTranslator } from '../../../../i18n'; +import { Notification } from '../../../../utils/types'; +import { CreatePage } from './CreatePage'; + +export type Entity = MerchantBackend.Products.ProductAddDetail +interface Props { + onBack?: () => void; + onConfirm: () => void; +} +export default function CreateProduct({ onConfirm, onBack }: Props): VNode { + const { createProduct } = useProductAPI() + const [notif, setNotif] = useState<Notification | undefined>(undefined) + const i18n = useTranslator() + + return <Fragment> + <NotificationCard notification={notif} /> + <CreatePage + onBack={onBack} + onCreate={(request: MerchantBackend.Products.ProductAddDetail) => { + return createProduct(request).then(() => onConfirm()).catch((error) => { + setNotif({ + message: i18n`could not create product`, + type: "ERROR", + description: error.message + }) + }) + }} /> + </Fragment> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/List.stories.tsx new file mode 100644 index 000000000..beae83b15 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/List.stories.tsx @@ -0,0 +1,58 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { CardTable as TestedComponent } from './Table'; + + +export default { + title: 'Pages/Product/List', + component: TestedComponent, + argTypes: { + onCreate: { action: 'onCreate' }, + onSelect: { action: 'onSelect' }, + onDelete: { action: 'onDelete' }, + onUpdate: { action: 'onUpdate' }, + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + + +export const Example = createExample(TestedComponent, { + instances: [{ + id: 'orderid', + description: 'description1', + description_i18n: {} as any, + image: '', + price: 'TESTKUDOS:10', + taxes: [], + total_lost: 10, + total_sold: 5, + total_stock: 15, + unit: 'bar', + address: {} + }] +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx new file mode 100644 index 000000000..9c85d976e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx @@ -0,0 +1,479 @@ +/* + 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 { format } from "date-fns"; +import { ComponentChildren, Fragment, h, VNode } from "preact"; +import { StateUpdater, useState } from "preact/hooks"; +import { + FormProvider, + FormErrors, +} from "../../../../components/form/FormProvider"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { InputNumber } from "../../../../components/form/InputNumber"; +import { MerchantBackend, WithId } from "../../../../declaration"; +import emptyImage from "../../../../assets/empty.png"; +import { Translate, useTranslator } from "../../../../i18n"; +import { Amounts } from "@gnu-taler/taler-util"; + +type Entity = MerchantBackend.Products.ProductDetail & WithId; + +interface Props { + instances: Entity[]; + onDelete: (id: Entity) => void; + onSelect: (product: Entity) => void; + onUpdate: ( + id: string, + data: MerchantBackend.Products.ProductPatchDetail + ) => Promise<void>; + onCreate: () => void; + selected?: boolean; +} + +export function CardTable({ + instances, + onCreate, + onSelect, + onUpdate, + onDelete, +}: Props): VNode { + const [rowSelection, rowSelectionHandler] = useState<string | undefined>( + undefined + ); + 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-shopping" /> + </span> + <Translate>Products</Translate> + </p> + <div class="card-header-icon" aria-label="more options"> + <span + class="has-tooltip-left" + data-tooltip={i18n`add product to inventory`} + > + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small"> + <i class="mdi mdi-plus mdi-36px" /> + </span> + </button> + </span> + </div> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {instances.length > 0 ? ( + <Table + instances={instances} + onSelect={onSelect} + onDelete={onDelete} + onUpdate={onUpdate} + rowSelection={rowSelection} + rowSelectionHandler={rowSelectionHandler} + /> + ) : ( + <EmptyTable /> + )} + </div> + </div> + </div> + </div> + ); +} +interface TableProps { + rowSelection: string | undefined; + instances: Entity[]; + onSelect: (id: Entity) => void; + onUpdate: ( + id: string, + data: MerchantBackend.Products.ProductPatchDetail + ) => Promise<void>; + onDelete: (id: Entity) => void; + rowSelectionHandler: StateUpdater<string | undefined>; +} + +function Table({ + rowSelection, + rowSelectionHandler, + instances, + onSelect, + onUpdate, + onDelete, +}: TableProps): VNode { + const i18n = useTranslator(); + return ( + <div class="table-container"> + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>Image</Translate> + </th> + <th> + <Translate>Description</Translate> + </th> + <th> + <Translate>Sell</Translate> + </th> + <th> + <Translate>Taxes</Translate> + </th> + <th> + <Translate>Profit</Translate> + </th> + <th> + <Translate>Stock</Translate> + </th> + <th> + <Translate>Sold</Translate> + </th> + <th /> + </tr> + </thead> + <tbody> + {instances.map((i) => { + const restStockInfo = !i.next_restock + ? "" + : i.next_restock.t_s === "never" + ? "never" + : `restock at ${format( + new Date(i.next_restock.t_s * 1000), + "yyyy/MM/dd" + )}`; + let stockInfo: ComponentChildren = ""; + if (i.total_stock < 0) { + stockInfo = "infinite"; + } else { + const totalStock = i.total_stock - i.total_lost - i.total_sold; + stockInfo = ( + <label title={restStockInfo}> + {totalStock} {i.unit} + </label> + ); + } + + const isFree = Amounts.isZero(Amounts.parseOrThrow(i.price)); + + return ( + <Fragment key={i.id}> + <tr key="info"> + <td + onClick={() => + rowSelection !== i.id && rowSelectionHandler(i.id) + } + style={{ cursor: "pointer" }} + > + <img + src={i.image ? i.image : emptyImage} + style={{ + border: "solid black 1px", + width: 100, + height: 100, + }} + /> + </td> + <td + onClick={() => + rowSelection !== i.id && rowSelectionHandler(i.id) + } + style={{ cursor: "pointer" }} + > + {i.description} + </td> + <td + onClick={() => + rowSelection !== i.id && rowSelectionHandler(i.id) + } + style={{ cursor: "pointer" }} + > + {isFree ? i18n`free` : `${i.price} / ${i.unit}`} + </td> + <td + onClick={() => + rowSelection !== i.id && rowSelectionHandler(i.id) + } + style={{ cursor: "pointer" }} + > + {sum(i.taxes)} + </td> + <td + onClick={() => + rowSelection !== i.id && rowSelectionHandler(i.id) + } + style={{ cursor: "pointer" }} + > + {difference(i.price, sum(i.taxes))} + </td> + <td + onClick={() => + rowSelection !== i.id && rowSelectionHandler(i.id) + } + style={{ cursor: "pointer" }} + > + {stockInfo} + </td> + <td + onClick={() => + rowSelection !== i.id && rowSelectionHandler(i.id) + } + style={{ cursor: "pointer" }} + > + {i.total_sold} {i.unit} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <span + class="has-tooltip-bottom" + data-tooltip={i18n`go to product update page`} + > + <button + class="button is-small is-success " + type="button" + onClick={(): void => onSelect(i)} + > + <Translate>Update</Translate> + </button> + </span> + <span + class="has-tooltip-left" + data-tooltip={i18n`remove this product from the database`} + > + <button + class="button is-small is-danger" + type="button" + onClick={(): void => onDelete(i)} + > + <Translate>Delete</Translate> + </button> + </span> + </div> + </td> + </tr> + {rowSelection === i.id && ( + <tr key="form"> + <td colSpan={10}> + <FastProductUpdateForm + product={i} + onUpdate={(prod) => + onUpdate(i.id, prod).then((r) => + rowSelectionHandler(undefined) + ) + } + onCancel={() => rowSelectionHandler(undefined)} + /> + </td> + </tr> + )} + </Fragment> + ); + })} + </tbody> + </table> + </div> + ); +} + +interface FastProductUpdateFormProps { + product: Entity; + onUpdate: ( + data: MerchantBackend.Products.ProductPatchDetail + ) => Promise<void>; + onCancel: () => void; +} +interface FastProductUpdate { + incoming: number; + lost: number; + price: string; +} +interface UpdatePrice { + price: string; +} + +function FastProductWithInfiniteStockUpdateForm({ + product, + onUpdate, + onCancel, +}: FastProductUpdateFormProps) { + const [value, valueHandler] = useState<UpdatePrice>({ price: product.price }); + const i18n = useTranslator(); + + return ( + <Fragment> + <FormProvider<FastProductUpdate> + name="added" + object={value} + valueHandler={valueHandler as any} + > + <InputCurrency<FastProductUpdate> + name="price" + label={i18n`Price`} + tooltip={i18n`update the product with new price`} + /> + </FormProvider> + + <div class="buttons is-right mt-5"> + <button class="button" onClick={onCancel}> + <Translate>Cancel</Translate> + </button> + <span + class="has-tooltip-left" + data-tooltip={i18n`update product with new price`} + > + <button + class="button is-info" + onClick={() => + onUpdate({ + ...product, + price: value.price, + }) + } + > + <Translate>Confirm</Translate> + </button> + </span> + </div> + </Fragment> + ); +} + +function FastProductWithManagedStockUpdateForm({ + product, + onUpdate, + onCancel, +}: FastProductUpdateFormProps) { + const [value, valueHandler] = useState<FastProductUpdate>({ + incoming: 0, + lost: 0, + price: product.price, + }); + + const currentStock = + product.total_stock - product.total_sold - product.total_lost; + + const errors: FormErrors<FastProductUpdate> = { + lost: + currentStock + value.incoming < value.lost + ? `lost cannot be greater that current + incoming (max ${ + currentStock + value.incoming + })` + : undefined, + }; + + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined + ); + const i18n = useTranslator(); + + return ( + <Fragment> + <FormProvider<FastProductUpdate> + name="added" + errors={errors} + object={value} + valueHandler={valueHandler as any} + > + <InputNumber<FastProductUpdate> + name="incoming" + label={i18n`Incoming`} + tooltip={i18n`add more elements to the inventory`} + /> + <InputNumber<FastProductUpdate> + name="lost" + label={i18n`Lost`} + tooltip={i18n`report elements lost in the inventory`} + /> + <InputCurrency<FastProductUpdate> + name="price" + label={i18n`Price`} + tooltip={i18n`new price for the product`} + /> + </FormProvider> + + <div class="buttons is-right mt-5"> + <button class="button" onClick={onCancel}> + <Translate>Cancel</Translate> + </button> + <span + class="has-tooltip-left" + data-tooltip={ + hasErrors + ? i18n`the are value with errors` + : i18n`update product with new stock and price` + } + > + <button + class="button is-info" + disabled={hasErrors} + onClick={() => + onUpdate({ + ...product, + total_stock: product.total_stock + value.incoming, + total_lost: product.total_lost + value.lost, + price: value.price, + }) + } + > + <Translate>Confirm</Translate> + </button> + </span> + </div> + </Fragment> + ); +} + +function FastProductUpdateForm(props: FastProductUpdateFormProps) { + return props.product.total_stock === -1 ? ( + <FastProductWithInfiniteStockUpdateForm {...props} /> + ) : ( + <FastProductWithManagedStockUpdateForm {...props} /> + ); +} + +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 products yet, add more pressing the + sign + </Translate> + </p> + </div> + ); +} + +function difference(price: string, tax: number) { + if (!tax) return price; + const ps = price.split(":"); + const p = parseInt(ps[1], 10); + ps[1] = `${p - tax}`; + return ps.join(":"); +} +function sum(taxes: MerchantBackend.Tax[]) { + return taxes.reduce((p, c) => p + parseInt(c.tax.split(":")[1], 10), 0); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx new file mode 100644 index 000000000..63e440df5 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx @@ -0,0 +1,80 @@ +/* + 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 { useState } from 'preact/hooks'; +import { Loading } from '../../../../components/exception/loading'; +import { NotificationCard } from '../../../../components/menu'; +import { MerchantBackend, WithId } from '../../../../declaration'; +import { HttpError } from '../../../../hooks/backend'; +import { useInstanceProducts, useProductAPI } from "../../../../hooks/product"; +import { useTranslator } from '../../../../i18n'; +import { Notification } from '../../../../utils/types'; +import { CardTable } from './Table'; + +interface Props { + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onCreate: () => void; + onSelect: (id: string) => void; + onLoadError: (e: HttpError) => VNode; +} +export default function ProductList({ onUnauthorized, onLoadError, onCreate, onSelect, onNotFound }: Props): VNode { + const result = useInstanceProducts() + const { deleteProduct, updateProduct } = useProductAPI() + const [notif, setNotif] = useState<Notification | undefined>(undefined) + + const i18n = useTranslator() + + if (result.clientError && result.isUnauthorized) return onUnauthorized() + if (result.clientError && result.isNotfound) return onNotFound() + if (result.loading) return <Loading /> + if (!result.ok) return onLoadError(result) + + return <section class="section is-main-section"> + <NotificationCard notification={notif} /> + + <CardTable instances={result.data} + onCreate={onCreate} + onUpdate={(id, prod) => updateProduct(id, prod) + .then(() => setNotif({ + message: i18n`product updated successfully`, + type: "SUCCESS" + })).catch((error) => setNotif({ + message: i18n`could not update the product`, + type: "ERROR", + description: error.message + })) + } + onSelect={(product) => onSelect(product.id)} + onDelete={(prod: (MerchantBackend.Products.ProductDetail & WithId)) => deleteProduct(prod.id) + .then(() => setNotif({ + message: i18n`product delete successfully`, + type: "SUCCESS" + })).catch((error) => setNotif({ + message: i18n`could not delete the product`, + type: "ERROR", + description: error.message + })) + } + /> + </section> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx new file mode 100644 index 000000000..3a57f7fac --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx @@ -0,0 +1,71 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { UpdatePage as TestedComponent } from './UpdatePage'; + + +export default { + title: 'Pages/Product/Update', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const WithManagedStock = createExample(TestedComponent, { + product: { + product_id: '20102-ASDAS-QWE', + description: 'description1', + description_i18n: {} as any, + image: '', + price: 'TESTKUDOS:10', + taxes: [], + total_lost: 10, + total_sold: 5, + total_stock: 15, + unit: 'bar', + address: {} + } +}); + +export const WithInfiniteStock = createExample(TestedComponent, { + product: { + product_id: '20102-ASDAS-QWE', + description: 'description1', + description_i18n: {} as any, + image: '', + price: 'TESTKUDOS:10', + taxes: [], + total_lost: 10, + total_sold: 5, + total_stock: -1, + unit: 'bar', + address: {} + } +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx new file mode 100644 index 000000000..d7eb3d162 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx @@ -0,0 +1,77 @@ +/* + 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 { AsyncButton } from "../../../../components/exception/AsyncButton"; +import { ProductForm } from "../../../../components/product/ProductForm"; +import { MerchantBackend, WithId } from "../../../../declaration"; +import { useListener } from "../../../../hooks/listener"; +import { Translate, useTranslator } from "../../../../i18n"; + +type Entity = MerchantBackend.Products.ProductDetail & { product_id: string } + +interface Props { + onUpdate: (d: Entity) => Promise<void>; + onBack?: () => void; + product: Entity; +} + +export function UpdatePage({ product, onUpdate, onBack }: Props): VNode { + const [submitForm, addFormSubmitter] = useListener<Entity | undefined>((result) => { + if (result) return onUpdate(result) + return Promise.resolve() + }) + + const i18n = useTranslator() + + return <div> + <section class="section"> + <section class="hero is-hero-bar"> + <div class="hero-body"> + + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <span class="is-size-4"><Translate>Product id:</Translate><b>{product.product_id}</b></span> + </div> + </div> + </div> + </div> + </section> + <hr /> + + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <ProductForm initial={product} onSubscribe={addFormSubmitter} alreadyExist /> + + <div class="buttons is-right mt-5"> + {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} + <AsyncButton onClick={submitForm} data-tooltip={ + !submitForm ? i18n`Need to complete marked fields` : 'confirm operation' + } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> + </div> + </div> + <div class="column" /> + </div> + </section> + </div> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx new file mode 100644 index 000000000..a6a61c815 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx @@ -0,0 +1,71 @@ +/* + 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 { Fragment, h, VNode } from 'preact'; +import { useState } from 'preact/hooks'; +import { Loading } from '../../../../components/exception/loading'; +import { NotificationCard } from '../../../../components/menu'; +import { MerchantBackend } from '../../../../declaration'; +import { HttpError } from '../../../../hooks/backend'; +import { useProductAPI, useProductDetails } from '../../../../hooks/product'; +import { useTranslator } from '../../../../i18n'; +import { Notification } from '../../../../utils/types'; +import { UpdatePage } from './UpdatePage'; + +export type Entity = MerchantBackend.Products.ProductAddDetail +interface Props { + onBack?: () => void; + onConfirm: () => void; + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onLoadError: (e: HttpError) => VNode; + pid: string; +} +export default function UpdateProduct({ pid, onConfirm, onBack, onUnauthorized, onNotFound, onLoadError }: Props): VNode { + const { updateProduct } = useProductAPI() + const result = useProductDetails(pid) + const [notif, setNotif] = useState<Notification | undefined>(undefined) + + const i18n = useTranslator() + + if (result.clientError && result.isUnauthorized) return onUnauthorized() + if (result.clientError && result.isNotfound) return onNotFound() + if (result.loading) return <Loading /> + if (!result.ok) return onLoadError(result) + + return <Fragment> + <NotificationCard notification={notif} /> + <UpdatePage + product={{ ...result.data, product_id: pid }} + onBack={onBack} + onUpdate={(data) => { + return updateProduct(pid, data) + .then(onConfirm) + .catch((error) => { + setNotif({ + message: i18n`could not create product`, + type: "ERROR", + description: error.message + }) + }) + }} /> + </Fragment> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx new file mode 100644 index 000000000..e138770a8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx @@ -0,0 +1,42 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { CreatePage as TestedComponent } from './CreatePage'; + + +export default { + title: 'Pages/Reserve/Create', + component: TestedComponent, + argTypes: { + onCreate: { action: 'onCreate' }, + onBack: { action: 'onBack' }, + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Example = createExample(TestedComponent, { +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx new file mode 100644 index 000000000..2e85cf9c8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx @@ -0,0 +1,168 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { StateUpdater, useEffect, useState } from "preact/hooks"; +import { FormErrors, FormProvider } from "../../../../components/form/FormProvider"; +import { Input } from "../../../../components/form/Input"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { ExchangeBackend, MerchantBackend } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; +import { AsyncButton } from "../../../../components/exception/AsyncButton"; +import { canonicalizeBaseUrl, ExchangeKeysJson } from "@gnu-taler/taler-util" +import { PAYTO_WIRE_METHOD_LOOKUP, URL_REGEX } from "../../../../utils/constants"; +import { request } from "../../../../hooks/backend"; +import { InputSelector } from "../../../../components/form/InputSelector"; + +type Entity = MerchantBackend.Tips.ReserveCreateRequest + +interface Props { + onCreate: (d: Entity) => Promise<void>; + onBack?: () => void; +} + + +enum Steps { + EXCHANGE, + WIRE_METHOD, +} + +interface ViewProps { + step: Steps, + setCurrentStep: (s: Steps) => void; + reserve: Partial<Entity>; + onBack?: () => void; + submitForm: () => Promise<void>; + setReserve: StateUpdater<Partial<Entity>>; +} +function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, setReserve }: ViewProps): VNode { + const i18n = useTranslator() + const [wireMethods, setWireMethods] = useState<Array<string>>([]) + const [exchangeQueryError, setExchangeQueryError] = useState<string | undefined>(undefined) + + useEffect(() => { + setExchangeQueryError(undefined) + }, [reserve.exchange_url]) + + switch (step) { + case Steps.EXCHANGE: { + const errors: FormErrors<Entity> = { + initial_balance: !reserve.initial_balance ? 'cannot be empty' : !(parseInt(reserve.initial_balance.split(':')[1], 10) > 0) ? i18n`it should be greater than 0` : undefined, + exchange_url: !reserve.exchange_url ? i18n`cannot be empty` : !URL_REGEX.test(reserve.exchange_url) ? i18n`must be a valid URL` : !exchangeQueryError ? undefined : exchangeQueryError, + } + + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + + return <Fragment> + <FormProvider<Entity> object={reserve} errors={errors} valueHandler={setReserve}> + <InputCurrency<Entity> name="initial_balance" label={i18n`Initial balance`} tooltip={i18n`balance prior to deposit`} /> + <Input<Entity> name="exchange_url" label={i18n`Exchange URL`} tooltip={i18n`URL of exchange`} /> + </FormProvider> + + <div class="buttons is-right mt-5"> + {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} + <AsyncButton class="has-tooltip-left" onClick={() => { + return request<ExchangeBackend.WireResponse>(`${reserve.exchange_url}wire`).then(r => { + const wireMethods = r.data.accounts.map(a => { + const match = PAYTO_WIRE_METHOD_LOOKUP.exec(a.payto_uri) + return match && match[1] || '' + }) + setWireMethods(wireMethods) + setCurrentStep(Steps.WIRE_METHOD) + return + }).catch((r: any) => { + setExchangeQueryError(r.message) + }) + }} data-tooltip={ + hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' + } disabled={hasErrors} ><Translate>Next</Translate></AsyncButton> + </div> + </Fragment> + } + + case Steps.WIRE_METHOD: { + const errors: FormErrors<Entity> = { + wire_method: !reserve.wire_method ? i18n`cannot be empty` : undefined, + } + + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + return <Fragment> + <FormProvider<Entity> object={reserve} errors={errors} valueHandler={setReserve}> + <InputCurrency<Entity> name="initial_balance" label={i18n`Initial balance`} tooltip={i18n`balance prior to deposit`} readonly /> + <Input<Entity> name="exchange_url" label={i18n`Exchange URL`} tooltip={i18n`URL of exchange`} readonly /> + <InputSelector<Entity> name="wire_method" label={i18n`Wire method`} tooltip={i18n`method to use for wire transfer`} values={wireMethods} placeholder={i18n`Select one wire method`} /> + </FormProvider> + <div class="buttons is-right mt-5"> + {onBack && <button class="button" onClick={() => setCurrentStep(Steps.EXCHANGE)} ><Translate>Back</Translate></button>} + <AsyncButton onClick={submitForm} data-tooltip={ + hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' + } disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> + </div> + </Fragment> + + } + } +} + +export function CreatePage({ onCreate, onBack }: Props): VNode { + const [reserve, setReserve] = useState<Partial<Entity>>({}) + + + const submitForm = () => { + return onCreate(reserve as Entity) + } + + const [currentStep, setCurrentStep] = useState(Steps.EXCHANGE) + + + return <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + + <div class="tabs is-toggle is-fullwidth is-small"> + <ul> + <li class={currentStep === Steps.EXCHANGE ? "is-active" : ""}> + <a style={{ cursor: 'initial' }}> + <span>Step 1: Specify exchange</span> + </a> + </li> + <li class={currentStep === Steps.WIRE_METHOD ? "is-active" : ""}> + <a style={{ cursor: 'initial' }}> + <span>Step 2: Select wire method</span> + </a> + </li> + </ul> + </div> + + <ViewStep step={currentStep} reserve={reserve} + setCurrentStep={setCurrentStep} + setReserve={setReserve} + submitForm={submitForm} + onBack={onBack} + /> + </div> + <div class="column" /> + </div> + </section> + </div> +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx new file mode 100644 index 000000000..f013040ff --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx @@ -0,0 +1,53 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { CreatedSuccessfully as TestedComponent } from './CreatedSuccessfully'; + + +export default { + title: 'Pages/Reserve/CreatedSuccessfully', + component: TestedComponent, + argTypes: { + onCreate: { action: 'onCreate' }, + onBack: { action: 'onBack' }, + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Example = createExample(TestedComponent, { + entity: { + request: { + exchange_url: 'http://exchange.taler/', + initial_balance: 'TESTKUDOS:1', + wire_method: 'x-taler-bank', + }, + response: { + payto_uri: 'payto://x-taler-bank/bank.taler:8080/exchange_account', + reserve_pub: 'WEQWDASDQWEASDADASDQWEQWEASDAS' + } + } +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx new file mode 100644 index 000000000..255486d22 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx @@ -0,0 +1,79 @@ +/* + 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/> + */ + +import { h, VNode } from "preact"; +import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully"; +import { MerchantBackend } from "../../../../declaration"; +import { Translate } from "../../../../i18n"; +import { QR } from "../../../../components/exception/QR"; + +type Entity = { request: MerchantBackend.Tips.ReserveCreateRequest, response: MerchantBackend.Tips.ReserveCreateConfirmation }; + +interface Props { + entity: Entity; + onConfirm: () => void; + onCreateAnother?: () => void; +} + +export function CreatedSuccessfully({ entity, onConfirm, onCreateAnother }: Props): VNode { + const link = `${entity.response.payto_uri}?message=${entity.response.reserve_pub}&amount=${entity.request.initial_balance}` + + return <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Amount</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input readonly class="input" value={entity.request.initial_balance} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Exchange bank account</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input readonly class="input" value={entity.response.payto_uri} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Wire transfer subject</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.response.reserve_pub} /> + </p> + </div> + </div> + </div> + <p class="is-size-5"><Translate>To complete the setup of the reserve, you must now initiate a wire transfer using the given wire transfer subject and crediting the specified amount to the indicated account of the exchange.</Translate></p> + <p class="is-size-5"><Translate>If your system supports RFC 8905, you can do this by opening this URI:</Translate></p> + <pre> + <a target="_blank" rel="noreferrer" href={link}>{link}</a> + </pre> + <QR text={link} /> + </Template>; +} + diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx new file mode 100644 index 000000000..5c2fdaf4b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx @@ -0,0 +1,71 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { NotificationCard } from "../../../../components/menu"; +import { MerchantBackend } from "../../../../declaration"; +import { useReservesAPI } from "../../../../hooks/reserves"; +import { useTranslator } from "../../../../i18n"; +import { Notification } from "../../../../utils/types"; +import { CreatedSuccessfully } from "./CreatedSuccessfully"; +import { CreatePage } from "./CreatePage"; +interface Props { + onBack: () => void; + onConfirm: () => void; +} +export default function CreateReserve({ onBack, onConfirm }: Props): VNode { + const { createReserve } = useReservesAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); + const i18n = useTranslator(); + + const [createdOk, setCreatedOk] = useState< + | { + request: MerchantBackend.Tips.ReserveCreateRequest; + response: MerchantBackend.Tips.ReserveCreateConfirmation; + } + | undefined + >(undefined); + + if (createdOk) { + return <CreatedSuccessfully entity={createdOk} onConfirm={onConfirm} />; + } + + return ( + <Fragment> + <NotificationCard notification={notif} /> + <CreatePage + onBack={onBack} + onCreate={(request: MerchantBackend.Tips.ReserveCreateRequest) => { + return createReserve(request) + .then((r) => setCreatedOk({ request, response: r.data })) + .catch((error) => { + setNotif({ + message: i18n`could not create reserve`, + type: "ERROR", + description: error.message, + }); + }); + }} + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx new file mode 100644 index 000000000..cbc70179b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx @@ -0,0 +1,278 @@ +/* + 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 { Amounts } from "@gnu-taler/taler-util"; +import { format } from "date-fns"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { QR } from "../../../../components/exception/QR"; +import { FormProvider } from "../../../../components/form/FormProvider"; +import { Input } from "../../../../components/form/Input"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { InputDate } from "../../../../components/form/InputDate"; +import { TextField } from "../../../../components/form/TextField"; +import { ContinueModal, SimpleModal } from "../../../../components/modal"; +import { MerchantBackend } from "../../../../declaration"; +import { useTipDetails } from "../../../../hooks/reserves"; +import { Translate, useTranslator } from "../../../../i18n"; +import { TipInfo } from "./TipInfo"; + +type Entity = MerchantBackend.Tips.ReserveDetail; +type CT = MerchantBackend.ContractTerms; + +interface Props { + onBack: () => void; + selected: Entity; + id: string; +} + +export function DetailPage({ id, selected, onBack }: Props): VNode { + const i18n = useTranslator(); + const didExchangeAckTransfer = Amounts.isNonZero( + Amounts.parseOrThrow(selected.exchange_initial_amount) + ); + const link = `${selected.payto_uri}?message=${id}&amount=${selected.merchant_initial_amount}`; + + return ( + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <div class="section main-section"> + <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 + /> + <TextField<Entity> + name="exchange_url" + label={i18n`Exchange URL`} + readonly + > + <a target="_blank" rel="noreferrer" href={selected.exchange_url}> + {selected.exchange_url} + </a> + </TextField> + + {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> + + {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> + To complete the setup of the reserve, you must now initiate a + wire transfer using the given wire transfer subject and + crediting the specified amount to the indicated account of the + exchange. + </Translate> + </p> + <p class="is-size-5"> + <Translate> + If your system supports RFC 8905, you can do this by opening + this URI: + </Translate> + </p> + <pre> + <a target="_blank" rel="noreferrer" href={link}> + {link} + </a> + </pre> + <QR text={link} /> + </Fragment> + )} + + <div class="buttons is-right mt-5"> + <button class="button" onClick={onBack}> + <Translate>Back</Translate> + </button> + </div> + </div> + </div> + <div class="column" /> + </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> + ); +} + +interface TableProps { + tips: MerchantBackend.Tips.TipStatusEntry[]; +} + +function Table({ tips }: TableProps): VNode { + return ( + <div class="table-container"> + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>Authorized</Translate> + </th> + <th> + <Translate>Picked up</Translate> + </th> + <th> + <Translate>Reason</Translate> + </th> + <th> + <Translate>Expiration</Translate> + </th> + </tr> + </thead> + <tbody> + {tips.map((t, i) => { + return <TipRow id={t.tip_id} key={i} entry={t} />; + })} + </tbody> + </table> + </div> + ); +} + +function TipRow({ + id, + entry, +}: { + id: string; + entry: MerchantBackend.Tips.TipStatusEntry; +}) { + const [selected, setSelected] = useState(false); + const result = useTipDetails(id); + if (result.loading) { + return ( + <tr> + <td>...</td> + <td>...</td> + <td>...</td> + <td>...</td> + </tr> + ); + } + if (!result.ok) { + return ( + <tr> + <td>...</td> {/* authorized */} + <td>{entry.total_amount}</td> + <td>{entry.reason}</td> + <td>...</td> {/* expired */} + </tr> + ); + } + const info = result.data; + function onSelect() { + setSelected(true); + } + return ( + <Fragment> + {selected && ( + <SimpleModal + description="tip" + active + onCancel={() => setSelected(false)} + > + <TipInfo id={id} amount={info.total_authorized} entity={info} /> + </SimpleModal> + )} + <tr> + <td onClick={onSelect}>{info.total_authorized}</td> + <td onClick={onSelect}>{info.total_picked_up}</td> + <td onClick={onSelect}>{info.reason}</td> + <td onClick={onSelect}> + {info.expiration.t_s === "never" + ? "never" + : format(info.expiration.t_s * 1000, "yyyy/MM/dd HH:mm:ss")} + </td> + </tr> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx new file mode 100644 index 000000000..98c1fa72e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx @@ -0,0 +1,105 @@ +/* + 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, FunctionalComponent } from "preact"; +import { DetailPage as TestedComponent } from "./DetailPage"; + +export default { + title: "Pages/Reserve/Detail", + component: TestedComponent, + argTypes: { + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const Funded = createExample(TestedComponent, { + id: "THISISTHERESERVEID", + selected: { + active: true, + committed_amount: "TESTKUDOS:10", + creation_time: { + t_s: new Date().getTime() / 1000, + }, + exchange_initial_amount: "TESTKUDOS:10", + expiration_time: { + t_s: new Date().getTime() / 1000, + }, + merchant_initial_amount: "TESTKUDOS:10", + pickup_amount: "TESTKUDOS:10", + payto_uri: "payto://x-taler-bank/bank.taler:8080/account", + exchange_url: "http://exchange.taler/", + }, +}); + +export const NotYetFunded = createExample(TestedComponent, { + id: "THISISTHERESERVEID", + selected: { + active: true, + committed_amount: "TESTKUDOS:10", + creation_time: { + t_s: new Date().getTime() / 1000, + }, + exchange_initial_amount: "TESTKUDOS:0", + expiration_time: { + t_s: new Date().getTime() / 1000, + }, + merchant_initial_amount: "TESTKUDOS:10", + pickup_amount: "TESTKUDOS:10", + payto_uri: "payto://x-taler-bank/bank.taler:8080/account", + exchange_url: "http://exchange.taler/", + }, +}); + +export const FundedWithEmptyTips = createExample(TestedComponent, { + id: "THISISTHERESERVEID", + selected: { + active: true, + committed_amount: "TESTKUDOS:10", + creation_time: { + t_s: new Date().getTime() / 1000, + }, + exchange_initial_amount: "TESTKUDOS:10", + expiration_time: { + t_s: new Date().getTime() / 1000, + }, + merchant_initial_amount: "TESTKUDOS:10", + pickup_amount: "TESTKUDOS:10", + payto_uri: "payto://x-taler-bank/bank.taler:8080/account", + exchange_url: "http://exchange.taler/", + tips: [ + { + reason: "asdasd", + tip_id: "123", + total_amount: "TESTKUDOS:1", + }, + ], + }, +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx new file mode 100644 index 000000000..3f384966b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx @@ -0,0 +1,87 @@ +/* + 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/> + */ +import { format } from "date-fns"; +import { Fragment, h, VNode } from "preact"; +import { useBackendContext } from "../../../../context/backend"; +import { MerchantBackend } from "../../../../declaration"; + +type Entity = MerchantBackend.Tips.TipDetails; + +interface Props { + id: string; + entity: Entity; + amount: string; +} + +export function TipInfo({ id, amount, entity }: Props): VNode { + const { url } = useBackendContext(); + const tipHost = url.replace(/.*:\/\//, ""); // remove protocol part + const proto = url.startsWith("http://") ? "taler+http" : "taler"; + const tipURL = `${proto}://tip/${tipHost}/${id}`; + return ( + <Fragment> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Amount</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input readonly class="input" value={amount} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">URL</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field" style={{ overflowWrap: "anywhere" }}> + <p class="control"> + <a target="_blank" rel="noreferrer" href={tipURL}> + {tipURL} + </a> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Valid until</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + class="input" + readonly + value={ + !entity.expiration || entity.expiration.t_s === "never" + ? "never" + : format( + entity.expiration.t_s * 1000, + "yyyy/MM/dd HH:mm:ss" + ) + } + /> + </p> + </div> + </div> + </div> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/index.tsx new file mode 100644 index 000000000..c2483f053 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/index.tsx @@ -0,0 +1,56 @@ +/* + 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 { Fragment, h, VNode } from "preact"; +import { Loading } from "../../../../components/exception/loading"; +import { HttpError } from "../../../../hooks/backend"; +import { useReserveDetails } from "../../../../hooks/reserves"; +import { DetailPage } from "./DetailPage"; + +interface Props { + rid: string; + + onUnauthorized: () => VNode; + onLoadError: (error: HttpError) => VNode; + onNotFound: () => VNode; + onDelete: () => void; + onBack: () => void; +} +export default function DetailReserve({ + rid, + onUnauthorized, + onLoadError, + onNotFound, + onBack, + onDelete, +}: Props): VNode { + const result = useReserveDetails(rid); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + return ( + <Fragment> + <DetailPage selected={result.data} onBack={onBack} id={rid} /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx new file mode 100644 index 000000000..ec468b2e9 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx @@ -0,0 +1,85 @@ +/* + 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 { useState } from "preact/hooks"; +import { FormErrors, FormProvider } from "../../../../components/form/FormProvider"; +import { Input } from "../../../../components/form/Input"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { ConfirmModal, ContinueModal } from "../../../../components/modal"; +import { MerchantBackend } from "../../../../declaration"; +import { useTranslator } from "../../../../i18n"; +import { AuthorizeTipSchema } from "../../../../schemas"; +import { CreatedSuccessfully } from "./CreatedSuccessfully"; +import * as yup from 'yup'; + +interface AuthorizeTipModalProps { + onCancel: () => void; + onConfirm: (value: MerchantBackend.Tips.TipCreateRequest) => void; + tipAuthorized?: { + response: MerchantBackend.Tips.TipCreateConfirmation; + request: MerchantBackend.Tips.TipCreateRequest; + }; +} + +export function AuthorizeTipModal({ onCancel, onConfirm, tipAuthorized }: AuthorizeTipModalProps): VNode { + // const result = useOrderDetails(id) + type State = MerchantBackend.Tips.TipCreateRequest + const [form, setValue] = useState<Partial<State>>({}) + const i18n = useTranslator(); + + // const [errors, setErrors] = useState<FormErrors<State>>({}) + let errors: FormErrors<State> = {} + try { + AuthorizeTipSchema.validateSync(form, { abortEarly: false }) + } catch (err) { + if (err instanceof yup.ValidationError) { + const yupErrors = err.inner as any[] + errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {}) + } + } + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + + const validateAndConfirm = () => { + onConfirm(form as State) + } + if (tipAuthorized) { + return <ContinueModal description="tip" active onConfirm={onCancel}> + <CreatedSuccessfully + entity={tipAuthorized.response} + request={tipAuthorized.request} + onConfirm={onCancel} + /> + </ContinueModal> + } + + return <ConfirmModal description="tip" active onCancel={onCancel} disabled={hasErrors} onConfirm={validateAndConfirm}> + + <FormProvider<State> errors={errors} object={form} valueHandler={setValue} > + <InputCurrency<State> name="amount" label={i18n`Amount`} tooltip={i18n`amount of tip`} /> + <Input<State> name="justification" label={i18n`Justification`} inputType="multiline" tooltip={i18n`reason for the tip`} /> + <Input<State> name="next_url" label={i18n`URL after tip`} tooltip={i18n`URL to visit after tip payment`} /> + </FormProvider> + + </ConfirmModal> +} + + diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx new file mode 100644 index 000000000..1e5f0758f --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx @@ -0,0 +1,100 @@ +/* + 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/> + */ +import { format } from "date-fns"; +import { Fragment, h, VNode } from "preact"; +import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully"; +import { MerchantBackend } from "../../../../declaration"; + +type Entity = MerchantBackend.Tips.TipCreateConfirmation; + +interface Props { + entity: Entity; + request: MerchantBackend.Tips.TipCreateRequest; + onConfirm: () => void; + onCreateAnother?: () => void; +} + +export function CreatedSuccessfully({ + request, + entity, + onConfirm, + onCreateAnother, +}: Props): VNode { + return ( + <Fragment> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Amount</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input readonly class="input" value={request.amount} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Justification</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input readonly class="input" value={request.justification} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">URL</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input readonly class="input" value={entity.tip_status_url} /> + </p> + </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label">Valid until</label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + class="input" + readonly + value={ + !entity.tip_expiration || + entity.tip_expiration.t_s === "never" + ? "never" + : format( + entity.tip_expiration.t_s * 1000, + "yyyy/MM/dd HH:mm:ss" + ) + } + /> + </p> + </div> + </div> + </div> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx new file mode 100644 index 000000000..1cb9e748c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx @@ -0,0 +1,102 @@ +/* + 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, FunctionalComponent } from "preact"; +import { CardTable as TestedComponent } from "./Table"; + +export default { + title: "Pages/Reserve/List", + component: TestedComponent, + argTypes: { + onCreate: { action: "onCreate" }, + onDelete: { action: "onDelete" }, + onNewTip: { action: "onNewTip" }, + onSelect: { action: "onSelect" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const AllFunded = createExample(TestedComponent, { + instances: [ + { + id: "reseverId", + active: true, + committed_amount: "TESTKUDOS:10", + creation_time: { + t_s: new Date().getTime() / 1000, + }, + exchange_initial_amount: "TESTKUDOS:10", + expiration_time: { + t_s: new Date().getTime() / 1000, + }, + merchant_initial_amount: "TESTKUDOS:10", + pickup_amount: "TESTKUDOS:10", + reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS", + }, + { + id: "reseverId2", + active: true, + committed_amount: "TESTKUDOS:13", + creation_time: { + t_s: new Date().getTime() / 1000, + }, + exchange_initial_amount: "TESTKUDOS:10", + expiration_time: { + t_s: new Date().getTime() / 1000, + }, + merchant_initial_amount: "TESTKUDOS:10", + pickup_amount: "TESTKUDOS:10", + reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS", + }, + ], +}); + +export const Empty = createExample(TestedComponent, { + instances: [], +}); + +export const OneNotYetFunded = createExample(TestedComponent, { + instances: [ + { + id: "reseverId", + active: true, + committed_amount: "TESTKUDOS:0", + creation_time: { + t_s: new Date().getTime() / 1000, + }, + exchange_initial_amount: "TESTKUDOS:0", + expiration_time: { + t_s: new Date().getTime() / 1000, + }, + merchant_initial_amount: "TESTKUDOS:10", + pickup_amount: "TESTKUDOS:10", + reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS", + }, + ], +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx new file mode 100644 index 000000000..b3bb7b020 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx @@ -0,0 +1,313 @@ +/* + 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 { format } from "date-fns"; +import { Fragment, h, VNode } from "preact"; +import { MerchantBackend, WithId } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; + +type Entity = MerchantBackend.Tips.ReserveStatusEntry & WithId; + +interface Props { + instances: Entity[]; + onNewTip: (id: Entity) => void; + onSelect: (id: Entity) => void; + onDelete: (id: Entity) => void; + onCreate: () => void; +} + +export function CardTable({ + instances, + onCreate, + onSelect, + onNewTip, + onDelete, +}: Props): VNode { + const [withoutFunds, withFunds] = instances.reduce((prev, current) => { + const amount = current.exchange_initial_amount; + if (amount.endsWith(":0")) { + prev[0] = prev[0].concat(current); + } else { + prev[1] = prev[1].concat(current); + } + return prev; + }, new Array<Array<Entity>>([], [])); + + const i18n = useTranslator(); + + return ( + <Fragment> + {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> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + <TableWithoutFund + instances={withoutFunds} + onNewTip={onNewTip} + onSelect={onSelect} + onDelete={onDelete} + /> + </div> + </div> + </div> + </div> + )} + + <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 ready</Translate> + </p> + <div class="card-header-icon" aria-label="more options" /> + <div class="card-header-icon" aria-label="more options"> + <span class="has-tooltip-left" data-tooltip={i18n`add new reserve`}> + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small"> + <i class="mdi mdi-plus mdi-36px" /> + </span> + </button> + </span> + </div> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {withFunds.length > 0 ? ( + <Table + instances={withFunds} + onNewTip={onNewTip} + onSelect={onSelect} + onDelete={onDelete} + /> + ) : ( + <EmptyTable /> + )} + </div> + </div> + </div> + </div> + </Fragment> + ); +} +interface TableProps { + instances: Entity[]; + onNewTip: (id: Entity) => void; + onDelete: (id: Entity) => void; + onSelect: (id: Entity) => void; +} + +function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode { + const i18n = useTranslator(); + return ( + <div class="table-container"> + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>Created at</Translate> + </th> + <th> + <Translate>Expires at</Translate> + </th> + <th> + <Translate>Initial</Translate> + </th> + <th> + <Translate>Picked up</Translate> + </th> + <th> + <Translate>Committed</Translate> + </th> + <th /> + </tr> + </thead> + <tbody> + {instances.map((i) => { + return ( + <tr key={i.id}> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.creation_time.t_s === "never" + ? "never" + : format(i.creation_time.t_s * 1000, "yyyy/MM/dd HH:mm:ss")} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.expiration_time.t_s === "never" + ? "never" + : format( + i.expiration_time.t_s * 1000, + "yyyy/MM/dd HH:mm:ss" + )} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.exchange_initial_amount} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.pickup_amount} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.committed_amount} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <button + class="button is-small is-danger has-tooltip-left" + data-tooltip={i18n`delete selected reserve from the database`} + type="button" + onClick={(): void => onDelete(i)} + > + Delete + </button> + <button + class="button is-small is-info has-tooltip-left" + data-tooltip={i18n`authorize new tip from selected reserve`} + type="button" + onClick={(): void => onNewTip(i)} + > + New Tip + </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 ready reserves yet, add more pressing the + sign or fund + them + </Translate> + </p> + </div> + ); +} + +function TableWithoutFund({ + instances, + onSelect, + onDelete, +}: TableProps): VNode { + const i18n = useTranslator(); + return ( + <div class="table-container"> + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>Created at</Translate> + </th> + <th> + <Translate>Expires at</Translate> + </th> + <th> + <Translate>Expected Balance</Translate> + </th> + <th /> + </tr> + </thead> + <tbody> + {instances.map((i) => { + return ( + <tr key={i.id}> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.creation_time.t_s === "never" + ? "never" + : format(i.creation_time.t_s * 1000, "yyyy/MM/dd HH:mm:ss")} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.expiration_time.t_s === "never" + ? "never" + : format( + i.expiration_time.t_s * 1000, + "yyyy/MM/dd HH:mm:ss" + )} + </td> + <td + onClick={(): void => onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.merchant_initial_amount} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <button + class="button is-small is-danger jb-modal has-tooltip-left" + type="button" + data-tooltip={i18n`delete selected reserve from the database`} + onClick={(): void => onDelete(i)} + > + Delete + </button> + </div> + </td> + </tr> + ); + })} + </tbody> + </table> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx new file mode 100644 index 000000000..f071b5635 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx @@ -0,0 +1,117 @@ +/* + 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 { useState } from "preact/hooks"; +import { Loading } from "../../../../components/exception/loading"; +import { NotificationCard } from "../../../../components/menu"; +import { MerchantBackend } from "../../../../declaration"; +import { HttpError } from "../../../../hooks/backend"; +import { + useInstanceReserves, + useReservesAPI, +} from "../../../../hooks/reserves"; +import { useTranslator } from "../../../../i18n"; +import { Notification } from "../../../../utils/types"; +import { CardTable } from "./Table"; +import { AuthorizeTipModal } from "./AutorizeTipModal"; + +interface Props { + onUnauthorized: () => VNode; + onLoadError: (e: HttpError) => VNode; + onSelect: (id: string) => void; + onNotFound: () => VNode; + onCreate: () => void; +} + +interface TipConfirmation { + response: MerchantBackend.Tips.TipCreateConfirmation; + request: MerchantBackend.Tips.TipCreateRequest; +} + +export default function ListTips({ + onUnauthorized, + onLoadError, + onNotFound, + onSelect, + onCreate, +}: Props): VNode { + const result = useInstanceReserves(); + const { deleteReserve, authorizeTipReserve } = useReservesAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); + const i18n = useTranslator(); + const [reserveForTip, setReserveForTip] = useState<string | undefined>( + undefined + ); + const [tipAuthorized, setTipAuthorized] = useState< + TipConfirmation | undefined + >(undefined); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + + return ( + <section class="section is-main-section"> + <NotificationCard notification={notif} /> + + {reserveForTip && ( + <AuthorizeTipModal + onCancel={() => { + setReserveForTip(undefined); + setTipAuthorized(undefined); + }} + tipAuthorized={tipAuthorized} + onConfirm={async (request) => { + try { + const response = await authorizeTipReserve( + reserveForTip, + request + ); + setTipAuthorized({ + request, + response: response.data, + }); + } catch (error) { + setNotif({ + message: i18n`could not create the tip`, + type: "ERROR", + description: error instanceof Error ? error.message : undefined, + }); + setReserveForTip(undefined); + } + }} + /> + )} + + <CardTable + instances={result.data.reserves + .filter((r) => r.active) + .map((o) => ({ ...o, id: o.reserve_pub }))} + onCreate={onCreate} + onDelete={(reserve) => deleteReserve(reserve.reserve_pub)} + onSelect={(reserve) => onSelect(reserve.id)} + onNewTip={(reserve) => setReserveForTip(reserve.id)} + /> + </section> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx new file mode 100644 index 000000000..535cb1e37 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx @@ -0,0 +1,43 @@ +/* + 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, FunctionalComponent } from 'preact'; +import { CreatePage as TestedComponent } from './CreatePage'; + + +export default { + title: 'Pages/Transfer/Create', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Example = createExample(TestedComponent, { + accounts: ['payto://x-taler-bank/account1','payto://x-taler-bank/account2'] +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx new file mode 100644 index 000000000..d0f5c5e95 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx @@ -0,0 +1,104 @@ +/* + 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 { useState } from "preact/hooks"; +import { AsyncButton } from "../../../../components/exception/AsyncButton"; +import { FormErrors, FormProvider } from "../../../../components/form/FormProvider"; +import { Input } from "../../../../components/form/Input"; +import { InputCurrency } from "../../../../components/form/InputCurrency"; +import { InputSelector } from "../../../../components/form/InputSelector"; +import { useConfigContext } from "../../../../context/config"; +import { MerchantBackend } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; +import { CROCKFORD_BASE32_REGEX, URL_REGEX } from "../../../../utils/constants"; + +type Entity = MerchantBackend.Transfers.TransferInformation + +interface Props { + onCreate: (d: Entity) => Promise<void>; + onBack?: () => void; + accounts: string[], +} + +export function CreatePage({ accounts, onCreate, onBack }: Props): VNode { + const i18n = useTranslator() + const { currency } = useConfigContext() + + const [state, setState] = useState<Partial<Entity>>({ + wtid: '', + // payto_uri: , + // exchange_url: 'http://exchange.taler:8081/', + credit_amount: ``, + }); + + const errors: FormErrors<Entity> = { + wtid: !state.wtid ? i18n`cannot be empty` : + (!CROCKFORD_BASE32_REGEX.test(state.wtid) ? i18n`check the id, does not look valid` : + (state.wtid.length !== 52 ? i18n`should have 52 characters, current ${state.wtid.length}` : + undefined)), + payto_uri: !state.payto_uri ? i18n`cannot be empty` : undefined, + credit_amount: !state.credit_amount ? i18n`cannot be empty` : undefined, + exchange_url: !state.exchange_url ? i18n`cannot be empty` : + (!URL_REGEX.test(state.exchange_url) ? i18n`URL doesn't have the right format` : undefined), + } + + const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) + + const submitForm = () => { + if (hasErrors) return Promise.reject() + return onCreate(state as any) + } + + return <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + + <FormProvider object={state} valueHandler={setState} errors={errors}> + <InputSelector name="payto_uri" label={i18n`Credited bank account`} + values={accounts} + placeholder={i18n`Select one account`} + tooltip={i18n`Bank account of the merchant where the payment was received`} + /> + <Input<Entity> name="wtid" label={i18n`Wire transfer ID`} help="" tooltip={i18n`unique identifier of the wire transfer used by the exchange, must be 52 characters long`} /> + <Input<Entity> name="exchange_url" + label={i18n`Exchange URL`} + tooltip={i18n`Base URL of the exchange that made the transfer, should have been in the wire transfer subject`} + help="http://exchange.taler:8081/" /> + <InputCurrency<Entity> name="credit_amount" label={i18n`Amount credited`} tooltip={i18n`Actual amount that was wired to the merchant's bank account`} /> + + </FormProvider> + + <div class="buttons is-right mt-5"> + {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} + <AsyncButton disabled={hasErrors} data-tooltip={ + hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' + } onClick={submitForm} ><Translate>Confirm</Translate></AsyncButton> + </div> + + </div> + <div class="column" /> + </div> + </section> + </div> +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx new file mode 100644 index 000000000..d95929a0e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx @@ -0,0 +1,60 @@ +/* + 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 { Fragment, h, VNode } from 'preact'; +import { useState } from 'preact/hooks'; +import { NotificationCard } from '../../../../components/menu'; +import { MerchantBackend } from '../../../../declaration'; +import { useInstanceDetails } from '../../../../hooks/instance'; +import { useTransferAPI } from '../../../../hooks/transfer'; +import { useTranslator } from '../../../../i18n'; +import { Notification } from '../../../../utils/types'; +import { CreatePage } from './CreatePage'; + +export type Entity = MerchantBackend.Transfers.TransferInformation +interface Props { + onBack?: () => void; + onConfirm: () => void; +} + +export default function CreateTransfer({onConfirm, onBack}:Props): VNode { + const { informTransfer } = useTransferAPI() + const [notif, setNotif] = useState<Notification | undefined>(undefined) + const i18n = useTranslator() + const instance = useInstanceDetails() + const accounts = !instance.ok ? [] : instance.data.accounts.map(a => a.payto_uri) + + return <> + <NotificationCard notification={notif} /> + <CreatePage + onBack={onBack} + accounts={accounts} + onCreate={(request: MerchantBackend.Transfers.TransferInformation) => { + return informTransfer(request).then(() => onConfirm()).catch((error) => { + setNotif({ + message: i18n`could not inform transfer`, + type: "ERROR", + description: error.message + }) + }) + }} /> + </> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx new file mode 100644 index 000000000..24a791187 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx @@ -0,0 +1,93 @@ +/* + 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, FunctionalComponent } from "preact"; +import { ListPage as TestedComponent } from "./ListPage"; + +export default { + title: "Pages/Transfer/List", + component: TestedComponent, + argTypes: { + onCreate: { action: "onCreate" }, + onDelete: { action: "onDelete" }, + onLoadMoreBefore: { action: "onLoadMoreBefore" }, + onLoadMoreAfter: { action: "onLoadMoreAfter" }, + onShowAll: { action: "onShowAll" }, + onShowVerified: { action: "onShowVerified" }, + onShowUnverified: { action: "onShowUnverified" }, + onChangePayTo: { action: "onChangePayTo" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const Example = createExample(TestedComponent, { + transfers: [ + { + exchange_url: "http://exchange.url/", + credit_amount: "TESTKUDOS:10", + payto_uri: "payto//x-taler-bank/bank:8080/account", + transfer_serial_id: 123123123, + wtid: "!@KJELQKWEJ!L@K#!J@", + confirmed: true, + execution_time: { + t_s: new Date().getTime() / 1000, + }, + verified: false, + }, + { + exchange_url: "http://exchange.url/", + credit_amount: "TESTKUDOS:10", + payto_uri: "payto//x-taler-bank/bank:8080/account", + transfer_serial_id: 123123123, + wtid: "!@KJELQKWEJ!L@K#!J@", + confirmed: true, + execution_time: { + t_s: new Date().getTime() / 1000, + }, + verified: false, + }, + { + exchange_url: "http://exchange.url/", + credit_amount: "TESTKUDOS:10", + payto_uri: "payto//x-taler-bank/bank:8080/account", + transfer_serial_id: 123123123, + wtid: "!@KJELQKWEJ!L@K#!J@", + confirmed: true, + execution_time: { + t_s: new Date().getTime() / 1000, + }, + verified: false, + }, + ], + accounts: ["payto://x-taler-bank/bank/some_account"], +}); +export const Empty = createExample(TestedComponent, { + transfers: [], + accounts: [], +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx new file mode 100644 index 000000000..544a720b8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx @@ -0,0 +1,89 @@ +/* + 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 { FormProvider } from '../../../../components/form/FormProvider'; +import { InputSelector } from '../../../../components/form/InputSelector'; +import { MerchantBackend } from '../../../../declaration'; +import { Translate, useTranslator } from '../../../../i18n'; +import { CardTable } from './Table'; + +export interface Props { + transfers: MerchantBackend.Transfers.TransferDetails[]; + onLoadMoreBefore?: () => void; + onLoadMoreAfter?: () => void; + onShowAll: () => void; + onShowVerified: () => void; + onShowUnverified: () => void; + isVerifiedTransfers?: boolean; + isNonVerifiedTransfers?: boolean; + isAllTransfers?: boolean; + accounts: string[]; + onChangePayTo: (p?: string) => void; + payTo?: string; + onCreate: () => void; + onDelete: () => void; +} + +export function ListPage({ payTo, onChangePayTo, transfers, onCreate, onDelete, accounts, onLoadMoreBefore, onLoadMoreAfter, isAllTransfers, isNonVerifiedTransfers, isVerifiedTransfers, onShowAll, onShowUnverified, onShowVerified }: Props): VNode { + const form = { payto_uri: payTo } + + const i18n = useTranslator(); + return <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-10"> + <FormProvider object={form} valueHandler={(updater) => onChangePayTo(updater(form).payto_uri)}> + <InputSelector name="payto_uri" label={i18n`Address`} + values={accounts} + placeholder={i18n`Select one account`} + tooltip={i18n`filter by account address`} /> + </FormProvider> + </div> + <div class="column" /> + </div> + <div class="tabs"> + <ul> + <li class={isAllTransfers ? 'is-active' : ''}> + <div class="has-tooltip-right" data-tooltip={i18n`remove all filters`}> + <a onClick={onShowAll}><Translate>All</Translate></a> + </div> + </li> + <li class={isVerifiedTransfers ? 'is-active' : ''}> + <div class="has-tooltip-right" data-tooltip={i18n`only show wire transfers confirmed by the merchant`}> + <a onClick={onShowVerified}><Translate>Verified</Translate></a> + </div> + </li> + <li class={isNonVerifiedTransfers ? 'is-active' : ''}> + <div class="has-tooltip-right" data-tooltip={i18n`only show wire transfers claimed by the exchange`}> + <a onClick={onShowUnverified}><Translate>Unverified</Translate></a> + </div> + </li> + </ul> + </div> + <CardTable transfers={transfers.map(o => ({ ...o, id: String(o.transfer_serial_id) }))} + accounts={accounts} + onCreate={onCreate} + onDelete={onDelete} + onLoadMoreBefore={onLoadMoreBefore} hasMoreBefore={!onLoadMoreBefore} + onLoadMoreAfter={onLoadMoreAfter} hasMoreAfter={!onLoadMoreAfter} /> + </section>; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx new file mode 100644 index 000000000..4cb04694d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx @@ -0,0 +1,225 @@ +/* + 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 { format } from "date-fns"; +import { h, VNode } from "preact"; +import { StateUpdater, useState } from "preact/hooks"; +import { MerchantBackend, WithId } from "../../../../declaration"; +import { Translate, useTranslator } from "../../../../i18n"; + +type Entity = MerchantBackend.Transfers.TransferDetails & WithId; + +interface Props { + transfers: Entity[]; + onDelete: (id: Entity) => void; + onCreate: () => void; + accounts: string[]; + onLoadMoreBefore?: () => void; + hasMoreBefore?: boolean; + hasMoreAfter?: boolean; + onLoadMoreAfter?: () => void; +} + +export function CardTable({ + transfers, + onCreate, + onDelete, + onLoadMoreAfter, + onLoadMoreBefore, + hasMoreAfter, + hasMoreBefore, +}: Props): VNode { + const [rowSelection, rowSelectionHandler] = useState<string[]>([]); + + 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-bank" /> + </span> + <Translate>Transfers</Translate> + </p> + <div class="card-header-icon" aria-label="more options"> + <span class="has-tooltip-left" data-tooltip={i18n`add new transfer`}> + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small"> + <i class="mdi mdi-plus mdi-36px" /> + </span> + </button> + </span> + </div> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {transfers.length > 0 ? ( + <Table + instances={transfers} + onDelete={onDelete} + rowSelection={rowSelection} + rowSelectionHandler={rowSelectionHandler} + onLoadMoreAfter={onLoadMoreAfter} + onLoadMoreBefore={onLoadMoreBefore} + hasMoreAfter={hasMoreAfter} + hasMoreBefore={hasMoreBefore} + /> + ) : ( + <EmptyTable /> + )} + </div> + </div> + </div> + </div> + ); +} +interface TableProps { + rowSelection: string[]; + instances: Entity[]; + onDelete: (id: Entity) => void; + rowSelectionHandler: StateUpdater<string[]>; + onLoadMoreBefore?: () => void; + hasMoreBefore?: boolean; + hasMoreAfter?: boolean; + onLoadMoreAfter?: () => 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, + onLoadMoreAfter, + onDelete, + onLoadMoreBefore, + hasMoreAfter, + hasMoreBefore, +}: TableProps): VNode { + const i18n = useTranslator(); + return ( + <div class="table-container"> + {onLoadMoreBefore && ( + <button + class="button is-fullwidth" + data-tooltip={i18n`load more transfers before the first one`} + disabled={!hasMoreBefore} + onClick={onLoadMoreBefore} + > + <Translate>load newer transfers</Translate> + </button> + )} + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <Translate>ID</Translate> + </th> + <th> + <Translate>Credit</Translate> + </th> + <th> + <Translate>Address</Translate> + </th> + <th> + <Translate>Exchange URL</Translate> + </th> + <th> + <Translate>Confirmed</Translate> + </th> + <th> + <Translate>Verified</Translate> + </th> + <th> + <Translate>Executed at</Translate> + </th> + <th /> + </tr> + </thead> + <tbody> + {instances.map((i) => { + return ( + <tr key={i.id}> + <td>{i.id}</td> + <td>{i.credit_amount}</td> + <td>{i.payto_uri}</td> + <td>{i.exchange_url}</td> + <td>{i.confirmed ? i18n`yes` : i18n`no`}</td> + <td>{i.verified ? i18n`yes` : i18n`no`}</td> + <td> + {i.execution_time + ? i.execution_time.t_s == "never" + ? i18n`never` + : format( + i.execution_time.t_s * 1000, + "yyyy/MM/dd HH:mm:ss" + ) + : i18n`unknown`} + </td> + <td> + {i.verified === undefined ? ( + <button + class="button is-danger is-small has-tooltip-left" + data-tooltip={i18n`delete selected transfer from the database`} + onClick={() => onDelete(i)} + > + Delete + </button> + ) : undefined} + </td> + </tr> + ); + })} + </tbody> + </table> + {onLoadMoreAfter && ( + <button + class="button is-fullwidth" + data-tooltip={i18n`load more transfer after the last one`} + disabled={!hasMoreAfter} + onClick={onLoadMoreAfter} + > + <Translate>load older transfers</Translate> + </button> + )} + </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 transfer yet, add more pressing the + sign + </Translate> + </p> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx new file mode 100644 index 000000000..d8e2f60e9 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx @@ -0,0 +1,85 @@ +/* + 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 { useState } from 'preact/hooks'; +import { Loading } from '../../../../components/exception/loading'; +import { MerchantBackend } from '../../../../declaration'; +import { HttpError } from '../../../../hooks/backend'; +import { useInstanceDetails } from '../../../../hooks/instance'; +import { useInstanceTransfers } from "../../../../hooks/transfer"; +import { ListPage } from './ListPage'; + +interface Props { + onUnauthorized: () => VNode; + onLoadError: (error: HttpError) => VNode; + onNotFound: () => VNode; + onCreate: () => void; +} +interface Form { + verified?: 'yes' | 'no'; + payto_uri?: string; +} + +export default function ListTransfer({ onUnauthorized, onLoadError, onCreate, onNotFound }: Props): VNode { + const [form, setForm] = useState<Form>({ payto_uri: '' }) + const setFilter = (s?: 'yes' | 'no') => setForm({ ...form, verified: s }) + + const [position, setPosition] = useState<string | undefined>(undefined) + + const instance = useInstanceDetails() + const accounts = !instance.ok ? [] : instance.data.accounts.map(a => a.payto_uri) + + const isVerifiedTransfers = form.verified === 'yes' + const isNonVerifiedTransfers = form.verified === 'no' + const isAllTransfers = form.verified === undefined + + const result = useInstanceTransfers({ + position, + payto_uri: form.payto_uri === '' ? undefined : form.payto_uri, + verified: form.verified, + }, (id) => setPosition(id)) + + if (result.clientError && result.isUnauthorized) return onUnauthorized() + if (result.clientError && result.isNotfound) return onNotFound() + if (result.loading) return <Loading /> + if (!result.ok) return onLoadError(result) + + return <ListPage + accounts={accounts} + transfers={result.data.transfers} + onLoadMoreBefore={result.isReachingStart ? result.loadMorePrev : undefined} + onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined} + onCreate={onCreate} + onDelete={() => {null}} + // position={position} setPosition={setPosition} + onShowAll={() => setFilter(undefined)} + onShowUnverified={() => setFilter('no')} + onShowVerified={() => setFilter('yes')} + isAllTransfers={isAllTransfers} + isVerifiedTransfers={isVerifiedTransfers} + isNonVerifiedTransfers={isNonVerifiedTransfers} + payTo={form.payto_uri} + onChangePayTo={(p) => setForm(v => ({ ...v, payto_uri: p }))} + /> + +} + diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/update/index.tsx new file mode 100644 index 000000000..caa808693 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/update/index.tsx @@ -0,0 +1,26 @@ +/* + 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'; + +export default function UpdateTransfer():VNode { + return <div>order transfer page</div> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx new file mode 100644 index 000000000..3239d9c5c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx @@ -0,0 +1,61 @@ +/* + 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, FunctionalComponent } from "preact"; +import { UpdatePage as TestedComponent } from "./UpdatePage"; + +export default { + title: "Pages/Instance/Update", + component: TestedComponent, + argTypes: { + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, + }, +}; + +function createExample<Props>( + Component: FunctionalComponent<Props>, + props: Partial<Props> +) { + const r = (args: any) => <Component {...args} />; + r.args = props; + return r; +} + +export const Example = createExample(TestedComponent, { + selected: { + accounts: [], + name: "name", + auth: { method: "external" }, + address: {}, + jurisdiction: {}, + default_max_deposit_fee: "TESTKUDOS:2", + default_max_wire_fee: "TESTKUDOS:1", + default_pay_delay: { + d_us: 1000000, + }, + default_wire_fee_amortization: 1, + default_wire_transfer_delay: { + d_us: 100000, + }, + merchant_pub: "ASDWQEKASJDKSADJ", + }, +}); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx new file mode 100644 index 000000000..4c7a51121 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx @@ -0,0 +1,259 @@ +/* + 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 { useState } from "preact/hooks"; +import * as yup from "yup"; +import { AsyncButton } from "../../../components/exception/AsyncButton"; +import { + FormProvider, + FormErrors, +} from "../../../components/form/FormProvider"; +import { UpdateTokenModal } from "../../../components/modal"; +import { useInstanceContext } from "../../../context/instance"; +import { MerchantBackend } from "../../../declaration"; +import { Translate, useTranslator } from "../../../i18n"; +import { InstanceUpdateSchema as schema } from "../../../schemas"; +import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields"; +import { PAYTO_REGEX } from "../../../utils/constants"; +import { Amounts } from "@gnu-taler/taler-util"; + +type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { + auth_token?: string; +}; + +//MerchantBackend.Instances.InstanceAuthConfigurationMessage +interface Props { + onUpdate: (d: Entity) => void; + onChangeAuth: ( + d: MerchantBackend.Instances.InstanceAuthConfigurationMessage + ) => Promise<void>; + selected: MerchantBackend.Instances.QueryInstancesResponse; + isLoading: boolean; + onBack: () => void; +} + +function convert( + from: MerchantBackend.Instances.QueryInstancesResponse +): Entity { + const { accounts, ...rest } = from; + const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri); + const defaults = { + default_wire_fee_amortization: 1, + default_pay_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 }, //two hours + default_wire_transfer_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 * 2 }, //two hours + }; + return { ...defaults, ...rest, payto_uris }; +} + +function getTokenValuePart(t?: string): string | undefined { + if (!t) return t; + const match = /secret-token:(.*)/.exec(t); + if (!match || !match[1]) return undefined; + return match[1]; +} + +function undefinedIfEmpty<T>(obj: T): T | undefined { + return Object.keys(obj).some((k) => (obj as any)[k] !== undefined) + ? obj + : undefined; +} + +export function UpdatePage({ + onUpdate, + onChangeAuth, + selected, + onBack, +}: Props): VNode { + const { id, token } = useInstanceContext(); + const currentTokenValue = getTokenValuePart(token); + + function updateToken(token: string | undefined | null) { + const value = + token && token.startsWith("secret-token:") + ? token.substring("secret-token:".length) + : token; + + if (!token) { + onChangeAuth({ method: "external" }); + } else { + onChangeAuth({ method: "token", token: `secret-token:${value}` }); + } + } + + const [value, valueHandler] = useState<Partial<Entity>>(convert(selected)); + + const i18n = useTranslator(); + + 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) => { + return !PAYTO_REGEX.test(p) ? i18n`is not valid` : undefined; + }) + ), + default_max_deposit_fee: !value.default_max_deposit_fee + ? i18n`required` + : !Amounts.parse(value.default_max_deposit_fee) + ? i18n`invalid format` + : undefined, + default_max_wire_fee: !value.default_max_wire_fee + ? i18n`required` + : !Amounts.parse(value.default_max_wire_fee) + ? i18n`invalid format` + : undefined, + default_wire_fee_amortization: + value.default_wire_fee_amortization === undefined + ? i18n`required` + : isNaN(value.default_wire_fee_amortization) + ? i18n`is not a number` + : value.default_wire_fee_amortization < 1 + ? i18n`must be 1 or greater` + : undefined, + default_pay_delay: !value.default_pay_delay ? i18n`required` : undefined, + default_wire_transfer_delay: !value.default_wire_transfer_delay + ? i18n`required` + : undefined, + address: undefinedIfEmpty({ + address_lines: + value.address?.address_lines && value.address?.address_lines.length > 7 + ? i18n`max 7 lines` + : undefined, + }), + jurisdiction: undefinedIfEmpty({ + address_lines: + value.address?.address_lines && value.address?.address_lines.length > 7 + ? i18n`max 7 lines` + : undefined, + }), + }; + + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined + ); + const submit = async (): Promise<void> => { + await onUpdate(value as Entity); + }; + const [active, setActive] = useState(false); + + return ( + <div> + <section class="section"> + <section class="hero is-hero-bar"> + <div class="hero-body"> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <span class="is-size-4"> + <Translate>Instance id</Translate>: <b>{id}</b> + </span> + </div> + </div> + <div class="level-right"> + <div class="level-item"> + <h1 class="title"> + <button + class="button is-danger" + data-tooltip={i18n`Change the authorization method use for this instance.`} + onClick={(): void => { + setActive(!active); + }} + > + <div class="icon is-left"> + <i class="mdi mdi-lock-reset" /> + </div> + <span> + <Translate>Manage access token</Translate> + </span> + </button> + </h1> + </div> + </div> + </div> + </div> + </section> + + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + {active && ( + <UpdateTokenModal + oldToken={currentTokenValue} + onCancel={() => { + setActive(false); + }} + onClear={() => { + updateToken(null); + setActive(false); + }} + onConfirm={(newToken) => { + updateToken(newToken); + setActive(false); + }} + /> + )} + </div> + <div class="column" /> + </div> + <hr /> + + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + <FormProvider<Entity> + errors={errors} + object={value} + valueHandler={valueHandler} + > + <DefaultInstanceFormFields showId={false} /> + </FormProvider> + + <div class="buttons is-right mt-4"> + <button + class="button" + onClick={onBack} + data-tooltip="cancel operation" + > + <Translate>Cancel</Translate> + </button> + + <AsyncButton + onClick={submit} + data-tooltip={ + hasErrors + ? i18n`Need to complete marked fields` + : "confirm operation" + } + disabled={hasErrors} + > + <Translate>Confirm</Translate> + </AsyncButton> + </div> + </div> + <div class="column" /> + </div> + </section> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx new file mode 100644 index 000000000..bd5f4c727 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx @@ -0,0 +1,113 @@ +/* + 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/> + */ +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Loading } from "../../../components/exception/loading"; +import { NotificationCard } from "../../../components/menu"; +import { useInstanceContext } from "../../../context/instance"; +import { MerchantBackend } from "../../../declaration"; +import { HttpError, HttpResponse } from "../../../hooks/backend"; +import { + useInstanceAPI, + useInstanceDetails, + useManagedInstanceDetails, + useManagementAPI, +} from "../../../hooks/instance"; +import { useTranslator } from "../../../i18n"; +import { Notification } from "../../../utils/types"; +import { UpdatePage } from "./UpdatePage"; + +export interface Props { + onBack: () => void; + onConfirm: () => void; + + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onLoadError: (e: HttpError) => VNode; + onUpdateError: (e: HttpError) => void; +} + +export default function Update(props: Props): VNode { + const { updateInstance, clearToken, setNewToken } = useInstanceAPI(); + const result = useInstanceDetails(); + return CommonUpdate(props, result, updateInstance, clearToken, setNewToken); +} + +export function AdminUpdate(props: Props & { instanceId: string }): VNode { + const { updateInstance, clearToken, setNewToken } = useManagementAPI( + props.instanceId + ); + const result = useManagedInstanceDetails(props.instanceId); + return CommonUpdate(props, result, updateInstance, clearToken, setNewToken); +} + +function CommonUpdate( + { + onBack, + onConfirm, + onLoadError, + onNotFound, + onUpdateError, + onUnauthorized, + }: Props, + result: HttpResponse<MerchantBackend.Instances.QueryInstancesResponse>, + updateInstance: any, + clearToken: any, + setNewToken: any +): VNode { + const { changeToken } = useInstanceContext(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); + const i18n = useTranslator(); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + + return ( + <Fragment> + <NotificationCard notification={notif} /> + <UpdatePage + onBack={onBack} + isLoading={false} + selected={result.data} + onUpdate={( + d: MerchantBackend.Instances.InstanceReconfigurationMessage + ): Promise<void> => { + return updateInstance(d) + .then(onConfirm) + .catch((error: Error) => + setNotif({ + message: i18n`Failed to create instance`, + type: "ERROR", + description: error.message, + }) + ); + }} + onChangeAuth={( + d: MerchantBackend.Instances.InstanceAuthConfigurationMessage + ): Promise<void> => { + const apiCall = + d.method === "external" ? clearToken() : setNewToken(d.token!); + return apiCall + .then(() => changeToken(d.token)) + .then(onConfirm) + .catch(onUpdateError); + }} + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx new file mode 100644 index 000000000..acad7fe0b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/login/index.tsx @@ -0,0 +1,29 @@ +/* + 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 { LoginModal } from '../../components/exception/login'; + +interface Props { + onConfirm: (url: string, token?: string) => void; +} +export default function LoginPage({ onConfirm }: Props): VNode { + return <LoginModal onConfirm={onConfirm} /> +}
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx b/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx new file mode 100644 index 000000000..10c3fac25 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx @@ -0,0 +1,36 @@ +/* + 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 { Link } from 'preact-router/match'; + +export default function NotFoundPage(): VNode { + return ( + <div> + <h1>Error 404</h1> + <p>That page doesn't exist.</p> + <Link href="/"> + <h4>Back to Home</h4> + </Link> + </div> + ); +} + diff --git a/packages/merchant-backoffice-ui/src/schemas/index.ts b/packages/merchant-backoffice-ui/src/schemas/index.ts new file mode 100644 index 000000000..00e80199a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/schemas/index.ts @@ -0,0 +1,202 @@ +/* + 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 { isAfter, isFuture } from 'date-fns'; +import * as yup from 'yup'; +import { AMOUNT_REGEX, PAYTO_REGEX } from "../utils/constants"; + +yup.setLocale({ + mixed: { + default: 'field_invalid', + }, + number: { + min: ({ min }: any) => ({ key: 'field_too_short', values: { min } }), + max: ({ max }: any) => ({ key: 'field_too_big', values: { max } }), + }, +}); + +function listOfPayToUrisAreValid(values?: (string | undefined)[]): boolean { + return !!values && values.every(v => v && PAYTO_REGEX.test(v)); +} + +function currencyWithAmountIsValid(value?: string): boolean { + return !!value && AMOUNT_REGEX.test(value) +} +function currencyGreaterThan0(value?: string) { + if (value) { + try { + const [, amount] = value.split(':') + const intAmount = parseInt(amount, 10) + return intAmount > 0 + } catch { + return false + } + } + return true +} + +export const InstanceSchema = yup.object().shape({ + id: yup.string().required().meta({ type: 'url' }), + name: yup.string().required(), + auth: yup.object().shape({ + method: yup.string().matches(/^(external|token)$/), + token: yup.string().optional().nullable(), + }), + payto_uris: yup.array().of(yup.string()) + .min(1) + .meta({ type: 'array' }) + .test('payto', '{path} is not valid', listOfPayToUrisAreValid), + default_max_deposit_fee: yup.string() + .required() + .test('amount', 'the amount is not valid', currencyWithAmountIsValid) + .meta({ type: 'amount' }), + default_max_wire_fee: yup.string() + .required() + .test('amount', '{path} is not valid', currencyWithAmountIsValid) + .meta({ type: 'amount' }), + default_wire_fee_amortization: yup.number() + .required(), + address: yup.object().shape({ + country: yup.string().optional(), + address_lines: yup.array().of(yup.string()).max(7).optional(), + building_number: yup.string().optional(), + building_name: yup.string().optional(), + street: yup.string().optional(), + post_code: yup.string().optional(), + town_location: yup.string().optional(), + town: yup.string(), + district: yup.string().optional(), + country_subdivision: yup.string().optional(), + }).meta({ type: 'group' }), + jurisdiction: yup.object().shape({ + country: yup.string().optional(), + address_lines: yup.array().of(yup.string()).max(7).optional(), + building_number: yup.string().optional(), + building_name: yup.string().optional(), + street: yup.string().optional(), + post_code: yup.string().optional(), + town_location: yup.string().optional(), + town: yup.string(), + district: yup.string().optional(), + country_subdivision: yup.string().optional(), + }).meta({ type: 'group' }), + // default_pay_delay: yup.object() + // .shape({ d_us: yup.number() }) + // .required() + // .meta({ type: 'duration' }), + // .transform(numberToDuration), + default_wire_transfer_delay: yup.object() + .shape({ d_us: yup.number() }) + .required() + .meta({ type: 'duration' }), + // .transform(numberToDuration), +}) + +export const InstanceUpdateSchema = InstanceSchema.clone().omit(['id']); +export const InstanceCreateSchema = InstanceSchema.clone(); + +export const AuthorizeTipSchema = yup.object().shape({ + justification: yup.string().required(), + amount: yup.string() + .required() + .test('amount', 'the amount is not valid', currencyWithAmountIsValid) + .test('amount_positive', 'the amount is not valid', currencyGreaterThan0), + next_url: yup.string().required(), +}) + +const stringIsValidJSON = (value?: string) => { + const p = value?.trim() + if (!p) return true; + try { + JSON.parse(p) + return true + } catch { + return false + } +} + +export const OrderCreateSchema = yup.object().shape({ + pricing: yup.object().required().shape({ + summary: yup.string().ensure().required(), + order_price: yup.string() + .ensure() + .required() + .test('amount', 'the amount is not valid', currencyWithAmountIsValid) + .test('amount_positive', 'the amount should be greater than 0', currencyGreaterThan0), + }), + extra: yup.string().test('extra', 'is not a JSON format', stringIsValidJSON), + payments: yup.object().required().shape({ + refund_deadline: yup.date() + .test('future', 'should be in the future', (d) => d ? isFuture(d) : true), + pay_deadline: yup.date() + .test('future', 'should be in the future', (d) => d ? isFuture(d) : true), + auto_refund_deadline: yup.date() + .test('future', 'should be in the future', (d) => d ? isFuture(d) : true), + delivery_date: yup.date() + .test('future', 'should be in the future', (d) => d ? isFuture(d) : true), + }).test('payment', 'dates', (d) => { + if (d.pay_deadline && d.refund_deadline && isAfter(d.refund_deadline, d.pay_deadline)) { + return new yup.ValidationError('pay deadline should be greater than refund', 'asd', 'payments.pay_deadline') + } + return true + }) +}) + +export const ProductCreateSchema = yup.object().shape({ + product_id: yup.string().ensure().required(), + description: yup.string().required(), + unit: yup.string().ensure().required(), + price: yup.string() + .required() + .test('amount', 'the amount is not valid', currencyWithAmountIsValid), + stock: yup.object({ + + }).optional(), + minimum_age: yup.number().optional().min(0), +}) + +export const ProductUpdateSchema = yup.object().shape({ + description: yup.string().required(), + price: yup.string() + .required() + .test('amount', 'the amount is not valid', currencyWithAmountIsValid), + stock: yup.object({ + + }).optional(), + minimum_age: yup.number().optional().min(0), +}) + + +export const TaxSchema = yup.object().shape({ + name: yup.string().required().ensure(), + tax: yup.string() + .required() + .test('amount', 'the amount is not valid', currencyWithAmountIsValid), +}) + +export const NonInventoryProductSchema = yup.object().shape({ + quantity: yup.number().required().positive(), + description: yup.string().required(), + unit: yup.string().ensure().required(), + price: yup.string() + .required() + .test('amount', 'the amount is not valid', currencyWithAmountIsValid), +}) diff --git a/packages/merchant-backoffice-ui/src/scss/DurationPicker.scss b/packages/merchant-backoffice-ui/src/scss/DurationPicker.scss new file mode 100644 index 000000000..a35575324 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/DurationPicker.scss @@ -0,0 +1,71 @@ + +.rdp-picker { + display: flex; + height: 175px; +} + +@media (max-width: 400px) { + .rdp-picker { + width: 250px; + } +} + +.rdp-masked-div { + overflow: hidden; + height: 175px; + position: relative; +} + +.rdp-column-container { + flex-grow: 1; + display: inline-block; +} + +.rdp-column { + position: absolute; + z-index: 0; + width: 100%; +} + +.rdp-reticule { + border: 0; + border-top: 2px solid rgba(109, 202, 236, 1); + height: 2px; + position: absolute; + width: 80%; + margin: 0; + z-index: 100; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} + +.rdp-text-overlay { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + height: 35px; + font-size: 20px; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); +} + +.rdp-cell div { + font-size: 17px; + color: gray; + font-style: italic; +} + +.rdp-cell { + display: flex; + align-items: center; + justify-content: center; + height: 35px; + font-size: 18px; +} + +.rdp-center { + font-size: 25px; +} diff --git a/packages/merchant-backoffice-ui/src/scss/_aside.scss b/packages/merchant-backoffice-ui/src/scss/_aside.scss new file mode 100644 index 000000000..22258acf8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_aside.scss @@ -0,0 +1,179 @@ +/* + 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) + */ + +@include desktop { + html { + &.has-aside-left { + &.has-aside-expanded { + nav.navbar, body { + padding-left: $aside-width; + } + } + aside.is-placed-left { + display: block; + } + } + } + + aside.aside.is-expanded { + width: $aside-width; + + .menu-list { + @include icon-with-update-mark($aside-icon-width); + + span.menu-item-label { + display: inline-block; + } + + li.is-active { + ul { + display: block; + } + } + } + } +} + +aside.aside { + display: none; + position: fixed; + top: 0; + left: 0; + z-index: 40; + height: 100vh; + padding: 0; + box-shadow: $aside-box-shadow; + background: $aside-background-color; + + .aside-tools { + display: flex; + flex-direction: row; + width: 100%; + background-color: $aside-tools-background-color; + color: $aside-tools-color; + line-height: $navbar-height; + height: $navbar-height; + padding-left: $default-padding * .5; + flex: 1; + + .icon { + margin-right: $default-padding * .5; + } + } + + .menu-list { + li { + a { + &.has-dropdown-icon { + position: relative; + padding-right: $aside-icon-width; + + .dropdown-icon { + position: absolute; + top: $size-base * .5; + right: 0; + } + } + } + ul { + display: none; + border-left: 0; + background-color: darken($base-color, 2.5%); + padding-left: 0; + margin: 0 0 $default-padding * .5; + + li { + a { + padding: $default-padding * .5 0 $default-padding * .5 $default-padding * .5; + font-size: $aside-submenu-font-size; + + &.has-icon { + padding-left: 0; + } + &.is-active { + &:not(:hover) { + background: transparent; + } + } + } + } + } + } + } + + .menu-label { + padding: 0 $default-padding * .5; + margin-top: $default-padding * .5; + margin-bottom: $default-padding * .5; + } + +} + +@include touch { + nav.navbar { + @include transition(margin-left); + } + aside.aside { + @include transition(left); + } + html.has-aside-mobile-transition { + body { + overflow-x: hidden; + } + body, nav.navbar { + width: 100vw; + } + aside.aside { + width: $aside-mobile-width; + display: block; + left: $aside-mobile-width * -1; + + .image { + img { + max-width: $aside-mobile-width * .33; + } + } + + .menu-list { + li.is-active { + ul { + display: block; + } + } + a { + @include icon-with-update-mark($aside-icon-width); + + span.menu-item-label { + display: inline-block; + } + } + } + } + } + div.has-aside-mobile-expanded { + nav.navbar { + margin-left: $aside-mobile-width; + } + aside.aside { + left: 0; + } + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_card.scss b/packages/merchant-backoffice-ui/src/scss/_card.scss new file mode 100644 index 000000000..b2eec27a1 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_card.scss @@ -0,0 +1,69 @@ +/* + 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) + */ + +.card:not(:last-child) { + margin-bottom: $default-padding; +} + +.card { + border-radius: $radius-large; + border: $card-border; + + &.has-table { + .card-content { + padding: 0; + } + .b-table { + border-radius: $radius-large; + overflow: hidden; + } + } + + &.is-card-widget { + .card-content { + padding: $default-padding * .5; + } + } + + .card-header { + border-bottom: 1px solid $base-color-light; + } + + .card-content { + hr { + margin-left: $card-content-padding * -1; + margin-right: $card-content-padding * -1; + } + } + + .is-widget-icon { + .icon { + width: 5rem; + height: 5rem; + } + } + + .is-widget-label { + .subtitle { + color: $grey; + } + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_custom-calendar.scss b/packages/merchant-backoffice-ui/src/scss/_custom-calendar.scss new file mode 100644 index 000000000..9ac877ce0 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_custom-calendar.scss @@ -0,0 +1,254 @@ +/* + 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/> + */ + +:root { + --primary-color: #3298dc; + + --primary-text-color-dark: rgba(0,0,0,.87); + --secondary-text-color-dark: rgba(0,0,0,.57); + --disabled-text-color-dark: rgba(0,0,0,.13); + + --primary-text-color-light: rgba(255,255,255,.87); + --secondary-text-color-light: rgba(255,255,255,.57); + --disabled-text-color-light: rgba(255,255,255,.13); + + --font-stack: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; + + --primary-card-color: #fff; + --primary-background-color: #f2f2f2; + + --box-shadow-lvl-1: 0 1px 3px rgba(0, 0, 0, 0.12), + 0 1px 2px rgba(0, 0, 0, 0.24); + --box-shadow-lvl-2: 0 3px 6px rgba(0, 0, 0, 0.16), + 0 3px 6px rgba(0, 0, 0, 0.23); + --box-shadow-lvl-3: 0 10px 20px rgba(0, 0, 0, 0.19), + 0 6px 6px rgba(0, 0, 0, 0.23); + --box-shadow-lvl-4: 0 14px 28px rgba(0, 0, 0, 0.25), + 0 10px 10px rgba(0, 0, 0, 0.22); +} + + +.datePicker { + text-align: left; + background: var(--primary-card-color); + border-radius: 3px; + z-index: 200; + position: fixed; + height: auto; + max-height: 90vh; + width: 90vw; + max-width: 448px; + transform-origin: top left; + transition: transform .22s ease-in-out, opacity .22s ease-in-out; + top: 50%; + left: 50%; + opacity: 0; + transform: scale(0) translate(-50%, -50%); + user-select: none; + + &.datePicker--opened { + opacity: 1; + transform: scale(1) translate(-50%, -50%); + } + + .datePicker--titles { + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 24px; + height: 100px; + background: var(--primary-color); + + h2, h3 { + cursor: pointer; + color: #fff; + line-height: 1; + padding: 0; + margin: 0; + font-size: 32px; + } + + h3 { + color: rgba(255,255,255,.57); + font-size: 18px; + padding-bottom: 2px; + } + } + + nav { + padding: 20px; + height: 56px; + + h4 { + width: calc(100% - 60px); + text-align: center; + display: inline-block; + padding: 0; + font-size: 14px; + line-height: 24px; + margin: 0; + position: relative; + top: -9px; + color: var(--primary-text-color); + } + + i { + cursor: pointer; + color: var(--secondary-text-color); + font-size: 26px; + user-select: none; + border-radius: 50%; + + &:hover { + background: var(--disabled-text-color-dark); + } + } + } + + .datePicker--scroll { + overflow-y: auto; + max-height: calc(90vh - 56px - 100px); + } + + .datePicker--calendar { + padding: 0 20px; + + .datePicker--dayNames { + width: 100%; + display: grid; + text-align: center; + + // there's probably a better way to do this, but wanted to try out CSS grid + grid-template-columns: calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7); + + span { + color: var(--secondary-text-color-dark); + font-size: 14px; + line-height: 42px; + display: inline-grid; + } + } + + .datePicker--days { + width: 100%; + display: grid; + text-align: center; + grid-template-columns: calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7); + + span { + color: var(--primary-text-color-dark); + line-height: 42px; + font-size: 14px; + display: inline-grid; + transition: color .22s; + height: 42px; + position: relative; + cursor: pointer; + user-select: none; + border-radius: 50%; + + &::before { + content: ''; + position: absolute; + z-index: -1; + height: 42px; + width: 42px; + left: calc(50% - 21px); + background: var(--primary-color); + border-radius: 50%; + transition: transform .22s, opacity .22s; + transform: scale(0); + opacity: 0; + } + + &[disabled=true] { + cursor: unset; + } + + &.datePicker--today { + font-weight: 700; + } + + &.datePicker--selected { + color: rgba(255,255,255,.87); + + &:before { + transform: scale(1); + opacity: 1; + } + } + } + } + } + + .datePicker--selectYear { + padding: 0 20px; + display: block; + width: 100%; + text-align: center; + max-height: 362px; + + span { + display: block; + width: 100%; + font-size: 24px; + margin: 20px auto; + cursor: pointer; + + &.selected { + font-size: 42px; + color: var(--primary-color); + } + } + } + + div.datePicker--actions { + width: 100%; + padding: 8px; + text-align: right; + + button { + margin-bottom: 0; + font-size: 15px; + cursor: pointer; + color: var(--primary-text-color); + border: none; + margin-left: 8px; + min-width: 64px; + line-height: 36px; + background-color: transparent; + appearance: none; + padding: 0 16px; + border-radius: 3px; + transition: background-color .13s; + + &:hover, &:focus { + outline: none; + background-color: var(--disabled-text-color-dark); + } + } + } +} + +.datePicker--background { + z-index: 199; + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: rgba(0,0,0,.52); + animation: fadeIn .22s forwards; +} diff --git a/packages/merchant-backoffice-ui/src/scss/_footer.scss b/packages/merchant-backoffice-ui/src/scss/_footer.scss new file mode 100644 index 000000000..027a5ca8b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_footer.scss @@ -0,0 +1,35 @@ +/* + 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) + */ + +footer.footer { + .logo { + img { + width: auto; + height: $footer-logo-height; + } + } +} + +@include mobile { + .footer-copyright { + text-align: center; + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_form.scss b/packages/merchant-backoffice-ui/src/scss/_form.scss new file mode 100644 index 000000000..71f0d4da4 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_form.scss @@ -0,0 +1,64 @@ +/* + 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) + */ + +.field { + &.has-check { + .field-body { + margin-top: $default-padding * .125; + } + } + .control { + .mdi-24px.mdi-set, .mdi-24px.mdi:before { + font-size: inherit; + } + } +} +.upload { + .upload-draggable { + display: block; + } +} + +.input, .textarea, select { + box-shadow: none; + + &:focus, &:active { + box-shadow: none!important; + } +} + +.switch input[type=checkbox]+.check:before { + box-shadow: none; +} + +.switch, .b-checkbox.checkbox { + input[type=checkbox] { + &:focus + .check, &:focus:checked + .check { + box-shadow: none!important; + } + } +} + +.b-checkbox.checkbox input[type=checkbox], .b-radio.radio input[type=radio] { + &+.check { + border: $checkbox-border; + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_hero-bar.scss b/packages/merchant-backoffice-ui/src/scss/_hero-bar.scss new file mode 100644 index 000000000..90b67a2ed --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_hero-bar.scss @@ -0,0 +1,55 @@ +/* + 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) + */ + +section.hero.is-hero-bar { + background-color: $hero-bar-background; + border-bottom: $light-border; + + .hero-body { + padding: $default-padding; + + .level-item { + &.is-hero-avatar-item { + margin-right: $default-padding; + } + + > div > .level { + margin-bottom: $default-padding * .5; + } + + .subtitle + p { + margin-top: $default-padding * .5; + } + } + + .button { + &.is-hero-button { + background-color: rgba($white, .5); + font-weight: 300; + @include transition(background-color); + + &:hover { + background-color: $white; + } + } + } + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_loading.scss b/packages/merchant-backoffice-ui/src/scss/_loading.scss new file mode 100644 index 000000000..d25bf8048 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_loading.scss @@ -0,0 +1,51 @@ +/* + 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/> + */ + +.lds-ring { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} +.lds-ring div { + box-sizing: border-box; + display: block; + position: absolute; + width: 64px; + height: 64px; + margin: 8px; + border: 8px solid black; + border-radius: 50%; + animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: black transparent transparent transparent; +} +.lds-ring div:nth-child(1) { + animation-delay: -0.45s; +} +.lds-ring div:nth-child(2) { + animation-delay: -0.3s; +} +.lds-ring div:nth-child(3) { + animation-delay: -0.15s; +} +@keyframes lds-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_main-section.scss b/packages/merchant-backoffice-ui/src/scss/_main-section.scss new file mode 100644 index 000000000..1a4fad81d --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_main-section.scss @@ -0,0 +1,24 @@ +/* + 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) + */ + +section.section.is-main-section { + padding-top: $default-padding; +} diff --git a/packages/merchant-backoffice-ui/src/scss/_misc.scss b/packages/merchant-backoffice-ui/src/scss/_misc.scss new file mode 100644 index 000000000..65bd28dbd --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_misc.scss @@ -0,0 +1,50 @@ +/* + 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) + */ + +.is-user-avatar { + &.has-max-width { + max-width: $size-base * 7; + } + + &.is-aligned-center { + margin: 0 auto; + } + + img { + margin: 0 auto; + border-radius: $radius-rounded; + } +} + +.icon.has-update-mark { + position: relative; + + &:after { + content: ""; + width: $icon-update-mark-size; + height: $icon-update-mark-size; + position: absolute; + top: 1px; + right: 1px; + background-color: $icon-update-mark-color; + border-radius: $radius-rounded; + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_mixins.scss b/packages/merchant-backoffice-ui/src/scss/_mixins.scss new file mode 100644 index 000000000..0809033ed --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_mixins.scss @@ -0,0 +1,34 @@ +/* + 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) + */ + +@mixin transition($t) { + transition: $t 250ms ease-in-out 50ms; +} + +@mixin icon-with-update-mark ($icon-base-width) { + .icon { + width: $icon-base-width; + + &.has-update-mark:after { + right: ($icon-base-width / 2) - .85; + } + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_modal.scss b/packages/merchant-backoffice-ui/src/scss/_modal.scss new file mode 100644 index 000000000..3edbb8d3a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_modal.scss @@ -0,0 +1,35 @@ +/* + 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) + */ + +.modal-card { + width: $modal-card-width; +} + +.modal-card-foot { + background-color: $modal-card-foot-background-color; +} + +@include mobile { + .modal .animation-content .modal-card { + width: $modal-card-width-mobile; + margin: 0 auto; + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_nav-bar.scss b/packages/merchant-backoffice-ui/src/scss/_nav-bar.scss new file mode 100644 index 000000000..09f1e2326 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_nav-bar.scss @@ -0,0 +1,144 @@ +/* + 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) + */ + +nav.navbar { + box-shadow: $navbar-box-shadow; + + .navbar-item { + &.has-user-avatar { + .is-user-avatar { + margin-right: $default-padding * .5; + display: inline-flex; + width: $navbar-avatar-size; + height: $navbar-avatar-size; + } + } + + &.has-divider { + border-right: $navbar-divider-border; + } + + &.no-left-space { + padding-left: 0; + } + + &.has-dropdown { + padding-right: 0; + padding-left: 0; + + .navbar-link { + padding-right: $navbar-item-h-padding; + padding-left: $navbar-item-h-padding; + } + } + + &.has-control { + padding-top: 0; + padding-bottom: 0; + } + + .control { + .input { + color: $navbar-input-color; + border: 0; + box-shadow: none; + background: transparent; + + &::placeholder { + color: $navbar-input-placeholder-color; + } + } + } + } +} + +@include touch { + nav.navbar { + display: flex; + padding-right: 0; + + .navbar-brand { + flex: 1; + + &.is-right { + flex: none; + } + } + + .navbar-item { + &.no-left-space-touch { + padding-left: 0; + } + } + + .navbar-menu { + position: absolute; + width: 100vw; + padding-top: 0; + top: $navbar-height; + left: 0; + + .navbar-item { + .icon:first-child { + margin-right: $default-padding * .5; + } + + &.has-dropdown { + >.navbar-link { + background-color: $white-ter; + .icon:last-child { + display: none; + } + } + } + + &.has-user-avatar { + >.navbar-link { + display: flex; + align-items: center; + padding-top: $default-padding * .5; + padding-bottom: $default-padding * .5; + } + } + } + } + } +} + +@include desktop { + nav.navbar { + .navbar-item { + padding-right: $navbar-item-h-padding; + padding-left: $navbar-item-h-padding; + + &:not(.is-desktop-icon-only) { + .icon:first-child { + margin-right: $default-padding * .5; + } + } + &.is-desktop-icon-only { + span:not(.icon) { + display: none; + } + } + } + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_table.scss b/packages/merchant-backoffice-ui/src/scss/_table.scss new file mode 100644 index 000000000..9cf6f4dcd --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_table.scss @@ -0,0 +1,173 @@ +/* + 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) + */ + +table.table { + thead { + th { + border-bottom-width: 1px; + } + } + + td, th { + &.checkbox-cell { + .b-checkbox.checkbox:not(.button) { + margin-right: 0; + width: 20px; + + .control-label { + display: none; + padding: 0; + } + } + } + } + + td { + .image { + margin: 0 auto; + width: $table-avatar-size; + height: $table-avatar-size; + } + + &.is-progress-col { + min-width: 5rem; + vertical-align: middle; + } + } +} + +.b-table { + .table { + border: 0; + border-radius: 0; + } + + /* This stylizes buefy's pagination */ + .table-wrapper { + margin-bottom: 0; + } + + .table-wrapper + .level { + padding: $notification-padding; + padding-left: $card-content-padding; + padding-right: $card-content-padding; + margin: 0; + border-top: $base-color-light; + background: $notification-background-color; + + .pagination-link { + background: $button-background-color; + color: $button-color; + border-color: $button-border-color; + + &.is-current { + border-color: $button-active-border-color; + } + } + + .pagination-previous, .pagination-next, .pagination-link { + border-color: $button-border-color; + color: $base-color; + + &[disabled] { + background-color: transparent; + } + } + } +} + +@include mobile { + .card { + &.has-table { + .b-table { + .table-wrapper + .level { + .level-left + .level-right { + margin-top: 0; + } + } + } + } + &.has-mobile-sort-spaced { + .b-table { + .field.table-mobile-sort { + padding-top: $default-padding * .5; + } + } + } + } + .b-table { + .field.table-mobile-sort { + padding: 0 $default-padding * .5; + } + + .table-wrapper.has-mobile-cards { + tr { + box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1); + margin-bottom: 3px!important; + } + td { + &.is-progress-col { + span, progress { + display: flex; + width: 45%; + align-items: center; + align-self: center; + } + } + + &.checkbox-cell, &.is-image-cell { + border-bottom: 0!important; + } + + &.checkbox-cell, &.is-actions-cell { + &:before { + display: none; + } + } + + &.has-no-head-mobile { + &:before { + display: none; + } + + span { + display: block; + width: 100%; + } + + &.is-progress-col { + progress { + width: 100%; + } + } + + &.is-image-cell { + .image { + width: $table-avatar-size-mobile; + height: auto; + margin: 0 auto $default-padding * .25; + } + } + } + } + } + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_theme-default.scss b/packages/merchant-backoffice-ui/src/scss/_theme-default.scss new file mode 100644 index 000000000..538dfd4da --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_theme-default.scss @@ -0,0 +1,136 @@ +/* + 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) + */ + +/* We'll need some initial vars to use here */ +@import "node_modules/bulma/sass/utilities/initial-variables"; + +/* Base: Size */ +$size-base: 1rem; +$default-padding: $size-base * 1.5; + +/* Default font */ +$family-sans-serif: "Nunito", sans-serif; + +/* Base color */ +$base-color: #2e323a; +$base-color-light: rgba(24, 28, 33, 0.06); + +/* General overrides */ +$primary: $turquoise; +$body-background-color: #f8f8f8; +$link: $blue; +$link-visited: $purple; +$light-border: 1px solid $base-color-light; +$hr-height: 1px; + +/* NavBar: specifics */ +$navbar-input-color: $grey-darker; +$navbar-input-placeholder-color: $grey-lighter; +$navbar-box-shadow: 0 1px 0 rgba(24, 28, 33, 0.04); +$navbar-divider-border: 1px solid rgba($grey-lighter, 0.25); +$navbar-item-h-padding: $default-padding * 0.75; +$navbar-avatar-size: 1.75rem; + +/* Aside: Bulma override */ +$menu-item-radius: 0; +$menu-list-link-padding: $size-base * 0.5 0; +$menu-label-color: lighten($base-color, 25%); +$menu-item-color: lighten($base-color, 30%); +$menu-item-hover-color: $white; +$menu-item-hover-background-color: darken($base-color, 3.5%); +$menu-item-active-color: $white; +$menu-item-active-background-color: darken($base-color, 2.5%); + +/* Aside: specifics */ +$aside-width: $size-base * 14; +$aside-mobile-width: $size-base * 15; +$aside-icon-width: $size-base * 3; +$aside-submenu-font-size: $size-base * 0.95; +$aside-box-shadow: none; +$aside-background-color: $base-color; +$aside-tools-background-color: darken($aside-background-color, 10%); +$aside-tools-color: $white; + +/* Title Bar: specifics */ +$title-bar-color: $grey; +$title-bar-active-color: $black-ter; + +/* Hero Bar: specifics */ +$hero-bar-background: $white; + +/* Card: Bulma override */ +$card-shadow: none; +$card-header-shadow: none; + +/* Card: specifics */ +$card-border: 1px solid $base-color-light; +$card-header-border-bottom-color: $base-color-light; + +/* Table: Bulma override */ +$table-cell-border: 1px solid $white-bis; + +/* Table: specifics */ +$table-avatar-size: $size-base * 1.5; +$table-avatar-size-mobile: 25vw; + +/* Form */ +$checkbox-border: 1px solid $base-color; + +/* Modal card: Bulma override */ +$modal-card-head-background-color: $white-ter; +$modal-card-title-size: $size-base; +$modal-card-body-padding: $default-padding 20px; +$modal-card-head-border-bottom: 1px solid $white-ter; +$modal-card-foot-border-top: 0; + +/* Modal card: specifics */ +$modal-card-width: 80vw; +$modal-card-width-mobile: 90vw; +$modal-card-foot-background-color: $white-ter; + +/* Notification: Bulma override */ +$notification-padding: $default-padding * 0.75 $default-padding; + +/* Footer: Bulma override */ +$footer-background-color: $white; +$footer-padding: $default-padding * 0.33 $default-padding; + +/* Footer: specifics */ +$footer-logo-height: $size-base * 2; + +/* Progress: Bulma override */ +$progress-bar-background-color: $grey-lighter; + +/* Icon: specifics */ +$icon-update-mark-size: $size-base * 0.5; +$icon-update-mark-color: $yellow; + +$input-disabled-border-color: $grey-lighter; +$table-row-hover-background-color: hsl(0, 0%, 80%); + +.menu-list { + div { + border-radius: $menu-item-radius; + color: $menu-item-color; + display: block; + padding: $menu-list-link-padding; + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/_tiles.scss b/packages/merchant-backoffice-ui/src/scss/_tiles.scss new file mode 100644 index 000000000..94fc04e70 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_tiles.scss @@ -0,0 +1,25 @@ +/* + 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) + */ + + +.is-tiles-wrapper { + margin-bottom: $default-padding; +} diff --git a/packages/merchant-backoffice-ui/src/scss/_title-bar.scss b/packages/merchant-backoffice-ui/src/scss/_title-bar.scss new file mode 100644 index 000000000..736f26cbd --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/_title-bar.scss @@ -0,0 +1,50 @@ +/* + 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) + */ + +section.section.is-title-bar { + padding: $default-padding; + border-bottom: $light-border; + + ul { + li { + display: inline-block; + padding: 0 $default-padding * .5 0 0; + font-size: $default-padding; + color: $title-bar-color; + + &:after { + display: inline-block; + content: '/'; + padding-left: $default-padding * .5; + } + + &:last-child { + padding-right: 0; + font-weight: 900; + color: $title-bar-active-color; + + &:after { + display: none; + } + } + } + } +} diff --git a/packages/merchant-backoffice-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf b/packages/merchant-backoffice-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf Binary files differnew file mode 100644 index 000000000..7665ee336 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf diff --git a/packages/merchant-backoffice-ui/src/scss/fonts/nunito.css b/packages/merchant-backoffice-ui/src/scss/fonts/nunito.css new file mode 100644 index 000000000..ab30db36b --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/fonts/nunito.css @@ -0,0 +1,22 @@ +/* + 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/> + */ + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + src: url(./XRXV3I6Li01BKofINeaE.ttf) format('truetype'); +} diff --git a/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot Binary files differnew file mode 100644 index 000000000..ab6b25ded --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot diff --git a/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf Binary files differnew file mode 100644 index 000000000..824be10fa --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf diff --git a/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff Binary files differnew file mode 100644 index 000000000..7e087c1de --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff diff --git a/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 Binary files differnew file mode 100644 index 000000000..b5caa4ddc --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 diff --git a/packages/merchant-backoffice-ui/src/scss/icons/materialdesignicons-4.9.95.min.css b/packages/merchant-backoffice-ui/src/scss/icons/materialdesignicons-4.9.95.min.css new file mode 100644 index 000000000..24a89d639 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/icons/materialdesignicons-4.9.95.min.css @@ -0,0 +1,3 @@ +@font-face{font-family:"Material Design Icons";src:url("./fonts/materialdesignicons-webfont-4.9.95.eot");src:url("./fonts/materialdesignicons-webfont-4.9.95.woff2") format("woff2"),url("./fonts/materialdesignicons-webfont-4.9.95.woff") format("woff"),url("./fonts/materialdesignicons-webfont-4.9.95.ttf") format("truetype");font-weight:normal;font-style:normal}.mdi:before,.mdi-set{display:inline-block;font:normal normal normal 24px/1 "Material Design Icons";font-size:inherit;text-rendering:auto;line-height:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.mdi-ab-testing::before{content:"\F001C"}.mdi-abjad-arabic::before{content:"\F0353"}.mdi-abjad-hebrew::before{content:"\F0354"}.mdi-abugida-devanagari::before{content:"\F0355"}.mdi-abugida-thai::before{content:"\F0356"}.mdi-access-point::before{content:"\F002"}.mdi-access-point-network::before{content:"\F003"}.mdi-access-point-network-off::before{content:"\FBBD"}.mdi-account::before{content:"\F004"}.mdi-account-alert::before{content:"\F005"}.mdi-account-alert-outline::before{content:"\FB2C"}.mdi-account-arrow-left::before{content:"\FB2D"}.mdi-account-arrow-left-outline::before{content:"\FB2E"}.mdi-account-arrow-right::before{content:"\FB2F"}.mdi-account-arrow-right-outline::before{content:"\FB30"}.mdi-account-badge::before{content:"\FD83"}.mdi-account-badge-alert::before{content:"\FD84"}.mdi-account-badge-alert-outline::before{content:"\FD85"}.mdi-account-badge-horizontal::before{content:"\FDF0"}.mdi-account-badge-horizontal-outline::before{content:"\FDF1"}.mdi-account-badge-outline::before{content:"\FD86"}.mdi-account-box::before{content:"\F006"}.mdi-account-box-multiple::before{content:"\F933"}.mdi-account-box-multiple-outline::before{content:"\F002C"}.mdi-account-box-outline::before{content:"\F007"}.mdi-account-cancel::before{content:"\F030A"}.mdi-account-cancel-outline::before{content:"\F030B"}.mdi-account-card-details::before{content:"\F5D2"}.mdi-account-card-details-outline::before{content:"\FD87"}.mdi-account-cash::before{content:"\F00C2"}.mdi-account-cash-outline::before{content:"\F00C3"}.mdi-account-check::before{content:"\F008"}.mdi-account-check-outline::before{content:"\FBBE"}.mdi-account-child::before{content:"\FA88"}.mdi-account-child-circle::before{content:"\FA89"}.mdi-account-child-outline::before{content:"\F00F3"}.mdi-account-circle::before{content:"\F009"}.mdi-account-circle-outline::before{content:"\FB31"}.mdi-account-clock::before{content:"\FB32"}.mdi-account-clock-outline::before{content:"\FB33"}.mdi-account-cog::before{content:"\F039B"}.mdi-account-cog-outline::before{content:"\F039C"}.mdi-account-convert::before{content:"\F00A"}.mdi-account-convert-outline::before{content:"\F032C"}.mdi-account-details::before{content:"\F631"}.mdi-account-details-outline::before{content:"\F039D"}.mdi-account-edit::before{content:"\F6BB"}.mdi-account-edit-outline::before{content:"\F001D"}.mdi-account-group::before{content:"\F848"}.mdi-account-group-outline::before{content:"\FB34"}.mdi-account-heart::before{content:"\F898"}.mdi-account-heart-outline::before{content:"\FBBF"}.mdi-account-key::before{content:"\F00B"}.mdi-account-key-outline::before{content:"\FBC0"}.mdi-account-lock::before{content:"\F0189"}.mdi-account-lock-outline::before{content:"\F018A"}.mdi-account-minus::before{content:"\F00D"}.mdi-account-minus-outline::before{content:"\FAEB"}.mdi-account-multiple::before{content:"\F00E"}.mdi-account-multiple-check::before{content:"\F8C4"}.mdi-account-multiple-check-outline::before{content:"\F0229"}.mdi-account-multiple-minus::before{content:"\F5D3"}.mdi-account-multiple-minus-outline::before{content:"\FBC1"}.mdi-account-multiple-outline::before{content:"\F00F"}.mdi-account-multiple-plus::before{content:"\F010"}.mdi-account-multiple-plus-outline::before{content:"\F7FF"}.mdi-account-multiple-remove::before{content:"\F0235"}.mdi-account-multiple-remove-outline::before{content:"\F0236"}.mdi-account-network::before{content:"\F011"}.mdi-account-network-outline::before{content:"\FBC2"}.mdi-account-off::before{content:"\F012"}.mdi-account-off-outline::before{content:"\FBC3"}.mdi-account-outline::before{content:"\F013"}.mdi-account-plus::before{content:"\F014"}.mdi-account-plus-outline::before{content:"\F800"}.mdi-account-question::before{content:"\FB35"}.mdi-account-question-outline::before{content:"\FB36"}.mdi-account-remove::before{content:"\F015"}.mdi-account-remove-outline::before{content:"\FAEC"}.mdi-account-search::before{content:"\F016"}.mdi-account-search-outline::before{content:"\F934"}.mdi-account-settings::before{content:"\F630"}.mdi-account-settings-outline::before{content:"\F00F4"}.mdi-account-star::before{content:"\F017"}.mdi-account-star-outline::before{content:"\FBC4"}.mdi-account-supervisor::before{content:"\FA8A"}.mdi-account-supervisor-circle::before{content:"\FA8B"}.mdi-account-supervisor-outline::before{content:"\F0158"}.mdi-account-switch::before{content:"\F019"}.mdi-account-tie::before{content:"\FCBF"}.mdi-account-tie-outline::before{content:"\F00F5"}.mdi-account-tie-voice::before{content:"\F0333"}.mdi-account-tie-voice-off::before{content:"\F0335"}.mdi-account-tie-voice-off-outline::before{content:"\F0336"}.mdi-account-tie-voice-outline::before{content:"\F0334"}.mdi-accusoft::before{content:"\F849"}.mdi-adjust::before{content:"\F01A"}.mdi-adobe::before{content:"\F935"}.mdi-adobe-acrobat::before{content:"\FFBD"}.mdi-air-conditioner::before{content:"\F01B"}.mdi-air-filter::before{content:"\FD1F"}.mdi-air-horn::before{content:"\FD88"}.mdi-air-humidifier::before{content:"\F00C4"}.mdi-air-purifier::before{content:"\FD20"}.mdi-airbag::before{content:"\FBC5"}.mdi-airballoon::before{content:"\F01C"}.mdi-airballoon-outline::before{content:"\F002D"}.mdi-airplane::before{content:"\F01D"}.mdi-airplane-landing::before{content:"\F5D4"}.mdi-airplane-off::before{content:"\F01E"}.mdi-airplane-takeoff::before{content:"\F5D5"}.mdi-airplay::before{content:"\F01F"}.mdi-airport::before{content:"\F84A"}.mdi-alarm::before{content:"\F020"}.mdi-alarm-bell::before{content:"\F78D"}.mdi-alarm-check::before{content:"\F021"}.mdi-alarm-light::before{content:"\F78E"}.mdi-alarm-light-outline::before{content:"\FBC6"}.mdi-alarm-multiple::before{content:"\F022"}.mdi-alarm-note::before{content:"\FE8E"}.mdi-alarm-note-off::before{content:"\FE8F"}.mdi-alarm-off::before{content:"\F023"}.mdi-alarm-plus::before{content:"\F024"}.mdi-alarm-snooze::before{content:"\F68D"}.mdi-album::before{content:"\F025"}.mdi-alert::before{content:"\F026"}.mdi-alert-box::before{content:"\F027"}.mdi-alert-box-outline::before{content:"\FCC0"}.mdi-alert-circle::before{content:"\F028"}.mdi-alert-circle-check::before{content:"\F0218"}.mdi-alert-circle-check-outline::before{content:"\F0219"}.mdi-alert-circle-outline::before{content:"\F5D6"}.mdi-alert-decagram::before{content:"\F6BC"}.mdi-alert-decagram-outline::before{content:"\FCC1"}.mdi-alert-octagon::before{content:"\F029"}.mdi-alert-octagon-outline::before{content:"\FCC2"}.mdi-alert-octagram::before{content:"\F766"}.mdi-alert-octagram-outline::before{content:"\FCC3"}.mdi-alert-outline::before{content:"\F02A"}.mdi-alert-rhombus::before{content:"\F01F9"}.mdi-alert-rhombus-outline::before{content:"\F01FA"}.mdi-alien::before{content:"\F899"}.mdi-alien-outline::before{content:"\F00F6"}.mdi-align-horizontal-center::before{content:"\F01EE"}.mdi-align-horizontal-left::before{content:"\F01ED"}.mdi-align-horizontal-right::before{content:"\F01EF"}.mdi-align-vertical-bottom::before{content:"\F01F0"}.mdi-align-vertical-center::before{content:"\F01F1"}.mdi-align-vertical-top::before{content:"\F01F2"}.mdi-all-inclusive::before{content:"\F6BD"}.mdi-allergy::before{content:"\F0283"}.mdi-alpha::before{content:"\F02B"}.mdi-alpha-a::before{content:"\41"}.mdi-alpha-a-box::before{content:"\FAED"}.mdi-alpha-a-box-outline::before{content:"\FBC7"}.mdi-alpha-a-circle::before{content:"\FBC8"}.mdi-alpha-a-circle-outline::before{content:"\FBC9"}.mdi-alpha-b::before{content:"\42"}.mdi-alpha-b-box::before{content:"\FAEE"}.mdi-alpha-b-box-outline::before{content:"\FBCA"}.mdi-alpha-b-circle::before{content:"\FBCB"}.mdi-alpha-b-circle-outline::before{content:"\FBCC"}.mdi-alpha-c::before{content:"\43"}.mdi-alpha-c-box::before{content:"\FAEF"}.mdi-alpha-c-box-outline::before{content:"\FBCD"}.mdi-alpha-c-circle::before{content:"\FBCE"}.mdi-alpha-c-circle-outline::before{content:"\FBCF"}.mdi-alpha-d::before{content:"\44"}.mdi-alpha-d-box::before{content:"\FAF0"}.mdi-alpha-d-box-outline::before{content:"\FBD0"}.mdi-alpha-d-circle::before{content:"\FBD1"}.mdi-alpha-d-circle-outline::before{content:"\FBD2"}.mdi-alpha-e::before{content:"\45"}.mdi-alpha-e-box::before{content:"\FAF1"}.mdi-alpha-e-box-outline::before{content:"\FBD3"}.mdi-alpha-e-circle::before{content:"\FBD4"}.mdi-alpha-e-circle-outline::before{content:"\FBD5"}.mdi-alpha-f::before{content:"\46"}.mdi-alpha-f-box::before{content:"\FAF2"}.mdi-alpha-f-box-outline::before{content:"\FBD6"}.mdi-alpha-f-circle::before{content:"\FBD7"}.mdi-alpha-f-circle-outline::before{content:"\FBD8"}.mdi-alpha-g::before{content:"\47"}.mdi-alpha-g-box::before{content:"\FAF3"}.mdi-alpha-g-box-outline::before{content:"\FBD9"}.mdi-alpha-g-circle::before{content:"\FBDA"}.mdi-alpha-g-circle-outline::before{content:"\FBDB"}.mdi-alpha-h::before{content:"\48"}.mdi-alpha-h-box::before{content:"\FAF4"}.mdi-alpha-h-box-outline::before{content:"\FBDC"}.mdi-alpha-h-circle::before{content:"\FBDD"}.mdi-alpha-h-circle-outline::before{content:"\FBDE"}.mdi-alpha-i::before{content:"\49"}.mdi-alpha-i-box::before{content:"\FAF5"}.mdi-alpha-i-box-outline::before{content:"\FBDF"}.mdi-alpha-i-circle::before{content:"\FBE0"}.mdi-alpha-i-circle-outline::before{content:"\FBE1"}.mdi-alpha-j::before{content:"\4A"}.mdi-alpha-j-box::before{content:"\FAF6"}.mdi-alpha-j-box-outline::before{content:"\FBE2"}.mdi-alpha-j-circle::before{content:"\FBE3"}.mdi-alpha-j-circle-outline::before{content:"\FBE4"}.mdi-alpha-k::before{content:"\4B"}.mdi-alpha-k-box::before{content:"\FAF7"}.mdi-alpha-k-box-outline::before{content:"\FBE5"}.mdi-alpha-k-circle::before{content:"\FBE6"}.mdi-alpha-k-circle-outline::before{content:"\FBE7"}.mdi-alpha-l::before{content:"\4C"}.mdi-alpha-l-box::before{content:"\FAF8"}.mdi-alpha-l-box-outline::before{content:"\FBE8"}.mdi-alpha-l-circle::before{content:"\FBE9"}.mdi-alpha-l-circle-outline::before{content:"\FBEA"}.mdi-alpha-m::before{content:"\4D"}.mdi-alpha-m-box::before{content:"\FAF9"}.mdi-alpha-m-box-outline::before{content:"\FBEB"}.mdi-alpha-m-circle::before{content:"\FBEC"}.mdi-alpha-m-circle-outline::before{content:"\FBED"}.mdi-alpha-n::before{content:"\4E"}.mdi-alpha-n-box::before{content:"\FAFA"}.mdi-alpha-n-box-outline::before{content:"\FBEE"}.mdi-alpha-n-circle::before{content:"\FBEF"}.mdi-alpha-n-circle-outline::before{content:"\FBF0"}.mdi-alpha-o::before{content:"\4F"}.mdi-alpha-o-box::before{content:"\FAFB"}.mdi-alpha-o-box-outline::before{content:"\FBF1"}.mdi-alpha-o-circle::before{content:"\FBF2"}.mdi-alpha-o-circle-outline::before{content:"\FBF3"}.mdi-alpha-p::before{content:"\50"}.mdi-alpha-p-box::before{content:"\FAFC"}.mdi-alpha-p-box-outline::before{content:"\FBF4"}.mdi-alpha-p-circle::before{content:"\FBF5"}.mdi-alpha-p-circle-outline::before{content:"\FBF6"}.mdi-alpha-q::before{content:"\51"}.mdi-alpha-q-box::before{content:"\FAFD"}.mdi-alpha-q-box-outline::before{content:"\FBF7"}.mdi-alpha-q-circle::before{content:"\FBF8"}.mdi-alpha-q-circle-outline::before{content:"\FBF9"}.mdi-alpha-r::before{content:"\52"}.mdi-alpha-r-box::before{content:"\FAFE"}.mdi-alpha-r-box-outline::before{content:"\FBFA"}.mdi-alpha-r-circle::before{content:"\FBFB"}.mdi-alpha-r-circle-outline::before{content:"\FBFC"}.mdi-alpha-s::before{content:"\53"}.mdi-alpha-s-box::before{content:"\FAFF"}.mdi-alpha-s-box-outline::before{content:"\FBFD"}.mdi-alpha-s-circle::before{content:"\FBFE"}.mdi-alpha-s-circle-outline::before{content:"\FBFF"}.mdi-alpha-t::before{content:"\54"}.mdi-alpha-t-box::before{content:"\FB00"}.mdi-alpha-t-box-outline::before{content:"\FC00"}.mdi-alpha-t-circle::before{content:"\FC01"}.mdi-alpha-t-circle-outline::before{content:"\FC02"}.mdi-alpha-u::before{content:"\55"}.mdi-alpha-u-box::before{content:"\FB01"}.mdi-alpha-u-box-outline::before{content:"\FC03"}.mdi-alpha-u-circle::before{content:"\FC04"}.mdi-alpha-u-circle-outline::before{content:"\FC05"}.mdi-alpha-v::before{content:"\56"}.mdi-alpha-v-box::before{content:"\FB02"}.mdi-alpha-v-box-outline::before{content:"\FC06"}.mdi-alpha-v-circle::before{content:"\FC07"}.mdi-alpha-v-circle-outline::before{content:"\FC08"}.mdi-alpha-w::before{content:"\57"}.mdi-alpha-w-box::before{content:"\FB03"}.mdi-alpha-w-box-outline::before{content:"\FC09"}.mdi-alpha-w-circle::before{content:"\FC0A"}.mdi-alpha-w-circle-outline::before{content:"\FC0B"}.mdi-alpha-x::before{content:"\58"}.mdi-alpha-x-box::before{content:"\FB04"}.mdi-alpha-x-box-outline::before{content:"\FC0C"}.mdi-alpha-x-circle::before{content:"\FC0D"}.mdi-alpha-x-circle-outline::before{content:"\FC0E"}.mdi-alpha-y::before{content:"\59"}.mdi-alpha-y-box::before{content:"\FB05"}.mdi-alpha-y-box-outline::before{content:"\FC0F"}.mdi-alpha-y-circle::before{content:"\FC10"}.mdi-alpha-y-circle-outline::before{content:"\FC11"}.mdi-alpha-z::before{content:"\5A"}.mdi-alpha-z-box::before{content:"\FB06"}.mdi-alpha-z-box-outline::before{content:"\FC12"}.mdi-alpha-z-circle::before{content:"\FC13"}.mdi-alpha-z-circle-outline::before{content:"\FC14"}.mdi-alphabet-aurebesh::before{content:"\F0357"}.mdi-alphabet-cyrillic::before{content:"\F0358"}.mdi-alphabet-greek::before{content:"\F0359"}.mdi-alphabet-latin::before{content:"\F035A"}.mdi-alphabet-piqad::before{content:"\F035B"}.mdi-alphabet-tengwar::before{content:"\F0362"}.mdi-alphabetical::before{content:"\F02C"}.mdi-alphabetical-off::before{content:"\F002E"}.mdi-alphabetical-variant::before{content:"\F002F"}.mdi-alphabetical-variant-off::before{content:"\F0030"}.mdi-altimeter::before{content:"\F5D7"}.mdi-amazon::before{content:"\F02D"}.mdi-amazon-alexa::before{content:"\F8C5"}.mdi-amazon-drive::before{content:"\F02E"}.mdi-ambulance::before{content:"\F02F"}.mdi-ammunition::before{content:"\FCC4"}.mdi-ampersand::before{content:"\FA8C"}.mdi-amplifier::before{content:"\F030"}.mdi-amplifier-off::before{content:"\F01E0"}.mdi-anchor::before{content:"\F031"}.mdi-android::before{content:"\F032"}.mdi-android-auto::before{content:"\FA8D"}.mdi-android-debug-bridge::before{content:"\F033"}.mdi-android-head::before{content:"\F78F"}.mdi-android-messages::before{content:"\FD21"}.mdi-android-studio::before{content:"\F034"}.mdi-angle-acute::before{content:"\F936"}.mdi-angle-obtuse::before{content:"\F937"}.mdi-angle-right::before{content:"\F938"}.mdi-angular::before{content:"\F6B1"}.mdi-angularjs::before{content:"\F6BE"}.mdi-animation::before{content:"\F5D8"}.mdi-animation-outline::before{content:"\FA8E"}.mdi-animation-play::before{content:"\F939"}.mdi-animation-play-outline::before{content:"\FA8F"}.mdi-ansible::before{content:"\F00C5"}.mdi-antenna::before{content:"\F0144"}.mdi-anvil::before{content:"\F89A"}.mdi-apache-kafka::before{content:"\F0031"}.mdi-api::before{content:"\F00C6"}.mdi-api-off::before{content:"\F0282"}.mdi-apple::before{content:"\F035"}.mdi-apple-finder::before{content:"\F036"}.mdi-apple-icloud::before{content:"\F038"}.mdi-apple-ios::before{content:"\F037"}.mdi-apple-keyboard-caps::before{content:"\F632"}.mdi-apple-keyboard-command::before{content:"\F633"}.mdi-apple-keyboard-control::before{content:"\F634"}.mdi-apple-keyboard-option::before{content:"\F635"}.mdi-apple-keyboard-shift::before{content:"\F636"}.mdi-apple-safari::before{content:"\F039"}.mdi-application::before{content:"\F614"}.mdi-application-export::before{content:"\FD89"}.mdi-application-import::before{content:"\FD8A"}.mdi-approximately-equal::before{content:"\FFBE"}.mdi-approximately-equal-box::before{content:"\FFBF"}.mdi-apps::before{content:"\F03B"}.mdi-apps-box::before{content:"\FD22"}.mdi-arch::before{content:"\F8C6"}.mdi-archive::before{content:"\F03C"}.mdi-archive-arrow-down::before{content:"\F0284"}.mdi-archive-arrow-down-outline::before{content:"\F0285"}.mdi-archive-arrow-up::before{content:"\F0286"}.mdi-archive-arrow-up-outline::before{content:"\F0287"}.mdi-archive-outline::before{content:"\F0239"}.mdi-arm-flex::before{content:"\F008F"}.mdi-arm-flex-outline::before{content:"\F0090"}.mdi-arrange-bring-forward::before{content:"\F03D"}.mdi-arrange-bring-to-front::before{content:"\F03E"}.mdi-arrange-send-backward::before{content:"\F03F"}.mdi-arrange-send-to-back::before{content:"\F040"}.mdi-arrow-all::before{content:"\F041"}.mdi-arrow-bottom-left::before{content:"\F042"}.mdi-arrow-bottom-left-bold-outline::before{content:"\F9B6"}.mdi-arrow-bottom-left-thick::before{content:"\F9B7"}.mdi-arrow-bottom-right::before{content:"\F043"}.mdi-arrow-bottom-right-bold-outline::before{content:"\F9B8"}.mdi-arrow-bottom-right-thick::before{content:"\F9B9"}.mdi-arrow-collapse::before{content:"\F615"}.mdi-arrow-collapse-all::before{content:"\F044"}.mdi-arrow-collapse-down::before{content:"\F791"}.mdi-arrow-collapse-horizontal::before{content:"\F84B"}.mdi-arrow-collapse-left::before{content:"\F792"}.mdi-arrow-collapse-right::before{content:"\F793"}.mdi-arrow-collapse-up::before{content:"\F794"}.mdi-arrow-collapse-vertical::before{content:"\F84C"}.mdi-arrow-decision::before{content:"\F9BA"}.mdi-arrow-decision-auto::before{content:"\F9BB"}.mdi-arrow-decision-auto-outline::before{content:"\F9BC"}.mdi-arrow-decision-outline::before{content:"\F9BD"}.mdi-arrow-down::before{content:"\F045"}.mdi-arrow-down-bold::before{content:"\F72D"}.mdi-arrow-down-bold-box::before{content:"\F72E"}.mdi-arrow-down-bold-box-outline::before{content:"\F72F"}.mdi-arrow-down-bold-circle::before{content:"\F047"}.mdi-arrow-down-bold-circle-outline::before{content:"\F048"}.mdi-arrow-down-bold-hexagon-outline::before{content:"\F049"}.mdi-arrow-down-bold-outline::before{content:"\F9BE"}.mdi-arrow-down-box::before{content:"\F6BF"}.mdi-arrow-down-circle::before{content:"\FCB7"}.mdi-arrow-down-circle-outline::before{content:"\FCB8"}.mdi-arrow-down-drop-circle::before{content:"\F04A"}.mdi-arrow-down-drop-circle-outline::before{content:"\F04B"}.mdi-arrow-down-thick::before{content:"\F046"}.mdi-arrow-expand::before{content:"\F616"}.mdi-arrow-expand-all::before{content:"\F04C"}.mdi-arrow-expand-down::before{content:"\F795"}.mdi-arrow-expand-horizontal::before{content:"\F84D"}.mdi-arrow-expand-left::before{content:"\F796"}.mdi-arrow-expand-right::before{content:"\F797"}.mdi-arrow-expand-up::before{content:"\F798"}.mdi-arrow-expand-vertical::before{content:"\F84E"}.mdi-arrow-horizontal-lock::before{content:"\F0186"}.mdi-arrow-left::before{content:"\F04D"}.mdi-arrow-left-bold::before{content:"\F730"}.mdi-arrow-left-bold-box::before{content:"\F731"}.mdi-arrow-left-bold-box-outline::before{content:"\F732"}.mdi-arrow-left-bold-circle::before{content:"\F04F"}.mdi-arrow-left-bold-circle-outline::before{content:"\F050"}.mdi-arrow-left-bold-hexagon-outline::before{content:"\F051"}.mdi-arrow-left-bold-outline::before{content:"\F9BF"}.mdi-arrow-left-box::before{content:"\F6C0"}.mdi-arrow-left-circle::before{content:"\FCB9"}.mdi-arrow-left-circle-outline::before{content:"\FCBA"}.mdi-arrow-left-drop-circle::before{content:"\F052"}.mdi-arrow-left-drop-circle-outline::before{content:"\F053"}.mdi-arrow-left-right::before{content:"\FE90"}.mdi-arrow-left-right-bold::before{content:"\FE91"}.mdi-arrow-left-right-bold-outline::before{content:"\F9C0"}.mdi-arrow-left-thick::before{content:"\F04E"}.mdi-arrow-right::before{content:"\F054"}.mdi-arrow-right-bold::before{content:"\F733"}.mdi-arrow-right-bold-box::before{content:"\F734"}.mdi-arrow-right-bold-box-outline::before{content:"\F735"}.mdi-arrow-right-bold-circle::before{content:"\F056"}.mdi-arrow-right-bold-circle-outline::before{content:"\F057"}.mdi-arrow-right-bold-hexagon-outline::before{content:"\F058"}.mdi-arrow-right-bold-outline::before{content:"\F9C1"}.mdi-arrow-right-box::before{content:"\F6C1"}.mdi-arrow-right-circle::before{content:"\FCBB"}.mdi-arrow-right-circle-outline::before{content:"\FCBC"}.mdi-arrow-right-drop-circle::before{content:"\F059"}.mdi-arrow-right-drop-circle-outline::before{content:"\F05A"}.mdi-arrow-right-thick::before{content:"\F055"}.mdi-arrow-split-horizontal::before{content:"\F93A"}.mdi-arrow-split-vertical::before{content:"\F93B"}.mdi-arrow-top-left::before{content:"\F05B"}.mdi-arrow-top-left-bold-outline::before{content:"\F9C2"}.mdi-arrow-top-left-bottom-right::before{content:"\FE92"}.mdi-arrow-top-left-bottom-right-bold::before{content:"\FE93"}.mdi-arrow-top-left-thick::before{content:"\F9C3"}.mdi-arrow-top-right::before{content:"\F05C"}.mdi-arrow-top-right-bold-outline::before{content:"\F9C4"}.mdi-arrow-top-right-bottom-left::before{content:"\FE94"}.mdi-arrow-top-right-bottom-left-bold::before{content:"\FE95"}.mdi-arrow-top-right-thick::before{content:"\F9C5"}.mdi-arrow-up::before{content:"\F05D"}.mdi-arrow-up-bold::before{content:"\F736"}.mdi-arrow-up-bold-box::before{content:"\F737"}.mdi-arrow-up-bold-box-outline::before{content:"\F738"}.mdi-arrow-up-bold-circle::before{content:"\F05F"}.mdi-arrow-up-bold-circle-outline::before{content:"\F060"}.mdi-arrow-up-bold-hexagon-outline::before{content:"\F061"}.mdi-arrow-up-bold-outline::before{content:"\F9C6"}.mdi-arrow-up-box::before{content:"\F6C2"}.mdi-arrow-up-circle::before{content:"\FCBD"}.mdi-arrow-up-circle-outline::before{content:"\FCBE"}.mdi-arrow-up-down::before{content:"\FE96"}.mdi-arrow-up-down-bold::before{content:"\FE97"}.mdi-arrow-up-down-bold-outline::before{content:"\F9C7"}.mdi-arrow-up-drop-circle::before{content:"\F062"}.mdi-arrow-up-drop-circle-outline::before{content:"\F063"}.mdi-arrow-up-thick::before{content:"\F05E"}.mdi-arrow-vertical-lock::before{content:"\F0187"}.mdi-artist::before{content:"\F802"}.mdi-artist-outline::before{content:"\FCC5"}.mdi-artstation::before{content:"\FB37"}.mdi-aspect-ratio::before{content:"\FA23"}.mdi-assistant::before{content:"\F064"}.mdi-asterisk::before{content:"\F6C3"}.mdi-at::before{content:"\F065"}.mdi-atlassian::before{content:"\F803"}.mdi-atm::before{content:"\FD23"}.mdi-atom::before{content:"\F767"}.mdi-atom-variant::before{content:"\FE98"}.mdi-attachment::before{content:"\F066"}.mdi-audio-video::before{content:"\F93C"}.mdi-audio-video-off::before{content:"\F01E1"}.mdi-audiobook::before{content:"\F067"}.mdi-augmented-reality::before{content:"\F84F"}.mdi-auto-download::before{content:"\F03A9"}.mdi-auto-fix::before{content:"\F068"}.mdi-auto-upload::before{content:"\F069"}.mdi-autorenew::before{content:"\F06A"}.mdi-av-timer::before{content:"\F06B"}.mdi-aws::before{content:"\FDF2"}.mdi-axe::before{content:"\F8C7"}.mdi-axis::before{content:"\FD24"}.mdi-axis-arrow::before{content:"\FD25"}.mdi-axis-arrow-lock::before{content:"\FD26"}.mdi-axis-lock::before{content:"\FD27"}.mdi-axis-x-arrow::before{content:"\FD28"}.mdi-axis-x-arrow-lock::before{content:"\FD29"}.mdi-axis-x-rotate-clockwise::before{content:"\FD2A"}.mdi-axis-x-rotate-counterclockwise::before{content:"\FD2B"}.mdi-axis-x-y-arrow-lock::before{content:"\FD2C"}.mdi-axis-y-arrow::before{content:"\FD2D"}.mdi-axis-y-arrow-lock::before{content:"\FD2E"}.mdi-axis-y-rotate-clockwise::before{content:"\FD2F"}.mdi-axis-y-rotate-counterclockwise::before{content:"\FD30"}.mdi-axis-z-arrow::before{content:"\FD31"}.mdi-axis-z-arrow-lock::before{content:"\FD32"}.mdi-axis-z-rotate-clockwise::before{content:"\FD33"}.mdi-axis-z-rotate-counterclockwise::before{content:"\FD34"}.mdi-azure::before{content:"\F804"}.mdi-azure-devops::before{content:"\F0091"}.mdi-babel::before{content:"\FA24"}.mdi-baby::before{content:"\F06C"}.mdi-baby-bottle::before{content:"\FF56"}.mdi-baby-bottle-outline::before{content:"\FF57"}.mdi-baby-carriage::before{content:"\F68E"}.mdi-baby-carriage-off::before{content:"\FFC0"}.mdi-baby-face::before{content:"\FE99"}.mdi-baby-face-outline::before{content:"\FE9A"}.mdi-backburger::before{content:"\F06D"}.mdi-backspace::before{content:"\F06E"}.mdi-backspace-outline::before{content:"\FB38"}.mdi-backspace-reverse::before{content:"\FE9B"}.mdi-backspace-reverse-outline::before{content:"\FE9C"}.mdi-backup-restore::before{content:"\F06F"}.mdi-bacteria::before{content:"\FEF2"}.mdi-bacteria-outline::before{content:"\FEF3"}.mdi-badminton::before{content:"\F850"}.mdi-bag-carry-on::before{content:"\FF58"}.mdi-bag-carry-on-check::before{content:"\FD41"}.mdi-bag-carry-on-off::before{content:"\FF59"}.mdi-bag-checked::before{content:"\FF5A"}.mdi-bag-personal::before{content:"\FDF3"}.mdi-bag-personal-off::before{content:"\FDF4"}.mdi-bag-personal-off-outline::before{content:"\FDF5"}.mdi-bag-personal-outline::before{content:"\FDF6"}.mdi-baguette::before{content:"\FF5B"}.mdi-balloon::before{content:"\FA25"}.mdi-ballot::before{content:"\F9C8"}.mdi-ballot-outline::before{content:"\F9C9"}.mdi-ballot-recount::before{content:"\FC15"}.mdi-ballot-recount-outline::before{content:"\FC16"}.mdi-bandage::before{content:"\FD8B"}.mdi-bandcamp::before{content:"\F674"}.mdi-bank::before{content:"\F070"}.mdi-bank-minus::before{content:"\FD8C"}.mdi-bank-outline::before{content:"\FE9D"}.mdi-bank-plus::before{content:"\FD8D"}.mdi-bank-remove::before{content:"\FD8E"}.mdi-bank-transfer::before{content:"\FA26"}.mdi-bank-transfer-in::before{content:"\FA27"}.mdi-bank-transfer-out::before{content:"\FA28"}.mdi-barcode::before{content:"\F071"}.mdi-barcode-off::before{content:"\F0261"}.mdi-barcode-scan::before{content:"\F072"}.mdi-barley::before{content:"\F073"}.mdi-barley-off::before{content:"\FB39"}.mdi-barn::before{content:"\FB3A"}.mdi-barrel::before{content:"\F074"}.mdi-baseball::before{content:"\F851"}.mdi-baseball-bat::before{content:"\F852"}.mdi-basecamp::before{content:"\F075"}.mdi-bash::before{content:"\F01AE"}.mdi-basket::before{content:"\F076"}.mdi-basket-fill::before{content:"\F077"}.mdi-basket-outline::before{content:"\F01AC"}.mdi-basket-unfill::before{content:"\F078"}.mdi-basketball::before{content:"\F805"}.mdi-basketball-hoop::before{content:"\FC17"}.mdi-basketball-hoop-outline::before{content:"\FC18"}.mdi-bat::before{content:"\FB3B"}.mdi-battery::before{content:"\F079"}.mdi-battery-10::before{content:"\F07A"}.mdi-battery-10-bluetooth::before{content:"\F93D"}.mdi-battery-20::before{content:"\F07B"}.mdi-battery-20-bluetooth::before{content:"\F93E"}.mdi-battery-30::before{content:"\F07C"}.mdi-battery-30-bluetooth::before{content:"\F93F"}.mdi-battery-40::before{content:"\F07D"}.mdi-battery-40-bluetooth::before{content:"\F940"}.mdi-battery-50::before{content:"\F07E"}.mdi-battery-50-bluetooth::before{content:"\F941"}.mdi-battery-60::before{content:"\F07F"}.mdi-battery-60-bluetooth::before{content:"\F942"}.mdi-battery-70::before{content:"\F080"}.mdi-battery-70-bluetooth::before{content:"\F943"}.mdi-battery-80::before{content:"\F081"}.mdi-battery-80-bluetooth::before{content:"\F944"}.mdi-battery-90::before{content:"\F082"}.mdi-battery-90-bluetooth::before{content:"\F945"}.mdi-battery-alert::before{content:"\F083"}.mdi-battery-alert-bluetooth::before{content:"\F946"}.mdi-battery-alert-variant::before{content:"\F00F7"}.mdi-battery-alert-variant-outline::before{content:"\F00F8"}.mdi-battery-bluetooth::before{content:"\F947"}.mdi-battery-bluetooth-variant::before{content:"\F948"}.mdi-battery-charging::before{content:"\F084"}.mdi-battery-charging-10::before{content:"\F89B"}.mdi-battery-charging-100::before{content:"\F085"}.mdi-battery-charging-20::before{content:"\F086"}.mdi-battery-charging-30::before{content:"\F087"}.mdi-battery-charging-40::before{content:"\F088"}.mdi-battery-charging-50::before{content:"\F89C"}.mdi-battery-charging-60::before{content:"\F089"}.mdi-battery-charging-70::before{content:"\F89D"}.mdi-battery-charging-80::before{content:"\F08A"}.mdi-battery-charging-90::before{content:"\F08B"}.mdi-battery-charging-high::before{content:"\F02D1"}.mdi-battery-charging-low::before{content:"\F02CF"}.mdi-battery-charging-medium::before{content:"\F02D0"}.mdi-battery-charging-outline::before{content:"\F89E"}.mdi-battery-charging-wireless::before{content:"\F806"}.mdi-battery-charging-wireless-10::before{content:"\F807"}.mdi-battery-charging-wireless-20::before{content:"\F808"}.mdi-battery-charging-wireless-30::before{content:"\F809"}.mdi-battery-charging-wireless-40::before{content:"\F80A"}.mdi-battery-charging-wireless-50::before{content:"\F80B"}.mdi-battery-charging-wireless-60::before{content:"\F80C"}.mdi-battery-charging-wireless-70::before{content:"\F80D"}.mdi-battery-charging-wireless-80::before{content:"\F80E"}.mdi-battery-charging-wireless-90::before{content:"\F80F"}.mdi-battery-charging-wireless-alert::before{content:"\F810"}.mdi-battery-charging-wireless-outline::before{content:"\F811"}.mdi-battery-heart::before{content:"\F023A"}.mdi-battery-heart-outline::before{content:"\F023B"}.mdi-battery-heart-variant::before{content:"\F023C"}.mdi-battery-high::before{content:"\F02CE"}.mdi-battery-low::before{content:"\F02CC"}.mdi-battery-medium::before{content:"\F02CD"}.mdi-battery-minus::before{content:"\F08C"}.mdi-battery-negative::before{content:"\F08D"}.mdi-battery-off::before{content:"\F0288"}.mdi-battery-off-outline::before{content:"\F0289"}.mdi-battery-outline::before{content:"\F08E"}.mdi-battery-plus::before{content:"\F08F"}.mdi-battery-positive::before{content:"\F090"}.mdi-battery-unknown::before{content:"\F091"}.mdi-battery-unknown-bluetooth::before{content:"\F949"}.mdi-battlenet::before{content:"\FB3C"}.mdi-beach::before{content:"\F092"}.mdi-beaker::before{content:"\FCC6"}.mdi-beaker-alert::before{content:"\F0254"}.mdi-beaker-alert-outline::before{content:"\F0255"}.mdi-beaker-check::before{content:"\F0256"}.mdi-beaker-check-outline::before{content:"\F0257"}.mdi-beaker-minus::before{content:"\F0258"}.mdi-beaker-minus-outline::before{content:"\F0259"}.mdi-beaker-outline::before{content:"\F68F"}.mdi-beaker-plus::before{content:"\F025A"}.mdi-beaker-plus-outline::before{content:"\F025B"}.mdi-beaker-question::before{content:"\F025C"}.mdi-beaker-question-outline::before{content:"\F025D"}.mdi-beaker-remove::before{content:"\F025E"}.mdi-beaker-remove-outline::before{content:"\F025F"}.mdi-beats::before{content:"\F097"}.mdi-bed-double::before{content:"\F0092"}.mdi-bed-double-outline::before{content:"\F0093"}.mdi-bed-empty::before{content:"\F89F"}.mdi-bed-king::before{content:"\F0094"}.mdi-bed-king-outline::before{content:"\F0095"}.mdi-bed-queen::before{content:"\F0096"}.mdi-bed-queen-outline::before{content:"\F0097"}.mdi-bed-single::before{content:"\F0098"}.mdi-bed-single-outline::before{content:"\F0099"}.mdi-bee::before{content:"\FFC1"}.mdi-bee-flower::before{content:"\FFC2"}.mdi-beehive-outline::before{content:"\F00F9"}.mdi-beer::before{content:"\F098"}.mdi-beer-outline::before{content:"\F0337"}.mdi-behance::before{content:"\F099"}.mdi-bell::before{content:"\F09A"}.mdi-bell-alert::before{content:"\FD35"}.mdi-bell-alert-outline::before{content:"\FE9E"}.mdi-bell-check::before{content:"\F0210"}.mdi-bell-check-outline::before{content:"\F0211"}.mdi-bell-circle::before{content:"\FD36"}.mdi-bell-circle-outline::before{content:"\FD37"}.mdi-bell-off::before{content:"\F09B"}.mdi-bell-off-outline::before{content:"\FA90"}.mdi-bell-outline::before{content:"\F09C"}.mdi-bell-plus::before{content:"\F09D"}.mdi-bell-plus-outline::before{content:"\FA91"}.mdi-bell-ring::before{content:"\F09E"}.mdi-bell-ring-outline::before{content:"\F09F"}.mdi-bell-sleep::before{content:"\F0A0"}.mdi-bell-sleep-outline::before{content:"\FA92"}.mdi-beta::before{content:"\F0A1"}.mdi-betamax::before{content:"\F9CA"}.mdi-biathlon::before{content:"\FDF7"}.mdi-bible::before{content:"\F0A2"}.mdi-bicycle::before{content:"\F00C7"}.mdi-bicycle-basket::before{content:"\F0260"}.mdi-bike::before{content:"\F0A3"}.mdi-bike-fast::before{content:"\F014A"}.mdi-billboard::before{content:"\F0032"}.mdi-billiards::before{content:"\FB3D"}.mdi-billiards-rack::before{content:"\FB3E"}.mdi-bing::before{content:"\F0A4"}.mdi-binoculars::before{content:"\F0A5"}.mdi-bio::before{content:"\F0A6"}.mdi-biohazard::before{content:"\F0A7"}.mdi-bitbucket::before{content:"\F0A8"}.mdi-bitcoin::before{content:"\F812"}.mdi-black-mesa::before{content:"\F0A9"}.mdi-blackberry::before{content:"\F0AA"}.mdi-blender::before{content:"\FCC7"}.mdi-blender-software::before{content:"\F0AB"}.mdi-blinds::before{content:"\F0AC"}.mdi-blinds-open::before{content:"\F0033"}.mdi-block-helper::before{content:"\F0AD"}.mdi-blogger::before{content:"\F0AE"}.mdi-blood-bag::before{content:"\FCC8"}.mdi-bluetooth::before{content:"\F0AF"}.mdi-bluetooth-audio::before{content:"\F0B0"}.mdi-bluetooth-connect::before{content:"\F0B1"}.mdi-bluetooth-off::before{content:"\F0B2"}.mdi-bluetooth-settings::before{content:"\F0B3"}.mdi-bluetooth-transfer::before{content:"\F0B4"}.mdi-blur::before{content:"\F0B5"}.mdi-blur-linear::before{content:"\F0B6"}.mdi-blur-off::before{content:"\F0B7"}.mdi-blur-radial::before{content:"\F0B8"}.mdi-bolnisi-cross::before{content:"\FCC9"}.mdi-bolt::before{content:"\FD8F"}.mdi-bomb::before{content:"\F690"}.mdi-bomb-off::before{content:"\F6C4"}.mdi-bone::before{content:"\F0B9"}.mdi-book::before{content:"\F0BA"}.mdi-book-information-variant::before{content:"\F009A"}.mdi-book-lock::before{content:"\F799"}.mdi-book-lock-open::before{content:"\F79A"}.mdi-book-minus::before{content:"\F5D9"}.mdi-book-minus-multiple::before{content:"\FA93"}.mdi-book-multiple::before{content:"\F0BB"}.mdi-book-open::before{content:"\F0BD"}.mdi-book-open-outline::before{content:"\FB3F"}.mdi-book-open-page-variant::before{content:"\F5DA"}.mdi-book-open-variant::before{content:"\F0BE"}.mdi-book-outline::before{content:"\FB40"}.mdi-book-play::before{content:"\FE9F"}.mdi-book-play-outline::before{content:"\FEA0"}.mdi-book-plus::before{content:"\F5DB"}.mdi-book-plus-multiple::before{content:"\FA94"}.mdi-book-remove::before{content:"\FA96"}.mdi-book-remove-multiple::before{content:"\FA95"}.mdi-book-search::before{content:"\FEA1"}.mdi-book-search-outline::before{content:"\FEA2"}.mdi-book-variant::before{content:"\F0BF"}.mdi-book-variant-multiple::before{content:"\F0BC"}.mdi-bookmark::before{content:"\F0C0"}.mdi-bookmark-check::before{content:"\F0C1"}.mdi-bookmark-check-outline::before{content:"\F03A6"}.mdi-bookmark-minus::before{content:"\F9CB"}.mdi-bookmark-minus-outline::before{content:"\F9CC"}.mdi-bookmark-multiple::before{content:"\FDF8"}.mdi-bookmark-multiple-outline::before{content:"\FDF9"}.mdi-bookmark-music::before{content:"\F0C2"}.mdi-bookmark-music-outline::before{content:"\F03A4"}.mdi-bookmark-off::before{content:"\F9CD"}.mdi-bookmark-off-outline::before{content:"\F9CE"}.mdi-bookmark-outline::before{content:"\F0C3"}.mdi-bookmark-plus::before{content:"\F0C5"}.mdi-bookmark-plus-outline::before{content:"\F0C4"}.mdi-bookmark-remove::before{content:"\F0C6"}.mdi-bookmark-remove-outline::before{content:"\F03A5"}.mdi-bookshelf::before{content:"\F028A"}.mdi-boom-gate::before{content:"\FEA3"}.mdi-boom-gate-alert::before{content:"\FEA4"}.mdi-boom-gate-alert-outline::before{content:"\FEA5"}.mdi-boom-gate-down::before{content:"\FEA6"}.mdi-boom-gate-down-outline::before{content:"\FEA7"}.mdi-boom-gate-outline::before{content:"\FEA8"}.mdi-boom-gate-up::before{content:"\FEA9"}.mdi-boom-gate-up-outline::before{content:"\FEAA"}.mdi-boombox::before{content:"\F5DC"}.mdi-boomerang::before{content:"\F00FA"}.mdi-bootstrap::before{content:"\F6C5"}.mdi-border-all::before{content:"\F0C7"}.mdi-border-all-variant::before{content:"\F8A0"}.mdi-border-bottom::before{content:"\F0C8"}.mdi-border-bottom-variant::before{content:"\F8A1"}.mdi-border-color::before{content:"\F0C9"}.mdi-border-horizontal::before{content:"\F0CA"}.mdi-border-inside::before{content:"\F0CB"}.mdi-border-left::before{content:"\F0CC"}.mdi-border-left-variant::before{content:"\F8A2"}.mdi-border-none::before{content:"\F0CD"}.mdi-border-none-variant::before{content:"\F8A3"}.mdi-border-outside::before{content:"\F0CE"}.mdi-border-right::before{content:"\F0CF"}.mdi-border-right-variant::before{content:"\F8A4"}.mdi-border-style::before{content:"\F0D0"}.mdi-border-top::before{content:"\F0D1"}.mdi-border-top-variant::before{content:"\F8A5"}.mdi-border-vertical::before{content:"\F0D2"}.mdi-bottle-soda::before{content:"\F009B"}.mdi-bottle-soda-classic::before{content:"\F009C"}.mdi-bottle-soda-classic-outline::before{content:"\F038E"}.mdi-bottle-soda-outline::before{content:"\F009D"}.mdi-bottle-tonic::before{content:"\F0159"}.mdi-bottle-tonic-outline::before{content:"\F015A"}.mdi-bottle-tonic-plus::before{content:"\F015B"}.mdi-bottle-tonic-plus-outline::before{content:"\F015C"}.mdi-bottle-tonic-skull::before{content:"\F015D"}.mdi-bottle-tonic-skull-outline::before{content:"\F015E"}.mdi-bottle-wine::before{content:"\F853"}.mdi-bottle-wine-outline::before{content:"\F033B"}.mdi-bow-tie::before{content:"\F677"}.mdi-bowl::before{content:"\F617"}.mdi-bowling::before{content:"\F0D3"}.mdi-box::before{content:"\F0D4"}.mdi-box-cutter::before{content:"\F0D5"}.mdi-box-shadow::before{content:"\F637"}.mdi-boxing-glove::before{content:"\FB41"}.mdi-braille::before{content:"\F9CF"}.mdi-brain::before{content:"\F9D0"}.mdi-bread-slice::before{content:"\FCCA"}.mdi-bread-slice-outline::before{content:"\FCCB"}.mdi-bridge::before{content:"\F618"}.mdi-briefcase::before{content:"\F0D6"}.mdi-briefcase-account::before{content:"\FCCC"}.mdi-briefcase-account-outline::before{content:"\FCCD"}.mdi-briefcase-check::before{content:"\F0D7"}.mdi-briefcase-check-outline::before{content:"\F0349"}.mdi-briefcase-clock::before{content:"\F00FB"}.mdi-briefcase-clock-outline::before{content:"\F00FC"}.mdi-briefcase-download::before{content:"\F0D8"}.mdi-briefcase-download-outline::before{content:"\FC19"}.mdi-briefcase-edit::before{content:"\FA97"}.mdi-briefcase-edit-outline::before{content:"\FC1A"}.mdi-briefcase-minus::before{content:"\FA29"}.mdi-briefcase-minus-outline::before{content:"\FC1B"}.mdi-briefcase-outline::before{content:"\F813"}.mdi-briefcase-plus::before{content:"\FA2A"}.mdi-briefcase-plus-outline::before{content:"\FC1C"}.mdi-briefcase-remove::before{content:"\FA2B"}.mdi-briefcase-remove-outline::before{content:"\FC1D"}.mdi-briefcase-search::before{content:"\FA2C"}.mdi-briefcase-search-outline::before{content:"\FC1E"}.mdi-briefcase-upload::before{content:"\F0D9"}.mdi-briefcase-upload-outline::before{content:"\FC1F"}.mdi-brightness-1::before{content:"\F0DA"}.mdi-brightness-2::before{content:"\F0DB"}.mdi-brightness-3::before{content:"\F0DC"}.mdi-brightness-4::before{content:"\F0DD"}.mdi-brightness-5::before{content:"\F0DE"}.mdi-brightness-6::before{content:"\F0DF"}.mdi-brightness-7::before{content:"\F0E0"}.mdi-brightness-auto::before{content:"\F0E1"}.mdi-brightness-percent::before{content:"\FCCE"}.mdi-broom::before{content:"\F0E2"}.mdi-brush::before{content:"\F0E3"}.mdi-buddhism::before{content:"\F94A"}.mdi-buffer::before{content:"\F619"}.mdi-bug::before{content:"\F0E4"}.mdi-bug-check::before{content:"\FA2D"}.mdi-bug-check-outline::before{content:"\FA2E"}.mdi-bug-outline::before{content:"\FA2F"}.mdi-bugle::before{content:"\FD90"}.mdi-bulldozer::before{content:"\FB07"}.mdi-bullet::before{content:"\FCCF"}.mdi-bulletin-board::before{content:"\F0E5"}.mdi-bullhorn::before{content:"\F0E6"}.mdi-bullhorn-outline::before{content:"\FB08"}.mdi-bullseye::before{content:"\F5DD"}.mdi-bullseye-arrow::before{content:"\F8C8"}.mdi-bulma::before{content:"\F0312"}.mdi-bunk-bed::before{content:"\F032D"}.mdi-bus::before{content:"\F0E7"}.mdi-bus-alert::before{content:"\FA98"}.mdi-bus-articulated-end::before{content:"\F79B"}.mdi-bus-articulated-front::before{content:"\F79C"}.mdi-bus-clock::before{content:"\F8C9"}.mdi-bus-double-decker::before{content:"\F79D"}.mdi-bus-marker::before{content:"\F023D"}.mdi-bus-multiple::before{content:"\FF5C"}.mdi-bus-school::before{content:"\F79E"}.mdi-bus-side::before{content:"\F79F"}.mdi-bus-stop::before{content:"\F0034"}.mdi-bus-stop-covered::before{content:"\F0035"}.mdi-bus-stop-uncovered::before{content:"\F0036"}.mdi-cached::before{content:"\F0E8"}.mdi-cactus::before{content:"\FD91"}.mdi-cake::before{content:"\F0E9"}.mdi-cake-layered::before{content:"\F0EA"}.mdi-cake-variant::before{content:"\F0EB"}.mdi-calculator::before{content:"\F0EC"}.mdi-calculator-variant::before{content:"\FA99"}.mdi-calendar::before{content:"\F0ED"}.mdi-calendar-account::before{content:"\FEF4"}.mdi-calendar-account-outline::before{content:"\FEF5"}.mdi-calendar-alert::before{content:"\FA30"}.mdi-calendar-arrow-left::before{content:"\F015F"}.mdi-calendar-arrow-right::before{content:"\F0160"}.mdi-calendar-blank::before{content:"\F0EE"}.mdi-calendar-blank-multiple::before{content:"\F009E"}.mdi-calendar-blank-outline::before{content:"\FB42"}.mdi-calendar-check::before{content:"\F0EF"}.mdi-calendar-check-outline::before{content:"\FC20"}.mdi-calendar-clock::before{content:"\F0F0"}.mdi-calendar-edit::before{content:"\F8A6"}.mdi-calendar-export::before{content:"\FB09"}.mdi-calendar-heart::before{content:"\F9D1"}.mdi-calendar-import::before{content:"\FB0A"}.mdi-calendar-minus::before{content:"\FD38"}.mdi-calendar-month::before{content:"\FDFA"}.mdi-calendar-month-outline::before{content:"\FDFB"}.mdi-calendar-multiple::before{content:"\F0F1"}.mdi-calendar-multiple-check::before{content:"\F0F2"}.mdi-calendar-multiselect::before{content:"\FA31"}.mdi-calendar-outline::before{content:"\FB43"}.mdi-calendar-plus::before{content:"\F0F3"}.mdi-calendar-question::before{content:"\F691"}.mdi-calendar-range::before{content:"\F678"}.mdi-calendar-range-outline::before{content:"\FB44"}.mdi-calendar-remove::before{content:"\F0F4"}.mdi-calendar-remove-outline::before{content:"\FC21"}.mdi-calendar-repeat::before{content:"\FEAB"}.mdi-calendar-repeat-outline::before{content:"\FEAC"}.mdi-calendar-search::before{content:"\F94B"}.mdi-calendar-star::before{content:"\F9D2"}.mdi-calendar-text::before{content:"\F0F5"}.mdi-calendar-text-outline::before{content:"\FC22"}.mdi-calendar-today::before{content:"\F0F6"}.mdi-calendar-week::before{content:"\FA32"}.mdi-calendar-week-begin::before{content:"\FA33"}.mdi-calendar-weekend::before{content:"\FEF6"}.mdi-calendar-weekend-outline::before{content:"\FEF7"}.mdi-call-made::before{content:"\F0F7"}.mdi-call-merge::before{content:"\F0F8"}.mdi-call-missed::before{content:"\F0F9"}.mdi-call-received::before{content:"\F0FA"}.mdi-call-split::before{content:"\F0FB"}.mdi-camcorder::before{content:"\F0FC"}.mdi-camcorder-box::before{content:"\F0FD"}.mdi-camcorder-box-off::before{content:"\F0FE"}.mdi-camcorder-off::before{content:"\F0FF"}.mdi-camera::before{content:"\F100"}.mdi-camera-account::before{content:"\F8CA"}.mdi-camera-burst::before{content:"\F692"}.mdi-camera-control::before{content:"\FB45"}.mdi-camera-enhance::before{content:"\F101"}.mdi-camera-enhance-outline::before{content:"\FB46"}.mdi-camera-front::before{content:"\F102"}.mdi-camera-front-variant::before{content:"\F103"}.mdi-camera-gopro::before{content:"\F7A0"}.mdi-camera-image::before{content:"\F8CB"}.mdi-camera-iris::before{content:"\F104"}.mdi-camera-metering-center::before{content:"\F7A1"}.mdi-camera-metering-matrix::before{content:"\F7A2"}.mdi-camera-metering-partial::before{content:"\F7A3"}.mdi-camera-metering-spot::before{content:"\F7A4"}.mdi-camera-off::before{content:"\F5DF"}.mdi-camera-outline::before{content:"\FD39"}.mdi-camera-party-mode::before{content:"\F105"}.mdi-camera-plus::before{content:"\FEF8"}.mdi-camera-plus-outline::before{content:"\FEF9"}.mdi-camera-rear::before{content:"\F106"}.mdi-camera-rear-variant::before{content:"\F107"}.mdi-camera-retake::before{content:"\FDFC"}.mdi-camera-retake-outline::before{content:"\FDFD"}.mdi-camera-switch::before{content:"\F108"}.mdi-camera-timer::before{content:"\F109"}.mdi-camera-wireless::before{content:"\FD92"}.mdi-camera-wireless-outline::before{content:"\FD93"}.mdi-campfire::before{content:"\FEFA"}.mdi-cancel::before{content:"\F739"}.mdi-candle::before{content:"\F5E2"}.mdi-candycane::before{content:"\F10A"}.mdi-cannabis::before{content:"\F7A5"}.mdi-caps-lock::before{content:"\FA9A"}.mdi-car::before{content:"\F10B"}.mdi-car-2-plus::before{content:"\F0037"}.mdi-car-3-plus::before{content:"\F0038"}.mdi-car-back::before{content:"\FDFE"}.mdi-car-battery::before{content:"\F10C"}.mdi-car-brake-abs::before{content:"\FC23"}.mdi-car-brake-alert::before{content:"\FC24"}.mdi-car-brake-hold::before{content:"\FD3A"}.mdi-car-brake-parking::before{content:"\FD3B"}.mdi-car-brake-retarder::before{content:"\F0039"}.mdi-car-child-seat::before{content:"\FFC3"}.mdi-car-clutch::before{content:"\F003A"}.mdi-car-connected::before{content:"\F10D"}.mdi-car-convertible::before{content:"\F7A6"}.mdi-car-coolant-level::before{content:"\F003B"}.mdi-car-cruise-control::before{content:"\FD3C"}.mdi-car-defrost-front::before{content:"\FD3D"}.mdi-car-defrost-rear::before{content:"\FD3E"}.mdi-car-door::before{content:"\FB47"}.mdi-car-door-lock::before{content:"\F00C8"}.mdi-car-electric::before{content:"\FB48"}.mdi-car-esp::before{content:"\FC25"}.mdi-car-estate::before{content:"\F7A7"}.mdi-car-hatchback::before{content:"\F7A8"}.mdi-car-info::before{content:"\F01E9"}.mdi-car-key::before{content:"\FB49"}.mdi-car-light-dimmed::before{content:"\FC26"}.mdi-car-light-fog::before{content:"\FC27"}.mdi-car-light-high::before{content:"\FC28"}.mdi-car-limousine::before{content:"\F8CC"}.mdi-car-multiple::before{content:"\FB4A"}.mdi-car-off::before{content:"\FDFF"}.mdi-car-parking-lights::before{content:"\FD3F"}.mdi-car-pickup::before{content:"\F7A9"}.mdi-car-seat::before{content:"\FFC4"}.mdi-car-seat-cooler::before{content:"\FFC5"}.mdi-car-seat-heater::before{content:"\FFC6"}.mdi-car-shift-pattern::before{content:"\FF5D"}.mdi-car-side::before{content:"\F7AA"}.mdi-car-sports::before{content:"\F7AB"}.mdi-car-tire-alert::before{content:"\FC29"}.mdi-car-traction-control::before{content:"\FD40"}.mdi-car-turbocharger::before{content:"\F003C"}.mdi-car-wash::before{content:"\F10E"}.mdi-car-windshield::before{content:"\F003D"}.mdi-car-windshield-outline::before{content:"\F003E"}.mdi-caravan::before{content:"\F7AC"}.mdi-card::before{content:"\FB4B"}.mdi-card-bulleted::before{content:"\FB4C"}.mdi-card-bulleted-off::before{content:"\FB4D"}.mdi-card-bulleted-off-outline::before{content:"\FB4E"}.mdi-card-bulleted-outline::before{content:"\FB4F"}.mdi-card-bulleted-settings::before{content:"\FB50"}.mdi-card-bulleted-settings-outline::before{content:"\FB51"}.mdi-card-outline::before{content:"\FB52"}.mdi-card-plus::before{content:"\F022A"}.mdi-card-plus-outline::before{content:"\F022B"}.mdi-card-search::before{content:"\F009F"}.mdi-card-search-outline::before{content:"\F00A0"}.mdi-card-text::before{content:"\FB53"}.mdi-card-text-outline::before{content:"\FB54"}.mdi-cards::before{content:"\F638"}.mdi-cards-club::before{content:"\F8CD"}.mdi-cards-diamond::before{content:"\F8CE"}.mdi-cards-diamond-outline::before{content:"\F003F"}.mdi-cards-heart::before{content:"\F8CF"}.mdi-cards-outline::before{content:"\F639"}.mdi-cards-playing-outline::before{content:"\F63A"}.mdi-cards-spade::before{content:"\F8D0"}.mdi-cards-variant::before{content:"\F6C6"}.mdi-carrot::before{content:"\F10F"}.mdi-cart::before{content:"\F110"}.mdi-cart-arrow-down::before{content:"\FD42"}.mdi-cart-arrow-right::before{content:"\FC2A"}.mdi-cart-arrow-up::before{content:"\FD43"}.mdi-cart-minus::before{content:"\FD44"}.mdi-cart-off::before{content:"\F66B"}.mdi-cart-outline::before{content:"\F111"}.mdi-cart-plus::before{content:"\F112"}.mdi-cart-remove::before{content:"\FD45"}.mdi-case-sensitive-alt::before{content:"\F113"}.mdi-cash::before{content:"\F114"}.mdi-cash-100::before{content:"\F115"}.mdi-cash-marker::before{content:"\FD94"}.mdi-cash-minus::before{content:"\F028B"}.mdi-cash-multiple::before{content:"\F116"}.mdi-cash-plus::before{content:"\F028C"}.mdi-cash-refund::before{content:"\FA9B"}.mdi-cash-register::before{content:"\FCD0"}.mdi-cash-remove::before{content:"\F028D"}.mdi-cash-usd::before{content:"\F01A1"}.mdi-cash-usd-outline::before{content:"\F117"}.mdi-cassette::before{content:"\F9D3"}.mdi-cast::before{content:"\F118"}.mdi-cast-audio::before{content:"\F0040"}.mdi-cast-connected::before{content:"\F119"}.mdi-cast-education::before{content:"\FE6D"}.mdi-cast-off::before{content:"\F789"}.mdi-castle::before{content:"\F11A"}.mdi-cat::before{content:"\F11B"}.mdi-cctv::before{content:"\F7AD"}.mdi-ceiling-light::before{content:"\F768"}.mdi-cellphone::before{content:"\F11C"}.mdi-cellphone-android::before{content:"\F11D"}.mdi-cellphone-arrow-down::before{content:"\F9D4"}.mdi-cellphone-basic::before{content:"\F11E"}.mdi-cellphone-dock::before{content:"\F11F"}.mdi-cellphone-erase::before{content:"\F94C"}.mdi-cellphone-information::before{content:"\FF5E"}.mdi-cellphone-iphone::before{content:"\F120"}.mdi-cellphone-key::before{content:"\F94D"}.mdi-cellphone-link::before{content:"\F121"}.mdi-cellphone-link-off::before{content:"\F122"}.mdi-cellphone-lock::before{content:"\F94E"}.mdi-cellphone-message::before{content:"\F8D2"}.mdi-cellphone-message-off::before{content:"\F00FD"}.mdi-cellphone-nfc::before{content:"\FEAD"}.mdi-cellphone-nfc-off::before{content:"\F0303"}.mdi-cellphone-off::before{content:"\F94F"}.mdi-cellphone-play::before{content:"\F0041"}.mdi-cellphone-screenshot::before{content:"\FA34"}.mdi-cellphone-settings::before{content:"\F123"}.mdi-cellphone-settings-variant::before{content:"\F950"}.mdi-cellphone-sound::before{content:"\F951"}.mdi-cellphone-text::before{content:"\F8D1"}.mdi-cellphone-wireless::before{content:"\F814"}.mdi-celtic-cross::before{content:"\FCD1"}.mdi-centos::before{content:"\F0145"}.mdi-certificate::before{content:"\F124"}.mdi-certificate-outline::before{content:"\F01B3"}.mdi-chair-rolling::before{content:"\FFBA"}.mdi-chair-school::before{content:"\F125"}.mdi-charity::before{content:"\FC2B"}.mdi-chart-arc::before{content:"\F126"}.mdi-chart-areaspline::before{content:"\F127"}.mdi-chart-areaspline-variant::before{content:"\FEAE"}.mdi-chart-bar::before{content:"\F128"}.mdi-chart-bar-stacked::before{content:"\F769"}.mdi-chart-bell-curve::before{content:"\FC2C"}.mdi-chart-bell-curve-cumulative::before{content:"\FFC7"}.mdi-chart-bubble::before{content:"\F5E3"}.mdi-chart-donut::before{content:"\F7AE"}.mdi-chart-donut-variant::before{content:"\F7AF"}.mdi-chart-gantt::before{content:"\F66C"}.mdi-chart-histogram::before{content:"\F129"}.mdi-chart-line::before{content:"\F12A"}.mdi-chart-line-stacked::before{content:"\F76A"}.mdi-chart-line-variant::before{content:"\F7B0"}.mdi-chart-multiline::before{content:"\F8D3"}.mdi-chart-multiple::before{content:"\F023E"}.mdi-chart-pie::before{content:"\F12B"}.mdi-chart-ppf::before{content:"\F03AB"}.mdi-chart-scatter-plot::before{content:"\FEAF"}.mdi-chart-scatter-plot-hexbin::before{content:"\F66D"}.mdi-chart-snakey::before{content:"\F020A"}.mdi-chart-snakey-variant::before{content:"\F020B"}.mdi-chart-timeline::before{content:"\F66E"}.mdi-chart-timeline-variant::before{content:"\FEB0"}.mdi-chart-tree::before{content:"\FEB1"}.mdi-chat::before{content:"\FB55"}.mdi-chat-alert::before{content:"\FB56"}.mdi-chat-alert-outline::before{content:"\F02F4"}.mdi-chat-outline::before{content:"\FEFB"}.mdi-chat-processing::before{content:"\FB57"}.mdi-chat-processing-outline::before{content:"\F02F5"}.mdi-chat-sleep::before{content:"\F02FC"}.mdi-chat-sleep-outline::before{content:"\F02FD"}.mdi-check::before{content:"\F12C"}.mdi-check-all::before{content:"\F12D"}.mdi-check-bold::before{content:"\FE6E"}.mdi-check-box-multiple-outline::before{content:"\FC2D"}.mdi-check-box-outline::before{content:"\FC2E"}.mdi-check-circle::before{content:"\F5E0"}.mdi-check-circle-outline::before{content:"\F5E1"}.mdi-check-decagram::before{content:"\F790"}.mdi-check-network::before{content:"\FC2F"}.mdi-check-network-outline::before{content:"\FC30"}.mdi-check-outline::before{content:"\F854"}.mdi-check-underline::before{content:"\FE70"}.mdi-check-underline-circle::before{content:"\FE71"}.mdi-check-underline-circle-outline::before{content:"\FE72"}.mdi-checkbook::before{content:"\FA9C"}.mdi-checkbox-blank::before{content:"\F12E"}.mdi-checkbox-blank-circle::before{content:"\F12F"}.mdi-checkbox-blank-circle-outline::before{content:"\F130"}.mdi-checkbox-blank-off::before{content:"\F0317"}.mdi-checkbox-blank-off-outline::before{content:"\F0318"}.mdi-checkbox-blank-outline::before{content:"\F131"}.mdi-checkbox-intermediate::before{content:"\F855"}.mdi-checkbox-marked::before{content:"\F132"}.mdi-checkbox-marked-circle::before{content:"\F133"}.mdi-checkbox-marked-circle-outline::before{content:"\F134"}.mdi-checkbox-marked-outline::before{content:"\F135"}.mdi-checkbox-multiple-blank::before{content:"\F136"}.mdi-checkbox-multiple-blank-circle::before{content:"\F63B"}.mdi-checkbox-multiple-blank-circle-outline::before{content:"\F63C"}.mdi-checkbox-multiple-blank-outline::before{content:"\F137"}.mdi-checkbox-multiple-marked::before{content:"\F138"}.mdi-checkbox-multiple-marked-circle::before{content:"\F63D"}.mdi-checkbox-multiple-marked-circle-outline::before{content:"\F63E"}.mdi-checkbox-multiple-marked-outline::before{content:"\F139"}.mdi-checkerboard::before{content:"\F13A"}.mdi-checkerboard-minus::before{content:"\F022D"}.mdi-checkerboard-plus::before{content:"\F022C"}.mdi-checkerboard-remove::before{content:"\F022E"}.mdi-cheese::before{content:"\F02E4"}.mdi-chef-hat::before{content:"\FB58"}.mdi-chemical-weapon::before{content:"\F13B"}.mdi-chess-bishop::before{content:"\F85B"}.mdi-chess-king::before{content:"\F856"}.mdi-chess-knight::before{content:"\F857"}.mdi-chess-pawn::before{content:"\F858"}.mdi-chess-queen::before{content:"\F859"}.mdi-chess-rook::before{content:"\F85A"}.mdi-chevron-double-down::before{content:"\F13C"}.mdi-chevron-double-left::before{content:"\F13D"}.mdi-chevron-double-right::before{content:"\F13E"}.mdi-chevron-double-up::before{content:"\F13F"}.mdi-chevron-down::before{content:"\F140"}.mdi-chevron-down-box::before{content:"\F9D5"}.mdi-chevron-down-box-outline::before{content:"\F9D6"}.mdi-chevron-down-circle::before{content:"\FB0B"}.mdi-chevron-down-circle-outline::before{content:"\FB0C"}.mdi-chevron-left::before{content:"\F141"}.mdi-chevron-left-box::before{content:"\F9D7"}.mdi-chevron-left-box-outline::before{content:"\F9D8"}.mdi-chevron-left-circle::before{content:"\FB0D"}.mdi-chevron-left-circle-outline::before{content:"\FB0E"}.mdi-chevron-right::before{content:"\F142"}.mdi-chevron-right-box::before{content:"\F9D9"}.mdi-chevron-right-box-outline::before{content:"\F9DA"}.mdi-chevron-right-circle::before{content:"\FB0F"}.mdi-chevron-right-circle-outline::before{content:"\FB10"}.mdi-chevron-triple-down::before{content:"\FD95"}.mdi-chevron-triple-left::before{content:"\FD96"}.mdi-chevron-triple-right::before{content:"\FD97"}.mdi-chevron-triple-up::before{content:"\FD98"}.mdi-chevron-up::before{content:"\F143"}.mdi-chevron-up-box::before{content:"\F9DB"}.mdi-chevron-up-box-outline::before{content:"\F9DC"}.mdi-chevron-up-circle::before{content:"\FB11"}.mdi-chevron-up-circle-outline::before{content:"\FB12"}.mdi-chili-hot::before{content:"\F7B1"}.mdi-chili-medium::before{content:"\F7B2"}.mdi-chili-mild::before{content:"\F7B3"}.mdi-chip::before{content:"\F61A"}.mdi-christianity::before{content:"\F952"}.mdi-christianity-outline::before{content:"\FCD2"}.mdi-church::before{content:"\F144"}.mdi-cigar::before{content:"\F01B4"}.mdi-circle::before{content:"\F764"}.mdi-circle-double::before{content:"\FEB2"}.mdi-circle-edit-outline::before{content:"\F8D4"}.mdi-circle-expand::before{content:"\FEB3"}.mdi-circle-medium::before{content:"\F9DD"}.mdi-circle-off-outline::before{content:"\F00FE"}.mdi-circle-outline::before{content:"\F765"}.mdi-circle-slice-1::before{content:"\FA9D"}.mdi-circle-slice-2::before{content:"\FA9E"}.mdi-circle-slice-3::before{content:"\FA9F"}.mdi-circle-slice-4::before{content:"\FAA0"}.mdi-circle-slice-5::before{content:"\FAA1"}.mdi-circle-slice-6::before{content:"\FAA2"}.mdi-circle-slice-7::before{content:"\FAA3"}.mdi-circle-slice-8::before{content:"\FAA4"}.mdi-circle-small::before{content:"\F9DE"}.mdi-circular-saw::before{content:"\FE73"}.mdi-cisco-webex::before{content:"\F145"}.mdi-city::before{content:"\F146"}.mdi-city-variant::before{content:"\FA35"}.mdi-city-variant-outline::before{content:"\FA36"}.mdi-clipboard::before{content:"\F147"}.mdi-clipboard-account::before{content:"\F148"}.mdi-clipboard-account-outline::before{content:"\FC31"}.mdi-clipboard-alert::before{content:"\F149"}.mdi-clipboard-alert-outline::before{content:"\FCD3"}.mdi-clipboard-arrow-down::before{content:"\F14A"}.mdi-clipboard-arrow-down-outline::before{content:"\FC32"}.mdi-clipboard-arrow-left::before{content:"\F14B"}.mdi-clipboard-arrow-left-outline::before{content:"\FCD4"}.mdi-clipboard-arrow-right::before{content:"\FCD5"}.mdi-clipboard-arrow-right-outline::before{content:"\FCD6"}.mdi-clipboard-arrow-up::before{content:"\FC33"}.mdi-clipboard-arrow-up-outline::before{content:"\FC34"}.mdi-clipboard-check::before{content:"\F14C"}.mdi-clipboard-check-multiple::before{content:"\F028E"}.mdi-clipboard-check-multiple-outline::before{content:"\F028F"}.mdi-clipboard-check-outline::before{content:"\F8A7"}.mdi-clipboard-file::before{content:"\F0290"}.mdi-clipboard-file-outline::before{content:"\F0291"}.mdi-clipboard-flow::before{content:"\F6C7"}.mdi-clipboard-flow-outline::before{content:"\F0142"}.mdi-clipboard-list::before{content:"\F00FF"}.mdi-clipboard-list-outline::before{content:"\F0100"}.mdi-clipboard-multiple::before{content:"\F0292"}.mdi-clipboard-multiple-outline::before{content:"\F0293"}.mdi-clipboard-outline::before{content:"\F14D"}.mdi-clipboard-play::before{content:"\FC35"}.mdi-clipboard-play-multiple::before{content:"\F0294"}.mdi-clipboard-play-multiple-outline::before{content:"\F0295"}.mdi-clipboard-play-outline::before{content:"\FC36"}.mdi-clipboard-plus::before{content:"\F750"}.mdi-clipboard-plus-outline::before{content:"\F034A"}.mdi-clipboard-pulse::before{content:"\F85C"}.mdi-clipboard-pulse-outline::before{content:"\F85D"}.mdi-clipboard-text::before{content:"\F14E"}.mdi-clipboard-text-multiple::before{content:"\F0296"}.mdi-clipboard-text-multiple-outline::before{content:"\F0297"}.mdi-clipboard-text-outline::before{content:"\FA37"}.mdi-clipboard-text-play::before{content:"\FC37"}.mdi-clipboard-text-play-outline::before{content:"\FC38"}.mdi-clippy::before{content:"\F14F"}.mdi-clock::before{content:"\F953"}.mdi-clock-alert::before{content:"\F954"}.mdi-clock-alert-outline::before{content:"\F5CE"}.mdi-clock-check::before{content:"\FFC8"}.mdi-clock-check-outline::before{content:"\FFC9"}.mdi-clock-digital::before{content:"\FEB4"}.mdi-clock-end::before{content:"\F151"}.mdi-clock-fast::before{content:"\F152"}.mdi-clock-in::before{content:"\F153"}.mdi-clock-out::before{content:"\F154"}.mdi-clock-outline::before{content:"\F150"}.mdi-clock-start::before{content:"\F155"}.mdi-close::before{content:"\F156"}.mdi-close-box::before{content:"\F157"}.mdi-close-box-multiple::before{content:"\FC39"}.mdi-close-box-multiple-outline::before{content:"\FC3A"}.mdi-close-box-outline::before{content:"\F158"}.mdi-close-circle::before{content:"\F159"}.mdi-close-circle-outline::before{content:"\F15A"}.mdi-close-network::before{content:"\F15B"}.mdi-close-network-outline::before{content:"\FC3B"}.mdi-close-octagon::before{content:"\F15C"}.mdi-close-octagon-outline::before{content:"\F15D"}.mdi-close-outline::before{content:"\F6C8"}.mdi-closed-caption::before{content:"\F15E"}.mdi-closed-caption-outline::before{content:"\FD99"}.mdi-cloud::before{content:"\F15F"}.mdi-cloud-alert::before{content:"\F9DF"}.mdi-cloud-braces::before{content:"\F7B4"}.mdi-cloud-check::before{content:"\F160"}.mdi-cloud-check-outline::before{content:"\F02F7"}.mdi-cloud-circle::before{content:"\F161"}.mdi-cloud-download::before{content:"\F162"}.mdi-cloud-download-outline::before{content:"\FB59"}.mdi-cloud-lock::before{content:"\F021C"}.mdi-cloud-lock-outline::before{content:"\F021D"}.mdi-cloud-off-outline::before{content:"\F164"}.mdi-cloud-outline::before{content:"\F163"}.mdi-cloud-print::before{content:"\F165"}.mdi-cloud-print-outline::before{content:"\F166"}.mdi-cloud-question::before{content:"\FA38"}.mdi-cloud-search::before{content:"\F955"}.mdi-cloud-search-outline::before{content:"\F956"}.mdi-cloud-sync::before{content:"\F63F"}.mdi-cloud-sync-outline::before{content:"\F0301"}.mdi-cloud-tags::before{content:"\F7B5"}.mdi-cloud-upload::before{content:"\F167"}.mdi-cloud-upload-outline::before{content:"\FB5A"}.mdi-clover::before{content:"\F815"}.mdi-coach-lamp::before{content:"\F0042"}.mdi-coat-rack::before{content:"\F00C9"}.mdi-code-array::before{content:"\F168"}.mdi-code-braces::before{content:"\F169"}.mdi-code-braces-box::before{content:"\F0101"}.mdi-code-brackets::before{content:"\F16A"}.mdi-code-equal::before{content:"\F16B"}.mdi-code-greater-than::before{content:"\F16C"}.mdi-code-greater-than-or-equal::before{content:"\F16D"}.mdi-code-less-than::before{content:"\F16E"}.mdi-code-less-than-or-equal::before{content:"\F16F"}.mdi-code-not-equal::before{content:"\F170"}.mdi-code-not-equal-variant::before{content:"\F171"}.mdi-code-parentheses::before{content:"\F172"}.mdi-code-parentheses-box::before{content:"\F0102"}.mdi-code-string::before{content:"\F173"}.mdi-code-tags::before{content:"\F174"}.mdi-code-tags-check::before{content:"\F693"}.mdi-codepen::before{content:"\F175"}.mdi-coffee::before{content:"\F176"}.mdi-coffee-maker::before{content:"\F00CA"}.mdi-coffee-off::before{content:"\FFCA"}.mdi-coffee-off-outline::before{content:"\FFCB"}.mdi-coffee-outline::before{content:"\F6C9"}.mdi-coffee-to-go::before{content:"\F177"}.mdi-coffee-to-go-outline::before{content:"\F0339"}.mdi-coffin::before{content:"\FB5B"}.mdi-cog-clockwise::before{content:"\F0208"}.mdi-cog-counterclockwise::before{content:"\F0209"}.mdi-cogs::before{content:"\F8D5"}.mdi-coin::before{content:"\F0196"}.mdi-coin-outline::before{content:"\F178"}.mdi-coins::before{content:"\F694"}.mdi-collage::before{content:"\F640"}.mdi-collapse-all::before{content:"\FAA5"}.mdi-collapse-all-outline::before{content:"\FAA6"}.mdi-color-helper::before{content:"\F179"}.mdi-comma::before{content:"\FE74"}.mdi-comma-box::before{content:"\FE75"}.mdi-comma-box-outline::before{content:"\FE76"}.mdi-comma-circle::before{content:"\FE77"}.mdi-comma-circle-outline::before{content:"\FE78"}.mdi-comment::before{content:"\F17A"}.mdi-comment-account::before{content:"\F17B"}.mdi-comment-account-outline::before{content:"\F17C"}.mdi-comment-alert::before{content:"\F17D"}.mdi-comment-alert-outline::before{content:"\F17E"}.mdi-comment-arrow-left::before{content:"\F9E0"}.mdi-comment-arrow-left-outline::before{content:"\F9E1"}.mdi-comment-arrow-right::before{content:"\F9E2"}.mdi-comment-arrow-right-outline::before{content:"\F9E3"}.mdi-comment-check::before{content:"\F17F"}.mdi-comment-check-outline::before{content:"\F180"}.mdi-comment-edit::before{content:"\F01EA"}.mdi-comment-edit-outline::before{content:"\F02EF"}.mdi-comment-eye::before{content:"\FA39"}.mdi-comment-eye-outline::before{content:"\FA3A"}.mdi-comment-multiple::before{content:"\F85E"}.mdi-comment-multiple-outline::before{content:"\F181"}.mdi-comment-outline::before{content:"\F182"}.mdi-comment-plus::before{content:"\F9E4"}.mdi-comment-plus-outline::before{content:"\F183"}.mdi-comment-processing::before{content:"\F184"}.mdi-comment-processing-outline::before{content:"\F185"}.mdi-comment-question::before{content:"\F816"}.mdi-comment-question-outline::before{content:"\F186"}.mdi-comment-quote::before{content:"\F0043"}.mdi-comment-quote-outline::before{content:"\F0044"}.mdi-comment-remove::before{content:"\F5DE"}.mdi-comment-remove-outline::before{content:"\F187"}.mdi-comment-search::before{content:"\FA3B"}.mdi-comment-search-outline::before{content:"\FA3C"}.mdi-comment-text::before{content:"\F188"}.mdi-comment-text-multiple::before{content:"\F85F"}.mdi-comment-text-multiple-outline::before{content:"\F860"}.mdi-comment-text-outline::before{content:"\F189"}.mdi-compare::before{content:"\F18A"}.mdi-compass::before{content:"\F18B"}.mdi-compass-off::before{content:"\FB5C"}.mdi-compass-off-outline::before{content:"\FB5D"}.mdi-compass-outline::before{content:"\F18C"}.mdi-compass-rose::before{content:"\F03AD"}.mdi-concourse-ci::before{content:"\F00CB"}.mdi-console::before{content:"\F18D"}.mdi-console-line::before{content:"\F7B6"}.mdi-console-network::before{content:"\F8A8"}.mdi-console-network-outline::before{content:"\FC3C"}.mdi-consolidate::before{content:"\F0103"}.mdi-contact-mail::before{content:"\F18E"}.mdi-contact-mail-outline::before{content:"\FEB5"}.mdi-contact-phone::before{content:"\FEB6"}.mdi-contact-phone-outline::before{content:"\FEB7"}.mdi-contactless-payment::before{content:"\FD46"}.mdi-contacts::before{content:"\F6CA"}.mdi-contain::before{content:"\FA3D"}.mdi-contain-end::before{content:"\FA3E"}.mdi-contain-start::before{content:"\FA3F"}.mdi-content-copy::before{content:"\F18F"}.mdi-content-cut::before{content:"\F190"}.mdi-content-duplicate::before{content:"\F191"}.mdi-content-paste::before{content:"\F192"}.mdi-content-save::before{content:"\F193"}.mdi-content-save-alert::before{content:"\FF5F"}.mdi-content-save-alert-outline::before{content:"\FF60"}.mdi-content-save-all::before{content:"\F194"}.mdi-content-save-all-outline::before{content:"\FF61"}.mdi-content-save-edit::before{content:"\FCD7"}.mdi-content-save-edit-outline::before{content:"\FCD8"}.mdi-content-save-move::before{content:"\FE79"}.mdi-content-save-move-outline::before{content:"\FE7A"}.mdi-content-save-outline::before{content:"\F817"}.mdi-content-save-settings::before{content:"\F61B"}.mdi-content-save-settings-outline::before{content:"\FB13"}.mdi-contrast::before{content:"\F195"}.mdi-contrast-box::before{content:"\F196"}.mdi-contrast-circle::before{content:"\F197"}.mdi-controller-classic::before{content:"\FB5E"}.mdi-controller-classic-outline::before{content:"\FB5F"}.mdi-cookie::before{content:"\F198"}.mdi-coolant-temperature::before{content:"\F3C8"}.mdi-copyright::before{content:"\F5E6"}.mdi-cordova::before{content:"\F957"}.mdi-corn::before{content:"\F7B7"}.mdi-counter::before{content:"\F199"}.mdi-cow::before{content:"\F19A"}.mdi-cowboy::before{content:"\FEB8"}.mdi-cpu-32-bit::before{content:"\FEFC"}.mdi-cpu-64-bit::before{content:"\FEFD"}.mdi-crane::before{content:"\F861"}.mdi-creation::before{content:"\F1C9"}.mdi-creative-commons::before{content:"\FD47"}.mdi-credit-card::before{content:"\F0010"}.mdi-credit-card-clock::before{content:"\FEFE"}.mdi-credit-card-clock-outline::before{content:"\FFBC"}.mdi-credit-card-marker::before{content:"\F6A7"}.mdi-credit-card-marker-outline::before{content:"\FD9A"}.mdi-credit-card-minus::before{content:"\FFCC"}.mdi-credit-card-minus-outline::before{content:"\FFCD"}.mdi-credit-card-multiple::before{content:"\F0011"}.mdi-credit-card-multiple-outline::before{content:"\F19C"}.mdi-credit-card-off::before{content:"\F0012"}.mdi-credit-card-off-outline::before{content:"\F5E4"}.mdi-credit-card-outline::before{content:"\F19B"}.mdi-credit-card-plus::before{content:"\F0013"}.mdi-credit-card-plus-outline::before{content:"\F675"}.mdi-credit-card-refund::before{content:"\F0014"}.mdi-credit-card-refund-outline::before{content:"\FAA7"}.mdi-credit-card-remove::before{content:"\FFCE"}.mdi-credit-card-remove-outline::before{content:"\FFCF"}.mdi-credit-card-scan::before{content:"\F0015"}.mdi-credit-card-scan-outline::before{content:"\F19D"}.mdi-credit-card-settings::before{content:"\F0016"}.mdi-credit-card-settings-outline::before{content:"\F8D6"}.mdi-credit-card-wireless::before{content:"\F801"}.mdi-credit-card-wireless-outline::before{content:"\FD48"}.mdi-cricket::before{content:"\FD49"}.mdi-crop::before{content:"\F19E"}.mdi-crop-free::before{content:"\F19F"}.mdi-crop-landscape::before{content:"\F1A0"}.mdi-crop-portrait::before{content:"\F1A1"}.mdi-crop-rotate::before{content:"\F695"}.mdi-crop-square::before{content:"\F1A2"}.mdi-crosshairs::before{content:"\F1A3"}.mdi-crosshairs-gps::before{content:"\F1A4"}.mdi-crosshairs-off::before{content:"\FF62"}.mdi-crosshairs-question::before{content:"\F0161"}.mdi-crown::before{content:"\F1A5"}.mdi-crown-outline::before{content:"\F01FB"}.mdi-cryengine::before{content:"\F958"}.mdi-crystal-ball::before{content:"\FB14"}.mdi-cube::before{content:"\F1A6"}.mdi-cube-outline::before{content:"\F1A7"}.mdi-cube-scan::before{content:"\FB60"}.mdi-cube-send::before{content:"\F1A8"}.mdi-cube-unfolded::before{content:"\F1A9"}.mdi-cup::before{content:"\F1AA"}.mdi-cup-off::before{content:"\F5E5"}.mdi-cup-off-outline::before{content:"\F03A8"}.mdi-cup-outline::before{content:"\F033A"}.mdi-cup-water::before{content:"\F1AB"}.mdi-cupboard::before{content:"\FF63"}.mdi-cupboard-outline::before{content:"\FF64"}.mdi-cupcake::before{content:"\F959"}.mdi-curling::before{content:"\F862"}.mdi-currency-bdt::before{content:"\F863"}.mdi-currency-brl::before{content:"\FB61"}.mdi-currency-btc::before{content:"\F1AC"}.mdi-currency-cny::before{content:"\F7B9"}.mdi-currency-eth::before{content:"\F7BA"}.mdi-currency-eur::before{content:"\F1AD"}.mdi-currency-eur-off::before{content:"\F0340"}.mdi-currency-gbp::before{content:"\F1AE"}.mdi-currency-ils::before{content:"\FC3D"}.mdi-currency-inr::before{content:"\F1AF"}.mdi-currency-jpy::before{content:"\F7BB"}.mdi-currency-krw::before{content:"\F7BC"}.mdi-currency-kzt::before{content:"\F864"}.mdi-currency-ngn::before{content:"\F1B0"}.mdi-currency-php::before{content:"\F9E5"}.mdi-currency-rial::before{content:"\FEB9"}.mdi-currency-rub::before{content:"\F1B1"}.mdi-currency-sign::before{content:"\F7BD"}.mdi-currency-try::before{content:"\F1B2"}.mdi-currency-twd::before{content:"\F7BE"}.mdi-currency-usd::before{content:"\F1B3"}.mdi-currency-usd-off::before{content:"\F679"}.mdi-current-ac::before{content:"\F95A"}.mdi-current-dc::before{content:"\F95B"}.mdi-cursor-default::before{content:"\F1B4"}.mdi-cursor-default-click::before{content:"\FCD9"}.mdi-cursor-default-click-outline::before{content:"\FCDA"}.mdi-cursor-default-gesture::before{content:"\F0152"}.mdi-cursor-default-gesture-outline::before{content:"\F0153"}.mdi-cursor-default-outline::before{content:"\F1B5"}.mdi-cursor-move::before{content:"\F1B6"}.mdi-cursor-pointer::before{content:"\F1B7"}.mdi-cursor-text::before{content:"\F5E7"}.mdi-database::before{content:"\F1B8"}.mdi-database-check::before{content:"\FAA8"}.mdi-database-edit::before{content:"\FB62"}.mdi-database-export::before{content:"\F95D"}.mdi-database-import::before{content:"\F95C"}.mdi-database-lock::before{content:"\FAA9"}.mdi-database-marker::before{content:"\F0321"}.mdi-database-minus::before{content:"\F1B9"}.mdi-database-plus::before{content:"\F1BA"}.mdi-database-refresh::before{content:"\FCDB"}.mdi-database-remove::before{content:"\FCDC"}.mdi-database-search::before{content:"\F865"}.mdi-database-settings::before{content:"\FCDD"}.mdi-death-star::before{content:"\F8D7"}.mdi-death-star-variant::before{content:"\F8D8"}.mdi-deathly-hallows::before{content:"\FB63"}.mdi-debian::before{content:"\F8D9"}.mdi-debug-step-into::before{content:"\F1BB"}.mdi-debug-step-out::before{content:"\F1BC"}.mdi-debug-step-over::before{content:"\F1BD"}.mdi-decagram::before{content:"\F76B"}.mdi-decagram-outline::before{content:"\F76C"}.mdi-decimal::before{content:"\F00CC"}.mdi-decimal-comma::before{content:"\F00CD"}.mdi-decimal-comma-decrease::before{content:"\F00CE"}.mdi-decimal-comma-increase::before{content:"\F00CF"}.mdi-decimal-decrease::before{content:"\F1BE"}.mdi-decimal-increase::before{content:"\F1BF"}.mdi-delete::before{content:"\F1C0"}.mdi-delete-alert::before{content:"\F00D0"}.mdi-delete-alert-outline::before{content:"\F00D1"}.mdi-delete-circle::before{content:"\F682"}.mdi-delete-circle-outline::before{content:"\FB64"}.mdi-delete-empty::before{content:"\F6CB"}.mdi-delete-empty-outline::before{content:"\FEBA"}.mdi-delete-forever::before{content:"\F5E8"}.mdi-delete-forever-outline::before{content:"\FB65"}.mdi-delete-off::before{content:"\F00D2"}.mdi-delete-off-outline::before{content:"\F00D3"}.mdi-delete-outline::before{content:"\F9E6"}.mdi-delete-restore::before{content:"\F818"}.mdi-delete-sweep::before{content:"\F5E9"}.mdi-delete-sweep-outline::before{content:"\FC3E"}.mdi-delete-variant::before{content:"\F1C1"}.mdi-delta::before{content:"\F1C2"}.mdi-desk::before{content:"\F0264"}.mdi-desk-lamp::before{content:"\F95E"}.mdi-deskphone::before{content:"\F1C3"}.mdi-desktop-classic::before{content:"\F7BF"}.mdi-desktop-mac::before{content:"\F1C4"}.mdi-desktop-mac-dashboard::before{content:"\F9E7"}.mdi-desktop-tower::before{content:"\F1C5"}.mdi-desktop-tower-monitor::before{content:"\FAAA"}.mdi-details::before{content:"\F1C6"}.mdi-dev-to::before{content:"\FD4A"}.mdi-developer-board::before{content:"\F696"}.mdi-deviantart::before{content:"\F1C7"}.mdi-devices::before{content:"\FFD0"}.mdi-diabetes::before{content:"\F0151"}.mdi-dialpad::before{content:"\F61C"}.mdi-diameter::before{content:"\FC3F"}.mdi-diameter-outline::before{content:"\FC40"}.mdi-diameter-variant::before{content:"\FC41"}.mdi-diamond::before{content:"\FB66"}.mdi-diamond-outline::before{content:"\FB67"}.mdi-diamond-stone::before{content:"\F1C8"}.mdi-dice-1::before{content:"\F1CA"}.mdi-dice-1-outline::before{content:"\F0175"}.mdi-dice-2::before{content:"\F1CB"}.mdi-dice-2-outline::before{content:"\F0176"}.mdi-dice-3::before{content:"\F1CC"}.mdi-dice-3-outline::before{content:"\F0177"}.mdi-dice-4::before{content:"\F1CD"}.mdi-dice-4-outline::before{content:"\F0178"}.mdi-dice-5::before{content:"\F1CE"}.mdi-dice-5-outline::before{content:"\F0179"}.mdi-dice-6::before{content:"\F1CF"}.mdi-dice-6-outline::before{content:"\F017A"}.mdi-dice-d10::before{content:"\F017E"}.mdi-dice-d10-outline::before{content:"\F76E"}.mdi-dice-d12::before{content:"\F017F"}.mdi-dice-d12-outline::before{content:"\F866"}.mdi-dice-d20::before{content:"\F0180"}.mdi-dice-d20-outline::before{content:"\F5EA"}.mdi-dice-d4::before{content:"\F017B"}.mdi-dice-d4-outline::before{content:"\F5EB"}.mdi-dice-d6::before{content:"\F017C"}.mdi-dice-d6-outline::before{content:"\F5EC"}.mdi-dice-d8::before{content:"\F017D"}.mdi-dice-d8-outline::before{content:"\F5ED"}.mdi-dice-multiple::before{content:"\F76D"}.mdi-dice-multiple-outline::before{content:"\F0181"}.mdi-dictionary::before{content:"\F61D"}.mdi-digital-ocean::before{content:"\F0262"}.mdi-dip-switch::before{content:"\F7C0"}.mdi-directions::before{content:"\F1D0"}.mdi-directions-fork::before{content:"\F641"}.mdi-disc::before{content:"\F5EE"}.mdi-disc-alert::before{content:"\F1D1"}.mdi-disc-player::before{content:"\F95F"}.mdi-discord::before{content:"\F66F"}.mdi-dishwasher::before{content:"\FAAB"}.mdi-dishwasher-alert::before{content:"\F01E3"}.mdi-dishwasher-off::before{content:"\F01E4"}.mdi-disqus::before{content:"\F1D2"}.mdi-disqus-outline::before{content:"\F1D3"}.mdi-distribute-horizontal-center::before{content:"\F01F4"}.mdi-distribute-horizontal-left::before{content:"\F01F3"}.mdi-distribute-horizontal-right::before{content:"\F01F5"}.mdi-distribute-vertical-bottom::before{content:"\F01F6"}.mdi-distribute-vertical-center::before{content:"\F01F7"}.mdi-distribute-vertical-top::before{content:"\F01F8"}.mdi-diving-flippers::before{content:"\FD9B"}.mdi-diving-helmet::before{content:"\FD9C"}.mdi-diving-scuba::before{content:"\FD9D"}.mdi-diving-scuba-flag::before{content:"\FD9E"}.mdi-diving-scuba-tank::before{content:"\FD9F"}.mdi-diving-scuba-tank-multiple::before{content:"\FDA0"}.mdi-diving-snorkel::before{content:"\FDA1"}.mdi-division::before{content:"\F1D4"}.mdi-division-box::before{content:"\F1D5"}.mdi-dlna::before{content:"\FA40"}.mdi-dna::before{content:"\F683"}.mdi-dns::before{content:"\F1D6"}.mdi-dns-outline::before{content:"\FB68"}.mdi-do-not-disturb::before{content:"\F697"}.mdi-do-not-disturb-off::before{content:"\F698"}.mdi-dock-bottom::before{content:"\F00D4"}.mdi-dock-left::before{content:"\F00D5"}.mdi-dock-right::before{content:"\F00D6"}.mdi-dock-window::before{content:"\F00D7"}.mdi-docker::before{content:"\F867"}.mdi-doctor::before{content:"\FA41"}.mdi-dog::before{content:"\FA42"}.mdi-dog-service::before{content:"\FAAC"}.mdi-dog-side::before{content:"\FA43"}.mdi-dolby::before{content:"\F6B2"}.mdi-dolly::before{content:"\FEBB"}.mdi-domain::before{content:"\F1D7"}.mdi-domain-off::before{content:"\FD4B"}.mdi-domain-plus::before{content:"\F00D8"}.mdi-domain-remove::before{content:"\F00D9"}.mdi-domino-mask::before{content:"\F0045"}.mdi-donkey::before{content:"\F7C1"}.mdi-door::before{content:"\F819"}.mdi-door-closed::before{content:"\F81A"}.mdi-door-closed-lock::before{content:"\F00DA"}.mdi-door-open::before{content:"\F81B"}.mdi-doorbell::before{content:"\F0311"}.mdi-doorbell-video::before{content:"\F868"}.mdi-dot-net::before{content:"\FAAD"}.mdi-dots-horizontal::before{content:"\F1D8"}.mdi-dots-horizontal-circle::before{content:"\F7C2"}.mdi-dots-horizontal-circle-outline::before{content:"\FB69"}.mdi-dots-vertical::before{content:"\F1D9"}.mdi-dots-vertical-circle::before{content:"\F7C3"}.mdi-dots-vertical-circle-outline::before{content:"\FB6A"}.mdi-douban::before{content:"\F699"}.mdi-download::before{content:"\F1DA"}.mdi-download-lock::before{content:"\F034B"}.mdi-download-lock-outline::before{content:"\F034C"}.mdi-download-multiple::before{content:"\F9E8"}.mdi-download-network::before{content:"\F6F3"}.mdi-download-network-outline::before{content:"\FC42"}.mdi-download-off::before{content:"\F00DB"}.mdi-download-off-outline::before{content:"\F00DC"}.mdi-download-outline::before{content:"\FB6B"}.mdi-drag::before{content:"\F1DB"}.mdi-drag-horizontal::before{content:"\F1DC"}.mdi-drag-horizontal-variant::before{content:"\F031B"}.mdi-drag-variant::before{content:"\FB6C"}.mdi-drag-vertical::before{content:"\F1DD"}.mdi-drag-vertical-variant::before{content:"\F031C"}.mdi-drama-masks::before{content:"\FCDE"}.mdi-draw::before{content:"\FF66"}.mdi-drawing::before{content:"\F1DE"}.mdi-drawing-box::before{content:"\F1DF"}.mdi-dresser::before{content:"\FF67"}.mdi-dresser-outline::before{content:"\FF68"}.mdi-dribbble::before{content:"\F1E0"}.mdi-dribbble-box::before{content:"\F1E1"}.mdi-drone::before{content:"\F1E2"}.mdi-dropbox::before{content:"\F1E3"}.mdi-drupal::before{content:"\F1E4"}.mdi-duck::before{content:"\F1E5"}.mdi-dumbbell::before{content:"\F1E6"}.mdi-dump-truck::before{content:"\FC43"}.mdi-ear-hearing::before{content:"\F7C4"}.mdi-ear-hearing-off::before{content:"\FA44"}.mdi-earth::before{content:"\F1E7"}.mdi-earth-arrow-right::before{content:"\F033C"}.mdi-earth-box::before{content:"\F6CC"}.mdi-earth-box-off::before{content:"\F6CD"}.mdi-earth-off::before{content:"\F1E8"}.mdi-edge::before{content:"\F1E9"}.mdi-edge-legacy::before{content:"\F027B"}.mdi-egg::before{content:"\FAAE"}.mdi-egg-easter::before{content:"\FAAF"}.mdi-eight-track::before{content:"\F9E9"}.mdi-eject::before{content:"\F1EA"}.mdi-eject-outline::before{content:"\FB6D"}.mdi-electric-switch::before{content:"\FEBC"}.mdi-electric-switch-closed::before{content:"\F0104"}.mdi-electron-framework::before{content:"\F0046"}.mdi-elephant::before{content:"\F7C5"}.mdi-elevation-decline::before{content:"\F1EB"}.mdi-elevation-rise::before{content:"\F1EC"}.mdi-elevator::before{content:"\F1ED"}.mdi-elevator-down::before{content:"\F02ED"}.mdi-elevator-passenger::before{content:"\F03AC"}.mdi-elevator-up::before{content:"\F02EC"}.mdi-ellipse::before{content:"\FEBD"}.mdi-ellipse-outline::before{content:"\FEBE"}.mdi-email::before{content:"\F1EE"}.mdi-email-alert::before{content:"\F6CE"}.mdi-email-alert-outline::before{content:"\FD1E"}.mdi-email-box::before{content:"\FCDF"}.mdi-email-check::before{content:"\FAB0"}.mdi-email-check-outline::before{content:"\FAB1"}.mdi-email-edit::before{content:"\FF00"}.mdi-email-edit-outline::before{content:"\FF01"}.mdi-email-lock::before{content:"\F1F1"}.mdi-email-mark-as-unread::before{content:"\FB6E"}.mdi-email-minus::before{content:"\FF02"}.mdi-email-minus-outline::before{content:"\FF03"}.mdi-email-multiple::before{content:"\FF04"}.mdi-email-multiple-outline::before{content:"\FF05"}.mdi-email-newsletter::before{content:"\FFD1"}.mdi-email-open::before{content:"\F1EF"}.mdi-email-open-multiple::before{content:"\FF06"}.mdi-email-open-multiple-outline::before{content:"\FF07"}.mdi-email-open-outline::before{content:"\F5EF"}.mdi-email-outline::before{content:"\F1F0"}.mdi-email-plus::before{content:"\F9EA"}.mdi-email-plus-outline::before{content:"\F9EB"}.mdi-email-receive::before{content:"\F0105"}.mdi-email-receive-outline::before{content:"\F0106"}.mdi-email-search::before{content:"\F960"}.mdi-email-search-outline::before{content:"\F961"}.mdi-email-send::before{content:"\F0107"}.mdi-email-send-outline::before{content:"\F0108"}.mdi-email-sync::before{content:"\F02F2"}.mdi-email-sync-outline::before{content:"\F02F3"}.mdi-email-variant::before{content:"\F5F0"}.mdi-ember::before{content:"\FB15"}.mdi-emby::before{content:"\F6B3"}.mdi-emoticon::before{content:"\FC44"}.mdi-emoticon-angry::before{content:"\FC45"}.mdi-emoticon-angry-outline::before{content:"\FC46"}.mdi-emoticon-confused::before{content:"\F0109"}.mdi-emoticon-confused-outline::before{content:"\F010A"}.mdi-emoticon-cool::before{content:"\FC47"}.mdi-emoticon-cool-outline::before{content:"\F1F3"}.mdi-emoticon-cry::before{content:"\FC48"}.mdi-emoticon-cry-outline::before{content:"\FC49"}.mdi-emoticon-dead::before{content:"\FC4A"}.mdi-emoticon-dead-outline::before{content:"\F69A"}.mdi-emoticon-devil::before{content:"\FC4B"}.mdi-emoticon-devil-outline::before{content:"\F1F4"}.mdi-emoticon-excited::before{content:"\FC4C"}.mdi-emoticon-excited-outline::before{content:"\F69B"}.mdi-emoticon-frown::before{content:"\FF69"}.mdi-emoticon-frown-outline::before{content:"\FF6A"}.mdi-emoticon-happy::before{content:"\FC4D"}.mdi-emoticon-happy-outline::before{content:"\F1F5"}.mdi-emoticon-kiss::before{content:"\FC4E"}.mdi-emoticon-kiss-outline::before{content:"\FC4F"}.mdi-emoticon-lol::before{content:"\F023F"}.mdi-emoticon-lol-outline::before{content:"\F0240"}.mdi-emoticon-neutral::before{content:"\FC50"}.mdi-emoticon-neutral-outline::before{content:"\F1F6"}.mdi-emoticon-outline::before{content:"\F1F2"}.mdi-emoticon-poop::before{content:"\F1F7"}.mdi-emoticon-poop-outline::before{content:"\FC51"}.mdi-emoticon-sad::before{content:"\FC52"}.mdi-emoticon-sad-outline::before{content:"\F1F8"}.mdi-emoticon-tongue::before{content:"\F1F9"}.mdi-emoticon-tongue-outline::before{content:"\FC53"}.mdi-emoticon-wink::before{content:"\FC54"}.mdi-emoticon-wink-outline::before{content:"\FC55"}.mdi-engine::before{content:"\F1FA"}.mdi-engine-off::before{content:"\FA45"}.mdi-engine-off-outline::before{content:"\FA46"}.mdi-engine-outline::before{content:"\F1FB"}.mdi-epsilon::before{content:"\F010B"}.mdi-equal::before{content:"\F1FC"}.mdi-equal-box::before{content:"\F1FD"}.mdi-equalizer::before{content:"\FEBF"}.mdi-equalizer-outline::before{content:"\FEC0"}.mdi-eraser::before{content:"\F1FE"}.mdi-eraser-variant::before{content:"\F642"}.mdi-escalator::before{content:"\F1FF"}.mdi-escalator-down::before{content:"\F02EB"}.mdi-escalator-up::before{content:"\F02EA"}.mdi-eslint::before{content:"\FC56"}.mdi-et::before{content:"\FAB2"}.mdi-ethereum::before{content:"\F869"}.mdi-ethernet::before{content:"\F200"}.mdi-ethernet-cable::before{content:"\F201"}.mdi-ethernet-cable-off::before{content:"\F202"}.mdi-etsy::before{content:"\F203"}.mdi-ev-station::before{content:"\F5F1"}.mdi-eventbrite::before{content:"\F7C6"}.mdi-evernote::before{content:"\F204"}.mdi-excavator::before{content:"\F0047"}.mdi-exclamation::before{content:"\F205"}.mdi-exclamation-thick::before{content:"\F0263"}.mdi-exit-run::before{content:"\FA47"}.mdi-exit-to-app::before{content:"\F206"}.mdi-expand-all::before{content:"\FAB3"}.mdi-expand-all-outline::before{content:"\FAB4"}.mdi-expansion-card::before{content:"\F8AD"}.mdi-expansion-card-variant::before{content:"\FFD2"}.mdi-exponent::before{content:"\F962"}.mdi-exponent-box::before{content:"\F963"}.mdi-export::before{content:"\F207"}.mdi-export-variant::before{content:"\FB6F"}.mdi-eye::before{content:"\F208"}.mdi-eye-check::before{content:"\FCE0"}.mdi-eye-check-outline::before{content:"\FCE1"}.mdi-eye-circle::before{content:"\FB70"}.mdi-eye-circle-outline::before{content:"\FB71"}.mdi-eye-minus::before{content:"\F0048"}.mdi-eye-minus-outline::before{content:"\F0049"}.mdi-eye-off::before{content:"\F209"}.mdi-eye-off-outline::before{content:"\F6D0"}.mdi-eye-outline::before{content:"\F6CF"}.mdi-eye-plus::before{content:"\F86A"}.mdi-eye-plus-outline::before{content:"\F86B"}.mdi-eye-settings::before{content:"\F86C"}.mdi-eye-settings-outline::before{content:"\F86D"}.mdi-eyedropper::before{content:"\F20A"}.mdi-eyedropper-variant::before{content:"\F20B"}.mdi-face::before{content:"\F643"}.mdi-face-agent::before{content:"\FD4C"}.mdi-face-outline::before{content:"\FB72"}.mdi-face-profile::before{content:"\F644"}.mdi-face-profile-woman::before{content:"\F00A1"}.mdi-face-recognition::before{content:"\FC57"}.mdi-face-woman::before{content:"\F00A2"}.mdi-face-woman-outline::before{content:"\F00A3"}.mdi-facebook::before{content:"\F20C"}.mdi-facebook-box::before{content:"\F20D"}.mdi-facebook-messenger::before{content:"\F20E"}.mdi-facebook-workplace::before{content:"\FB16"}.mdi-factory::before{content:"\F20F"}.mdi-fan::before{content:"\F210"}.mdi-fan-off::before{content:"\F81C"}.mdi-fast-forward::before{content:"\F211"}.mdi-fast-forward-10::before{content:"\FD4D"}.mdi-fast-forward-30::before{content:"\FCE2"}.mdi-fast-forward-5::before{content:"\F0223"}.mdi-fast-forward-outline::before{content:"\F6D1"}.mdi-fax::before{content:"\F212"}.mdi-feather::before{content:"\F6D2"}.mdi-feature-search::before{content:"\FA48"}.mdi-feature-search-outline::before{content:"\FA49"}.mdi-fedora::before{content:"\F8DA"}.mdi-ferris-wheel::before{content:"\FEC1"}.mdi-ferry::before{content:"\F213"}.mdi-file::before{content:"\F214"}.mdi-file-account::before{content:"\F73A"}.mdi-file-account-outline::before{content:"\F004A"}.mdi-file-alert::before{content:"\FA4A"}.mdi-file-alert-outline::before{content:"\FA4B"}.mdi-file-cabinet::before{content:"\FAB5"}.mdi-file-cad::before{content:"\FF08"}.mdi-file-cad-box::before{content:"\FF09"}.mdi-file-cancel::before{content:"\FDA2"}.mdi-file-cancel-outline::before{content:"\FDA3"}.mdi-file-certificate::before{content:"\F01B1"}.mdi-file-certificate-outline::before{content:"\F01B2"}.mdi-file-chart::before{content:"\F215"}.mdi-file-chart-outline::before{content:"\F004B"}.mdi-file-check::before{content:"\F216"}.mdi-file-check-outline::before{content:"\FE7B"}.mdi-file-clock::before{content:"\F030C"}.mdi-file-clock-outline::before{content:"\F030D"}.mdi-file-cloud::before{content:"\F217"}.mdi-file-cloud-outline::before{content:"\F004C"}.mdi-file-code::before{content:"\F22E"}.mdi-file-code-outline::before{content:"\F004D"}.mdi-file-compare::before{content:"\F8A9"}.mdi-file-delimited::before{content:"\F218"}.mdi-file-delimited-outline::before{content:"\FEC2"}.mdi-file-document::before{content:"\F219"}.mdi-file-document-box::before{content:"\F21A"}.mdi-file-document-box-check::before{content:"\FEC3"}.mdi-file-document-box-check-outline::before{content:"\FEC4"}.mdi-file-document-box-minus::before{content:"\FEC5"}.mdi-file-document-box-minus-outline::before{content:"\FEC6"}.mdi-file-document-box-multiple::before{content:"\FAB6"}.mdi-file-document-box-multiple-outline::before{content:"\FAB7"}.mdi-file-document-box-outline::before{content:"\F9EC"}.mdi-file-document-box-plus::before{content:"\FEC7"}.mdi-file-document-box-plus-outline::before{content:"\FEC8"}.mdi-file-document-box-remove::before{content:"\FEC9"}.mdi-file-document-box-remove-outline::before{content:"\FECA"}.mdi-file-document-box-search::before{content:"\FECB"}.mdi-file-document-box-search-outline::before{content:"\FECC"}.mdi-file-document-edit::before{content:"\FDA4"}.mdi-file-document-edit-outline::before{content:"\FDA5"}.mdi-file-document-outline::before{content:"\F9ED"}.mdi-file-download::before{content:"\F964"}.mdi-file-download-outline::before{content:"\F965"}.mdi-file-edit::before{content:"\F0212"}.mdi-file-edit-outline::before{content:"\F0213"}.mdi-file-excel::before{content:"\F21B"}.mdi-file-excel-box::before{content:"\F21C"}.mdi-file-excel-box-outline::before{content:"\F004E"}.mdi-file-excel-outline::before{content:"\F004F"}.mdi-file-export::before{content:"\F21D"}.mdi-file-export-outline::before{content:"\F0050"}.mdi-file-eye::before{content:"\FDA6"}.mdi-file-eye-outline::before{content:"\FDA7"}.mdi-file-find::before{content:"\F21E"}.mdi-file-find-outline::before{content:"\FB73"}.mdi-file-hidden::before{content:"\F613"}.mdi-file-image::before{content:"\F21F"}.mdi-file-image-outline::before{content:"\FECD"}.mdi-file-import::before{content:"\F220"}.mdi-file-import-outline::before{content:"\F0051"}.mdi-file-key::before{content:"\F01AF"}.mdi-file-key-outline::before{content:"\F01B0"}.mdi-file-link::before{content:"\F01A2"}.mdi-file-link-outline::before{content:"\F01A3"}.mdi-file-lock::before{content:"\F221"}.mdi-file-lock-outline::before{content:"\F0052"}.mdi-file-move::before{content:"\FAB8"}.mdi-file-move-outline::before{content:"\F0053"}.mdi-file-multiple::before{content:"\F222"}.mdi-file-multiple-outline::before{content:"\F0054"}.mdi-file-music::before{content:"\F223"}.mdi-file-music-outline::before{content:"\FE7C"}.mdi-file-outline::before{content:"\F224"}.mdi-file-pdf::before{content:"\F225"}.mdi-file-pdf-box::before{content:"\F226"}.mdi-file-pdf-box-outline::before{content:"\FFD3"}.mdi-file-pdf-outline::before{content:"\FE7D"}.mdi-file-percent::before{content:"\F81D"}.mdi-file-percent-outline::before{content:"\F0055"}.mdi-file-phone::before{content:"\F01A4"}.mdi-file-phone-outline::before{content:"\F01A5"}.mdi-file-plus::before{content:"\F751"}.mdi-file-plus-outline::before{content:"\FF0A"}.mdi-file-powerpoint::before{content:"\F227"}.mdi-file-powerpoint-box::before{content:"\F228"}.mdi-file-powerpoint-box-outline::before{content:"\F0056"}.mdi-file-powerpoint-outline::before{content:"\F0057"}.mdi-file-presentation-box::before{content:"\F229"}.mdi-file-question::before{content:"\F86E"}.mdi-file-question-outline::before{content:"\F0058"}.mdi-file-remove::before{content:"\FB74"}.mdi-file-remove-outline::before{content:"\F0059"}.mdi-file-replace::before{content:"\FB17"}.mdi-file-replace-outline::before{content:"\FB18"}.mdi-file-restore::before{content:"\F670"}.mdi-file-restore-outline::before{content:"\F005A"}.mdi-file-search::before{content:"\FC58"}.mdi-file-search-outline::before{content:"\FC59"}.mdi-file-send::before{content:"\F22A"}.mdi-file-send-outline::before{content:"\F005B"}.mdi-file-settings::before{content:"\F00A4"}.mdi-file-settings-outline::before{content:"\F00A5"}.mdi-file-settings-variant::before{content:"\F00A6"}.mdi-file-settings-variant-outline::before{content:"\F00A7"}.mdi-file-star::before{content:"\F005C"}.mdi-file-star-outline::before{content:"\F005D"}.mdi-file-swap::before{content:"\FFD4"}.mdi-file-swap-outline::before{content:"\FFD5"}.mdi-file-sync::before{content:"\F0241"}.mdi-file-sync-outline::before{content:"\F0242"}.mdi-file-table::before{content:"\FC5A"}.mdi-file-table-box::before{content:"\F010C"}.mdi-file-table-box-multiple::before{content:"\F010D"}.mdi-file-table-box-multiple-outline::before{content:"\F010E"}.mdi-file-table-box-outline::before{content:"\F010F"}.mdi-file-table-outline::before{content:"\FC5B"}.mdi-file-tree::before{content:"\F645"}.mdi-file-undo::before{content:"\F8DB"}.mdi-file-undo-outline::before{content:"\F005E"}.mdi-file-upload::before{content:"\FA4C"}.mdi-file-upload-outline::before{content:"\FA4D"}.mdi-file-video::before{content:"\F22B"}.mdi-file-video-outline::before{content:"\FE10"}.mdi-file-word::before{content:"\F22C"}.mdi-file-word-box::before{content:"\F22D"}.mdi-file-word-box-outline::before{content:"\F005F"}.mdi-file-word-outline::before{content:"\F0060"}.mdi-film::before{content:"\F22F"}.mdi-filmstrip::before{content:"\F230"}.mdi-filmstrip-off::before{content:"\F231"}.mdi-filter::before{content:"\F232"}.mdi-filter-menu::before{content:"\F0110"}.mdi-filter-menu-outline::before{content:"\F0111"}.mdi-filter-minus::before{content:"\FF0B"}.mdi-filter-minus-outline::before{content:"\FF0C"}.mdi-filter-outline::before{content:"\F233"}.mdi-filter-plus::before{content:"\FF0D"}.mdi-filter-plus-outline::before{content:"\FF0E"}.mdi-filter-remove::before{content:"\F234"}.mdi-filter-remove-outline::before{content:"\F235"}.mdi-filter-variant::before{content:"\F236"}.mdi-filter-variant-minus::before{content:"\F013D"}.mdi-filter-variant-plus::before{content:"\F013E"}.mdi-filter-variant-remove::before{content:"\F0061"}.mdi-finance::before{content:"\F81E"}.mdi-find-replace::before{content:"\F6D3"}.mdi-fingerprint::before{content:"\F237"}.mdi-fingerprint-off::before{content:"\FECE"}.mdi-fire::before{content:"\F238"}.mdi-fire-extinguisher::before{content:"\FF0F"}.mdi-fire-hydrant::before{content:"\F0162"}.mdi-fire-hydrant-alert::before{content:"\F0163"}.mdi-fire-hydrant-off::before{content:"\F0164"}.mdi-fire-truck::before{content:"\F8AA"}.mdi-firebase::before{content:"\F966"}.mdi-firefox::before{content:"\F239"}.mdi-fireplace::before{content:"\FE11"}.mdi-fireplace-off::before{content:"\FE12"}.mdi-firework::before{content:"\FE13"}.mdi-fish::before{content:"\F23A"}.mdi-fishbowl::before{content:"\FF10"}.mdi-fishbowl-outline::before{content:"\FF11"}.mdi-fit-to-page::before{content:"\FF12"}.mdi-fit-to-page-outline::before{content:"\FF13"}.mdi-flag::before{content:"\F23B"}.mdi-flag-checkered::before{content:"\F23C"}.mdi-flag-minus::before{content:"\FB75"}.mdi-flag-minus-outline::before{content:"\F00DD"}.mdi-flag-outline::before{content:"\F23D"}.mdi-flag-plus::before{content:"\FB76"}.mdi-flag-plus-outline::before{content:"\F00DE"}.mdi-flag-remove::before{content:"\FB77"}.mdi-flag-remove-outline::before{content:"\F00DF"}.mdi-flag-triangle::before{content:"\F23F"}.mdi-flag-variant::before{content:"\F240"}.mdi-flag-variant-outline::before{content:"\F23E"}.mdi-flare::before{content:"\FD4E"}.mdi-flash::before{content:"\F241"}.mdi-flash-alert::before{content:"\FF14"}.mdi-flash-alert-outline::before{content:"\FF15"}.mdi-flash-auto::before{content:"\F242"}.mdi-flash-circle::before{content:"\F81F"}.mdi-flash-off::before{content:"\F243"}.mdi-flash-outline::before{content:"\F6D4"}.mdi-flash-red-eye::before{content:"\F67A"}.mdi-flashlight::before{content:"\F244"}.mdi-flashlight-off::before{content:"\F245"}.mdi-flask::before{content:"\F093"}.mdi-flask-empty::before{content:"\F094"}.mdi-flask-empty-minus::before{content:"\F0265"}.mdi-flask-empty-minus-outline::before{content:"\F0266"}.mdi-flask-empty-outline::before{content:"\F095"}.mdi-flask-empty-plus::before{content:"\F0267"}.mdi-flask-empty-plus-outline::before{content:"\F0268"}.mdi-flask-empty-remove::before{content:"\F0269"}.mdi-flask-empty-remove-outline::before{content:"\F026A"}.mdi-flask-minus::before{content:"\F026B"}.mdi-flask-minus-outline::before{content:"\F026C"}.mdi-flask-outline::before{content:"\F096"}.mdi-flask-plus::before{content:"\F026D"}.mdi-flask-plus-outline::before{content:"\F026E"}.mdi-flask-remove::before{content:"\F026F"}.mdi-flask-remove-outline::before{content:"\F0270"}.mdi-flask-round-bottom::before{content:"\F0276"}.mdi-flask-round-bottom-empty::before{content:"\F0277"}.mdi-flask-round-bottom-empty-outline::before{content:"\F0278"}.mdi-flask-round-bottom-outline::before{content:"\F0279"}.mdi-flattr::before{content:"\F246"}.mdi-fleur-de-lis::before{content:"\F032E"}.mdi-flickr::before{content:"\FCE3"}.mdi-flip-horizontal::before{content:"\F0112"}.mdi-flip-to-back::before{content:"\F247"}.mdi-flip-to-front::before{content:"\F248"}.mdi-flip-vertical::before{content:"\F0113"}.mdi-floor-lamp::before{content:"\F8DC"}.mdi-floor-lamp-dual::before{content:"\F0062"}.mdi-floor-lamp-variant::before{content:"\F0063"}.mdi-floor-plan::before{content:"\F820"}.mdi-floppy::before{content:"\F249"}.mdi-floppy-variant::before{content:"\F9EE"}.mdi-flower::before{content:"\F24A"}.mdi-flower-outline::before{content:"\F9EF"}.mdi-flower-poppy::before{content:"\FCE4"}.mdi-flower-tulip::before{content:"\F9F0"}.mdi-flower-tulip-outline::before{content:"\F9F1"}.mdi-focus-auto::before{content:"\FF6B"}.mdi-focus-field::before{content:"\FF6C"}.mdi-focus-field-horizontal::before{content:"\FF6D"}.mdi-focus-field-vertical::before{content:"\FF6E"}.mdi-folder::before{content:"\F24B"}.mdi-folder-account::before{content:"\F24C"}.mdi-folder-account-outline::before{content:"\FB78"}.mdi-folder-alert::before{content:"\FDA8"}.mdi-folder-alert-outline::before{content:"\FDA9"}.mdi-folder-clock::before{content:"\FAB9"}.mdi-folder-clock-outline::before{content:"\FABA"}.mdi-folder-download::before{content:"\F24D"}.mdi-folder-download-outline::before{content:"\F0114"}.mdi-folder-edit::before{content:"\F8DD"}.mdi-folder-edit-outline::before{content:"\FDAA"}.mdi-folder-google-drive::before{content:"\F24E"}.mdi-folder-heart::before{content:"\F0115"}.mdi-folder-heart-outline::before{content:"\F0116"}.mdi-folder-home::before{content:"\F00E0"}.mdi-folder-home-outline::before{content:"\F00E1"}.mdi-folder-image::before{content:"\F24F"}.mdi-folder-information::before{content:"\F00E2"}.mdi-folder-information-outline::before{content:"\F00E3"}.mdi-folder-key::before{content:"\F8AB"}.mdi-folder-key-network::before{content:"\F8AC"}.mdi-folder-key-network-outline::before{content:"\FC5C"}.mdi-folder-key-outline::before{content:"\F0117"}.mdi-folder-lock::before{content:"\F250"}.mdi-folder-lock-open::before{content:"\F251"}.mdi-folder-marker::before{content:"\F0298"}.mdi-folder-marker-outline::before{content:"\F0299"}.mdi-folder-move::before{content:"\F252"}.mdi-folder-move-outline::before{content:"\F0271"}.mdi-folder-multiple::before{content:"\F253"}.mdi-folder-multiple-image::before{content:"\F254"}.mdi-folder-multiple-outline::before{content:"\F255"}.mdi-folder-music::before{content:"\F0384"}.mdi-folder-music-outline::before{content:"\F0385"}.mdi-folder-network::before{content:"\F86F"}.mdi-folder-network-outline::before{content:"\FC5D"}.mdi-folder-open::before{content:"\F76F"}.mdi-folder-open-outline::before{content:"\FDAB"}.mdi-folder-outline::before{content:"\F256"}.mdi-folder-plus::before{content:"\F257"}.mdi-folder-plus-outline::before{content:"\FB79"}.mdi-folder-pound::before{content:"\FCE5"}.mdi-folder-pound-outline::before{content:"\FCE6"}.mdi-folder-remove::before{content:"\F258"}.mdi-folder-remove-outline::before{content:"\FB7A"}.mdi-folder-search::before{content:"\F967"}.mdi-folder-search-outline::before{content:"\F968"}.mdi-folder-settings::before{content:"\F00A8"}.mdi-folder-settings-outline::before{content:"\F00A9"}.mdi-folder-settings-variant::before{content:"\F00AA"}.mdi-folder-settings-variant-outline::before{content:"\F00AB"}.mdi-folder-star::before{content:"\F69C"}.mdi-folder-star-outline::before{content:"\FB7B"}.mdi-folder-swap::before{content:"\FFD6"}.mdi-folder-swap-outline::before{content:"\FFD7"}.mdi-folder-sync::before{content:"\FCE7"}.mdi-folder-sync-outline::before{content:"\FCE8"}.mdi-folder-table::before{content:"\F030E"}.mdi-folder-table-outline::before{content:"\F030F"}.mdi-folder-text::before{content:"\FC5E"}.mdi-folder-text-outline::before{content:"\FC5F"}.mdi-folder-upload::before{content:"\F259"}.mdi-folder-upload-outline::before{content:"\F0118"}.mdi-folder-zip::before{content:"\F6EA"}.mdi-folder-zip-outline::before{content:"\F7B8"}.mdi-font-awesome::before{content:"\F03A"}.mdi-food::before{content:"\F25A"}.mdi-food-apple::before{content:"\F25B"}.mdi-food-apple-outline::before{content:"\FC60"}.mdi-food-croissant::before{content:"\F7C7"}.mdi-food-fork-drink::before{content:"\F5F2"}.mdi-food-off::before{content:"\F5F3"}.mdi-food-variant::before{content:"\F25C"}.mdi-foot-print::before{content:"\FF6F"}.mdi-football::before{content:"\F25D"}.mdi-football-australian::before{content:"\F25E"}.mdi-football-helmet::before{content:"\F25F"}.mdi-forklift::before{content:"\F7C8"}.mdi-format-align-bottom::before{content:"\F752"}.mdi-format-align-center::before{content:"\F260"}.mdi-format-align-justify::before{content:"\F261"}.mdi-format-align-left::before{content:"\F262"}.mdi-format-align-middle::before{content:"\F753"}.mdi-format-align-right::before{content:"\F263"}.mdi-format-align-top::before{content:"\F754"}.mdi-format-annotation-minus::before{content:"\FABB"}.mdi-format-annotation-plus::before{content:"\F646"}.mdi-format-bold::before{content:"\F264"}.mdi-format-clear::before{content:"\F265"}.mdi-format-color-fill::before{content:"\F266"}.mdi-format-color-highlight::before{content:"\FE14"}.mdi-format-color-marker-cancel::before{content:"\F033E"}.mdi-format-color-text::before{content:"\F69D"}.mdi-format-columns::before{content:"\F8DE"}.mdi-format-float-center::before{content:"\F267"}.mdi-format-float-left::before{content:"\F268"}.mdi-format-float-none::before{content:"\F269"}.mdi-format-float-right::before{content:"\F26A"}.mdi-format-font::before{content:"\F6D5"}.mdi-format-font-size-decrease::before{content:"\F9F2"}.mdi-format-font-size-increase::before{content:"\F9F3"}.mdi-format-header-1::before{content:"\F26B"}.mdi-format-header-2::before{content:"\F26C"}.mdi-format-header-3::before{content:"\F26D"}.mdi-format-header-4::before{content:"\F26E"}.mdi-format-header-5::before{content:"\F26F"}.mdi-format-header-6::before{content:"\F270"}.mdi-format-header-decrease::before{content:"\F271"}.mdi-format-header-equal::before{content:"\F272"}.mdi-format-header-increase::before{content:"\F273"}.mdi-format-header-pound::before{content:"\F274"}.mdi-format-horizontal-align-center::before{content:"\F61E"}.mdi-format-horizontal-align-left::before{content:"\F61F"}.mdi-format-horizontal-align-right::before{content:"\F620"}.mdi-format-indent-decrease::before{content:"\F275"}.mdi-format-indent-increase::before{content:"\F276"}.mdi-format-italic::before{content:"\F277"}.mdi-format-letter-case::before{content:"\FB19"}.mdi-format-letter-case-lower::before{content:"\FB1A"}.mdi-format-letter-case-upper::before{content:"\FB1B"}.mdi-format-letter-ends-with::before{content:"\FFD8"}.mdi-format-letter-matches::before{content:"\FFD9"}.mdi-format-letter-starts-with::before{content:"\FFDA"}.mdi-format-line-spacing::before{content:"\F278"}.mdi-format-line-style::before{content:"\F5C8"}.mdi-format-line-weight::before{content:"\F5C9"}.mdi-format-list-bulleted::before{content:"\F279"}.mdi-format-list-bulleted-square::before{content:"\FDAC"}.mdi-format-list-bulleted-triangle::before{content:"\FECF"}.mdi-format-list-bulleted-type::before{content:"\F27A"}.mdi-format-list-checkbox::before{content:"\F969"}.mdi-format-list-checks::before{content:"\F755"}.mdi-format-list-numbered::before{content:"\F27B"}.mdi-format-list-numbered-rtl::before{content:"\FCE9"}.mdi-format-list-text::before{content:"\F029A"}.mdi-format-overline::before{content:"\FED0"}.mdi-format-page-break::before{content:"\F6D6"}.mdi-format-paint::before{content:"\F27C"}.mdi-format-paragraph::before{content:"\F27D"}.mdi-format-pilcrow::before{content:"\F6D7"}.mdi-format-quote-close::before{content:"\F27E"}.mdi-format-quote-close-outline::before{content:"\F01D3"}.mdi-format-quote-open::before{content:"\F756"}.mdi-format-quote-open-outline::before{content:"\F01D2"}.mdi-format-rotate-90::before{content:"\F6A9"}.mdi-format-section::before{content:"\F69E"}.mdi-format-size::before{content:"\F27F"}.mdi-format-strikethrough::before{content:"\F280"}.mdi-format-strikethrough-variant::before{content:"\F281"}.mdi-format-subscript::before{content:"\F282"}.mdi-format-superscript::before{content:"\F283"}.mdi-format-text::before{content:"\F284"}.mdi-format-text-rotation-angle-down::before{content:"\FFDB"}.mdi-format-text-rotation-angle-up::before{content:"\FFDC"}.mdi-format-text-rotation-down::before{content:"\FD4F"}.mdi-format-text-rotation-down-vertical::before{content:"\FFDD"}.mdi-format-text-rotation-none::before{content:"\FD50"}.mdi-format-text-rotation-up::before{content:"\FFDE"}.mdi-format-text-rotation-vertical::before{content:"\FFDF"}.mdi-format-text-variant::before{content:"\FE15"}.mdi-format-text-wrapping-clip::before{content:"\FCEA"}.mdi-format-text-wrapping-overflow::before{content:"\FCEB"}.mdi-format-text-wrapping-wrap::before{content:"\FCEC"}.mdi-format-textbox::before{content:"\FCED"}.mdi-format-textdirection-l-to-r::before{content:"\F285"}.mdi-format-textdirection-r-to-l::before{content:"\F286"}.mdi-format-title::before{content:"\F5F4"}.mdi-format-underline::before{content:"\F287"}.mdi-format-vertical-align-bottom::before{content:"\F621"}.mdi-format-vertical-align-center::before{content:"\F622"}.mdi-format-vertical-align-top::before{content:"\F623"}.mdi-format-wrap-inline::before{content:"\F288"}.mdi-format-wrap-square::before{content:"\F289"}.mdi-format-wrap-tight::before{content:"\F28A"}.mdi-format-wrap-top-bottom::before{content:"\F28B"}.mdi-forum::before{content:"\F28C"}.mdi-forum-outline::before{content:"\F821"}.mdi-forward::before{content:"\F28D"}.mdi-forwardburger::before{content:"\FD51"}.mdi-fountain::before{content:"\F96A"}.mdi-fountain-pen::before{content:"\FCEE"}.mdi-fountain-pen-tip::before{content:"\FCEF"}.mdi-foursquare::before{content:"\F28E"}.mdi-freebsd::before{content:"\F8DF"}.mdi-frequently-asked-questions::before{content:"\FED1"}.mdi-fridge::before{content:"\F290"}.mdi-fridge-alert::before{content:"\F01DC"}.mdi-fridge-alert-outline::before{content:"\F01DD"}.mdi-fridge-bottom::before{content:"\F292"}.mdi-fridge-off::before{content:"\F01DA"}.mdi-fridge-off-outline::before{content:"\F01DB"}.mdi-fridge-outline::before{content:"\F28F"}.mdi-fridge-top::before{content:"\F291"}.mdi-fruit-cherries::before{content:"\F0064"}.mdi-fruit-citrus::before{content:"\F0065"}.mdi-fruit-grapes::before{content:"\F0066"}.mdi-fruit-grapes-outline::before{content:"\F0067"}.mdi-fruit-pineapple::before{content:"\F0068"}.mdi-fruit-watermelon::before{content:"\F0069"}.mdi-fuel::before{content:"\F7C9"}.mdi-fullscreen::before{content:"\F293"}.mdi-fullscreen-exit::before{content:"\F294"}.mdi-function::before{content:"\F295"}.mdi-function-variant::before{content:"\F870"}.mdi-furigana-horizontal::before{content:"\F00AC"}.mdi-furigana-vertical::before{content:"\F00AD"}.mdi-fuse::before{content:"\FC61"}.mdi-fuse-blade::before{content:"\FC62"}.mdi-gamepad::before{content:"\F296"}.mdi-gamepad-circle::before{content:"\FE16"}.mdi-gamepad-circle-down::before{content:"\FE17"}.mdi-gamepad-circle-left::before{content:"\FE18"}.mdi-gamepad-circle-outline::before{content:"\FE19"}.mdi-gamepad-circle-right::before{content:"\FE1A"}.mdi-gamepad-circle-up::before{content:"\FE1B"}.mdi-gamepad-down::before{content:"\FE1C"}.mdi-gamepad-left::before{content:"\FE1D"}.mdi-gamepad-right::before{content:"\FE1E"}.mdi-gamepad-round::before{content:"\FE1F"}.mdi-gamepad-round-down::before{content:"\FE7E"}.mdi-gamepad-round-left::before{content:"\FE7F"}.mdi-gamepad-round-outline::before{content:"\FE80"}.mdi-gamepad-round-right::before{content:"\FE81"}.mdi-gamepad-round-up::before{content:"\FE82"}.mdi-gamepad-square::before{content:"\FED2"}.mdi-gamepad-square-outline::before{content:"\FED3"}.mdi-gamepad-up::before{content:"\FE83"}.mdi-gamepad-variant::before{content:"\F297"}.mdi-gamepad-variant-outline::before{content:"\FED4"}.mdi-gamma::before{content:"\F0119"}.mdi-gantry-crane::before{content:"\FDAD"}.mdi-garage::before{content:"\F6D8"}.mdi-garage-alert::before{content:"\F871"}.mdi-garage-alert-variant::before{content:"\F0300"}.mdi-garage-open::before{content:"\F6D9"}.mdi-garage-open-variant::before{content:"\F02FF"}.mdi-garage-variant::before{content:"\F02FE"}.mdi-gas-cylinder::before{content:"\F647"}.mdi-gas-station::before{content:"\F298"}.mdi-gas-station-outline::before{content:"\FED5"}.mdi-gate::before{content:"\F299"}.mdi-gate-and::before{content:"\F8E0"}.mdi-gate-arrow-right::before{content:"\F0194"}.mdi-gate-nand::before{content:"\F8E1"}.mdi-gate-nor::before{content:"\F8E2"}.mdi-gate-not::before{content:"\F8E3"}.mdi-gate-open::before{content:"\F0195"}.mdi-gate-or::before{content:"\F8E4"}.mdi-gate-xnor::before{content:"\F8E5"}.mdi-gate-xor::before{content:"\F8E6"}.mdi-gatsby::before{content:"\FE84"}.mdi-gauge::before{content:"\F29A"}.mdi-gauge-empty::before{content:"\F872"}.mdi-gauge-full::before{content:"\F873"}.mdi-gauge-low::before{content:"\F874"}.mdi-gavel::before{content:"\F29B"}.mdi-gender-female::before{content:"\F29C"}.mdi-gender-male::before{content:"\F29D"}.mdi-gender-male-female::before{content:"\F29E"}.mdi-gender-male-female-variant::before{content:"\F016A"}.mdi-gender-non-binary::before{content:"\F016B"}.mdi-gender-transgender::before{content:"\F29F"}.mdi-gentoo::before{content:"\F8E7"}.mdi-gesture::before{content:"\F7CA"}.mdi-gesture-double-tap::before{content:"\F73B"}.mdi-gesture-pinch::before{content:"\FABC"}.mdi-gesture-spread::before{content:"\FABD"}.mdi-gesture-swipe::before{content:"\FD52"}.mdi-gesture-swipe-down::before{content:"\F73C"}.mdi-gesture-swipe-horizontal::before{content:"\FABE"}.mdi-gesture-swipe-left::before{content:"\F73D"}.mdi-gesture-swipe-right::before{content:"\F73E"}.mdi-gesture-swipe-up::before{content:"\F73F"}.mdi-gesture-swipe-vertical::before{content:"\FABF"}.mdi-gesture-tap::before{content:"\F740"}.mdi-gesture-tap-box::before{content:"\F02D4"}.mdi-gesture-tap-button::before{content:"\F02D3"}.mdi-gesture-tap-hold::before{content:"\FD53"}.mdi-gesture-two-double-tap::before{content:"\F741"}.mdi-gesture-two-tap::before{content:"\F742"}.mdi-ghost::before{content:"\F2A0"}.mdi-ghost-off::before{content:"\F9F4"}.mdi-gif::before{content:"\FD54"}.mdi-gift::before{content:"\FE85"}.mdi-gift-outline::before{content:"\F2A1"}.mdi-git::before{content:"\F2A2"}.mdi-github-box::before{content:"\F2A3"}.mdi-github-circle::before{content:"\F2A4"}.mdi-github-face::before{content:"\F6DA"}.mdi-gitlab::before{content:"\FB7C"}.mdi-glass-cocktail::before{content:"\F356"}.mdi-glass-flute::before{content:"\F2A5"}.mdi-glass-mug::before{content:"\F2A6"}.mdi-glass-mug-variant::before{content:"\F0141"}.mdi-glass-pint-outline::before{content:"\F0338"}.mdi-glass-stange::before{content:"\F2A7"}.mdi-glass-tulip::before{content:"\F2A8"}.mdi-glass-wine::before{content:"\F875"}.mdi-glassdoor::before{content:"\F2A9"}.mdi-glasses::before{content:"\F2AA"}.mdi-globe-light::before{content:"\F0302"}.mdi-globe-model::before{content:"\F8E8"}.mdi-gmail::before{content:"\F2AB"}.mdi-gnome::before{content:"\F2AC"}.mdi-go-kart::before{content:"\FD55"}.mdi-go-kart-track::before{content:"\FD56"}.mdi-gog::before{content:"\FB7D"}.mdi-gold::before{content:"\F027A"}.mdi-golf::before{content:"\F822"}.mdi-golf-cart::before{content:"\F01CF"}.mdi-golf-tee::before{content:"\F00AE"}.mdi-gondola::before{content:"\F685"}.mdi-goodreads::before{content:"\FD57"}.mdi-google::before{content:"\F2AD"}.mdi-google-adwords::before{content:"\FC63"}.mdi-google-analytics::before{content:"\F7CB"}.mdi-google-assistant::before{content:"\F7CC"}.mdi-google-cardboard::before{content:"\F2AE"}.mdi-google-chrome::before{content:"\F2AF"}.mdi-google-circles::before{content:"\F2B0"}.mdi-google-circles-communities::before{content:"\F2B1"}.mdi-google-circles-extended::before{content:"\F2B2"}.mdi-google-circles-group::before{content:"\F2B3"}.mdi-google-classroom::before{content:"\F2C0"}.mdi-google-cloud::before{content:"\F0221"}.mdi-google-controller::before{content:"\F2B4"}.mdi-google-controller-off::before{content:"\F2B5"}.mdi-google-downasaur::before{content:"\F038D"}.mdi-google-drive::before{content:"\F2B6"}.mdi-google-earth::before{content:"\F2B7"}.mdi-google-fit::before{content:"\F96B"}.mdi-google-glass::before{content:"\F2B8"}.mdi-google-hangouts::before{content:"\F2C9"}.mdi-google-home::before{content:"\F823"}.mdi-google-keep::before{content:"\F6DB"}.mdi-google-lens::before{content:"\F9F5"}.mdi-google-maps::before{content:"\F5F5"}.mdi-google-my-business::before{content:"\F006A"}.mdi-google-nearby::before{content:"\F2B9"}.mdi-google-pages::before{content:"\F2BA"}.mdi-google-photos::before{content:"\F6DC"}.mdi-google-physical-web::before{content:"\F2BB"}.mdi-google-play::before{content:"\F2BC"}.mdi-google-plus::before{content:"\F2BD"}.mdi-google-plus-box::before{content:"\F2BE"}.mdi-google-podcast::before{content:"\FED6"}.mdi-google-spreadsheet::before{content:"\F9F6"}.mdi-google-street-view::before{content:"\FC64"}.mdi-google-translate::before{content:"\F2BF"}.mdi-gradient::before{content:"\F69F"}.mdi-grain::before{content:"\FD58"}.mdi-graph::before{content:"\F006B"}.mdi-graph-outline::before{content:"\F006C"}.mdi-graphql::before{content:"\F876"}.mdi-grave-stone::before{content:"\FB7E"}.mdi-grease-pencil::before{content:"\F648"}.mdi-greater-than::before{content:"\F96C"}.mdi-greater-than-or-equal::before{content:"\F96D"}.mdi-grid::before{content:"\F2C1"}.mdi-grid-large::before{content:"\F757"}.mdi-grid-off::before{content:"\F2C2"}.mdi-grill::before{content:"\FE86"}.mdi-grill-outline::before{content:"\F01B5"}.mdi-group::before{content:"\F2C3"}.mdi-guitar-acoustic::before{content:"\F770"}.mdi-guitar-electric::before{content:"\F2C4"}.mdi-guitar-pick::before{content:"\F2C5"}.mdi-guitar-pick-outline::before{content:"\F2C6"}.mdi-guy-fawkes-mask::before{content:"\F824"}.mdi-hackernews::before{content:"\F624"}.mdi-hail::before{content:"\FAC0"}.mdi-hair-dryer::before{content:"\F011A"}.mdi-hair-dryer-outline::before{content:"\F011B"}.mdi-halloween::before{content:"\FB7F"}.mdi-hamburger::before{content:"\F684"}.mdi-hammer::before{content:"\F8E9"}.mdi-hammer-screwdriver::before{content:"\F034D"}.mdi-hammer-wrench::before{content:"\F034E"}.mdi-hand::before{content:"\FA4E"}.mdi-hand-heart::before{content:"\F011C"}.mdi-hand-left::before{content:"\FE87"}.mdi-hand-okay::before{content:"\FA4F"}.mdi-hand-peace::before{content:"\FA50"}.mdi-hand-peace-variant::before{content:"\FA51"}.mdi-hand-pointing-down::before{content:"\FA52"}.mdi-hand-pointing-left::before{content:"\FA53"}.mdi-hand-pointing-right::before{content:"\F2C7"}.mdi-hand-pointing-up::before{content:"\FA54"}.mdi-hand-right::before{content:"\FE88"}.mdi-hand-saw::before{content:"\FE89"}.mdi-handball::before{content:"\FF70"}.mdi-handcuffs::before{content:"\F0169"}.mdi-handshake::before{content:"\F0243"}.mdi-hanger::before{content:"\F2C8"}.mdi-hard-hat::before{content:"\F96E"}.mdi-harddisk::before{content:"\F2CA"}.mdi-harddisk-plus::before{content:"\F006D"}.mdi-harddisk-remove::before{content:"\F006E"}.mdi-hat-fedora::before{content:"\FB80"}.mdi-hazard-lights::before{content:"\FC65"}.mdi-hdr::before{content:"\FD59"}.mdi-hdr-off::before{content:"\FD5A"}.mdi-head::before{content:"\F0389"}.mdi-head-alert::before{content:"\F0363"}.mdi-head-alert-outline::before{content:"\F0364"}.mdi-head-check::before{content:"\F0365"}.mdi-head-check-outline::before{content:"\F0366"}.mdi-head-cog::before{content:"\F0367"}.mdi-head-cog-outline::before{content:"\F0368"}.mdi-head-dots-horizontal::before{content:"\F0369"}.mdi-head-dots-horizontal-outline::before{content:"\F036A"}.mdi-head-flash::before{content:"\F036B"}.mdi-head-flash-outline::before{content:"\F036C"}.mdi-head-heart::before{content:"\F036D"}.mdi-head-heart-outline::before{content:"\F036E"}.mdi-head-lightbulb::before{content:"\F036F"}.mdi-head-lightbulb-outline::before{content:"\F0370"}.mdi-head-minus::before{content:"\F0371"}.mdi-head-minus-outline::before{content:"\F0372"}.mdi-head-outline::before{content:"\F038A"}.mdi-head-plus::before{content:"\F0373"}.mdi-head-plus-outline::before{content:"\F0374"}.mdi-head-question::before{content:"\F0375"}.mdi-head-question-outline::before{content:"\F0376"}.mdi-head-remove::before{content:"\F0377"}.mdi-head-remove-outline::before{content:"\F0378"}.mdi-head-snowflake::before{content:"\F0379"}.mdi-head-snowflake-outline::before{content:"\F037A"}.mdi-head-sync::before{content:"\F037B"}.mdi-head-sync-outline::before{content:"\F037C"}.mdi-headphones::before{content:"\F2CB"}.mdi-headphones-bluetooth::before{content:"\F96F"}.mdi-headphones-box::before{content:"\F2CC"}.mdi-headphones-off::before{content:"\F7CD"}.mdi-headphones-settings::before{content:"\F2CD"}.mdi-headset::before{content:"\F2CE"}.mdi-headset-dock::before{content:"\F2CF"}.mdi-headset-off::before{content:"\F2D0"}.mdi-heart::before{content:"\F2D1"}.mdi-heart-box::before{content:"\F2D2"}.mdi-heart-box-outline::before{content:"\F2D3"}.mdi-heart-broken::before{content:"\F2D4"}.mdi-heart-broken-outline::before{content:"\FCF0"}.mdi-heart-circle::before{content:"\F970"}.mdi-heart-circle-outline::before{content:"\F971"}.mdi-heart-flash::before{content:"\FF16"}.mdi-heart-half::before{content:"\F6DE"}.mdi-heart-half-full::before{content:"\F6DD"}.mdi-heart-half-outline::before{content:"\F6DF"}.mdi-heart-multiple::before{content:"\FA55"}.mdi-heart-multiple-outline::before{content:"\FA56"}.mdi-heart-off::before{content:"\F758"}.mdi-heart-outline::before{content:"\F2D5"}.mdi-heart-pulse::before{content:"\F5F6"}.mdi-helicopter::before{content:"\FAC1"}.mdi-help::before{content:"\F2D6"}.mdi-help-box::before{content:"\F78A"}.mdi-help-circle::before{content:"\F2D7"}.mdi-help-circle-outline::before{content:"\F625"}.mdi-help-network::before{content:"\F6F4"}.mdi-help-network-outline::before{content:"\FC66"}.mdi-help-rhombus::before{content:"\FB81"}.mdi-help-rhombus-outline::before{content:"\FB82"}.mdi-hexadecimal::before{content:"\F02D2"}.mdi-hexagon::before{content:"\F2D8"}.mdi-hexagon-multiple::before{content:"\F6E0"}.mdi-hexagon-multiple-outline::before{content:"\F011D"}.mdi-hexagon-outline::before{content:"\F2D9"}.mdi-hexagon-slice-1::before{content:"\FAC2"}.mdi-hexagon-slice-2::before{content:"\FAC3"}.mdi-hexagon-slice-3::before{content:"\FAC4"}.mdi-hexagon-slice-4::before{content:"\FAC5"}.mdi-hexagon-slice-5::before{content:"\FAC6"}.mdi-hexagon-slice-6::before{content:"\FAC7"}.mdi-hexagram::before{content:"\FAC8"}.mdi-hexagram-outline::before{content:"\FAC9"}.mdi-high-definition::before{content:"\F7CE"}.mdi-high-definition-box::before{content:"\F877"}.mdi-highway::before{content:"\F5F7"}.mdi-hiking::before{content:"\FD5B"}.mdi-hinduism::before{content:"\F972"}.mdi-history::before{content:"\F2DA"}.mdi-hockey-puck::before{content:"\F878"}.mdi-hockey-sticks::before{content:"\F879"}.mdi-hololens::before{content:"\F2DB"}.mdi-home::before{content:"\F2DC"}.mdi-home-account::before{content:"\F825"}.mdi-home-alert::before{content:"\F87A"}.mdi-home-analytics::before{content:"\FED7"}.mdi-home-assistant::before{content:"\F7CF"}.mdi-home-automation::before{content:"\F7D0"}.mdi-home-circle::before{content:"\F7D1"}.mdi-home-circle-outline::before{content:"\F006F"}.mdi-home-city::before{content:"\FCF1"}.mdi-home-city-outline::before{content:"\FCF2"}.mdi-home-currency-usd::before{content:"\F8AE"}.mdi-home-edit::before{content:"\F0184"}.mdi-home-edit-outline::before{content:"\F0185"}.mdi-home-export-outline::before{content:"\FFB8"}.mdi-home-flood::before{content:"\FF17"}.mdi-home-floor-0::before{content:"\FDAE"}.mdi-home-floor-1::before{content:"\FD5C"}.mdi-home-floor-2::before{content:"\FD5D"}.mdi-home-floor-3::before{content:"\FD5E"}.mdi-home-floor-a::before{content:"\FD5F"}.mdi-home-floor-b::before{content:"\FD60"}.mdi-home-floor-g::before{content:"\FD61"}.mdi-home-floor-l::before{content:"\FD62"}.mdi-home-floor-negative-1::before{content:"\FDAF"}.mdi-home-group::before{content:"\FDB0"}.mdi-home-heart::before{content:"\F826"}.mdi-home-import-outline::before{content:"\FFB9"}.mdi-home-lightbulb::before{content:"\F027C"}.mdi-home-lightbulb-outline::before{content:"\F027D"}.mdi-home-lock::before{content:"\F8EA"}.mdi-home-lock-open::before{content:"\F8EB"}.mdi-home-map-marker::before{content:"\F5F8"}.mdi-home-minus::before{content:"\F973"}.mdi-home-modern::before{content:"\F2DD"}.mdi-home-outline::before{content:"\F6A0"}.mdi-home-plus::before{content:"\F974"}.mdi-home-remove::before{content:"\F0272"}.mdi-home-roof::before{content:"\F0156"}.mdi-home-thermometer::before{content:"\FF71"}.mdi-home-thermometer-outline::before{content:"\FF72"}.mdi-home-variant::before{content:"\F2DE"}.mdi-home-variant-outline::before{content:"\FB83"}.mdi-hook::before{content:"\F6E1"}.mdi-hook-off::before{content:"\F6E2"}.mdi-hops::before{content:"\F2DF"}.mdi-horizontal-rotate-clockwise::before{content:"\F011E"}.mdi-horizontal-rotate-counterclockwise::before{content:"\F011F"}.mdi-horseshoe::before{content:"\FA57"}.mdi-hospital::before{content:"\F0017"}.mdi-hospital-box::before{content:"\F2E0"}.mdi-hospital-box-outline::before{content:"\F0018"}.mdi-hospital-building::before{content:"\F2E1"}.mdi-hospital-marker::before{content:"\F2E2"}.mdi-hot-tub::before{content:"\F827"}.mdi-hotel::before{content:"\F2E3"}.mdi-houzz::before{content:"\F2E4"}.mdi-houzz-box::before{content:"\F2E5"}.mdi-hubspot::before{content:"\FCF3"}.mdi-hulu::before{content:"\F828"}.mdi-human::before{content:"\F2E6"}.mdi-human-child::before{content:"\F2E7"}.mdi-human-female::before{content:"\F649"}.mdi-human-female-boy::before{content:"\FA58"}.mdi-human-female-female::before{content:"\FA59"}.mdi-human-female-girl::before{content:"\FA5A"}.mdi-human-greeting::before{content:"\F64A"}.mdi-human-handsdown::before{content:"\F64B"}.mdi-human-handsup::before{content:"\F64C"}.mdi-human-male::before{content:"\F64D"}.mdi-human-male-boy::before{content:"\FA5B"}.mdi-human-male-female::before{content:"\F2E8"}.mdi-human-male-girl::before{content:"\FA5C"}.mdi-human-male-height::before{content:"\FF18"}.mdi-human-male-height-variant::before{content:"\FF19"}.mdi-human-male-male::before{content:"\FA5D"}.mdi-human-pregnant::before{content:"\F5CF"}.mdi-humble-bundle::before{content:"\F743"}.mdi-hvac::before{content:"\F037D"}.mdi-hydraulic-oil-level::before{content:"\F034F"}.mdi-hydraulic-oil-temperature::before{content:"\F0350"}.mdi-hydro-power::before{content:"\F0310"}.mdi-ice-cream::before{content:"\F829"}.mdi-ice-pop::before{content:"\FF1A"}.mdi-id-card::before{content:"\FFE0"}.mdi-identifier::before{content:"\FF1B"}.mdi-ideogram-cjk::before{content:"\F035C"}.mdi-ideogram-cjk-variant::before{content:"\F035D"}.mdi-iframe::before{content:"\FC67"}.mdi-iframe-array::before{content:"\F0120"}.mdi-iframe-array-outline::before{content:"\F0121"}.mdi-iframe-braces::before{content:"\F0122"}.mdi-iframe-braces-outline::before{content:"\F0123"}.mdi-iframe-outline::before{content:"\FC68"}.mdi-iframe-parentheses::before{content:"\F0124"}.mdi-iframe-parentheses-outline::before{content:"\F0125"}.mdi-iframe-variable::before{content:"\F0126"}.mdi-iframe-variable-outline::before{content:"\F0127"}.mdi-image::before{content:"\F2E9"}.mdi-image-album::before{content:"\F2EA"}.mdi-image-area::before{content:"\F2EB"}.mdi-image-area-close::before{content:"\F2EC"}.mdi-image-auto-adjust::before{content:"\FFE1"}.mdi-image-broken::before{content:"\F2ED"}.mdi-image-broken-variant::before{content:"\F2EE"}.mdi-image-edit::before{content:"\F020E"}.mdi-image-edit-outline::before{content:"\F020F"}.mdi-image-filter::before{content:"\F2EF"}.mdi-image-filter-black-white::before{content:"\F2F0"}.mdi-image-filter-center-focus::before{content:"\F2F1"}.mdi-image-filter-center-focus-strong::before{content:"\FF1C"}.mdi-image-filter-center-focus-strong-outline::before{content:"\FF1D"}.mdi-image-filter-center-focus-weak::before{content:"\F2F2"}.mdi-image-filter-drama::before{content:"\F2F3"}.mdi-image-filter-frames::before{content:"\F2F4"}.mdi-image-filter-hdr::before{content:"\F2F5"}.mdi-image-filter-none::before{content:"\F2F6"}.mdi-image-filter-tilt-shift::before{content:"\F2F7"}.mdi-image-filter-vintage::before{content:"\F2F8"}.mdi-image-frame::before{content:"\FE8A"}.mdi-image-move::before{content:"\F9F7"}.mdi-image-multiple::before{content:"\F2F9"}.mdi-image-off::before{content:"\F82A"}.mdi-image-off-outline::before{content:"\F01FC"}.mdi-image-outline::before{content:"\F975"}.mdi-image-plus::before{content:"\F87B"}.mdi-image-search::before{content:"\F976"}.mdi-image-search-outline::before{content:"\F977"}.mdi-image-size-select-actual::before{content:"\FC69"}.mdi-image-size-select-large::before{content:"\FC6A"}.mdi-image-size-select-small::before{content:"\FC6B"}.mdi-import::before{content:"\F2FA"}.mdi-inbox::before{content:"\F686"}.mdi-inbox-arrow-down::before{content:"\F2FB"}.mdi-inbox-arrow-down-outline::before{content:"\F029B"}.mdi-inbox-arrow-up::before{content:"\F3D1"}.mdi-inbox-arrow-up-outline::before{content:"\F029C"}.mdi-inbox-full::before{content:"\F029D"}.mdi-inbox-full-outline::before{content:"\F029E"}.mdi-inbox-multiple::before{content:"\F8AF"}.mdi-inbox-multiple-outline::before{content:"\FB84"}.mdi-inbox-outline::before{content:"\F029F"}.mdi-incognito::before{content:"\F5F9"}.mdi-infinity::before{content:"\F6E3"}.mdi-information::before{content:"\F2FC"}.mdi-information-outline::before{content:"\F2FD"}.mdi-information-variant::before{content:"\F64E"}.mdi-instagram::before{content:"\F2FE"}.mdi-instapaper::before{content:"\F2FF"}.mdi-instrument-triangle::before{content:"\F0070"}.mdi-internet-explorer::before{content:"\F300"}.mdi-invert-colors::before{content:"\F301"}.mdi-invert-colors-off::before{content:"\FE8B"}.mdi-iobroker::before{content:"\F0313"}.mdi-ip::before{content:"\FA5E"}.mdi-ip-network::before{content:"\FA5F"}.mdi-ip-network-outline::before{content:"\FC6C"}.mdi-ipod::before{content:"\FC6D"}.mdi-islam::before{content:"\F978"}.mdi-island::before{content:"\F0071"}.mdi-itunes::before{content:"\F676"}.mdi-iv-bag::before{content:"\F00E4"}.mdi-jabber::before{content:"\FDB1"}.mdi-jeepney::before{content:"\F302"}.mdi-jellyfish::before{content:"\FF1E"}.mdi-jellyfish-outline::before{content:"\FF1F"}.mdi-jira::before{content:"\F303"}.mdi-jquery::before{content:"\F87C"}.mdi-jsfiddle::before{content:"\F304"}.mdi-json::before{content:"\F626"}.mdi-judaism::before{content:"\F979"}.mdi-jump-rope::before{content:"\F032A"}.mdi-kabaddi::before{content:"\FD63"}.mdi-karate::before{content:"\F82B"}.mdi-keg::before{content:"\F305"}.mdi-kettle::before{content:"\F5FA"}.mdi-kettle-alert::before{content:"\F0342"}.mdi-kettle-alert-outline::before{content:"\F0343"}.mdi-kettle-off::before{content:"\F0346"}.mdi-kettle-off-outline::before{content:"\F0347"}.mdi-kettle-outline::before{content:"\FF73"}.mdi-kettle-steam::before{content:"\F0344"}.mdi-kettle-steam-outline::before{content:"\F0345"}.mdi-kettlebell::before{content:"\F032B"}.mdi-key::before{content:"\F306"}.mdi-key-arrow-right::before{content:"\F033D"}.mdi-key-change::before{content:"\F307"}.mdi-key-link::before{content:"\F01CA"}.mdi-key-minus::before{content:"\F308"}.mdi-key-outline::before{content:"\FDB2"}.mdi-key-plus::before{content:"\F309"}.mdi-key-remove::before{content:"\F30A"}.mdi-key-star::before{content:"\F01C9"}.mdi-key-variant::before{content:"\F30B"}.mdi-key-wireless::before{content:"\FFE2"}.mdi-keyboard::before{content:"\F30C"}.mdi-keyboard-backspace::before{content:"\F30D"}.mdi-keyboard-caps::before{content:"\F30E"}.mdi-keyboard-close::before{content:"\F30F"}.mdi-keyboard-esc::before{content:"\F02E2"}.mdi-keyboard-f1::before{content:"\F02D6"}.mdi-keyboard-f10::before{content:"\F02DF"}.mdi-keyboard-f11::before{content:"\F02E0"}.mdi-keyboard-f12::before{content:"\F02E1"}.mdi-keyboard-f2::before{content:"\F02D7"}.mdi-keyboard-f3::before{content:"\F02D8"}.mdi-keyboard-f4::before{content:"\F02D9"}.mdi-keyboard-f5::before{content:"\F02DA"}.mdi-keyboard-f6::before{content:"\F02DB"}.mdi-keyboard-f7::before{content:"\F02DC"}.mdi-keyboard-f8::before{content:"\F02DD"}.mdi-keyboard-f9::before{content:"\F02DE"}.mdi-keyboard-off::before{content:"\F310"}.mdi-keyboard-off-outline::before{content:"\FE8C"}.mdi-keyboard-outline::before{content:"\F97A"}.mdi-keyboard-return::before{content:"\F311"}.mdi-keyboard-settings::before{content:"\F9F8"}.mdi-keyboard-settings-outline::before{content:"\F9F9"}.mdi-keyboard-space::before{content:"\F0072"}.mdi-keyboard-tab::before{content:"\F312"}.mdi-keyboard-variant::before{content:"\F313"}.mdi-khanda::before{content:"\F0128"}.mdi-kickstarter::before{content:"\F744"}.mdi-klingon::before{content:"\F0386"}.mdi-knife::before{content:"\F9FA"}.mdi-knife-military::before{content:"\F9FB"}.mdi-kodi::before{content:"\F314"}.mdi-kotlin::before{content:"\F0244"}.mdi-kubernetes::before{content:"\F0129"}.mdi-label::before{content:"\F315"}.mdi-label-multiple::before{content:"\F03A0"}.mdi-label-multiple-outline::before{content:"\F03A1"}.mdi-label-off::before{content:"\FACA"}.mdi-label-off-outline::before{content:"\FACB"}.mdi-label-outline::before{content:"\F316"}.mdi-label-percent::before{content:"\F0315"}.mdi-label-percent-outline::before{content:"\F0316"}.mdi-label-variant::before{content:"\FACC"}.mdi-label-variant-outline::before{content:"\FACD"}.mdi-ladybug::before{content:"\F82C"}.mdi-lambda::before{content:"\F627"}.mdi-lamp::before{content:"\F6B4"}.mdi-lan::before{content:"\F317"}.mdi-lan-check::before{content:"\F02D5"}.mdi-lan-connect::before{content:"\F318"}.mdi-lan-disconnect::before{content:"\F319"}.mdi-lan-pending::before{content:"\F31A"}.mdi-language-c::before{content:"\F671"}.mdi-language-cpp::before{content:"\F672"}.mdi-language-csharp::before{content:"\F31B"}.mdi-language-css3::before{content:"\F31C"}.mdi-language-fortran::before{content:"\F0245"}.mdi-language-go::before{content:"\F7D2"}.mdi-language-haskell::before{content:"\FC6E"}.mdi-language-html5::before{content:"\F31D"}.mdi-language-java::before{content:"\FB1C"}.mdi-language-javascript::before{content:"\F31E"}.mdi-language-lua::before{content:"\F8B0"}.mdi-language-php::before{content:"\F31F"}.mdi-language-python::before{content:"\F320"}.mdi-language-python-text::before{content:"\F321"}.mdi-language-r::before{content:"\F7D3"}.mdi-language-ruby-on-rails::before{content:"\FACE"}.mdi-language-swift::before{content:"\F6E4"}.mdi-language-typescript::before{content:"\F6E5"}.mdi-laptop::before{content:"\F322"}.mdi-laptop-chromebook::before{content:"\F323"}.mdi-laptop-mac::before{content:"\F324"}.mdi-laptop-off::before{content:"\F6E6"}.mdi-laptop-windows::before{content:"\F325"}.mdi-laravel::before{content:"\FACF"}.mdi-lasso::before{content:"\FF20"}.mdi-lastfm::before{content:"\F326"}.mdi-lastpass::before{content:"\F446"}.mdi-latitude::before{content:"\FF74"}.mdi-launch::before{content:"\F327"}.mdi-lava-lamp::before{content:"\F7D4"}.mdi-layers::before{content:"\F328"}.mdi-layers-minus::before{content:"\FE8D"}.mdi-layers-off::before{content:"\F329"}.mdi-layers-off-outline::before{content:"\F9FC"}.mdi-layers-outline::before{content:"\F9FD"}.mdi-layers-plus::before{content:"\FE30"}.mdi-layers-remove::before{content:"\FE31"}.mdi-layers-search::before{content:"\F0231"}.mdi-layers-search-outline::before{content:"\F0232"}.mdi-layers-triple::before{content:"\FF75"}.mdi-layers-triple-outline::before{content:"\FF76"}.mdi-lead-pencil::before{content:"\F64F"}.mdi-leaf::before{content:"\F32A"}.mdi-leaf-maple::before{content:"\FC6F"}.mdi-leaf-maple-off::before{content:"\F0305"}.mdi-leaf-off::before{content:"\F0304"}.mdi-leak::before{content:"\FDB3"}.mdi-leak-off::before{content:"\FDB4"}.mdi-led-off::before{content:"\F32B"}.mdi-led-on::before{content:"\F32C"}.mdi-led-outline::before{content:"\F32D"}.mdi-led-strip::before{content:"\F7D5"}.mdi-led-strip-variant::before{content:"\F0073"}.mdi-led-variant-off::before{content:"\F32E"}.mdi-led-variant-on::before{content:"\F32F"}.mdi-led-variant-outline::before{content:"\F330"}.mdi-leek::before{content:"\F01A8"}.mdi-less-than::before{content:"\F97B"}.mdi-less-than-or-equal::before{content:"\F97C"}.mdi-library::before{content:"\F331"}.mdi-library-books::before{content:"\F332"}.mdi-library-movie::before{content:"\FCF4"}.mdi-library-music::before{content:"\F333"}.mdi-library-music-outline::before{content:"\FF21"}.mdi-library-shelves::before{content:"\FB85"}.mdi-library-video::before{content:"\FCF5"}.mdi-license::before{content:"\FFE3"}.mdi-lifebuoy::before{content:"\F87D"}.mdi-light-switch::before{content:"\F97D"}.mdi-lightbulb::before{content:"\F335"}.mdi-lightbulb-cfl::before{content:"\F0233"}.mdi-lightbulb-cfl-off::before{content:"\F0234"}.mdi-lightbulb-cfl-spiral::before{content:"\F02A0"}.mdi-lightbulb-cfl-spiral-off::before{content:"\F02EE"}.mdi-lightbulb-group::before{content:"\F027E"}.mdi-lightbulb-group-off::before{content:"\F02F8"}.mdi-lightbulb-group-off-outline::before{content:"\F02F9"}.mdi-lightbulb-group-outline::before{content:"\F027F"}.mdi-lightbulb-multiple::before{content:"\F0280"}.mdi-lightbulb-multiple-off::before{content:"\F02FA"}.mdi-lightbulb-multiple-off-outline::before{content:"\F02FB"}.mdi-lightbulb-multiple-outline::before{content:"\F0281"}.mdi-lightbulb-off::before{content:"\FE32"}.mdi-lightbulb-off-outline::before{content:"\FE33"}.mdi-lightbulb-on::before{content:"\F6E7"}.mdi-lightbulb-on-outline::before{content:"\F6E8"}.mdi-lightbulb-outline::before{content:"\F336"}.mdi-lighthouse::before{content:"\F9FE"}.mdi-lighthouse-on::before{content:"\F9FF"}.mdi-link::before{content:"\F337"}.mdi-link-box::before{content:"\FCF6"}.mdi-link-box-outline::before{content:"\FCF7"}.mdi-link-box-variant::before{content:"\FCF8"}.mdi-link-box-variant-outline::before{content:"\FCF9"}.mdi-link-lock::before{content:"\F00E5"}.mdi-link-off::before{content:"\F338"}.mdi-link-plus::before{content:"\FC70"}.mdi-link-variant::before{content:"\F339"}.mdi-link-variant-minus::before{content:"\F012A"}.mdi-link-variant-off::before{content:"\F33A"}.mdi-link-variant-plus::before{content:"\F012B"}.mdi-link-variant-remove::before{content:"\F012C"}.mdi-linkedin::before{content:"\F33B"}.mdi-linkedin-box::before{content:"\F33C"}.mdi-linux::before{content:"\F33D"}.mdi-linux-mint::before{content:"\F8EC"}.mdi-litecoin::before{content:"\FA60"}.mdi-loading::before{content:"\F771"}.mdi-location-enter::before{content:"\FFE4"}.mdi-location-exit::before{content:"\FFE5"}.mdi-lock::before{content:"\F33E"}.mdi-lock-alert::before{content:"\F8ED"}.mdi-lock-clock::before{content:"\F97E"}.mdi-lock-open::before{content:"\F33F"}.mdi-lock-open-outline::before{content:"\F340"}.mdi-lock-open-variant::before{content:"\FFE6"}.mdi-lock-open-variant-outline::before{content:"\FFE7"}.mdi-lock-outline::before{content:"\F341"}.mdi-lock-pattern::before{content:"\F6E9"}.mdi-lock-plus::before{content:"\F5FB"}.mdi-lock-question::before{content:"\F8EE"}.mdi-lock-reset::before{content:"\F772"}.mdi-lock-smart::before{content:"\F8B1"}.mdi-locker::before{content:"\F7D6"}.mdi-locker-multiple::before{content:"\F7D7"}.mdi-login::before{content:"\F342"}.mdi-login-variant::before{content:"\F5FC"}.mdi-logout::before{content:"\F343"}.mdi-logout-variant::before{content:"\F5FD"}.mdi-longitude::before{content:"\FF77"}.mdi-looks::before{content:"\F344"}.mdi-loupe::before{content:"\F345"}.mdi-lumx::before{content:"\F346"}.mdi-lungs::before{content:"\F00AF"}.mdi-lyft::before{content:"\FB1D"}.mdi-magnet::before{content:"\F347"}.mdi-magnet-on::before{content:"\F348"}.mdi-magnify::before{content:"\F349"}.mdi-magnify-close::before{content:"\F97F"}.mdi-magnify-minus::before{content:"\F34A"}.mdi-magnify-minus-cursor::before{content:"\FA61"}.mdi-magnify-minus-outline::before{content:"\F6EB"}.mdi-magnify-plus::before{content:"\F34B"}.mdi-magnify-plus-cursor::before{content:"\FA62"}.mdi-magnify-plus-outline::before{content:"\F6EC"}.mdi-magnify-remove-cursor::before{content:"\F0237"}.mdi-magnify-remove-outline::before{content:"\F0238"}.mdi-magnify-scan::before{content:"\F02A1"}.mdi-mail::before{content:"\FED8"}.mdi-mail-ru::before{content:"\F34C"}.mdi-mailbox::before{content:"\F6ED"}.mdi-mailbox-open::before{content:"\FD64"}.mdi-mailbox-open-outline::before{content:"\FD65"}.mdi-mailbox-open-up::before{content:"\FD66"}.mdi-mailbox-open-up-outline::before{content:"\FD67"}.mdi-mailbox-outline::before{content:"\FD68"}.mdi-mailbox-up::before{content:"\FD69"}.mdi-mailbox-up-outline::before{content:"\FD6A"}.mdi-map::before{content:"\F34D"}.mdi-map-check::before{content:"\FED9"}.mdi-map-check-outline::before{content:"\FEDA"}.mdi-map-clock::before{content:"\FCFA"}.mdi-map-clock-outline::before{content:"\FCFB"}.mdi-map-legend::before{content:"\FA00"}.mdi-map-marker::before{content:"\F34E"}.mdi-map-marker-alert::before{content:"\FF22"}.mdi-map-marker-alert-outline::before{content:"\FF23"}.mdi-map-marker-check::before{content:"\FC71"}.mdi-map-marker-check-outline::before{content:"\F0326"}.mdi-map-marker-circle::before{content:"\F34F"}.mdi-map-marker-distance::before{content:"\F8EF"}.mdi-map-marker-down::before{content:"\F012D"}.mdi-map-marker-left::before{content:"\F0306"}.mdi-map-marker-left-outline::before{content:"\F0308"}.mdi-map-marker-minus::before{content:"\F650"}.mdi-map-marker-minus-outline::before{content:"\F0324"}.mdi-map-marker-multiple::before{content:"\F350"}.mdi-map-marker-multiple-outline::before{content:"\F02A2"}.mdi-map-marker-off::before{content:"\F351"}.mdi-map-marker-off-outline::before{content:"\F0328"}.mdi-map-marker-outline::before{content:"\F7D8"}.mdi-map-marker-path::before{content:"\FCFC"}.mdi-map-marker-plus::before{content:"\F651"}.mdi-map-marker-plus-outline::before{content:"\F0323"}.mdi-map-marker-question::before{content:"\FF24"}.mdi-map-marker-question-outline::before{content:"\FF25"}.mdi-map-marker-radius::before{content:"\F352"}.mdi-map-marker-radius-outline::before{content:"\F0327"}.mdi-map-marker-remove::before{content:"\FF26"}.mdi-map-marker-remove-outline::before{content:"\F0325"}.mdi-map-marker-remove-variant::before{content:"\FF27"}.mdi-map-marker-right::before{content:"\F0307"}.mdi-map-marker-right-outline::before{content:"\F0309"}.mdi-map-marker-up::before{content:"\F012E"}.mdi-map-minus::before{content:"\F980"}.mdi-map-outline::before{content:"\F981"}.mdi-map-plus::before{content:"\F982"}.mdi-map-search::before{content:"\F983"}.mdi-map-search-outline::before{content:"\F984"}.mdi-mapbox::before{content:"\FB86"}.mdi-margin::before{content:"\F353"}.mdi-markdown::before{content:"\F354"}.mdi-markdown-outline::before{content:"\FF78"}.mdi-marker::before{content:"\F652"}.mdi-marker-cancel::before{content:"\FDB5"}.mdi-marker-check::before{content:"\F355"}.mdi-mastodon::before{content:"\FAD0"}.mdi-mastodon-variant::before{content:"\FAD1"}.mdi-material-design::before{content:"\F985"}.mdi-material-ui::before{content:"\F357"}.mdi-math-compass::before{content:"\F358"}.mdi-math-cos::before{content:"\FC72"}.mdi-math-integral::before{content:"\FFE8"}.mdi-math-integral-box::before{content:"\FFE9"}.mdi-math-log::before{content:"\F00B0"}.mdi-math-norm::before{content:"\FFEA"}.mdi-math-norm-box::before{content:"\FFEB"}.mdi-math-sin::before{content:"\FC73"}.mdi-math-tan::before{content:"\FC74"}.mdi-matrix::before{content:"\F628"}.mdi-medal::before{content:"\F986"}.mdi-medal-outline::before{content:"\F0351"}.mdi-medical-bag::before{content:"\F6EE"}.mdi-meditation::before{content:"\F01A6"}.mdi-medium::before{content:"\F35A"}.mdi-meetup::before{content:"\FAD2"}.mdi-memory::before{content:"\F35B"}.mdi-menu::before{content:"\F35C"}.mdi-menu-down::before{content:"\F35D"}.mdi-menu-down-outline::before{content:"\F6B5"}.mdi-menu-left::before{content:"\F35E"}.mdi-menu-left-outline::before{content:"\FA01"}.mdi-menu-open::before{content:"\FB87"}.mdi-menu-right::before{content:"\F35F"}.mdi-menu-right-outline::before{content:"\FA02"}.mdi-menu-swap::before{content:"\FA63"}.mdi-menu-swap-outline::before{content:"\FA64"}.mdi-menu-up::before{content:"\F360"}.mdi-menu-up-outline::before{content:"\F6B6"}.mdi-merge::before{content:"\FF79"}.mdi-message::before{content:"\F361"}.mdi-message-alert::before{content:"\F362"}.mdi-message-alert-outline::before{content:"\FA03"}.mdi-message-arrow-left::before{content:"\F031D"}.mdi-message-arrow-left-outline::before{content:"\F031E"}.mdi-message-arrow-right::before{content:"\F031F"}.mdi-message-arrow-right-outline::before{content:"\F0320"}.mdi-message-bulleted::before{content:"\F6A1"}.mdi-message-bulleted-off::before{content:"\F6A2"}.mdi-message-draw::before{content:"\F363"}.mdi-message-image::before{content:"\F364"}.mdi-message-image-outline::before{content:"\F0197"}.mdi-message-lock::before{content:"\FFEC"}.mdi-message-lock-outline::before{content:"\F0198"}.mdi-message-minus::before{content:"\F0199"}.mdi-message-minus-outline::before{content:"\F019A"}.mdi-message-outline::before{content:"\F365"}.mdi-message-plus::before{content:"\F653"}.mdi-message-plus-outline::before{content:"\F00E6"}.mdi-message-processing::before{content:"\F366"}.mdi-message-processing-outline::before{content:"\F019B"}.mdi-message-reply::before{content:"\F367"}.mdi-message-reply-text::before{content:"\F368"}.mdi-message-settings::before{content:"\F6EF"}.mdi-message-settings-outline::before{content:"\F019C"}.mdi-message-settings-variant::before{content:"\F6F0"}.mdi-message-settings-variant-outline::before{content:"\F019D"}.mdi-message-text::before{content:"\F369"}.mdi-message-text-clock::before{content:"\F019E"}.mdi-message-text-clock-outline::before{content:"\F019F"}.mdi-message-text-lock::before{content:"\FFED"}.mdi-message-text-lock-outline::before{content:"\F01A0"}.mdi-message-text-outline::before{content:"\F36A"}.mdi-message-video::before{content:"\F36B"}.mdi-meteor::before{content:"\F629"}.mdi-metronome::before{content:"\F7D9"}.mdi-metronome-tick::before{content:"\F7DA"}.mdi-micro-sd::before{content:"\F7DB"}.mdi-microphone::before{content:"\F36C"}.mdi-microphone-minus::before{content:"\F8B2"}.mdi-microphone-off::before{content:"\F36D"}.mdi-microphone-outline::before{content:"\F36E"}.mdi-microphone-plus::before{content:"\F8B3"}.mdi-microphone-settings::before{content:"\F36F"}.mdi-microphone-variant::before{content:"\F370"}.mdi-microphone-variant-off::before{content:"\F371"}.mdi-microscope::before{content:"\F654"}.mdi-microsoft::before{content:"\F372"}.mdi-microsoft-dynamics::before{content:"\F987"}.mdi-microwave::before{content:"\FC75"}.mdi-middleware::before{content:"\FF7A"}.mdi-middleware-outline::before{content:"\FF7B"}.mdi-midi::before{content:"\F8F0"}.mdi-midi-port::before{content:"\F8F1"}.mdi-mine::before{content:"\FDB6"}.mdi-minecraft::before{content:"\F373"}.mdi-mini-sd::before{content:"\FA04"}.mdi-minidisc::before{content:"\FA05"}.mdi-minus::before{content:"\F374"}.mdi-minus-box::before{content:"\F375"}.mdi-minus-box-multiple::before{content:"\F016C"}.mdi-minus-box-multiple-outline::before{content:"\F016D"}.mdi-minus-box-outline::before{content:"\F6F1"}.mdi-minus-circle::before{content:"\F376"}.mdi-minus-circle-outline::before{content:"\F377"}.mdi-minus-network::before{content:"\F378"}.mdi-minus-network-outline::before{content:"\FC76"}.mdi-mirror::before{content:"\F0228"}.mdi-mixcloud::before{content:"\F62A"}.mdi-mixed-martial-arts::before{content:"\FD6B"}.mdi-mixed-reality::before{content:"\F87E"}.mdi-mixer::before{content:"\F7DC"}.mdi-molecule::before{content:"\FB88"}.mdi-monitor::before{content:"\F379"}.mdi-monitor-cellphone::before{content:"\F988"}.mdi-monitor-cellphone-star::before{content:"\F989"}.mdi-monitor-clean::before{content:"\F012F"}.mdi-monitor-dashboard::before{content:"\FA06"}.mdi-monitor-edit::before{content:"\F02F1"}.mdi-monitor-lock::before{content:"\FDB7"}.mdi-monitor-multiple::before{content:"\F37A"}.mdi-monitor-off::before{content:"\FD6C"}.mdi-monitor-screenshot::before{content:"\FE34"}.mdi-monitor-speaker::before{content:"\FF7C"}.mdi-monitor-speaker-off::before{content:"\FF7D"}.mdi-monitor-star::before{content:"\FDB8"}.mdi-moon-first-quarter::before{content:"\FF7E"}.mdi-moon-full::before{content:"\FF7F"}.mdi-moon-last-quarter::before{content:"\FF80"}.mdi-moon-new::before{content:"\FF81"}.mdi-moon-waning-crescent::before{content:"\FF82"}.mdi-moon-waning-gibbous::before{content:"\FF83"}.mdi-moon-waxing-crescent::before{content:"\FF84"}.mdi-moon-waxing-gibbous::before{content:"\FF85"}.mdi-moped::before{content:"\F00B1"}.mdi-more::before{content:"\F37B"}.mdi-mother-heart::before{content:"\F033F"}.mdi-mother-nurse::before{content:"\FCFD"}.mdi-motion-sensor::before{content:"\FD6D"}.mdi-motorbike::before{content:"\F37C"}.mdi-mouse::before{content:"\F37D"}.mdi-mouse-bluetooth::before{content:"\F98A"}.mdi-mouse-off::before{content:"\F37E"}.mdi-mouse-variant::before{content:"\F37F"}.mdi-mouse-variant-off::before{content:"\F380"}.mdi-move-resize::before{content:"\F655"}.mdi-move-resize-variant::before{content:"\F656"}.mdi-movie::before{content:"\F381"}.mdi-movie-edit::before{content:"\F014D"}.mdi-movie-edit-outline::before{content:"\F014E"}.mdi-movie-filter::before{content:"\F014F"}.mdi-movie-filter-outline::before{content:"\F0150"}.mdi-movie-open::before{content:"\FFEE"}.mdi-movie-open-outline::before{content:"\FFEF"}.mdi-movie-outline::before{content:"\FDB9"}.mdi-movie-roll::before{content:"\F7DD"}.mdi-movie-search::before{content:"\F01FD"}.mdi-movie-search-outline::before{content:"\F01FE"}.mdi-muffin::before{content:"\F98B"}.mdi-multiplication::before{content:"\F382"}.mdi-multiplication-box::before{content:"\F383"}.mdi-mushroom::before{content:"\F7DE"}.mdi-mushroom-outline::before{content:"\F7DF"}.mdi-music::before{content:"\F759"}.mdi-music-accidental-double-flat::before{content:"\FF86"}.mdi-music-accidental-double-sharp::before{content:"\FF87"}.mdi-music-accidental-flat::before{content:"\FF88"}.mdi-music-accidental-natural::before{content:"\FF89"}.mdi-music-accidental-sharp::before{content:"\FF8A"}.mdi-music-box::before{content:"\F384"}.mdi-music-box-outline::before{content:"\F385"}.mdi-music-circle::before{content:"\F386"}.mdi-music-circle-outline::before{content:"\FAD3"}.mdi-music-clef-alto::before{content:"\FF8B"}.mdi-music-clef-bass::before{content:"\FF8C"}.mdi-music-clef-treble::before{content:"\FF8D"}.mdi-music-note::before{content:"\F387"}.mdi-music-note-bluetooth::before{content:"\F5FE"}.mdi-music-note-bluetooth-off::before{content:"\F5FF"}.mdi-music-note-eighth::before{content:"\F388"}.mdi-music-note-eighth-dotted::before{content:"\FF8E"}.mdi-music-note-half::before{content:"\F389"}.mdi-music-note-half-dotted::before{content:"\FF8F"}.mdi-music-note-off::before{content:"\F38A"}.mdi-music-note-off-outline::before{content:"\FF90"}.mdi-music-note-outline::before{content:"\FF91"}.mdi-music-note-plus::before{content:"\FDBA"}.mdi-music-note-quarter::before{content:"\F38B"}.mdi-music-note-quarter-dotted::before{content:"\FF92"}.mdi-music-note-sixteenth::before{content:"\F38C"}.mdi-music-note-sixteenth-dotted::before{content:"\FF93"}.mdi-music-note-whole::before{content:"\F38D"}.mdi-music-note-whole-dotted::before{content:"\FF94"}.mdi-music-off::before{content:"\F75A"}.mdi-music-rest-eighth::before{content:"\FF95"}.mdi-music-rest-half::before{content:"\FF96"}.mdi-music-rest-quarter::before{content:"\FF97"}.mdi-music-rest-sixteenth::before{content:"\FF98"}.mdi-music-rest-whole::before{content:"\FF99"}.mdi-nail::before{content:"\FDBB"}.mdi-nas::before{content:"\F8F2"}.mdi-nativescript::before{content:"\F87F"}.mdi-nature::before{content:"\F38E"}.mdi-nature-people::before{content:"\F38F"}.mdi-navigation::before{content:"\F390"}.mdi-near-me::before{content:"\F5CD"}.mdi-necklace::before{content:"\FF28"}.mdi-needle::before{content:"\F391"}.mdi-netflix::before{content:"\F745"}.mdi-network::before{content:"\F6F2"}.mdi-network-off::before{content:"\FC77"}.mdi-network-off-outline::before{content:"\FC78"}.mdi-network-outline::before{content:"\FC79"}.mdi-network-router::before{content:"\F00B2"}.mdi-network-strength-1::before{content:"\F8F3"}.mdi-network-strength-1-alert::before{content:"\F8F4"}.mdi-network-strength-2::before{content:"\F8F5"}.mdi-network-strength-2-alert::before{content:"\F8F6"}.mdi-network-strength-3::before{content:"\F8F7"}.mdi-network-strength-3-alert::before{content:"\F8F8"}.mdi-network-strength-4::before{content:"\F8F9"}.mdi-network-strength-4-alert::before{content:"\F8FA"}.mdi-network-strength-off::before{content:"\F8FB"}.mdi-network-strength-off-outline::before{content:"\F8FC"}.mdi-network-strength-outline::before{content:"\F8FD"}.mdi-new-box::before{content:"\F394"}.mdi-newspaper::before{content:"\F395"}.mdi-newspaper-minus::before{content:"\FF29"}.mdi-newspaper-plus::before{content:"\FF2A"}.mdi-newspaper-variant::before{content:"\F0023"}.mdi-newspaper-variant-multiple::before{content:"\F0024"}.mdi-newspaper-variant-multiple-outline::before{content:"\F0025"}.mdi-newspaper-variant-outline::before{content:"\F0026"}.mdi-nfc::before{content:"\F396"}.mdi-nfc-off::before{content:"\FE35"}.mdi-nfc-search-variant::before{content:"\FE36"}.mdi-nfc-tap::before{content:"\F397"}.mdi-nfc-variant::before{content:"\F398"}.mdi-nfc-variant-off::before{content:"\FE37"}.mdi-ninja::before{content:"\F773"}.mdi-nintendo-switch::before{content:"\F7E0"}.mdi-nix::before{content:"\F0130"}.mdi-nodejs::before{content:"\F399"}.mdi-noodles::before{content:"\F01A9"}.mdi-not-equal::before{content:"\F98C"}.mdi-not-equal-variant::before{content:"\F98D"}.mdi-note::before{content:"\F39A"}.mdi-note-multiple::before{content:"\F6B7"}.mdi-note-multiple-outline::before{content:"\F6B8"}.mdi-note-outline::before{content:"\F39B"}.mdi-note-plus::before{content:"\F39C"}.mdi-note-plus-outline::before{content:"\F39D"}.mdi-note-text::before{content:"\F39E"}.mdi-note-text-outline::before{content:"\F0202"}.mdi-notebook::before{content:"\F82D"}.mdi-notebook-multiple::before{content:"\FE38"}.mdi-notebook-outline::before{content:"\FEDC"}.mdi-notification-clear-all::before{content:"\F39F"}.mdi-npm::before{content:"\F6F6"}.mdi-npm-variant::before{content:"\F98E"}.mdi-npm-variant-outline::before{content:"\F98F"}.mdi-nuke::before{content:"\F6A3"}.mdi-null::before{content:"\F7E1"}.mdi-numeric::before{content:"\F3A0"}.mdi-numeric-0::before{content:"\30"}.mdi-numeric-0-box::before{content:"\F3A1"}.mdi-numeric-0-box-multiple::before{content:"\FF2B"}.mdi-numeric-0-box-multiple-outline::before{content:"\F3A2"}.mdi-numeric-0-box-outline::before{content:"\F3A3"}.mdi-numeric-0-circle::before{content:"\FC7A"}.mdi-numeric-0-circle-outline::before{content:"\FC7B"}.mdi-numeric-1::before{content:"\31"}.mdi-numeric-1-box::before{content:"\F3A4"}.mdi-numeric-1-box-multiple::before{content:"\FF2C"}.mdi-numeric-1-box-multiple-outline::before{content:"\F3A5"}.mdi-numeric-1-box-outline::before{content:"\F3A6"}.mdi-numeric-1-circle::before{content:"\FC7C"}.mdi-numeric-1-circle-outline::before{content:"\FC7D"}.mdi-numeric-10::before{content:"\F000A"}.mdi-numeric-10-box::before{content:"\FF9A"}.mdi-numeric-10-box-multiple::before{content:"\F000B"}.mdi-numeric-10-box-multiple-outline::before{content:"\F000C"}.mdi-numeric-10-box-outline::before{content:"\FF9B"}.mdi-numeric-10-circle::before{content:"\F000D"}.mdi-numeric-10-circle-outline::before{content:"\F000E"}.mdi-numeric-2::before{content:"\32"}.mdi-numeric-2-box::before{content:"\F3A7"}.mdi-numeric-2-box-multiple::before{content:"\FF2D"}.mdi-numeric-2-box-multiple-outline::before{content:"\F3A8"}.mdi-numeric-2-box-outline::before{content:"\F3A9"}.mdi-numeric-2-circle::before{content:"\FC7E"}.mdi-numeric-2-circle-outline::before{content:"\FC7F"}.mdi-numeric-3::before{content:"\33"}.mdi-numeric-3-box::before{content:"\F3AA"}.mdi-numeric-3-box-multiple::before{content:"\FF2E"}.mdi-numeric-3-box-multiple-outline::before{content:"\F3AB"}.mdi-numeric-3-box-outline::before{content:"\F3AC"}.mdi-numeric-3-circle::before{content:"\FC80"}.mdi-numeric-3-circle-outline::before{content:"\FC81"}.mdi-numeric-4::before{content:"\34"}.mdi-numeric-4-box::before{content:"\F3AD"}.mdi-numeric-4-box-multiple::before{content:"\FF2F"}.mdi-numeric-4-box-multiple-outline::before{content:"\F3AE"}.mdi-numeric-4-box-outline::before{content:"\F3AF"}.mdi-numeric-4-circle::before{content:"\FC82"}.mdi-numeric-4-circle-outline::before{content:"\FC83"}.mdi-numeric-5::before{content:"\35"}.mdi-numeric-5-box::before{content:"\F3B0"}.mdi-numeric-5-box-multiple::before{content:"\FF30"}.mdi-numeric-5-box-multiple-outline::before{content:"\F3B1"}.mdi-numeric-5-box-outline::before{content:"\F3B2"}.mdi-numeric-5-circle::before{content:"\FC84"}.mdi-numeric-5-circle-outline::before{content:"\FC85"}.mdi-numeric-6::before{content:"\36"}.mdi-numeric-6-box::before{content:"\F3B3"}.mdi-numeric-6-box-multiple::before{content:"\FF31"}.mdi-numeric-6-box-multiple-outline::before{content:"\F3B4"}.mdi-numeric-6-box-outline::before{content:"\F3B5"}.mdi-numeric-6-circle::before{content:"\FC86"}.mdi-numeric-6-circle-outline::before{content:"\FC87"}.mdi-numeric-7::before{content:"\37"}.mdi-numeric-7-box::before{content:"\F3B6"}.mdi-numeric-7-box-multiple::before{content:"\FF32"}.mdi-numeric-7-box-multiple-outline::before{content:"\F3B7"}.mdi-numeric-7-box-outline::before{content:"\F3B8"}.mdi-numeric-7-circle::before{content:"\FC88"}.mdi-numeric-7-circle-outline::before{content:"\FC89"}.mdi-numeric-8::before{content:"\38"}.mdi-numeric-8-box::before{content:"\F3B9"}.mdi-numeric-8-box-multiple::before{content:"\FF33"}.mdi-numeric-8-box-multiple-outline::before{content:"\F3BA"}.mdi-numeric-8-box-outline::before{content:"\F3BB"}.mdi-numeric-8-circle::before{content:"\FC8A"}.mdi-numeric-8-circle-outline::before{content:"\FC8B"}.mdi-numeric-9::before{content:"\39"}.mdi-numeric-9-box::before{content:"\F3BC"}.mdi-numeric-9-box-multiple::before{content:"\FF34"}.mdi-numeric-9-box-multiple-outline::before{content:"\F3BD"}.mdi-numeric-9-box-outline::before{content:"\F3BE"}.mdi-numeric-9-circle::before{content:"\FC8C"}.mdi-numeric-9-circle-outline::before{content:"\FC8D"}.mdi-numeric-9-plus::before{content:"\F000F"}.mdi-numeric-9-plus-box::before{content:"\F3BF"}.mdi-numeric-9-plus-box-multiple::before{content:"\FF35"}.mdi-numeric-9-plus-box-multiple-outline::before{content:"\F3C0"}.mdi-numeric-9-plus-box-outline::before{content:"\F3C1"}.mdi-numeric-9-plus-circle::before{content:"\FC8E"}.mdi-numeric-9-plus-circle-outline::before{content:"\FC8F"}.mdi-numeric-negative-1::before{content:"\F0074"}.mdi-nut::before{content:"\F6F7"}.mdi-nutrition::before{content:"\F3C2"}.mdi-nuxt::before{content:"\F0131"}.mdi-oar::before{content:"\F67B"}.mdi-ocarina::before{content:"\FDBC"}.mdi-oci::before{content:"\F0314"}.mdi-ocr::before{content:"\F0165"}.mdi-octagon::before{content:"\F3C3"}.mdi-octagon-outline::before{content:"\F3C4"}.mdi-octagram::before{content:"\F6F8"}.mdi-octagram-outline::before{content:"\F774"}.mdi-odnoklassniki::before{content:"\F3C5"}.mdi-offer::before{content:"\F0246"}.mdi-office::before{content:"\F3C6"}.mdi-office-building::before{content:"\F990"}.mdi-oil::before{content:"\F3C7"}.mdi-oil-lamp::before{content:"\FF36"}.mdi-oil-level::before{content:"\F0075"}.mdi-oil-temperature::before{content:"\F0019"}.mdi-omega::before{content:"\F3C9"}.mdi-one-up::before{content:"\FB89"}.mdi-onedrive::before{content:"\F3CA"}.mdi-onenote::before{content:"\F746"}.mdi-onepassword::before{content:"\F880"}.mdi-opacity::before{content:"\F5CC"}.mdi-open-in-app::before{content:"\F3CB"}.mdi-open-in-new::before{content:"\F3CC"}.mdi-open-source-initiative::before{content:"\FB8A"}.mdi-openid::before{content:"\F3CD"}.mdi-opera::before{content:"\F3CE"}.mdi-orbit::before{content:"\F018"}.mdi-origin::before{content:"\FB2B"}.mdi-ornament::before{content:"\F3CF"}.mdi-ornament-variant::before{content:"\F3D0"}.mdi-outdoor-lamp::before{content:"\F0076"}.mdi-outlook::before{content:"\FCFE"}.mdi-overscan::before{content:"\F0027"}.mdi-owl::before{content:"\F3D2"}.mdi-pac-man::before{content:"\FB8B"}.mdi-package::before{content:"\F3D3"}.mdi-package-down::before{content:"\F3D4"}.mdi-package-up::before{content:"\F3D5"}.mdi-package-variant::before{content:"\F3D6"}.mdi-package-variant-closed::before{content:"\F3D7"}.mdi-page-first::before{content:"\F600"}.mdi-page-last::before{content:"\F601"}.mdi-page-layout-body::before{content:"\F6F9"}.mdi-page-layout-footer::before{content:"\F6FA"}.mdi-page-layout-header::before{content:"\F6FB"}.mdi-page-layout-header-footer::before{content:"\FF9C"}.mdi-page-layout-sidebar-left::before{content:"\F6FC"}.mdi-page-layout-sidebar-right::before{content:"\F6FD"}.mdi-page-next::before{content:"\FB8C"}.mdi-page-next-outline::before{content:"\FB8D"}.mdi-page-previous::before{content:"\FB8E"}.mdi-page-previous-outline::before{content:"\FB8F"}.mdi-palette::before{content:"\F3D8"}.mdi-palette-advanced::before{content:"\F3D9"}.mdi-palette-outline::before{content:"\FE6C"}.mdi-palette-swatch::before{content:"\F8B4"}.mdi-palette-swatch-outline::before{content:"\F0387"}.mdi-palm-tree::before{content:"\F0077"}.mdi-pan::before{content:"\FB90"}.mdi-pan-bottom-left::before{content:"\FB91"}.mdi-pan-bottom-right::before{content:"\FB92"}.mdi-pan-down::before{content:"\FB93"}.mdi-pan-horizontal::before{content:"\FB94"}.mdi-pan-left::before{content:"\FB95"}.mdi-pan-right::before{content:"\FB96"}.mdi-pan-top-left::before{content:"\FB97"}.mdi-pan-top-right::before{content:"\FB98"}.mdi-pan-up::before{content:"\FB99"}.mdi-pan-vertical::before{content:"\FB9A"}.mdi-panda::before{content:"\F3DA"}.mdi-pandora::before{content:"\F3DB"}.mdi-panorama::before{content:"\F3DC"}.mdi-panorama-fisheye::before{content:"\F3DD"}.mdi-panorama-horizontal::before{content:"\F3DE"}.mdi-panorama-vertical::before{content:"\F3DF"}.mdi-panorama-wide-angle::before{content:"\F3E0"}.mdi-paper-cut-vertical::before{content:"\F3E1"}.mdi-paper-roll::before{content:"\F0182"}.mdi-paper-roll-outline::before{content:"\F0183"}.mdi-paperclip::before{content:"\F3E2"}.mdi-parachute::before{content:"\FC90"}.mdi-parachute-outline::before{content:"\FC91"}.mdi-parking::before{content:"\F3E3"}.mdi-party-popper::before{content:"\F0078"}.mdi-passport::before{content:"\F7E2"}.mdi-passport-biometric::before{content:"\FDBD"}.mdi-pasta::before{content:"\F018B"}.mdi-patio-heater::before{content:"\FF9D"}.mdi-patreon::before{content:"\F881"}.mdi-pause::before{content:"\F3E4"}.mdi-pause-circle::before{content:"\F3E5"}.mdi-pause-circle-outline::before{content:"\F3E6"}.mdi-pause-octagon::before{content:"\F3E7"}.mdi-pause-octagon-outline::before{content:"\F3E8"}.mdi-paw::before{content:"\F3E9"}.mdi-paw-off::before{content:"\F657"}.mdi-paypal::before{content:"\F882"}.mdi-pdf-box::before{content:"\FE39"}.mdi-peace::before{content:"\F883"}.mdi-peanut::before{content:"\F001E"}.mdi-peanut-off::before{content:"\F001F"}.mdi-peanut-off-outline::before{content:"\F0021"}.mdi-peanut-outline::before{content:"\F0020"}.mdi-pen::before{content:"\F3EA"}.mdi-pen-lock::before{content:"\FDBE"}.mdi-pen-minus::before{content:"\FDBF"}.mdi-pen-off::before{content:"\FDC0"}.mdi-pen-plus::before{content:"\FDC1"}.mdi-pen-remove::before{content:"\FDC2"}.mdi-pencil::before{content:"\F3EB"}.mdi-pencil-box::before{content:"\F3EC"}.mdi-pencil-box-multiple::before{content:"\F016F"}.mdi-pencil-box-multiple-outline::before{content:"\F0170"}.mdi-pencil-box-outline::before{content:"\F3ED"}.mdi-pencil-circle::before{content:"\F6FE"}.mdi-pencil-circle-outline::before{content:"\F775"}.mdi-pencil-lock::before{content:"\F3EE"}.mdi-pencil-lock-outline::before{content:"\FDC3"}.mdi-pencil-minus::before{content:"\FDC4"}.mdi-pencil-minus-outline::before{content:"\FDC5"}.mdi-pencil-off::before{content:"\F3EF"}.mdi-pencil-off-outline::before{content:"\FDC6"}.mdi-pencil-outline::before{content:"\FC92"}.mdi-pencil-plus::before{content:"\FDC7"}.mdi-pencil-plus-outline::before{content:"\FDC8"}.mdi-pencil-remove::before{content:"\FDC9"}.mdi-pencil-remove-outline::before{content:"\FDCA"}.mdi-pencil-ruler::before{content:"\F037E"}.mdi-penguin::before{content:"\FEDD"}.mdi-pentagon::before{content:"\F6FF"}.mdi-pentagon-outline::before{content:"\F700"}.mdi-percent::before{content:"\F3F0"}.mdi-percent-outline::before{content:"\F02A3"}.mdi-periodic-table::before{content:"\F8B5"}.mdi-periodic-table-co::before{content:"\F0329"}.mdi-periodic-table-co2::before{content:"\F7E3"}.mdi-periscope::before{content:"\F747"}.mdi-perspective-less::before{content:"\FCFF"}.mdi-perspective-more::before{content:"\FD00"}.mdi-pharmacy::before{content:"\F3F1"}.mdi-phone::before{content:"\F3F2"}.mdi-phone-alert::before{content:"\FF37"}.mdi-phone-alert-outline::before{content:"\F01B9"}.mdi-phone-bluetooth::before{content:"\F3F3"}.mdi-phone-bluetooth-outline::before{content:"\F01BA"}.mdi-phone-cancel::before{content:"\F00E7"}.mdi-phone-cancel-outline::before{content:"\F01BB"}.mdi-phone-check::before{content:"\F01D4"}.mdi-phone-check-outline::before{content:"\F01D5"}.mdi-phone-classic::before{content:"\F602"}.mdi-phone-classic-off::before{content:"\F02A4"}.mdi-phone-forward::before{content:"\F3F4"}.mdi-phone-forward-outline::before{content:"\F01BC"}.mdi-phone-hangup::before{content:"\F3F5"}.mdi-phone-hangup-outline::before{content:"\F01BD"}.mdi-phone-in-talk::before{content:"\F3F6"}.mdi-phone-in-talk-outline::before{content:"\F01AD"}.mdi-phone-incoming::before{content:"\F3F7"}.mdi-phone-incoming-outline::before{content:"\F01BE"}.mdi-phone-lock::before{content:"\F3F8"}.mdi-phone-lock-outline::before{content:"\F01BF"}.mdi-phone-log::before{content:"\F3F9"}.mdi-phone-log-outline::before{content:"\F01C0"}.mdi-phone-message::before{content:"\F01C1"}.mdi-phone-message-outline::before{content:"\F01C2"}.mdi-phone-minus::before{content:"\F658"}.mdi-phone-minus-outline::before{content:"\F01C3"}.mdi-phone-missed::before{content:"\F3FA"}.mdi-phone-missed-outline::before{content:"\F01D0"}.mdi-phone-off::before{content:"\FDCB"}.mdi-phone-off-outline::before{content:"\F01D1"}.mdi-phone-outgoing::before{content:"\F3FB"}.mdi-phone-outgoing-outline::before{content:"\F01C4"}.mdi-phone-outline::before{content:"\FDCC"}.mdi-phone-paused::before{content:"\F3FC"}.mdi-phone-paused-outline::before{content:"\F01C5"}.mdi-phone-plus::before{content:"\F659"}.mdi-phone-plus-outline::before{content:"\F01C6"}.mdi-phone-return::before{content:"\F82E"}.mdi-phone-return-outline::before{content:"\F01C7"}.mdi-phone-ring::before{content:"\F01D6"}.mdi-phone-ring-outline::before{content:"\F01D7"}.mdi-phone-rotate-landscape::before{content:"\F884"}.mdi-phone-rotate-portrait::before{content:"\F885"}.mdi-phone-settings::before{content:"\F3FD"}.mdi-phone-settings-outline::before{content:"\F01C8"}.mdi-phone-voip::before{content:"\F3FE"}.mdi-pi::before{content:"\F3FF"}.mdi-pi-box::before{content:"\F400"}.mdi-pi-hole::before{content:"\FDCD"}.mdi-piano::before{content:"\F67C"}.mdi-pickaxe::before{content:"\F8B6"}.mdi-picture-in-picture-bottom-right::before{content:"\FE3A"}.mdi-picture-in-picture-bottom-right-outline::before{content:"\FE3B"}.mdi-picture-in-picture-top-right::before{content:"\FE3C"}.mdi-picture-in-picture-top-right-outline::before{content:"\FE3D"}.mdi-pier::before{content:"\F886"}.mdi-pier-crane::before{content:"\F887"}.mdi-pig::before{content:"\F401"}.mdi-pig-variant::before{content:"\F0028"}.mdi-piggy-bank::before{content:"\F0029"}.mdi-pill::before{content:"\F402"}.mdi-pillar::before{content:"\F701"}.mdi-pin::before{content:"\F403"}.mdi-pin-off::before{content:"\F404"}.mdi-pin-off-outline::before{content:"\F92F"}.mdi-pin-outline::before{content:"\F930"}.mdi-pine-tree::before{content:"\F405"}.mdi-pine-tree-box::before{content:"\F406"}.mdi-pinterest::before{content:"\F407"}.mdi-pinterest-box::before{content:"\F408"}.mdi-pinwheel::before{content:"\FAD4"}.mdi-pinwheel-outline::before{content:"\FAD5"}.mdi-pipe::before{content:"\F7E4"}.mdi-pipe-disconnected::before{content:"\F7E5"}.mdi-pipe-leak::before{content:"\F888"}.mdi-pipe-wrench::before{content:"\F037F"}.mdi-pirate::before{content:"\FA07"}.mdi-pistol::before{content:"\F702"}.mdi-piston::before{content:"\F889"}.mdi-pizza::before{content:"\F409"}.mdi-play::before{content:"\F40A"}.mdi-play-box::before{content:"\F02A5"}.mdi-play-box-outline::before{content:"\F40B"}.mdi-play-circle::before{content:"\F40C"}.mdi-play-circle-outline::before{content:"\F40D"}.mdi-play-network::before{content:"\F88A"}.mdi-play-network-outline::before{content:"\FC93"}.mdi-play-outline::before{content:"\FF38"}.mdi-play-pause::before{content:"\F40E"}.mdi-play-protected-content::before{content:"\F40F"}.mdi-play-speed::before{content:"\F8FE"}.mdi-playlist-check::before{content:"\F5C7"}.mdi-playlist-edit::before{content:"\F8FF"}.mdi-playlist-minus::before{content:"\F410"}.mdi-playlist-music::before{content:"\FC94"}.mdi-playlist-music-outline::before{content:"\FC95"}.mdi-playlist-play::before{content:"\F411"}.mdi-playlist-plus::before{content:"\F412"}.mdi-playlist-remove::before{content:"\F413"}.mdi-playlist-star::before{content:"\FDCE"}.mdi-playstation::before{content:"\F414"}.mdi-plex::before{content:"\F6B9"}.mdi-plus::before{content:"\F415"}.mdi-plus-box::before{content:"\F416"}.mdi-plus-box-multiple::before{content:"\F334"}.mdi-plus-box-multiple-outline::before{content:"\F016E"}.mdi-plus-box-outline::before{content:"\F703"}.mdi-plus-circle::before{content:"\F417"}.mdi-plus-circle-multiple-outline::before{content:"\F418"}.mdi-plus-circle-outline::before{content:"\F419"}.mdi-plus-minus::before{content:"\F991"}.mdi-plus-minus-box::before{content:"\F992"}.mdi-plus-network::before{content:"\F41A"}.mdi-plus-network-outline::before{content:"\FC96"}.mdi-plus-one::before{content:"\F41B"}.mdi-plus-outline::before{content:"\F704"}.mdi-plus-thick::before{content:"\F0217"}.mdi-pocket::before{content:"\F41C"}.mdi-podcast::before{content:"\F993"}.mdi-podium::before{content:"\FD01"}.mdi-podium-bronze::before{content:"\FD02"}.mdi-podium-gold::before{content:"\FD03"}.mdi-podium-silver::before{content:"\FD04"}.mdi-point-of-sale::before{content:"\FD6E"}.mdi-pokeball::before{content:"\F41D"}.mdi-pokemon-go::before{content:"\FA08"}.mdi-poker-chip::before{content:"\F82F"}.mdi-polaroid::before{content:"\F41E"}.mdi-police-badge::before{content:"\F0192"}.mdi-police-badge-outline::before{content:"\F0193"}.mdi-poll::before{content:"\F41F"}.mdi-poll-box::before{content:"\F420"}.mdi-poll-box-outline::before{content:"\F02A6"}.mdi-polymer::before{content:"\F421"}.mdi-pool::before{content:"\F606"}.mdi-popcorn::before{content:"\F422"}.mdi-post::before{content:"\F002A"}.mdi-post-outline::before{content:"\F002B"}.mdi-postage-stamp::before{content:"\FC97"}.mdi-pot::before{content:"\F65A"}.mdi-pot-mix::before{content:"\F65B"}.mdi-pound::before{content:"\F423"}.mdi-pound-box::before{content:"\F424"}.mdi-pound-box-outline::before{content:"\F01AA"}.mdi-power::before{content:"\F425"}.mdi-power-cycle::before{content:"\F900"}.mdi-power-off::before{content:"\F901"}.mdi-power-on::before{content:"\F902"}.mdi-power-plug::before{content:"\F6A4"}.mdi-power-plug-off::before{content:"\F6A5"}.mdi-power-settings::before{content:"\F426"}.mdi-power-sleep::before{content:"\F903"}.mdi-power-socket::before{content:"\F427"}.mdi-power-socket-au::before{content:"\F904"}.mdi-power-socket-de::before{content:"\F0132"}.mdi-power-socket-eu::before{content:"\F7E6"}.mdi-power-socket-fr::before{content:"\F0133"}.mdi-power-socket-jp::before{content:"\F0134"}.mdi-power-socket-uk::before{content:"\F7E7"}.mdi-power-socket-us::before{content:"\F7E8"}.mdi-power-standby::before{content:"\F905"}.mdi-powershell::before{content:"\FA09"}.mdi-prescription::before{content:"\F705"}.mdi-presentation::before{content:"\F428"}.mdi-presentation-play::before{content:"\F429"}.mdi-printer::before{content:"\F42A"}.mdi-printer-3d::before{content:"\F42B"}.mdi-printer-3d-nozzle::before{content:"\FE3E"}.mdi-printer-3d-nozzle-alert::before{content:"\F01EB"}.mdi-printer-3d-nozzle-alert-outline::before{content:"\F01EC"}.mdi-printer-3d-nozzle-outline::before{content:"\FE3F"}.mdi-printer-alert::before{content:"\F42C"}.mdi-printer-check::before{content:"\F0171"}.mdi-printer-off::before{content:"\FE40"}.mdi-printer-pos::before{content:"\F0079"}.mdi-printer-settings::before{content:"\F706"}.mdi-printer-wireless::before{content:"\FA0A"}.mdi-priority-high::before{content:"\F603"}.mdi-priority-low::before{content:"\F604"}.mdi-professional-hexagon::before{content:"\F42D"}.mdi-progress-alert::before{content:"\FC98"}.mdi-progress-check::before{content:"\F994"}.mdi-progress-clock::before{content:"\F995"}.mdi-progress-close::before{content:"\F0135"}.mdi-progress-download::before{content:"\F996"}.mdi-progress-upload::before{content:"\F997"}.mdi-progress-wrench::before{content:"\FC99"}.mdi-projector::before{content:"\F42E"}.mdi-projector-screen::before{content:"\F42F"}.mdi-propane-tank::before{content:"\F0382"}.mdi-propane-tank-outline::before{content:"\F0383"}.mdi-protocol::before{content:"\FFF9"}.mdi-publish::before{content:"\F6A6"}.mdi-pulse::before{content:"\F430"}.mdi-pumpkin::before{content:"\FB9B"}.mdi-purse::before{content:"\FF39"}.mdi-purse-outline::before{content:"\FF3A"}.mdi-puzzle::before{content:"\F431"}.mdi-puzzle-outline::before{content:"\FA65"}.mdi-qi::before{content:"\F998"}.mdi-qqchat::before{content:"\F605"}.mdi-qrcode::before{content:"\F432"}.mdi-qrcode-edit::before{content:"\F8B7"}.mdi-qrcode-minus::before{content:"\F01B7"}.mdi-qrcode-plus::before{content:"\F01B6"}.mdi-qrcode-remove::before{content:"\F01B8"}.mdi-qrcode-scan::before{content:"\F433"}.mdi-quadcopter::before{content:"\F434"}.mdi-quality-high::before{content:"\F435"}.mdi-quality-low::before{content:"\FA0B"}.mdi-quality-medium::before{content:"\FA0C"}.mdi-quicktime::before{content:"\F436"}.mdi-quora::before{content:"\FD05"}.mdi-rabbit::before{content:"\F906"}.mdi-racing-helmet::before{content:"\FD6F"}.mdi-racquetball::before{content:"\FD70"}.mdi-radar::before{content:"\F437"}.mdi-radiator::before{content:"\F438"}.mdi-radiator-disabled::before{content:"\FAD6"}.mdi-radiator-off::before{content:"\FAD7"}.mdi-radio::before{content:"\F439"}.mdi-radio-am::before{content:"\FC9A"}.mdi-radio-fm::before{content:"\FC9B"}.mdi-radio-handheld::before{content:"\F43A"}.mdi-radio-off::before{content:"\F0247"}.mdi-radio-tower::before{content:"\F43B"}.mdi-radioactive::before{content:"\F43C"}.mdi-radioactive-off::before{content:"\FEDE"}.mdi-radiobox-blank::before{content:"\F43D"}.mdi-radiobox-marked::before{content:"\F43E"}.mdi-radius::before{content:"\FC9C"}.mdi-radius-outline::before{content:"\FC9D"}.mdi-railroad-light::before{content:"\FF3B"}.mdi-raspberry-pi::before{content:"\F43F"}.mdi-ray-end::before{content:"\F440"}.mdi-ray-end-arrow::before{content:"\F441"}.mdi-ray-start::before{content:"\F442"}.mdi-ray-start-arrow::before{content:"\F443"}.mdi-ray-start-end::before{content:"\F444"}.mdi-ray-vertex::before{content:"\F445"}.mdi-react::before{content:"\F707"}.mdi-read::before{content:"\F447"}.mdi-receipt::before{content:"\F449"}.mdi-record::before{content:"\F44A"}.mdi-record-circle::before{content:"\FEDF"}.mdi-record-circle-outline::before{content:"\FEE0"}.mdi-record-player::before{content:"\F999"}.mdi-record-rec::before{content:"\F44B"}.mdi-rectangle::before{content:"\FE41"}.mdi-rectangle-outline::before{content:"\FE42"}.mdi-recycle::before{content:"\F44C"}.mdi-reddit::before{content:"\F44D"}.mdi-redhat::before{content:"\F0146"}.mdi-redo::before{content:"\F44E"}.mdi-redo-variant::before{content:"\F44F"}.mdi-reflect-horizontal::before{content:"\FA0D"}.mdi-reflect-vertical::before{content:"\FA0E"}.mdi-refresh::before{content:"\F450"}.mdi-refresh-circle::before{content:"\F03A2"}.mdi-regex::before{content:"\F451"}.mdi-registered-trademark::before{content:"\FA66"}.mdi-relative-scale::before{content:"\F452"}.mdi-reload::before{content:"\F453"}.mdi-reload-alert::before{content:"\F0136"}.mdi-reminder::before{content:"\F88B"}.mdi-remote::before{content:"\F454"}.mdi-remote-desktop::before{content:"\F8B8"}.mdi-remote-off::before{content:"\FEE1"}.mdi-remote-tv::before{content:"\FEE2"}.mdi-remote-tv-off::before{content:"\FEE3"}.mdi-rename-box::before{content:"\F455"}.mdi-reorder-horizontal::before{content:"\F687"}.mdi-reorder-vertical::before{content:"\F688"}.mdi-repeat::before{content:"\F456"}.mdi-repeat-off::before{content:"\F457"}.mdi-repeat-once::before{content:"\F458"}.mdi-replay::before{content:"\F459"}.mdi-reply::before{content:"\F45A"}.mdi-reply-all::before{content:"\F45B"}.mdi-reply-all-outline::before{content:"\FF3C"}.mdi-reply-circle::before{content:"\F01D9"}.mdi-reply-outline::before{content:"\FF3D"}.mdi-reproduction::before{content:"\F45C"}.mdi-resistor::before{content:"\FB1F"}.mdi-resistor-nodes::before{content:"\FB20"}.mdi-resize::before{content:"\FA67"}.mdi-resize-bottom-right::before{content:"\F45D"}.mdi-responsive::before{content:"\F45E"}.mdi-restart::before{content:"\F708"}.mdi-restart-alert::before{content:"\F0137"}.mdi-restart-off::before{content:"\FD71"}.mdi-restore::before{content:"\F99A"}.mdi-restore-alert::before{content:"\F0138"}.mdi-rewind::before{content:"\F45F"}.mdi-rewind-10::before{content:"\FD06"}.mdi-rewind-30::before{content:"\FD72"}.mdi-rewind-5::before{content:"\F0224"}.mdi-rewind-outline::before{content:"\F709"}.mdi-rhombus::before{content:"\F70A"}.mdi-rhombus-medium::before{content:"\FA0F"}.mdi-rhombus-outline::before{content:"\F70B"}.mdi-rhombus-split::before{content:"\FA10"}.mdi-ribbon::before{content:"\F460"}.mdi-rice::before{content:"\F7E9"}.mdi-ring::before{content:"\F7EA"}.mdi-rivet::before{content:"\FE43"}.mdi-road::before{content:"\F461"}.mdi-road-variant::before{content:"\F462"}.mdi-robber::before{content:"\F007A"}.mdi-robot::before{content:"\F6A8"}.mdi-robot-industrial::before{content:"\FB21"}.mdi-robot-mower::before{content:"\F0222"}.mdi-robot-mower-outline::before{content:"\F021E"}.mdi-robot-vacuum::before{content:"\F70C"}.mdi-robot-vacuum-variant::before{content:"\F907"}.mdi-rocket::before{content:"\F463"}.mdi-rodent::before{content:"\F0352"}.mdi-roller-skate::before{content:"\FD07"}.mdi-rollerblade::before{content:"\FD08"}.mdi-rollupjs::before{content:"\FB9C"}.mdi-roman-numeral-1::before{content:"\F00B3"}.mdi-roman-numeral-10::before{content:"\F00BC"}.mdi-roman-numeral-2::before{content:"\F00B4"}.mdi-roman-numeral-3::before{content:"\F00B5"}.mdi-roman-numeral-4::before{content:"\F00B6"}.mdi-roman-numeral-5::before{content:"\F00B7"}.mdi-roman-numeral-6::before{content:"\F00B8"}.mdi-roman-numeral-7::before{content:"\F00B9"}.mdi-roman-numeral-8::before{content:"\F00BA"}.mdi-roman-numeral-9::before{content:"\F00BB"}.mdi-room-service::before{content:"\F88C"}.mdi-room-service-outline::before{content:"\FD73"}.mdi-rotate-3d::before{content:"\FEE4"}.mdi-rotate-3d-variant::before{content:"\F464"}.mdi-rotate-left::before{content:"\F465"}.mdi-rotate-left-variant::before{content:"\F466"}.mdi-rotate-orbit::before{content:"\FD74"}.mdi-rotate-right::before{content:"\F467"}.mdi-rotate-right-variant::before{content:"\F468"}.mdi-rounded-corner::before{content:"\F607"}.mdi-router::before{content:"\F020D"}.mdi-router-wireless::before{content:"\F469"}.mdi-router-wireless-settings::before{content:"\FA68"}.mdi-routes::before{content:"\F46A"}.mdi-routes-clock::before{content:"\F007B"}.mdi-rowing::before{content:"\F608"}.mdi-rss::before{content:"\F46B"}.mdi-rss-box::before{content:"\F46C"}.mdi-rss-off::before{content:"\FF3E"}.mdi-ruby::before{content:"\FD09"}.mdi-rugby::before{content:"\FD75"}.mdi-ruler::before{content:"\F46D"}.mdi-ruler-square::before{content:"\FC9E"}.mdi-ruler-square-compass::before{content:"\FEDB"}.mdi-run::before{content:"\F70D"}.mdi-run-fast::before{content:"\F46E"}.mdi-rv-truck::before{content:"\F01FF"}.mdi-sack::before{content:"\FD0A"}.mdi-sack-percent::before{content:"\FD0B"}.mdi-safe::before{content:"\FA69"}.mdi-safe-square::before{content:"\F02A7"}.mdi-safe-square-outline::before{content:"\F02A8"}.mdi-safety-goggles::before{content:"\FD0C"}.mdi-sailing::before{content:"\FEE5"}.mdi-sale::before{content:"\F46F"}.mdi-salesforce::before{content:"\F88D"}.mdi-sass::before{content:"\F7EB"}.mdi-satellite::before{content:"\F470"}.mdi-satellite-uplink::before{content:"\F908"}.mdi-satellite-variant::before{content:"\F471"}.mdi-sausage::before{content:"\F8B9"}.mdi-saw-blade::before{content:"\FE44"}.mdi-saxophone::before{content:"\F609"}.mdi-scale::before{content:"\F472"}.mdi-scale-balance::before{content:"\F5D1"}.mdi-scale-bathroom::before{content:"\F473"}.mdi-scale-off::before{content:"\F007C"}.mdi-scanner::before{content:"\F6AA"}.mdi-scanner-off::before{content:"\F909"}.mdi-scatter-plot::before{content:"\FEE6"}.mdi-scatter-plot-outline::before{content:"\FEE7"}.mdi-school::before{content:"\F474"}.mdi-school-outline::before{content:"\F01AB"}.mdi-scissors-cutting::before{content:"\FA6A"}.mdi-scooter::before{content:"\F0214"}.mdi-scoreboard::before{content:"\F02A9"}.mdi-scoreboard-outline::before{content:"\F02AA"}.mdi-screen-rotation::before{content:"\F475"}.mdi-screen-rotation-lock::before{content:"\F476"}.mdi-screw-flat-top::before{content:"\FDCF"}.mdi-screw-lag::before{content:"\FE54"}.mdi-screw-machine-flat-top::before{content:"\FE55"}.mdi-screw-machine-round-top::before{content:"\FE56"}.mdi-screw-round-top::before{content:"\FE57"}.mdi-screwdriver::before{content:"\F477"}.mdi-script::before{content:"\FB9D"}.mdi-script-outline::before{content:"\F478"}.mdi-script-text::before{content:"\FB9E"}.mdi-script-text-outline::before{content:"\FB9F"}.mdi-sd::before{content:"\F479"}.mdi-seal::before{content:"\F47A"}.mdi-seal-variant::before{content:"\FFFA"}.mdi-search-web::before{content:"\F70E"}.mdi-seat::before{content:"\FC9F"}.mdi-seat-flat::before{content:"\F47B"}.mdi-seat-flat-angled::before{content:"\F47C"}.mdi-seat-individual-suite::before{content:"\F47D"}.mdi-seat-legroom-extra::before{content:"\F47E"}.mdi-seat-legroom-normal::before{content:"\F47F"}.mdi-seat-legroom-reduced::before{content:"\F480"}.mdi-seat-outline::before{content:"\FCA0"}.mdi-seat-passenger::before{content:"\F0274"}.mdi-seat-recline-extra::before{content:"\F481"}.mdi-seat-recline-normal::before{content:"\F482"}.mdi-seatbelt::before{content:"\FCA1"}.mdi-security::before{content:"\F483"}.mdi-security-network::before{content:"\F484"}.mdi-seed::before{content:"\FE45"}.mdi-seed-outline::before{content:"\FE46"}.mdi-segment::before{content:"\FEE8"}.mdi-select::before{content:"\F485"}.mdi-select-all::before{content:"\F486"}.mdi-select-color::before{content:"\FD0D"}.mdi-select-compare::before{content:"\FAD8"}.mdi-select-drag::before{content:"\FA6B"}.mdi-select-group::before{content:"\FF9F"}.mdi-select-inverse::before{content:"\F487"}.mdi-select-marker::before{content:"\F02AB"}.mdi-select-multiple::before{content:"\F02AC"}.mdi-select-multiple-marker::before{content:"\F02AD"}.mdi-select-off::before{content:"\F488"}.mdi-select-place::before{content:"\FFFB"}.mdi-select-search::before{content:"\F022F"}.mdi-selection::before{content:"\F489"}.mdi-selection-drag::before{content:"\FA6C"}.mdi-selection-ellipse::before{content:"\FD0E"}.mdi-selection-ellipse-arrow-inside::before{content:"\FF3F"}.mdi-selection-marker::before{content:"\F02AE"}.mdi-selection-multiple-marker::before{content:"\F02AF"}.mdi-selection-mutliple::before{content:"\F02B0"}.mdi-selection-off::before{content:"\F776"}.mdi-selection-search::before{content:"\F0230"}.mdi-semantic-web::before{content:"\F0341"}.mdi-send::before{content:"\F48A"}.mdi-send-check::before{content:"\F018C"}.mdi-send-check-outline::before{content:"\F018D"}.mdi-send-circle::before{content:"\FE58"}.mdi-send-circle-outline::before{content:"\FE59"}.mdi-send-clock::before{content:"\F018E"}.mdi-send-clock-outline::before{content:"\F018F"}.mdi-send-lock::before{content:"\F7EC"}.mdi-send-lock-outline::before{content:"\F0191"}.mdi-send-outline::before{content:"\F0190"}.mdi-serial-port::before{content:"\F65C"}.mdi-server::before{content:"\F48B"}.mdi-server-minus::before{content:"\F48C"}.mdi-server-network::before{content:"\F48D"}.mdi-server-network-off::before{content:"\F48E"}.mdi-server-off::before{content:"\F48F"}.mdi-server-plus::before{content:"\F490"}.mdi-server-remove::before{content:"\F491"}.mdi-server-security::before{content:"\F492"}.mdi-set-all::before{content:"\F777"}.mdi-set-center::before{content:"\F778"}.mdi-set-center-right::before{content:"\F779"}.mdi-set-left::before{content:"\F77A"}.mdi-set-left-center::before{content:"\F77B"}.mdi-set-left-right::before{content:"\F77C"}.mdi-set-none::before{content:"\F77D"}.mdi-set-right::before{content:"\F77E"}.mdi-set-top-box::before{content:"\F99E"}.mdi-settings::before{content:"\F493"}.mdi-settings-box::before{content:"\F494"}.mdi-settings-helper::before{content:"\FA6D"}.mdi-settings-outline::before{content:"\F8BA"}.mdi-settings-transfer::before{content:"\F007D"}.mdi-settings-transfer-outline::before{content:"\F007E"}.mdi-shaker::before{content:"\F0139"}.mdi-shaker-outline::before{content:"\F013A"}.mdi-shape::before{content:"\F830"}.mdi-shape-circle-plus::before{content:"\F65D"}.mdi-shape-outline::before{content:"\F831"}.mdi-shape-oval-plus::before{content:"\F0225"}.mdi-shape-plus::before{content:"\F495"}.mdi-shape-polygon-plus::before{content:"\F65E"}.mdi-shape-rectangle-plus::before{content:"\F65F"}.mdi-shape-square-plus::before{content:"\F660"}.mdi-share::before{content:"\F496"}.mdi-share-all::before{content:"\F021F"}.mdi-share-all-outline::before{content:"\F0220"}.mdi-share-circle::before{content:"\F01D8"}.mdi-share-off::before{content:"\FF40"}.mdi-share-off-outline::before{content:"\FF41"}.mdi-share-outline::before{content:"\F931"}.mdi-share-variant::before{content:"\F497"}.mdi-sheep::before{content:"\FCA2"}.mdi-shield::before{content:"\F498"}.mdi-shield-account::before{content:"\F88E"}.mdi-shield-account-outline::before{content:"\FA11"}.mdi-shield-airplane::before{content:"\F6BA"}.mdi-shield-airplane-outline::before{content:"\FCA3"}.mdi-shield-alert::before{content:"\FEE9"}.mdi-shield-alert-outline::before{content:"\FEEA"}.mdi-shield-car::before{content:"\FFA0"}.mdi-shield-check::before{content:"\F565"}.mdi-shield-check-outline::before{content:"\FCA4"}.mdi-shield-cross::before{content:"\FCA5"}.mdi-shield-cross-outline::before{content:"\FCA6"}.mdi-shield-edit::before{content:"\F01CB"}.mdi-shield-edit-outline::before{content:"\F01CC"}.mdi-shield-half::before{content:"\F038B"}.mdi-shield-half-full::before{content:"\F77F"}.mdi-shield-home::before{content:"\F689"}.mdi-shield-home-outline::before{content:"\FCA7"}.mdi-shield-key::before{content:"\FBA0"}.mdi-shield-key-outline::before{content:"\FBA1"}.mdi-shield-link-variant::before{content:"\FD0F"}.mdi-shield-link-variant-outline::before{content:"\FD10"}.mdi-shield-lock::before{content:"\F99C"}.mdi-shield-lock-outline::before{content:"\FCA8"}.mdi-shield-off::before{content:"\F99D"}.mdi-shield-off-outline::before{content:"\F99B"}.mdi-shield-outline::before{content:"\F499"}.mdi-shield-plus::before{content:"\FAD9"}.mdi-shield-plus-outline::before{content:"\FADA"}.mdi-shield-refresh::before{content:"\F01CD"}.mdi-shield-refresh-outline::before{content:"\F01CE"}.mdi-shield-remove::before{content:"\FADB"}.mdi-shield-remove-outline::before{content:"\FADC"}.mdi-shield-search::before{content:"\FD76"}.mdi-shield-star::before{content:"\F0166"}.mdi-shield-star-outline::before{content:"\F0167"}.mdi-shield-sun::before{content:"\F007F"}.mdi-shield-sun-outline::before{content:"\F0080"}.mdi-ship-wheel::before{content:"\F832"}.mdi-shoe-formal::before{content:"\FB22"}.mdi-shoe-heel::before{content:"\FB23"}.mdi-shoe-print::before{content:"\FE5A"}.mdi-shopify::before{content:"\FADD"}.mdi-shopping::before{content:"\F49A"}.mdi-shopping-music::before{content:"\F49B"}.mdi-shopping-outline::before{content:"\F0200"}.mdi-shopping-search::before{content:"\FFA1"}.mdi-shovel::before{content:"\F70F"}.mdi-shovel-off::before{content:"\F710"}.mdi-shower::before{content:"\F99F"}.mdi-shower-head::before{content:"\F9A0"}.mdi-shredder::before{content:"\F49C"}.mdi-shuffle::before{content:"\F49D"}.mdi-shuffle-disabled::before{content:"\F49E"}.mdi-shuffle-variant::before{content:"\F49F"}.mdi-shuriken::before{content:"\F03AA"}.mdi-sigma::before{content:"\F4A0"}.mdi-sigma-lower::before{content:"\F62B"}.mdi-sign-caution::before{content:"\F4A1"}.mdi-sign-direction::before{content:"\F780"}.mdi-sign-direction-minus::before{content:"\F0022"}.mdi-sign-direction-plus::before{content:"\FFFD"}.mdi-sign-direction-remove::before{content:"\FFFE"}.mdi-sign-real-estate::before{content:"\F0143"}.mdi-sign-text::before{content:"\F781"}.mdi-signal::before{content:"\F4A2"}.mdi-signal-2g::before{content:"\F711"}.mdi-signal-3g::before{content:"\F712"}.mdi-signal-4g::before{content:"\F713"}.mdi-signal-5g::before{content:"\FA6E"}.mdi-signal-cellular-1::before{content:"\F8BB"}.mdi-signal-cellular-2::before{content:"\F8BC"}.mdi-signal-cellular-3::before{content:"\F8BD"}.mdi-signal-cellular-outline::before{content:"\F8BE"}.mdi-signal-distance-variant::before{content:"\FE47"}.mdi-signal-hspa::before{content:"\F714"}.mdi-signal-hspa-plus::before{content:"\F715"}.mdi-signal-off::before{content:"\F782"}.mdi-signal-variant::before{content:"\F60A"}.mdi-signature::before{content:"\FE5B"}.mdi-signature-freehand::before{content:"\FE5C"}.mdi-signature-image::before{content:"\FE5D"}.mdi-signature-text::before{content:"\FE5E"}.mdi-silo::before{content:"\FB24"}.mdi-silverware::before{content:"\F4A3"}.mdi-silverware-clean::before{content:"\FFFF"}.mdi-silverware-fork::before{content:"\F4A4"}.mdi-silverware-fork-knife::before{content:"\FA6F"}.mdi-silverware-spoon::before{content:"\F4A5"}.mdi-silverware-variant::before{content:"\F4A6"}.mdi-sim::before{content:"\F4A7"}.mdi-sim-alert::before{content:"\F4A8"}.mdi-sim-off::before{content:"\F4A9"}.mdi-simple-icons::before{content:"\F0348"}.mdi-sina-weibo::before{content:"\FADE"}.mdi-sitemap::before{content:"\F4AA"}.mdi-skate::before{content:"\FD11"}.mdi-skew-less::before{content:"\FD12"}.mdi-skew-more::before{content:"\FD13"}.mdi-ski::before{content:"\F032F"}.mdi-ski-cross-country::before{content:"\F0330"}.mdi-ski-water::before{content:"\F0331"}.mdi-skip-backward::before{content:"\F4AB"}.mdi-skip-backward-outline::before{content:"\FF42"}.mdi-skip-forward::before{content:"\F4AC"}.mdi-skip-forward-outline::before{content:"\FF43"}.mdi-skip-next::before{content:"\F4AD"}.mdi-skip-next-circle::before{content:"\F661"}.mdi-skip-next-circle-outline::before{content:"\F662"}.mdi-skip-next-outline::before{content:"\FF44"}.mdi-skip-previous::before{content:"\F4AE"}.mdi-skip-previous-circle::before{content:"\F663"}.mdi-skip-previous-circle-outline::before{content:"\F664"}.mdi-skip-previous-outline::before{content:"\FF45"}.mdi-skull::before{content:"\F68B"}.mdi-skull-crossbones::before{content:"\FBA2"}.mdi-skull-crossbones-outline::before{content:"\FBA3"}.mdi-skull-outline::before{content:"\FBA4"}.mdi-skype::before{content:"\F4AF"}.mdi-skype-business::before{content:"\F4B0"}.mdi-slack::before{content:"\F4B1"}.mdi-slackware::before{content:"\F90A"}.mdi-slash-forward::before{content:"\F0000"}.mdi-slash-forward-box::before{content:"\F0001"}.mdi-sleep::before{content:"\F4B2"}.mdi-sleep-off::before{content:"\F4B3"}.mdi-slope-downhill::before{content:"\FE5F"}.mdi-slope-uphill::before{content:"\FE60"}.mdi-slot-machine::before{content:"\F013F"}.mdi-slot-machine-outline::before{content:"\F0140"}.mdi-smart-card::before{content:"\F00E8"}.mdi-smart-card-outline::before{content:"\F00E9"}.mdi-smart-card-reader::before{content:"\F00EA"}.mdi-smart-card-reader-outline::before{content:"\F00EB"}.mdi-smog::before{content:"\FA70"}.mdi-smoke-detector::before{content:"\F392"}.mdi-smoking::before{content:"\F4B4"}.mdi-smoking-off::before{content:"\F4B5"}.mdi-snapchat::before{content:"\F4B6"}.mdi-snowboard::before{content:"\F0332"}.mdi-snowflake::before{content:"\F716"}.mdi-snowflake-alert::before{content:"\FF46"}.mdi-snowflake-melt::before{content:"\F02F6"}.mdi-snowflake-variant::before{content:"\FF47"}.mdi-snowman::before{content:"\F4B7"}.mdi-soccer::before{content:"\F4B8"}.mdi-soccer-field::before{content:"\F833"}.mdi-sofa::before{content:"\F4B9"}.mdi-solar-panel::before{content:"\FD77"}.mdi-solar-panel-large::before{content:"\FD78"}.mdi-solar-power::before{content:"\FA71"}.mdi-soldering-iron::before{content:"\F00BD"}.mdi-solid::before{content:"\F68C"}.mdi-sort::before{content:"\F4BA"}.mdi-sort-alphabetical::before{content:"\F4BB"}.mdi-sort-alphabetical-ascending::before{content:"\F0173"}.mdi-sort-alphabetical-descending::before{content:"\F0174"}.mdi-sort-ascending::before{content:"\F4BC"}.mdi-sort-descending::before{content:"\F4BD"}.mdi-sort-numeric::before{content:"\F4BE"}.mdi-sort-variant::before{content:"\F4BF"}.mdi-sort-variant-lock::before{content:"\FCA9"}.mdi-sort-variant-lock-open::before{content:"\FCAA"}.mdi-sort-variant-remove::before{content:"\F0172"}.mdi-soundcloud::before{content:"\F4C0"}.mdi-source-branch::before{content:"\F62C"}.mdi-source-commit::before{content:"\F717"}.mdi-source-commit-end::before{content:"\F718"}.mdi-source-commit-end-local::before{content:"\F719"}.mdi-source-commit-local::before{content:"\F71A"}.mdi-source-commit-next-local::before{content:"\F71B"}.mdi-source-commit-start::before{content:"\F71C"}.mdi-source-commit-start-next-local::before{content:"\F71D"}.mdi-source-fork::before{content:"\F4C1"}.mdi-source-merge::before{content:"\F62D"}.mdi-source-pull::before{content:"\F4C2"}.mdi-source-repository::before{content:"\FCAB"}.mdi-source-repository-multiple::before{content:"\FCAC"}.mdi-soy-sauce::before{content:"\F7ED"}.mdi-spa::before{content:"\FCAD"}.mdi-spa-outline::before{content:"\FCAE"}.mdi-space-invaders::before{content:"\FBA5"}.mdi-space-station::before{content:"\F03AE"}.mdi-spade::before{content:"\FE48"}.mdi-speaker::before{content:"\F4C3"}.mdi-speaker-bluetooth::before{content:"\F9A1"}.mdi-speaker-multiple::before{content:"\FD14"}.mdi-speaker-off::before{content:"\F4C4"}.mdi-speaker-wireless::before{content:"\F71E"}.mdi-speedometer::before{content:"\F4C5"}.mdi-speedometer-medium::before{content:"\FFA2"}.mdi-speedometer-slow::before{content:"\FFA3"}.mdi-spellcheck::before{content:"\F4C6"}.mdi-spider::before{content:"\F0215"}.mdi-spider-thread::before{content:"\F0216"}.mdi-spider-web::before{content:"\FBA6"}.mdi-spotify::before{content:"\F4C7"}.mdi-spotlight::before{content:"\F4C8"}.mdi-spotlight-beam::before{content:"\F4C9"}.mdi-spray::before{content:"\F665"}.mdi-spray-bottle::before{content:"\FADF"}.mdi-sprinkler::before{content:"\F0081"}.mdi-sprinkler-variant::before{content:"\F0082"}.mdi-sprout::before{content:"\FE49"}.mdi-sprout-outline::before{content:"\FE4A"}.mdi-square::before{content:"\F763"}.mdi-square-edit-outline::before{content:"\F90B"}.mdi-square-inc::before{content:"\F4CA"}.mdi-square-inc-cash::before{content:"\F4CB"}.mdi-square-medium::before{content:"\FA12"}.mdi-square-medium-outline::before{content:"\FA13"}.mdi-square-off::before{content:"\F0319"}.mdi-square-off-outline::before{content:"\F031A"}.mdi-square-outline::before{content:"\F762"}.mdi-square-root::before{content:"\F783"}.mdi-square-root-box::before{content:"\F9A2"}.mdi-square-small::before{content:"\FA14"}.mdi-squeegee::before{content:"\FAE0"}.mdi-ssh::before{content:"\F8BF"}.mdi-stack-exchange::before{content:"\F60B"}.mdi-stack-overflow::before{content:"\F4CC"}.mdi-stackpath::before{content:"\F359"}.mdi-stadium::before{content:"\F001A"}.mdi-stadium-variant::before{content:"\F71F"}.mdi-stairs::before{content:"\F4CD"}.mdi-stairs-down::before{content:"\F02E9"}.mdi-stairs-up::before{content:"\F02E8"}.mdi-stamper::before{content:"\FD15"}.mdi-standard-definition::before{content:"\F7EE"}.mdi-star::before{content:"\F4CE"}.mdi-star-box::before{content:"\FA72"}.mdi-star-box-multiple::before{content:"\F02B1"}.mdi-star-box-multiple-outline::before{content:"\F02B2"}.mdi-star-box-outline::before{content:"\FA73"}.mdi-star-circle::before{content:"\F4CF"}.mdi-star-circle-outline::before{content:"\F9A3"}.mdi-star-face::before{content:"\F9A4"}.mdi-star-four-points::before{content:"\FAE1"}.mdi-star-four-points-outline::before{content:"\FAE2"}.mdi-star-half::before{content:"\F4D0"}.mdi-star-off::before{content:"\F4D1"}.mdi-star-outline::before{content:"\F4D2"}.mdi-star-three-points::before{content:"\FAE3"}.mdi-star-three-points-outline::before{content:"\FAE4"}.mdi-state-machine::before{content:"\F021A"}.mdi-steam::before{content:"\F4D3"}.mdi-steam-box::before{content:"\F90C"}.mdi-steering::before{content:"\F4D4"}.mdi-steering-off::before{content:"\F90D"}.mdi-step-backward::before{content:"\F4D5"}.mdi-step-backward-2::before{content:"\F4D6"}.mdi-step-forward::before{content:"\F4D7"}.mdi-step-forward-2::before{content:"\F4D8"}.mdi-stethoscope::before{content:"\F4D9"}.mdi-sticker::before{content:"\F038F"}.mdi-sticker-alert::before{content:"\F0390"}.mdi-sticker-alert-outline::before{content:"\F0391"}.mdi-sticker-check::before{content:"\F0392"}.mdi-sticker-check-outline::before{content:"\F0393"}.mdi-sticker-circle-outline::before{content:"\F5D0"}.mdi-sticker-emoji::before{content:"\F784"}.mdi-sticker-minus::before{content:"\F0394"}.mdi-sticker-minus-outline::before{content:"\F0395"}.mdi-sticker-outline::before{content:"\F0396"}.mdi-sticker-plus::before{content:"\F0397"}.mdi-sticker-plus-outline::before{content:"\F0398"}.mdi-sticker-remove::before{content:"\F0399"}.mdi-sticker-remove-outline::before{content:"\F039A"}.mdi-stocking::before{content:"\F4DA"}.mdi-stomach::before{content:"\F00BE"}.mdi-stop::before{content:"\F4DB"}.mdi-stop-circle::before{content:"\F666"}.mdi-stop-circle-outline::before{content:"\F667"}.mdi-store::before{content:"\F4DC"}.mdi-store-24-hour::before{content:"\F4DD"}.mdi-store-outline::before{content:"\F038C"}.mdi-storefront::before{content:"\F00EC"}.mdi-stove::before{content:"\F4DE"}.mdi-strategy::before{content:"\F0201"}.mdi-strava::before{content:"\FB25"}.mdi-stretch-to-page::before{content:"\FF48"}.mdi-stretch-to-page-outline::before{content:"\FF49"}.mdi-string-lights::before{content:"\F02E5"}.mdi-string-lights-off::before{content:"\F02E6"}.mdi-subdirectory-arrow-left::before{content:"\F60C"}.mdi-subdirectory-arrow-right::before{content:"\F60D"}.mdi-subtitles::before{content:"\FA15"}.mdi-subtitles-outline::before{content:"\FA16"}.mdi-subway::before{content:"\F6AB"}.mdi-subway-alert-variant::before{content:"\FD79"}.mdi-subway-variant::before{content:"\F4DF"}.mdi-summit::before{content:"\F785"}.mdi-sunglasses::before{content:"\F4E0"}.mdi-surround-sound::before{content:"\F5C5"}.mdi-surround-sound-2-0::before{content:"\F7EF"}.mdi-surround-sound-3-1::before{content:"\F7F0"}.mdi-surround-sound-5-1::before{content:"\F7F1"}.mdi-surround-sound-7-1::before{content:"\F7F2"}.mdi-svg::before{content:"\F720"}.mdi-swap-horizontal::before{content:"\F4E1"}.mdi-swap-horizontal-bold::before{content:"\FBA9"}.mdi-swap-horizontal-circle::before{content:"\F0002"}.mdi-swap-horizontal-circle-outline::before{content:"\F0003"}.mdi-swap-horizontal-variant::before{content:"\F8C0"}.mdi-swap-vertical::before{content:"\F4E2"}.mdi-swap-vertical-bold::before{content:"\FBAA"}.mdi-swap-vertical-circle::before{content:"\F0004"}.mdi-swap-vertical-circle-outline::before{content:"\F0005"}.mdi-swap-vertical-variant::before{content:"\F8C1"}.mdi-swim::before{content:"\F4E3"}.mdi-switch::before{content:"\F4E4"}.mdi-sword::before{content:"\F4E5"}.mdi-sword-cross::before{content:"\F786"}.mdi-syllabary-hangul::before{content:"\F035E"}.mdi-syllabary-hiragana::before{content:"\F035F"}.mdi-syllabary-katakana::before{content:"\F0360"}.mdi-syllabary-katakana-half-width::before{content:"\F0361"}.mdi-symfony::before{content:"\FAE5"}.mdi-sync::before{content:"\F4E6"}.mdi-sync-alert::before{content:"\F4E7"}.mdi-sync-circle::before{content:"\F03A3"}.mdi-sync-off::before{content:"\F4E8"}.mdi-tab::before{content:"\F4E9"}.mdi-tab-minus::before{content:"\FB26"}.mdi-tab-plus::before{content:"\F75B"}.mdi-tab-remove::before{content:"\FB27"}.mdi-tab-unselected::before{content:"\F4EA"}.mdi-table::before{content:"\F4EB"}.mdi-table-border::before{content:"\FA17"}.mdi-table-chair::before{content:"\F0083"}.mdi-table-column::before{content:"\F834"}.mdi-table-column-plus-after::before{content:"\F4EC"}.mdi-table-column-plus-before::before{content:"\F4ED"}.mdi-table-column-remove::before{content:"\F4EE"}.mdi-table-column-width::before{content:"\F4EF"}.mdi-table-edit::before{content:"\F4F0"}.mdi-table-eye::before{content:"\F00BF"}.mdi-table-headers-eye::before{content:"\F0248"}.mdi-table-headers-eye-off::before{content:"\F0249"}.mdi-table-large::before{content:"\F4F1"}.mdi-table-large-plus::before{content:"\FFA4"}.mdi-table-large-remove::before{content:"\FFA5"}.mdi-table-merge-cells::before{content:"\F9A5"}.mdi-table-of-contents::before{content:"\F835"}.mdi-table-plus::before{content:"\FA74"}.mdi-table-remove::before{content:"\FA75"}.mdi-table-row::before{content:"\F836"}.mdi-table-row-height::before{content:"\F4F2"}.mdi-table-row-plus-after::before{content:"\F4F3"}.mdi-table-row-plus-before::before{content:"\F4F4"}.mdi-table-row-remove::before{content:"\F4F5"}.mdi-table-search::before{content:"\F90E"}.mdi-table-settings::before{content:"\F837"}.mdi-table-tennis::before{content:"\FE4B"}.mdi-tablet::before{content:"\F4F6"}.mdi-tablet-android::before{content:"\F4F7"}.mdi-tablet-cellphone::before{content:"\F9A6"}.mdi-tablet-dashboard::before{content:"\FEEB"}.mdi-tablet-ipad::before{content:"\F4F8"}.mdi-taco::before{content:"\F761"}.mdi-tag::before{content:"\F4F9"}.mdi-tag-faces::before{content:"\F4FA"}.mdi-tag-heart::before{content:"\F68A"}.mdi-tag-heart-outline::before{content:"\FBAB"}.mdi-tag-minus::before{content:"\F90F"}.mdi-tag-minus-outline::before{content:"\F024A"}.mdi-tag-multiple::before{content:"\F4FB"}.mdi-tag-multiple-outline::before{content:"\F0322"}.mdi-tag-off::before{content:"\F024B"}.mdi-tag-off-outline::before{content:"\F024C"}.mdi-tag-outline::before{content:"\F4FC"}.mdi-tag-plus::before{content:"\F721"}.mdi-tag-plus-outline::before{content:"\F024D"}.mdi-tag-remove::before{content:"\F722"}.mdi-tag-remove-outline::before{content:"\F024E"}.mdi-tag-text::before{content:"\F024F"}.mdi-tag-text-outline::before{content:"\F4FD"}.mdi-tank::before{content:"\FD16"}.mdi-tanker-truck::before{content:"\F0006"}.mdi-tape-measure::before{content:"\FB28"}.mdi-target::before{content:"\F4FE"}.mdi-target-account::before{content:"\FBAC"}.mdi-target-variant::before{content:"\FA76"}.mdi-taxi::before{content:"\F4FF"}.mdi-tea::before{content:"\FD7A"}.mdi-tea-outline::before{content:"\FD7B"}.mdi-teach::before{content:"\F88F"}.mdi-teamviewer::before{content:"\F500"}.mdi-telegram::before{content:"\F501"}.mdi-telescope::before{content:"\FB29"}.mdi-television::before{content:"\F502"}.mdi-television-ambient-light::before{content:"\F0381"}.mdi-television-box::before{content:"\F838"}.mdi-television-classic::before{content:"\F7F3"}.mdi-television-classic-off::before{content:"\F839"}.mdi-television-clean::before{content:"\F013B"}.mdi-television-guide::before{content:"\F503"}.mdi-television-off::before{content:"\F83A"}.mdi-television-pause::before{content:"\FFA6"}.mdi-television-play::before{content:"\FEEC"}.mdi-television-stop::before{content:"\FFA7"}.mdi-temperature-celsius::before{content:"\F504"}.mdi-temperature-fahrenheit::before{content:"\F505"}.mdi-temperature-kelvin::before{content:"\F506"}.mdi-tennis::before{content:"\FD7C"}.mdi-tennis-ball::before{content:"\F507"}.mdi-tent::before{content:"\F508"}.mdi-terraform::before{content:"\F0084"}.mdi-terrain::before{content:"\F509"}.mdi-test-tube::before{content:"\F668"}.mdi-test-tube-empty::before{content:"\F910"}.mdi-test-tube-off::before{content:"\F911"}.mdi-text::before{content:"\F9A7"}.mdi-text-recognition::before{content:"\F0168"}.mdi-text-shadow::before{content:"\F669"}.mdi-text-short::before{content:"\F9A8"}.mdi-text-subject::before{content:"\F9A9"}.mdi-text-to-speech::before{content:"\F50A"}.mdi-text-to-speech-off::before{content:"\F50B"}.mdi-textarea::before{content:"\F00C0"}.mdi-textbox::before{content:"\F60E"}.mdi-textbox-lock::before{content:"\F0388"}.mdi-textbox-password::before{content:"\F7F4"}.mdi-texture::before{content:"\F50C"}.mdi-texture-box::before{content:"\F0007"}.mdi-theater::before{content:"\F50D"}.mdi-theme-light-dark::before{content:"\F50E"}.mdi-thermometer::before{content:"\F50F"}.mdi-thermometer-alert::before{content:"\FE61"}.mdi-thermometer-chevron-down::before{content:"\FE62"}.mdi-thermometer-chevron-up::before{content:"\FE63"}.mdi-thermometer-high::before{content:"\F00ED"}.mdi-thermometer-lines::before{content:"\F510"}.mdi-thermometer-low::before{content:"\F00EE"}.mdi-thermometer-minus::before{content:"\FE64"}.mdi-thermometer-plus::before{content:"\FE65"}.mdi-thermostat::before{content:"\F393"}.mdi-thermostat-box::before{content:"\F890"}.mdi-thought-bubble::before{content:"\F7F5"}.mdi-thought-bubble-outline::before{content:"\F7F6"}.mdi-thumb-down::before{content:"\F511"}.mdi-thumb-down-outline::before{content:"\F512"}.mdi-thumb-up::before{content:"\F513"}.mdi-thumb-up-outline::before{content:"\F514"}.mdi-thumbs-up-down::before{content:"\F515"}.mdi-ticket::before{content:"\F516"}.mdi-ticket-account::before{content:"\F517"}.mdi-ticket-confirmation::before{content:"\F518"}.mdi-ticket-outline::before{content:"\F912"}.mdi-ticket-percent::before{content:"\F723"}.mdi-tie::before{content:"\F519"}.mdi-tilde::before{content:"\F724"}.mdi-timelapse::before{content:"\F51A"}.mdi-timeline::before{content:"\FBAD"}.mdi-timeline-alert::before{content:"\FFB2"}.mdi-timeline-alert-outline::before{content:"\FFB5"}.mdi-timeline-clock::before{content:"\F0226"}.mdi-timeline-clock-outline::before{content:"\F0227"}.mdi-timeline-help::before{content:"\FFB6"}.mdi-timeline-help-outline::before{content:"\FFB7"}.mdi-timeline-outline::before{content:"\FBAE"}.mdi-timeline-plus::before{content:"\FFB3"}.mdi-timeline-plus-outline::before{content:"\FFB4"}.mdi-timeline-text::before{content:"\FBAF"}.mdi-timeline-text-outline::before{content:"\FBB0"}.mdi-timer::before{content:"\F51B"}.mdi-timer-10::before{content:"\F51C"}.mdi-timer-3::before{content:"\F51D"}.mdi-timer-off::before{content:"\F51E"}.mdi-timer-sand::before{content:"\F51F"}.mdi-timer-sand-empty::before{content:"\F6AC"}.mdi-timer-sand-full::before{content:"\F78B"}.mdi-timetable::before{content:"\F520"}.mdi-toaster::before{content:"\F0085"}.mdi-toaster-off::before{content:"\F01E2"}.mdi-toaster-oven::before{content:"\FCAF"}.mdi-toggle-switch::before{content:"\F521"}.mdi-toggle-switch-off::before{content:"\F522"}.mdi-toggle-switch-off-outline::before{content:"\FA18"}.mdi-toggle-switch-outline::before{content:"\FA19"}.mdi-toilet::before{content:"\F9AA"}.mdi-toolbox::before{content:"\F9AB"}.mdi-toolbox-outline::before{content:"\F9AC"}.mdi-tools::before{content:"\F0086"}.mdi-tooltip::before{content:"\F523"}.mdi-tooltip-account::before{content:"\F00C"}.mdi-tooltip-edit::before{content:"\F524"}.mdi-tooltip-edit-outline::before{content:"\F02F0"}.mdi-tooltip-image::before{content:"\F525"}.mdi-tooltip-image-outline::before{content:"\FBB1"}.mdi-tooltip-outline::before{content:"\F526"}.mdi-tooltip-plus::before{content:"\FBB2"}.mdi-tooltip-plus-outline::before{content:"\F527"}.mdi-tooltip-text::before{content:"\F528"}.mdi-tooltip-text-outline::before{content:"\FBB3"}.mdi-tooth::before{content:"\F8C2"}.mdi-tooth-outline::before{content:"\F529"}.mdi-toothbrush::before{content:"\F0154"}.mdi-toothbrush-electric::before{content:"\F0157"}.mdi-toothbrush-paste::before{content:"\F0155"}.mdi-tor::before{content:"\F52A"}.mdi-tortoise::before{content:"\FD17"}.mdi-toslink::before{content:"\F02E3"}.mdi-tournament::before{content:"\F9AD"}.mdi-tower-beach::before{content:"\F680"}.mdi-tower-fire::before{content:"\F681"}.mdi-towing::before{content:"\F83B"}.mdi-toy-brick::before{content:"\F02B3"}.mdi-toy-brick-marker::before{content:"\F02B4"}.mdi-toy-brick-marker-outline::before{content:"\F02B5"}.mdi-toy-brick-minus::before{content:"\F02B6"}.mdi-toy-brick-minus-outline::before{content:"\F02B7"}.mdi-toy-brick-outline::before{content:"\F02B8"}.mdi-toy-brick-plus::before{content:"\F02B9"}.mdi-toy-brick-plus-outline::before{content:"\F02BA"}.mdi-toy-brick-remove::before{content:"\F02BB"}.mdi-toy-brick-remove-outline::before{content:"\F02BC"}.mdi-toy-brick-search::before{content:"\F02BD"}.mdi-toy-brick-search-outline::before{content:"\F02BE"}.mdi-track-light::before{content:"\F913"}.mdi-trackpad::before{content:"\F7F7"}.mdi-trackpad-lock::before{content:"\F932"}.mdi-tractor::before{content:"\F891"}.mdi-trademark::before{content:"\FA77"}.mdi-traffic-cone::before{content:"\F03A7"}.mdi-traffic-light::before{content:"\F52B"}.mdi-train::before{content:"\F52C"}.mdi-train-car::before{content:"\FBB4"}.mdi-train-variant::before{content:"\F8C3"}.mdi-tram::before{content:"\F52D"}.mdi-tram-side::before{content:"\F0008"}.mdi-transcribe::before{content:"\F52E"}.mdi-transcribe-close::before{content:"\F52F"}.mdi-transfer::before{content:"\F0087"}.mdi-transfer-down::before{content:"\FD7D"}.mdi-transfer-left::before{content:"\FD7E"}.mdi-transfer-right::before{content:"\F530"}.mdi-transfer-up::before{content:"\FD7F"}.mdi-transit-connection::before{content:"\FD18"}.mdi-transit-connection-variant::before{content:"\FD19"}.mdi-transit-detour::before{content:"\FFA8"}.mdi-transit-transfer::before{content:"\F6AD"}.mdi-transition::before{content:"\F914"}.mdi-transition-masked::before{content:"\F915"}.mdi-translate::before{content:"\F5CA"}.mdi-translate-off::before{content:"\FE66"}.mdi-transmission-tower::before{content:"\FD1A"}.mdi-trash-can::before{content:"\FA78"}.mdi-trash-can-outline::before{content:"\FA79"}.mdi-tray::before{content:"\F02BF"}.mdi-tray-alert::before{content:"\F02C0"}.mdi-tray-full::before{content:"\F02C1"}.mdi-tray-minus::before{content:"\F02C2"}.mdi-tray-plus::before{content:"\F02C3"}.mdi-tray-remove::before{content:"\F02C4"}.mdi-treasure-chest::before{content:"\F725"}.mdi-tree::before{content:"\F531"}.mdi-tree-outline::before{content:"\FE4C"}.mdi-trello::before{content:"\F532"}.mdi-trending-down::before{content:"\F533"}.mdi-trending-neutral::before{content:"\F534"}.mdi-trending-up::before{content:"\F535"}.mdi-triangle::before{content:"\F536"}.mdi-triangle-outline::before{content:"\F537"}.mdi-triforce::before{content:"\FBB5"}.mdi-trophy::before{content:"\F538"}.mdi-trophy-award::before{content:"\F539"}.mdi-trophy-broken::before{content:"\FD80"}.mdi-trophy-outline::before{content:"\F53A"}.mdi-trophy-variant::before{content:"\F53B"}.mdi-trophy-variant-outline::before{content:"\F53C"}.mdi-truck::before{content:"\F53D"}.mdi-truck-check::before{content:"\FCB0"}.mdi-truck-check-outline::before{content:"\F02C5"}.mdi-truck-delivery::before{content:"\F53E"}.mdi-truck-delivery-outline::before{content:"\F02C6"}.mdi-truck-fast::before{content:"\F787"}.mdi-truck-fast-outline::before{content:"\F02C7"}.mdi-truck-outline::before{content:"\F02C8"}.mdi-truck-trailer::before{content:"\F726"}.mdi-trumpet::before{content:"\F00C1"}.mdi-tshirt-crew::before{content:"\FA7A"}.mdi-tshirt-crew-outline::before{content:"\F53F"}.mdi-tshirt-v::before{content:"\FA7B"}.mdi-tshirt-v-outline::before{content:"\F540"}.mdi-tumble-dryer::before{content:"\F916"}.mdi-tumble-dryer-alert::before{content:"\F01E5"}.mdi-tumble-dryer-off::before{content:"\F01E6"}.mdi-tumblr::before{content:"\F541"}.mdi-tumblr-box::before{content:"\F917"}.mdi-tumblr-reblog::before{content:"\F542"}.mdi-tune::before{content:"\F62E"}.mdi-tune-vertical::before{content:"\F66A"}.mdi-turnstile::before{content:"\FCB1"}.mdi-turnstile-outline::before{content:"\FCB2"}.mdi-turtle::before{content:"\FCB3"}.mdi-twitch::before{content:"\F543"}.mdi-twitter::before{content:"\F544"}.mdi-twitter-box::before{content:"\F545"}.mdi-twitter-circle::before{content:"\F546"}.mdi-twitter-retweet::before{content:"\F547"}.mdi-two-factor-authentication::before{content:"\F9AE"}.mdi-typewriter::before{content:"\FF4A"}.mdi-uber::before{content:"\F748"}.mdi-ubisoft::before{content:"\FBB6"}.mdi-ubuntu::before{content:"\F548"}.mdi-ufo::before{content:"\F00EF"}.mdi-ufo-outline::before{content:"\F00F0"}.mdi-ultra-high-definition::before{content:"\F7F8"}.mdi-umbraco::before{content:"\F549"}.mdi-umbrella::before{content:"\F54A"}.mdi-umbrella-closed::before{content:"\F9AF"}.mdi-umbrella-outline::before{content:"\F54B"}.mdi-undo::before{content:"\F54C"}.mdi-undo-variant::before{content:"\F54D"}.mdi-unfold-less-horizontal::before{content:"\F54E"}.mdi-unfold-less-vertical::before{content:"\F75F"}.mdi-unfold-more-horizontal::before{content:"\F54F"}.mdi-unfold-more-vertical::before{content:"\F760"}.mdi-ungroup::before{content:"\F550"}.mdi-unicode::before{content:"\FEED"}.mdi-unity::before{content:"\F6AE"}.mdi-unreal::before{content:"\F9B0"}.mdi-untappd::before{content:"\F551"}.mdi-update::before{content:"\F6AF"}.mdi-upload::before{content:"\F552"}.mdi-upload-lock::before{content:"\F039E"}.mdi-upload-lock-outline::before{content:"\F039F"}.mdi-upload-multiple::before{content:"\F83C"}.mdi-upload-network::before{content:"\F6F5"}.mdi-upload-network-outline::before{content:"\FCB4"}.mdi-upload-off::before{content:"\F00F1"}.mdi-upload-off-outline::before{content:"\F00F2"}.mdi-upload-outline::before{content:"\FE67"}.mdi-usb::before{content:"\F553"}.mdi-usb-flash-drive::before{content:"\F02C9"}.mdi-usb-flash-drive-outline::before{content:"\F02CA"}.mdi-usb-port::before{content:"\F021B"}.mdi-valve::before{content:"\F0088"}.mdi-valve-closed::before{content:"\F0089"}.mdi-valve-open::before{content:"\F008A"}.mdi-van-passenger::before{content:"\F7F9"}.mdi-van-utility::before{content:"\F7FA"}.mdi-vanish::before{content:"\F7FB"}.mdi-vanity-light::before{content:"\F020C"}.mdi-variable::before{content:"\FAE6"}.mdi-variable-box::before{content:"\F013C"}.mdi-vector-arrange-above::before{content:"\F554"}.mdi-vector-arrange-below::before{content:"\F555"}.mdi-vector-bezier::before{content:"\FAE7"}.mdi-vector-circle::before{content:"\F556"}.mdi-vector-circle-variant::before{content:"\F557"}.mdi-vector-combine::before{content:"\F558"}.mdi-vector-curve::before{content:"\F559"}.mdi-vector-difference::before{content:"\F55A"}.mdi-vector-difference-ab::before{content:"\F55B"}.mdi-vector-difference-ba::before{content:"\F55C"}.mdi-vector-ellipse::before{content:"\F892"}.mdi-vector-intersection::before{content:"\F55D"}.mdi-vector-line::before{content:"\F55E"}.mdi-vector-link::before{content:"\F0009"}.mdi-vector-point::before{content:"\F55F"}.mdi-vector-polygon::before{content:"\F560"}.mdi-vector-polyline::before{content:"\F561"}.mdi-vector-polyline-edit::before{content:"\F0250"}.mdi-vector-polyline-minus::before{content:"\F0251"}.mdi-vector-polyline-plus::before{content:"\F0252"}.mdi-vector-polyline-remove::before{content:"\F0253"}.mdi-vector-radius::before{content:"\F749"}.mdi-vector-rectangle::before{content:"\F5C6"}.mdi-vector-selection::before{content:"\F562"}.mdi-vector-square::before{content:"\F001"}.mdi-vector-triangle::before{content:"\F563"}.mdi-vector-union::before{content:"\F564"}.mdi-venmo::before{content:"\F578"}.mdi-vhs::before{content:"\FA1A"}.mdi-vibrate::before{content:"\F566"}.mdi-vibrate-off::before{content:"\FCB5"}.mdi-video::before{content:"\F567"}.mdi-video-3d::before{content:"\F7FC"}.mdi-video-3d-variant::before{content:"\FEEE"}.mdi-video-4k-box::before{content:"\F83D"}.mdi-video-account::before{content:"\F918"}.mdi-video-check::before{content:"\F008B"}.mdi-video-check-outline::before{content:"\F008C"}.mdi-video-image::before{content:"\F919"}.mdi-video-input-antenna::before{content:"\F83E"}.mdi-video-input-component::before{content:"\F83F"}.mdi-video-input-hdmi::before{content:"\F840"}.mdi-video-input-scart::before{content:"\FFA9"}.mdi-video-input-svideo::before{content:"\F841"}.mdi-video-minus::before{content:"\F9B1"}.mdi-video-off::before{content:"\F568"}.mdi-video-off-outline::before{content:"\FBB7"}.mdi-video-outline::before{content:"\FBB8"}.mdi-video-plus::before{content:"\F9B2"}.mdi-video-stabilization::before{content:"\F91A"}.mdi-video-switch::before{content:"\F569"}.mdi-video-vintage::before{content:"\FA1B"}.mdi-video-wireless::before{content:"\FEEF"}.mdi-video-wireless-outline::before{content:"\FEF0"}.mdi-view-agenda::before{content:"\F56A"}.mdi-view-agenda-outline::before{content:"\F0203"}.mdi-view-array::before{content:"\F56B"}.mdi-view-carousel::before{content:"\F56C"}.mdi-view-column::before{content:"\F56D"}.mdi-view-comfy::before{content:"\FE4D"}.mdi-view-compact::before{content:"\FE4E"}.mdi-view-compact-outline::before{content:"\FE4F"}.mdi-view-dashboard::before{content:"\F56E"}.mdi-view-dashboard-outline::before{content:"\FA1C"}.mdi-view-dashboard-variant::before{content:"\F842"}.mdi-view-day::before{content:"\F56F"}.mdi-view-grid::before{content:"\F570"}.mdi-view-grid-outline::before{content:"\F0204"}.mdi-view-grid-plus::before{content:"\FFAA"}.mdi-view-grid-plus-outline::before{content:"\F0205"}.mdi-view-headline::before{content:"\F571"}.mdi-view-list::before{content:"\F572"}.mdi-view-module::before{content:"\F573"}.mdi-view-parallel::before{content:"\F727"}.mdi-view-quilt::before{content:"\F574"}.mdi-view-sequential::before{content:"\F728"}.mdi-view-split-horizontal::before{content:"\FBA7"}.mdi-view-split-vertical::before{content:"\FBA8"}.mdi-view-stream::before{content:"\F575"}.mdi-view-week::before{content:"\F576"}.mdi-vimeo::before{content:"\F577"}.mdi-violin::before{content:"\F60F"}.mdi-virtual-reality::before{content:"\F893"}.mdi-visual-studio::before{content:"\F610"}.mdi-visual-studio-code::before{content:"\FA1D"}.mdi-vk::before{content:"\F579"}.mdi-vk-box::before{content:"\F57A"}.mdi-vk-circle::before{content:"\F57B"}.mdi-vlc::before{content:"\F57C"}.mdi-voice::before{content:"\F5CB"}.mdi-voice-off::before{content:"\FEF1"}.mdi-voicemail::before{content:"\F57D"}.mdi-volleyball::before{content:"\F9B3"}.mdi-volume-high::before{content:"\F57E"}.mdi-volume-low::before{content:"\F57F"}.mdi-volume-medium::before{content:"\F580"}.mdi-volume-minus::before{content:"\F75D"}.mdi-volume-mute::before{content:"\F75E"}.mdi-volume-off::before{content:"\F581"}.mdi-volume-plus::before{content:"\F75C"}.mdi-volume-source::before{content:"\F014B"}.mdi-volume-variant-off::before{content:"\FE68"}.mdi-volume-vibrate::before{content:"\F014C"}.mdi-vote::before{content:"\FA1E"}.mdi-vote-outline::before{content:"\FA1F"}.mdi-vpn::before{content:"\F582"}.mdi-vuejs::before{content:"\F843"}.mdi-vuetify::before{content:"\FE50"}.mdi-walk::before{content:"\F583"}.mdi-wall::before{content:"\F7FD"}.mdi-wall-sconce::before{content:"\F91B"}.mdi-wall-sconce-flat::before{content:"\F91C"}.mdi-wall-sconce-variant::before{content:"\F91D"}.mdi-wallet::before{content:"\F584"}.mdi-wallet-giftcard::before{content:"\F585"}.mdi-wallet-membership::before{content:"\F586"}.mdi-wallet-outline::before{content:"\FBB9"}.mdi-wallet-plus::before{content:"\FFAB"}.mdi-wallet-plus-outline::before{content:"\FFAC"}.mdi-wallet-travel::before{content:"\F587"}.mdi-wallpaper::before{content:"\FE69"}.mdi-wan::before{content:"\F588"}.mdi-wardrobe::before{content:"\FFAD"}.mdi-wardrobe-outline::before{content:"\FFAE"}.mdi-warehouse::before{content:"\FFBB"}.mdi-washing-machine::before{content:"\F729"}.mdi-washing-machine-alert::before{content:"\F01E7"}.mdi-washing-machine-off::before{content:"\F01E8"}.mdi-watch::before{content:"\F589"}.mdi-watch-export::before{content:"\F58A"}.mdi-watch-export-variant::before{content:"\F894"}.mdi-watch-import::before{content:"\F58B"}.mdi-watch-import-variant::before{content:"\F895"}.mdi-watch-variant::before{content:"\F896"}.mdi-watch-vibrate::before{content:"\F6B0"}.mdi-watch-vibrate-off::before{content:"\FCB6"}.mdi-water::before{content:"\F58C"}.mdi-water-boiler::before{content:"\FFAF"}.mdi-water-boiler-alert::before{content:"\F01DE"}.mdi-water-boiler-off::before{content:"\F01DF"}.mdi-water-off::before{content:"\F58D"}.mdi-water-outline::before{content:"\FE6A"}.mdi-water-percent::before{content:"\F58E"}.mdi-water-polo::before{content:"\F02CB"}.mdi-water-pump::before{content:"\F58F"}.mdi-water-pump-off::before{content:"\FFB0"}.mdi-water-well::before{content:"\F008D"}.mdi-water-well-outline::before{content:"\F008E"}.mdi-watermark::before{content:"\F612"}.mdi-wave::before{content:"\FF4B"}.mdi-waves::before{content:"\F78C"}.mdi-waze::before{content:"\FBBA"}.mdi-weather-cloudy::before{content:"\F590"}.mdi-weather-cloudy-alert::before{content:"\FF4C"}.mdi-weather-cloudy-arrow-right::before{content:"\FE51"}.mdi-weather-fog::before{content:"\F591"}.mdi-weather-hail::before{content:"\F592"}.mdi-weather-hazy::before{content:"\FF4D"}.mdi-weather-hurricane::before{content:"\F897"}.mdi-weather-lightning::before{content:"\F593"}.mdi-weather-lightning-rainy::before{content:"\F67D"}.mdi-weather-night::before{content:"\F594"}.mdi-weather-night-partly-cloudy::before{content:"\FF4E"}.mdi-weather-partly-cloudy::before{content:"\F595"}.mdi-weather-partly-lightning::before{content:"\FF4F"}.mdi-weather-partly-rainy::before{content:"\FF50"}.mdi-weather-partly-snowy::before{content:"\FF51"}.mdi-weather-partly-snowy-rainy::before{content:"\FF52"}.mdi-weather-pouring::before{content:"\F596"}.mdi-weather-rainy::before{content:"\F597"}.mdi-weather-snowy::before{content:"\F598"}.mdi-weather-snowy-heavy::before{content:"\FF53"}.mdi-weather-snowy-rainy::before{content:"\F67E"}.mdi-weather-sunny::before{content:"\F599"}.mdi-weather-sunny-alert::before{content:"\FF54"}.mdi-weather-sunset::before{content:"\F59A"}.mdi-weather-sunset-down::before{content:"\F59B"}.mdi-weather-sunset-up::before{content:"\F59C"}.mdi-weather-tornado::before{content:"\FF55"}.mdi-weather-windy::before{content:"\F59D"}.mdi-weather-windy-variant::before{content:"\F59E"}.mdi-web::before{content:"\F59F"}.mdi-web-box::before{content:"\FFB1"}.mdi-web-clock::before{content:"\F0275"}.mdi-webcam::before{content:"\F5A0"}.mdi-webhook::before{content:"\F62F"}.mdi-webpack::before{content:"\F72A"}.mdi-webrtc::before{content:"\F0273"}.mdi-wechat::before{content:"\F611"}.mdi-weight::before{content:"\F5A1"}.mdi-weight-gram::before{content:"\FD1B"}.mdi-weight-kilogram::before{content:"\F5A2"}.mdi-weight-lifter::before{content:"\F0188"}.mdi-weight-pound::before{content:"\F9B4"}.mdi-whatsapp::before{content:"\F5A3"}.mdi-wheelchair-accessibility::before{content:"\F5A4"}.mdi-whistle::before{content:"\F9B5"}.mdi-whistle-outline::before{content:"\F02E7"}.mdi-white-balance-auto::before{content:"\F5A5"}.mdi-white-balance-incandescent::before{content:"\F5A6"}.mdi-white-balance-iridescent::before{content:"\F5A7"}.mdi-white-balance-sunny::before{content:"\F5A8"}.mdi-widgets::before{content:"\F72B"}.mdi-widgets-outline::before{content:"\F0380"}.mdi-wifi::before{content:"\F5A9"}.mdi-wifi-off::before{content:"\F5AA"}.mdi-wifi-star::before{content:"\FE6B"}.mdi-wifi-strength-1::before{content:"\F91E"}.mdi-wifi-strength-1-alert::before{content:"\F91F"}.mdi-wifi-strength-1-lock::before{content:"\F920"}.mdi-wifi-strength-2::before{content:"\F921"}.mdi-wifi-strength-2-alert::before{content:"\F922"}.mdi-wifi-strength-2-lock::before{content:"\F923"}.mdi-wifi-strength-3::before{content:"\F924"}.mdi-wifi-strength-3-alert::before{content:"\F925"}.mdi-wifi-strength-3-lock::before{content:"\F926"}.mdi-wifi-strength-4::before{content:"\F927"}.mdi-wifi-strength-4-alert::before{content:"\F928"}.mdi-wifi-strength-4-lock::before{content:"\F929"}.mdi-wifi-strength-alert-outline::before{content:"\F92A"}.mdi-wifi-strength-lock-outline::before{content:"\F92B"}.mdi-wifi-strength-off::before{content:"\F92C"}.mdi-wifi-strength-off-outline::before{content:"\F92D"}.mdi-wifi-strength-outline::before{content:"\F92E"}.mdi-wii::before{content:"\F5AB"}.mdi-wiiu::before{content:"\F72C"}.mdi-wikipedia::before{content:"\F5AC"}.mdi-wind-turbine::before{content:"\FD81"}.mdi-window-close::before{content:"\F5AD"}.mdi-window-closed::before{content:"\F5AE"}.mdi-window-closed-variant::before{content:"\F0206"}.mdi-window-maximize::before{content:"\F5AF"}.mdi-window-minimize::before{content:"\F5B0"}.mdi-window-open::before{content:"\F5B1"}.mdi-window-open-variant::before{content:"\F0207"}.mdi-window-restore::before{content:"\F5B2"}.mdi-window-shutter::before{content:"\F0147"}.mdi-window-shutter-alert::before{content:"\F0148"}.mdi-window-shutter-open::before{content:"\F0149"}.mdi-windows::before{content:"\F5B3"}.mdi-windows-classic::before{content:"\FA20"}.mdi-wiper::before{content:"\FAE8"}.mdi-wiper-wash::before{content:"\FD82"}.mdi-wordpress::before{content:"\F5B4"}.mdi-worker::before{content:"\F5B5"}.mdi-wrap::before{content:"\F5B6"}.mdi-wrap-disabled::before{content:"\FBBB"}.mdi-wrench::before{content:"\F5B7"}.mdi-wrench-outline::before{content:"\FBBC"}.mdi-wunderlist::before{content:"\F5B8"}.mdi-xamarin::before{content:"\F844"}.mdi-xamarin-outline::before{content:"\F845"}.mdi-xaml::before{content:"\F673"}.mdi-xbox::before{content:"\F5B9"}.mdi-xbox-controller::before{content:"\F5BA"}.mdi-xbox-controller-battery-alert::before{content:"\F74A"}.mdi-xbox-controller-battery-charging::before{content:"\FA21"}.mdi-xbox-controller-battery-empty::before{content:"\F74B"}.mdi-xbox-controller-battery-full::before{content:"\F74C"}.mdi-xbox-controller-battery-low::before{content:"\F74D"}.mdi-xbox-controller-battery-medium::before{content:"\F74E"}.mdi-xbox-controller-battery-unknown::before{content:"\F74F"}.mdi-xbox-controller-menu::before{content:"\FE52"}.mdi-xbox-controller-off::before{content:"\F5BB"}.mdi-xbox-controller-view::before{content:"\FE53"}.mdi-xda::before{content:"\F5BC"}.mdi-xing::before{content:"\F5BD"}.mdi-xing-box::before{content:"\F5BE"}.mdi-xing-circle::before{content:"\F5BF"}.mdi-xml::before{content:"\F5C0"}.mdi-xmpp::before{content:"\F7FE"}.mdi-yahoo::before{content:"\FB2A"}.mdi-yammer::before{content:"\F788"}.mdi-yeast::before{content:"\F5C1"}.mdi-yelp::before{content:"\F5C2"}.mdi-yin-yang::before{content:"\F67F"}.mdi-yoga::before{content:"\F01A7"}.mdi-youtube::before{content:"\F5C3"}.mdi-youtube-creator-studio::before{content:"\F846"}.mdi-youtube-gaming::before{content:"\F847"}.mdi-youtube-subscription::before{content:"\FD1C"}.mdi-youtube-tv::before{content:"\F448"}.mdi-z-wave::before{content:"\FAE9"}.mdi-zend::before{content:"\FAEA"}.mdi-zigbee::before{content:"\FD1D"}.mdi-zip-box::before{content:"\F5C4"}.mdi-zip-box-outline::before{content:"\F001B"}.mdi-zip-disk::before{content:"\FA22"}.mdi-zodiac-aquarius::before{content:"\FA7C"}.mdi-zodiac-aries::before{content:"\FA7D"}.mdi-zodiac-cancer::before{content:"\FA7E"}.mdi-zodiac-capricorn::before{content:"\FA7F"}.mdi-zodiac-gemini::before{content:"\FA80"}.mdi-zodiac-leo::before{content:"\FA81"}.mdi-zodiac-libra::before{content:"\FA82"}.mdi-zodiac-pisces::before{content:"\FA83"}.mdi-zodiac-sagittarius::before{content:"\FA84"}.mdi-zodiac-scorpio::before{content:"\FA85"}.mdi-zodiac-taurus::before{content:"\FA86"}.mdi-zodiac-virgo::before{content:"\FA87"}.mdi-blank::before{content:"\F68C";visibility:hidden}.mdi-18px.mdi-set,.mdi-18px.mdi:before{font-size:18px}.mdi-24px.mdi-set,.mdi-24px.mdi:before{font-size:24px}.mdi-36px.mdi-set,.mdi-36px.mdi:before{font-size:36px}.mdi-48px.mdi-set,.mdi-48px.mdi:before{font-size:48px}.mdi-dark:before{color:rgba(0,0,0,0.54)}.mdi-dark.mdi-inactive:before{color:rgba(0,0,0,0.26)}.mdi-light:before{color:#fff}.mdi-light.mdi-inactive:before{color:rgba(255,255,255,0.3)}.mdi-rotate-45:before{-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.mdi-rotate-90:before{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.mdi-rotate-135:before{-webkit-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg)}.mdi-rotate-180:before{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.mdi-rotate-225:before{-webkit-transform:rotate(225deg);-ms-transform:rotate(225deg);transform:rotate(225deg)}.mdi-rotate-270:before{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.mdi-rotate-315:before{-webkit-transform:rotate(315deg);-ms-transform:rotate(315deg);transform:rotate(315deg)}.mdi-flip-h:before{-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.mdi-flip-v:before{-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.mdi-spin:before{-webkit-animation:mdi-spin 2s infinite linear;animation:mdi-spin 2s infinite linear}@-webkit-keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes mdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}} + +/*# sourceMappingURL=materialdesignicons.css.map */ diff --git a/packages/merchant-backoffice-ui/src/scss/libs/_all.scss b/packages/merchant-backoffice-ui/src/scss/libs/_all.scss new file mode 100644 index 000000000..313eb52f9 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/libs/_all.scss @@ -0,0 +1,29 @@ +/* + 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 "node_modules/bulma-radio/bulma-radio"; +// @import "node_modules/bulma-responsive-tables/bulma-responsive-tables"; +@import "node_modules/bulma-checkbox/bulma-checkbox"; +@import "node_modules/bulma-switch-control/bulma-switch-control"; +@import "node_modules/bulma-upload-control/bulma-upload-control"; + +/* Bulma */ +@import "node_modules/bulma/bulma"; diff --git a/packages/merchant-backoffice-ui/src/scss/main.scss b/packages/merchant-backoffice-ui/src/scss/main.scss new file mode 100644 index 000000000..b523566c1 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/scss/main.scss @@ -0,0 +1,191 @@ +/* + 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) + */ + +/* Theme style (colors & sizes) */ +@import "theme-default"; + +/* Core Libs & Lib configs */ +@import "libs/all"; + +/* Mixins */ +@import "mixins"; + +/* Theme components */ +@import "nav-bar"; +@import "aside"; +@import "title-bar"; +@import "hero-bar"; +@import "card"; +@import "table"; +@import "tiles"; +@import "form"; +@import "main-section"; +@import "modal"; +@import "footer"; +@import "misc"; +@import "custom-calendar"; +@import "loading"; + +@import "fonts/nunito.css"; +@import "icons/materialdesignicons-4.9.95.min.css"; + +$tooltip-color: red; + +@import "../../node_modules/@creativebulma/bulma-tooltip/dist/bulma-tooltip.min.css"; +@import "../../node_modules/bulma-timeline/dist/css/bulma-timeline.min.css"; + +.notification { + background-color: transparent; +} + +.timeline .timeline-item .timeline-content { + padding-top: 0; +} + +.timeline .timeline-item:last-child::before { + display: none; +} + +.timeline .timeline-item .timeline-marker { + top: 0; +} + +.toast { + position: absolute; + width: 60%; + margin-left: 10%; + margin-right: 10%; + z-index: 999; + + display: flex; + flex-direction: column; + padding: 15px; + text-align: center; + pointer-events: none; +} + +.toast > .message { + white-space: pre-wrap; + opacity: 80%; +} + +div { + &.is-loading { + position: relative; + pointer-events: none; + opacity: 0.5; + &:after { + // @include loader; + position: absolute; + top: calc(50% - 2.5em); + left: calc(50% - 2.5em); + width: 5em; + height: 5em; + border-width: 0.25em; + } + } +} + +input[type="checkbox"]:indeterminate + .check { + background: red !important; +} + +.right-sticky { + position: sticky; + right: 0px; + background-color: $white; +} + +.right-sticky .buttons { + flex-wrap: nowrap; +} + +.table.is-striped tbody tr:not(.is-selected):nth-child(even) .right-sticky { + background-color: #fafafa; +} + +tr:hover .right-sticky { + background-color: hsl(0, 0%, 80%); +} +.table.is-striped tbody tr:nth-child(even):hover .right-sticky { + background-color: hsl(0, 0%, 95%); +} + +.content-full-size { + height: calc(100% - 3rem); + position: absolute; + width: calc(100% - 14rem); + display: flex; +} + +.content-full-size .column .card { + min-width: 200px; +} + +@include touch { + .content-full-size { + height: 100%; + position: absolute; + width: 100%; + } +} + +.column.is-half { + flex: none; + width: 50%; +} + +input:read-only { + cursor: initial; +} + +[data-tooltip]:before { + max-width: 15rem; + width: max-content; + text-align: left; + transition: opacity 0.1s linear 1s; + // transform: inherit !important; + white-space: pre-wrap !important; + font-weight: normal; + // position: relative; +} + +.icon[data-tooltip]:before { + transition: none; + z-index: 5; +} + +span[data-tooltip] { + border-bottom: none; +} + +div[data-tooltip]::before { + position: absolute; +} + +.modal-card-body > p { + padding: 1em; +} + +.modal-card-body > p.warning { + background-color: #fffbdd; + border: solid 1px #f2e9bf; +} diff --git a/packages/merchant-backoffice-ui/src/sw.js b/packages/merchant-backoffice-ui/src/sw.js new file mode 100644 index 000000000..5fcde8281 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/sw.js @@ -0,0 +1,25 @@ +/* + 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 { getFiles, setupPrecaching, setupRouting } from 'preact-cli/sw/'; + +// setupRouting(); +// setupPrecaching(getFiles()); diff --git a/packages/merchant-backoffice-ui/src/template.html b/packages/merchant-backoffice-ui/src/template.html new file mode 100644 index 000000000..ccccab167 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/template.html @@ -0,0 +1,52 @@ +<!-- + 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 +--> +<!DOCTYPE html> +<html lang="en" class="has-aside-left has-aside-mobile-transition has-navbar-fixed-top has-aside-expanded"> + <head> + <meta charset="utf-8"> + <title><%= htmlWebpackPlugin.options.title %></title> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <meta name="mobile-web-app-capable" content="yes"> + <meta name="apple-mobile-web-app-capable" content="yes"> + + <link rel="icon" href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" /> + <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" /> + + <% if (htmlWebpackPlugin.options.manifest.theme_color) { %> + <meta name="theme-color" content="<%= htmlWebpackPlugin.options.manifest.theme_color %>"> + <% } %> + + <% for (const index in htmlWebpackPlugin.files.css) { %> + <% const file = htmlWebpackPlugin.files.css[index] %> + <style data-href='<%= file %>' > + <%= compilation.assets[file.substr(htmlWebpackPlugin.files.publicPath.length)].source() %> + </style> + <% } %> + + </head> + <body> + + <script> + <%= compilation.assets[htmlWebpackPlugin.files.chunks["polyfills"].entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %> + </script> + <script> + <%= compilation.assets[htmlWebpackPlugin.files.chunks["bundle"].entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %> + </script> + + </body> +</html> diff --git a/packages/merchant-backoffice-ui/src/utils/amount.ts b/packages/merchant-backoffice-ui/src/utils/amount.ts new file mode 100644 index 000000000..61e33129c --- /dev/null +++ b/packages/merchant-backoffice-ui/src/utils/amount.ts @@ -0,0 +1,70 @@ +/* + 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/> + */ +import { amountFractionalBase, AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { MerchantBackend } from "../declaration"; + +/** + * sums two prices, + * @param one + * @param two + * @returns + */ +const sumPrices = (one: string, two: string) => { + const [currency, valueOne] = one.split(':') + const [, valueTwo] = two.split(':') + return `${currency}:${parseInt(valueOne, 10) + parseInt(valueTwo, 10)}` +} + +/** + * merge refund with the same description and a difference less than one minute + * @param prev list of refunds that will hold the merged refunds + * @param cur new refund to add to the list + * @returns list with the new refund, may be merged with the last + */ +export function mergeRefunds(prev: MerchantBackend.Orders.RefundDetails[], cur: MerchantBackend.Orders.RefundDetails) { + let tail; + + if (prev.length === 0 || //empty list + cur.timestamp.t_s === 'never' || //current doesnt have timestamp + (tail = prev[prev.length - 1]).timestamp.t_s === 'never' || // last doesnt have timestamp + cur.reason !== tail.reason || //different reason + cur.pending !== tail.pending || //different pending state + Math.abs(cur.timestamp.t_s - tail.timestamp.t_s) > 1000 * 60) {//more than 1 minute difference + + prev.push(cur) + return prev + } + + prev[prev.length - 1] = { + ...tail, + amount: sumPrices(tail.amount, cur.amount) + } + + return prev +} + +export const rate = (one: string, two: string) => { + const a = Amounts.parseOrThrow(one) + const b = Amounts.parseOrThrow(two) + 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); +} diff --git a/packages/merchant-backoffice-ui/src/utils/constants.ts b/packages/merchant-backoffice-ui/src/utils/constants.ts new file mode 100644 index 000000000..5356a1a49 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/utils/constants.ts @@ -0,0 +1,194 @@ +/* + 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) +*/ + +//https://tools.ietf.org/html/rfc8905 +export const PAYTO_REGEX = /^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]+(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/ +export const PAYTO_WIRE_METHOD_LOOKUP = /payto:\/\/([a-zA-Z][a-zA-Z0-9-.]+)\/.*/ + +export const AMOUNT_REGEX = /^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/ + +export const INSTANCE_ID_LOOKUP = /\/instances\/([^/]*)\/?$/ + +export const AMOUNT_ZERO_REGEX = /^[a-zA-Z][a-zA-Z]*:0$/ + +export const CROCKFORD_BASE32_REGEX = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]+[*~$=U]*$/ + +export const URL_REGEX = /^((https?:)(\/\/\/?)([\w]*(?::[\w]*)?@)?([\d\w\.-]+)(?::(\d+))?)\/$/ + +// how much rows we add every time user hit load more +export const PAGE_SIZE = 20 +// how bigger can be the result set +// after this threshold, load more with move the cursor +export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1; + +// how much we will wait for all request, in seconds +export const DEFAULT_REQUEST_TIMEOUT = 10; + +export const MAX_IMAGE_SIZE = 1024 * 1024; + +export const INSTANCE_ID_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_.@-]+$/ + +export const COUNTRY_TABLE = { + AE: "U.A.E.", + AF: "Afghanistan", + AL: "Albania", + AM: "Armenia", + AN: "Netherlands Antilles", + AR: "Argentina", + AT: "Austria", + AU: "Australia", + AZ: "Azerbaijan", + BA: "Bosnia and Herzegovina", + BD: "Bangladesh", + BE: "Belgium", + BG: "Bulgaria", + BH: "Bahrain", + BN: "Brunei Darussalam", + BO: "Bolivia", + BR: "Brazil", + BT: "Bhutan", + BY: "Belarus", + BZ: "Belize", + CA: "Canada", + CG: "Congo", + CH: "Switzerland", + CI: "Cote d'Ivoire", + CL: "Chile", + CM: "Cameroon", + CN: "People's Republic of China", + CO: "Colombia", + CR: "Costa Rica", + CS: "Serbia and Montenegro", + CZ: "Czech Republic", + DE: "Germany", + DK: "Denmark", + DO: "Dominican Republic", + DZ: "Algeria", + EC: "Ecuador", + EE: "Estonia", + EG: "Egypt", + ER: "Eritrea", + ES: "Spain", + ET: "Ethiopia", + FI: "Finland", + FO: "Faroe Islands", + FR: "France", + GB: "United Kingdom", + GD: "Caribbean", + GE: "Georgia", + GL: "Greenland", + GR: "Greece", + GT: "Guatemala", + HK: "Hong Kong", + // HK: "Hong Kong S.A.R.", + HN: "Honduras", + HR: "Croatia", + HT: "Haiti", + HU: "Hungary", + ID: "Indonesia", + IE: "Ireland", + IL: "Israel", + IN: "India", + IQ: "Iraq", + IR: "Iran", + IS: "Iceland", + IT: "Italy", + JM: "Jamaica", + JO: "Jordan", + JP: "Japan", + KE: "Kenya", + KG: "Kyrgyzstan", + KH: "Cambodia", + KR: "South Korea", + KW: "Kuwait", + KZ: "Kazakhstan", + LA: "Laos", + LB: "Lebanon", + LI: "Liechtenstein", + LK: "Sri Lanka", + LT: "Lithuania", + LU: "Luxembourg", + LV: "Latvia", + LY: "Libya", + MA: "Morocco", + MC: "Principality of Monaco", + MD: "Moldava", + // MD: "Moldova", + ME: "Montenegro", + MK: "Former Yugoslav Republic of Macedonia", + ML: "Mali", + MM: "Myanmar", + MN: "Mongolia", + MO: "Macau S.A.R.", + MT: "Malta", + MV: "Maldives", + MX: "Mexico", + MY: "Malaysia", + NG: "Nigeria", + NI: "Nicaragua", + NL: "Netherlands", + NO: "Norway", + NP: "Nepal", + NZ: "New Zealand", + OM: "Oman", + PA: "Panama", + PE: "Peru", + PH: "Philippines", + PK: "Islamic Republic of Pakistan", + PL: "Poland", + PR: "Puerto Rico", + PT: "Portugal", + PY: "Paraguay", + QA: "Qatar", + RE: "Reunion", + RO: "Romania", + RS: "Serbia", + RU: "Russia", + RW: "Rwanda", + SA: "Saudi Arabia", + SE: "Sweden", + SG: "Singapore", + SI: "Slovenia", + SK: "Slovak", + SN: "Senegal", + SO: "Somalia", + SR: "Suriname", + SV: "El Salvador", + SY: "Syria", + TH: "Thailand", + TJ: "Tajikistan", + TM: "Turkmenistan", + TN: "Tunisia", + TR: "Turkey", + TT: "Trinidad and Tobago", + TW: "Taiwan", + TZ: "Tanzania", + UA: "Ukraine", + US: "United States", + UY: "Uruguay", + VA: "Vatican", + VE: "Venezuela", + VN: "Viet Nam", + YE: "Yemen", + ZA: "South Africa", + ZW: "Zimbabwe" +} + diff --git a/packages/merchant-backoffice-ui/src/utils/switchableAxios.ts b/packages/merchant-backoffice-ui/src/utils/switchableAxios.ts new file mode 100644 index 000000000..be7eedd48 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/utils/switchableAxios.ts @@ -0,0 +1,66 @@ +/* + 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/> + */ + +import axios, { AxiosPromise, AxiosRequestConfig } from "axios"; + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +export let removeAxiosCancelToken = false; + +export let axiosHandler = function doAxiosRequest(config: AxiosRequestConfig): AxiosPromise<any> { + return axios(config) +} + +/** + * Set this backend library to testing mode. + * Instead of calling the axios library the @handler will be called + * + * @param handler callback that will mock axios + */ +export function setAxiosRequestAsTestingEnvironment(handler: AxiosHandler): void { + removeAxiosCancelToken = true; + axiosHandler = function defaultTestingHandler(config) { + const currentHanlder = listOfHandlersToUseOnce.shift() + if (!currentHanlder) { + return handler(config) + } + + return currentHanlder(config) + } +} + +type AxiosHandler = (config: AxiosRequestConfig) => AxiosPromise<any>; +type AxiosArguments = { args: AxiosRequestConfig | undefined } + + +const listOfHandlersToUseOnce = new Array<AxiosHandler>() + +/** + * + * @param handler mock function + * @returns savedArgs + */ +export function mockAxiosOnce(handler: AxiosHandler): { args: AxiosRequestConfig | undefined } { + const savedArgs: AxiosArguments = { args: undefined } + listOfHandlersToUseOnce.push((config: AxiosRequestConfig): AxiosPromise<any> => { + savedArgs.args = config; + return handler(config) + }) + return savedArgs; +} diff --git a/packages/merchant-backoffice-ui/src/utils/table.ts b/packages/merchant-backoffice-ui/src/utils/table.ts new file mode 100644 index 000000000..3d713a6f7 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/utils/table.ts @@ -0,0 +1,37 @@ +/* + 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/> + */ + +import { WithId } from "../declaration"; + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +export interface Actions<T extends WithId> { + element: T; + type: 'DELETE' | 'UPDATE'; +} + +function notEmpty<TValue>(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; +} + +export function buildActions<T extends WithId>(intances: T[], selected: string[], action: 'DELETE'): Actions<T>[] { + return selected.map(id => intances.find(i => i.id === id)) + .filter(notEmpty) + .map(id => ({ element: id, type: action })) +} diff --git a/packages/merchant-backoffice-ui/src/utils/types.ts b/packages/merchant-backoffice-ui/src/utils/types.ts new file mode 100644 index 000000000..a3f23ac10 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/utils/types.ts @@ -0,0 +1,31 @@ +/* + 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/> + */ + +import { VNode } from "preact"; + +export interface KeyValue { + [key: string]: string; +} + +export interface Notification { + message: string; + description?: string | VNode; + details?: string | VNode; + type: MessageType; +} + +export type ValueOrFunction<T> = T | ((p: T) => T); +export type MessageType = "INFO" | "WARN" | "ERROR" | "SUCCESS"; diff --git a/packages/merchant-backoffice-ui/tests/__mocks__/browserMocks.ts b/packages/merchant-backoffice-ui/tests/__mocks__/browserMocks.ts new file mode 100644 index 000000000..ee6bba505 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/__mocks__/browserMocks.ts @@ -0,0 +1,42 @@ +/* + 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) + */ + +// Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage +/** + * An example how to mock localStorage is given below 👇 + */ + +/* +// Mocks localStorage +const localStorageMock = (function() { + let store = {}; + + return { + getItem: (key) => store[key] || null, + setItem: (key, value) => store[key] = value.toString(), + clear: () => store = {} + }; + +})(); + +Object.defineProperty(window, 'localStorage', { + value: localStorageMock +}); */ diff --git a/packages/merchant-backoffice-ui/tests/__mocks__/fileMocks.ts b/packages/merchant-backoffice-ui/tests/__mocks__/fileMocks.ts new file mode 100644 index 000000000..0c045e9d1 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/__mocks__/fileMocks.ts @@ -0,0 +1,24 @@ +/* + 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) + */ + +// This fixed an error related to the CSS and loading gif breaking my Jest test +// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets +export default 'test-file-stub'; diff --git a/packages/merchant-backoffice-ui/tests/__mocks__/fileTransformer.js b/packages/merchant-backoffice-ui/tests/__mocks__/fileTransformer.js new file mode 100644 index 000000000..e6193f8fd --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/__mocks__/fileTransformer.js @@ -0,0 +1,31 @@ +/* + 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) +*/ +// fileTransformer.js + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +module.exports = { + process(src, filename, config, options) { + return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; + }, +}; + diff --git a/packages/merchant-backoffice-ui/tests/__mocks__/setupTests.ts b/packages/merchant-backoffice-ui/tests/__mocks__/setupTests.ts new file mode 100644 index 000000000..b08eb7fe6 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/__mocks__/setupTests.ts @@ -0,0 +1,28 @@ +/* + 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 "regenerator-runtime/runtime"; +// import { configure } from 'enzyme'; +// import Adapter from 'enzyme-adapter-preact-pure'; + +// configure({ +// adapter: new Adapter() +// }); diff --git a/packages/merchant-backoffice-ui/tests/axiosMock.ts b/packages/merchant-backoffice-ui/tests/axiosMock.ts new file mode 100644 index 000000000..13ddab598 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/axiosMock.ts @@ -0,0 +1,445 @@ +/* + 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 * as axios from 'axios'; +import { MerchantBackend } from '../src/declaration'; +import { mockAxiosOnce, setAxiosRequestAsTestingEnvironment } from '../src/utils/switchableAxios'; +// import { mockAxiosOnce, setAxiosRequestAsTestingEnvironment } from "../src/hooks/backend"; + +export type Query<Req, Res> = (GetQuery | PostQuery | DeleteQuery | PatchQuery) & RequestResponse<Req, Res> + +interface RequestResponse<Req, Res> { + code?: number, +} +interface GetQuery { get: string } +interface PostQuery { post: string } +interface DeleteQuery { delete: string } +interface PatchQuery { patch: string } + + +const JEST_DEBUG_LOG = process.env['JEST_DEBUG_LOG'] !== undefined + +type ExpectationValues = { query: Query<any, any>; params?: { auth?: string, request?: any, qparam?: any, response?: any } } + +type TestValues = [axios.AxiosRequestConfig | undefined, ExpectationValues | undefined] + +const defaultCallback = (actualQuery?: axios.AxiosRequestConfig): axios.AxiosPromise<any> => { + if (JEST_DEBUG_LOG) { + console.log('UNEXPECTED QUERY', actualQuery) + } + throw Error('Default Axios mock callback is called, this mean that the test did a tried to use axios but there was no expectation in place, try using JEST_DEBUG_LOG env') +} + +setAxiosRequestAsTestingEnvironment( + defaultCallback +); + +export class AxiosMockEnvironment { + expectations: Array<{ + query: Query<any, any>, + auth?: string, + params?: { request?: any, qparam?: any, response?: any }, + result: { args: axios.AxiosRequestConfig | undefined } + } | undefined> = [] + // axiosMock: jest.MockedFunction<axios.AxiosStatic> + + addRequestExpectation<RequestType, ResponseType>(expectedQuery: Query<RequestType, ResponseType>, params: { auth?: string, request?: RequestType, qparam?: any, response?: ResponseType }): void { + const result = mockAxiosOnce(function (actualQuery?: axios.AxiosRequestConfig): axios.AxiosPromise { + + if (JEST_DEBUG_LOG) { + console.log('query to the backend is made', actualQuery) + } + if (!expectedQuery) { + return Promise.reject("a query was made but it was not expected") + } + if (JEST_DEBUG_LOG) { + console.log('expected query:', params?.request) + console.log('expected qparams:', params?.qparam) + console.log('sending response:', params?.response) + } + + const responseCode = expectedQuery.code || 200 + + //This response is what buildRequestOk is expecting in file hook/backend.ts + if (responseCode >= 200 && responseCode < 300) { + return Promise.resolve({ + data: params?.response, config: { + data: params?.response, + params: actualQuery?.params || {}, + }, request: { params: actualQuery?.params || {} } + } as any); + } + //This response is what buildRequestFailed is expecting in file hook/backend.ts + return Promise.reject({ + response: { + status: responseCode + }, + request: { + data: params?.response, + params: actualQuery?.params || {}, + } + }) + + } as any) + + this.expectations.push(expectedQuery ? { query: expectedQuery, params, result } : undefined) + } + + getLastTestValues(): TestValues { + const expectedQuery = this.expectations.shift() + + return [ + expectedQuery?.result.args, expectedQuery + ] + } + +} + +export function assertJustExpectedRequestWereMade(env: AxiosMockEnvironment): void { + let size = env.expectations.length + while (size-- > 0) { + assertNextRequest(env) + } + assertNoMoreRequestWereMade(env) +} + +export function assertNoMoreRequestWereMade(env: AxiosMockEnvironment): void { + const [actualQuery, expectedQuery] = env.getLastTestValues() + + expect(actualQuery).toBeUndefined(); + expect(expectedQuery).toBeUndefined(); +} + +export function assertNextRequest(env: AxiosMockEnvironment): void { + const [actualQuery, expectedQuery] = env.getLastTestValues() + + if (!actualQuery) { + //expected one query but the tested component didn't execute one + expect(actualQuery).toBe(expectedQuery); + return + } + + if (!expectedQuery) { + const errorMessage = 'a query was made to the backend but the test explicitly expected no query'; + if (JEST_DEBUG_LOG) { + console.log(errorMessage, actualQuery) + } + throw Error(errorMessage) + } + if ('get' in expectedQuery.query) { + expect(actualQuery.method).toBe('get'); + expect(actualQuery.url).toBe(expectedQuery.query.get); + } + if ('post' in expectedQuery.query) { + expect(actualQuery.method).toBe('post'); + expect(actualQuery.url).toBe(expectedQuery.query.post); + } + if ('delete' in expectedQuery.query) { + expect(actualQuery.method).toBe('delete'); + expect(actualQuery.url).toBe(expectedQuery.query.delete); + } + if ('patch' in expectedQuery.query) { + expect(actualQuery.method).toBe('patch'); + expect(actualQuery.url).toBe(expectedQuery.query.patch); + } + + if (expectedQuery.params?.request) { + expect(actualQuery.data).toMatchObject(expectedQuery.params.request) + } + if (expectedQuery.params?.qparam) { + expect(actualQuery.params).toMatchObject(expectedQuery.params.qparam) + } + + if (expectedQuery.params?.auth) { + expect(actualQuery.headers.Authorization).toBe(expectedQuery.params?.auth) + } + +} + +//////////////////// +// ORDER +//////////////////// + +export const API_CREATE_ORDER: Query< + MerchantBackend.Orders.PostOrderRequest, + MerchantBackend.Orders.PostOrderResponse +> = { + post: "http://backend/instances/default/private/orders", +}; + +export const API_GET_ORDER_BY_ID = ( + id: string +): Query< + unknown, + MerchantBackend.Orders.MerchantOrderStatusResponse +> => ({ + get: `http://backend/instances/default/private/orders/${id}`, +}); + +export const API_LIST_ORDERS: Query< + unknown, + MerchantBackend.Orders.OrderHistory +> = { + get: "http://backend/instances/default/private/orders", +}; + +export const API_REFUND_ORDER_BY_ID = ( + id: string +): Query< + MerchantBackend.Orders.RefundRequest, + MerchantBackend.Orders.MerchantRefundResponse +> => ({ + post: `http://backend/instances/default/private/orders/${id}/refund`, +}); + +export const API_FORGET_ORDER_BY_ID = ( + id: string +): Query< + MerchantBackend.Orders.ForgetRequest, + unknown +> => ({ + patch: `http://backend/instances/default/private/orders/${id}/forget`, +}); + +export const API_DELETE_ORDER = ( + id: string +): Query< + MerchantBackend.Orders.ForgetRequest, + unknown +> => ({ + delete: `http://backend/instances/default/private/orders/${id}`, +}); + +//////////////////// +// TRANSFER +//////////////////// + +export const API_LIST_TRANSFERS: Query< + unknown, + MerchantBackend.Transfers.TransferList +> = { + get: "http://backend/instances/default/private/transfers", +}; + +export const API_INFORM_TRANSFERS: Query< + MerchantBackend.Transfers.TransferInformation, + MerchantBackend.Transfers.MerchantTrackTransferResponse +> = { + post: "http://backend/instances/default/private/transfers", +}; + +//////////////////// +// PRODUCT +//////////////////// + +export const API_CREATE_PRODUCT: Query< + MerchantBackend.Products.ProductAddDetail, + unknown +> = { + post: "http://backend/instances/default/private/products", +}; + +export const API_LIST_PRODUCTS: Query< + unknown, + MerchantBackend.Products.InventorySummaryResponse +> = { + get: "http://backend/instances/default/private/products", +}; + +export const API_GET_PRODUCT_BY_ID = ( + id: string +): Query<unknown, MerchantBackend.Products.ProductDetail> => ({ + get: `http://backend/instances/default/private/products/${id}`, +}); + +export const API_UPDATE_PRODUCT_BY_ID = ( + id: string +): Query< + MerchantBackend.Products.ProductPatchDetail, + MerchantBackend.Products.InventorySummaryResponse +> => ({ + patch: `http://backend/instances/default/private/products/${id}`, +}); + +export const API_DELETE_PRODUCT = ( + id: string +): Query< + unknown, unknown +> => ({ + delete: `http://backend/instances/default/private/products/${id}`, +}); + +//////////////////// +// RESERVES +//////////////////// + +export const API_CREATE_RESERVE: Query< + MerchantBackend.Tips.ReserveCreateRequest, + MerchantBackend.Tips.ReserveCreateConfirmation +> = { + post: "http://backend/instances/default/private/reserves", +}; +export const API_LIST_RESERVES: Query< + unknown, + MerchantBackend.Tips.TippingReserveStatus +> = { + get: "http://backend/instances/default/private/reserves", +}; + +export const API_GET_RESERVE_BY_ID = ( + pub: string +): Query<unknown, MerchantBackend.Tips.ReserveDetail> => ({ + get: `http://backend/instances/default/private/reserves/${pub}`, +}); + +export const API_GET_TIP_BY_ID = ( + pub: string +): Query< + unknown, + MerchantBackend.Tips.TipDetails +> => ({ + get: `http://backend/instances/default/private/tips/${pub}`, +}); + +export const API_AUTHORIZE_TIP_FOR_RESERVE = ( + pub: string +): Query< + MerchantBackend.Tips.TipCreateRequest, + MerchantBackend.Tips.TipCreateConfirmation +> => ({ + post: `http://backend/instances/default/private/reserves/${pub}/authorize-tip`, +}); + +export const API_AUTHORIZE_TIP: Query< + MerchantBackend.Tips.TipCreateRequest, + MerchantBackend.Tips.TipCreateConfirmation +> = ({ + post: `http://backend/instances/default/private/tips`, +}); + + +export const API_DELETE_RESERVE = ( + id: string +): Query<unknown, unknown> => ({ + delete: `http://backend/instances/default/private/reserves/${id}`, +}); + + +//////////////////// +// INSTANCE ADMIN +//////////////////// + +export const API_CREATE_INSTANCE: Query< + MerchantBackend.Instances.InstanceConfigurationMessage, + unknown +> = { + post: "http://backend/management/instances", +}; + +export const API_GET_INSTANCE_BY_ID = ( + id: string +): Query< + unknown, + MerchantBackend.Instances.QueryInstancesResponse +> => ({ + get: `http://backend/management/instances/${id}`, +}); + +export const API_GET_INSTANCE_KYC_BY_ID = ( + id: string +): Query< + unknown, + MerchantBackend.Instances.AccountKycRedirects +> => ({ + get: `http://backend/management/instances/${id}/kyc`, +}); + +export const API_LIST_INSTANCES: Query< + unknown, + MerchantBackend.Instances.InstancesResponse +> = { + get: "http://backend/management/instances", +}; + +export const API_UPDATE_INSTANCE_BY_ID = ( + id: string +): Query< + MerchantBackend.Instances.InstanceReconfigurationMessage, + unknown +> => ({ + patch: `http://backend/management/instances/${id}`, +}); + +export const API_UPDATE_INSTANCE_AUTH_BY_ID = ( + id: string +): Query< + MerchantBackend.Instances.InstanceAuthConfigurationMessage, + unknown +> => ({ + post: `http://backend/management/instances/${id}/auth`, +}); + +export const API_DELETE_INSTANCE = ( + id: string +): Query<unknown, unknown> => ({ + delete: `http://backend/management/instances/${id}`, +}); + +//////////////////// +// INSTANCE +//////////////////// + +export const API_GET_CURRENT_INSTANCE: Query< + unknown, + MerchantBackend.Instances.QueryInstancesResponse +> = ({ + get: `http://backend/instances/default/private/`, +}); + +export const API_GET_CURRENT_INSTANCE_KYC: Query< + unknown, + MerchantBackend.Instances.AccountKycRedirects +> = + ({ + get: `http://backend/instances/default/private/kyc`, + }); + +export const API_UPDATE_CURRENT_INSTANCE: Query< + MerchantBackend.Instances.InstanceReconfigurationMessage, + unknown +> = { + patch: `http://backend/instances/default/private/`, +}; + +export const API_UPDATE_CURRENT_INSTANCE_AUTH: Query< + MerchantBackend.Instances.InstanceAuthConfigurationMessage, + unknown +> = { + post: `http://backend/instances/default/private/auth`, +}; + +export const API_DELETE_CURRENT_INSTANCE: Query< + unknown, + unknown +> = ({ + delete: `http://backend/instances/default/private`, +}); + + diff --git a/packages/merchant-backoffice-ui/tests/context/backend.test.tsx b/packages/merchant-backoffice-ui/tests/context/backend.test.tsx new file mode 100644 index 000000000..b7b50fd47 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/context/backend.test.tsx @@ -0,0 +1,172 @@ +/* + 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 { renderHook } from "@testing-library/preact-hooks"; +import { ComponentChildren, h, VNode } from "preact"; +import { act } from "preact/test-utils"; +import { BackendContextProvider } from "../../src/context/backend"; +import { InstanceContextProvider } from "../../src/context/instance"; +import { MerchantBackend } from "../../src/declaration"; +import { + useAdminAPI, + useInstanceAPI, + useManagementAPI, +} from "../../src/hooks/instance"; +import { + API_CREATE_INSTANCE, + API_GET_CURRENT_INSTANCE, + API_UPDATE_CURRENT_INSTANCE_AUTH, + API_UPDATE_INSTANCE_AUTH_BY_ID, + assertJustExpectedRequestWereMade, + AxiosMockEnvironment, +} from "../axiosMock"; + +interface TestingContextProps { + children?: ComponentChildren; +} + +function TestingContext({ children }: TestingContextProps): VNode { + return ( + <BackendContextProvider defaultUrl="http://backend" initialToken="token"> + {children} + </BackendContextProvider> + ); +} +function AdminTestingContext({ children }: TestingContextProps): VNode { + return ( + <BackendContextProvider defaultUrl="http://backend" initialToken="token"> + <InstanceContextProvider + value={{ + token: "token", + id: "default", + admin: true, + changeToken: () => null, + }} + > + {children} + </InstanceContextProvider> + </BackendContextProvider> + ); +} + +describe("backend context api ", () => { + it("should use new token after updating the instance token in the settings as user", async () => { + const env = new AxiosMockEnvironment(); + + const { result, waitForNextUpdate } = renderHook( + () => { + const instance = useInstanceAPI(); + const management = useManagementAPI("default"); + const admin = useAdminAPI(); + + return { instance, management, admin }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + env.addRequestExpectation(API_UPDATE_INSTANCE_AUTH_BY_ID("default"), { + request: { + method: "token", + token: "another_token", + }, + response: { + name: "instance_name", + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + await act(async () => { + await result.current?.management.setNewToken("another_token"); + }); + + // await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_CREATE_INSTANCE, { + auth: "Bearer another_token", + request: { + id: "new_instance_id", + } as MerchantBackend.Instances.InstanceConfigurationMessage, + }); + + result.current.admin.createInstance({ + id: "new_instance_id", + } as MerchantBackend.Instances.InstanceConfigurationMessage); + + assertJustExpectedRequestWereMade(env); + }); + + it("should use new token after updating the instance token in the settings as admin", async () => { + const env = new AxiosMockEnvironment(); + + const { result, waitForNextUpdate } = renderHook( + () => { + const instance = useInstanceAPI(); + const management = useManagementAPI("default"); + const admin = useAdminAPI(); + + return { instance, management, admin }; + }, + { wrapper: AdminTestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, { + request: { + method: "token", + token: "another_token", + }, + response: { + name: "instance_name", + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + await act(async () => { + await result.current?.instance.setNewToken("another_token"); + }); + + // await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_CREATE_INSTANCE, { + auth: "Bearer another_token", + request: { + id: "new_instance_id", + } as MerchantBackend.Instances.InstanceConfigurationMessage, + }); + + result.current.admin.createInstance({ + id: "new_instance_id", + } as MerchantBackend.Instances.InstanceConfigurationMessage); + + assertJustExpectedRequestWereMade(env); + }); +}); diff --git a/packages/merchant-backoffice-ui/tests/declarations.d.ts b/packages/merchant-backoffice-ui/tests/declarations.d.ts new file mode 100644 index 000000000..61a53dc69 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/declarations.d.ts @@ -0,0 +1,28 @@ +/* + 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) + */ + +declare global { + namespace jest { + interface Matchers<R> { + toBeWithinRange(a: number, b: number): R; + } + } +} diff --git a/packages/merchant-backoffice-ui/tests/functions/regex.test.ts b/packages/merchant-backoffice-ui/tests/functions/regex.test.ts new file mode 100644 index 000000000..fc8a6a42f --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/functions/regex.test.ts @@ -0,0 +1,87 @@ +/* + 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 { AMOUNT_REGEX, PAYTO_REGEX } from "../../src/utils/constants"; + +describe('payto uri format', () => { + const valids = [ + 'payto://iban/DE75512108001245126199?amount=EUR:200.0&message=hello', + 'payto://ach/122000661/1234', + 'payto://upi/alice@example.com?receiver-name=Alice&amount=INR:200', + 'payto://void/?amount=EUR:10.5', + 'payto://ilp/g.acme.bob' + ] + + test('should be valid', () => { + valids.forEach(v => expect(v).toMatch(PAYTO_REGEX)) + }); + + const invalids = [ + // has two question marks + 'payto://iban/DE75?512108001245126199?amount=EUR:200.0&message=hello', + // has a space + 'payto://ach /122000661/1234', + // has a space + 'payto://upi/alice@ example.com?receiver-name=Alice&amount=INR:200', + // invalid field name (mount instead of amount) + 'payto://void/?mount=EUR:10.5', + // payto:// is incomplete + 'payto: //ilp/g.acme.bob' + ] + + test('should not be valid', () => { + invalids.forEach(v => expect(v).not.toMatch(PAYTO_REGEX)) + }); +}) + +describe('amount format', () => { + const valids = [ + 'ARS:10', + 'COL:10.2', + 'UY:1,000.2', + 'ARS:10.123,123', + 'ARS:1,000,000', + 'ARSCOL:10', + 'THISISTHEMOTHERCOIN:1,000,000.123,123', + ] + + test('should be valid', () => { + valids.forEach(v => expect(v).toMatch(AMOUNT_REGEX)) + }); + + const invalids = [ + //no currency name + ':10', + //use . instead of , + 'ARS:1.000.000', + //currency name with numbers + '1ARS:10', + //currency name with numbers + 'AR5:10', + //missing value + 'USD:', + ] + + test('should not be valid', () => { + invalids.forEach(v => expect(v).not.toMatch(AMOUNT_REGEX)) + }); + +})
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/tests/header.test.tsx b/packages/merchant-backoffice-ui/tests/header.test.tsx new file mode 100644 index 000000000..f098b70d5 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/header.test.tsx @@ -0,0 +1,63 @@ +/* + 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 } from "preact"; +import { ProductList } from "../src/components/product/ProductList"; +// See: https://github.com/preactjs/enzyme-adapter-preact-pure +// import { shallow } from 'enzyme'; +import * as backend from "../src/context/config"; +import { render, findAllByText } from "@testing-library/preact"; +import * as i18n from "../src/context/translation"; + +import * as jedLib from "jed"; +const handler = new jedLib.Jed("en"); + +describe("Initial Test of the Sidebar", () => { + beforeEach(() => { + jest + .spyOn(backend, "useConfigContext") + .mockImplementation(() => ({ version: "", currency: "" })); + jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({ + changeLanguage: () => null, + handler, + lang: "en", + })); + }); + test("Product list renders a table", () => { + const context = render( + <ProductList + list={[ + { + description: "description of the product", + image: "asdasda", + price: "USD:10", + quantity: 1, + taxes: [{ name: "VAT", tax: "EUR:1" }], + unit: "book", + }, + ]} + /> + ); + + expect(context.findAllByText("description of the product")).toBeDefined(); + // expect(context.find('table tr td img').map(img => img.prop('src'))).toEqual(''); + }); +}); diff --git a/packages/merchant-backoffice-ui/tests/hooks/async.test.ts b/packages/merchant-backoffice-ui/tests/hooks/async.test.ts new file mode 100644 index 000000000..a6d0cddfa --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/async.test.ts @@ -0,0 +1,158 @@ +/* + 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/> + */ + +import { renderHook } from "@testing-library/preact-hooks" +import { useAsync } from "../../src/hooks/async" + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ +test("async function is called", async () => { + jest.useFakeTimers() + + const timeout = 500 + + const asyncFunction = jest.fn(() => new Promise((res) => { + setTimeout(() => { + res({ the_answer: 'yes' }) + }, timeout); + })) + + const { result, waitForNextUpdate } = renderHook(() => { + return useAsync(asyncFunction) + }) + + expect(result.current?.isLoading).toBeFalsy() + + result.current?.request() + expect(asyncFunction).toBeCalled() + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeTruthy() + + jest.advanceTimersByTime(timeout + 1) + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeFalsy() + expect(result.current?.data).toMatchObject({ the_answer: 'yes' }) + expect(result.current?.error).toBeUndefined() + expect(result.current?.isSlow).toBeFalsy() +}) + +test("async function return error if rejected", async () => { + jest.useFakeTimers() + + const timeout = 500 + + const asyncFunction = jest.fn(() => new Promise((_, rej) => { + setTimeout(() => { + rej({ the_error: 'yes' }) + }, timeout); + })) + + const { result, waitForNextUpdate } = renderHook(() => { + return useAsync(asyncFunction) + }) + + expect(result.current?.isLoading).toBeFalsy() + + result.current?.request() + expect(asyncFunction).toBeCalled() + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeTruthy() + + jest.advanceTimersByTime(timeout + 1) + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeFalsy() + expect(result.current?.error).toMatchObject({ the_error: 'yes' }) + expect(result.current?.data).toBeUndefined() + expect(result.current?.isSlow).toBeFalsy() +}) + +test("async function is slow", async () => { + jest.useFakeTimers() + + const timeout = 2200 + + const asyncFunction = jest.fn(() => new Promise((res) => { + setTimeout(() => { + res({ the_answer: 'yes' }) + }, timeout); + })) + + const { result, waitForNextUpdate } = renderHook(() => { + return useAsync(asyncFunction) + }) + + expect(result.current?.isLoading).toBeFalsy() + + result.current?.request() + expect(asyncFunction).toBeCalled() + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeTruthy() + + jest.advanceTimersByTime(timeout / 2) + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeTruthy() + expect(result.current?.isSlow).toBeTruthy() + expect(result.current?.data).toBeUndefined() + expect(result.current?.error).toBeUndefined() + + jest.advanceTimersByTime(timeout / 2) + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeFalsy() + expect(result.current?.data).toMatchObject({ the_answer: 'yes' }) + expect(result.current?.error).toBeUndefined() + expect(result.current?.isSlow).toBeFalsy() + +}) + +test("async function is cancellable", async () => { + jest.useFakeTimers() + + const timeout = 2200 + + const asyncFunction = jest.fn(() => new Promise((res) => { + setTimeout(() => { + res({ the_answer: 'yes' }) + }, timeout); + })) + + const { result, waitForNextUpdate } = renderHook(() => { + return useAsync(asyncFunction) + }) + + expect(result.current?.isLoading).toBeFalsy() + + result.current?.request() + expect(asyncFunction).toBeCalled() + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeTruthy() + + jest.advanceTimersByTime(timeout / 2) + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeTruthy() + expect(result.current?.isSlow).toBeTruthy() + expect(result.current?.data).toBeUndefined() + expect(result.current?.error).toBeUndefined() + + result.current?.cancel() + await waitForNextUpdate({ timeout: 1 }) + expect(result.current?.isLoading).toBeFalsy() + expect(result.current?.data).toBeUndefined() + expect(result.current?.error).toBeUndefined() + expect(result.current?.isSlow).toBeFalsy() + +}) diff --git a/packages/merchant-backoffice-ui/tests/hooks/listener.test.ts b/packages/merchant-backoffice-ui/tests/hooks/listener.test.ts new file mode 100644 index 000000000..ae34c1339 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/listener.test.ts @@ -0,0 +1,62 @@ +/* + 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 { renderHook, act } from '@testing-library/preact-hooks'; +import { useListener } from '../../src/hooks/listener'; + +// jest.useFakeTimers() + +test('listener', async () => { + + + function createSomeString() { + return "hello" + } + async function addWorldToTheEnd(resultFromComponentB: string) { + return `${resultFromComponentB} world` + } + const expectedResult = "hello world" + + const { result } = renderHook(() => useListener(addWorldToTheEnd)) + + if (!result.current) { + expect(result.current).toBeDefined() + return; + } + + { + const [activator, subscriber] = result.current + expect(activator).toBeUndefined() + + act(() => { + subscriber(createSomeString) + }) + + } + + const [activator] = result.current + expect(activator).toBeDefined() + if (!activator) return; + + const response = await activator() + expect(response).toBe(expectedResult) + +}); diff --git a/packages/merchant-backoffice-ui/tests/hooks/notification.test.ts b/packages/merchant-backoffice-ui/tests/hooks/notification.test.ts new file mode 100644 index 000000000..6825a825a --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/notification.test.ts @@ -0,0 +1,51 @@ +/* + 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 { renderHook, act} from '@testing-library/preact-hooks'; +import { useNotifications } from '../../src/hooks/notifications'; + +jest.useFakeTimers() + +test('notification should disapear after timeout', () => { + jest.spyOn(global, 'setTimeout'); + + const timeout = 1000 + const { result, rerender } = renderHook(() => useNotifications(undefined, timeout)); + + expect(result.current?.notifications.length).toBe(0); + + act(() => { + result.current?.pushNotification({ + message: 'some_id', + type: 'INFO' + }); + }); + expect(result.current?.notifications.length).toBe(1); + + jest.advanceTimersByTime(timeout/2); + rerender() + expect(result.current?.notifications.length).toBe(1); + + jest.advanceTimersByTime(timeout); + rerender() + expect(result.current?.notifications.length).toBe(0); + +}); diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/index.tsx b/packages/merchant-backoffice-ui/tests/hooks/swr/index.tsx new file mode 100644 index 000000000..44514855d --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/swr/index.tsx @@ -0,0 +1,45 @@ +/* + 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 { ComponentChildren, h, VNode } from "preact"; +import { SWRConfig } from "swr"; +import { BackendContextProvider } from "../../../src/context/backend"; +import { InstanceContextProvider } from "../../../src/context/instance"; + +interface TestingContextProps { + children?: ComponentChildren; +} +export function TestingContext({ children }: TestingContextProps): VNode { + return ( + <BackendContextProvider defaultUrl="http://backend" initialToken="token"> + <InstanceContextProvider + value={{ + token: "token", + id: "default", + admin: true, + changeToken: () => null, + }} + > + <SWRConfig value={{ provider: () => new Map() }}>{children}</SWRConfig> + </InstanceContextProvider> + </BackendContextProvider> + ); +} diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/instance.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/instance.test.ts new file mode 100644 index 000000000..55d9fa6ee --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/swr/instance.test.ts @@ -0,0 +1,636 @@ +/* + 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 { renderHook } from "@testing-library/preact-hooks"; +import { act } from "preact/test-utils"; +import { MerchantBackend } from "../../../src/declaration"; +import { useAdminAPI, useBackendInstances, useInstanceAPI, useInstanceDetails, useManagementAPI } from "../../../src/hooks/instance"; +import { + API_CREATE_INSTANCE, + API_DELETE_INSTANCE, + API_GET_CURRENT_INSTANCE, + API_LIST_INSTANCES, + API_UPDATE_CURRENT_INSTANCE, + API_UPDATE_CURRENT_INSTANCE_AUTH, + API_UPDATE_INSTANCE_AUTH_BY_ID, + API_UPDATE_INSTANCE_BY_ID, + assertJustExpectedRequestWereMade, + AxiosMockEnvironment +} from "../../axiosMock"; +import { TestingContext } from "./index"; + +describe("instance api interaction with details ", () => { + + it("should evict cache when updating an instance", async () => { + + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_CURRENT_INSTANCE, { + response: { + name: 'instance_name' + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useInstanceAPI(); + const query = useInstanceDetails(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + name: 'instance_name' + }); + + env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE, { + request: { + name: 'other_name' + } as MerchantBackend.Instances.InstanceReconfigurationMessage, + }); + + act(async () => { + await result.current?.api.updateInstance({ + name: 'other_name' + } as MerchantBackend.Instances.InstanceReconfigurationMessage); + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_GET_CURRENT_INSTANCE, { + response: { + name: 'other_name' + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + name: 'other_name' + }); + }); + + it("should evict cache when setting the instance's token", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_CURRENT_INSTANCE, { + response: { + name: 'instance_name', + auth: { + method: 'token', + token: 'not-secret', + } + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useInstanceAPI(); + const query = useInstanceDetails(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + name: 'instance_name', + auth: { + method: 'token', + token: 'not-secret', + } + }); + + env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, { + request: { + method: 'token', + token: 'secret' + } as MerchantBackend.Instances.InstanceAuthConfigurationMessage, + }); + + act(async () => { + await result.current?.api.setNewToken('secret'); + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_GET_CURRENT_INSTANCE, { + response: { + name: 'instance_name', + auth: { + method: 'token', + token: 'secret', + } + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + name: 'instance_name', + auth: { + method: 'token', + token: 'secret', + } + }); + }); + + it("should evict cache when clearing the instance's token", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_CURRENT_INSTANCE, { + response: { + name: 'instance_name', + auth: { + method: 'token', + token: 'not-secret', + } + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useInstanceAPI(); + const query = useInstanceDetails(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + name: 'instance_name', + auth: { + method: 'token', + token: 'not-secret', + } + }); + + env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, { + request: { + method: 'external', + } as MerchantBackend.Instances.InstanceAuthConfigurationMessage, + }); + + act(async () => { + await result.current?.api.clearToken(); + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_GET_CURRENT_INSTANCE, { + response: { + name: 'instance_name', + auth: { + method: 'external', + } + } as MerchantBackend.Instances.QueryInstancesResponse, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + name: 'instance_name', + auth: { + method: 'external', + } + }); + }); +}); + +describe("instance admin api interaction with listing ", () => { + + it("should evict cache when creating a new instance", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [{ + name: 'instance_name' + } as MerchantBackend.Instances.Instance] + }, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useAdminAPI(); + const query = useBackendInstances(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + instances: [{ + name: 'instance_name' + }] + }); + + env.addRequestExpectation(API_CREATE_INSTANCE, { + request: { + name: 'other_name' + } as MerchantBackend.Instances.InstanceConfigurationMessage, + }); + + act(async () => { + await result.current?.api.createInstance({ + name: 'other_name' + } as MerchantBackend.Instances.InstanceConfigurationMessage); + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [{ + name: 'instance_name' + } as MerchantBackend.Instances.Instance, + { + name: 'other_name' + } as MerchantBackend.Instances.Instance] + }, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + instances: [{ + name: 'instance_name' + }, { + name: 'other_name' + }] + }); + }); + + it("should evict cache when deleting an instance", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [{ + id: 'default', + name: 'instance_name' + } as MerchantBackend.Instances.Instance, + { + id: 'the_id', + name: 'second_instance' + } as MerchantBackend.Instances.Instance] + }, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useAdminAPI(); + const query = useBackendInstances(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + instances: [{ + id: 'default', + name: 'instance_name' + }, { + id: 'the_id', + name: 'second_instance' + }] + }); + + env.addRequestExpectation(API_DELETE_INSTANCE('the_id'), {}); + + act(async () => { + await result.current?.api.deleteInstance('the_id'); + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [{ + id: 'default', + name: 'instance_name' + } as MerchantBackend.Instances.Instance] + }, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + instances: [{ + id: 'default', + name: 'instance_name' + }] + }); + }); + it("should evict cache when deleting (purge) an instance", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [{ + id: 'default', + name: 'instance_name' + } as MerchantBackend.Instances.Instance, + { + id: 'the_id', + name: 'second_instance' + } as MerchantBackend.Instances.Instance] + }, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useAdminAPI(); + const query = useBackendInstances(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + instances: [{ + id: 'default', + name: 'instance_name' + }, { + id: 'the_id', + name: 'second_instance' + }] + }); + + env.addRequestExpectation(API_DELETE_INSTANCE('the_id'), { + qparam: { + purge: 'YES' + } + }); + + act(async () => { + await result.current?.api.purgeInstance('the_id'); + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [{ + id: 'default', + name: 'instance_name' + } as MerchantBackend.Instances.Instance] + }, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + instances: [{ + id: 'default', + name: 'instance_name' + }] + }); + }); +}); + +describe("instance management api interaction with listing ", () => { + + it("should evict cache when updating an instance", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [{ + id: 'managed', + name: 'instance_name' + } as MerchantBackend.Instances.Instance] + }, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useManagementAPI('managed'); + const query = useBackendInstances(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + instances: [{ + id: 'managed', + name: 'instance_name' + }] + }); + + env.addRequestExpectation(API_UPDATE_INSTANCE_BY_ID('managed'), { + request: { + name: 'other_name' + } as MerchantBackend.Instances.InstanceReconfigurationMessage, + }); + + act(async () => { + await result.current?.api.updateInstance({ + name: 'other_name' + } as MerchantBackend.Instances.InstanceConfigurationMessage); + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_INSTANCES, { + response: { + instances: [ + { + id: 'managed', + name: 'other_name' + } as MerchantBackend.Instances.Instance] + }, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + instances: [{ + id: 'managed', + name: 'other_name' + }] + }); + }); + +}); + diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/order.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/order.test.ts new file mode 100644 index 000000000..e7f6c9334 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/swr/order.test.ts @@ -0,0 +1,567 @@ +/* + 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 { renderHook } from "@testing-library/preact-hooks"; +import { act } from "preact/test-utils"; +import { TestingContext } from "."; +import { MerchantBackend } from "../../../src/declaration"; +import { useInstanceOrders, useOrderAPI, useOrderDetails } from "../../../src/hooks/order"; +import { + API_CREATE_ORDER, + API_DELETE_ORDER, + API_FORGET_ORDER_BY_ID, + API_GET_ORDER_BY_ID, + API_LIST_ORDERS, API_REFUND_ORDER_BY_ID, assertJustExpectedRequestWereMade, assertNextRequest, assertNoMoreRequestWereMade, AxiosMockEnvironment +} from "../../axiosMock"; + +describe("order api interaction with listing", () => { + + it("should evict cache when creating an order", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 0, paid: "yes" }, + response: { + orders: [{ order_id: "1" } as MerchantBackend.Orders.OrderHistoryEntry], + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, paid: "yes" }, + response: { + orders: [{ order_id: "2" } as MerchantBackend.Orders.OrderHistoryEntry], + }, + }); + + + const { result, waitForNextUpdate } = renderHook(() => { + const newDate = (d: Date) => { + console.log("new date", d); + }; + const query = useInstanceOrders({ paid: "yes" }, newDate); + const api = useOrderAPI(); + + return { query, api }; + }, { wrapper: TestingContext }); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [{ order_id: "1" }, { order_id: "2" }], + }); + + env.addRequestExpectation(API_CREATE_ORDER, { + request: { + order: { amount: "ARS:12", summary: "pay me" }, + }, + response: { order_id: "3" }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 0, paid: "yes" }, + response: { + orders: [{ order_id: "1" } as any], + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, paid: "yes" }, + response: { + orders: [{ order_id: "2" } as any, { order_id: "3" } as any], + }, + }); + + act(async () => { + await result.current?.api.createOrder({ + order: { amount: "ARS:12", summary: "pay me" }, + } as any); + }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [{ order_id: "1" }, { order_id: "2" }, { order_id: "3" }], + }); + }); + it("should evict cache when doing a refund", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 0, paid: "yes" }, + response: { + orders: [{ order_id: "1", amount: 'EUR:12', refundable: true } as MerchantBackend.Orders.OrderHistoryEntry], + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, paid: "yes" }, + response: { orders: [], }, + }); + + + const { result, waitForNextUpdate } = renderHook(() => { + const newDate = (d: Date) => { + console.log("new date", d); + }; + const query = useInstanceOrders({ paid: "yes" }, newDate); + const api = useOrderAPI(); + + return { query, api }; + }, { wrapper: TestingContext }); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [{ + order_id: "1", + amount: 'EUR:12', + refundable: true, + }], + }); + + env.addRequestExpectation(API_REFUND_ORDER_BY_ID('1'), { + request: { + reason: 'double pay', + refund: 'EUR:1' + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 0, paid: "yes" }, + response: { + orders: [{ order_id: "1", amount: 'EUR:12', refundable: false } as any], + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, paid: "yes" }, + response: { orders: [], }, + }); + + act(async () => { + await result.current?.api.refundOrder('1', { + reason: 'double pay', + refund: 'EUR:1' + }); + }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [{ + order_id: "1", + amount: 'EUR:12', + refundable: false, + }], + }); + }); + it("should evict cache when deleting an order", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 0, paid: "yes" }, + response: { + orders: [{ order_id: "1" } as MerchantBackend.Orders.OrderHistoryEntry], + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, paid: "yes" }, + response: { + orders: [{ order_id: "2" } as MerchantBackend.Orders.OrderHistoryEntry], + }, + }); + + + const { result, waitForNextUpdate } = renderHook(() => { + const newDate = (d: Date) => { + console.log("new date", d); + }; + const query = useInstanceOrders({ paid: "yes" }, newDate); + const api = useOrderAPI(); + + return { query, api }; + }, { wrapper: TestingContext }); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [{ order_id: "1" }, { order_id: "2" }], + }); + + env.addRequestExpectation(API_DELETE_ORDER('1'), {}); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 0, paid: "yes" }, + response: { + orders: [], + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, paid: "yes" }, + response: { + orders: [{ order_id: "2" } as any], + }, + }); + + act(async () => { + await result.current?.api.deleteOrder('1'); + }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [{ order_id: "2" }], + }); + }); + +}); + +describe("order api interaction with details", () => { + + it("should evict cache when doing a refund", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), { + // qparam: { delta: 0, paid: "yes" }, + response: { + summary: 'description', + refund_amount: 'EUR:0', + } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse, + }); + + const { result, waitForNextUpdate } = renderHook(() => { + const query = useOrderDetails('1') + const api = useOrderAPI(); + + return { query, api }; + }, { wrapper: TestingContext }); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + summary: 'description', + refund_amount: 'EUR:0', + }); + + env.addRequestExpectation(API_REFUND_ORDER_BY_ID('1'), { + request: { + reason: 'double pay', + refund: 'EUR:1' + }, + }); + + env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), { + response: { + summary: 'description', + refund_amount: 'EUR:1', + } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse, + }); + + act(async () => { + await result.current?.api.refundOrder('1', { + reason: 'double pay', + refund: 'EUR:1' + }); + }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + summary: 'description', + refund_amount: 'EUR:1', + }); + }) + it("should evict cache when doing a forget", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), { + // qparam: { delta: 0, paid: "yes" }, + response: { + summary: 'description', + refund_amount: 'EUR:0', + } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse, + }); + + const { result, waitForNextUpdate } = renderHook(() => { + const query = useOrderDetails('1') + const api = useOrderAPI(); + + return { query, api }; + }, { wrapper: TestingContext }); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + summary: 'description', + refund_amount: 'EUR:0', + }); + + env.addRequestExpectation(API_FORGET_ORDER_BY_ID('1'), { + request: { + fields: ['$.summary'] + }, + }); + + env.addRequestExpectation(API_GET_ORDER_BY_ID('1'), { + response: { + summary: undefined, + } as unknown as MerchantBackend.Orders.CheckPaymentPaidResponse, + }); + + act(async () => { + await result.current?.api.forgetOrder('1', { + fields: ['$.summary'] + }); + }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + summary: undefined, + }); + }) +}) + +describe("order listing pagination", () => { + + it("should not load more if has reach the end", async () => { + const env = new AxiosMockEnvironment(); + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 20, wired: "yes", date_ms: 12 }, + response: { + orders: [{ order_id: "1" } as any], + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, wired: "yes", date_ms: 13 }, + response: { + orders: [{ order_id: "2" } as any], + }, + }); + + + const { result, waitForNextUpdate } = renderHook(() => { + const newDate = (d: Date) => { + console.log("new date", d); + }; + const date = new Date(12); + const query = useInstanceOrders({ wired: "yes", date }, newDate) + return { query } + }, { wrapper: TestingContext }); + + assertJustExpectedRequestWereMade(env); + + await waitForNextUpdate(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [{ order_id: "1" }, { order_id: "2" }], + }); + + expect(result.current.query.isReachingEnd).toBeTruthy() + expect(result.current.query.isReachingStart).toBeTruthy() + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMore(); + }); + assertNoMoreRequestWereMade(env); + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMorePrev(); + }); + assertNoMoreRequestWereMade(env); + + expect(result.current.query.data).toEqual({ + orders: [ + { order_id: "1" }, + { order_id: "2" }, + ], + }); + }); + + it("should load more if result brings more that PAGE_SIZE", async () => { + const env = new AxiosMockEnvironment(); + + const ordersFrom0to20 = Array.from({ length: 20 }).map((e, i) => ({ order_id: String(i) })) + const ordersFrom20to40 = Array.from({ length: 20 }).map((e, i) => ({ order_id: String(i + 20) })) + const ordersFrom20to0 = [...ordersFrom0to20].reverse() + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 20, wired: "yes", date_ms: 12 }, + response: { + orders: ordersFrom0to20, + }, + }); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -20, wired: "yes", date_ms: 13 }, + response: { + orders: ordersFrom20to40, + }, + }); + + const { result, waitForNextUpdate } = renderHook(() => { + const newDate = (d: Date) => { + console.log("new date", d); + }; + const date = new Date(12); + const query = useInstanceOrders({ wired: "yes", date }, newDate) + return { query } + }, { wrapper: TestingContext }); + + assertJustExpectedRequestWereMade(env); + + await waitForNextUpdate({ timeout: 1 }); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + orders: [...ordersFrom20to0, ...ordersFrom20to40], + }); + + expect(result.current.query.isReachingEnd).toBeFalsy() + expect(result.current.query.isReachingStart).toBeFalsy() + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: -40, wired: "yes", date_ms: 13 }, + response: { + orders: [...ordersFrom20to40, { order_id: '41' }], + }, + }); + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMore(); + }); + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_ORDERS, { + qparam: { delta: 40, wired: "yes", date_ms: 12 }, + response: { + orders: [...ordersFrom0to20, { order_id: '-1' }], + }, + }); + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMorePrev(); + }); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.data).toEqual({ + orders: [{ order_id: '-1' }, ...ordersFrom20to0, ...ordersFrom20to40, { order_id: '41' }], + }); + }); + + +}); diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/product.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/product.test.ts new file mode 100644 index 000000000..5d39a7c47 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/swr/product.test.ts @@ -0,0 +1,338 @@ +/* + 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 { renderHook } from "@testing-library/preact-hooks"; +import { act } from "preact/test-utils"; +import { TestingContext } from "."; +import { MerchantBackend } from "../../../src/declaration"; +import { useInstanceProducts, useProductAPI, useProductDetails } from "../../../src/hooks/product"; +import { + API_CREATE_PRODUCT, + API_DELETE_PRODUCT, API_GET_PRODUCT_BY_ID, + API_LIST_PRODUCTS, + API_UPDATE_PRODUCT_BY_ID, + assertJustExpectedRequestWereMade, + assertNextRequest, + AxiosMockEnvironment +} from "../../axiosMock"; + +describe("product api interaction with listing ", () => { + it("should evict cache when creating a product", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_PRODUCTS, { + response: { + products: [{ product_id: "1234" }], + }, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), { + response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const query = useInstanceProducts(); + const api = useProductAPI(); + return { api, query }; + }, + { wrapper: TestingContext } + ); // get products -> loading + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual([ + { id: "1234", price: "ARS:12" }, + ]); + + env.addRequestExpectation(API_CREATE_PRODUCT, { + request: { price: "ARS:23" } as MerchantBackend.Products.ProductAddDetail, + }); + + env.addRequestExpectation(API_LIST_PRODUCTS, { + response: { + products: [{ product_id: "1234" }, { product_id: "2345" }], + }, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), { + response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), { + response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), { + response: { price: "ARS:23" } as MerchantBackend.Products.ProductDetail, + }); + + act(async () => { + await result.current?.api.createProduct({ + price: "ARS:23", + } as any); + }); + + assertNextRequest(env); + await waitForNextUpdate({ timeout: 1 }); + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual([ + { + id: "1234", + price: "ARS:12", + }, + { + id: "2345", + price: "ARS:23", + }, + ]); + }); + + it("should evict cache when updating a product", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_PRODUCTS, { + response: { + products: [{ product_id: "1234" }], + }, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), { + response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const query = useInstanceProducts(); + const api = useProductAPI(); + return { api, query }; + }, + { wrapper: TestingContext } + ); // get products -> loading + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual([ + { id: "1234", price: "ARS:12" }, + ]); + + env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("1234"), { + request: { price: "ARS:13" } as MerchantBackend.Products.ProductPatchDetail, + }); + + env.addRequestExpectation(API_LIST_PRODUCTS, { + response: { + products: [{ product_id: "1234" }], + }, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), { + response: { price: "ARS:13" } as MerchantBackend.Products.ProductDetail, + }); + + act(async () => { + await result.current?.api.updateProduct("1234", { + price: "ARS:13", + } as any); + }); + + assertNextRequest(env); + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual([ + { + id: "1234", + price: "ARS:13", + }, + ]); + }); + + it("should evict cache when deleting a product", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_PRODUCTS, { + response: { + products: [{ product_id: "1234" }, { product_id: "2345" }], + }, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), { + response: { price: "ARS:12" } as MerchantBackend.Products.ProductDetail, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("2345"), { + response: { price: "ARS:23" } as MerchantBackend.Products.ProductDetail, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const query = useInstanceProducts(); + const api = useProductAPI(); + return { api, query }; + }, + { wrapper: TestingContext } + ); // get products -> loading + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + + await waitForNextUpdate({ timeout: 1 }); + // await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual([ + { id: "1234", price: "ARS:12" }, + { id: "2345", price: "ARS:23" }, + ]); + + env.addRequestExpectation(API_DELETE_PRODUCT("2345"), {}); + + env.addRequestExpectation(API_LIST_PRODUCTS, { + response: { + products: [{ product_id: "1234" }], + }, + }); + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("1234"), { + response: { price: "ARS:13" } as MerchantBackend.Products.ProductDetail, + }); + + act(async () => { + await result.current?.api.deleteProduct("2345"); + }); + + assertNextRequest(env); + await waitForNextUpdate({ timeout: 1 }); + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual([ + { + id: "1234", + price: "ARS:13", + }, + ]); + }); + +}); + +describe("product api interaction with details", () => { + it("should evict cache when updating a product", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("12"), { + response: { + description: "this is a description", + } as MerchantBackend.Products.ProductDetail, + }); + + const { result, waitForNextUpdate } = renderHook(() => { + const query = useProductDetails("12"); + const api = useProductAPI(); + return { query, api }; + }, { wrapper: TestingContext }); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate(); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + description: "this is a description", + }); + + env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("12"), { + request: { description: "other description" } as MerchantBackend.Products.ProductPatchDetail, + }); + + env.addRequestExpectation(API_GET_PRODUCT_BY_ID("12"), { + response: { + description: "other description", + } as MerchantBackend.Products.ProductDetail, + }); + + act(async () => { + return await result.current?.api.updateProduct("12", { + description: "other description", + } as any); + }); + + assertNextRequest(env); + await waitForNextUpdate(); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + description: "other description", + }); + }) +})
\ No newline at end of file diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/reserve.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/reserve.test.ts new file mode 100644 index 000000000..0361c54e8 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/swr/reserve.test.ts @@ -0,0 +1,470 @@ +/* + 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 { renderHook } from "@testing-library/preact-hooks"; +import { act } from "preact/test-utils"; +import { MerchantBackend } from "../../../src/declaration"; +import { + useInstanceReserves, + useReserveDetails, + useReservesAPI, + useTipDetails, +} from "../../../src/hooks/reserves"; +import { + API_AUTHORIZE_TIP, + API_AUTHORIZE_TIP_FOR_RESERVE, + API_CREATE_RESERVE, + API_DELETE_RESERVE, + API_GET_RESERVE_BY_ID, + API_GET_TIP_BY_ID, + API_LIST_RESERVES, + assertJustExpectedRequestWereMade, + AxiosMockEnvironment, +} from "../../axiosMock"; +import { TestingContext } from "./index"; + +describe("reserve api interaction with listing ", () => { + it("should evict cache when creating a reserve", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_RESERVES, { + response: { + reserves: [ + { + reserve_pub: "11", + } as MerchantBackend.Tips.ReserveStatusEntry, + ], + }, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useReservesAPI(); + const query = useInstanceReserves(); + + return { query, api }; + }, + { wrapper: TestingContext } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + reserves: [{ reserve_pub: "11" }], + }); + + env.addRequestExpectation(API_CREATE_RESERVE, { + request: { + initial_balance: "ARS:3333", + exchange_url: "http://url", + wire_method: "iban", + }, + response: { + reserve_pub: "22", + payto_uri: "payto", + }, + }); + + act(async () => { + await result.current?.api.createReserve({ + initial_balance: "ARS:3333", + exchange_url: "http://url", + wire_method: "iban", + }); + return; + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_RESERVES, { + response: { + reserves: [ + { + reserve_pub: "11", + } as MerchantBackend.Tips.ReserveStatusEntry, + { + reserve_pub: "22", + } as MerchantBackend.Tips.ReserveStatusEntry, + ], + }, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + reserves: [ + { + reserve_pub: "11", + } as MerchantBackend.Tips.ReserveStatusEntry, + { + reserve_pub: "22", + } as MerchantBackend.Tips.ReserveStatusEntry, + ], + }); + }); + + it("should evict cache when deleting a reserve", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_RESERVES, { + response: { + reserves: [ + { + reserve_pub: "11", + } as MerchantBackend.Tips.ReserveStatusEntry, + { + reserve_pub: "22", + } as MerchantBackend.Tips.ReserveStatusEntry, + { + reserve_pub: "33", + } as MerchantBackend.Tips.ReserveStatusEntry, + ], + }, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useReservesAPI(); + const query = useInstanceReserves(); + + return { query, api }; + }, + { + wrapper: TestingContext, + } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + reserves: [ + { reserve_pub: "11" }, + { reserve_pub: "22" }, + { reserve_pub: "33" }, + ], + }); + + env.addRequestExpectation(API_DELETE_RESERVE("11"), {}); + + act(async () => { + await result.current?.api.deleteReserve("11"); + return; + }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_RESERVES, { + response: { + reserves: [ + { + reserve_pub: "22", + } as MerchantBackend.Tips.ReserveStatusEntry, + { + reserve_pub: "33", + } as MerchantBackend.Tips.ReserveStatusEntry, + ], + }, + }); + + expect(result.current.query.loading).toBeFalsy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + reserves: [ + { + reserve_pub: "22", + } as MerchantBackend.Tips.ReserveStatusEntry, + { + reserve_pub: "33", + } as MerchantBackend.Tips.ReserveStatusEntry, + ], + }); + }); +}); + +describe("reserve api interaction with details", () => { + it("should evict cache when adding a tip for a specific reserve", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), { + response: { + payto_uri: "payto://here", + tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }], + } as MerchantBackend.Tips.ReserveDetail, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useReservesAPI(); + const query = useReserveDetails("11"); + + return { query, api }; + }, + { + wrapper: TestingContext, + } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + payto_uri: "payto://here", + tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }], + }); + + env.addRequestExpectation(API_AUTHORIZE_TIP_FOR_RESERVE("11"), { + request: { + amount: "USD:12", + justification: "not", + next_url: "http://taler.net", + }, + response: { + tip_id: "id2", + taler_tip_uri: "uri", + tip_expiration: { t_s: 1 }, + tip_status_url: "url", + }, + }); + + act(async () => { + await result.current?.api.authorizeTipReserve("11", { + amount: "USD:12", + justification: "not", + next_url: "http://taler.net", + }); + }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), { + response: { + payto_uri: "payto://here", + tips: [ + { reason: "why?", tip_id: "id1", total_amount: "USD:10" }, + { reason: "not", tip_id: "id2", total_amount: "USD:12" }, + ], + } as MerchantBackend.Tips.ReserveDetail, + }); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + payto_uri: "payto://here", + tips: [ + { reason: "why?", tip_id: "id1", total_amount: "USD:10" }, + { reason: "not", tip_id: "id2", total_amount: "USD:12" }, + ], + }); + }); + + it("should evict cache when adding a tip for a random reserve", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), { + response: { + payto_uri: "payto://here", + tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }], + } as MerchantBackend.Tips.ReserveDetail, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + const api = useReservesAPI(); + const query = useReserveDetails("11"); + + return { query, api }; + }, + { + wrapper: TestingContext, + } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + payto_uri: "payto://here", + tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }], + }); + + env.addRequestExpectation(API_AUTHORIZE_TIP, { + request: { + amount: "USD:12", + justification: "not", + next_url: "http://taler.net", + }, + response: { + tip_id: "id2", + taler_tip_uri: "uri", + tip_expiration: { t_s: 1 }, + tip_status_url: "url", + }, + }); + + act(async () => { + await result.current?.api.authorizeTip({ + amount: "USD:12", + justification: "not", + next_url: "http://taler.net", + }); + }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + + env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), { + response: { + payto_uri: "payto://here", + tips: [ + { reason: "why?", tip_id: "id1", total_amount: "USD:10" }, + { reason: "not", tip_id: "id2", total_amount: "USD:12" }, + ], + } as MerchantBackend.Tips.ReserveDetail, + }); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + + expect(result.current.query.data).toEqual({ + payto_uri: "payto://here", + tips: [ + { reason: "why?", tip_id: "id1", total_amount: "USD:10" }, + { reason: "not", tip_id: "id2", total_amount: "USD:12" }, + ], + }); + }); +}); + +describe("reserve api interaction with tip details", () => { + it("should list tips", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_GET_TIP_BY_ID("11"), { + response: { + total_picked_up: "USD:12", + reason: "not", + } as MerchantBackend.Tips.TipDetails, + }); + + const { result, waitForNextUpdate } = renderHook( + () => { + // const api = useReservesAPI(); + const query = useTipDetails("11"); + + return { query }; + }, + { + wrapper: TestingContext, + } + ); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + expect(result.current.query.loading).toBeTruthy(); + + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + total_picked_up: "USD:12", + reason: "not", + }); + }); +}); diff --git a/packages/merchant-backoffice-ui/tests/hooks/swr/transfer.test.ts b/packages/merchant-backoffice-ui/tests/hooks/swr/transfer.test.ts new file mode 100644 index 000000000..612cf8842 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/hooks/swr/transfer.test.ts @@ -0,0 +1,268 @@ +/* + 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 { act, renderHook } from "@testing-library/preact-hooks"; +import { TestingContext } from "./index"; +import { useInstanceTransfers, useTransferAPI } from "../../../src/hooks/transfer"; +import { + API_INFORM_TRANSFERS, + API_LIST_TRANSFERS, + assertJustExpectedRequestWereMade, + assertNoMoreRequestWereMade, + AxiosMockEnvironment, +} from "../../axiosMock"; +import { MerchantBackend } from "../../../src/declaration"; + +describe("transfer api interaction with listing", () => { + + it("should evict cache when informing a transfer", async () => { + const env = new AxiosMockEnvironment(); + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: 0 }, + response: { + transfers: [{ wtid: "2" } as MerchantBackend.Transfers.TransferDetails], + }, + }); + // FIXME: is this query really needed? if the hook is rendered without + // position argument then then backend is returning the newest and no need + // to this second query + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: -20 }, + response: { + transfers: [], + }, + }); + + const { result, waitForNextUpdate } = renderHook(() => { + const moveCursor = (d: string) => { + console.log("new position", d); + }; + const query = useInstanceTransfers({}, moveCursor); + const api = useTransferAPI(); + + return { query, api }; + }, { wrapper: TestingContext }); + + if (!result.current) { + expect(result.current).toBeDefined(); + return; + } + + expect(result.current.query.loading).toBeTruthy(); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + if (!result.current.query.ok) return; + + expect(result.current.query.data).toEqual({ + transfers: [{ wtid: "2" }], + }); + + env.addRequestExpectation(API_INFORM_TRANSFERS, { + request: { + wtid: '3', + credit_amount: 'EUR:1', + exchange_url: 'exchange.url', + payto_uri: 'payto://' + }, + response: { total: '' } as any, + }); + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: 0 }, + response: { + transfers: [{ wtid: "2" } as any, { wtid: "3" } as any], + }, + }); + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: -20 }, + response: { + transfers: [], + }, + }); + + act(async () => { + await result.current?.api.informTransfer({ + wtid: '3', + credit_amount: 'EUR:1', + exchange_url: 'exchange.url', + payto_uri: 'payto://' + }); + }); + + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.loading).toBeFalsy(); + expect(result.current.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + transfers: [{ wtid: "3" }, { wtid: "2" }], + }); + }); + +}); + +describe("transfer listing pagination", () => { + + it("should not load more if has reach the end", async () => { + const env = new AxiosMockEnvironment(); + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: 0, payto_uri: 'payto://' }, + response: { + transfers: [{ wtid: "2" } as any], + }, + }); + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: -20, payto_uri: 'payto://' }, + response: { + transfers: [{ wtid: "1" } as any], + }, + }); + + + const { result, waitForNextUpdate } = renderHook(() => { + const moveCursor = (d: string) => { + console.log("new position", d); + }; + const query = useInstanceTransfers({ payto_uri: 'payto://' }, moveCursor) + return { query } + }, { wrapper: TestingContext }); + + assertJustExpectedRequestWereMade(env); + + await waitForNextUpdate(); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + transfers: [{ wtid: "2" }, { wtid: "1" }], + }); + + expect(result.current.query.isReachingEnd).toBeTruthy() + expect(result.current.query.isReachingStart).toBeTruthy() + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMore(); + }); + assertNoMoreRequestWereMade(env); + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMorePrev(); + }); + assertNoMoreRequestWereMade(env); + + expect(result.current.query.data).toEqual({ + transfers: [ + { wtid: "2" }, + { wtid: "1" }, + ], + }); + }); + + it("should load more if result brings more that PAGE_SIZE", async () => { + const env = new AxiosMockEnvironment(); + + const transfersFrom0to20 = Array.from({ length: 20 }).map((e, i) => ({ wtid: String(i) })) + const transfersFrom20to40 = Array.from({ length: 20 }).map((e, i) => ({ wtid: String(i + 20) })) + const transfersFrom20to0 = [...transfersFrom0to20].reverse() + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: 20, payto_uri: 'payto://' }, + response: { + transfers: transfersFrom0to20, + }, + }); + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: -20, payto_uri: 'payto://' }, + response: { + transfers: transfersFrom20to40, + }, + }); + + const { result, waitForNextUpdate } = renderHook(() => { + const moveCursor = (d: string) => { + console.log("new position", d); + }; + const query = useInstanceTransfers({ payto_uri: 'payto://', position: '1' }, moveCursor) + return { query } + }, { wrapper: TestingContext }); + + assertJustExpectedRequestWereMade(env); + + await waitForNextUpdate({ timeout: 1 }); + + expect(result.current?.query.ok).toBeTruthy(); + if (!result.current?.query.ok) return; + + expect(result.current.query.data).toEqual({ + transfers: [...transfersFrom20to0, ...transfersFrom20to40], + }); + + expect(result.current.query.isReachingEnd).toBeFalsy() + expect(result.current.query.isReachingStart).toBeFalsy() + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: -40, payto_uri: 'payto://', offset: "1" }, + response: { + transfers: [...transfersFrom20to40, { wtid: '41' }], + }, + }); + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMore(); + }); + await waitForNextUpdate({ timeout: 1 }); + + assertJustExpectedRequestWereMade(env); + + env.addRequestExpectation(API_LIST_TRANSFERS, { + qparam: { limit: 40, payto_uri: 'payto://', offset: "1" }, + response: { + transfers: [...transfersFrom0to20, { wtid: '-1' }], + }, + }); + + await act(() => { + if (!result.current?.query.ok) throw Error("not ok"); + result.current.query.loadMorePrev(); + }); + await waitForNextUpdate({ timeout: 1 }); + assertJustExpectedRequestWereMade(env); + + expect(result.current.query.data).toEqual({ + transfers: [{ wtid: '-1' }, ...transfersFrom20to0, ...transfersFrom20to40, { wtid: '41' }], + }); + }); + + +}); diff --git a/packages/merchant-backoffice-ui/tests/stories.test.tsx b/packages/merchant-backoffice-ui/tests/stories.test.tsx new file mode 100644 index 000000000..5fb3483d2 --- /dev/null +++ b/packages/merchant-backoffice-ui/tests/stories.test.tsx @@ -0,0 +1,89 @@ +/* + 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 * as config from "../src/context/config"; +import * as i18n from "../src/context/translation"; +import { cleanup, render as originalRender } from "@testing-library/preact"; +import { SWRConfig } from "swr"; + +import fs from "fs"; + +function getFiles(dir: string, files_: string[] = []) { + const files = fs.readdirSync(dir); + for (const i in files) { + const name = dir + "/" + files[i]; + if (fs.statSync(name).isDirectory()) { + getFiles(name, files_); + } else { + files_.push(name); + } + } + return files_; +} + +const STORIES_NAME_REGEX = RegExp(".*.stories.tsx"); + +function render(vnode: VNode) { + return originalRender( + <SWRConfig + value={{ + provider: () => new Map(), + }} + > + {vnode} + </SWRConfig> + ); +} + +import * as jedLib from "jed"; +const handler = new jedLib.Jed("en"); + +describe("storybook testing", () => { + it("render every story", () => { + jest + .spyOn(config, "useConfigContext") + .mockImplementation(() => ({ version: "1.0.0", currency: "EUR" })); + jest.spyOn(i18n, "useTranslationContext").mockImplementation(() => ({ + changeLanguage: () => null, + handler, + lang: "en", + })); + + getFiles("./src") + .filter((f) => STORIES_NAME_REGEX.test(f)) + .map((f) => { + // const f = "./src/paths/instance/transfers/list/List.stories.tsx"; + // eslint-disable-next-line @typescript-eslint/no-var-requires + const s = require(`../${f}`); + + delete s.default; + Object.keys(s).forEach((k) => { + const Component = s[k]; + const vdom = <Component {...Component.args} />; + expect(() => { + const { unmount } = render(vdom); + unmount(); + }).not.toThrow(); //`problem rendering ${f} example ${k}` + cleanup(); + }); + }); + }); +}); diff --git a/packages/merchant-backoffice-ui/tsconfig.json b/packages/merchant-backoffice-ui/tsconfig.json new file mode 100644 index 000000000..dfb87582a --- /dev/null +++ b/packages/merchant-backoffice-ui/tsconfig.json @@ -0,0 +1,61 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ + "module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation: */ + "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */ + "jsxFragmentFactory": "Fragment", // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#custom-jsx-factories + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "removeComments": true, /* Do not emit comments to output. */ + "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "esModuleInterop": true, /* */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */ + }, + "include": ["src/**/*", "tests/**/*"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e506d65f..6aa8ce202 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,115 @@ importers: typescript: 4.8.4 ws: 7.4.5 + packages/demobank-ui: + specifiers: + '@babel/core': ^7.13.16 + '@babel/plugin-transform-react-jsx': ^7.12.13 + '@babel/plugin-transform-react-jsx-source': ^7.12.13 + '@babel/preset-env': ^7.16.7 + '@creativebulma/bulma-tooltip': ^1.2.0 + '@gnu-taler/pogen': ^0.0.5 + '@storybook/addon-a11y': 6.2.9 + '@storybook/addon-actions': 6.2.9 + '@storybook/addon-essentials': 6.2.9 + '@storybook/addon-links': 6.2.9 + '@storybook/preact': 6.2.9 + '@storybook/preset-scss': ^1.0.3 + '@testing-library/jest-dom': ^5.16.1 + '@testing-library/preact': ^2.0.1 + '@testing-library/preact-hooks': ^1.1.0 + '@types/enzyme': ^3.10.10 + '@types/jest': ^27.0.2 + '@typescript-eslint/eslint-plugin': ^5.3.0 + '@typescript-eslint/parser': ^5.3.0 + babel-loader: ^8.2.2 + base64-inline-loader: 1.1.1 + bulma: ^0.9.3 + bulma-checkbox: ^1.1.1 + bulma-radio: ^1.1.1 + date-fns: 2.25.0 + enzyme: ^3.11.0 + enzyme-adapter-preact-pure: ^3.2.0 + eslint: ^8.1.0 + eslint-config-preact: ^1.2.0 + html-webpack-inline-chunk-plugin: ^1.1.1 + html-webpack-inline-source-plugin: 0.0.10 + html-webpack-skip-assets-plugin: ^1.0.1 + inline-chunk-html-plugin: ^1.1.1 + jed: 1.1.1 + jest: ^27.3.1 + jest-environment-jsdom: ^27.4.6 + jest-fetch-mock: ^3.0.3 + jest-preset-preact: ^4.0.5 + jest-watch-typeahead: ^1.0.0 + jssha: ^3.2.0 + po2json: ^0.4.5 + preact: ^10.5.15 + preact-cli: 3.0.5 + preact-render-to-string: ^5.1.19 + preact-router: ^3.2.1 + qrcode-generator: ^1.4.4 + sass: 1.32.13 + sass-loader: ^10 + script-ext-html-webpack-plugin: ^2.1.5 + sirv-cli: ^1.0.14 + swr: '1.1' + typescript: ^4.4.4 + dependencies: + base64-inline-loader: 1.1.1 + date-fns: 2.25.0 + jed: 1.1.1 + preact: 10.6.5 + preact-render-to-string: 5.1.19_preact@10.6.5 + preact-router: 3.2.1_preact@10.6.5 + qrcode-generator: 1.4.4 + swr: 1.1.2 + devDependencies: + '@babel/core': 7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx-source': 7.14.5_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@creativebulma/bulma-tooltip': 1.2.0 + '@gnu-taler/pogen': link:../pogen + '@storybook/addon-a11y': 6.2.9 + '@storybook/addon-actions': 6.2.9 + '@storybook/addon-essentials': 6.2.9_dtgd2mm6ybnh5irau5bfapxhdy + '@storybook/addon-links': 6.2.9 + '@storybook/preact': 6.2.9_lmcptaky2e4dapkymvnphph734 + '@storybook/preset-scss': 1.0.3_sass-loader@10.3.1 + '@testing-library/jest-dom': 5.16.5 + '@testing-library/preact': 2.0.1_preact@10.6.5 + '@testing-library/preact-hooks': 1.1.0_vfcmu6iy7nffpurikpgxo6gwxi + '@types/enzyme': 3.10.12 + '@types/jest': 27.5.2 + '@typescript-eslint/eslint-plugin': 5.36.1_gjcw3hhr2cxnngiu5lw4bi633m + '@typescript-eslint/parser': 5.36.1_o2nrgn6wwxunlqlzzokx4es3q4 + babel-loader: 8.2.3_@babel+core@7.17.2 + bulma: 0.9.3 + bulma-checkbox: 1.2.1 + bulma-radio: 1.2.0 + enzyme: 3.11.0 + enzyme-adapter-preact-pure: 3.4.0_fh4cerfcdrs5uit63qwkqtrfyi + eslint: 8.8.0 + eslint-config-preact: 1.3.0_w4k36q7phb5aratcwbohw6kmxe + html-webpack-inline-chunk-plugin: 1.1.1 + html-webpack-inline-source-plugin: 0.0.10 + html-webpack-skip-assets-plugin: 1.0.3 + inline-chunk-html-plugin: 1.1.1 + jest: 27.5.1 + jest-environment-jsdom: 27.5.1 + jest-fetch-mock: 3.0.3 + jest-preset-preact: 4.0.5_e2p4bsvs32uygapo46tobni7si + jest-watch-typeahead: 1.1.0_jest@27.5.1 + jssha: 3.2.0 + po2json: 0.4.5 + preact-cli: 3.0.5_ka7slmjrdyz4arupkjrkg4jzfm + sass: 1.32.13 + sass-loader: 10.3.1_sass@1.32.13 + script-ext-html-webpack-plugin: 2.1.5 + sirv-cli: 1.0.14 + typescript: 4.8.4 + packages/idb-bridge: specifiers: '@types/node': ^18.8.5 @@ -109,6 +218,290 @@ importers: rimraf: 3.0.2 typescript: 4.8.4 + packages/merchant-backend-ui: + specifiers: + '@babel/core': ^7.13.16 + '@babel/plugin-transform-react-jsx-source': ^7.12.13 + '@creativebulma/bulma-tooltip': ^1.2.0 + '@gnu-taler/pogen': ^0.0.5 + '@gnu-taler/taler-util': workspace:* + '@linaria/babel-preset': ^3.0.0-beta.4 + '@linaria/core': ^3.0.0-beta.4 + '@linaria/react': ^3.0.0-beta.7 + '@linaria/rollup': ^3.0.0-beta.7 + '@linaria/shaker': ^3.0.0-beta.7 + '@linaria/webpack-loader': ^3.0.0-beta.7 + '@rollup/plugin-alias': ^3.1.5 + '@rollup/plugin-babel': ^5.3.0 + '@rollup/plugin-commonjs': ^20.0.0 + '@rollup/plugin-html': ^0.2.3 + '@rollup/plugin-image': ^2.1.1 + '@rollup/plugin-json': ^4.1.0 + '@rollup/plugin-replace': ^3.0.0 + '@rollup/plugin-typescript': ^8.2.5 + '@storybook/addon-a11y': ^6.2.9 + '@storybook/addon-actions': ^6.2.9 + '@storybook/addon-essentials': ^6.2.9 + '@storybook/addon-links': ^6.2.9 + '@storybook/preact': ^6.2.9 + '@storybook/preset-scss': ^1.0.3 + '@testing-library/preact': ^2.0.1 + '@testing-library/preact-hooks': ^1.1.0 + '@types/enzyme': ^3.10.8 + '@types/history': ^4.7.8 + '@types/jest': ^26.0.23 + '@types/mocha': ^8.2.2 + '@types/mustache': ^4.1.2 + '@typescript-eslint/eslint-plugin': ^4.22.0 + '@typescript-eslint/parser': ^4.22.0 + axios: ^0.21.1 + babel-loader: ^8.2.2 + base64-inline-loader: ^1.1.1 + bulma: ^0.9.2 + bulma-checkbox: ^1.1.1 + bulma-radio: ^1.1.1 + bulma-responsive-tables: ^1.2.3 + bulma-switch-control: ^1.1.1 + bulma-timeline: ^3.0.4 + bulma-upload-control: ^1.2.0 + date-fns: ^2.21.1 + dotenv: ^8.2.0 + enzyme: ^3.11.0 + enzyme-adapter-preact-pure: ^3.1.0 + eslint: ^7.25.0 + eslint-config-preact: ^1.1.4 + eslint-plugin-header: ^3.1.1 + history: 4.10.1 + html-webpack-inline-chunk-plugin: ^1.1.1 + html-webpack-inline-source-plugin: 0.0.10 + html-webpack-skip-assets-plugin: ^1.0.1 + inline-chunk-html-plugin: ^1.1.1 + jed: ^1.1.1 + jest: ^26.6.3 + jest-preset-preact: ^4.0.2 + mustache: ^4.2.0 + po2json: ^0.4.5 + preact: ^10.5.13 + preact-cli: ^3.0.5 + preact-render-to-json: ^3.6.6 + preact-render-to-string: ^5.1.19 + preact-router: ^3.2.1 + qrcode-generator: ^1.4.4 + rimraf: ^3.0.2 + rollup: ^2.56.3 + rollup-plugin-bundle-html: ^0.2.2 + rollup-plugin-css-only: ^3.1.0 + sass: ^1.32.13 + sass-loader: 10.1.1 + script-ext-html-webpack-plugin: ^2.1.5 + sirv-cli: ^1.0.11 + swr: ^0.5.5 + tslib: ^2.3.1 + typedoc: ^0.20.36 + typescript: ^4.2.4 + yup: ^0.32.9 + dependencies: + '@gnu-taler/taler-util': link:../taler-util + axios: 0.21.4 + date-fns: 2.29.2 + history: 4.10.1 + jed: 1.1.1 + preact: 10.6.5 + preact-router: 3.2.1_preact@10.6.5 + qrcode-generator: 1.4.4 + swr: 0.5.7 + yup: 0.32.11 + devDependencies: + '@babel/core': 7.17.2 + '@babel/plugin-transform-react-jsx-source': 7.14.5_@babel+core@7.17.2 + '@creativebulma/bulma-tooltip': 1.2.0 + '@gnu-taler/pogen': link:../pogen + '@linaria/babel-preset': 3.0.0-beta.15_@babel+core@7.17.2 + '@linaria/core': 3.0.0-beta.15 + '@linaria/react': 3.0.0-beta.22 + '@linaria/rollup': 3.0.0-beta.23 + '@linaria/shaker': 3.0.0-beta.15_@babel+core@7.17.2 + '@linaria/webpack-loader': 3.0.0-beta.23 + '@rollup/plugin-alias': 3.1.9_rollup@2.79.0 + '@rollup/plugin-babel': 5.3.0_lubkdqoa5gexe4rox23cswxwm4 + '@rollup/plugin-commonjs': 20.0.0_rollup@2.79.0 + '@rollup/plugin-html': 0.2.4_rollup@2.79.0 + '@rollup/plugin-image': 2.1.1_rollup@2.79.0 + '@rollup/plugin-json': 4.1.0_rollup@2.79.0 + '@rollup/plugin-replace': 3.1.0_rollup@2.79.0 + '@rollup/plugin-typescript': 8.5.0_jnsxykt6ocebvgsxrxe2hsbo6y + '@storybook/addon-a11y': 6.5.13 + '@storybook/addon-actions': 6.5.13 + '@storybook/addon-essentials': 6.5.13_t6isvk4c5wetct53pn5ufojx2u + '@storybook/addon-links': 6.5.13 + '@storybook/preact': 6.5.13_rv7hwuimljxvhghl3f4jocbf4q + '@storybook/preset-scss': 1.0.3_sass-loader@10.1.1 + '@testing-library/preact': 2.0.1_preact@10.6.5 + '@testing-library/preact-hooks': 1.1.0_vfcmu6iy7nffpurikpgxo6gwxi + '@types/enzyme': 3.10.12 + '@types/history': 4.7.9 + '@types/jest': 26.0.24 + '@types/mocha': 8.2.3 + '@types/mustache': 4.2.1 + '@typescript-eslint/eslint-plugin': 4.33.0_k4l66av2tbo6kxzw52jzgbfzii + '@typescript-eslint/parser': 4.33.0_3rubbgt5ekhqrcgx4uwls3neim + babel-loader: 8.2.3_@babel+core@7.17.2 + base64-inline-loader: 1.1.1 + bulma: 0.9.3 + bulma-checkbox: 1.2.1 + bulma-radio: 1.2.0 + bulma-responsive-tables: 1.2.5 + bulma-switch-control: 1.2.2 + bulma-timeline: 3.0.5 + bulma-upload-control: 1.2.0 + dotenv: 8.6.0 + enzyme: 3.11.0 + enzyme-adapter-preact-pure: 3.4.0_fh4cerfcdrs5uit63qwkqtrfyi + eslint: 7.32.0 + eslint-config-preact: 1.3.0_nxlzr75jbqkso2fds5zjovs2ii + eslint-plugin-header: 3.1.1_eslint@7.32.0 + html-webpack-inline-chunk-plugin: 1.1.1 + html-webpack-inline-source-plugin: 0.0.10 + html-webpack-skip-assets-plugin: 1.0.3 + inline-chunk-html-plugin: 1.1.1 + jest: 26.6.3 + jest-preset-preact: 4.0.5_ewndwes2ovzexxcig72sj2dfke + mustache: 4.2.0 + po2json: 0.4.5 + preact-cli: 3.3.5_p6ocopta3zc4zccdbpyllaobd4 + preact-render-to-json: 3.6.6_preact@10.6.5 + preact-render-to-string: 5.1.19_preact@10.6.5 + rimraf: 3.0.2 + rollup: 2.79.0 + rollup-plugin-bundle-html: 0.2.2 + rollup-plugin-css-only: 3.1.0_rollup@2.79.0 + sass: 1.32.13 + sass-loader: 10.1.1_sass@1.32.13 + script-ext-html-webpack-plugin: 2.1.5 + sirv-cli: 1.0.14 + tslib: 2.4.0 + typedoc: 0.20.37_typescript@4.8.4 + typescript: 4.8.4 + + packages/merchant-backoffice-ui: + specifiers: + '@babel/core': ^7.13.16 + '@babel/plugin-transform-react-jsx-source': ^7.12.13 + '@creativebulma/bulma-tooltip': ^1.2.0 + '@gnu-taler/pogen': ^0.0.5 + '@gnu-taler/taler-util': workspace:* + '@storybook/addon-a11y': ^6.2.9 + '@storybook/addon-actions': ^6.2.9 + '@storybook/addon-essentials': ^6.2.9 + '@storybook/addon-links': ^6.2.9 + '@storybook/preact': ^6.2.9 + '@storybook/preset-scss': ^1.0.3 + '@testing-library/preact': ^2.0.1 + '@testing-library/preact-hooks': ^1.1.0 + '@types/history': ^4.7.8 + '@types/jest': ^26.0.23 + '@types/mocha': ^8.2.2 + '@typescript-eslint/eslint-plugin': ^4.22.0 + '@typescript-eslint/parser': ^4.22.0 + axios: ^0.21.1 + babel-loader: ^8.2.2 + base64-inline-loader: ^1.1.1 + bulma: ^0.9.2 + bulma-checkbox: ^1.1.1 + bulma-radio: ^1.1.1 + bulma-responsive-tables: ^1.2.3 + bulma-switch-control: ^1.1.1 + bulma-timeline: ^3.0.4 + bulma-upload-control: ^1.2.0 + date-fns: ^2.21.1 + dotenv: ^8.2.0 + eslint: ^7.25.0 + eslint-config-preact: ^1.1.4 + eslint-plugin-header: ^3.1.1 + history: 4.10.1 + html-webpack-inline-chunk-plugin: ^1.1.1 + html-webpack-inline-source-plugin: 0.0.10 + html-webpack-skip-assets-plugin: ^1.0.1 + inline-chunk-html-plugin: ^1.1.1 + jed: ^1.1.1 + jest: ^26.6.3 + jest-preset-preact: ^4.0.2 + po2json: ^0.4.5 + preact: 10.6.1 + preact-cli: ^3.0.5 + preact-render-to-json: ^3.6.6 + preact-render-to-string: ^5.1.19 + preact-router: ^3.2.1 + qrcode-generator: ^1.4.4 + rimraf: ^3.0.2 + sass: ^1.32.13 + sass-loader: 10.1.1 + script-ext-html-webpack-plugin: ^2.1.5 + sirv-cli: ^1.0.11 + swr: 1.1.0 + typedoc: ^0.20.36 + typescript: ^4.2.4 + yup: ^0.32.9 + dependencies: + '@gnu-taler/taler-util': link:../taler-util + axios: 0.21.4 + date-fns: 2.29.2 + history: 4.10.1 + jed: 1.1.1 + preact: 10.6.1 + preact-router: 3.2.1_preact@10.6.1 + qrcode-generator: 1.4.4 + swr: 1.1.0 + yup: 0.32.11 + devDependencies: + '@babel/core': 7.17.2 + '@babel/plugin-transform-react-jsx-source': 7.14.5_@babel+core@7.17.2 + '@creativebulma/bulma-tooltip': 1.2.0 + '@gnu-taler/pogen': link:../pogen + '@storybook/addon-a11y': 6.5.13 + '@storybook/addon-actions': 6.5.13 + '@storybook/addon-essentials': 6.5.13_t6isvk4c5wetct53pn5ufojx2u + '@storybook/addon-links': 6.5.13 + '@storybook/preact': 6.5.13_pzutvqtg2sm6mgs5je6xwivarm + '@storybook/preset-scss': 1.0.3_sass-loader@10.1.1 + '@testing-library/preact': 2.0.1_preact@10.6.1 + '@testing-library/preact-hooks': 1.1.0_p7poi7nh2j5v3fg73wd3em3ise + '@types/history': 4.7.9 + '@types/jest': 26.0.24 + '@types/mocha': 8.2.3 + '@typescript-eslint/eslint-plugin': 4.33.0_k4l66av2tbo6kxzw52jzgbfzii + '@typescript-eslint/parser': 4.33.0_3rubbgt5ekhqrcgx4uwls3neim + babel-loader: 8.2.3_@babel+core@7.17.2 + base64-inline-loader: 1.1.1 + bulma: 0.9.3 + bulma-checkbox: 1.2.1 + bulma-radio: 1.2.0 + bulma-responsive-tables: 1.2.5 + bulma-switch-control: 1.2.2 + bulma-timeline: 3.0.5 + bulma-upload-control: 1.2.0 + dotenv: 8.6.0 + eslint: 7.32.0 + eslint-config-preact: 1.3.0_nxlzr75jbqkso2fds5zjovs2ii + eslint-plugin-header: 3.1.1_eslint@7.32.0 + html-webpack-inline-chunk-plugin: 1.1.1 + html-webpack-inline-source-plugin: 0.0.10 + html-webpack-skip-assets-plugin: 1.0.3 + inline-chunk-html-plugin: 1.1.1 + jest: 26.6.3 + jest-preset-preact: 4.0.5_5pwcttm7mk4uq46yrrfyt2sdyu + po2json: 0.4.5 + preact-cli: 3.3.5_4lrkxlnsyltikzdya2zqvxqmyq + preact-render-to-json: 3.6.6_preact@10.6.1 + preact-render-to-string: 5.1.19_preact@10.6.1 + rimraf: 3.0.2 + sass: 1.32.13 + sass-loader: 10.1.1_sass@1.32.13 + script-ext-html-webpack-plugin: 2.1.5 + sirv-cli: 1.0.14 + typedoc: 0.20.37_typescript@4.8.4 + typescript: 4.8.4 + packages/pogen: specifiers: '@types/node': ^18.8.5 @@ -361,11 +754,15 @@ importers: packages: + /@adobe/css-tools/4.0.1: + resolution: {integrity: sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==} + dev: true + /@ampproject/remapping/2.1.0: resolution: {integrity: sha512-d5RysTlJ7hmw5Tw4UxgxcY3lkMe92n8sXCcuLPAyIAHK6j8DefDwtGnVVDgOnv+RnEosulDJ9NPKQL27bDId0g==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.4 + '@jridgewell/trace-mapping': 0.3.13 dev: true /@apideck/better-ajv-errors/0.3.3_ajv@8.10.0: @@ -388,6 +785,18 @@ packages: execa: 5.1.1 dev: true + /@babel/code-frame/7.10.4: + resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/code-frame/7.12.11: + resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} + dependencies: + '@babel/highlight': 7.16.10 + dev: true + /@babel/code-frame/7.16.7: resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==} engines: {node: '>=6.9.0'} @@ -395,11 +804,47 @@ packages: '@babel/highlight': 7.16.10 dev: true + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + /@babel/compat-data/7.17.0: resolution: {integrity: sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==} engines: {node: '>=6.9.0'} dev: true + /@babel/compat-data/7.19.4: + resolution: {integrity: sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core/7.12.9: + resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.19.6 + '@babel/helper-module-transforms': 7.19.6 + '@babel/helpers': 7.19.4 + '@babel/parser': 7.19.6 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 + convert-source-map: 1.8.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.1 + lodash: 4.17.21 + resolve: 1.22.1 + semver: 5.7.1 + source-map: 0.5.7 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/core/7.13.16: resolution: {integrity: sha512-sXHpixBiWWFti0AV2Zq7avpTasr6sIAu7Y396c608541qAU2ui4a193m0KSQmfPSKFZLnQ3cvlKDOm3XkuXm3Q==} engines: {node: '>=6.9.0'} @@ -423,57 +868,85 @@ packages: - supports-color dev: true - /@babel/core/7.16.7: - resolution: {integrity: sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==} + /@babel/core/7.17.2: + resolution: {integrity: sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==} engines: {node: '>=6.9.0'} dependencies: + '@ampproject/remapping': 2.1.0 '@babel/code-frame': 7.16.7 - '@babel/generator': 7.17.0 - '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.16.7 + '@babel/generator': 7.18.2 + '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.17.2 '@babel/helper-module-transforms': 7.18.0 - '@babel/helpers': 7.16.7 - '@babel/parser': 7.17.0 + '@babel/helpers': 7.17.2 + '@babel/parser': 7.18.4 '@babel/template': 7.16.7 - '@babel/traverse': 7.16.7 - '@babel/types': 7.16.7 + '@babel/traverse': 7.18.2 + '@babel/types': 7.18.4 convert-source-map: 1.8.0 - debug: 4.3.3 + debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.0 semver: 6.3.0 - source-map: 0.5.7 transitivePeerDependencies: - supports-color dev: true - /@babel/core/7.17.2: - resolution: {integrity: sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==} + /@babel/core/7.19.6: + resolution: {integrity: sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.1.0 - '@babel/code-frame': 7.16.7 - '@babel/generator': 7.17.0 - '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.17.2 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helpers': 7.17.2 - '@babel/parser': 7.17.0 - '@babel/template': 7.16.7 - '@babel/traverse': 7.17.0 - '@babel/types': 7.17.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.19.6 + '@babel/helper-compilation-targets': 7.19.3_@babel+core@7.19.6 + '@babel/helper-module-transforms': 7.19.6 + '@babel/helpers': 7.19.4 + '@babel/parser': 7.19.6 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 convert-source-map: 1.8.0 - debug: 4.3.3 + debug: 4.3.4 gensync: 1.0.0-beta.2 - json5: 2.2.0 + json5: 2.2.1 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true + /@babel/eslint-parser/7.19.1_ifghgpypvdmamphfg2ieta34qe: + resolution: {integrity: sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==} + engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} + peerDependencies: + '@babel/core': '>=7.11.0' + eslint: ^7.5.0 || ^8.0.0 + dependencies: + '@babel/core': 7.17.2 + '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 + eslint: 7.32.0 + eslint-visitor-keys: 2.1.0 + semver: 6.3.0 + dev: true + + /@babel/eslint-parser/7.19.1_rakzipanemow5i3hc6etgvncsm: + resolution: {integrity: sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==} + engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} + peerDependencies: + '@babel/core': '>=7.11.0' + eslint: ^7.5.0 || ^8.0.0 + dependencies: + '@babel/core': 7.17.2 + '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 + eslint: 8.8.0 + eslint-visitor-keys: 2.1.0 + semver: 6.3.0 + dev: true + /@babel/generator/7.15.0: resolution: {integrity: sha512-eKl4XdMrbpYvuB505KTta4AV9g+wWzmVBW69tX0H2NwKVKd2YJbKgyK6M8j/rgLbmHOYJn6rUklV677nOyJrEQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.15.0 + '@babel/types': 7.18.4 jsesc: 2.5.2 source-map: 0.5.7 dev: true @@ -482,7 +955,7 @@ packages: resolution: {integrity: sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 jsesc: 2.5.2 source-map: 0.5.7 dev: true @@ -496,18 +969,27 @@ packages: jsesc: 2.5.2 dev: true - /@babel/helper-annotate-as-pure/7.14.5: - resolution: {integrity: sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==} + /@babel/generator/7.19.6: + resolution: {integrity: sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.19.4 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 dev: true /@babel/helper-annotate-as-pure/7.16.7: resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 + dev: true + + /@babel/helper-annotate-as-pure/7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.19.4 dev: true /@babel/helper-builder-binary-assignment-operator-visitor/7.16.7: @@ -531,32 +1013,58 @@ packages: semver: 6.3.0 dev: true - /@babel/helper-compilation-targets/7.16.7_@babel+core@7.16.7: + /@babel/helper-compilation-targets/7.16.7_@babel+core@7.17.2: resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/compat-data': 7.17.0 - '@babel/core': 7.16.7 + '@babel/core': 7.17.2 '@babel/helper-validator-option': 7.16.7 browserslist: 4.19.1 semver: 6.3.0 dev: true - /@babel/helper-compilation-targets/7.16.7_@babel+core@7.17.2: + /@babel/helper-compilation-targets/7.16.7_@babel+core@7.19.6: resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/compat-data': 7.17.0 - '@babel/core': 7.17.2 + '@babel/core': 7.19.6 '@babel/helper-validator-option': 7.16.7 browserslist: 4.19.1 semver: 6.3.0 dev: true + /@babel/helper-compilation-targets/7.19.3_@babel+core@7.17.2: + resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.19.4 + '@babel/core': 7.17.2 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + semver: 6.3.0 + dev: true + + /@babel/helper-compilation-targets/7.19.3_@babel+core@7.19.6: + resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.19.4 + '@babel/core': 7.19.6 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + semver: 6.3.0 + dev: true + /@babel/helper-create-class-features-plugin/7.15.0_@babel+core@7.13.16: resolution: {integrity: sha512-MdmDXgvTIi4heDVX/e9EFfeGpugqm9fobBVg/iioE8kueXrOHdRDe36FAY7SnE9xXLVeYCoJR/gdrBEIHRC83Q==} engines: {node: '>=6.9.0'} @@ -564,12 +1072,12 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-annotate-as-pure': 7.14.5 - '@babel/helper-function-name': 7.14.5 + '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-function-name': 7.17.9 '@babel/helper-member-expression-to-functions': 7.15.0 - '@babel/helper-optimise-call-expression': 7.14.5 - '@babel/helper-replace-supers': 7.15.0 - '@babel/helper-split-export-declaration': 7.14.5 + '@babel/helper-optimise-call-expression': 7.16.7 + '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-split-export-declaration': 7.16.7 transitivePeerDependencies: - supports-color dev: true @@ -582,8 +1090,8 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-environment-visitor': 7.16.7 - '@babel/helper-function-name': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 + '@babel/helper-function-name': 7.17.9 '@babel/helper-member-expression-to-functions': 7.16.7 '@babel/helper-optimise-call-expression': 7.16.7 '@babel/helper-replace-supers': 7.16.7 @@ -600,8 +1108,26 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-environment-visitor': 7.16.7 - '@babel/helper-function-name': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 + '@babel/helper-function-name': 7.17.9 + '@babel/helper-member-expression-to-functions': 7.16.7 + '@babel/helper-optimise-call-expression': 7.16.7 + '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-split-export-declaration': 7.16.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-create-class-features-plugin/7.17.1_@babel+core@7.19.6: + resolution: {integrity: sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 + '@babel/helper-function-name': 7.17.9 '@babel/helper-member-expression-to-functions': 7.16.7 '@babel/helper-optimise-call-expression': 7.16.7 '@babel/helper-replace-supers': 7.16.7 @@ -632,6 +1158,53 @@ packages: regexpu-core: 5.0.1 dev: true + /@babel/helper-create-regexp-features-plugin/7.17.0_@babel+core@7.19.6: + resolution: {integrity: sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-annotate-as-pure': 7.16.7 + regexpu-core: 5.0.1 + dev: true + + /@babel/helper-define-polyfill-provider/0.1.5_@babel+core@7.17.2: + resolution: {integrity: sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-compilation-targets': 7.19.3_@babel+core@7.17.2 + '@babel/helper-module-imports': 7.16.7 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/traverse': 7.19.6 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-define-polyfill-provider/0.1.5_@babel+core@7.19.6: + resolution: {integrity: sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-compilation-targets': 7.19.3_@babel+core@7.19.6 + '@babel/helper-module-imports': 7.16.7 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/traverse': 7.19.6 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-define-polyfill-provider/0.3.1_@babel+core@7.13.16: resolution: {integrity: sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==} peerDependencies: @@ -640,9 +1213,9 @@ packages: '@babel/core': 7.13.16 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.13.16 '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 - '@babel/traverse': 7.17.0 - debug: 4.3.3 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/traverse': 7.18.2 + debug: 4.3.4 lodash.debounce: 4.0.8 resolve: 1.22.1 semver: 6.3.0 @@ -658,9 +1231,27 @@ packages: '@babel/core': 7.17.2 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.17.2 '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 - '@babel/traverse': 7.17.0 - debug: 4.3.3 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/traverse': 7.18.2 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-define-polyfill-provider/0.3.1_@babel+core@7.19.6: + resolution: {integrity: sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.19.6 + '@babel/helper-module-imports': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/traverse': 7.18.2 + debug: 4.3.4 lodash.debounce: 4.0.8 resolve: 1.22.1 semver: 6.3.0 @@ -672,7 +1263,7 @@ packages: resolution: {integrity: sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.19.4 dev: true /@babel/helper-environment-visitor/7.18.2: @@ -680,20 +1271,16 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-explode-assignable-expression/7.16.7: - resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==} + /@babel/helper-environment-visitor/7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.4 dev: true - /@babel/helper-function-name/7.14.5: - resolution: {integrity: sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==} + /@babel/helper-explode-assignable-expression/7.16.7: + resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-get-function-arity': 7.14.5 - '@babel/template': 7.16.7 - '@babel/types': 7.18.4 + '@babel/types': 7.19.4 dev: true /@babel/helper-function-name/7.16.7: @@ -701,8 +1288,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-get-function-arity': 7.16.7 - '@babel/template': 7.16.7 - '@babel/types': 7.17.0 + '@babel/template': 7.18.10 + '@babel/types': 7.19.4 dev: true /@babel/helper-function-name/7.17.9: @@ -713,46 +1300,61 @@ packages: '@babel/types': 7.18.4 dev: true - /@babel/helper-get-function-arity/7.14.5: - resolution: {integrity: sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==} + /@babel/helper-function-name/7.19.0: + resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/template': 7.18.10 + '@babel/types': 7.19.4 dev: true /@babel/helper-get-function-arity/7.16.7: resolution: {integrity: sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.19.4 dev: true /@babel/helper-hoist-variables/7.16.7: resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 + dev: true + + /@babel/helper-hoist-variables/7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.19.4 dev: true /@babel/helper-member-expression-to-functions/7.15.0: resolution: {integrity: sha512-Jq8H8U2kYiafuj2xMTPQwkTBnEEdGKpT35lJEQsRRjnG0LW3neucsaMWLgKcwu3OHKNeYugfw+Z20BXBSEs2Lg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.19.4 dev: true /@babel/helper-member-expression-to-functions/7.16.7: resolution: {integrity: sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.19.4 dev: true /@babel/helper-module-imports/7.16.7: resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 + dev: true + + /@babel/helper-module-imports/7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.19.4 dev: true /@babel/helper-module-transforms/7.16.7: @@ -765,8 +1367,8 @@ packages: '@babel/helper-split-export-declaration': 7.16.7 '@babel/helper-validator-identifier': 7.16.7 '@babel/template': 7.16.7 - '@babel/traverse': 7.17.0 - '@babel/types': 7.17.0 + '@babel/traverse': 7.18.2 + '@babel/types': 7.18.4 transitivePeerDependencies: - supports-color dev: true @@ -775,7 +1377,7 @@ packages: resolution: {integrity: sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-environment-visitor': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 '@babel/helper-module-imports': 7.16.7 '@babel/helper-simple-access': 7.18.2 '@babel/helper-split-export-declaration': 7.16.7 @@ -787,11 +1389,20 @@ packages: - supports-color dev: true - /@babel/helper-optimise-call-expression/7.14.5: - resolution: {integrity: sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==} + /@babel/helper-module-transforms/7.19.6: + resolution: {integrity: sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.19.4 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 + transitivePeerDependencies: + - supports-color dev: true /@babel/helper-optimise-call-expression/7.16.7: @@ -801,18 +1412,22 @@ packages: '@babel/types': 7.18.4 dev: true + /@babel/helper-plugin-utils/7.10.4: + resolution: {integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==} + dev: true + /@babel/helper-plugin-utils/7.14.5: resolution: {integrity: sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-plugin-utils/7.16.7: - resolution: {integrity: sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==} + /@babel/helper-plugin-utils/7.17.12: + resolution: {integrity: sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-plugin-utils/7.17.12: - resolution: {integrity: sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==} + /@babel/helper-plugin-utils/7.19.0: + resolution: {integrity: sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==} engines: {node: '>=6.9.0'} dev: true @@ -827,27 +1442,15 @@ packages: - supports-color dev: true - /@babel/helper-replace-supers/7.15.0: - resolution: {integrity: sha512-6O+eWrhx+HEra/uJnifCwhwMd6Bp5+ZfZeJwbqUTuqkhIT6YcRhiZCOOFChRypOIe0cV46kFrRBlm+t5vHCEaA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-member-expression-to-functions': 7.15.0 - '@babel/helper-optimise-call-expression': 7.14.5 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/helper-replace-supers/7.16.7: resolution: {integrity: sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-environment-visitor': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 '@babel/helper-member-expression-to-functions': 7.16.7 '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/traverse': 7.17.0 - '@babel/types': 7.17.0 + '@babel/traverse': 7.18.2 + '@babel/types': 7.18.4 transitivePeerDependencies: - supports-color dev: true @@ -856,7 +1459,7 @@ packages: resolution: {integrity: sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.19.4 dev: true /@babel/helper-simple-access/7.18.2: @@ -866,6 +1469,13 @@ packages: '@babel/types': 7.18.4 dev: true + /@babel/helper-simple-access/7.19.4: + resolution: {integrity: sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.19.4 + dev: true + /@babel/helper-skip-transparent-expression-wrappers/7.16.0: resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} engines: {node: '>=6.9.0'} @@ -873,22 +1483,22 @@ packages: '@babel/types': 7.18.4 dev: true - /@babel/helper-split-export-declaration/7.14.5: - resolution: {integrity: sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==} + /@babel/helper-split-export-declaration/7.16.7: + resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.4 dev: true - /@babel/helper-split-export-declaration/7.16.7: - resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==} + /@babel/helper-split-export-declaration/7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.19.4 dev: true - /@babel/helper-validator-identifier/7.14.9: - resolution: {integrity: sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==} + /@babel/helper-string-parser/7.19.4: + resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} dev: true @@ -897,6 +1507,11 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-identifier/7.19.1: + resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-option/7.14.5: resolution: {integrity: sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==} engines: {node: '>=6.9.0'} @@ -907,36 +1522,41 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-option/7.18.6: + resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-wrap-function/7.16.8: resolution: {integrity: sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.16.7 - '@babel/template': 7.16.7 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/helper-function-name': 7.17.9 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 transitivePeerDependencies: - supports-color dev: true - /@babel/helpers/7.16.7: - resolution: {integrity: sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==} + /@babel/helpers/7.17.2: + resolution: {integrity: sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.16.7 - '@babel/traverse': 7.17.0 + '@babel/traverse': 7.18.2 '@babel/types': 7.18.4 transitivePeerDependencies: - supports-color dev: true - /@babel/helpers/7.17.2: - resolution: {integrity: sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==} + /@babel/helpers/7.19.4: + resolution: {integrity: sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.16.7 - '@babel/traverse': 7.17.0 - '@babel/types': 7.17.0 + '@babel/template': 7.18.10 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 transitivePeerDependencies: - supports-color dev: true @@ -950,12 +1570,13 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser/7.15.3: - resolution: {integrity: sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==} - engines: {node: '>=6.0.0'} - hasBin: true + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.17.0 + '@babel/helper-validator-identifier': 7.19.1 + chalk: 2.4.2 + js-tokens: 4.0.0 dev: true /@babel/parser/7.17.0: @@ -963,7 +1584,7 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 dev: true /@babel/parser/7.18.4: @@ -974,6 +1595,14 @@ packages: '@babel/types': 7.18.4 dev: true + /@babel/parser/7.19.6: + resolution: {integrity: sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.19.4 + dev: true + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==} engines: {node: '>=6.9.0'} @@ -981,7 +1610,7 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.17.2: @@ -991,7 +1620,17 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.16.7_@babel+core@7.13.16: @@ -1001,7 +1640,7 @@ packages: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.13.16 dev: true @@ -1013,11 +1652,23 @@ packages: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.17.2 dev: true + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-async-generator-functions/7.16.8_@babel+core@7.13.16: resolution: {integrity: sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==} engines: {node: '>=6.9.0'} @@ -1025,7 +1676,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-remap-async-to-generator': 7.16.8 '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.13.16 transitivePeerDependencies: @@ -1039,13 +1690,27 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-remap-async-to-generator': 7.16.8 '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.17.2 transitivePeerDependencies: - supports-color dev: true + /@babel/plugin-proposal-async-generator-functions/7.16.8_@babel+core@7.19.6: + resolution: {integrity: sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-remap-async-to-generator': 7.16.8 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.19.6 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-class-properties/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==} engines: {node: '>=6.9.0'} @@ -1054,7 +1719,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 transitivePeerDependencies: - supports-color dev: true @@ -1067,7 +1732,20 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-properties/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 transitivePeerDependencies: - supports-color dev: true @@ -1080,7 +1758,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.13.16 transitivePeerDependencies: - supports-color @@ -1094,12 +1772,26 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.17.2 transitivePeerDependencies: - supports-color dev: true + /@babel/plugin-proposal-class-static-block/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.19.6 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-decorators/7.17.2_@babel+core@7.17.2: resolution: {integrity: sha512-WH8Z95CwTq/W8rFbMqb9p3hicpt4RX4f0K659ax2VHxgOyT6qQmUaEVEjIh4WR9Eh9NymkVn5vwsrE68fAQNUw==} engines: {node: '>=6.9.0'} @@ -1108,7 +1800,7 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-replace-supers': 7.16.7 '@babel/plugin-syntax-decorators': 7.17.0_@babel+core@7.17.2 charcodes: 0.2.0 @@ -1116,6 +1808,22 @@ packages: - supports-color dev: true + /@babel/plugin-proposal-decorators/7.17.2_@babel+core@7.19.6: + resolution: {integrity: sha512-WH8Z95CwTq/W8rFbMqb9p3hicpt4RX4f0K659ax2VHxgOyT6qQmUaEVEjIh4WR9Eh9NymkVn5vwsrE68fAQNUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-replace-supers': 7.16.7 + '@babel/plugin-syntax-decorators': 7.17.0_@babel+core@7.19.6 + charcodes: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} engines: {node: '>=6.9.0'} @@ -1123,7 +1831,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.13.16 dev: true @@ -1134,10 +1842,43 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.19.6 + dev: true + + /@babel/plugin-proposal-export-default-from/7.18.10_@babel+core@7.17.2: + resolution: {integrity: sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-export-default-from': 7.18.6_@babel+core@7.17.2 + dev: true + + /@babel/plugin-proposal-export-default-from/7.18.10_@babel+core@7.19.6: + resolution: {integrity: sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-export-default-from': 7.18.6_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-export-namespace-from/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==} engines: {node: '>=6.9.0'} @@ -1145,7 +1886,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.13.16 dev: true @@ -1156,10 +1897,21 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-export-namespace-from/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-json-strings/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==} engines: {node: '>=6.9.0'} @@ -1167,7 +1919,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.13.16 dev: true @@ -1178,10 +1930,21 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-json-strings/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-logical-assignment-operators/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==} engines: {node: '>=6.9.0'} @@ -1189,7 +1952,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.13.16 dev: true @@ -1200,10 +1963,21 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-logical-assignment-operators/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-nullish-coalescing-operator/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==} engines: {node: '>=6.9.0'} @@ -1211,7 +1985,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.13.16 dev: true @@ -1222,10 +1996,21 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-nullish-coalescing-operator/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} engines: {node: '>=6.9.0'} @@ -1233,7 +2018,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.13.16 dev: true @@ -1244,10 +2029,32 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.19.6 + dev: true + + /@babel/plugin-proposal-object-rest-spread/7.12.1_@babel+core@7.12.9: + resolution: {integrity: sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.12.9 + dev: true + /@babel/plugin-proposal-object-rest-spread/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==} engines: {node: '>=6.9.0'} @@ -1257,7 +2064,7 @@ packages: '@babel/compat-data': 7.17.0 '@babel/core': 7.13.16 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.13.16 '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.13.16 dev: true @@ -1271,11 +2078,25 @@ packages: '@babel/compat-data': 7.17.0 '@babel/core': 7.17.2 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.17.2 '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-object-rest-spread/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.17.0 + '@babel/core': 7.19.6 + '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-optional-catch-binding/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==} engines: {node: '>=6.9.0'} @@ -1283,7 +2104,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.13.16 dev: true @@ -1294,10 +2115,21 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-optional-catch-binding/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-optional-chaining/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==} engines: {node: '>=6.9.0'} @@ -1305,7 +2137,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.13.16 dev: true @@ -1317,11 +2149,23 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.17.2 dev: true + /@babel/plugin-proposal-optional-chaining/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.19.6 + dev: true + /@babel/plugin-proposal-private-methods/7.16.11_@babel+core@7.13.16: resolution: {integrity: sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==} engines: {node: '>=6.9.0'} @@ -1330,7 +2174,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 transitivePeerDependencies: - supports-color dev: true @@ -1343,7 +2187,20 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-private-methods/7.16.11_@babel+core@7.19.6: + resolution: {integrity: sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 transitivePeerDependencies: - supports-color dev: true @@ -1357,7 +2214,7 @@ packages: '@babel/core': 7.13.16 '@babel/helper-annotate-as-pure': 7.16.7 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.13.16 transitivePeerDependencies: - supports-color @@ -1372,12 +2229,27 @@ packages: '@babel/core': 7.17.2 '@babel/helper-annotate-as-pure': 7.16.7 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.17.2 transitivePeerDependencies: - supports-color dev: true + /@babel/plugin-proposal-private-property-in-object/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.19.6 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-unicode-property-regex/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==} engines: {node: '>=4'} @@ -1386,7 +2258,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-proposal-unicode-property-regex/7.16.7_@babel+core@7.17.2: @@ -1397,7 +2269,18 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-proposal-unicode-property-regex/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==} + engines: {node: '>=4'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.13.16: @@ -1406,7 +2289,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.17.2: @@ -1415,7 +2298,34 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.19.6: + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.17.2: + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.13.16: @@ -1424,7 +2334,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.17.2: @@ -1433,7 +2343,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.19.6: + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.13.16: @@ -1443,7 +2362,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.17.2: @@ -1453,7 +2372,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.19.6: + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-decorators/7.17.0_@babel+core@7.17.2: @@ -1466,13 +2395,23 @@ packages: '@babel/helper-plugin-utils': 7.17.12 dev: true + /@babel/plugin-syntax-decorators/7.17.0_@babel+core@7.19.6: + resolution: {integrity: sha512-qWe85yCXsvDEluNP0OyeQjH63DlhAR3W7K9BxxU1MvbDb48tgBG+Ao6IJJ6smPDrrVzSQZrbF6donpkFBMcs3A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.13.16: resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.17.2: @@ -1481,7 +2420,36 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-export-default-from/7.18.6_@babel+core@7.17.2: + resolution: {integrity: sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-export-default-from/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.13.16: @@ -1490,7 +2458,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.17.2: @@ -1499,7 +2467,34 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.17.2: + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.19.6: + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.13.16: @@ -1508,7 +2503,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.17.2: @@ -1517,7 +2512,25 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-jsx/7.12.1_@babel+core@7.12.9: + resolution: {integrity: sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-syntax-jsx/7.16.7_@babel+core@7.17.2: @@ -1530,13 +2543,33 @@ packages: '@babel/helper-plugin-utils': 7.17.12 dev: true + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.17.2: + resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.13.16: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.17.2: @@ -1545,7 +2578,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.19.6: + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.13.16: @@ -1554,7 +2596,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.17.2: @@ -1563,7 +2605,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.13.16: @@ -1572,7 +2623,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.17.2: @@ -1581,7 +2632,25 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.19.6: + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.12.9: + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.13.16: @@ -1590,7 +2659,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.17.2: @@ -1599,7 +2668,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.13.16: @@ -1608,7 +2686,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.17.2: @@ -1617,7 +2695,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.13.16: @@ -1626,7 +2713,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.17.2: @@ -1635,7 +2722,16 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.19.6: + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.13.16: @@ -1645,7 +2741,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.17.2: @@ -1655,7 +2751,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.19.6: + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.13.16: @@ -1665,7 +2771,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.17.2: @@ -1675,7 +2781,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.19.6: + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-syntax-typescript/7.14.5_@babel+core@7.13.16: @@ -1685,7 +2801,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-syntax-typescript/7.16.7_@babel+core@7.17.2: @@ -1695,7 +2811,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-syntax-typescript/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-transform-arrow-functions/7.16.7_@babel+core@7.13.16: @@ -1705,7 +2831,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-arrow-functions/7.16.7_@babel+core@7.17.2: @@ -1715,7 +2841,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-arrow-functions/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-async-to-generator/7.16.8_@babel+core@7.13.16: @@ -1726,7 +2862,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-remap-async-to-generator': 7.16.8 transitivePeerDependencies: - supports-color @@ -1740,7 +2876,21 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-remap-async-to-generator': 7.16.8 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-async-to-generator/7.16.8_@babel+core@7.19.6: + resolution: {integrity: sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-module-imports': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-remap-async-to-generator': 7.16.8 transitivePeerDependencies: - supports-color @@ -1753,7 +2903,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-block-scoped-functions/7.16.7_@babel+core@7.17.2: @@ -1763,7 +2913,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-block-scoped-functions/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-block-scoping/7.16.7_@babel+core@7.13.16: @@ -1773,7 +2933,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-block-scoping/7.16.7_@babel+core@7.17.2: @@ -1783,7 +2943,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-block-scoping/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-classes/7.16.7_@babel+core@7.13.16: @@ -1794,10 +2964,10 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-environment-visitor': 7.16.7 - '@babel/helper-function-name': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 + '@babel/helper-function-name': 7.17.9 '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-replace-supers': 7.16.7 '@babel/helper-split-export-declaration': 7.16.7 globals: 11.12.0 @@ -1813,10 +2983,29 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-environment-visitor': 7.16.7 - '@babel/helper-function-name': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 + '@babel/helper-function-name': 7.17.9 '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-split-export-declaration': 7.16.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-classes/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-environment-visitor': 7.18.2 + '@babel/helper-function-name': 7.17.9 + '@babel/helper-optimise-call-expression': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-replace-supers': 7.16.7 '@babel/helper-split-export-declaration': 7.16.7 globals: 11.12.0 @@ -1831,7 +3020,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-computed-properties/7.16.7_@babel+core@7.17.2: @@ -1841,7 +3030,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-computed-properties/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-destructuring/7.16.7_@babel+core@7.13.16: @@ -1851,7 +3050,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-destructuring/7.16.7_@babel+core@7.17.2: @@ -1861,7 +3060,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-destructuring/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.13.16: @@ -1872,7 +3081,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.17.2: @@ -1883,7 +3092,18 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-duplicate-keys/7.16.7_@babel+core@7.13.16: @@ -1893,7 +3113,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-duplicate-keys/7.16.7_@babel+core@7.17.2: @@ -1903,7 +3123,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-duplicate-keys/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.13.16: @@ -1914,7 +3144,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-builder-binary-assignment-operator-visitor': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.17.2: @@ -1925,7 +3155,18 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-builder-binary-assignment-operator-visitor': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-for-of/7.16.7_@babel+core@7.13.16: @@ -1935,7 +3176,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-for-of/7.16.7_@babel+core@7.17.2: @@ -1945,7 +3186,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-for-of/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.13.16: @@ -1956,8 +3207,8 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.13.16 - '@babel/helper-function-name': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-function-name': 7.17.9 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.17.2: @@ -1968,8 +3219,20 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.17.2 - '@babel/helper-function-name': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-function-name': 7.17.9 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.19.6 + '@babel/helper-function-name': 7.17.9 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-literals/7.16.7_@babel+core@7.13.16: @@ -1979,7 +3242,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-literals/7.16.7_@babel+core@7.17.2: @@ -1989,7 +3252,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-literals/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.13.16: @@ -1999,7 +3272,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.17.2: @@ -2009,7 +3282,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-modules-amd/7.16.7_@babel+core@7.13.16: @@ -2019,8 +3302,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color @@ -2033,50 +3316,64 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-commonjs/7.16.8_@babel+core@7.13.16: - resolution: {integrity: sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==} + /@babel/plugin-transform-modules-amd/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-commonjs/7.18.2_@babel+core@7.13.16: + resolution: {integrity: sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 - '@babel/helper-simple-access': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-simple-access': 7.18.2 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-commonjs/7.16.8_@babel+core@7.17.2: - resolution: {integrity: sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==} + /@babel/plugin-transform-modules-commonjs/7.18.2_@babel+core@7.17.2: + resolution: {integrity: sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 - '@babel/helper-simple-access': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-simple-access': 7.18.2 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-commonjs/7.18.2_@babel+core@7.13.16: + /@babel/plugin-transform-modules-commonjs/7.18.2_@babel+core@7.19.6: resolution: {integrity: sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.13.16 + '@babel/core': 7.19.6 '@babel/helper-module-transforms': 7.18.0 '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-simple-access': 7.18.2 @@ -2093,8 +3390,8 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-hoist-variables': 7.16.7 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-validator-identifier': 7.16.7 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: @@ -2109,8 +3406,24 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-hoist-variables': 7.16.7 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-validator-identifier': 7.16.7 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-systemjs/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-hoist-variables': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-validator-identifier': 7.16.7 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: @@ -2124,8 +3437,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 transitivePeerDependencies: - supports-color dev: true @@ -2137,8 +3450,21 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-module-transforms': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-umd/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-module-transforms': 7.18.0 + '@babel/helper-plugin-utils': 7.17.12 transitivePeerDependencies: - supports-color dev: true @@ -2163,6 +3489,16 @@ packages: '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.17.2 dev: true + /@babel/plugin-transform-named-capturing-groups-regex/7.16.8_@babel+core@7.19.6: + resolution: {integrity: sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.19.6 + dev: true + /@babel/plugin-transform-new-target/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==} engines: {node: '>=6.9.0'} @@ -2170,7 +3506,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-new-target/7.16.7_@babel+core@7.17.2: @@ -2180,7 +3516,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-new-target/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-object-assign/7.16.7_@babel+core@7.17.2: @@ -2190,7 +3536,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.13.16: @@ -2200,7 +3546,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-replace-supers': 7.16.7 transitivePeerDependencies: - supports-color @@ -2213,12 +3559,35 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-replace-supers': 7.16.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-replace-supers': 7.16.7 transitivePeerDependencies: - supports-color dev: true + /@babel/plugin-transform-parameters/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + /@babel/plugin-transform-parameters/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==} engines: {node: '>=6.9.0'} @@ -2226,7 +3595,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-parameters/7.16.7_@babel+core@7.17.2: @@ -2236,7 +3605,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-parameters/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.13.16: @@ -2246,7 +3625,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.17.2: @@ -2256,7 +3635,57 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-react-display-name/7.18.6_@babel+core@7.17.2: + resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-react-display-name/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.17.2: + resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.17.2 + dev: true + + /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.19.6 dev: true /@babel/plugin-transform-react-jsx-source/7.14.5_@babel+core@7.13.16: @@ -2266,7 +3695,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.14.5 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-react-jsx-source/7.14.5_@babel+core@7.17.2: + resolution: {integrity: sha512-1TpSDnD9XR/rQ2tzunBVPThF5poaYT9GqP+of8fAtguYuI/dm2RkrMBDemsxtY0XBzvW7nXjYM0hRyKX9QYj7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-react-jsx/7.16.7_@babel+core@7.17.2: @@ -2278,9 +3717,59 @@ packages: '@babel/core': 7.17.2 '@babel/helper-annotate-as-pure': 7.16.7 '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-jsx': 7.16.7_@babel+core@7.17.2 - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 + dev: true + + /@babel/plugin-transform-react-jsx/7.19.0_@babel+core@7.17.2: + resolution: {integrity: sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.17.2 + '@babel/types': 7.19.4 + dev: true + + /@babel/plugin-transform-react-jsx/7.19.0_@babel+core@7.19.6: + resolution: {integrity: sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.19.6 + '@babel/types': 7.19.4 + dev: true + + /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.17.2: + resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 + dev: true + + /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-transform-regenerator/7.16.7_@babel+core@7.13.16: @@ -2303,6 +3792,16 @@ packages: regenerator-transform: 0.14.5 dev: true + /@babel/plugin-transform-regenerator/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + regenerator-transform: 0.14.5 + dev: true + /@babel/plugin-transform-reserved-words/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==} engines: {node: '>=6.9.0'} @@ -2310,7 +3809,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-reserved-words/7.16.7_@babel+core@7.17.2: @@ -2320,7 +3819,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-reserved-words/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-runtime/7.17.0_@babel+core@7.13.16: @@ -2331,7 +3840,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.13.16 babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.13.16 babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.13.16 @@ -2340,6 +3849,23 @@ packages: - supports-color dev: true + /@babel/plugin-transform-runtime/7.17.0_@babel+core@7.17.2: + resolution: {integrity: sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-module-imports': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.17.2 + babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.17.2 + babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.17.2 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==} engines: {node: '>=6.9.0'} @@ -2347,7 +3873,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.17.2: @@ -2357,7 +3883,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-spread/7.16.7_@babel+core@7.13.16: @@ -2367,7 +3903,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 dev: true @@ -2378,7 +3914,18 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 + dev: true + + /@babel/plugin-transform-spread/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 dev: true @@ -2389,7 +3936,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.17.2: @@ -2399,7 +3946,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-template-literals/7.16.7_@babel+core@7.13.16: @@ -2409,7 +3966,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-template-literals/7.16.7_@babel+core@7.17.2: @@ -2419,7 +3976,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-template-literals/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-typeof-symbol/7.16.7_@babel+core@7.13.16: @@ -2429,7 +3996,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-typeof-symbol/7.16.7_@babel+core@7.17.2: @@ -2439,7 +4006,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-typeof-symbol/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-typescript/7.15.0_@babel+core@7.13.16: @@ -2450,7 +4027,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-create-class-features-plugin': 7.15.0_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.14.5 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-syntax-typescript': 7.14.5_@babel+core@7.13.16 transitivePeerDependencies: - supports-color @@ -2464,12 +4041,26 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-typescript': 7.16.7_@babel+core@7.17.2 transitivePeerDependencies: - supports-color dev: true + /@babel/plugin-transform-typescript/7.16.8_@babel+core@7.19.6: + resolution: {integrity: sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-class-features-plugin': 7.17.1_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/plugin-syntax-typescript': 7.16.7_@babel+core@7.19.6 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-unicode-escapes/7.16.7_@babel+core@7.13.16: resolution: {integrity: sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==} engines: {node: '>=6.9.0'} @@ -2477,7 +4068,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-unicode-escapes/7.16.7_@babel+core@7.17.2: @@ -2487,7 +4078,17 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-unicode-escapes/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.13.16: @@ -2498,7 +4099,7 @@ packages: dependencies: '@babel/core': 7.13.16 '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.17.2: @@ -2509,7 +4110,18 @@ packages: dependencies: '@babel/core': 7.17.2 '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + + /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-create-regexp-features-plugin': 7.17.0_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 dev: true /@babel/preset-env/7.16.11_@babel+core@7.13.16: @@ -2521,7 +4133,7 @@ packages: '@babel/compat-data': 7.17.0 '@babel/core': 7.13.16 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.13.16 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.7_@babel+core@7.13.16 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.16.7_@babel+core@7.13.16 @@ -2569,7 +4181,7 @@ packages: '@babel/plugin-transform-literals': 7.16.7_@babel+core@7.13.16 '@babel/plugin-transform-member-expression-literals': 7.16.7_@babel+core@7.13.16 '@babel/plugin-transform-modules-amd': 7.16.7_@babel+core@7.13.16 - '@babel/plugin-transform-modules-commonjs': 7.16.8_@babel+core@7.13.16 + '@babel/plugin-transform-modules-commonjs': 7.18.2_@babel+core@7.13.16 '@babel/plugin-transform-modules-systemjs': 7.16.7_@babel+core@7.13.16 '@babel/plugin-transform-modules-umd': 7.16.7_@babel+core@7.13.16 '@babel/plugin-transform-named-capturing-groups-regex': 7.16.8_@babel+core@7.13.16 @@ -2587,7 +4199,7 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.13.16 '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.13.16 '@babel/preset-modules': 0.1.5_@babel+core@7.13.16 - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.13.16 babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.13.16 babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.13.16 @@ -2606,7 +4218,7 @@ packages: '@babel/compat-data': 7.17.0 '@babel/core': 7.17.2 '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.7_@babel+core@7.17.2 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.16.7_@babel+core@7.17.2 @@ -2654,7 +4266,7 @@ packages: '@babel/plugin-transform-literals': 7.16.7_@babel+core@7.17.2 '@babel/plugin-transform-member-expression-literals': 7.16.7_@babel+core@7.17.2 '@babel/plugin-transform-modules-amd': 7.16.7_@babel+core@7.17.2 - '@babel/plugin-transform-modules-commonjs': 7.16.8_@babel+core@7.17.2 + '@babel/plugin-transform-modules-commonjs': 7.18.2_@babel+core@7.17.2 '@babel/plugin-transform-modules-systemjs': 7.16.7_@babel+core@7.17.2 '@babel/plugin-transform-modules-umd': 7.16.7_@babel+core@7.17.2 '@babel/plugin-transform-named-capturing-groups-regex': 7.16.8_@babel+core@7.17.2 @@ -2672,7 +4284,7 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.17.2 '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.17.2 '@babel/preset-modules': 0.1.5_@babel+core@7.17.2 - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.17.2 babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.17.2 babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.17.2 @@ -2682,16 +4294,101 @@ packages: - supports-color dev: true + /@babel/preset-env/7.16.11_@babel+core@7.19.6: + resolution: {integrity: sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.17.0 + '@babel/core': 7.19.6 + '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-validator-option': 7.16.7 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-async-generator-functions': 7.16.8_@babel+core@7.19.6 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-class-static-block': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-dynamic-import': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-export-namespace-from': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-json-strings': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-logical-assignment-operators': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-numeric-separator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-optional-catch-binding': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.19.6 + '@babel/plugin-proposal-private-property-in-object': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-unicode-property-regex': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.19.6 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.19.6 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.19.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.19.6 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.19.6 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-async-to-generator': 7.16.8_@babel+core@7.19.6 + '@babel/plugin-transform-block-scoped-functions': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-computed-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-destructuring': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-duplicate-keys': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-exponentiation-operator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-function-name': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-literals': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-member-expression-literals': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-modules-amd': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-modules-commonjs': 7.18.2_@babel+core@7.19.6 + '@babel/plugin-transform-modules-systemjs': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-modules-umd': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-named-capturing-groups-regex': 7.16.8_@babel+core@7.19.6 + '@babel/plugin-transform-new-target': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-object-super': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-property-literals': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-regenerator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-reserved-words': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-sticky-regex': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-typeof-symbol': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.19.6 + '@babel/preset-modules': 0.1.5_@babel+core@7.19.6 + '@babel/types': 7.18.4 + babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.19.6 + babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.19.6 + babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.19.6 + core-js-compat: 3.21.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/preset-modules/0.1.5_@babel+core@7.13.16: resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.13.16 - '@babel/helper-plugin-utils': 7.14.5 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-proposal-unicode-property-regex': 7.16.7_@babel+core@7.13.16 '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.13.16 - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 esutils: 2.0.3 dev: true @@ -2701,13 +4398,56 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.14.5 + '@babel/helper-plugin-utils': 7.17.12 '@babel/plugin-proposal-unicode-property-regex': 7.16.7_@babel+core@7.17.2 '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.17.2 - '@babel/types': 7.17.0 + '@babel/types': 7.18.4 + esutils: 2.0.3 + dev: true + + /@babel/preset-modules/0.1.5_@babel+core@7.19.6: + resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-proposal-unicode-property-regex': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.19.6 + '@babel/types': 7.18.4 esutils: 2.0.3 dev: true + /@babel/preset-react/7.18.6_@babel+core@7.17.2: + resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-validator-option': 7.18.6 + '@babel/plugin-transform-react-display-name': 7.18.6_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.17.2 + '@babel/plugin-transform-react-pure-annotations': 7.18.6_@babel+core@7.17.2 + dev: true + + /@babel/preset-react/7.18.6_@babel+core@7.19.6: + resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-validator-option': 7.18.6 + '@babel/plugin-transform-react-display-name': 7.18.6_@babel+core@7.19.6 + '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.19.6 + '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.19.6 + '@babel/plugin-transform-react-pure-annotations': 7.18.6_@babel+core@7.19.6 + dev: true + /@babel/preset-typescript/7.15.0_@babel+core@7.13.16: resolution: {integrity: sha512-lt0Y/8V3y06Wq/8H/u0WakrqciZ7Fz7mwPDHWUJAXlABL5hiUG42BNlRXiELNjeWjO5rWmnNKlx+yzJvxezHow==} engines: {node: '>=6.9.0'} @@ -2729,13 +4469,55 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.2 - '@babel/helper-plugin-utils': 7.16.7 + '@babel/helper-plugin-utils': 7.17.12 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-transform-typescript': 7.16.8_@babel+core@7.17.2 transitivePeerDependencies: - supports-color dev: true + /@babel/preset-typescript/7.16.7_@babel+core@7.19.6: + resolution: {integrity: sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-validator-option': 7.16.7 + '@babel/plugin-transform-typescript': 7.16.8_@babel+core@7.19.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/register/7.18.9_@babel+core@7.17.2: + resolution: {integrity: sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + clone-deep: 4.0.1 + find-cache-dir: 2.1.0 + make-dir: 2.1.0 + pirates: 4.0.5 + source-map-support: 0.5.21 + dev: true + + /@babel/register/7.18.9_@babel+core@7.19.6: + resolution: {integrity: sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + clone-deep: 4.0.1 + find-cache-dir: 2.1.0 + make-dir: 2.1.0 + pirates: 4.0.5 + source-map-support: 0.5.21 + dev: true + /@babel/runtime-corejs3/7.16.7: resolution: {integrity: sha512-MiYR1yk8+TW/CpOD0CyX7ve9ffWTKqLk/L6pk8TPl0R8pNi+1pFY8fH9yET55KlvukQ4PAWfXsGr2YHVjcI4Pw==} engines: {node: '>=6.9.0'} @@ -2751,13 +4533,6 @@ packages: regenerator-runtime: 0.13.9 dev: false - /@babel/runtime/7.16.3: - resolution: {integrity: sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.13.9 - dev: true - /@babel/runtime/7.16.7: resolution: {integrity: sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==} engines: {node: '>=6.9.0'} @@ -2777,15 +4552,14 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.9 - dev: true /@babel/template/7.14.5: resolution: {integrity: sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/parser': 7.15.3 - '@babel/types': 7.15.0 + '@babel/parser': 7.18.4 + '@babel/types': 7.18.4 dev: true /@babel/template/7.16.7: @@ -2793,26 +4567,17 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/parser': 7.17.0 - '@babel/types': 7.17.0 + '@babel/parser': 7.18.4 + '@babel/types': 7.18.4 dev: true - /@babel/traverse/7.16.7: - resolution: {integrity: sha512-8KWJPIb8c2VvY8AJrydh6+fVRo2ODx1wYBU2398xJVq0JomuLBZmVQzLPBblJgHIGYG4znCpUZUZ0Pt2vdmVYQ==} + /@babel/template/7.18.10: + resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.16.7 - '@babel/generator': 7.17.0 - '@babel/helper-environment-visitor': 7.16.7 - '@babel/helper-function-name': 7.16.7 - '@babel/helper-hoist-variables': 7.16.7 - '@babel/helper-split-export-declaration': 7.16.7 - '@babel/parser': 7.17.0 - '@babel/types': 7.18.4 - debug: 4.3.3 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.19.6 + '@babel/types': 7.19.4 dev: true /@babel/traverse/7.17.0: @@ -2820,14 +4585,14 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/generator': 7.17.0 + '@babel/generator': 7.18.2 '@babel/helper-environment-visitor': 7.16.7 '@babel/helper-function-name': 7.16.7 '@babel/helper-hoist-variables': 7.16.7 '@babel/helper-split-export-declaration': 7.16.7 - '@babel/parser': 7.17.0 - '@babel/types': 7.17.0 - debug: 4.3.3 + '@babel/parser': 7.18.4 + '@babel/types': 7.18.4 + debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -2845,52 +4610,136 @@ packages: '@babel/helper-split-export-declaration': 7.16.7 '@babel/parser': 7.18.4 '@babel/types': 7.18.4 - debug: 4.3.3 + debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types/7.15.0: - resolution: {integrity: sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==} + /@babel/traverse/7.19.6: + resolution: {integrity: sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.14.9 - to-fast-properties: 2.0.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.19.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.19.6 + '@babel/types': 7.19.4 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color dev: true - /@babel/types/7.16.7: - resolution: {integrity: sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg==} + /@babel/types/7.17.0: + resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-validator-identifier': 7.16.7 to-fast-properties: 2.0.0 dev: true - /@babel/types/7.17.0: - resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} + /@babel/types/7.18.4: + resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-validator-identifier': 7.16.7 to-fast-properties: 2.0.0 dev: true - /@babel/types/7.18.4: - resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==} + /@babel/types/7.19.4: + resolution: {integrity: sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.16.7 + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 dev: true + /@base2/pretty-print-object/1.0.1: + resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} + dev: true + /@bcoe/v8-coverage/0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@cnakazawa/watch/1.0.4: + resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==} + engines: {node: '>=0.1.95'} + hasBin: true + dependencies: + exec-sh: 0.3.6 + minimist: 1.2.5 + dev: true + + /@colors/colors/1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + requiresBuild: true + dev: true + optional: true + /@creativebulma/bulma-tooltip/1.2.0: resolution: {integrity: sha512-ooImbeXEBxf77cttbzA7X5rC5aAWm9UsXIGViFOnsqB+6M944GkB28S5R4UWRqjFd2iW4zGEkEifAU+q43pt2w==} dev: true + /@discoveryjs/json-ext/0.5.7: + resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} + engines: {node: '>=10.0.0'} + dev: true + + /@emotion/cache/10.0.29: + resolution: {integrity: sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==} + dependencies: + '@emotion/sheet': 0.9.4 + '@emotion/stylis': 0.8.5 + '@emotion/utils': 0.11.3 + '@emotion/weak-memoize': 0.2.5 + dev: true + + /@emotion/core/10.3.1: + resolution: {integrity: sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==} + peerDependencies: + react: '>=16.3.0' + dependencies: + '@babel/runtime': 7.17.8 + '@emotion/cache': 10.0.29 + '@emotion/css': 10.0.27 + '@emotion/serialize': 0.11.16 + '@emotion/sheet': 0.9.4 + '@emotion/utils': 0.11.3 + dev: true + + /@emotion/core/10.3.1_react@16.14.0: + resolution: {integrity: sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==} + peerDependencies: + react: '>=16.3.0' + dependencies: + '@babel/runtime': 7.17.8 + '@emotion/cache': 10.0.29 + '@emotion/css': 10.0.27 + '@emotion/serialize': 0.11.16 + '@emotion/sheet': 0.9.4 + '@emotion/utils': 0.11.3 + react: 16.14.0 + dev: true + + /@emotion/css/10.0.27: + resolution: {integrity: sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==} + dependencies: + '@emotion/serialize': 0.11.16 + '@emotion/utils': 0.11.3 + babel-plugin-emotion: 10.2.2 + dev: true + + /@emotion/hash/0.8.0: + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + dev: true + /@emotion/is-prop-valid/0.8.8: resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} dependencies: @@ -2901,18 +4750,115 @@ packages: resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} dev: true + /@emotion/serialize/0.11.16: + resolution: {integrity: sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==} + dependencies: + '@emotion/hash': 0.8.0 + '@emotion/memoize': 0.7.4 + '@emotion/unitless': 0.7.5 + '@emotion/utils': 0.11.3 + csstype: 2.6.21 + dev: true + + /@emotion/sheet/0.9.4: + resolution: {integrity: sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==} + dev: true + + /@emotion/styled-base/10.3.0_@emotion+core@10.3.1: + resolution: {integrity: sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w==} + peerDependencies: + '@emotion/core': ^10.0.28 + react: '>=16.3.0' + dependencies: + '@babel/runtime': 7.17.8 + '@emotion/core': 10.3.1 + '@emotion/is-prop-valid': 0.8.8 + '@emotion/serialize': 0.11.16 + '@emotion/utils': 0.11.3 + dev: true + + /@emotion/styled-base/10.3.0_qzeatvug73zaio2r3dlvejynye: + resolution: {integrity: sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w==} + peerDependencies: + '@emotion/core': ^10.0.28 + react: '>=16.3.0' + dependencies: + '@babel/runtime': 7.17.8 + '@emotion/core': 10.3.1_react@16.14.0 + '@emotion/is-prop-valid': 0.8.8 + '@emotion/serialize': 0.11.16 + '@emotion/utils': 0.11.3 + react: 16.14.0 + dev: true + + /@emotion/styled/10.3.0_@emotion+core@10.3.1: + resolution: {integrity: sha512-GgcUpXBBEU5ido+/p/mCT2/Xx+Oqmp9JzQRuC+a4lYM4i4LBBn/dWvc0rQ19N9ObA8/T4NWMrPNe79kMBDJqoQ==} + peerDependencies: + '@emotion/core': ^10.0.27 + react: '>=16.3.0' + dependencies: + '@emotion/core': 10.3.1 + '@emotion/styled-base': 10.3.0_@emotion+core@10.3.1 + babel-plugin-emotion: 10.2.2 + dev: true + + /@emotion/styled/10.3.0_qzeatvug73zaio2r3dlvejynye: + resolution: {integrity: sha512-GgcUpXBBEU5ido+/p/mCT2/Xx+Oqmp9JzQRuC+a4lYM4i4LBBn/dWvc0rQ19N9ObA8/T4NWMrPNe79kMBDJqoQ==} + peerDependencies: + '@emotion/core': ^10.0.27 + react: '>=16.3.0' + dependencies: + '@emotion/core': 10.3.1_react@16.14.0 + '@emotion/styled-base': 10.3.0_qzeatvug73zaio2r3dlvejynye + babel-plugin-emotion: 10.2.2 + react: 16.14.0 + dev: true + + /@emotion/stylis/0.8.5: + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} + dev: true + + /@emotion/unitless/0.7.5: + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + dev: true + + /@emotion/utils/0.11.3: + resolution: {integrity: sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==} + dev: true + + /@emotion/weak-memoize/0.2.5: + resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==} + dev: true + + /@eslint/eslintrc/0.4.3: + resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 7.3.1 + globals: 13.12.1 + ignore: 4.0.6 + import-fresh: 3.3.0 + js-yaml: 3.14.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + /@eslint/eslintrc/1.0.5: resolution: {integrity: sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.3 + debug: 4.3.4 espree: 9.3.0 globals: 13.12.1 ignore: 4.0.6 import-fresh: 3.3.0 js-yaml: 4.1.0 - minimatch: 3.0.5 + minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color @@ -2922,13 +4868,56 @@ packages: resolution: {integrity: sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==} dev: true + /@hapi/address/2.1.4: + resolution: {integrity: sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==} + deprecated: Moved to 'npm install @sideway/address' + dev: true + + /@hapi/bourne/1.3.2: + resolution: {integrity: sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==} + deprecated: This version has been deprecated and is no longer supported or maintained + dev: true + + /@hapi/hoek/8.5.1: + resolution: {integrity: sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==} + deprecated: This version has been deprecated and is no longer supported or maintained + dev: true + + /@hapi/joi/15.1.1: + resolution: {integrity: sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==} + deprecated: Switch to 'npm install joi' + dependencies: + '@hapi/address': 2.1.4 + '@hapi/bourne': 1.3.2 + '@hapi/hoek': 8.5.1 + '@hapi/topo': 3.1.6 + dev: true + + /@hapi/topo/3.1.6: + resolution: {integrity: sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==} + deprecated: This version has been deprecated and is no longer supported or maintained + dependencies: + '@hapi/hoek': 8.5.1 + dev: true + + /@humanwhocodes/config-array/0.5.0: + resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /@humanwhocodes/config-array/0.9.3: resolution: {integrity: sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==} engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.3 - minimatch: 3.0.5 + debug: 4.3.4 + minimatch: 3.1.2 transitivePeerDependencies: - supports-color dev: true @@ -2953,6 +4942,391 @@ packages: engines: {node: '>=8'} dev: true + /@jest/console/26.6.2: + resolution: {integrity: sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + chalk: 4.1.2 + jest-message-util: 26.6.2 + jest-util: 26.6.2 + slash: 3.0.0 + dev: true + + /@jest/console/27.5.1: + resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + chalk: 4.1.2 + jest-message-util: 27.5.1 + jest-util: 27.5.1 + slash: 3.0.0 + dev: true + + /@jest/console/28.1.3: + resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/types': 28.1.3 + '@types/node': 18.8.5 + chalk: 4.1.2 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + slash: 3.0.0 + dev: true + + /@jest/core/26.6.3: + resolution: {integrity: sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/console': 26.6.2 + '@jest/reporters': 26.6.2 + '@jest/test-result': 26.6.2 + '@jest/transform': 26.6.2 + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.10 + jest-changed-files: 26.6.2 + jest-config: 26.6.3 + jest-haste-map: 26.6.2 + jest-message-util: 26.6.2 + jest-regex-util: 26.0.0 + jest-resolve: 26.6.2 + jest-resolve-dependencies: 26.6.3 + jest-runner: 26.6.3 + jest-runtime: 26.6.3 + jest-snapshot: 26.6.2 + jest-util: 26.6.2 + jest-validate: 26.6.2 + jest-watcher: 26.6.2 + micromatch: 4.0.5 + p-each-series: 2.2.0 + rimraf: 3.0.2 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /@jest/core/27.5.1: + resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 27.5.1 + '@jest/reporters': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.8.1 + exit: 0.1.2 + graceful-fs: 4.2.10 + jest-changed-files: 27.5.1 + jest-config: 27.5.1 + jest-haste-map: 27.5.1 + jest-message-util: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-resolve-dependencies: 27.5.1 + jest-runner: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + jest-watcher: 27.5.1 + micromatch: 4.0.5 + rimraf: 3.0.2 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /@jest/environment/26.6.2: + resolution: {integrity: sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/fake-timers': 26.6.2 + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + jest-mock: 26.6.2 + dev: true + + /@jest/environment/27.5.1: + resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + jest-mock: 27.5.1 + dev: true + + /@jest/fake-timers/26.6.2: + resolution: {integrity: sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + '@sinonjs/fake-timers': 6.0.1 + '@types/node': 18.8.5 + jest-message-util: 26.6.2 + jest-mock: 26.6.2 + jest-util: 26.6.2 + dev: true + + /@jest/fake-timers/27.5.1: + resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + '@sinonjs/fake-timers': 8.1.0 + '@types/node': 18.8.5 + jest-message-util: 27.5.1 + jest-mock: 27.5.1 + jest-util: 27.5.1 + dev: true + + /@jest/globals/26.6.2: + resolution: {integrity: sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/environment': 26.6.2 + '@jest/types': 26.6.2 + expect: 26.6.2 + dev: true + + /@jest/globals/27.5.1: + resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/environment': 27.5.1 + '@jest/types': 27.5.1 + expect: 27.5.1 + dev: true + + /@jest/reporters/26.6.2: + resolution: {integrity: sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==} + engines: {node: '>= 10.14.2'} + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 26.6.2 + '@jest/test-result': 26.6.2 + '@jest/transform': 26.6.2 + '@jest/types': 26.6.2 + chalk: 4.1.2 + collect-v8-coverage: 1.0.1 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 4.0.3 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.3 + jest-haste-map: 26.6.2 + jest-resolve: 26.6.2 + jest-util: 26.6.2 + jest-worker: 26.6.2 + slash: 3.0.0 + source-map: 0.6.1 + string-length: 4.0.2 + terminal-link: 2.1.1 + v8-to-istanbul: 7.1.2 + optionalDependencies: + node-notifier: 8.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters/27.5.1: + resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + chalk: 4.1.2 + collect-v8-coverage: 1.0.1 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.3 + jest-haste-map: 27.5.1 + jest-resolve: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + slash: 3.0.0 + source-map: 0.6.1 + string-length: 4.0.2 + terminal-link: 2.1.1 + v8-to-istanbul: 8.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas/28.1.3: + resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@sinclair/typebox': 0.24.50 + dev: true + + /@jest/source-map/26.6.2: + resolution: {integrity: sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==} + engines: {node: '>= 10.14.2'} + dependencies: + callsites: 3.1.0 + graceful-fs: 4.2.10 + source-map: 0.6.1 + dev: true + + /@jest/source-map/27.5.1: + resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + callsites: 3.1.0 + graceful-fs: 4.2.10 + source-map: 0.6.1 + dev: true + + /@jest/test-result/26.6.2: + resolution: {integrity: sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/console': 26.6.2 + '@jest/types': 26.6.2 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.1 + dev: true + + /@jest/test-result/27.5.1: + resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/console': 27.5.1 + '@jest/types': 27.5.1 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.1 + dev: true + + /@jest/test-result/28.1.3: + resolution: {integrity: sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/console': 28.1.3 + '@jest/types': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.1 + dev: true + + /@jest/test-sequencer/26.6.3: + resolution: {integrity: sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/test-result': 26.6.2 + graceful-fs: 4.2.10 + jest-haste-map: 26.6.2 + jest-runner: 26.6.3 + jest-runtime: 26.6.3 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /@jest/test-sequencer/27.5.1: + resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/test-result': 27.5.1 + graceful-fs: 4.2.10 + jest-haste-map: 27.5.1 + jest-runtime: 27.5.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/transform/26.6.2: + resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} + engines: {node: '>= 10.14.2'} + dependencies: + '@babel/core': 7.19.6 + '@jest/types': 26.6.2 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.8.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.10 + jest-haste-map: 26.6.2 + jest-regex-util: 26.0.0 + jest-util: 26.6.2 + micromatch: 4.0.5 + pirates: 4.0.5 + slash: 3.0.0 + source-map: 0.6.1 + write-file-atomic: 3.0.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/transform/27.5.1: + resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@babel/core': 7.19.6 + '@jest/types': 27.5.1 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.8.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.10 + jest-haste-map: 27.5.1 + jest-regex-util: 27.5.1 + jest-util: 27.5.1 + micromatch: 4.0.5 + pirates: 4.0.5 + slash: 3.0.0 + source-map: 0.6.1 + write-file-atomic: 3.0.3 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/types/26.6.2: resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} engines: {node: '>= 10.14.2'} @@ -2964,6 +5338,29 @@ packages: chalk: 4.1.2 dev: true + /@jest/types/27.5.1: + resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.8.5 + '@types/yargs': 16.0.4 + chalk: 4.1.2 + dev: true + + /@jest/types/28.1.3: + resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/schemas': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.8.5 + '@types/yargs': 17.0.13 + chalk: 4.1.2 + dev: true + /@jridgewell/gen-mapping/0.3.1: resolution: {integrity: sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==} engines: {node: '>=6.0.0'} @@ -2973,6 +5370,15 @@ packages: '@jridgewell/trace-mapping': 0.3.13 dev: true + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.1 + '@jridgewell/sourcemap-codec': 1.4.10 + '@jridgewell/trace-mapping': 0.3.13 + dev: true + /@jridgewell/resolve-uri/3.0.4: resolution: {integrity: sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg==} engines: {node: '>=6.0.0'} @@ -2994,21 +5400,14 @@ packages: '@jridgewell/sourcemap-codec': 1.4.10 dev: true - /@jridgewell/trace-mapping/0.3.4: - resolution: {integrity: sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==} - dependencies: - '@jridgewell/resolve-uri': 3.0.4 - '@jridgewell/sourcemap-codec': 1.4.10 - dev: true - - /@linaria/babel-preset/3.0.0-beta.15_@babel+core@7.13.16: + /@linaria/babel-preset/3.0.0-beta.15_@babel+core@7.17.2: resolution: {integrity: sha512-oJIyUn2SCIH4vW1vBxOsN2BTU6Y0mq4ywkqxwQ1py4HgtEnYlvpq4Ca0ba7vXvMWk1pMCgd/V+VpMjbr19uXgQ==} peerDependencies: '@babel/core': '>=7' dependencies: - '@babel/core': 7.13.16 - '@babel/generator': 7.17.0 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.13.16 + '@babel/core': 7.17.2 + '@babel/generator': 7.18.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.2 '@babel/template': 7.16.7 '@linaria/core': 3.0.0-beta.15 '@linaria/logger': 3.0.0-beta.15 @@ -3020,26 +5419,30 @@ packages: - supports-color dev: true - /@linaria/babel-preset/3.0.0-beta.4_@babel+core@7.13.16: - resolution: {integrity: sha512-Bjsk4VZUQXK3u04MuLlyP/+/tDd7bWeLXYCOnq4US9H2QFRdka97fm6hH34SRinoHm9fSPCHrj9d+KtY8ge2wg==} - peerDependencies: - '@babel/core': '>=7' + /@linaria/babel-preset/3.0.0-beta.23: + resolution: {integrity: sha512-NhxUZokEq12RLpDo4v/f59dB9A/1BbLgGLFotnrDzNBHfylm0qXSIIel68pZOXUB5lVdPJHqZWcT2zxbpGW6fA==} + engines: {node: ^12.16.0 || >=13.7.0} dependencies: - '@babel/core': 7.13.16 - '@babel/generator': 7.15.0 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.13.16 - '@babel/template': 7.14.5 - '@linaria/core': 3.0.0-beta.4 - '@linaria/logger': 3.0.0-beta.3 + '@babel/core': 7.19.6 + '@babel/generator': 7.18.2 + '@babel/plugin-proposal-export-namespace-from': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-transform-modules-commonjs': 7.18.2_@babel+core@7.19.6 + '@babel/template': 7.16.7 + '@babel/traverse': 7.18.2 + '@linaria/core': 3.0.0-beta.22 + '@linaria/logger': 3.0.0-beta.20 + '@linaria/utils': 3.0.0-beta.20 cosmiconfig: 5.2.1 - source-map: 0.6.1 + find-up: 5.0.0 + source-map: 0.7.3 stylis: 3.5.4 transitivePeerDependencies: - supports-color dev: true - /@linaria/babel-preset/3.0.0-beta.7_@babel+core@7.13.16: - resolution: {integrity: sha512-NE5f//T9ywXZA+W+Pw1YjWKdzskUpaV7GVkxXhkxLM2la1+S4xOoZR1rzW77bR1C9GFFwzZTeb8XP85whb2ZqQ==} + /@linaria/babel-preset/3.0.0-beta.4_@babel+core@7.13.16: + resolution: {integrity: sha512-Bjsk4VZUQXK3u04MuLlyP/+/tDd7bWeLXYCOnq4US9H2QFRdka97fm6hH34SRinoHm9fSPCHrj9d+KtY8ge2wg==} peerDependencies: '@babel/core': '>=7' dependencies: @@ -3047,10 +5450,10 @@ packages: '@babel/generator': 7.15.0 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.13.16 '@babel/template': 7.14.5 - '@linaria/core': 3.0.0-beta.4 + '@linaria/core': 3.0.0-beta.22 '@linaria/logger': 3.0.0-beta.3 cosmiconfig: 5.2.1 - source-map: 0.7.3 + source-map: 0.6.1 stylis: 3.5.4 transitivePeerDependencies: - supports-color @@ -3062,6 +5465,16 @@ packages: '@linaria/utils': 3.0.0-beta.15 dev: true + /@linaria/core/3.0.0-beta.22: + resolution: {integrity: sha512-BPSecW8QmhQ0y+5cWXEja+MTmLsuo0T1PjqRlSWsmDgjJFFObqCnPEgbR1KNtQb3Msmx1/9q3dYKpA5Zk3g8KQ==} + engines: {node: ^12.16.0 || >=13.7.0} + dependencies: + '@linaria/logger': 3.0.0-beta.20 + '@linaria/utils': 3.0.0-beta.20 + transitivePeerDependencies: + - supports-color + dev: true + /@linaria/core/3.0.0-beta.4: resolution: {integrity: sha512-NzxeMDxRt57nR6tLFZ8xIstp5ld9JQPIyp9+TKtQZhoX3oJuUru+S4vXPr1Gach6VaqKKKT5T6fmJgJl9MMprw==} dev: true @@ -3072,7 +5485,7 @@ packages: '@babel/core': '>=7' dependencies: '@babel/core': 7.13.16 - '@linaria/babel-preset': 3.0.0-beta.15_@babel+core@7.13.16 + '@linaria/babel-preset': 3.0.0-beta.23 esbuild: 0.12.29 transitivePeerDependencies: - supports-color @@ -3081,7 +5494,17 @@ packages: /@linaria/logger/3.0.0-beta.15: resolution: {integrity: sha512-tKytEEus47CZ4/7pbgRYD+Por4tH7/5Um2Quof6Qamd7Xj7brhmAPRRoZzYu4pht+S7V9oNL0X4IBSmNIOYRZw==} dependencies: - debug: 4.3.3 + debug: 4.3.4 + picocolors: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@linaria/logger/3.0.0-beta.20: + resolution: {integrity: sha512-wCxWnldCHf7HXdLG3QtbKyBur+z5V1qZTouSEvcVYDfd4aSRPOi/jLdwsZlsUq2PFGpA3jW6JnreZJ/vxuEl7g==} + engines: {node: ^12.16.0 || >=13.7.0} + dependencies: + debug: 4.3.4 picocolors: 1.0.0 transitivePeerDependencies: - supports-color @@ -3090,7 +5513,7 @@ packages: /@linaria/logger/3.0.0-beta.3: resolution: {integrity: sha512-Z2k0RJuA4PffcZcwBN1By8FmcCvcFUe9GHc846B6hNP09zDVhHSFLKJN9NfXJCzJ/9PifOxSUKyOjLtpv3EhGA==} dependencies: - debug: 4.3.2 + debug: 4.3.4 transitivePeerDependencies: - supports-color dev: true @@ -3101,7 +5524,31 @@ packages: '@babel/core': '>=7' dependencies: '@babel/core': 7.13.16 - '@linaria/babel-preset': 3.0.0-beta.15_@babel+core@7.13.16 + '@linaria/babel-preset': 3.0.0-beta.23 + transitivePeerDependencies: + - supports-color + dev: true + + /@linaria/preeval/3.0.0-beta.15_@babel+core@7.17.2: + resolution: {integrity: sha512-621wPHry4L4YFRdMXE0wxVKsocFL2KC+RECSdc6Oy4MVtMMeTyps671pV27+kQldWIRvp1ZsdhknNMEug+rKdg==} + peerDependencies: + '@babel/core': '>=7' + dependencies: + '@babel/core': 7.17.2 + '@linaria/babel-preset': 3.0.0-beta.23 + transitivePeerDependencies: + - supports-color + dev: true + + /@linaria/react/3.0.0-beta.22: + resolution: {integrity: sha512-14rnb/zkzhFhJM3hbBOzLLS0bu01mOg23Rv2nGQUt5CWd+HOhksmqzqBtC/ijeVlY2hRI0rJJcng9r07LGGAPA==} + engines: {node: ^12.16.0 || >=13.7.0} + peerDependencies: + react: '>=16' + dependencies: + '@emotion/is-prop-valid': 0.8.8 + '@linaria/core': 3.0.0-beta.22 + ts-invariant: 0.10.3 transitivePeerDependencies: - supports-color dev: true @@ -3112,7 +5559,19 @@ packages: react: '>=16' dependencies: '@emotion/is-prop-valid': 0.8.8 - '@linaria/core': 3.0.0-beta.4 + '@linaria/core': 3.0.0-beta.22 + transitivePeerDependencies: + - supports-color + dev: true + + /@linaria/rollup/3.0.0-beta.23: + resolution: {integrity: sha512-qi6lOsMHN3wYfBy3/4kz9bJcK1cPVTThWyfVh8OJxFOdqXi/C8ogT69VobsNVqrng/0REAXYc082m1ovxYGBgA==} + engines: {node: ^12.16.0 || >=13.7.0} + dependencies: + '@linaria/babel-preset': 3.0.0-beta.23 + '@rollup/pluginutils': 4.2.1 + transitivePeerDependencies: + - supports-color dev: true /@linaria/shaker/3.0.0-beta.15_@babel+core@7.13.16: @@ -3125,7 +5584,7 @@ packages: '@babel/plugin-transform-runtime': 7.17.0_@babel+core@7.13.16 '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.13.16 '@babel/preset-env': 7.16.11_@babel+core@7.13.16 - '@linaria/babel-preset': 3.0.0-beta.15_@babel+core@7.13.16 + '@linaria/babel-preset': 3.0.0-beta.23 '@linaria/logger': 3.0.0-beta.15 '@linaria/preeval': 3.0.0-beta.15_@babel+core@7.13.16 babel-plugin-transform-react-remove-prop-types: 0.4.24 @@ -3134,10 +5593,45 @@ packages: - supports-color dev: true + /@linaria/shaker/3.0.0-beta.15_@babel+core@7.17.2: + resolution: {integrity: sha512-+PZRf8hM7fO4RiNpVVYUhIXQPPKYxowXYMaiILq/9zGcy2tQ6onqsKaQ71SCXvVrL+gVo6A/R13njOCJRZRTew==} + peerDependencies: + '@babel/core': '>=7' + dependencies: + '@babel/core': 7.17.2 + '@babel/generator': 7.17.0 + '@babel/plugin-transform-runtime': 7.17.0_@babel+core@7.17.2 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@linaria/babel-preset': 3.0.0-beta.23 + '@linaria/logger': 3.0.0-beta.15 + '@linaria/preeval': 3.0.0-beta.15_@babel+core@7.17.2 + babel-plugin-transform-react-remove-prop-types: 0.4.24 + ts-invariant: 0.9.4 + transitivePeerDependencies: + - supports-color + dev: true + /@linaria/utils/3.0.0-beta.15: resolution: {integrity: sha512-CF0T8ueWjHK8zJT0oqdAq8JgL9a40WCyE5/t6q8WPvC7xRdsrfyUKA7Z8qEsQse2LKESVWJ5cvkkn8J80B6c+A==} dev: true + /@linaria/utils/3.0.0-beta.20: + resolution: {integrity: sha512-SKRC9dBApzu0kTksVtGZ7eJz1vMu7xew/JEAjQj6XTQDblzWpTPyKQHBOGXNkqXjIB8PwAqWfvKzKapzaOwQaQ==} + engines: {node: ^12.16.0 || >=13.7.0} + dev: true + + /@linaria/webpack-loader/3.0.0-beta.23: + resolution: {integrity: sha512-DC6RhGyIw/EHwwKnywty8PLpDfDVD/hdiWxECw0dnuXe6t8ye5OoPhc14gDx42PjEFrVYf6IJprPlXnX0Usp2Q==} + engines: {node: ^12.16.0 || >=13.7.0} + dependencies: + '@linaria/webpack4-loader': 3.0.0-beta.23 + '@linaria/webpack5-loader': 3.0.0-beta.23 + transitivePeerDependencies: + - supports-color + - webpack + dev: true + /@linaria/webpack-loader/3.0.0-beta.4_@babel+core@7.13.16: resolution: {integrity: sha512-v2Z4QgkBwddKwS/M0ISkLKQBPBNfoyw4AGCBFsjFO5ov/icNrmIy8BKUFCi/yqWu8mk/krmvzPyOBSp4DpFsIA==} dependencies: @@ -3149,14 +5643,29 @@ packages: - webpack dev: true + /@linaria/webpack4-loader/3.0.0-beta.23: + resolution: {integrity: sha512-I1pwrRKpGCARWbPwTFqOKLrkyxrZ+huYC3WH4pMllfoY+fv3O2dmDH6vKrZ582mQ5Uo/H3FmHBt8CLaMBv3pmg==} + engines: {node: ^12.16.0 || >=13.7.0} + peerDependencies: + webpack: '>=4.0.0 <5.0.0' + dependencies: + '@linaria/babel-preset': 3.0.0-beta.23 + '@linaria/logger': 3.0.0-beta.20 + enhanced-resolve: 4.5.0 + loader-utils: 1.4.0 + mkdirp: 0.5.5 + transitivePeerDependencies: + - supports-color + dev: true + /@linaria/webpack4-loader/3.0.0-beta.7_@babel+core@7.13.16: resolution: {integrity: sha512-B2c5vr9b8igcILM/ZcxE9Vu0J2w7NS9xERTvGD7Kp4TdLnFRpALMTJgYqlk3Gxq4T7RlAEi1vu8kHx65mXqA6g==} peerDependencies: '@babel/core': '>=7' dependencies: '@babel/core': 7.13.16 - '@linaria/babel-preset': 3.0.0-beta.7_@babel+core@7.13.16 - '@linaria/logger': 3.0.0-beta.3 + '@linaria/babel-preset': 3.0.0-beta.23 + '@linaria/logger': 3.0.0-beta.20 cosmiconfig: 5.2.1 enhanced-resolve: 4.5.0 find-yarn-workspace-root: 1.2.1 @@ -3167,6 +5676,20 @@ packages: - supports-color dev: true + /@linaria/webpack5-loader/3.0.0-beta.23: + resolution: {integrity: sha512-yIjhnDT1otwfx6JAA9HNfDzim7N93z9++8apzXE57GXg5wRO2hlajruatclpUDcMOsodS9p2+mMXd8GGR8CGCA==} + engines: {node: ^12.16.0 || >=13.7.0} + peerDependencies: + webpack: ^5.0.0 + dependencies: + '@linaria/babel-preset': 3.0.0-beta.23 + '@linaria/logger': 3.0.0-beta.20 + enhanced-resolve: 5.8.2 + mkdirp: 0.5.5 + transitivePeerDependencies: + - supports-color + dev: true + /@linaria/webpack5-loader/3.0.0-beta.7_@babel+core@7.13.16: resolution: {integrity: sha512-s2C44ml1fjDFjEJS1PFXjgCklOd3KWiG4Z3l+nUuCidncn9abnv18rDkiukUcKGwwAGJ3NhgfhU9SwXPIYFfMw==} peerDependencies: @@ -3174,18 +5697,87 @@ packages: webpack: '>=5' dependencies: '@babel/core': 7.13.16 - '@linaria/babel-preset': 3.0.0-beta.7_@babel+core@7.13.16 - '@linaria/logger': 3.0.0-beta.3 + '@linaria/babel-preset': 3.0.0-beta.23 + '@linaria/logger': 3.0.0-beta.20 cosmiconfig: 5.2.1 enhanced-resolve: 5.8.2 find-yarn-workspace-root: 1.2.1 - loader-utils: 2.0.0 + loader-utils: 2.0.2 mkdirp: 0.5.5 normalize-path: 3.0.0 transitivePeerDependencies: - supports-color dev: true + /@mdn/browser-compat-data/3.3.14: + resolution: {integrity: sha512-n2RC9d6XatVbWFdHLimzzUJxJ1KY8LdjqrW6YvGPiRmsHkhOUx74/Ct10x5Yo7bC/Jvqx7cDEW8IMPv/+vwEzA==} + dev: true + + /@mdn/browser-compat-data/4.2.1: + resolution: {integrity: sha512-EWUguj2kd7ldmrF9F+vI5hUOralPd+sdsUnYbRy33vZTuZkduC1shE9TtEMEjAQwyfyMb4ole5KtjF8MsnQOlA==} + dev: true + + /@mdx-js/loader/1.6.22: + resolution: {integrity: sha512-9CjGwy595NaxAYp0hF9B/A0lH6C8Rms97e2JS9d3jVUtILn6pT5i5IV965ra3lIWc7Rs1GG1tBdVF7dCowYe6Q==} + dependencies: + '@mdx-js/mdx': 1.6.22 + '@mdx-js/react': 1.6.22 + loader-utils: 2.0.0 + transitivePeerDependencies: + - react + - supports-color + dev: true + + /@mdx-js/mdx/1.6.22: + resolution: {integrity: sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==} + dependencies: + '@babel/core': 7.12.9 + '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.12.9 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 + '@mdx-js/util': 1.6.22 + babel-plugin-apply-mdx-type-prop: 1.6.22_@babel+core@7.12.9 + babel-plugin-extract-import-names: 1.6.22 + camelcase-css: 2.0.1 + detab: 2.0.4 + hast-util-raw: 6.0.1 + lodash.uniq: 4.5.0 + mdast-util-to-hast: 10.0.1 + remark-footnotes: 2.0.0 + remark-mdx: 1.6.22 + remark-parse: 8.0.3 + remark-squeeze-paragraphs: 4.0.0 + style-to-object: 0.3.0 + unified: 9.2.0 + unist-builder: 2.0.3 + unist-util-visit: 2.0.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@mdx-js/react/1.6.22: + resolution: {integrity: sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==} + peerDependencies: + react: ^16.13.1 || ^17.0.0 + dev: true + + /@mdx-js/util/1.6.22: + resolution: {integrity: sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==} + dev: true + + /@mrmlnc/readdir-enhanced/2.2.1: + resolution: {integrity: sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==} + engines: {node: '>=4'} + dependencies: + call-me-maybe: 1.0.1 + glob-to-regexp: 0.3.0 + dev: true + + /@nicolo-ribaudo/eslint-scope-5-internals/5.1.1-v1: + resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} + dependencies: + eslint-scope: 5.1.1 + dev: true + /@nodelib/fs.scandir/2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -3194,6 +5786,11 @@ packages: run-parallel: 1.2.0 dev: true + /@nodelib/fs.stat/1.1.3: + resolution: {integrity: sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==} + engines: {node: '>= 6'} + dev: true + /@nodelib/fs.stat/2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} @@ -3210,9 +5807,10 @@ packages: /@npmcli/fs/1.1.0: resolution: {integrity: sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==} engines: {node: ^12.13.0 || ^14.15.0 || >=16} + deprecated: this version had an improper engines field added, update to 1.1.1 dependencies: '@gar/promisify': 1.1.2 - semver: 7.3.5 + semver: 7.3.8 dev: true /@npmcli/move-file/1.1.2: @@ -3227,6 +5825,21 @@ packages: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true + /@popperjs/core/2.11.6: + resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} + dev: true + + /@preact/async-loader/3.0.1_preact@10.6.1: + resolution: {integrity: sha512-BoUN24hxEfAQYnWjliAmkZLuliv+ONQi7AWn+/+VOJHTIHmbFiXrvmSxITf7PDkKiK0a5xy4OErZtVVLlk96Tg==} + engines: {node: '>=8'} + peerDependencies: + preact: '>= 10.0.0' + dependencies: + kleur: 4.1.4 + loader-utils: 2.0.2 + preact: 10.6.1 + dev: true + /@preact/async-loader/3.0.1_preact@10.6.5: resolution: {integrity: sha512-BoUN24hxEfAQYnWjliAmkZLuliv+ONQi7AWn+/+VOJHTIHmbFiXrvmSxITf7PDkKiK0a5xy4OErZtVVLlk96Tg==} engines: {node: '>=8'} @@ -3242,6 +5855,22 @@ packages: resolution: {integrity: sha512-gj3ekiYtHlZNz0zFI1z6a9mcYX80Qacw84+2++7V1skvO7kQoV2ux56r8bJkTBbKMVxwAgaYrxxIdUCYlclE7Q==} dev: true + /@prefresh/core/0.8.1_preact@10.6.5: + resolution: {integrity: sha512-woho+Ja8w3pxnZwq68MnWzH9ffdidrpJsV6PDTNIsJOpsLYmfCNxqxGsxIqYw40d1yjg4h6HFGbb6Y9lhyTPNA==} + peerDependencies: + preact: ^10.0.0 + dependencies: + preact: 10.6.5 + dev: true + + /@prefresh/core/1.3.2_preact@10.6.1: + resolution: {integrity: sha512-Iv+uI698KDgWsrKpLvOgN3hmAMyvhVgn09mcnhZ98BUNdg/qrxE7tcUf5yFCImkgqED5/Dcn8G5hFy4IikEDvg==} + peerDependencies: + preact: ^10.0.0 + dependencies: + preact: 10.6.1 + dev: true + /@prefresh/core/1.3.2_preact@10.6.5: resolution: {integrity: sha512-Iv+uI698KDgWsrKpLvOgN3hmAMyvhVgn09mcnhZ98BUNdg/qrxE7tcUf5yFCImkgqED5/Dcn8G5hFy4IikEDvg==} peerDependencies: @@ -3250,10 +5879,26 @@ packages: preact: 10.6.5 dev: true + /@prefresh/utils/0.3.1: + resolution: {integrity: sha512-9kLzPWN4teeiKuc+Rle3SF/hyx5lzo35X4rHr+kQXnJT+BaEb1ymDWIHGkv85xjnw8+l6I1r1H7JB4BHOMJfmg==} + dev: true + /@prefresh/utils/1.1.1: resolution: {integrity: sha512-MUhT5m2XNN5NsZl4GnpuvlzLo6VSTa/+wBfBd3fiWUvHGhv0GF9hnA1pd//v0uJaKwUnVRQ1hYElxCV7DtYsCQ==} dev: true + /@prefresh/webpack/1.1.0_iaukxvobhnxulwhqqdnbfsnwxu: + resolution: {integrity: sha512-a3JG2maH3bacDobb4WywVTuqvAyBxJ7dRNSG2Ywv1AytAdgpgNZKJpR4xUTzPTwPGpRkfNOOf4mODqoOZ7W0Sw==} + peerDependencies: + preact: ^10.4.0 + webpack: ^4.0.0 || ^5.0.0 + dependencies: + '@prefresh/core': 0.8.1_preact@10.6.5 + '@prefresh/utils': 0.3.1 + preact: 10.6.5 + webpack: 4.46.0 + dev: true + /@prefresh/webpack/3.3.2_dveknyjmyxkzkf4ybureeu5fae: resolution: {integrity: sha512-1cX0t5G7IXWO2164sl2O32G02BzDl6C4UUZWfDb0x1CQM1g3It9PSLWd+rIlHfSg4MEU9YHM8e6/OK8uavRJhA==} peerDependencies: @@ -3268,6 +5913,56 @@ packages: webpack: 4.46.0 dev: true + /@prefresh/webpack/3.3.2_ev3yg7augojg2gbov5lykbl5qy: + resolution: {integrity: sha512-1cX0t5G7IXWO2164sl2O32G02BzDl6C4UUZWfDb0x1CQM1g3It9PSLWd+rIlHfSg4MEU9YHM8e6/OK8uavRJhA==} + peerDependencies: + '@prefresh/babel-plugin': ^0.4.0 + preact: ^10.4.0 + webpack: ^4.0.0 || ^5.0.0 + dependencies: + '@prefresh/babel-plugin': 0.4.1 + '@prefresh/core': 1.3.2_preact@10.6.1 + '@prefresh/utils': 1.1.1 + preact: 10.6.1 + webpack: 4.46.0 + dev: true + + /@reach/router/1.3.4: + resolution: {integrity: sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA==} + peerDependencies: + react: 15.x || 16.x || 16.4.0-alpha.0911da3 + react-dom: 15.x || 16.x || 16.4.0-alpha.0911da3 + dependencies: + create-react-context: 0.3.0_prop-types@15.8.1 + invariant: 2.2.4 + prop-types: 15.8.1 + react-lifecycles-compat: 3.0.4 + dev: true + + /@reach/router/1.3.4_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA==} + peerDependencies: + react: 15.x || 16.x || 16.4.0-alpha.0911da3 + react-dom: 15.x || 16.x || 16.4.0-alpha.0911da3 + dependencies: + create-react-context: 0.3.0_4vyaxm4rsh2mpfdenvlqy7kmya + invariant: 2.2.4 + prop-types: 15.8.1 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-lifecycles-compat: 3.0.4 + dev: true + + /@rollup/plugin-alias/3.1.9_rollup@2.79.0: + resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==} + engines: {node: '>=8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + rollup: 2.79.0 + slash: 3.0.0 + dev: true + /@rollup/plugin-babel/5.3.0_lubkdqoa5gexe4rox23cswxwm4: resolution: {integrity: sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==} engines: {node: '>= 10.0.0'} @@ -3285,6 +5980,39 @@ packages: rollup: 2.79.0 dev: true + /@rollup/plugin-babel/5.3.0_s45mkc5s7is4owdeow33qgy2s4: + resolution: {integrity: sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-module-imports': 7.16.7 + '@rollup/pluginutils': 3.1.0_rollup@2.79.0 + rollup: 2.79.0 + dev: true + + /@rollup/plugin-commonjs/20.0.0_rollup@2.79.0: + resolution: {integrity: sha512-5K0g5W2Ol8hAcTHqcTBHiA7M58tfmYi1o9KxeJuuRNpGaTa5iLjcyemBitCBcKXaHamOBBEH2dGom6v6Unmqjg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^2.38.3 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.0 + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 7.2.3 + is-reference: 1.2.1 + magic-string: 0.25.9 + resolve: 1.22.1 + rollup: 2.79.0 + dev: true + /@rollup/plugin-commonjs/22.0.2_rollup@2.79.0: resolution: {integrity: sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==} engines: {node: '>= 12.0.0'} @@ -3301,6 +6029,26 @@ packages: rollup: 2.79.0 dev: true + /@rollup/plugin-html/0.2.4_rollup@2.79.0: + resolution: {integrity: sha512-x0qpNXxbmGa9Jnl4OX89AORPe2T/a4DqNK69BGRnEdaPKq6MdiUXSTam/eCkF5DxkQGcRcPq0L4vzr/E3q4mVA==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + rollup: 2.79.0 + dev: true + + /@rollup/plugin-image/2.1.1_rollup@2.79.0: + resolution: {integrity: sha512-AgP4U85zuQJdUopLUCM+hTf45RepgXeTb8EJsleExVy99dIoYpt3ZlDYJdKmAc2KLkNntCDg6BPJvgJU3uGF+g==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.0 + mini-svg-data-uri: 1.4.4 + rollup: 2.79.0 + dev: true + /@rollup/plugin-json/4.1.0_rollup@2.79.0: resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==} peerDependencies: @@ -3340,6 +6088,30 @@ packages: rollup: 2.79.0 dev: true + /@rollup/plugin-node-resolve/7.1.3_rollup@1.32.1: + resolution: {integrity: sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@1.32.1 + '@types/resolve': 0.0.8 + builtin-modules: 3.3.0 + is-module: 1.0.0 + resolve: 1.22.1 + rollup: 1.32.1 + dev: true + + /@rollup/plugin-replace/2.4.2_rollup@1.32.1: + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@1.32.1 + magic-string: 0.25.9 + rollup: 1.32.1 + dev: true + /@rollup/plugin-replace/2.4.2_rollup@2.79.0: resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} peerDependencies: @@ -3350,6 +6122,16 @@ packages: rollup: 2.79.0 dev: true + /@rollup/plugin-replace/3.1.0_rollup@2.79.0: + resolution: {integrity: sha512-pA3XRUrSKybVYqmH5TqWNZpGxF+VV+1GrYchKgCNIj2vsSOX7CVm2RCtx8p2nrC7xvkziYyK+lSi74T93MU3YA==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.0 + magic-string: 0.25.9 + rollup: 2.79.0 + dev: true + /@rollup/plugin-replace/4.0.0_rollup@2.79.0: resolution: {integrity: sha512-+rumQFiaNac9y64OHtkHGmdjm7us9bo1PlbgQfdihQtuNxzjpaB064HbRnewUOggLQxVCCyINfStkgmBeQpv1g==} peerDependencies: @@ -3360,6 +6142,36 @@ packages: rollup: 2.79.0 dev: true + /@rollup/plugin-typescript/8.5.0_jnsxykt6ocebvgsxrxe2hsbo6y: + resolution: {integrity: sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==} + engines: {node: '>=8.0.0'} + peerDependencies: + rollup: ^2.14.0 + tslib: '*' + typescript: '>=3.7.0' + peerDependenciesMeta: + tslib: + optional: true + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.0 + resolve: 1.22.1 + rollup: 2.79.0 + tslib: 2.4.0 + typescript: 4.8.4 + dev: true + + /@rollup/pluginutils/3.1.0_rollup@1.32.1: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 1.32.1 + dev: true + /@rollup/pluginutils/3.1.0_rollup@2.79.0: resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} @@ -3372,16 +6184,2955 @@ packages: rollup: 2.79.0 dev: true + /@rollup/pluginutils/4.2.1: + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: true + + /@sinclair/typebox/0.24.50: + resolution: {integrity: sha512-k8ETQOOQDg5FtK7y9KJWpsGLik+QlPmIi8zzl/dGUgshV2QitprkFlCR/AemjWOTyKn9UwSSGRTzLVotvgCjYQ==} + dev: true + /@sindresorhus/is/0.14.0: resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} engines: {node: '>=6'} dev: true + /@sinonjs/commons/1.8.3: + resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers/6.0.1: + resolution: {integrity: sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==} + dependencies: + '@sinonjs/commons': 1.8.3 + dev: true + + /@sinonjs/fake-timers/8.1.0: + resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} + dependencies: + '@sinonjs/commons': 1.8.3 + dev: true + + /@storybook/addon-a11y/6.2.9: + resolution: {integrity: sha512-wo7nFpEqEeiHDsRKnhqe2gIHZ9Z7/Aefw570kBgReU5tKlmrb5rFAfTVBWGBZlLHWeJMsFsRsWrWrmkf1B52OQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/channels': 6.2.9 + '@storybook/client-api': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/theming': 6.2.9 + axe-core: 4.3.5 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + react-sizeme: 3.0.2 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/addon-a11y/6.5.13: + resolution: {integrity: sha512-+Tcl/4LWRh3ygLUZFGvkjT42CF/tJcP+kgsIho7i2MxpgZyD6+BUhL9srPZusjbR+uHcHXJ/yxw/vxFQ+UCTLA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/theming': 6.5.13 + axe-core: 4.3.5 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + react-sizeme: 3.0.2 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/addon-actions/6.2.9: + resolution: {integrity: sha512-CkUYSMt+fvuHfWvtDzlhhaeQBCWlUo99xdL88JTsTml05P43bIHZNIRv2QJ8DwhHuxdIPeHKLmz9y/ymOagOnw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/client-api': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/theming': 6.2.9 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + polished: 4.1.4 + prop-types: 15.8.1 + react-inspector: 5.1.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + uuid-browser: 3.1.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/addon-actions/6.5.13: + resolution: {integrity: sha512-3Tji0gIy95havhTpSc6CsFl5lNxGn4O5Y1U9fyji+GRkKqDFOrvVLYAHPtLOpYdEI5tF0bDo+akiqfDouY8+eA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/theming': 6.5.13 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + polished: 4.2.2 + prop-types: 15.8.1 + react-inspector: 5.1.1 + regenerator-runtime: 0.13.9 + telejson: 6.0.8 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + uuid-browser: 3.1.0 + dev: true + + /@storybook/addon-backgrounds/6.2.9: + resolution: {integrity: sha512-oPSdeoUuvaXshY5sQRagbYXpr6ZEVUuLhGYBnZTlvm19QMeNCXQE+rdlgzcgyafq4mc1FI/udE2MpJ1dhfS6pQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/theming': 6.2.9 + core-js: 3.26.0 + global: 4.4.0 + memoizerific: 1.11.3 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/addon-backgrounds/6.5.13: + resolution: {integrity: sha512-b4JX7JMY7e50y1l6g71D+2XWV3GO0TO2z1ta8J6W4OQt8f44V7sSkRQaJUzXdLjQMrA+Anojuy1ZwPjVeLC6vg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/theming': 6.5.13 + core-js: 3.26.0 + global: 4.4.0 + memoizerific: 1.11.3 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/addon-controls/6.2.9: + resolution: {integrity: sha512-NvXAJ7I5U4CLxv4wL3/Ne9rehJlgnSmQlLIG/z6dg5zm7JIb48LT4IY6GzjlUP5LkjmO9KJ8gJC249uRt2iPBQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/client-api': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/node-logger': 6.2.9 + '@storybook/theming': 6.2.9 + core-js: 3.26.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/addon-controls/6.5.13_3rubbgt5ekhqrcgx4uwls3neim: + resolution: {integrity: sha512-lYq3uf2mlVevm0bi6ueL3H6TpUMRYW9s/pTNTVJT225l27kLdFR9wEKxAkCBrlKaTgDLJmzzDRsJE3NLZlR/5Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-common': 6.5.13_3rubbgt5ekhqrcgx4uwls3neim + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/node-logger': 6.5.13 + '@storybook/store': 6.5.13 + '@storybook/theming': 6.5.13 + core-js: 3.26.0 + lodash: 4.17.21 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/addon-docs/6.2.9_dtgd2mm6ybnh5irau5bfapxhdy: + resolution: {integrity: sha512-qOtwgiqI3LMqT0eXYNV6ykp7qSu0LQGeXxy3wOBGuDDqAizfgnAjomYEWGFcyKp5ahV7HCRCjxbixAklFPUmyw==} + peerDependencies: + '@babel/core': ^7.11.5 + '@storybook/angular': 6.2.9 + '@storybook/vue': 6.2.9 + '@storybook/vue3': 6.2.9 + babel-loader: ^8.0.0 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + svelte: ^3.31.2 + sveltedoc-parser: ^4.1.0 + vue: ^2.6.10 || ^3.0.0 + webpack: '*' + peerDependenciesMeta: + '@storybook/angular': + optional: true + '@storybook/vue': + optional: true + '@storybook/vue3': + optional: true + react: + optional: true + react-dom: + optional: true + svelte: + optional: true + sveltedoc-parser: + optional: true + vue: + optional: true + webpack: + optional: true + dependencies: + '@babel/core': 7.17.2 + '@babel/generator': 7.18.2 + '@babel/parser': 7.18.4 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@jest/transform': 26.6.2 + '@mdx-js/loader': 1.6.22 + '@mdx-js/mdx': 1.6.22 + '@mdx-js/react': 1.6.22 + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/builder-webpack4': 6.2.9_o2nrgn6wwxunlqlzzokx4es3q4 + '@storybook/client-api': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/core': 6.2.9_o2nrgn6wwxunlqlzzokx4es3q4 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/node-logger': 6.2.9 + '@storybook/postinstall': 6.2.9 + '@storybook/source-loader': 6.2.9 + '@storybook/theming': 6.2.9 + acorn: 7.4.1 + acorn-jsx: 5.3.2_acorn@7.4.1 + acorn-walk: 7.2.0 + babel-loader: 8.2.3_@babel+core@7.17.2 + core-js: 3.26.0 + doctrine: 3.0.0 + escodegen: 2.0.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + html-tags: 3.2.0 + js-string-escape: 1.0.1 + loader-utils: 2.0.2 + lodash: 4.17.21 + prettier: 2.2.1 + prop-types: 15.8.1 + react-element-to-jsx-string: 14.3.4 + regenerator-runtime: 0.13.9 + remark-external-links: 8.0.0 + remark-slug: 6.1.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@types/react' + - bluebird + - encoding + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/addon-docs/6.5.13_t6isvk4c5wetct53pn5ufojx2u: + resolution: {integrity: sha512-RG/NjsheD9FixZ789RJlNyNccaR2Cuy7CtAwph4oUNi3aDFjtOI8Oe9L+FOT7qtVnZLw/YMjF+pZxoDqJNKLPw==} + peerDependencies: + '@storybook/mdx2-csf': ^0.0.3 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@storybook/mdx2-csf': + optional: true + react: + optional: true + react-dom: + optional: true + dependencies: + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@jest/transform': 26.6.2 + '@mdx-js/react': 1.6.22 + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-common': 6.5.13_3rubbgt5ekhqrcgx4uwls3neim + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/docs-tools': 6.5.13 + '@storybook/mdx1-csf': 0.0.1_@babel+core@7.17.2 + '@storybook/node-logger': 6.5.13 + '@storybook/postinstall': 6.5.13 + '@storybook/preview-web': 6.5.13 + '@storybook/source-loader': 6.5.13 + '@storybook/store': 6.5.13 + '@storybook/theming': 6.5.13 + babel-loader: 8.2.3_@babel+core@7.17.2 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + regenerator-runtime: 0.13.9 + remark-external-links: 8.0.0 + remark-slug: 6.1.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@babel/core' + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack + - webpack-cli + - webpack-command + dev: true + + /@storybook/addon-essentials/6.2.9_dtgd2mm6ybnh5irau5bfapxhdy: + resolution: {integrity: sha512-zXsV4e1TCkHyDwi7hew4h9eJfDW++f2BNKzTif+DAcjPUVFDp7yC17gLjS5IhOjcQk+db0UUlFSx/OrTxhy7Xw==} + peerDependencies: + '@babel/core': ^7.9.6 + '@storybook/vue': 6.2.9 + babel-loader: ^8.0.0 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + webpack: '*' + peerDependenciesMeta: + '@storybook/vue': + optional: true + react: + optional: true + react-dom: + optional: true + webpack: + optional: true + dependencies: + '@babel/core': 7.17.2 + '@storybook/addon-actions': 6.2.9 + '@storybook/addon-backgrounds': 6.2.9 + '@storybook/addon-controls': 6.2.9 + '@storybook/addon-docs': 6.2.9_dtgd2mm6ybnh5irau5bfapxhdy + '@storybook/addon-toolbars': 6.2.9 + '@storybook/addon-viewport': 6.2.9 + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/node-logger': 6.2.9 + babel-loader: 8.2.3_@babel+core@7.17.2 + core-js: 3.26.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@storybook/angular' + - '@storybook/builder-webpack5' + - '@storybook/vue3' + - '@types/react' + - bluebird + - encoding + - eslint + - supports-color + - svelte + - sveltedoc-parser + - typescript + - vue + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/addon-essentials/6.5.13_t6isvk4c5wetct53pn5ufojx2u: + resolution: {integrity: sha512-G9FVAWV7ixjVLWeLgIX+VT90tcAk6yQxfZQegfg5ucRilGysJCDaNnoab4xuuvm1R40TfFhba3iAGZtQYsddmw==} + peerDependencies: + '@babel/core': ^7.9.6 + '@storybook/angular': '*' + '@storybook/builder-manager4': '*' + '@storybook/builder-manager5': '*' + '@storybook/builder-webpack4': '*' + '@storybook/builder-webpack5': '*' + '@storybook/html': '*' + '@storybook/vue': '*' + '@storybook/vue3': '*' + '@storybook/web-components': '*' + lit: '*' + lit-html: '*' + react: '*' + react-dom: '*' + svelte: '*' + sveltedoc-parser: '*' + vue: '*' + webpack: '*' + peerDependenciesMeta: + '@storybook/angular': + optional: true + '@storybook/builder-manager4': + optional: true + '@storybook/builder-manager5': + optional: true + '@storybook/builder-webpack4': + optional: true + '@storybook/builder-webpack5': + optional: true + '@storybook/html': + optional: true + '@storybook/vue': + optional: true + '@storybook/vue3': + optional: true + '@storybook/web-components': + optional: true + lit: + optional: true + lit-html: + optional: true + react: + optional: true + react-dom: + optional: true + svelte: + optional: true + sveltedoc-parser: + optional: true + vue: + optional: true + webpack: + optional: true + dependencies: + '@babel/core': 7.17.2 + '@storybook/addon-actions': 6.5.13 + '@storybook/addon-backgrounds': 6.5.13 + '@storybook/addon-controls': 6.5.13_3rubbgt5ekhqrcgx4uwls3neim + '@storybook/addon-docs': 6.5.13_t6isvk4c5wetct53pn5ufojx2u + '@storybook/addon-measure': 6.5.13 + '@storybook/addon-outline': 6.5.13 + '@storybook/addon-toolbars': 6.5.13 + '@storybook/addon-viewport': 6.5.13 + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/core-common': 6.5.13_3rubbgt5ekhqrcgx4uwls3neim + '@storybook/node-logger': 6.5.13 + core-js: 3.26.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@storybook/mdx2-csf' + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/addon-links/6.2.9: + resolution: {integrity: sha512-pBiL6EUZI3c9qtCqnGx3RXF46kAxGMdo4xDC2y3mM132W//DzxkzLZRe4ZhxxGwaLzTNlNrypZ6Li6WyIaPZ/w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/router': 6.2.9 + '@types/qs': 6.9.7 + core-js: 3.26.0 + global: 4.4.0 + prop-types: 15.8.1 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + dev: true + + /@storybook/addon-links/6.5.13: + resolution: {integrity: sha512-K/LYYu9R/Xoah5h9MNh4mSHOic3q5csqjderLqr2YW/KPYiuNubgvzEbAAbzI5xq5JrtAZqnINrZUv2A4CyYbQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.13 + '@types/qs': 6.9.7 + core-js: 3.26.0 + global: 4.4.0 + prop-types: 15.8.1 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + dev: true + + /@storybook/addon-measure/6.5.13: + resolution: {integrity: sha512-pi5RFB9YTnESRFtYHAVRUrgEI5to0TFc4KndtwcCKt1fMJ8OFjXQeznEfdj95PFeUvW5TNUwjL38vK4LhicB+g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + core-js: 3.26.0 + global: 4.4.0 + dev: true + + /@storybook/addon-outline/6.5.13: + resolution: {integrity: sha512-8d8taPheO/tryflzXbj2QRuxHOIS8CtzRzcaglCcioqHEMhOIDOx9BdXKdheq54gdk/UN94HdGJUoVxYyXwZ4Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + core-js: 3.26.0 + global: 4.4.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + dev: true + + /@storybook/addon-toolbars/6.2.9: + resolution: {integrity: sha512-4WjIofN5npBPNZ8v1UhzPeATB9RnAWRH/y1AVS1HB+zl6Ku92o7aOMqVxs8zR1oSSmtkHh/rcUcpATFKjuofdw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/client-api': 6.2.9 + '@storybook/components': 6.2.9 + core-js: 3.26.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/addon-toolbars/6.5.13: + resolution: {integrity: sha512-Qgr4wKRSP+gY1VaN7PYT4TM1um7KY341X3GHTglXLFHd8nDsCweawfV2shaX3WxCfZmVro8g4G+Oest30kLLCw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/theming': 6.5.13 + core-js: 3.26.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/addon-viewport/6.2.9: + resolution: {integrity: sha512-IK2mu5njmfcAT967SJtBOY2B6NPMikySZga9QuaLdSpQxPd3vXKNMVG1CjnduMLeDaAoUlvlJISeEPbYGuE+1A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/theming': 6.2.9 + core-js: 3.26.0 + global: 4.4.0 + memoizerific: 1.11.3 + prop-types: 15.8.1 + regenerator-runtime: 0.13.9 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/addon-viewport/6.5.13: + resolution: {integrity: sha512-KSfeuCSIjncwWGnUu6cZBx8WNqYvm5gHyFvkSPKEu0+MJtgncbUy7pl53lrEEr6QmIq0GRXvS3A0XzV8RCnrSA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/api': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/theming': 6.5.13 + core-js: 3.26.0 + global: 4.4.0 + memoizerific: 1.11.3 + prop-types: 15.8.1 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/addons/6.2.9: + resolution: {integrity: sha512-GnmEKbJwiN1jncN9NSA8CuR1i2XAlasPcl/Zn0jkfV9WitQeczVcJCPw86SGH84AD+tTBCyF2i9UC0KaOV1YBQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@storybook/api': 6.2.9 + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/router': 6.2.9 + '@storybook/theming': 6.2.9 + core-js: 3.26.0 + global: 4.4.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/addons/6.2.9_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-GnmEKbJwiN1jncN9NSA8CuR1i2XAlasPcl/Zn0jkfV9WitQeczVcJCPw86SGH84AD+tTBCyF2i9UC0KaOV1YBQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@storybook/api': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/router': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/theming': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + core-js: 3.26.0 + global: 4.4.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/addons/6.5.13: + resolution: {integrity: sha512-18CqzNnrGMfeZtiKz+R/3rHtSNnfNwz6y6prIQIbWseK16jY8ELTfIFGviwO5V2OqpbHDQi5+xQQ63QAIb89YA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/api': 6.5.13 + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.13 + '@storybook/theming': 6.5.13 + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + global: 4.4.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/addons/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-18CqzNnrGMfeZtiKz+R/3rHtSNnfNwz6y6prIQIbWseK16jY8ELTfIFGviwO5V2OqpbHDQi5+xQQ63QAIb89YA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/api': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/theming': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + global: 4.4.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/api/6.2.9: + resolution: {integrity: sha512-okkA3HAScE9tGnYBrjTOcgzT+L1lRHNoEh3ZfGgh1u/XNEyHGNkj4grvkd6nX7BzRcYQ/l2VkcKCqmOjUnSkVQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@reach/router': 1.3.4 + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/router': 6.2.9 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9 + '@types/reach__router': 1.3.11 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + store2: 2.14.2 + telejson: 5.3.3 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/api/6.2.9_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-okkA3HAScE9tGnYBrjTOcgzT+L1lRHNoEh3ZfGgh1u/XNEyHGNkj4grvkd6nX7BzRcYQ/l2VkcKCqmOjUnSkVQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@reach/router': 1.3.4_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/router': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@types/reach__router': 1.3.11 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + store2: 2.14.2 + telejson: 5.3.3 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/api/6.5.13: + resolution: {integrity: sha512-xVSmB7/IuFd6G7eiJjbI2MuS7SZunoUM6d+YCWpjiehfMeX47MXt1gZtOwFrgJC1ShZlefXFahq/dvxwtmWs+w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.13 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.5.13 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + regenerator-runtime: 0.13.9 + store2: 2.14.2 + telejson: 6.0.8 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/api/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-xVSmB7/IuFd6G7eiJjbI2MuS7SZunoUM6d+YCWpjiehfMeX47MXt1gZtOwFrgJC1ShZlefXFahq/dvxwtmWs+w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + store2: 2.14.2 + telejson: 6.0.8 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/builder-webpack4/6.2.9_g2frytwdyb7gw6koky3kitwvuu: + resolution: {integrity: sha512-swECic1huVdj+B+iRJIQ8ds59HuPVE4fmhI+j/nhw0CQCsgAEKqDlOQVYEimW6nZX8GO4WxNm6tiiRzxixejbw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.19.6 + '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.19.6 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.19.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-destructuring': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/preset-react': 7.18.6_@babel+core@7.19.6 + '@babel/preset-typescript': 7.16.7_@babel+core@7.19.6 + '@storybook/addons': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/api': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.2.9 + '@storybook/channels': 6.2.9 + '@storybook/client-api': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core-common': 6.2.9_g2frytwdyb7gw6koky3kitwvuu + '@storybook/core-events': 6.2.9 + '@storybook/node-logger': 6.2.9 + '@storybook/router': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/ui': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@types/node': 14.18.32 + '@types/webpack': 4.41.32 + autoprefixer: 9.8.8 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + babel-plugin-macros: 2.8.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.19.6 + case-sensitive-paths-webpack-plugin: 2.4.0 + core-js: 3.26.0 + css-loader: 3.6.0_webpack@4.46.0 + dotenv-webpack: 1.8.0_webpack@4.46.0 + file-loader: 6.2.0_webpack@4.46.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 4.1.6_ef2lra3u3fsnrdrpybbvbgzate + fs-extra: 9.1.0 + glob: 7.2.3 + glob-promise: 3.4.0_glob@7.2.3 + global: 4.4.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + pnp-webpack-plugin: 1.6.4_typescript@4.8.4 + postcss: 7.0.39 + postcss-flexbugs-fixes: 4.2.1 + postcss-loader: 4.3.0_gzaxsinx64nntyd3vmdqwl7coe + raw-loader: 4.0.2_webpack@4.46.0 + react: 16.14.0 + react-dev-utils: 11.0.4_ef2lra3u3fsnrdrpybbvbgzate + react-dom: 16.14.0_react@16.14.0 + stable: 0.1.8 + style-loader: 1.3.0_webpack@4.46.0 + terser-webpack-plugin: 3.1.0_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-filter-warnings-plugin: 1.2.1_webpack@4.46.0 + webpack-hot-middleware: 2.25.2 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - bluebird + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/builder-webpack4/6.2.9_o2nrgn6wwxunlqlzzokx4es3q4: + resolution: {integrity: sha512-swECic1huVdj+B+iRJIQ8ds59HuPVE4fmhI+j/nhw0CQCsgAEKqDlOQVYEimW6nZX8GO4WxNm6tiiRzxixejbw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.19.6 + '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.19.6 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.19.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-destructuring': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/preset-react': 7.18.6_@babel+core@7.19.6 + '@babel/preset-typescript': 7.16.7_@babel+core@7.19.6 + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/channel-postmessage': 6.2.9 + '@storybook/channels': 6.2.9 + '@storybook/client-api': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/core-common': 6.2.9_o2nrgn6wwxunlqlzzokx4es3q4 + '@storybook/core-events': 6.2.9 + '@storybook/node-logger': 6.2.9 + '@storybook/router': 6.2.9 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9 + '@storybook/ui': 6.2.9 + '@types/node': 14.18.32 + '@types/webpack': 4.41.32 + autoprefixer: 9.8.8 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + babel-plugin-macros: 2.8.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.19.6 + case-sensitive-paths-webpack-plugin: 2.4.0 + core-js: 3.26.0 + css-loader: 3.6.0_webpack@4.46.0 + dotenv-webpack: 1.8.0_webpack@4.46.0 + file-loader: 6.2.0_webpack@4.46.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 4.1.6_ef2lra3u3fsnrdrpybbvbgzate + fs-extra: 9.1.0 + glob: 7.2.3 + glob-promise: 3.4.0_glob@7.2.3 + global: 4.4.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + pnp-webpack-plugin: 1.6.4_typescript@4.8.4 + postcss: 7.0.39 + postcss-flexbugs-fixes: 4.2.1 + postcss-loader: 4.3.0_gzaxsinx64nntyd3vmdqwl7coe + raw-loader: 4.0.2_webpack@4.46.0 + react-dev-utils: 11.0.4_ef2lra3u3fsnrdrpybbvbgzate + stable: 0.1.8 + style-loader: 1.3.0_webpack@4.46.0 + terser-webpack-plugin: 3.1.0_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-filter-warnings-plugin: 1.2.1_webpack@4.46.0 + webpack-hot-middleware: 2.25.2 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - bluebird + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/builder-webpack4/6.5.13_u5cwnb36e3nipolzgtjnnpepdu: + resolution: {integrity: sha512-Agqy3IKPv3Nl8QqdS7PjtqLp+c0BD8+/3A2ki/YfKqVz+F+J34EpbZlh3uU053avm1EoNQHSmhZok3ZlWH6O7A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/api': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.5.13 + '@storybook/channels': 6.5.13 + '@storybook/client-api': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core-common': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@storybook/core-events': 6.5.13 + '@storybook/node-logger': 6.5.13 + '@storybook/preview-web': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/router': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/semver': 7.3.2 + '@storybook/store': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/theming': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/ui': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@types/node': 16.18.0 + '@types/webpack': 4.41.32 + autoprefixer: 9.8.8 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + case-sensitive-paths-webpack-plugin: 2.4.0 + core-js: 3.26.0 + css-loader: 3.6.0_webpack@4.46.0 + file-loader: 6.2.0_webpack@4.46.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 4.1.6_3n2x3j6farblcaf52bherr6og4 + glob: 7.2.3 + glob-promise: 3.4.0_glob@7.2.3 + global: 4.4.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + pnp-webpack-plugin: 1.6.4_typescript@4.8.4 + postcss: 7.0.39 + postcss-flexbugs-fixes: 4.2.1 + postcss-loader: 4.3.0_gzaxsinx64nntyd3vmdqwl7coe + raw-loader: 4.0.2_webpack@4.46.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + stable: 0.1.8 + style-loader: 1.3.0_webpack@4.46.0 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-filter-warnings-plugin: 1.2.1_webpack@4.46.0 + webpack-hot-middleware: 2.25.2 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - bluebird + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/channel-postmessage/6.2.9: + resolution: {integrity: sha512-OqV+gLeeCHR0KExsIz0B7gD17Cjd9D+I75qnBsLWM9inWO5kc/WZ5svw8Bvjlcm6snWpvxUaT8L+svuqcPSmww==} + dependencies: + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + core-js: 3.26.0 + global: 4.4.0 + qs: 6.11.0 + telejson: 5.3.3 + dev: true + + /@storybook/channel-postmessage/6.5.13: + resolution: {integrity: sha512-R79MBs0mQ7TV8M/a6x/SiTRyvZBidDfMEEthG7Cyo9p35JYiKOhj2535zhW4qlVMESBu95pwKYBibTjASoStPw==} + dependencies: + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + core-js: 3.26.0 + global: 4.4.0 + qs: 6.11.0 + telejson: 6.0.8 + dev: true + + /@storybook/channel-websocket/6.5.13: + resolution: {integrity: sha512-kwh667H+tzCiNvs92GNwYOwVXdj9uHZyieRAN5rJtTBJ7XgLzGkpTEU50mWlbc0nDKhgE0qYvzyr5H393Iy5ug==} + dependencies: + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + core-js: 3.26.0 + global: 4.4.0 + telejson: 6.0.8 + dev: true + + /@storybook/channels/6.2.9: + resolution: {integrity: sha512-6dC8Fb2ipNyOQXnUZMDeEUaJGH5DMLzyHlGLhVyDtrO5WR6bO8mQdkzf4+5dSKXgCBNX0BSkssXth4pDjn18rg==} + dependencies: + core-js: 3.26.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/channels/6.5.13: + resolution: {integrity: sha512-sGYSilE30bz0jG+HdHnkv0B4XkAv2hP+KRZr4xmnv+MOOQpRnZpJ5Z3HVU16s17cj/83NWihKj6BuKcEVzyilg==} + dependencies: + core-js: 3.26.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/client-api/6.2.9: + resolution: {integrity: sha512-aLvEUVkbvv6Qo/2mF4rFCecdqi2CGOUDdsV1a6EFIVS/9gXFdpirsOwKHo9qNjacGdWPlBYGCUcbrw+DvNaSFA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/channel-postmessage': 6.2.9 + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@types/qs': 6.9.7 + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + stable: 0.1.8 + store2: 2.14.2 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/client-api/6.2.9_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-aLvEUVkbvv6Qo/2mF4rFCecdqi2CGOUDdsV1a6EFIVS/9gXFdpirsOwKHo9qNjacGdWPlBYGCUcbrw+DvNaSFA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@storybook/addons': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.2.9 + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@types/qs': 6.9.7 + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + stable: 0.1.8 + store2: 2.14.2 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/client-api/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-uH1mAWbidPiuuTdMUVEiuaNOfrYXm+9QLSP1MMYTKULqEOZI5MSOGkEDqRfVWxbYv/iWBOPTQ+OM9TQ6ecYacg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.5.13 + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@types/qs': 6.9.7 + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + store2: 2.14.2 + synchronous-promise: 2.0.16 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/client-logger/6.2.9: + resolution: {integrity: sha512-IfOQZuvpjh66qBInQCJOb9S0dTGpzZ/Cxlcvokp+PYt95KztaWN3mPm+HaDQCeRsrWNe0Bpm1zuickcJ6dBOXg==} + dependencies: + core-js: 3.26.0 + global: 4.4.0 + dev: true + + /@storybook/client-logger/6.5.13: + resolution: {integrity: sha512-F2SMW3LWFGXLm2ENTwTitrLWJgmMXRf3CWQXdN2EbkNCIBHy5Zcbt+91K4OX8e2e5h9gjGfrdYbyYDYOoUCEfA==} + dependencies: + core-js: 3.26.0 + global: 4.4.0 + dev: true + + /@storybook/components/6.2.9: + resolution: {integrity: sha512-hnV1MI2aB2g1sJ7NJphpxi7TwrMZQ/tpCJeHnkjmzyC6ez1MXqcBXGrEEdSXzRfAxjQTOEpu6H1mnns0xMP0Ag==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@popperjs/core': 2.11.6 + '@storybook/client-logger': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/theming': 6.2.9 + '@types/color-convert': 2.0.0 + '@types/overlayscrollbars': 1.12.1 + '@types/react-syntax-highlighter': 11.0.5 + color-convert: 2.0.1 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + markdown-to-jsx: 7.1.7 + memoizerific: 1.11.3 + overlayscrollbars: 1.13.3 + polished: 4.2.2 + prop-types: 15.8.1 + react-colorful: 5.6.1 + react-popper-tooltip: 3.1.1 + react-syntax-highlighter: 13.5.3 + react-textarea-autosize: 8.3.4 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/components/6.2.9_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-hnV1MI2aB2g1sJ7NJphpxi7TwrMZQ/tpCJeHnkjmzyC6ez1MXqcBXGrEEdSXzRfAxjQTOEpu6H1mnns0xMP0Ag==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@popperjs/core': 2.11.6 + '@storybook/client-logger': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/theming': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@types/color-convert': 2.0.0 + '@types/overlayscrollbars': 1.12.1 + '@types/react-syntax-highlighter': 11.0.5 + color-convert: 2.0.1 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + markdown-to-jsx: 7.1.7_react@16.14.0 + memoizerific: 1.11.3 + overlayscrollbars: 1.13.3 + polished: 4.2.2 + prop-types: 15.8.1 + react: 16.14.0 + react-colorful: 5.6.1_wcqkhtmu7mswc6yz4uyexck3ty + react-dom: 16.14.0_react@16.14.0 + react-popper-tooltip: 3.1.1_wcqkhtmu7mswc6yz4uyexck3ty + react-syntax-highlighter: 13.5.3_react@16.14.0 + react-textarea-autosize: 8.3.4_react@16.14.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/components/6.5.13: + resolution: {integrity: sha512-6Hhx70JK5pGfKCkqMU4yq/BBH+vRTmzj7tZKfPwba+f8VmTMoOr/2ysTQFRtXryiHB6Z15xBYgfq5x2pIwQzLQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/theming': 6.5.13 + core-js: 3.26.0 + memoizerific: 1.11.3 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + util-deprecate: 1.0.2 + dev: true + + /@storybook/components/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-6Hhx70JK5pGfKCkqMU4yq/BBH+vRTmzj7tZKfPwba+f8VmTMoOr/2ysTQFRtXryiHB6Z15xBYgfq5x2pIwQzLQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/theming': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + core-js: 3.26.0 + memoizerific: 1.11.3 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + util-deprecate: 1.0.2 + dev: true + + /@storybook/core-client/6.2.9_lasgyenclx45ngbljrbo537mpe: + resolution: {integrity: sha512-jW841J5lCe1Ub5ZMtzYPgCy/OUddFxxVYeHLZyuNxlH5RoiQQxbDpuFlzuZMYGuIzD6eZw+ANE4w5vW/y5oBfA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/channel-postmessage': 6.2.9 + '@storybook/client-api': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.2.9 + ansi-to-html: 0.6.15 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 4.8.4 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/core-client/6.2.9_typescript@4.8.4: + resolution: {integrity: sha512-jW841J5lCe1Ub5ZMtzYPgCy/OUddFxxVYeHLZyuNxlH5RoiQQxbDpuFlzuZMYGuIzD6eZw+ANE4w5vW/y5oBfA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/channel-postmessage': 6.2.9 + '@storybook/client-api': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.2.9 + ansi-to-html: 0.6.15 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 4.8.4 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/core-client/6.2.9_vvswzvegta47ikremfl73qk64u: + resolution: {integrity: sha512-jW841J5lCe1Ub5ZMtzYPgCy/OUddFxxVYeHLZyuNxlH5RoiQQxbDpuFlzuZMYGuIzD6eZw+ANE4w5vW/y5oBfA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.2.9 + '@storybook/client-api': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + ansi-to-html: 0.6.15 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 4.8.4 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/core-client/6.2.9_zvdcumho7mqj3lfknr2wnpofbm: + resolution: {integrity: sha512-jW841J5lCe1Ub5ZMtzYPgCy/OUddFxxVYeHLZyuNxlH5RoiQQxbDpuFlzuZMYGuIzD6eZw+ANE4w5vW/y5oBfA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.2.9 + '@storybook/client-api': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/client-logger': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + ansi-to-html: 0.6.15 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 4.8.4 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/core-client/6.5.13_vvswzvegta47ikremfl73qk64u: + resolution: {integrity: sha512-YuELbRokTBdqjbx/R4/7O4rou9kvbBIOJjlUkor9hdLLuJ3P0yGianERGNkZFfvcfMBAxU0p52o7QvDldSR3kA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.5.13 + '@storybook/channel-websocket': 6.5.13 + '@storybook/client-api': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/preview-web': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/store': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/ui': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + airbnb-js-shims: 2.2.1 + ansi-to-html: 0.6.15 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 4.8.4 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + dev: true + + /@storybook/core-common/6.2.9_g2frytwdyb7gw6koky3kitwvuu: + resolution: {integrity: sha512-ve0Qb4EMit8jGibfZBprmaU2i4LtpB4vSMIzD9nB1YeBmw2cGhHubtmayZ0TwcV3fPQhtYH9wwRWuWyzzHyQyw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.17.2 + '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.17.2 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.17.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-destructuring': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-react': 7.18.6_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + '@babel/register': 7.18.9_@babel+core@7.17.2 + '@storybook/node-logger': 6.2.9 + '@storybook/semver': 7.3.2 + '@types/glob-base': 0.3.0 + '@types/micromatch': 4.0.2 + '@types/node': 14.18.32 + '@types/pretty-hrtime': 1.0.1 + babel-loader: 8.2.3_mc362qep5qjjhwk7q3m45kuizi + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.17.2 + chalk: 4.1.2 + core-js: 3.26.0 + express: 4.17.2 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.2_ef2lra3u3fsnrdrpybbvbgzate + glob: 7.2.3 + glob-base: 0.3.0 + interpret: 2.2.0 + json5: 2.2.0 + lazy-universal-dotenv: 3.0.1 + micromatch: 4.0.5 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/core-common/6.2.9_o2nrgn6wwxunlqlzzokx4es3q4: + resolution: {integrity: sha512-ve0Qb4EMit8jGibfZBprmaU2i4LtpB4vSMIzD9nB1YeBmw2cGhHubtmayZ0TwcV3fPQhtYH9wwRWuWyzzHyQyw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.17.2 + '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.17.2 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.17.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-destructuring': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-react': 7.18.6_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + '@babel/register': 7.18.9_@babel+core@7.17.2 + '@storybook/node-logger': 6.2.9 + '@storybook/semver': 7.3.2 + '@types/glob-base': 0.3.0 + '@types/micromatch': 4.0.2 + '@types/node': 14.18.32 + '@types/pretty-hrtime': 1.0.1 + babel-loader: 8.2.3_mc362qep5qjjhwk7q3m45kuizi + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.17.2 + chalk: 4.1.2 + core-js: 3.26.0 + express: 4.17.2 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.2_ef2lra3u3fsnrdrpybbvbgzate + glob: 7.2.3 + glob-base: 0.3.0 + interpret: 2.2.0 + json5: 2.2.0 + lazy-universal-dotenv: 3.0.1 + micromatch: 4.0.5 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/core-common/6.5.13_3rubbgt5ekhqrcgx4uwls3neim: + resolution: {integrity: sha512-+DVZrRsteE9pw0X5MNffkdBgejQnbnL+UOG3qXkE9xxUamQALnuqS/w1BzpHE9WmOHuf7RWMKflyQEW3OLKAJg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.19.6 + '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.19.6 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.19.6 + '@babel/plugin-proposal-private-property-in-object': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-destructuring': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/preset-react': 7.18.6_@babel+core@7.19.6 + '@babel/preset-typescript': 7.16.7_@babel+core@7.19.6 + '@babel/register': 7.18.9_@babel+core@7.19.6 + '@storybook/node-logger': 6.5.13 + '@storybook/semver': 7.3.2 + '@types/node': 16.18.0 + '@types/pretty-hrtime': 1.0.1 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.19.6 + chalk: 4.1.2 + core-js: 3.26.0 + express: 4.17.2 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.2_3n2x3j6farblcaf52bherr6og4 + fs-extra: 9.1.0 + glob: 7.2.3 + handlebars: 4.7.7 + interpret: 2.2.0 + json5: 2.2.0 + lazy-universal-dotenv: 3.0.1 + picomatch: 2.3.1 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + resolve-from: 5.0.0 + slash: 3.0.0 + telejson: 6.0.8 + ts-dedent: 2.2.0 + typescript: 4.8.4 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/core-common/6.5.13_u5cwnb36e3nipolzgtjnnpepdu: + resolution: {integrity: sha512-+DVZrRsteE9pw0X5MNffkdBgejQnbnL+UOG3qXkE9xxUamQALnuqS/w1BzpHE9WmOHuf7RWMKflyQEW3OLKAJg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.19.6 + '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.19.6 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.19.6 + '@babel/plugin-proposal-private-property-in-object': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-destructuring': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.19.6 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/preset-react': 7.18.6_@babel+core@7.19.6 + '@babel/preset-typescript': 7.16.7_@babel+core@7.19.6 + '@babel/register': 7.18.9_@babel+core@7.19.6 + '@storybook/node-logger': 6.5.13 + '@storybook/semver': 7.3.2 + '@types/node': 16.18.0 + '@types/pretty-hrtime': 1.0.1 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.19.6 + chalk: 4.1.2 + core-js: 3.26.0 + express: 4.17.2 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.2_3n2x3j6farblcaf52bherr6og4 + fs-extra: 9.1.0 + glob: 7.2.3 + handlebars: 4.7.7 + interpret: 2.2.0 + json5: 2.2.0 + lazy-universal-dotenv: 3.0.1 + picomatch: 2.3.1 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + resolve-from: 5.0.0 + slash: 3.0.0 + telejson: 6.0.8 + ts-dedent: 2.2.0 + typescript: 4.8.4 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/core-events/6.2.9: + resolution: {integrity: sha512-xQmbX/oYQK1QsAGN8hriXX5SUKOoTUe3L4dVaVHxJqy7MReRWJpprJmCpbAPJzWS6WCbDFfCM5kVEexHLOzJlQ==} + dependencies: + core-js: 3.26.0 + dev: true + + /@storybook/core-events/6.5.13: + resolution: {integrity: sha512-kL745tPpRKejzHToA3/CoBNbI+NPRVk186vGxXBmk95OEg0TlwgQExP8BnqEtLlRZMbW08e4+6kilc1M1M4N5w==} + dependencies: + core-js: 3.26.0 + dev: true + + /@storybook/core-server/6.2.9_g2frytwdyb7gw6koky3kitwvuu: + resolution: {integrity: sha512-DzihO73pj1Ro0Y4tq9hjw2mLMUYeSRPrx7CndCOBxcTHCKQ8Kd7Dee3wJ49t5/19V7TW1+4lYR59GAy73FeOAQ==} + peerDependencies: + '@storybook/builder-webpack5': 6.2.9 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.19.6 + '@babel/preset-react': 7.18.6_@babel+core@7.19.6 + '@storybook/addons': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/builder-webpack4': 6.2.9_g2frytwdyb7gw6koky3kitwvuu + '@storybook/core-client': 6.2.9_vvswzvegta47ikremfl73qk64u + '@storybook/core-common': 6.2.9_g2frytwdyb7gw6koky3kitwvuu + '@storybook/node-logger': 6.2.9 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/ui': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@types/node': 14.18.32 + '@types/node-fetch': 2.6.2 + '@types/pretty-hrtime': 1.0.1 + '@types/webpack': 4.41.32 + airbnb-js-shims: 2.2.1 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + better-opn: 2.1.1 + boxen: 4.2.0 + case-sensitive-paths-webpack-plugin: 2.4.0 + chalk: 4.1.2 + cli-table3: 0.6.0 + commander: 6.2.1 + core-js: 3.26.0 + cpy: 8.1.2 + css-loader: 3.6.0_webpack@4.46.0 + detect-port: 1.5.1 + dotenv-webpack: 1.8.0_webpack@4.46.0 + express: 4.17.2 + file-loader: 6.2.0_webpack@4.46.0 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fs-extra: 9.1.0 + global: 4.4.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + ip: 1.1.5 + node-fetch: 2.6.7 + pnp-webpack-plugin: 1.6.4_typescript@4.8.4 + pretty-hrtime: 1.0.3 + prompts: 2.4.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + serve-favicon: 2.5.0 + style-loader: 1.3.0_webpack@4.46.0 + telejson: 5.3.3 + terser-webpack-plugin: 3.1.0_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - bluebird + - encoding + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/core-server/6.2.9_o2nrgn6wwxunlqlzzokx4es3q4: + resolution: {integrity: sha512-DzihO73pj1Ro0Y4tq9hjw2mLMUYeSRPrx7CndCOBxcTHCKQ8Kd7Dee3wJ49t5/19V7TW1+4lYR59GAy73FeOAQ==} + peerDependencies: + '@storybook/builder-webpack5': 6.2.9 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.19.6 + '@babel/preset-react': 7.18.6_@babel+core@7.19.6 + '@storybook/addons': 6.2.9 + '@storybook/builder-webpack4': 6.2.9_o2nrgn6wwxunlqlzzokx4es3q4 + '@storybook/core-client': 6.2.9_lasgyenclx45ngbljrbo537mpe + '@storybook/core-common': 6.2.9_o2nrgn6wwxunlqlzzokx4es3q4 + '@storybook/node-logger': 6.2.9 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9 + '@storybook/ui': 6.2.9 + '@types/node': 14.18.32 + '@types/node-fetch': 2.6.2 + '@types/pretty-hrtime': 1.0.1 + '@types/webpack': 4.41.32 + airbnb-js-shims: 2.2.1 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + better-opn: 2.1.1 + boxen: 4.2.0 + case-sensitive-paths-webpack-plugin: 2.4.0 + chalk: 4.1.2 + cli-table3: 0.6.0 + commander: 6.2.1 + core-js: 3.26.0 + cpy: 8.1.2 + css-loader: 3.6.0_webpack@4.46.0 + detect-port: 1.5.1 + dotenv-webpack: 1.8.0_webpack@4.46.0 + express: 4.17.2 + file-loader: 6.2.0_webpack@4.46.0 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fs-extra: 9.1.0 + global: 4.4.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + ip: 1.1.5 + node-fetch: 2.6.7 + pnp-webpack-plugin: 1.6.4_typescript@4.8.4 + pretty-hrtime: 1.0.3 + prompts: 2.4.2 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + serve-favicon: 2.5.0 + style-loader: 1.3.0_webpack@4.46.0 + telejson: 5.3.3 + terser-webpack-plugin: 3.1.0_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - bluebird + - encoding + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/core-server/6.5.13_u5cwnb36e3nipolzgtjnnpepdu: + resolution: {integrity: sha512-vs7tu3kAnFwuINio1p87WyqDNlFyZESmeh9s7vvrZVbe/xS/ElqDscr9DT5seW+jbtxufAaHsx+JUTver1dheQ==} + peerDependencies: + '@storybook/builder-webpack5': '*' + '@storybook/manager-webpack5': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + '@storybook/manager-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@discoveryjs/json-ext': 0.5.7 + '@storybook/builder-webpack4': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@storybook/core-client': 6.5.13_vvswzvegta47ikremfl73qk64u + '@storybook/core-common': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/csf-tools': 6.5.13 + '@storybook/manager-webpack4': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@storybook/node-logger': 6.5.13 + '@storybook/semver': 7.3.2 + '@storybook/store': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/telemetry': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@types/node': 16.18.0 + '@types/node-fetch': 2.6.2 + '@types/pretty-hrtime': 1.0.1 + '@types/webpack': 4.41.32 + better-opn: 2.1.1 + boxen: 5.1.2 + chalk: 4.1.2 + cli-table3: 0.6.3 + commander: 6.2.1 + compression: 1.7.4 + core-js: 3.26.0 + cpy: 8.1.2 + detect-port: 1.5.1 + express: 4.17.2 + fs-extra: 9.1.0 + global: 4.4.0 + globby: 11.1.0 + ip: 2.0.0 + lodash: 4.17.21 + node-fetch: 2.6.7 + open: 8.4.0 + pretty-hrtime: 1.0.3 + prompts: 2.4.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + serve-favicon: 2.5.0 + slash: 3.0.0 + telejson: 6.0.8 + ts-dedent: 2.2.0 + typescript: 4.8.4 + util-deprecate: 1.0.2 + watchpack: 2.4.0 + webpack: 4.46.0 + ws: 8.5.0 + x-default-browser: 0.4.0 + transitivePeerDependencies: + - '@storybook/mdx2-csf' + - bluebird + - bufferutil + - encoding + - eslint + - supports-color + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/core/6.2.9_g2frytwdyb7gw6koky3kitwvuu: + resolution: {integrity: sha512-pzbyjWvj0t8m0kR2pC9GQne4sZn7Y/zfcbm6/31CL+yhzOQjfJEj3n4ZFUlxikXqQJPg1aWfypfyaeaLL0QyuA==} + peerDependencies: + '@storybook/builder-webpack5': 6.2.9 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@storybook/core-client': 6.2.9_zvdcumho7mqj3lfknr2wnpofbm + '@storybook/core-server': 6.2.9_g2frytwdyb7gw6koky3kitwvuu + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + typescript: 4.8.4 + transitivePeerDependencies: + - '@types/react' + - bluebird + - encoding + - eslint + - supports-color + - vue-template-compiler + - webpack + - webpack-cli + - webpack-command + dev: true + + /@storybook/core/6.2.9_o2nrgn6wwxunlqlzzokx4es3q4: + resolution: {integrity: sha512-pzbyjWvj0t8m0kR2pC9GQne4sZn7Y/zfcbm6/31CL+yhzOQjfJEj3n4ZFUlxikXqQJPg1aWfypfyaeaLL0QyuA==} + peerDependencies: + '@storybook/builder-webpack5': 6.2.9 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@storybook/core-client': 6.2.9_typescript@4.8.4 + '@storybook/core-server': 6.2.9_o2nrgn6wwxunlqlzzokx4es3q4 + typescript: 4.8.4 + transitivePeerDependencies: + - '@types/react' + - bluebird + - encoding + - eslint + - supports-color + - vue-template-compiler + - webpack + - webpack-cli + - webpack-command + dev: true + + /@storybook/core/6.5.13_brfvc2pvdspackskhn4rfzxuge: + resolution: {integrity: sha512-kw1lCgbsxzUimGww6t5rmuWJmFPe9kGGyzIqvj4RC4BBcEsP40LEu9XhSfvnb8vTOLIULFZeZpdRFfJs4TYbUw==} + peerDependencies: + '@storybook/builder-webpack5': '*' + '@storybook/manager-webpack5': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + '@storybook/manager-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@storybook/core-client': 6.5.13_vvswzvegta47ikremfl73qk64u + '@storybook/core-server': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + typescript: 4.8.4 + webpack: 4.46.0 + transitivePeerDependencies: + - '@storybook/mdx2-csf' + - bluebird + - bufferutil + - encoding + - eslint + - supports-color + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/csf-tools/6.5.13: + resolution: {integrity: sha512-63Ev+VmBqzwSwfUzbuXOLKBD5dMTK2zBYLQ9anTVw70FuTikwTsGIbPgb098K0vsxRCgxl7KM7NpivHqtZtdjw==} + peerDependencies: + '@storybook/mdx2-csf': ^0.0.3 + peerDependenciesMeta: + '@storybook/mdx2-csf': + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/generator': 7.19.6 + '@babel/parser': 7.19.6 + '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/traverse': 7.19.6 + '@babel/types': 7.19.4 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/mdx1-csf': 0.0.1_@babel+core@7.19.6 + core-js: 3.26.0 + fs-extra: 9.1.0 + global: 4.4.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@storybook/csf/0.0.1: + resolution: {integrity: sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==} + dependencies: + lodash: 4.17.21 + dev: true + + /@storybook/csf/0.0.2--canary.4566f4d.1: + resolution: {integrity: sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ==} + dependencies: + lodash: 4.17.21 + dev: true + + /@storybook/docs-tools/6.5.13: + resolution: {integrity: sha512-hB+hk+895ny4SW84j3X5iV55DHs3bCfTOp7cDdcZJdQrlm0wuDb4A6d4ffNC7ZLh9VkUjU6ST4VEV5Bb0Cptow==} + dependencies: + '@babel/core': 7.19.6 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.13 + core-js: 3.26.0 + doctrine: 3.0.0 + lodash: 4.17.21 + regenerator-runtime: 0.13.9 + transitivePeerDependencies: + - react + - react-dom + - supports-color + dev: true + + /@storybook/manager-webpack4/6.5.13_u5cwnb36e3nipolzgtjnnpepdu: + resolution: {integrity: sha512-pURzS5W3XM0F7bCBWzpl7TRsuy+OXFwLXiWLaexuvo0POZe31Ueo2A1R4rx3MT5Iee8O9mYvG2XTmvK9MlLefQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.19.6 + '@babel/preset-react': 7.18.6_@babel+core@7.19.6 + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core-client': 6.5.13_vvswzvegta47ikremfl73qk64u + '@storybook/core-common': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@storybook/node-logger': 6.5.13 + '@storybook/theming': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/ui': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@types/node': 16.18.0 + '@types/webpack': 4.41.32 + babel-loader: 8.2.3_q4ydpsrmbqywduja5orgah6fgq + case-sensitive-paths-webpack-plugin: 2.4.0 + chalk: 4.1.2 + core-js: 3.26.0 + css-loader: 3.6.0_webpack@4.46.0 + express: 4.17.2 + file-loader: 6.2.0_webpack@4.46.0 + find-up: 5.0.0 + fs-extra: 9.1.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + node-fetch: 2.6.7 + pnp-webpack-plugin: 1.6.4_typescript@4.8.4 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + style-loader: 1.3.0_webpack@4.46.0 + telejson: 6.0.8 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 4.8.4 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - bluebird + - encoding + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/mdx1-csf/0.0.1_@babel+core@7.17.2: + resolution: {integrity: sha512-4biZIWWzoWlCarMZmTpqcJNgo/RBesYZwGFbQeXiGYsswuvfWARZnW9RE9aUEMZ4XPn7B1N3EKkWcdcWe/K2tg==} + dependencies: + '@babel/generator': 7.19.6 + '@babel/parser': 7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/types': 7.19.4 + '@mdx-js/mdx': 1.6.22 + '@types/lodash': 4.14.186 + js-string-escape: 1.0.1 + loader-utils: 2.0.2 + lodash: 4.17.21 + prettier: 2.2.1 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@storybook/mdx1-csf/0.0.1_@babel+core@7.19.6: + resolution: {integrity: sha512-4biZIWWzoWlCarMZmTpqcJNgo/RBesYZwGFbQeXiGYsswuvfWARZnW9RE9aUEMZ4XPn7B1N3EKkWcdcWe/K2tg==} + dependencies: + '@babel/generator': 7.19.6 + '@babel/parser': 7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/types': 7.19.4 + '@mdx-js/mdx': 1.6.22 + '@types/lodash': 4.14.186 + js-string-escape: 1.0.1 + loader-utils: 2.0.2 + lodash: 4.17.21 + prettier: 2.2.1 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@storybook/node-logger/6.2.9: + resolution: {integrity: sha512-ryRBChWZf1A5hOVONErJZosS25IdMweoMVFAUAcj91iC0ynoSA6YL2jmoE71jQchxEXEgkDeRkX9lR/GlqFGZQ==} + dependencies: + '@types/npmlog': 4.1.4 + chalk: 4.1.2 + core-js: 3.26.0 + npmlog: 4.1.2 + pretty-hrtime: 1.0.3 + dev: true + + /@storybook/node-logger/6.5.13: + resolution: {integrity: sha512-/r5aVZAqZRoy5FyNk/G4pj7yKJd3lJfPbAaOHVROv2IF7PJP/vtRaDkcfh0g2U6zwuDxGIqSn80j+qoEli9m5A==} + dependencies: + '@types/npmlog': 4.1.4 + chalk: 4.1.2 + core-js: 3.26.0 + npmlog: 5.0.1 + pretty-hrtime: 1.0.3 + dev: true + + /@storybook/postinstall/6.2.9: + resolution: {integrity: sha512-HjAjXZV+WItonC7lVrfrUsQuRFZNz1g1lE0GgsEK2LdC5rAcD/JwJxjiWREwY+RGxKL9rpWgqyxVQajpIJRjhA==} + dependencies: + core-js: 3.26.0 + dev: true + + /@storybook/postinstall/6.5.13: + resolution: {integrity: sha512-qmqP39FGIP5NdhXC5IpAs9cFoYx9fg1psoQKwb9snYb98eVQU31uHc1W2MBUh3lG4AjAm7pQaXJci7ti4jOh3g==} + dependencies: + core-js: 3.26.0 + dev: true + + /@storybook/preact/6.2.9_lmcptaky2e4dapkymvnphph734: + resolution: {integrity: sha512-AnbRtJfIyI6AGIIaduBe2Fnr4HPldycWr1fadqpytm9LBMQsYRCzXy2+AtBIfa0O5YDVqDcKda/uBsj1tNJzqw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + '@babel/core': '*' + preact: ^8.0.0||^10.0.0 + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@storybook/addons': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core': 6.2.9_g2frytwdyb7gw6koky3kitwvuu + '@storybook/core-common': 6.2.9_g2frytwdyb7gw6koky3kitwvuu + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + global: 4.4.0 + preact: 10.6.5 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@types/react' + - bluebird + - encoding + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack + - webpack-cli + - webpack-command + dev: true + + /@storybook/preact/6.5.13_pzutvqtg2sm6mgs5je6xwivarm: + resolution: {integrity: sha512-5/ufRgxh5VypFcOeIBQMg/AqZQ2+KfUw4Glo9HU75dbIe/kYqSnN3+5SEv8J6ykxHHtUWcmnILS1r7/I5R7j/w==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + '@babel/core': '*' + preact: ^8.0.0||^10.0.0 + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core': 6.5.13_brfvc2pvdspackskhn4rfzxuge + '@storybook/core-common': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@types/node': 16.18.0 + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + global: 4.4.0 + preact: 10.6.1 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + webpack: 4.46.0 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@storybook/manager-webpack5' + - '@storybook/mdx2-csf' + - bluebird + - bufferutil + - encoding + - eslint + - supports-color + - typescript + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/preact/6.5.13_rv7hwuimljxvhghl3f4jocbf4q: + resolution: {integrity: sha512-5/ufRgxh5VypFcOeIBQMg/AqZQ2+KfUw4Glo9HU75dbIe/kYqSnN3+5SEv8J6ykxHHtUWcmnILS1r7/I5R7j/w==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + '@babel/core': '*' + preact: ^8.0.0||^10.0.0 + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core': 6.5.13_brfvc2pvdspackskhn4rfzxuge + '@storybook/core-common': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@types/node': 16.18.0 + '@types/webpack-env': 1.18.0 + core-js: 3.26.0 + global: 4.4.0 + preact: 10.6.5 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + webpack: 4.46.0 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@storybook/manager-webpack5' + - '@storybook/mdx2-csf' + - bluebird + - bufferutil + - encoding + - eslint + - supports-color + - typescript + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/preset-scss/1.0.3_sass-loader@10.1.1: + resolution: {integrity: sha512-o9Iz6wxPeNENrQa2mKlsDKynBfqU2uWaRP80HeWp4TkGgf7/x3DVF2O7yi9N0x/PI1qzzTTpxlQ90D62XmpiTw==} + peerDependencies: + css-loader: '*' + sass-loader: '*' + style-loader: '*' + dependencies: + sass-loader: 10.1.1_sass@1.32.13 + dev: true + + /@storybook/preset-scss/1.0.3_sass-loader@10.3.1: + resolution: {integrity: sha512-o9Iz6wxPeNENrQa2mKlsDKynBfqU2uWaRP80HeWp4TkGgf7/x3DVF2O7yi9N0x/PI1qzzTTpxlQ90D62XmpiTw==} + peerDependencies: + css-loader: '*' + sass-loader: '*' + style-loader: '*' + dependencies: + sass-loader: 10.3.1_sass@1.32.13 + dev: true + + /@storybook/preview-web/6.5.13: + resolution: {integrity: sha512-GNNYVzw4SmRua3dOc52Ye6Us4iQbq5GKQ56U3iwnzZM3TBdJB+Rft94Fn1/pypHujEHS8hl5Xgp9td6C1lLCow==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/channel-postmessage': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.13 + ansi-to-html: 0.6.15 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + synchronous-promise: 2.0.16 + ts-dedent: 2.2.0 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/preview-web/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-GNNYVzw4SmRua3dOc52Ye6Us4iQbq5GKQ56U3iwnzZM3TBdJB+Rft94Fn1/pypHujEHS8hl5Xgp9td6C1lLCow==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channel-postmessage': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + ansi-to-html: 0.6.15 + core-js: 3.26.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + synchronous-promise: 2.0.16 + ts-dedent: 2.2.0 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/router/6.2.9: + resolution: {integrity: sha512-7Bn1OFoItCl8whXRT8N1qp1Lky7kzXJ3aslWp5E8HcM8rxh4OYXfbaeiyJEJxBTGC5zxgY+tAEXHFjsAviFROg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@reach/router': 1.3.4 + '@storybook/client-logger': 6.2.9 + '@types/reach__router': 1.3.11 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.0 + ts-dedent: 2.2.0 + dev: true + + /@storybook/router/6.2.9_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-7Bn1OFoItCl8whXRT8N1qp1Lky7kzXJ3aslWp5E8HcM8rxh4OYXfbaeiyJEJxBTGC5zxgY+tAEXHFjsAviFROg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@reach/router': 1.3.4_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/client-logger': 6.2.9 + '@types/reach__router': 1.3.11 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + ts-dedent: 2.2.0 + dev: true + + /@storybook/router/6.5.13: + resolution: {integrity: sha512-sf5aogfirH5ucD0d0hc2mKf2iyWsZsvXhr5kjxUQmgkcoflkGUWhc34sbSQVRQ1i8K5lkLIDH/q2s1Zr2SbzhQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 6.5.13 + core-js: 3.26.0 + memoizerific: 1.11.3 + qs: 6.11.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/router/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-sf5aogfirH5ucD0d0hc2mKf2iyWsZsvXhr5kjxUQmgkcoflkGUWhc34sbSQVRQ1i8K5lkLIDH/q2s1Zr2SbzhQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 6.5.13 + core-js: 3.26.0 + memoizerific: 1.11.3 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/semver/7.3.2: + resolution: {integrity: sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + core-js: 3.26.0 + find-up: 4.1.0 + dev: true + + /@storybook/source-loader/6.2.9: + resolution: {integrity: sha512-cx499g7BG2oeXvRFx45r0W0p2gKEy/e88WsUFnqqfMKZBJ8K0R/lx5DI0l1hq+TzSrE6uGe0/uPlaLkJNIro7g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@storybook/addons': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/csf': 0.0.1 + core-js: 3.26.0 + estraverse: 5.3.0 + global: 4.4.0 + loader-utils: 2.0.2 + lodash: 4.17.21 + prettier: 2.2.1 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/source-loader/6.5.13: + resolution: {integrity: sha512-tHuM8PfeB/0m+JigbaFp+Ld0euFH+fgOObH2W9rjEXy5vnwmaeex/JAdCprv4oL+LcDQEERqNULUUNIvbcTPAg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + core-js: 3.26.0 + estraverse: 5.3.0 + global: 4.4.0 + loader-utils: 2.0.2 + lodash: 4.17.21 + prettier: 2.2.1 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/store/6.5.13: + resolution: {integrity: sha512-GG6lm+8fBX1tNUnX7x3raBOjYhhf14bPWLtYiPlxDTFEMs3sJte7zWKZq6NQ79MoBLL6jjzTeolBfDCBw6fiWQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addons': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + regenerator-runtime: 0.13.9 + slash: 3.0.0 + stable: 0.1.8 + synchronous-promise: 2.0.16 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/store/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-GG6lm+8fBX1tNUnX7x3raBOjYhhf14bPWLtYiPlxDTFEMs3sJte7zWKZq6NQ79MoBLL6jjzTeolBfDCBw6fiWQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/client-logger': 6.5.13 + '@storybook/core-events': 6.5.13 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + core-js: 3.26.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + slash: 3.0.0 + stable: 0.1.8 + synchronous-promise: 2.0.16 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + + /@storybook/telemetry/6.5.13_u5cwnb36e3nipolzgtjnnpepdu: + resolution: {integrity: sha512-PFJEfGbunmfFWabD3rdCF8EHH+45578OHOkMPpXJjqXl94vPQxUH2XTVKQgEQJbYrgX0Vx9Z4tSkdMHuzYDbWQ==} + dependencies: + '@storybook/client-logger': 6.5.13 + '@storybook/core-common': 6.5.13_u5cwnb36e3nipolzgtjnnpepdu + chalk: 4.1.2 + core-js: 3.26.0 + detect-package-manager: 2.0.1 + fetch-retry: 5.0.3 + fs-extra: 9.1.0 + global: 4.4.0 + isomorphic-unfetch: 3.1.0 + nanoid: 3.3.4 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + transitivePeerDependencies: + - encoding + - eslint + - react + - react-dom + - supports-color + - typescript + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /@storybook/theming/6.2.9: + resolution: {integrity: sha512-183oJW7AD7Fhqg5NT4ct3GJntwteAb9jZnQ6yhf9JSdY+fk8OhxRbPf7ov0au2gYACcGrWDd9K5pYQsvWlP5gA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@emotion/core': 10.3.1 + '@emotion/is-prop-valid': 0.8.8 + '@emotion/styled': 10.3.0_@emotion+core@10.3.1 + '@storybook/client-logger': 6.2.9 + core-js: 3.26.0 + deep-object-diff: 1.1.7 + emotion-theming: 10.3.0_@emotion+core@10.3.1 + global: 4.4.0 + memoizerific: 1.11.3 + polished: 4.2.2 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + dev: true + + /@storybook/theming/6.2.9_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-183oJW7AD7Fhqg5NT4ct3GJntwteAb9jZnQ6yhf9JSdY+fk8OhxRbPf7ov0au2gYACcGrWDd9K5pYQsvWlP5gA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@emotion/core': 10.3.1_react@16.14.0 + '@emotion/is-prop-valid': 0.8.8 + '@emotion/styled': 10.3.0_qzeatvug73zaio2r3dlvejynye + '@storybook/client-logger': 6.2.9 + core-js: 3.26.0 + deep-object-diff: 1.1.7 + emotion-theming: 10.3.0_qzeatvug73zaio2r3dlvejynye + global: 4.4.0 + memoizerific: 1.11.3 + polished: 4.2.2 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + dev: true + + /@storybook/theming/6.5.13: + resolution: {integrity: sha512-oif5NGFAUQhizo50r+ctw2hZNLWV4dPHai+L/gFvbaSeRBeHSNkIcMoZ2FlrO566HdGZTDutYXcR+xus8rI28g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 6.5.13 + core-js: 3.26.0 + memoizerific: 1.11.3 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/theming/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-oif5NGFAUQhizo50r+ctw2hZNLWV4dPHai+L/gFvbaSeRBeHSNkIcMoZ2FlrO566HdGZTDutYXcR+xus8rI28g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/client-logger': 6.5.13 + core-js: 3.26.0 + memoizerific: 1.11.3 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + dev: true + + /@storybook/ui/6.2.9: + resolution: {integrity: sha512-jq2xmw3reIqik/6ibUSbNKGR+Xvr9wkAEwexiOl+5WQ5BeYJpw4dmDmsFQf+SQuWaSEUUPolbzkakRQM778Kdg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@emotion/core': 10.3.1 + '@storybook/addons': 6.2.9 + '@storybook/api': 6.2.9 + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9 + '@storybook/core-events': 6.2.9 + '@storybook/router': 6.2.9 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9 + '@types/markdown-to-jsx': 6.11.3 + copy-to-clipboard: 3.3.2 + core-js: 3.26.0 + core-js-pure: 3.20.2 + downshift: 6.1.12 + emotion-theming: 10.3.0_@emotion+core@10.3.1 + fuse.js: 3.6.1 + global: 4.4.0 + lodash: 4.17.21 + markdown-to-jsx: 6.11.4 + memoizerific: 1.11.3 + polished: 4.2.2 + qs: 6.11.0 + react-draggable: 4.4.5 + react-helmet-async: 1.3.0 + react-sizeme: 3.0.2 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + store2: 2.14.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/ui/6.2.9_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-jq2xmw3reIqik/6ibUSbNKGR+Xvr9wkAEwexiOl+5WQ5BeYJpw4dmDmsFQf+SQuWaSEUUPolbzkakRQM778Kdg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@emotion/core': 10.3.1_react@16.14.0 + '@storybook/addons': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/api': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channels': 6.2.9 + '@storybook/client-logger': 6.2.9 + '@storybook/components': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core-events': 6.2.9 + '@storybook/router': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.2.9_wcqkhtmu7mswc6yz4uyexck3ty + '@types/markdown-to-jsx': 6.11.3 + copy-to-clipboard: 3.3.2 + core-js: 3.26.0 + core-js-pure: 3.20.2 + downshift: 6.1.12_react@16.14.0 + emotion-theming: 10.3.0_qzeatvug73zaio2r3dlvejynye + fuse.js: 3.6.1 + global: 4.4.0 + lodash: 4.17.21 + markdown-to-jsx: 6.11.4_react@16.14.0 + memoizerific: 1.11.3 + polished: 4.2.2 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-draggable: 4.4.5_wcqkhtmu7mswc6yz4uyexck3ty + react-helmet-async: 1.3.0_wcqkhtmu7mswc6yz4uyexck3ty + react-sizeme: 3.0.2 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + store2: 2.14.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/ui/6.5.13_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-MklJuSg4Bc+MWjwhZVmZhJaucaeEBUMMa2V9oRWbIgZOdRHqdW72S2vCbaarDAYfBQdnfaoq1GkSQiw+EnWOzA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@storybook/addons': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/api': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/channels': 6.5.13 + '@storybook/client-logger': 6.5.13 + '@storybook/components': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/core-events': 6.5.13 + '@storybook/router': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.5.13_wcqkhtmu7mswc6yz4uyexck3ty + core-js: 3.26.0 + memoizerific: 1.11.3 + qs: 6.11.0 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + dev: true + + /@surma/rollup-plugin-off-main-thread/1.4.2: + resolution: {integrity: sha512-yBMPqmd1yEJo/280PAMkychuaALyQ9Lkb5q1ck3mjJrFuEobIfhnQ4J3mbvBoISmR3SWMWV+cGB/I0lCQee79A==} + dependencies: + ejs: 2.7.4 + magic-string: 0.25.9 + dev: true + /@surma/rollup-plugin-off-main-thread/2.2.3: resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} dependencies: ejs: 3.1.6 - json5: 2.2.0 + json5: 2.2.1 magic-string: 0.25.9 string.prototype.matchall: 4.0.6 dev: true @@ -3398,7 +9149,7 @@ packages: engines: {node: '>=10'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/runtime': 7.16.3 + '@babel/runtime': 7.17.8 '@types/aria-query': 4.2.2 aria-query: 4.2.2 chalk: 4.1.2 @@ -3407,6 +9158,31 @@ packages: pretty-format: 26.6.2 dev: true + /@testing-library/jest-dom/5.16.5: + resolution: {integrity: sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==} + engines: {node: '>=8', npm: '>=6', yarn: '>=1'} + dependencies: + '@adobe/css-tools': 4.0.1 + '@babel/runtime': 7.17.8 + '@types/testing-library__jest-dom': 5.14.5 + aria-query: 5.1.1 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.5.7 + lodash: 4.17.21 + redent: 3.0.0 + dev: true + + /@testing-library/preact-hooks/1.1.0_p7poi7nh2j5v3fg73wd3em3ise: + resolution: {integrity: sha512-+JIor+NsOHkK3oIrwMDGKGHXTN0JJi462dBJlj4FNbGaDPTlctE6eu2ranWQirh7/FJMkWfzQCP+tk7jmY8ZrQ==} + peerDependencies: + '@testing-library/preact': ^2.0.0 + preact: ^10.4.8 + dependencies: + '@testing-library/preact': 2.0.1_preact@10.6.1 + preact: 10.6.1 + dev: true + /@testing-library/preact-hooks/1.1.0_vfcmu6iy7nffpurikpgxo6gwxi: resolution: {integrity: sha512-+JIor+NsOHkK3oIrwMDGKGHXTN0JJi462dBJlj4FNbGaDPTlctE6eu2ranWQirh7/FJMkWfzQCP+tk7jmY8ZrQ==} peerDependencies: @@ -3417,6 +9193,16 @@ packages: preact: 10.6.5 dev: true + /@testing-library/preact/2.0.1_preact@10.6.1: + resolution: {integrity: sha512-79kwVOY+3caoLgaPbiPzikjgY0Aya7Fc7TvGtR1upCnz2wrtmPDnN2t9vO7I7vDP2zoA+feSwOH5Q0BFErhaaQ==} + engines: {node: '>= 10'} + peerDependencies: + preact: '>=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0' + dependencies: + '@testing-library/dom': 7.31.2 + preact: 10.6.1 + dev: true + /@testing-library/preact/2.0.1_preact@10.6.5: resolution: {integrity: sha512-79kwVOY+3caoLgaPbiPzikjgY0Aya7Fc7TvGtR1upCnz2wrtmPDnN2t9vO7I7vDP2zoA+feSwOH5Q0BFErhaaQ==} engines: {node: '>= 10'} @@ -3427,6 +9213,11 @@ packages: preact: 10.6.5 dev: true + /@tootallnate/once/1.1.2: + resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} + engines: {node: '>= 6'} + dev: true + /@trysound/sax/0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -3436,6 +9227,35 @@ packages: resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} dev: true + /@types/babel__core/7.1.19: + resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} + dependencies: + '@babel/parser': 7.18.4 + '@babel/types': 7.18.4 + '@types/babel__generator': 7.6.4 + '@types/babel__template': 7.4.1 + '@types/babel__traverse': 7.18.2 + dev: true + + /@types/babel__generator/7.6.4: + resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} + dependencies: + '@babel/types': 7.19.4 + dev: true + + /@types/babel__template/7.4.1: + resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} + dependencies: + '@babel/parser': 7.19.6 + '@babel/types': 7.19.4 + dev: true + + /@types/babel__traverse/7.18.2: + resolution: {integrity: sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==} + dependencies: + '@babel/types': 7.19.4 + dev: true + /@types/body-parser/1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: @@ -3449,9 +9269,19 @@ packages: '@types/node': 18.8.5 dev: true + /@types/braces/3.0.1: + resolution: {integrity: sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==} + dev: true + /@types/chai/4.3.0: resolution: {integrity: sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==} + /@types/cheerio/0.22.31: + resolution: {integrity: sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==} + dependencies: + '@types/node': 18.8.5 + dev: true + /@types/chrome/0.0.197: resolution: {integrity: sha512-m1NfS5bOjaypyqQfaX6CxmJodZVcvj5+Mt/K94EBHkflYjPNmXHAzbxfifdLMa0YM3PDyOxohoTS5ug/e6p5jA==} dependencies: @@ -3459,6 +9289,16 @@ packages: '@types/har-format': 1.2.9 dev: true + /@types/color-convert/2.0.0: + resolution: {integrity: sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==} + dependencies: + '@types/color-name': 1.1.1 + dev: true + + /@types/color-name/1.1.1: + resolution: {integrity: sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==} + dev: true + /@types/connect-history-api-fallback/1.3.5: resolution: {integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==} dependencies: @@ -3472,6 +9312,13 @@ packages: '@types/node': 18.8.5 dev: true + /@types/enzyme/3.10.12: + resolution: {integrity: sha512-xryQlOEIe1TduDWAOphR0ihfebKFSWOXpIsk+70JskCfRfW+xALdnJ0r1ZOTo85F9Qsjk6vtlU7edTYHbls9tA==} + dependencies: + '@types/cheerio': 0.22.31 + '@types/react': 18.0.21 + dev: true + /@types/estree/0.0.39: resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} dev: true @@ -3507,20 +9354,58 @@ packages: resolution: {integrity: sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==} dev: true + /@types/glob-base/0.3.0: + resolution: {integrity: sha512-NRCU51ALpNedUvwiwifAkDIWIC25MqF9+0STzAzvhlzR5U+iHTiaUlZ1iOMCwqZAU05X9UlqL63FVrZTZ6tySA==} + dev: true + + /@types/glob/7.2.0: + resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + dependencies: + '@types/minimatch': 5.1.2 + '@types/node': 18.8.5 + dev: true + + /@types/glob/8.0.0: + resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} + dependencies: + '@types/minimatch': 5.1.2 + '@types/node': 18.8.5 + dev: true + + /@types/graceful-fs/4.1.5: + resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} + dependencies: + '@types/node': 18.8.5 + dev: true + /@types/har-format/1.2.9: resolution: {integrity: sha512-rffW6MhQ9yoa75bdNi+rjZBAvu2HhehWJXlhuWXnWdENeuKe82wUgAwxYOb7KRKKmxYN+D/iRKd2NDQMLqlUmg==} dev: true + /@types/hast/2.3.4: + resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} + dependencies: + '@types/unist': 2.0.6 + dev: true + /@types/history/4.7.9: resolution: {integrity: sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==} dev: true + /@types/html-minifier-terser/5.1.2: + resolution: {integrity: sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==} + dev: true + /@types/http-proxy/1.17.8: resolution: {integrity: sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==} dependencies: '@types/node': 18.8.5 dev: true + /@types/is-function/1.0.1: + resolution: {integrity: sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q==} + dev: true + /@types/istanbul-lib-coverage/2.0.3: resolution: {integrity: sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==} dev: true @@ -3541,12 +9426,22 @@ packages: '@types/istanbul-lib-report': 3.0.0 dev: true - /@types/json-schema/7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + /@types/jest/26.0.24: + resolution: {integrity: sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==} + dependencies: + jest-diff: 26.6.2 + pretty-format: 26.6.2 + dev: true + + /@types/jest/27.5.2: + resolution: {integrity: sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==} + dependencies: + jest-matcher-utils: 27.5.1 + pretty-format: 27.5.1 dev: true - /@types/json-schema/7.0.9: - resolution: {integrity: sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==} + /@types/json-schema/7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true /@types/json5/0.0.29: @@ -3559,25 +9454,101 @@ packages: '@types/node': 18.8.5 dev: true + /@types/lodash/4.14.186: + resolution: {integrity: sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==} + + /@types/markdown-to-jsx/6.11.3: + resolution: {integrity: sha512-30nFYpceM/ZEvhGiqWjm5quLUxNeld0HCzJEXMZZDpq53FPkS85mTwkWtCXzCqq8s5JYLgM5W392a02xn8Bdaw==} + dependencies: + '@types/react': 18.0.21 + dev: true + + /@types/mdast/3.0.10: + resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} + dependencies: + '@types/unist': 2.0.6 + dev: true + + /@types/micromatch/4.0.2: + resolution: {integrity: sha512-oqXqVb0ci19GtH0vOA/U2TmHTcRY9kuZl4mqUxe0QmJAlIW13kzhuK5pi1i9+ngav8FjpSb9FVS/GE00GLX1VA==} + dependencies: + '@types/braces': 3.0.1 + dev: true + /@types/mime/1.3.2: resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} dev: true + /@types/minimatch/5.1.2: + resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} + dev: true + + /@types/mocha/8.2.3: + resolution: {integrity: sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==} + dev: true + /@types/mocha/9.0.0: resolution: {integrity: sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==} dev: true + /@types/mustache/4.2.1: + resolution: {integrity: sha512-gFAlWL9Ik21nJioqjlGCnNYbf9zHi0sVbaZ/1hQEBcCEuxfLJDvz4bVJSV6v6CUaoLOz0XEIoP7mSrhJ6o237w==} + dev: true + + /@types/node-fetch/2.6.2: + resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} + dependencies: + '@types/node': 18.8.5 + form-data: 3.0.1 + dev: true + + /@types/node/14.18.32: + resolution: {integrity: sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow==} + dev: true + + /@types/node/16.18.0: + resolution: {integrity: sha512-LqYqYzYvnbCaQfLAwRt0zboqnsViwhZm+vjaMSqcfN36vulAg7Pt0T83q4WZO2YOBw3XdyHi8cQ88H22zmULOA==} + dev: true + /@types/node/18.8.5: resolution: {integrity: sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q==} + /@types/normalize-package-data/2.4.1: + resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + dev: true + + /@types/npmlog/4.1.4: + resolution: {integrity: sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==} + dev: true + /@types/offscreencanvas/2019.7.0: resolution: {integrity: sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==} dev: false + /@types/overlayscrollbars/1.12.1: + resolution: {integrity: sha512-V25YHbSoKQN35UasHf0EKD9U2vcmexRSp78qa8UglxFH8H3D+adEa9zGZwrqpH4TdvqeMrgMqVqsLB4woAryrQ==} + dev: true + /@types/parse-json/4.0.0: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: true + /@types/parse5/5.0.3: + resolution: {integrity: sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==} + dev: true + + /@types/prettier/2.7.1: + resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} + dev: true + + /@types/pretty-hrtime/1.0.1: + resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==} + dev: true + + /@types/prop-types/15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: true + /@types/q/1.5.5: resolution: {integrity: sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==} dev: true @@ -3590,6 +9561,32 @@ packages: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} dev: true + /@types/reach__router/1.3.11: + resolution: {integrity: sha512-j23ChnIEiW8aAP4KT8OVyTXOFr+Ri65BDnwzmfHFO9WHypXYevHFjeil1Cj7YH3emfCE924BwAmgW4hOv7Wg3g==} + dependencies: + '@types/react': 18.0.21 + dev: true + + /@types/react-syntax-highlighter/11.0.5: + resolution: {integrity: sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg==} + dependencies: + '@types/react': 18.0.21 + dev: true + + /@types/react/18.0.21: + resolution: {integrity: sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.1 + dev: true + + /@types/resolve/0.0.8: + resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} + dependencies: + '@types/node': 18.8.5 + dev: true + /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: @@ -3606,6 +9603,14 @@ packages: resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} dev: true + /@types/scheduler/0.16.2: + resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + dev: true + + /@types/semver/7.3.12: + resolution: {integrity: sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==} + dev: true + /@types/serve-index/1.9.1: resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==} dependencies: @@ -3629,10 +9634,20 @@ packages: resolution: {integrity: sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==} dev: true + /@types/stack-utils/2.0.1: + resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true + /@types/tapable/1.0.8: resolution: {integrity: sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==} dev: true + /@types/testing-library__jest-dom/5.14.5: + resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==} + dependencies: + '@types/jest': 27.5.2 + dev: true + /@types/trusted-types/2.0.2: resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} dev: true @@ -3643,6 +9658,14 @@ packages: source-map: 0.6.1 dev: true + /@types/unist/2.0.6: + resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} + dev: true + + /@types/webpack-env/1.18.0: + resolution: {integrity: sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==} + dev: true + /@types/webpack-sources/3.2.0: resolution: {integrity: sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==} dependencies: @@ -3678,6 +9701,44 @@ packages: '@types/yargs-parser': 20.2.1 dev: true + /@types/yargs/16.0.4: + resolution: {integrity: sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==} + dependencies: + '@types/yargs-parser': 20.2.1 + dev: true + + /@types/yargs/17.0.13: + resolution: {integrity: sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==} + dependencies: + '@types/yargs-parser': 20.2.1 + dev: true + + /@typescript-eslint/eslint-plugin/4.33.0_k4l66av2tbo6kxzw52jzgbfzii: + resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + '@typescript-eslint/parser': ^4.0.0 + eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/experimental-utils': 4.33.0_3rubbgt5ekhqrcgx4uwls3neim + '@typescript-eslint/parser': 4.33.0_3rubbgt5ekhqrcgx4uwls3neim + '@typescript-eslint/scope-manager': 4.33.0 + debug: 4.3.4 + eslint: 7.32.0 + functional-red-black-tree: 1.0.1 + ignore: 5.2.0 + regexpp: 3.2.0 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.8.4 + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/eslint-plugin/5.36.1_gjcw3hhr2cxnngiu5lw4bi633m: resolution: {integrity: sha512-iC40UK8q1tMepSDwiLbTbMXKDxzNy+4TfPWgIL661Ym0sD42vRcQU93IsZIrmi+x292DBr60UI/gSwfdVYexCA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3705,6 +9766,70 @@ packages: - supports-color dev: true + /@typescript-eslint/experimental-utils/4.33.0_3rubbgt5ekhqrcgx4uwls3neim: + resolution: {integrity: sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: '*' + dependencies: + '@types/json-schema': 7.0.11 + '@typescript-eslint/scope-manager': 4.33.0 + '@typescript-eslint/types': 4.33.0 + '@typescript-eslint/typescript-estree': 4.33.0_typescript@4.8.4 + eslint: 7.32.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@7.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/experimental-utils/5.40.1_3rubbgt5ekhqrcgx4uwls3neim: + resolution: {integrity: sha512-lynjgnQuoCgxtYgYWjoQqijk0kYQNiztnVhoqha3N0kMYFVPURidzCq2vn9XvUUu2XxP130ZRKVDKyeGa2bhbw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@typescript-eslint/utils': 5.40.1_3rubbgt5ekhqrcgx4uwls3neim + eslint: 7.32.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/experimental-utils/5.40.1_o2nrgn6wwxunlqlzzokx4es3q4: + resolution: {integrity: sha512-lynjgnQuoCgxtYgYWjoQqijk0kYQNiztnVhoqha3N0kMYFVPURidzCq2vn9XvUUu2XxP130ZRKVDKyeGa2bhbw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@typescript-eslint/utils': 5.40.1_o2nrgn6wwxunlqlzzokx4es3q4 + eslint: 8.8.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/parser/4.33.0_3rubbgt5ekhqrcgx4uwls3neim: + resolution: {integrity: sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 4.33.0 + '@typescript-eslint/types': 4.33.0 + '@typescript-eslint/typescript-estree': 4.33.0_typescript@4.8.4 + debug: 4.3.4 + eslint: 7.32.0 + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/parser/5.36.1_o2nrgn6wwxunlqlzzokx4es3q4: resolution: {integrity: sha512-/IsgNGOkBi7CuDfUbwt1eOqUXF9WGVBW9dwEe1pi+L32XrTsZIgmDFIi2RxjzsvB/8i+MIf5JIoTEH8LOZ368A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3725,6 +9850,14 @@ packages: - supports-color dev: true + /@typescript-eslint/scope-manager/4.33.0: + resolution: {integrity: sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==} + engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} + dependencies: + '@typescript-eslint/types': 4.33.0 + '@typescript-eslint/visitor-keys': 4.33.0 + dev: true + /@typescript-eslint/scope-manager/5.36.1: resolution: {integrity: sha512-pGC2SH3/tXdu9IH3ItoqciD3f3RRGCh7hb9zPdN2Drsr341zgd6VbhP5OHQO/reUqihNltfPpMpTNihFMarP2w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3733,6 +9866,14 @@ packages: '@typescript-eslint/visitor-keys': 5.36.1 dev: true + /@typescript-eslint/scope-manager/5.40.1: + resolution: {integrity: sha512-jkn4xsJiUQucI16OLCXrLRXDZ3afKhOIqXs4R3O+M00hdQLKR58WuyXPZZjhKLFCEP2g+TXdBRtLQ33UfAdRUg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.40.1 + '@typescript-eslint/visitor-keys': 5.40.1 + dev: true + /@typescript-eslint/type-utils/5.36.1_o2nrgn6wwxunlqlzzokx4es3q4: resolution: {integrity: sha512-xfZhfmoQT6m3lmlqDvDzv9TiCYdw22cdj06xY0obSznBsT///GK5IEZQdGliXpAOaRL34o8phEvXzEo/VJx13Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3753,11 +9894,42 @@ packages: - supports-color dev: true + /@typescript-eslint/types/4.33.0: + resolution: {integrity: sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==} + engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} + dev: true + /@typescript-eslint/types/5.36.1: resolution: {integrity: sha512-jd93ShpsIk1KgBTx9E+hCSEuLCUFwi9V/urhjOWnOaksGZFbTOxAT47OH2d4NLJnLhkVD+wDbB48BuaycZPLBg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@typescript-eslint/types/5.40.1: + resolution: {integrity: sha512-Icg9kiuVJSwdzSQvtdGspOlWNjVDnF3qVIKXdJ103o36yRprdl3Ge5cABQx+csx960nuMF21v8qvO31v9t3OHw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/4.33.0_typescript@4.8.4: + resolution: {integrity: sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 4.33.0 + '@typescript-eslint/visitor-keys': 4.33.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.8.4 + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/typescript-estree/5.36.1_typescript@4.8.4: resolution: {integrity: sha512-ih7V52zvHdiX6WcPjsOdmADhYMDN15SylWRZrT2OMy80wzKbc79n8wFW0xpWpU0x3VpBz/oDgTm2xwDAnFTl+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3772,7 +9944,28 @@ packages: debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.8.4 + typescript: 4.8.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/typescript-estree/5.40.1_typescript@4.8.4: + resolution: {integrity: sha512-5QTP/nW5+60jBcEPfXy/EZL01qrl9GZtbgDZtDPlfW5zj/zjNrdI2B5zMUHmOsfvOr2cWqwVdWjobCiHcedmQA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.40.1 + '@typescript-eslint/visitor-keys': 5.40.1 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 tsutils: 3.21.0_typescript@4.8.4 typescript: 4.8.4 transitivePeerDependencies: @@ -3797,6 +9990,54 @@ packages: - typescript dev: true + /@typescript-eslint/utils/5.40.1_3rubbgt5ekhqrcgx4uwls3neim: + resolution: {integrity: sha512-a2TAVScoX9fjryNrW6BZRnreDUszxqm9eQ9Esv8n5nXApMW0zeANUYlwh/DED04SC/ifuBvXgZpIK5xeJHQ3aw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.12 + '@typescript-eslint/scope-manager': 5.40.1 + '@typescript-eslint/types': 5.40.1 + '@typescript-eslint/typescript-estree': 5.40.1_typescript@4.8.4 + eslint: 7.32.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@7.32.0 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils/5.40.1_o2nrgn6wwxunlqlzzokx4es3q4: + resolution: {integrity: sha512-a2TAVScoX9fjryNrW6BZRnreDUszxqm9eQ9Esv8n5nXApMW0zeANUYlwh/DED04SC/ifuBvXgZpIK5xeJHQ3aw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.12 + '@typescript-eslint/scope-manager': 5.40.1 + '@typescript-eslint/types': 5.40.1 + '@typescript-eslint/typescript-estree': 5.40.1_typescript@4.8.4 + eslint: 8.8.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.8.0 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys/4.33.0: + resolution: {integrity: sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==} + engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} + dependencies: + '@typescript-eslint/types': 4.33.0 + eslint-visitor-keys: 2.1.0 + dev: true + /@typescript-eslint/visitor-keys/5.36.1: resolution: {integrity: sha512-ojB9aRyRFzVMN3b5joSYni6FAS10BBSCAfKJhjJAV08t/a95aM6tAhz+O1jF+EtgxktuSO3wJysp2R+Def/IWQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3805,6 +10046,14 @@ packages: eslint-visitor-keys: 3.3.0 dev: true + /@typescript-eslint/visitor-keys/5.40.1: + resolution: {integrity: sha512-A2DGmeZ+FMja0geX5rww+DpvILpwo1OsiQs0M+joPWJYsiEFBLsH0y1oFymPNul6Z5okSmHpP4ivkc2N0Cgfkw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.40.1 + eslint-visitor-keys: 3.3.0 + dev: true + /@ungap/promise-all-settled/1.1.2: resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} dev: true @@ -3952,10 +10201,14 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} dependencies: - mime-types: 2.1.34 + mime-types: 2.1.35 negotiator: 0.6.3 dev: true + /acorn-es7-plugin/1.1.7: + resolution: {integrity: sha512-7D+8kscFMf6F2t+8ZRYmv82CncDZETsaZ4dEl5lh3qQez7FVABk2Vz616SAbnIq1PbNsLVaZjl2oSkk5BWAKng==} + dev: true + /acorn-globals/4.3.4: resolution: {integrity: sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==} dependencies: @@ -3963,6 +10216,21 @@ packages: acorn-walk: 6.2.0 dev: true + /acorn-globals/6.0.0: + resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + dev: true + + /acorn-jsx/5.3.2_acorn@7.4.1: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 7.4.1 + dev: true + /acorn-jsx/5.3.2_acorn@8.7.0: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -3976,17 +10244,34 @@ packages: engines: {node: '>=0.4.0'} dev: true + /acorn-walk/7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + dev: true + /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true + /acorn/5.7.4: + resolution: {integrity: sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /acorn/6.4.2: resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} engines: {node: '>=0.4.0'} hasBin: true dev: true + /acorn/7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /acorn/8.7.0: resolution: {integrity: sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==} engines: {node: '>=0.4.0'} @@ -3999,6 +10284,25 @@ packages: hasBin: true dev: true + /address/1.1.2: + resolution: {integrity: sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==} + engines: {node: '>= 0.12.0'} + dev: true + + /address/1.2.1: + resolution: {integrity: sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==} + engines: {node: '>= 10.0.0'} + dev: true + + /agent-base/6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /aggregate-error/3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -4015,6 +10319,28 @@ packages: indent-string: 5.0.0 dev: true + /airbnb-js-shims/2.2.1: + resolution: {integrity: sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ==} + dependencies: + array-includes: 3.1.4 + array.prototype.flat: 1.2.5 + array.prototype.flatmap: 1.2.5 + es5-shim: 4.6.7 + es6-shim: 0.35.6 + function.prototype.name: 1.1.5 + globalthis: 1.0.3 + object.entries: 1.1.5 + object.fromentries: 2.0.5 + object.getownpropertydescriptors: 2.1.3 + object.values: 1.1.5 + promise.allsettled: 1.0.5 + promise.prototype.finally: 3.1.3 + string.prototype.matchall: 4.0.6 + string.prototype.padend: 3.1.3 + string.prototype.padstart: 3.1.3 + symbol.prototype.description: 1.0.5 + dev: true + /ajv-errors/1.0.1_ajv@6.12.6: resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==} peerDependencies: @@ -4038,7 +10364,6 @@ packages: ajv: ^6.9.1 dependencies: ajv: 6.12.6 - dev: true /ajv-keywords/5.1.0_ajv@8.10.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} @@ -4056,7 +10381,6 @@ packages: fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - dev: true /ajv/8.10.0: resolution: {integrity: sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==} @@ -4068,7 +10392,7 @@ packages: dev: true /alphanum-sort/1.0.2: - resolution: {integrity: sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=} + resolution: {integrity: sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==} dev: true /ansi-align/3.0.1: @@ -4087,6 +10411,13 @@ packages: engines: {node: '>=6'} dev: true + /ansi-escapes/4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + /ansi-html-community/0.0.8: resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} engines: {'0': node >= 0.8.0} @@ -4094,10 +10425,15 @@ packages: dev: true /ansi-regex/2.1.1: - resolution: {integrity: sha1-w7M6te42DYbg5ijwRorn7yfWVN8=} + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} engines: {node: '>=0.10.0'} dev: true + /ansi-regex/4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + dev: true + /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -4109,7 +10445,7 @@ packages: dev: true /ansi-styles/1.0.0: - resolution: {integrity: sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=} + resolution: {integrity: sha512-3iF4FIKdxaVYT3JqQuY3Wat/T2t7TRbbQ94Fu50ZUCbLy4TFbTzr90NOHQodQkNqmeEGCw8WbeP78WNi6SKYUA==} engines: {node: '>=0.8.0'} dev: true @@ -4127,11 +10463,24 @@ packages: color-convert: 2.0.1 dev: true + /ansi-styles/5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + /ansi-styles/6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} dev: true + /ansi-to-html/0.6.15: + resolution: {integrity: sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + entities: 2.2.0 + dev: true + /anymatch/2.0.0: resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} dependencies: @@ -4140,7 +10489,15 @@ packages: transitivePeerDependencies: - supports-color dev: true - optional: true + + /anymatch/2.0.0_supports-color@6.1.0: + resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} + dependencies: + micromatch: 3.1.10_supports-color@6.1.0 + normalize-path: 2.1.1 + transitivePeerDependencies: + - supports-color + dev: true /anymatch/3.1.2: resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} @@ -4150,6 +10507,10 @@ packages: picomatch: 2.3.1 dev: true + /app-root-dir/1.0.2: + resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} + dev: true + /append-transform/2.0.0: resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==} engines: {node: '>=8'} @@ -4165,6 +10526,21 @@ packages: resolution: {integrity: sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=} dev: true + /are-we-there-yet/1.1.7: + resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} + dependencies: + delegates: 1.0.0 + readable-stream: 2.3.7 + dev: true + + /are-we-there-yet/2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.0 + dev: true + /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -4179,12 +10555,18 @@ packages: resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} engines: {node: '>=6.0'} dependencies: - '@babel/runtime': 7.16.7 + '@babel/runtime': 7.17.8 '@babel/runtime-corejs3': 7.16.7 dev: true + /aria-query/5.1.1: + resolution: {integrity: sha512-4cPQjOYM2mqq7mZG8CSxkUvL2Yv/x29VhGq5LKehTsxRnoVQps1YGt9NyjcNQsznEsD4rr8a6zGxqeNTqJWjpA==} + dependencies: + deep-equal: 2.0.5 + dev: true + /arr-diff/4.0.0: - resolution: {integrity: sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=} + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} engines: {node: '>=0.10.0'} dev: true @@ -4194,12 +10576,12 @@ packages: dev: true /arr-union/3.1.0: - resolution: {integrity: sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=} + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} engines: {node: '>=0.10.0'} dev: true /array-equal/1.0.0: - resolution: {integrity: sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=} + resolution: {integrity: sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==} dev: true /array-find-index/1.0.2: @@ -4208,7 +10590,7 @@ packages: dev: true /array-flatten/1.1.1: - resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=} + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} dev: true /array-flatten/2.1.2: @@ -4226,16 +10608,39 @@ packages: is-string: 1.0.7 dev: true + /array-union/1.0.2: + resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} + engines: {node: '>=0.10.0'} + dependencies: + array-uniq: 1.0.3 + dev: true + /array-union/2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} dev: true + /array-uniq/1.0.3: + resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} + engines: {node: '>=0.10.0'} + dev: true + /array-unique/0.3.2: - resolution: {integrity: sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=} + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} engines: {node: '>=0.10.0'} dev: true + /array.prototype.filter/1.0.1: + resolution: {integrity: sha512-Dk3Ty7N42Odk7PjU/Ci3zT4pLj20YvuVnneG/58ICM6bt4Ij5kZaJTVQ9TSaWaIECX2sFyz4KItkVZqHNnciqw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-array-method-boxes-properly: 1.0.0 + is-string: 1.0.7 + dev: true + /array.prototype.flat/1.2.5: resolution: {integrity: sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==} engines: {node: '>= 0.4'} @@ -4250,8 +10655,19 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.3 - es-abstract: 1.19.1 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /array.prototype.map/1.0.4: + resolution: {integrity: sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-array-method-boxes-properly: 1.0.0 + is-string: 1.0.7 dev: true /arrgv/1.0.2: @@ -4259,6 +10675,11 @@ packages: engines: {node: '>=8.0.0'} dev: true + /arrify/2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + dev: true + /arrify/3.0.0: resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} engines: {node: '>=12'} @@ -4280,7 +10701,7 @@ packages: dev: true /assert-plus/1.0.0: - resolution: {integrity: sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=} + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} dev: true @@ -4295,25 +10716,35 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} /assign-symbols/1.0.0: - resolution: {integrity: sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=} + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} dev: true + /ast-metadata-inferer/0.7.0: + resolution: {integrity: sha512-OkMLzd8xelb3gmnp6ToFvvsHLtS6CbagTkFQvQ+ZYFe3/AIl9iKikNR9G7pY3GfOR/2Xc222hwBjzI7HLkE76Q==} + dependencies: + '@mdn/browser-compat-data': 3.3.14 + dev: true + /ast-types-flow/0.0.7: resolution: {integrity: sha1-9wtzXGvKGlycItmCw+Oef+ujva0=} dev: true + /astral-regex/2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + /async-each/1.0.3: resolution: {integrity: sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==} dev: true - optional: true /async-limiter/1.0.1: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} dev: true /async/0.9.2: - resolution: {integrity: sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=} + resolution: {integrity: sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==} dev: true /async/2.6.3: @@ -4352,6 +10783,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /autoprefixer/9.8.8: + resolution: {integrity: sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==} + hasBin: true + dependencies: + browserslist: 4.19.1 + caniuse-lite: 1.0.30001311 + normalize-range: 0.1.2 + num2fraction: 1.2.2 + picocolors: 0.2.1 + postcss: 7.0.39 + postcss-value-parser: 4.2.0 + dev: true + /ava/4.3.3: resolution: {integrity: sha512-9Egq/d9R74ExrWohHeqUlexjDbgZJX5jA1Wq4KCTqc3wIfpGEK79zVy4rBtofJ9YKIxs4PzhJ8BgbW5PlAYe6w==} engines: {node: '>=12.22 <13 || >=14.17 <15 || >=16.4 <17 || >=18'} @@ -4471,8 +10915,13 @@ packages: - supports-color dev: true + /available-typed-arrays/1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + /aws-sign2/0.7.0: - resolution: {integrity: sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=} + resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} dev: true /aws4/1.11.0: @@ -4487,10 +10936,9 @@ packages: /axios/0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.14.8 + follow-redirects: 1.15.2 transitivePeerDependencies: - debug - dev: true /axios/0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} @@ -4515,6 +10963,13 @@ packages: webpack: 4.46.0 dev: true + /babel-extract-comments/1.0.0: + resolution: {integrity: sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==} + engines: {node: '>=4'} + dependencies: + babylon: 6.18.0 + dev: true + /babel-helper-builder-react-jsx/6.26.0: resolution: {integrity: sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=} dependencies: @@ -4523,6 +10978,63 @@ packages: esutils: 2.0.3 dev: true + /babel-jest/26.6.3_@babel+core@7.19.6: + resolution: {integrity: sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==} + engines: {node: '>= 10.14.2'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + '@jest/transform': 26.6.2 + '@jest/types': 26.6.2 + '@types/babel__core': 7.1.19 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 26.6.2_@babel+core@7.19.6 + chalk: 4.1.2 + graceful-fs: 4.2.10 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-jest/27.5.1_@babel+core@7.17.2: + resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.17.2 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/babel__core': 7.1.19 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 27.5.1_@babel+core@7.17.2 + chalk: 4.1.2 + graceful-fs: 4.2.10 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-jest/27.5.1_@babel+core@7.19.6: + resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.19.6 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/babel__core': 7.1.19 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 27.5.1_@babel+core@7.19.6 + chalk: 4.1.2 + graceful-fs: 4.2.10 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-loader/8.2.3_@babel+core@7.13.16: resolution: {integrity: sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==} engines: {node: '>= 8.9'} @@ -4537,6 +11049,20 @@ packages: schema-utils: 2.7.1 dev: true + /babel-loader/8.2.3_@babel+core@7.17.2: + resolution: {integrity: sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==} + engines: {node: '>= 8.9'} + peerDependencies: + '@babel/core': ^7.0.0 + webpack: '>=2' + dependencies: + '@babel/core': 7.17.2 + find-cache-dir: 3.3.2 + loader-utils: 1.4.0 + make-dir: 3.1.0 + schema-utils: 2.7.1 + dev: true + /babel-loader/8.2.3_mc362qep5qjjhwk7q3m45kuizi: resolution: {integrity: sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==} engines: {node: '>= 8.9'} @@ -4552,19 +11078,106 @@ packages: webpack: 4.46.0 dev: true + /babel-loader/8.2.3_q4ydpsrmbqywduja5orgah6fgq: + resolution: {integrity: sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==} + engines: {node: '>= 8.9'} + peerDependencies: + '@babel/core': ^7.0.0 + webpack: '>=2' + dependencies: + '@babel/core': 7.19.6 + find-cache-dir: 3.3.2 + loader-utils: 1.4.0 + make-dir: 3.1.0 + schema-utils: 2.7.1 + webpack: 4.46.0 + dev: true + + /babel-plugin-apply-mdx-type-prop/1.6.22_@babel+core@7.12.9: + resolution: {integrity: sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==} + peerDependencies: + '@babel/core': ^7.11.6 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.10.4 + '@mdx-js/util': 1.6.22 + dev: true + /babel-plugin-dynamic-import-node/2.3.3: resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} dependencies: object.assign: 4.1.2 dev: true + /babel-plugin-emotion/10.2.2: + resolution: {integrity: sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==} + dependencies: + '@babel/helper-module-imports': 7.18.6 + '@emotion/hash': 0.8.0 + '@emotion/memoize': 0.7.4 + '@emotion/serialize': 0.11.16 + babel-plugin-macros: 2.8.0 + babel-plugin-syntax-jsx: 6.18.0 + convert-source-map: 1.8.0 + escape-string-regexp: 1.0.5 + find-root: 1.1.0 + source-map: 0.5.7 + dev: true + + /babel-plugin-extract-import-names/1.6.22: + resolution: {integrity: sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==} + dependencies: + '@babel/helper-plugin-utils': 7.10.4 + dev: true + + /babel-plugin-istanbul/6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.19.0 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist/26.6.2: + resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==} + engines: {node: '>= 10.14.2'} + dependencies: + '@babel/template': 7.18.10 + '@babel/types': 7.19.4 + '@types/babel__core': 7.1.19 + '@types/babel__traverse': 7.18.2 + dev: true + + /babel-plugin-jest-hoist/27.5.1: + resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@babel/template': 7.18.10 + '@babel/types': 7.19.4 + '@types/babel__core': 7.1.19 + '@types/babel__traverse': 7.18.2 + dev: true + + /babel-plugin-macros/2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + dependencies: + '@babel/runtime': 7.17.8 + cosmiconfig: 6.0.0 + resolve: 1.22.1 + dev: true + /babel-plugin-macros/3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} dependencies: - '@babel/runtime': 7.17.2 + '@babel/runtime': 7.17.8 cosmiconfig: 7.0.1 - resolve: 1.22.0 + resolve: 1.22.1 dev: true /babel-plugin-polyfill-corejs2/0.3.1_@babel+core@7.13.16: @@ -4593,6 +11206,43 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-corejs2/0.3.1_@babel+core@7.19.6: + resolution: {integrity: sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.17.0 + '@babel/core': 7.19.6 + '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.19.6 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs3/0.1.7_@babel+core@7.17.2: + resolution: {integrity: sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.2 + '@babel/helper-define-polyfill-provider': 0.1.5_@babel+core@7.17.2 + core-js-compat: 3.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs3/0.1.7_@babel+core@7.19.6: + resolution: {integrity: sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-define-polyfill-provider': 0.1.5_@babel+core@7.19.6 + core-js-compat: 3.21.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-polyfill-corejs3/0.5.2_@babel+core@7.13.16: resolution: {integrity: sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==} peerDependencies: @@ -4617,6 +11267,18 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-corejs3/0.5.2_@babel+core@7.19.6: + resolution: {integrity: sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.19.6 + core-js-compat: 3.21.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-polyfill-regenerator/0.3.1_@babel+core@7.13.16: resolution: {integrity: sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==} peerDependencies: @@ -4639,10 +11301,32 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-regenerator/0.3.1_@babel+core@7.19.6: + resolution: {integrity: sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.19.6 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-syntax-jsx/6.18.0: resolution: {integrity: sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=} dev: true + /babel-plugin-syntax-object-rest-spread/6.13.0: + resolution: {integrity: sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==} + dev: true + + /babel-plugin-transform-object-rest-spread/6.26.0: + resolution: {integrity: sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==} + dependencies: + babel-plugin-syntax-object-rest-spread: 6.13.0 + babel-runtime: 6.26.0 + dev: true + /babel-plugin-transform-react-jsx/6.24.1: resolution: {integrity: sha1-hAoCjn30YN/DotKfDA2R9jduZqM=} dependencies: @@ -4655,6 +11339,79 @@ packages: resolution: {integrity: sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==} dev: true + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.17.2: + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.17.2 + '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.17.2 + '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.17.2 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.17.2 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.17.2 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.17.2 + dev: true + + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.19.6: + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.19.6 + '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.19.6 + '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.19.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.19.6 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.19.6 + dev: true + + /babel-preset-jest/26.6.2_@babel+core@7.19.6: + resolution: {integrity: sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==} + engines: {node: '>= 10.14.2'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + babel-plugin-jest-hoist: 26.6.2 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.6 + dev: true + + /babel-preset-jest/27.5.1_@babel+core@7.17.2: + resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.17.2 + babel-plugin-jest-hoist: 27.5.1 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.17.2 + dev: true + + /babel-preset-jest/27.5.1_@babel+core@7.19.6: + resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.19.6 + babel-plugin-jest-hoist: 27.5.1 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.6 + dev: true + /babel-runtime/6.26.0: resolution: {integrity: sha1-llxwWGaOgrVde/4E/yM3vItWR/4=} dependencies: @@ -4671,6 +11428,15 @@ packages: to-fast-properties: 1.0.3 dev: true + /babylon/6.18.0: + resolution: {integrity: sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==} + hasBin: true + dev: true + + /bail/1.0.5: + resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} + dev: true + /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -4687,24 +11453,54 @@ packages: pascalcase: 0.1.1 dev: true + /base64-inline-loader/1.1.1: + resolution: {integrity: sha512-v/bHvXQ8sW28t9ZwBsFGgyqZw2bpT49/dtPTtlmixoSM/s9wnOngOKFVQLRH8BfMTy6jTl5m5CdvqpZt8y5d6g==} + engines: {node: '>=6.2', npm: '>=3.8'} + peerDependencies: + webpack: ^4.x + dependencies: + file-loader: 1.1.11 + loader-utils: 1.4.0 + mime-types: 2.1.35 + /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true + /batch-processor/1.0.0: + resolution: {integrity: sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=} + dev: true + /batch/0.6.1: resolution: {integrity: sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=} dev: true /bcrypt-pbkdf/1.0.2: - resolution: {integrity: sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=} + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} dependencies: tweetnacl: 0.14.5 dev: true + /better-opn/2.1.1: + resolution: {integrity: sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA==} + engines: {node: '>8.0.0'} + dependencies: + open: 7.4.2 + dev: true + + /bfj/6.1.2: + resolution: {integrity: sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==} + engines: {node: '>= 6.0.0'} + dependencies: + bluebird: 3.7.2 + check-types: 8.0.3 + hoopy: 0.1.4 + tryer: 1.0.1 + dev: true + /big-integer/1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} - dev: false /big.js/3.2.0: resolution: {integrity: sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==} @@ -4712,13 +11508,11 @@ packages: /big.js/5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - dev: true /binary-extensions/1.13.1: resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} engines: {node: '>=0.10.0'} dev: true - optional: true /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} @@ -4775,8 +11569,26 @@ packages: - supports-color dev: true + /body-parser/1.19.1_supports-color@6.1.0: + resolution: {integrity: sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.1 + content-type: 1.0.4 + debug: 2.6.9_supports-color@6.1.0 + depd: 1.1.2 + http-errors: 1.8.1 + iconv-lite: 0.4.24 + on-finished: 2.3.0 + qs: 6.9.6 + raw-body: 2.4.2 + type-is: 1.6.18 + transitivePeerDependencies: + - supports-color + dev: true + /bonjour/3.5.0: - resolution: {integrity: sha1-jokKGD2O6aI5OzhExpGkK897yfU=} + resolution: {integrity: sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==} dependencies: array-flatten: 2.1.2 deep-equal: 1.1.1 @@ -4787,7 +11599,21 @@ packages: dev: true /boolbase/1.0.0: - resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=} + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + + /boxen/4.2.0: + resolution: {integrity: sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==} + engines: {node: '>=8'} + dependencies: + ansi-align: 3.0.1 + camelcase: 5.3.1 + chalk: 3.0.0 + cli-boxes: 2.2.1 + string-width: 4.2.3 + term-size: 2.2.1 + type-fest: 0.8.1 + widest-line: 3.1.0 dev: true /boxen/5.1.2: @@ -4804,6 +11630,13 @@ packages: wrap-ansi: 7.0.0 dev: true + /bplist-parser/0.1.1: + resolution: {integrity: sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q==} + dependencies: + big-integer: 1.6.51 + dev: true + optional: true + /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -4834,6 +11667,24 @@ packages: - supports-color dev: true + /braces/2.3.2_supports-color@6.1.0: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2_supports-color@6.1.0 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /braces/3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -4842,7 +11693,7 @@ packages: dev: true /brorand/1.1.0: - resolution: {integrity: sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=} + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} dev: true /browser-process-hrtime/1.0.0: @@ -4908,6 +11759,17 @@ packages: pako: 1.0.11 dev: true + /browserslist/4.14.2: + resolution: {integrity: sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001311 + electron-to-chromium: 1.4.68 + escalade: 3.1.1 + node-releases: 1.1.77 + dev: true + /browserslist/4.19.1: resolution: {integrity: sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -4920,6 +11782,23 @@ packages: picocolors: 1.0.0 dev: true + /browserslist/4.21.4: + resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001423 + electron-to-chromium: 1.4.284 + node-releases: 2.0.6 + update-browserslist-db: 1.0.10_browserslist@4.21.4 + dev: true + + /bser/2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -4928,7 +11807,7 @@ packages: dev: true /buffer-xor/1.0.3: - resolution: {integrity: sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=} + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} dev: true /buffer/4.9.2: @@ -4952,11 +11831,11 @@ packages: dev: true /builtin-status-codes/3.0.0: - resolution: {integrity: sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=} + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} dev: true /builtins/1.0.3: - resolution: {integrity: sha1-y5T662HIaWRR2zZTThQi+U8K7og=} + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} dev: true /bulma-checkbox/1.2.1: @@ -4971,6 +11850,28 @@ packages: bulma: 0.9.3 dev: true + /bulma-responsive-tables/1.2.5: + resolution: {integrity: sha512-8/qYiv21cJnGYMkjIF52iKCV/B6XIswu58Vwi3/TS+wLavvA7OFXhBy0quxOnqPNqnovHly2dTCyVCqHLJU7Sg==} + dependencies: + bulma: 0.9.3 + dev: true + + /bulma-switch-control/1.2.2: + resolution: {integrity: sha512-1eHlga1Z4RBRU6DIxNiwb6+I9n9vDkj9/MmwS4pL68P7STE1vbwRutxh9oFeFWuxLXGNLILJEXJXiwyEjT9upw==} + dependencies: + bulma: 0.9.3 + dev: true + + /bulma-timeline/3.0.5: + resolution: {integrity: sha512-gBwdx7PmAEZ/+5zSn/ZBJBqkenT8wi+9nlD0uP8lXa3rGcbhG+fp8Oz98+3O10LQPlUEdKPYEUxtQ55okRfhTQ==} + dev: true + + /bulma-upload-control/1.2.0: + resolution: {integrity: sha512-2raueVPVoG3KjHH+7Aok44nGSPIl76qzdkLKX/ziHAOwbiXBrlEYHXca8Hk0UDa0KElLiPT6Eb2Cvz+8FFUwBw==} + dependencies: + bulma: 0.9.3 + dev: true + /bulma/0.9.3: resolution: {integrity: sha512-0d7GNW1PY4ud8TWxdNcP6Cc8Bu7MxcntD/RRLGWuiw/s0a9P+XlH/6QoOIrmbj6o8WWJzJYhytiu9nFjTszk1g==} dev: true @@ -5010,8 +11911,8 @@ packages: bluebird: 3.7.2 chownr: 1.1.4 figgy-pudding: 3.5.2 - glob: 7.2.0 - graceful-fs: 4.2.9 + glob: 7.2.3 + graceful-fs: 4.2.10 infer-owner: 1.0.4 lru-cache: 5.1.1 mississippi: 3.0.0 @@ -5032,7 +11933,7 @@ packages: '@npmcli/move-file': 1.1.2 chownr: 2.0.0 fs-minipass: 2.1.0 - glob: 7.2.0 + glob: 7.2.3 infer-owner: 1.0.4 lru-cache: 6.0.0 minipass: 3.1.6 @@ -5041,7 +11942,7 @@ packages: minipass-pipeline: 1.2.4 mkdirp: 1.0.4 p-map: 4.0.0 - promise-inflight: 1.0.1_bluebird@3.7.2 + promise-inflight: 1.0.1 rimraf: 3.0.2 ssri: 8.0.1 tar: 6.1.11 @@ -5095,22 +11996,26 @@ packages: get-intrinsic: 1.1.1 dev: true + /call-me-maybe/1.0.1: + resolution: {integrity: sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==} + dev: true + /caller-callsite/2.0.0: - resolution: {integrity: sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=} + resolution: {integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==} engines: {node: '>=4'} dependencies: callsites: 2.0.0 dev: true /caller-path/2.0.0: - resolution: {integrity: sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=} + resolution: {integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==} engines: {node: '>=4'} dependencies: caller-callsite: 2.0.0 dev: true /callsites/2.0.0: - resolution: {integrity: sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=} + resolution: {integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==} engines: {node: '>=4'} dev: true @@ -5125,12 +12030,39 @@ packages: dev: true /camel-case/3.0.0: - resolution: {integrity: sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=} + resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} dependencies: no-case: 2.3.2 upper-case: 1.1.3 dev: true + /camel-case/4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + dependencies: + pascal-case: 3.1.2 + tslib: 2.4.0 + dev: true + + /camelcase-css/2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + + /camelcase-keys/2.1.0: + resolution: {integrity: sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==} + engines: {node: '>=0.10.0'} + dependencies: + camelcase: 2.1.1 + map-obj: 1.0.1 + dev: true + optional: true + + /camelcase/2.1.1: + resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==} + engines: {node: '>=0.10.0'} + dev: true + optional: true + /camelcase/5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -5144,8 +12076,8 @@ packages: /caniuse-api/3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: - browserslist: 4.19.1 - caniuse-lite: 1.0.30001311 + browserslist: 4.21.4 + caniuse-lite: 1.0.30001423 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true @@ -5154,8 +12086,24 @@ packages: resolution: {integrity: sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A==} dev: true + /caniuse-lite/1.0.30001423: + resolution: {integrity: sha512-09iwWGOlifvE1XuHokFMP7eR38a0JnajoyL3/i87c8ZjRWRrdKo1fqjNfugfBD0UDBIOz0U+jtNhJ0EPm1VleQ==} + dev: true + + /capture-exit/2.0.0: + resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} + engines: {node: 6.* || 8.* || >= 10.*} + dependencies: + rsvp: 4.8.5 + dev: true + + /case-sensitive-paths-webpack-plugin/2.4.0: + resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} + engines: {node: '>=4'} + dev: true + /caseless/0.12.0: - resolution: {integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=} + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} dev: true /cbor/8.1.0: @@ -5165,6 +12113,10 @@ packages: nofilter: 3.1.0 dev: true + /ccount/1.1.0: + resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} + dev: true + /chai/4.3.6: resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==} engines: {node: '>=4'} @@ -5178,7 +12130,7 @@ packages: type-detect: 4.0.8 /chalk/0.4.0: - resolution: {integrity: sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=} + resolution: {integrity: sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ==} engines: {node: '>=0.8.0'} dependencies: ansi-styles: 1.0.0 @@ -5225,6 +12177,28 @@ packages: engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: true + /char-regex/1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /char-regex/2.0.1: + resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==} + engines: {node: '>=12.20'} + dev: true + + /character-entities-legacy/1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + dev: true + + /character-entities/1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + dev: true + + /character-reference-invalid/1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + dev: true + /charcodes/0.2.0: resolution: {integrity: sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==} engines: {node: '>=6'} @@ -5233,6 +12207,34 @@ packages: /check-error/1.0.2: resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + /check-types/8.0.3: + resolution: {integrity: sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==} + dev: true + + /cheerio-select/2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + dev: true + + /cheerio/1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.0.1 + htmlparser2: 8.0.1 + parse5: 7.1.1 + parse5-htmlparser2-tree-adapter: 7.0.0 + dev: true + /chokidar/2.1.8: resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies @@ -5255,6 +12257,27 @@ packages: dev: true optional: true + /chokidar/2.1.8_supports-color@6.1.0: + resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} + deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + dependencies: + anymatch: 2.0.0_supports-color@6.1.0 + async-each: 1.0.3 + braces: 2.3.2_supports-color@6.1.0 + glob-parent: 3.1.0 + inherits: 2.0.4 + is-binary-path: 1.0.1 + is-glob: 4.0.3 + normalize-path: 3.0.0 + path-is-absolute: 1.0.1 + readdirp: 2.2.1_supports-color@6.1.0 + upath: 1.2.0 + optionalDependencies: + fsevents: 1.2.13 + transitivePeerDependencies: + - supports-color + dev: true + /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -5311,6 +12334,14 @@ packages: safe-buffer: 5.2.1 dev: true + /cjs-module-lexer/0.6.0: + resolution: {integrity: sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==} + dev: true + + /cjs-module-lexer/1.2.2: + resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} + dev: true + /class-utils/0.3.6: resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} engines: {node: '>=0.10.0'} @@ -5362,6 +12393,25 @@ packages: engines: {node: '>=6'} dev: true + /cli-table3/0.6.0: + resolution: {integrity: sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==} + engines: {node: 10.* || >= 12.*} + dependencies: + object-assign: 4.1.1 + string-width: 4.2.3 + optionalDependencies: + colors: 1.4.0 + dev: true + + /cli-table3/0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: true + /cli-truncate/3.1.0: resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5370,6 +12420,14 @@ packages: string-width: 5.1.2 dev: true + /cliui/5.0.0: + resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} + dependencies: + string-width: 3.1.0 + strip-ansi: 5.2.0 + wrap-ansi: 5.1.0 + dev: true + /cliui/6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -5405,16 +12463,26 @@ packages: dev: true /clone-response/1.0.2: - resolution: {integrity: sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=} + resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==} dependencies: mimic-response: 1.0.1 dev: true /clone/1.0.4: - resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=} + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} dev: true + /clsx/1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: true + + /co/4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + /coa/2.0.2: resolution: {integrity: sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==} engines: {node: '>= 4.0'} @@ -5431,8 +12499,21 @@ packages: convert-to-spaces: 2.0.1 dev: true + /code-point-at/1.1.0: + resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} + engines: {node: '>=0.10.0'} + dev: true + + /collapse-white-space/1.0.6: + resolution: {integrity: sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==} + dev: true + + /collect-v8-coverage/1.0.1: + resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} + dev: true + /collection-visit/1.0.0: - resolution: {integrity: sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=} + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} engines: {node: '>=0.10.0'} dependencies: map-visit: 1.0.0 @@ -5467,6 +12548,11 @@ packages: simple-swizzle: 0.2.2 dev: true + /color-support/1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: true + /color/3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} dependencies: @@ -5482,12 +12568,21 @@ packages: resolution: {integrity: sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==} dev: true + /colors/1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + dev: true + /combined-stream/1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 + /comma-separated-tokens/1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + dev: true + /commander/2.17.1: resolution: {integrity: sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==} dev: true @@ -5500,6 +12595,16 @@ packages: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true + /commander/4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /commander/6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + dev: true + /commander/7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -5526,7 +12631,23 @@ packages: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} dependencies: - mime-db: 1.51.0 + mime-db: 1.52.0 + dev: true + + /compression-webpack-plugin/4.0.1_webpack@4.46.0: + resolution: {integrity: sha512-0mg6PgwTsUe5LEcUrOu3ob32vraDx2VdbMGAT1PARcOV+UJWDYZFdkSo6RbHoGQ061mmmkC7XpRKOlvwm/gzJQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + cacache: 15.3.0 + find-cache-dir: 3.3.2 + schema-utils: 2.7.1 + serialize-javascript: 4.0.0 + webpack: 4.46.0 + webpack-sources: 1.4.3 + transitivePeerDependencies: + - bluebird dev: true /compression-webpack-plugin/6.1.1_webpack@4.46.0: @@ -5560,6 +12681,25 @@ packages: - supports-color dev: true + /compression/1.7.4_supports-color@6.1.0: + resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + engines: {node: '>= 0.8.0'} + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9_supports-color@6.1.0 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /compute-scroll-into-view/1.0.17: + resolution: {integrity: sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==} + dev: true + /concat-map/0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} @@ -5592,7 +12732,7 @@ packages: engines: {node: '>=8'} dependencies: dot-prop: 5.3.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 make-dir: 3.1.0 unique-string: 2.0.0 write-file-atomic: 3.0.3 @@ -5617,8 +12757,12 @@ packages: engines: {node: '>=4'} dev: true + /console-control-strings/1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + dev: true + /constants-browserify/1.0.0: - resolution: {integrity: sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=} + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} dev: true /content-disposition/0.5.4: @@ -5665,10 +12809,37 @@ packages: dev: true /copy-descriptor/0.1.1: - resolution: {integrity: sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=} + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} engines: {node: '>=0.10.0'} dev: true + /copy-to-clipboard/3.3.2: + resolution: {integrity: sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==} + dependencies: + toggle-selection: 1.0.6 + dev: true + + /copy-webpack-plugin/5.1.2_webpack@4.46.0: + resolution: {integrity: sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==} + engines: {node: '>= 6.9.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + cacache: 12.0.4 + find-cache-dir: 2.1.0 + glob-parent: 3.1.0 + globby: 7.1.1 + is-glob: 4.0.3 + loader-utils: 1.4.0 + minimatch: 3.1.2 + normalize-path: 3.0.0 + p-limit: 2.3.0 + schema-utils: 1.0.0 + serialize-javascript: 4.0.0 + webpack: 4.46.0 + webpack-log: 2.0.0 + dev: true + /copy-webpack-plugin/6.4.1_webpack@4.46.0: resolution: {integrity: sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA==} engines: {node: '>= 10.13.0'} @@ -5700,17 +12871,23 @@ packages: /core-js-pure/3.20.2: resolution: {integrity: sha512-CmWHvSKn2vNL6p6StNp1EmMIfVY/pqn3JLAjfZQ8WZGPOlGoO92EkX9/Mk81i6GxvoPXjUqEQnpM3rJ5QxxIOg==} + deprecated: core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure. requiresBuild: true dev: true /core-js/2.6.12: resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} - deprecated: core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js. + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + requiresBuild: true + dev: true + + /core-js/3.26.0: + resolution: {integrity: sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==} requiresBuild: true dev: true /core-util-is/1.0.2: - resolution: {integrity: sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=} + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} dev: true /core-util-is/1.0.3: @@ -5727,6 +12904,17 @@ packages: parse-json: 4.0.0 dev: true + /cosmiconfig/6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + /cosmiconfig/7.0.1: resolution: {integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==} engines: {node: '>=10'} @@ -5738,6 +12926,33 @@ packages: yaml: 1.10.2 dev: true + /cp-file/7.0.0: + resolution: {integrity: sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==} + engines: {node: '>=8'} + dependencies: + graceful-fs: 4.2.10 + make-dir: 3.1.0 + nested-error-stacks: 2.1.1 + p-event: 4.2.0 + dev: true + + /cpy/8.1.2: + resolution: {integrity: sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg==} + engines: {node: '>=8'} + dependencies: + arrify: 2.0.1 + cp-file: 7.0.0 + globby: 9.2.0 + has-glob: 1.0.0 + junk: 3.1.0 + nested-error-stacks: 2.1.1 + p-all: 2.1.0 + p-filter: 2.1.0 + p-map: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /create-ecdh/4.0.4: resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} dependencies: @@ -5766,6 +12981,29 @@ packages: sha.js: 2.4.11 dev: true + /create-react-context/0.3.0_4vyaxm4rsh2mpfdenvlqy7kmya: + resolution: {integrity: sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==} + peerDependencies: + prop-types: ^15.0.0 + react: ^0.14.0 || ^15.0.0 || ^16.0.0 + dependencies: + gud: 1.0.0 + prop-types: 15.8.1 + react: 16.14.0 + warning: 4.0.3 + dev: true + + /create-react-context/0.3.0_prop-types@15.8.1: + resolution: {integrity: sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==} + peerDependencies: + prop-types: ^15.0.0 + react: ^0.14.0 || ^15.0.0 || ^16.0.0 + dependencies: + gud: 1.0.0 + prop-types: 15.8.1 + warning: 4.0.3 + dev: true + /critters-webpack-plugin/2.5.0_html-webpack-plugin@3.2.0: resolution: {integrity: sha512-O41TSPV2orAfrV6kSVC0SivZCtVkeypCNKb7xtrbqE/CfjrHeRaFaGuxglcjOI2IGf+oNg6E+ZoOktdlhXPTIQ==} peerDependencies: @@ -5778,7 +13016,7 @@ packages: cssnano: 4.1.11 html-webpack-plugin: 3.2.0_webpack@4.46.0 jsdom: 12.2.0 - minimatch: 3.0.5 + minimatch: 3.1.2 parse5: 4.0.0 postcss: 7.0.39 pretty-bytes: 4.0.2 @@ -5789,6 +13027,14 @@ packages: - utf-8-validate dev: true + /cross-fetch/3.1.5: + resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} + dependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + dev: true + /cross-spawn-promise/0.10.2: resolution: {integrity: sha512-74PXJf6DYaab2klRS+D+9qxKJL1Weo3/ao9OPoH6NFzxtINSa/HE2mcyAPu1fpEmRTPD4Gdmpg3xEXQSgI8lpg==} engines: {node: '>=4'} @@ -5797,13 +13043,24 @@ packages: dev: true /cross-spawn/5.1.0: - resolution: {integrity: sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=} + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 dev: true + /cross-spawn/6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.1 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -5829,13 +13086,18 @@ packages: randomfill: 1.0.4 dev: true + /crypto-random-string/1.0.0: + resolution: {integrity: sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==} + engines: {node: '>=4'} + dev: true + /crypto-random-string/2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} dev: true /css-color-names/0.0.4: - resolution: {integrity: sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=} + resolution: {integrity: sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==} dev: true /css-declaration-sorter/4.0.1: @@ -5856,6 +13118,28 @@ packages: timsort: 0.3.0 dev: true + /css-loader/3.6.0_webpack@4.46.0: + resolution: {integrity: sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==} + engines: {node: '>= 8.9.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + camelcase: 5.3.1 + cssesc: 3.0.0 + icss-utils: 4.1.1 + loader-utils: 1.4.0 + normalize-path: 3.0.0 + postcss: 7.0.39 + postcss-modules-extract-imports: 2.0.0 + postcss-modules-local-by-default: 3.0.3 + postcss-modules-scope: 2.2.0 + postcss-modules-values: 3.0.0 + postcss-value-parser: 4.2.0 + schema-utils: 2.7.1 + semver: 6.3.0 + webpack: 4.46.0 + dev: true + /css-loader/5.2.7_webpack@4.46.0: resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==} engines: {node: '>= 10.13.0'} @@ -5871,7 +13155,7 @@ packages: postcss-modules-values: 4.0.0_postcss@8.4.6 postcss-value-parser: 4.2.0 schema-utils: 3.1.1 - semver: 7.3.5 + semver: 7.3.8 webpack: 4.46.0 dev: true @@ -5898,6 +13182,16 @@ packages: nth-check: 2.0.1 dev: true + /css-select/5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.0.1 + nth-check: 2.0.1 + dev: true + /css-tree/1.0.0-alpha.37: resolution: {integrity: sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==} engines: {node: '>=8.0.0'} @@ -5924,6 +13218,15 @@ packages: engines: {node: '>= 6'} dev: true + /css-what/6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: true + + /css.escape/1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: true + /css/2.2.4: resolution: {integrity: sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==} dependencies: @@ -6014,12 +13317,12 @@ packages: dev: true /cssnano-util-get-arguments/4.0.0: - resolution: {integrity: sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=} + resolution: {integrity: sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==} engines: {node: '>=6.9.0'} dev: true /cssnano-util-get-match/4.0.0: - resolution: {integrity: sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=} + resolution: {integrity: sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==} engines: {node: '>=6.9.0'} dev: true @@ -6077,12 +13380,31 @@ packages: resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} dev: true + /cssom/0.4.4: + resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==} + dev: true + /cssstyle/1.4.0: resolution: {integrity: sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==} dependencies: cssom: 0.3.8 dev: true + /cssstyle/2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + dependencies: + cssom: 0.3.8 + dev: true + + /csstype/2.6.21: + resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} + dev: true + + /csstype/3.1.1: + resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + dev: true + /currently-unhandled/0.4.1: resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} engines: {node: '>=0.10.0'} @@ -6091,7 +13413,7 @@ packages: dev: true /cyclist/1.0.1: - resolution: {integrity: sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=} + resolution: {integrity: sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==} dev: true /damerau-levenshtein/1.0.7: @@ -6099,7 +13421,7 @@ packages: dev: true /dashdash/1.14.1: - resolution: {integrity: sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=} + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} dependencies: assert-plus: 1.0.0 @@ -6118,6 +13440,20 @@ packages: whatwg-url: 7.1.0 dev: true + /data-urls/2.0.0: + resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} + engines: {node: '>=10'} + dependencies: + abab: 2.0.5 + whatwg-mimetype: 2.3.0 + whatwg-url: 8.7.0 + dev: true + + /date-fns/2.25.0: + resolution: {integrity: sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w==} + engines: {node: '>=0.11'} + dev: false + /date-fns/2.29.2: resolution: {integrity: sha512-0VNbwmWJDS/G3ySwFSJA3ayhbURMTJLtwM2DTxf9CWondCnh6DTNlO9JgRSq6ibf4eD0lfMJNBxUdEAHHix+bA==} engines: {node: '>=0.11'} @@ -6141,6 +13477,18 @@ packages: ms: 2.0.0 dev: true + /debug/2.6.9_supports-color@6.1.0: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + supports-color: 6.1.0 + dev: true + /debug/3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -6152,16 +13500,16 @@ packages: ms: 2.1.3 dev: true - /debug/4.3.2: - resolution: {integrity: sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==} - engines: {node: '>=6.0'} + /debug/3.2.7_supports-color@6.1.0: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true dependencies: - ms: 2.1.2 + ms: 2.1.3 + supports-color: 6.1.0 dev: true /debug/4.3.3: @@ -6201,6 +13549,19 @@ packages: ms: 2.1.2 dev: true + /debug/4.3.4_supports-color@6.1.0: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + supports-color: 6.1.0 + dev: true + /decamelize/1.2.0: resolution: {integrity: sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=} engines: {node: '>=0.10.0'} @@ -6211,18 +13572,26 @@ packages: engines: {node: '>=10'} dev: true + /decimal.js/10.4.2: + resolution: {integrity: sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==} + dev: true + /decode-uri-component/0.2.0: resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==} engines: {node: '>=0.10'} dev: true /decompress-response/3.3.0: - resolution: {integrity: sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=} + resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==} engines: {node: '>=4'} dependencies: mimic-response: 1.0.1 dev: true + /dedent/0.7.0: + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + dev: true + /deep-eql/3.0.1: resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==} engines: {node: '>=0.12'} @@ -6237,7 +13606,27 @@ packages: is-regex: 1.1.4 object-is: 1.1.5 object-keys: 1.1.1 + regexp.prototype.flags: 1.4.3 + dev: true + + /deep-equal/2.0.5: + resolution: {integrity: sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==} + dependencies: + call-bind: 1.0.2 + es-get-iterator: 1.1.2 + get-intrinsic: 1.1.1 + is-arguments: 1.1.1 + is-date-object: 1.0.5 + is-regex: 1.1.4 + isarray: 2.0.5 + object-is: 1.1.5 + object-keys: 1.1.1 + object.assign: 4.1.2 regexp.prototype.flags: 1.4.1 + side-channel: 1.0.4 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.8 dev: true /deep-extend/0.6.0: @@ -6249,6 +13638,10 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /deep-object-diff/1.1.7: + resolution: {integrity: sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==} + dev: true + /deepcopy/1.0.0: resolution: {integrity: sha512-WJrecobaoqqgQHtvRI2/VCzWoWXPAnFYyAkF/spmL46lZMnd0gW0gLGuyeFVSrqt2B3s0oEEj6i+j2L/2QiS4g==} dependencies: @@ -6260,6 +13653,26 @@ packages: engines: {node: '>=0.10.0'} dev: true + /default-browser-id/1.0.4: + resolution: {integrity: sha512-qPy925qewwul9Hifs+3sx1ZYn14obHxpkX+mPD369w4Rzg+YkJBgi3SOvwUq81nWSjqGUegIgEPwD8u+HUnxlw==} + engines: {node: '>=0.10.0'} + hasBin: true + requiresBuild: true + dependencies: + bplist-parser: 0.1.1 + meow: 3.7.0 + untildify: 2.1.0 + dev: true + optional: true + + /default-gateway/4.2.0: + resolution: {integrity: sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==} + engines: {node: '>=6'} + dependencies: + execa: 1.0.0 + ip-regex: 2.1.0 + dev: true + /default-gateway/6.0.3: resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} engines: {node: '>= 10'} @@ -6275,7 +13688,7 @@ packages: dev: true /defaults/1.0.3: - resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=} + resolution: {integrity: sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==} dependencies: clone: 1.0.4 dev: true @@ -6296,15 +13709,23 @@ packages: object-keys: 1.1.1 dev: true + /define-properties/1.1.4: + resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + /define-property/0.2.5: - resolution: {integrity: sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=} + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} engines: {node: '>=0.10.0'} dependencies: is-descriptor: 0.1.6 dev: true /define-property/1.0.0: - resolution: {integrity: sha1-dp66rz9KY6rTr56NMEybvnm/sOY=} + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} engines: {node: '>=0.10.0'} dependencies: is-descriptor: 1.0.2 @@ -6318,12 +13739,25 @@ packages: isobject: 3.0.1 dev: true + /del/4.1.1: + resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==} + engines: {node: '>=6'} + dependencies: + '@types/glob': 7.2.0 + globby: 6.1.0 + is-path-cwd: 2.2.0 + is-path-in-cwd: 2.1.0 + p-map: 2.1.0 + pify: 4.0.1 + rimraf: 2.7.1 + dev: true + /del/6.0.0: resolution: {integrity: sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==} engines: {node: '>=10'} dependencies: globby: 11.1.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 is-glob: 4.0.3 is-path-cwd: 2.2.0 is-path-inside: 3.0.3 @@ -6350,11 +13784,20 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + /delegates/1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: true + /depd/1.1.2: - resolution: {integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=} + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} dev: true + /dequal/2.0.2: + resolution: {integrity: sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==} + engines: {node: '>=6'} + dev: false + /des.js/1.0.1: resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==} dependencies: @@ -6363,13 +13806,62 @@ packages: dev: true /destroy/1.0.4: - resolution: {integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=} + resolution: {integrity: sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==} + dev: true + + /detab/2.0.4: + resolution: {integrity: sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==} + dependencies: + repeat-string: 1.6.1 + dev: true + + /detect-newline/3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} dev: true /detect-node/2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} dev: true + /detect-package-manager/2.0.1: + resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==} + engines: {node: '>=12'} + dependencies: + execa: 5.1.1 + dev: true + + /detect-port-alt/1.1.6: + resolution: {integrity: sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==} + engines: {node: '>= 4.2.1'} + hasBin: true + dependencies: + address: 1.1.2 + debug: 2.6.9 + transitivePeerDependencies: + - supports-color + dev: true + + /detect-port/1.5.1: + resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==} + hasBin: true + dependencies: + address: 1.2.1 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /diff-sequences/26.6.2: + resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==} + engines: {node: '>= 10.14.2'} + dev: true + + /diff-sequences/27.5.1: + resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true + /diff/5.0.0: resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} engines: {node: '>=0.3.1'} @@ -6383,6 +13875,13 @@ packages: randombytes: 2.1.0 dev: true + /dir-glob/2.2.2: + resolution: {integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==} + engines: {node: '>=4'} + dependencies: + path-type: 3.0.0 + dev: true + /dir-glob/3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -6390,8 +13889,12 @@ packages: path-type: 4.0.0 dev: true + /discontinuous-range/1.0.0: + resolution: {integrity: sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=} + dev: true + /dns-equal/1.0.0: - resolution: {integrity: sha1-s55/HabrCnW6nBcySzR1PEfgZU0=} + resolution: {integrity: sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==} dev: true /dns-packet/1.3.4: @@ -6402,7 +13905,7 @@ packages: dev: true /dns-txt/2.0.2: - resolution: {integrity: sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=} + resolution: {integrity: sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==} dependencies: buffer-indexof: 1.1.1 dev: true @@ -6434,18 +13937,30 @@ packages: /dom-serializer/0.2.2: resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} dependencies: - domelementtype: 2.2.0 + domelementtype: 2.3.0 entities: 2.2.0 dev: true /dom-serializer/1.3.2: resolution: {integrity: sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==} dependencies: - domelementtype: 2.2.0 + domelementtype: 2.3.0 domhandler: 4.3.0 entities: 2.2.0 dev: true + /dom-serializer/2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.4.0 + dev: true + + /dom-walk/0.1.2: + resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} + dev: true + /domain-browser/1.2.0: resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==} engines: {node: '>=0.4', npm: '>=1.2'} @@ -6455,8 +13970,8 @@ packages: resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} dev: true - /domelementtype/2.2.0: - resolution: {integrity: sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==} + /domelementtype/2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} dev: true /domexception/1.0.1: @@ -6465,11 +13980,25 @@ packages: webidl-conversions: 4.0.2 dev: true + /domexception/2.0.1: + resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==} + engines: {node: '>=8'} + dependencies: + webidl-conversions: 5.0.0 + dev: true + /domhandler/4.3.0: resolution: {integrity: sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==} engines: {node: '>= 4'} dependencies: - domelementtype: 2.2.0 + domelementtype: 2.3.0 + dev: true + + /domhandler/5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 dev: true /domutils/1.7.0: @@ -6483,10 +14012,25 @@ packages: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} dependencies: dom-serializer: 1.3.2 - domelementtype: 2.2.0 + domelementtype: 2.3.0 domhandler: 4.3.0 dev: true + /domutils/3.0.1: + resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dev: true + + /dot-case/3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dependencies: + no-case: 3.0.4 + tslib: 2.4.0 + dev: true + /dot-prop/5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -6494,12 +14038,66 @@ packages: is-obj: 2.0.0 dev: true + /dotenv-defaults/1.1.1: + resolution: {integrity: sha512-6fPRo9o/3MxKvmRZBD3oNFdxODdhJtIy1zcJeUSCs6HCy4tarUpd+G67UTU9tF6OWXeSPqsm4fPAB+2eY9Rt9Q==} + dependencies: + dotenv: 6.2.0 + dev: true + + /dotenv-expand/5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dev: true + + /dotenv-webpack/1.8.0_webpack@4.46.0: + resolution: {integrity: sha512-o8pq6NLBehtrqA8Jv8jFQNtG9nhRtVqmoD4yWbgUyoU3+9WBlPe+c2EAiaJok9RB28QvrWvdWLZGeTT5aATDMg==} + peerDependencies: + webpack: ^1 || ^2 || ^3 || ^4 + dependencies: + dotenv-defaults: 1.1.1 + webpack: 4.46.0 + dev: true + + /dotenv/6.2.0: + resolution: {integrity: sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==} + engines: {node: '>=6'} + dev: true + + /dotenv/8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + dev: true + + /downshift/6.1.12: + resolution: {integrity: sha512-7XB/iaSJVS4T8wGFT3WRXmSF1UlBHAA40DshZtkrIscIN+VC+Lh363skLxFTvJwtNgHxAMDGEHT4xsyQFWL+UA==} + peerDependencies: + react: '>=16.12.0' + dependencies: + '@babel/runtime': 7.17.8 + compute-scroll-into-view: 1.0.17 + prop-types: 15.8.1 + react-is: 17.0.2 + tslib: 2.4.0 + dev: true + + /downshift/6.1.12_react@16.14.0: + resolution: {integrity: sha512-7XB/iaSJVS4T8wGFT3WRXmSF1UlBHAA40DshZtkrIscIN+VC+Lh363skLxFTvJwtNgHxAMDGEHT4xsyQFWL+UA==} + peerDependencies: + react: '>=16.12.0' + dependencies: + '@babel/runtime': 7.17.8 + compute-scroll-into-view: 1.0.17 + prop-types: 15.8.1 + react: 16.14.0 + react-is: 17.0.2 + tslib: 2.4.0 + dev: true + /duplexer/0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} dev: true /duplexer3/0.1.4: - resolution: {integrity: sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=} + resolution: {integrity: sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==} dev: true /duplexify/3.7.1: @@ -6516,7 +14114,7 @@ packages: dev: true /ecc-jsbn/0.1.2: - resolution: {integrity: sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=} + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} dependencies: jsbn: 0.1.1 safer-buffer: 2.1.2 @@ -6533,6 +14131,12 @@ packages: lodash: 4.17.21 dev: true + /ejs/2.7.4: + resolution: {integrity: sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + /ejs/3.1.6: resolution: {integrity: sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==} engines: {node: '>=0.10.0'} @@ -6541,10 +14145,20 @@ packages: jake: 10.8.2 dev: true + /electron-to-chromium/1.4.284: + resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} + dev: true + /electron-to-chromium/1.4.68: resolution: {integrity: sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==} dev: true + /element-resize-detector/1.2.4: + resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==} + dependencies: + batch-processor: 1.0.0 + dev: true + /elliptic/6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -6557,11 +14171,30 @@ packages: minimalistic-crypto-utils: 1.0.1 dev: true + /emittery/0.10.2: + resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} + engines: {node: '>=12'} + dev: true + /emittery/0.11.0: resolution: {integrity: sha512-S/7tzL6v5i+4iJd627Nhv9cLFIo5weAIlGccqJFpnBoDB8U1TF2k5tez4J/QNuxyyhWuFqHg1L84Kd3m7iXg6g==} engines: {node: '>=12'} dev: true + /emittery/0.7.2: + resolution: {integrity: sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==} + engines: {node: '>=10'} + dev: true + + /emittery/0.8.1: + resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} + engines: {node: '>=10'} + dev: true + + /emoji-regex/7.0.3: + resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} + dev: true + /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -6571,17 +14204,41 @@ packages: dev: true /emojis-list/2.1.0: - resolution: {integrity: sha1-TapNnbAPmBmIDHn6RXrlsJof04k=} + resolution: {integrity: sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==} engines: {node: '>= 0.10'} dev: true /emojis-list/3.0.0: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} + + /emotion-theming/10.3.0_@emotion+core@10.3.1: + resolution: {integrity: sha512-mXiD2Oj7N9b6+h/dC6oLf9hwxbtKHQjoIqtodEyL8CpkN4F3V4IK/BT4D0C7zSs4BBFOu4UlPJbvvBLa88SGEA==} + peerDependencies: + '@emotion/core': ^10.0.27 + react: '>=16.3.0' + dependencies: + '@babel/runtime': 7.17.8 + '@emotion/core': 10.3.1 + '@emotion/weak-memoize': 0.2.5 + hoist-non-react-statics: 3.3.2 + dev: true + + /emotion-theming/10.3.0_qzeatvug73zaio2r3dlvejynye: + resolution: {integrity: sha512-mXiD2Oj7N9b6+h/dC6oLf9hwxbtKHQjoIqtodEyL8CpkN4F3V4IK/BT4D0C7zSs4BBFOu4UlPJbvvBLa88SGEA==} + peerDependencies: + '@emotion/core': ^10.0.27 + react: '>=16.3.0' + dependencies: + '@babel/runtime': 7.17.8 + '@emotion/core': 10.3.1_react@16.14.0 + '@emotion/weak-memoize': 0.2.5 + hoist-non-react-statics: 3.3.2 + react: 16.14.0 dev: true /encodeurl/1.0.2: - resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=} + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} dev: true @@ -6601,7 +14258,7 @@ packages: resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==} engines: {node: '>=6.9.0'} dependencies: - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 memory-fs: 0.5.0 tapable: 1.1.3 dev: true @@ -6610,20 +14267,77 @@ packages: resolution: {integrity: sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==} engines: {node: '>=10.13.0'} dependencies: - graceful-fs: 4.2.8 + graceful-fs: 4.2.10 tapable: 2.2.0 dev: true + /enquirer/2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + dependencies: + ansi-colors: 4.1.1 + dev: true + /entities/2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} dev: true + /entities/4.4.0: + resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} + engines: {node: '>=0.12'} + dev: true + /envinfo/7.8.1: resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} engines: {node: '>=4'} hasBin: true dev: true + /enzyme-adapter-preact-pure/3.4.0_fh4cerfcdrs5uit63qwkqtrfyi: + resolution: {integrity: sha512-bP7HX8l5xoBG8d/nYZVIQgm2LFJWNHEl1+xfdQj02tYuveHg/C/iLtjzF/89LjgyTAhUEczGiWuXfn4/KhJeCw==} + peerDependencies: + enzyme: ^3.11.0 + preact: ^10.0.0 + dependencies: + array.prototype.flatmap: 1.2.5 + enzyme: 3.11.0 + preact: 10.6.5 + dev: true + + /enzyme-shallow-equal/1.0.4: + resolution: {integrity: sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==} + dependencies: + has: 1.0.3 + object-is: 1.1.5 + dev: true + + /enzyme/3.11.0: + resolution: {integrity: sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==} + dependencies: + array.prototype.flat: 1.2.5 + cheerio: 1.0.0-rc.12 + enzyme-shallow-equal: 1.0.4 + function.prototype.name: 1.1.5 + has: 1.0.3 + html-element-map: 1.3.1 + is-boolean-object: 1.1.2 + is-callable: 1.2.4 + is-number-object: 1.0.6 + is-regex: 1.1.4 + is-string: 1.0.7 + is-subset: 0.1.1 + lodash.escape: 4.0.1 + lodash.isequal: 4.5.0 + object-inspect: 1.12.0 + object-is: 1.1.5 + object.assign: 4.1.2 + object.entries: 1.1.5 + object.values: 1.1.5 + raf: 3.4.1 + rst-selector-parser: 2.2.3 + string.prototype.trim: 1.2.6 + dev: true + /errno/0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -6663,19 +14377,75 @@ packages: unbox-primitive: 1.0.1 dev: true + /es-abstract/1.20.4: + resolution: {integrity: sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.1.3 + get-symbol-description: 1.0.0 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-weakref: 1.0.2 + object-inspect: 1.12.2 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + safe-regex-test: 1.0.0 + string.prototype.trimend: 1.0.5 + string.prototype.trimstart: 1.0.5 + unbox-primitive: 1.0.2 + dev: true + + /es-array-method-boxes-properly/1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + dev: true + + /es-get-iterator/1.1.2: + resolution: {integrity: sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + has-symbols: 1.0.3 + is-arguments: 1.1.1 + is-map: 2.0.2 + is-set: 2.0.2 + is-string: 1.0.7 + isarray: 2.0.5 + dev: true + /es-to-primitive/1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} dependencies: - is-callable: 1.2.4 + is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 dev: true + /es5-shim/4.6.7: + resolution: {integrity: sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==} + engines: {node: '>=0.4.0'} + dev: true + /es6-error/4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} dev: true + /es6-shim/0.35.6: + resolution: {integrity: sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==} + dev: true + /esbuild-android-arm64/0.14.21: resolution: {integrity: sha512-Bqgld1TY0wZv8TqiQmVxQFgYzz8ZmyzT7clXBDZFkOOdRybzsnj8AZuK1pwcLVA7Ya6XncHgJqIao7NFd3s0RQ==} engines: {node: '>=12'} @@ -6891,7 +14661,7 @@ packages: dev: true /escape-html/1.0.3: - resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=} + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} dev: true /escape-string-regexp/1.0.5: @@ -6927,6 +14697,19 @@ packages: source-map: 0.6.1 dev: true + /escodegen/2.0.0: + resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + /eslint-config-airbnb-base/15.0.0_hexytdhmo422l55jsqymxqfu6q: resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} engines: {node: ^10.12.0 || >=12.0.0} @@ -6956,6 +14739,50 @@ packages: - eslint-plugin-import dev: true + /eslint-config-preact/1.3.0_nxlzr75jbqkso2fds5zjovs2ii: + resolution: {integrity: sha512-yHYXg5qNzEJd3D/30AmsIW0W8MuY858KpApXp7xxBF08IYUljSKCOqMx+dVucXHQnAm7+11wOnMkgVHIBAechw==} + peerDependencies: + eslint: 6.x || 7.x || 8.x + dependencies: + '@babel/core': 7.17.2 + '@babel/eslint-parser': 7.19.1_ifghgpypvdmamphfg2ieta34qe + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.17.2 + '@babel/plugin-syntax-decorators': 7.17.0_@babel+core@7.17.2 + '@babel/plugin-syntax-jsx': 7.16.7_@babel+core@7.17.2 + eslint: 7.32.0 + eslint-plugin-compat: 4.0.2_eslint@7.32.0 + eslint-plugin-jest: 25.7.0_nxlzr75jbqkso2fds5zjovs2ii + eslint-plugin-react: 7.28.0_eslint@7.32.0 + eslint-plugin-react-hooks: 4.3.0_eslint@7.32.0 + transitivePeerDependencies: + - '@typescript-eslint/eslint-plugin' + - jest + - supports-color + - typescript + dev: true + + /eslint-config-preact/1.3.0_w4k36q7phb5aratcwbohw6kmxe: + resolution: {integrity: sha512-yHYXg5qNzEJd3D/30AmsIW0W8MuY858KpApXp7xxBF08IYUljSKCOqMx+dVucXHQnAm7+11wOnMkgVHIBAechw==} + peerDependencies: + eslint: 6.x || 7.x || 8.x + dependencies: + '@babel/core': 7.17.2 + '@babel/eslint-parser': 7.19.1_rakzipanemow5i3hc6etgvncsm + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.17.2 + '@babel/plugin-syntax-decorators': 7.17.0_@babel+core@7.17.2 + '@babel/plugin-syntax-jsx': 7.16.7_@babel+core@7.17.2 + eslint: 8.8.0 + eslint-plugin-compat: 4.0.2_eslint@8.8.0 + eslint-plugin-jest: 25.7.0_w4k36q7phb5aratcwbohw6kmxe + eslint-plugin-react: 7.28.0_eslint@8.8.0 + eslint-plugin-react-hooks: 4.3.0_eslint@8.8.0 + transitivePeerDependencies: + - '@typescript-eslint/eslint-plugin' + - jest + - supports-color + - typescript + dev: true + /eslint-import-resolver-node/0.3.6: resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} dependencies: @@ -6991,12 +14818,54 @@ packages: - supports-color dev: true + /eslint-plugin-compat/4.0.2_eslint@7.32.0: + resolution: {integrity: sha512-xqvoO54CLTVaEYGMzhu35Wzwk/As7rCvz/2dqwnFiWi0OJccEtGIn+5qq3zqIu9nboXlpdBN579fZcItC73Ycg==} + engines: {node: '>=9.x'} + peerDependencies: + eslint: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@mdn/browser-compat-data': 4.2.1 + ast-metadata-inferer: 0.7.0 + browserslist: 4.19.1 + caniuse-lite: 1.0.30001311 + core-js: 3.26.0 + eslint: 7.32.0 + find-up: 5.0.0 + lodash.memoize: 4.1.2 + semver: 7.3.5 + dev: true + + /eslint-plugin-compat/4.0.2_eslint@8.8.0: + resolution: {integrity: sha512-xqvoO54CLTVaEYGMzhu35Wzwk/As7rCvz/2dqwnFiWi0OJccEtGIn+5qq3zqIu9nboXlpdBN579fZcItC73Ycg==} + engines: {node: '>=9.x'} + peerDependencies: + eslint: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@mdn/browser-compat-data': 4.2.1 + ast-metadata-inferer: 0.7.0 + browserslist: 4.19.1 + caniuse-lite: 1.0.30001311 + core-js: 3.26.0 + eslint: 8.8.0 + find-up: 5.0.0 + lodash.memoize: 4.1.2 + semver: 7.3.5 + dev: true + /eslint-plugin-header/3.1.1: resolution: {integrity: sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==} peerDependencies: eslint: '>=7.7.0' dev: true + /eslint-plugin-header/3.1.1_eslint@7.32.0: + resolution: {integrity: sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==} + peerDependencies: + eslint: '>=7.7.0' + dependencies: + eslint: 7.32.0 + dev: true + /eslint-plugin-import/2.25.4_r2xatzqf7unmty2b7kgqnez6uu: resolution: {integrity: sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==} engines: {node: '>=4'} @@ -7028,6 +14897,50 @@ packages: - supports-color dev: true + /eslint-plugin-jest/25.7.0_nxlzr75jbqkso2fds5zjovs2ii: + resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^4.0.0 || ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 4.33.0_k4l66av2tbo6kxzw52jzgbfzii + '@typescript-eslint/experimental-utils': 5.40.1_3rubbgt5ekhqrcgx4uwls3neim + eslint: 7.32.0 + jest: 26.6.3 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-jest/25.7.0_w4k36q7phb5aratcwbohw6kmxe: + resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^4.0.0 || ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 5.36.1_gjcw3hhr2cxnngiu5lw4bi633m + '@typescript-eslint/experimental-utils': 5.40.1_o2nrgn6wwxunlqlzzokx4es3q4 + eslint: 8.8.0 + jest: 27.5.1 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /eslint-plugin-jsx-a11y/6.5.1_eslint@8.8.0: resolution: {integrity: sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==} engines: {node: '>=4.0'} @@ -7049,6 +14962,15 @@ packages: minimatch: 3.0.4 dev: true + /eslint-plugin-react-hooks/4.3.0_eslint@7.32.0: + resolution: {integrity: sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 7.32.0 + dev: true + /eslint-plugin-react-hooks/4.3.0_eslint@8.8.0: resolution: {integrity: sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==} engines: {node: '>=10'} @@ -7058,6 +14980,29 @@ packages: eslint: 8.8.0 dev: true + /eslint-plugin-react/7.28.0_eslint@7.32.0: + resolution: {integrity: sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.4 + array.prototype.flatmap: 1.2.5 + doctrine: 2.1.0 + eslint: 7.32.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.2.1 + minimatch: 3.0.5 + object.entries: 1.1.5 + object.fromentries: 2.0.5 + object.hasown: 1.1.0 + object.values: 1.1.5 + prop-types: 15.8.1 + resolve: 2.0.0-next.3 + semver: 6.3.0 + string.prototype.matchall: 4.0.6 + dev: true + /eslint-plugin-react/7.28.0_eslint@8.8.0: resolution: {integrity: sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==} engines: {node: '>=4'} @@ -7105,6 +15050,23 @@ packages: estraverse: 5.3.0 dev: true + /eslint-utils/2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@7.32.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 7.32.0 + eslint-visitor-keys: 2.1.0 + dev: true + /eslint-utils/3.0.0_eslint@8.8.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} @@ -7115,6 +15077,11 @@ packages: eslint-visitor-keys: 2.1.0 dev: true + /eslint-visitor-keys/1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + /eslint-visitor-keys/2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} @@ -7130,6 +15097,55 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /eslint/7.32.0: + resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} + engines: {node: ^10.12.0 || >=12.0.0} + hasBin: true + dependencies: + '@babel/code-frame': 7.12.11 + '@eslint/eslintrc': 0.4.3 + '@humanwhocodes/config-array': 0.5.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + enquirer: 2.3.6 + escape-string-regexp: 4.0.0 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 2.1.0 + espree: 7.3.1 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 5.1.2 + globals: 13.12.1 + ignore: 4.0.6 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 3.14.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.3.8 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + table: 6.8.0 + text-table: 0.2.0 + v8-compile-cache: 2.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /eslint/8.8.0: resolution: {integrity: sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7179,6 +15195,15 @@ packages: engines: {node: '>=6'} dev: true + /espree/7.3.1: + resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + acorn: 7.4.1 + acorn-jsx: 5.3.2_acorn@7.4.1 + eslint-visitor-keys: 1.3.0 + dev: true + /espree/9.3.0: resolution: {integrity: sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7218,6 +15243,10 @@ packages: engines: {node: '>=4.0'} dev: true + /estree-walker/0.6.1: + resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} + dev: true + /estree-walker/1.0.1: resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} dev: true @@ -7232,7 +15261,7 @@ packages: dev: true /etag/1.8.1: - resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=} + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} dev: true @@ -7245,6 +15274,11 @@ packages: engines: {node: '>=0.8.x'} dev: true + /eventsource/2.0.2: + resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} + engines: {node: '>=12.0.0'} + dev: true + /evp_bytestokey/1.0.3: resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} dependencies: @@ -7252,6 +15286,38 @@ packages: safe-buffer: 5.2.1 dev: true + /exec-sh/0.3.6: + resolution: {integrity: sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==} + dev: true + + /execa/1.0.0: + resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} + engines: {node: '>=6'} + dependencies: + cross-spawn: 6.0.5 + get-stream: 4.1.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + dev: true + + /execa/4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + /execa/5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -7267,8 +15333,13 @@ packages: strip-final-newline: 2.0.0 dev: true + /exit/0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + /expand-brackets/2.1.4: - resolution: {integrity: sha1-t3c14xXOMPa27/D4OwQVGiJEliI=} + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} engines: {node: '>=0.10.0'} dependencies: debug: 2.6.9 @@ -7282,6 +15353,43 @@ packages: - supports-color dev: true + /expand-brackets/2.1.4_supports-color@6.1.0: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + dependencies: + debug: 2.6.9_supports-color@6.1.0 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2_supports-color@6.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /expect/26.6.2: + resolution: {integrity: sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + ansi-styles: 4.3.0 + jest-get-type: 26.3.0 + jest-matcher-utils: 26.6.2 + jest-message-util: 26.6.2 + jest-regex-util: 26.0.0 + dev: true + + /expect/27.5.1: + resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + jest-get-type: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + dev: true + /express/4.17.2: resolution: {integrity: sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==} engines: {node: '>= 0.10.0'} @@ -7320,15 +15428,53 @@ packages: - supports-color dev: true + /express/4.17.2_supports-color@6.1.0: + resolution: {integrity: sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.19.1_supports-color@6.1.0 + content-disposition: 0.5.4 + content-type: 1.0.4 + cookie: 0.4.1 + cookie-signature: 1.0.6 + debug: 2.6.9_supports-color@6.1.0 + depd: 1.1.2 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.1.2_supports-color@6.1.0 + fresh: 0.5.2 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.3.0 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.9.6 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.17.2_supports-color@6.1.0 + serve-static: 1.14.2_supports-color@6.1.0 + setprototypeof: 1.2.0 + statuses: 1.5.0 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /extend-shallow/2.0.1: - resolution: {integrity: sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=} + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} dependencies: is-extendable: 0.1.1 dev: true /extend-shallow/3.0.2: - resolution: {integrity: sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=} + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} engines: {node: '>=0.10.0'} dependencies: assign-symbols: 1.0.0 @@ -7355,19 +15501,55 @@ packages: - supports-color dev: true + /extglob/2.0.4_supports-color@6.1.0: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4_supports-color@6.1.0 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2_supports-color@6.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /extsprintf/1.3.0: - resolution: {integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=} + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} dev: true + /fast-async/6.3.8: + resolution: {integrity: sha512-TjlooyqrYm/gOXjD2UHNwfrWkvTbzU105Nk4bvcRTeRoL+wIeK6rqbqDg3CN9z5p37cE2iXhP6SxQFz8OVIaUg==} + dependencies: + nodent-compiler: 3.2.13 + nodent-runtime: 3.2.1 + dev: true + /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true /fast-diff/1.2.0: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} dev: true + /fast-glob/2.2.7: + resolution: {integrity: sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==} + engines: {node: '>=4.0.0'} + dependencies: + '@mrmlnc/readdir-enhanced': 2.2.1 + '@nodelib/fs.stat': 1.1.3 + glob-parent: 3.1.0 + is-glob: 4.0.3 + merge2: 1.4.1 + micromatch: 3.1.10 + transitivePeerDependencies: + - supports-color + dev: true + /fast-glob/3.2.11: resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} engines: {node: '>=8.6.0'} @@ -7376,7 +15558,7 @@ packages: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.4 + micromatch: 4.0.5 dev: true /fast-glob/3.2.12: @@ -7392,7 +15574,6 @@ packages: /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true /fast-levenshtein/2.0.6: resolution: {integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=} @@ -7404,6 +15585,12 @@ packages: reusify: 1.0.4 dev: true + /fault/1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + dependencies: + format: 0.2.2 + dev: true + /faye-websocket/0.11.4: resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} engines: {node: '>=0.8.0'} @@ -7411,6 +15598,12 @@ packages: websocket-driver: 0.7.4 dev: true + /fb-watchman/2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + /fetch-blob/3.1.4: resolution: {integrity: sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==} engines: {node: ^12.20 || >= 14.13} @@ -7427,6 +15620,10 @@ packages: - encoding dev: false + /fetch-retry/5.0.3: + resolution: {integrity: sha512-uJQyMrX5IJZkhoEUBQ3EjxkeiZkppBd5jS/fMTJmfZxLSiaQjv2zD0kTvuvkSH89uFvgSlB6ueGpjD3HWN7Bxw==} + dev: true + /fflate/0.7.4: resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} dev: false @@ -7450,6 +15647,15 @@ packages: flat-cache: 3.0.4 dev: true + /file-loader/1.1.11: + resolution: {integrity: sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==} + engines: {node: '>= 4.3 < 5.0.0 || >= 5.10'} + peerDependencies: + webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 + dependencies: + loader-utils: 1.4.0 + schema-utils: 0.4.7 + /file-loader/6.2.0_webpack@4.46.0: resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} @@ -7461,6 +15667,13 @@ packages: webpack: 4.46.0 dev: true + /file-system-cache/1.1.0: + resolution: {integrity: sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw==} + dependencies: + fs-extra: 10.1.0 + ramda: 0.28.0 + dev: true + /file-uri-to-path/1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} requiresBuild: true @@ -7470,7 +15683,17 @@ packages: /filelist/1.0.2: resolution: {integrity: sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==} dependencies: - minimatch: 3.0.5 + minimatch: 3.1.2 + dev: true + + /filesize/3.6.1: + resolution: {integrity: sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==} + engines: {node: '>= 0.4.0'} + dev: true + + /filesize/6.1.0: + resolution: {integrity: sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==} + engines: {node: '>= 0.4.0'} dev: true /fill-range/4.0.0: @@ -7505,6 +15728,21 @@ packages: - supports-color dev: true + /finalhandler/1.1.2_supports-color@6.1.0: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9_supports-color@6.1.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /find-cache-dir/2.1.0: resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} engines: {node: '>=6'} @@ -7523,6 +15761,19 @@ packages: pkg-dir: 4.2.0 dev: true + /find-root/1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + dev: true + + /find-up/1.1.2: + resolution: {integrity: sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==} + engines: {node: '>=0.10.0'} + dependencies: + path-exists: 2.1.0 + pinkie-promise: 2.0.1 + dev: true + optional: true + /find-up/2.1.0: resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=} engines: {node: '>=4'} @@ -7594,16 +15845,6 @@ packages: readable-stream: 2.3.7 dev: true - /follow-redirects/1.14.8: - resolution: {integrity: sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: true - /follow-redirects/1.15.2: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} @@ -7612,16 +15853,15 @@ packages: peerDependenciesMeta: debug: optional: true - dev: false /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: - is-callable: 1.2.4 + is-callable: 1.2.7 dev: true /for-in/1.0.2: - resolution: {integrity: sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=} + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} engines: {node: '>=0.10.0'} dev: true @@ -7634,7 +15874,63 @@ packages: dev: true /forever-agent/0.6.1: - resolution: {integrity: sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=} + resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + dev: true + + /fork-ts-checker-webpack-plugin/4.1.6_3awoqomffoooecyduzzbrfpye4: + resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==} + engines: {node: '>=6.11.5', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + dependencies: + '@babel/code-frame': 7.16.7 + chalk: 2.4.2 + eslint: 8.8.0 + micromatch: 3.1.10 + minimatch: 3.1.2 + semver: 5.7.1 + tapable: 1.1.3 + typescript: 3.9.10 + webpack: 4.46.0 + worker-rpc: 0.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /fork-ts-checker-webpack-plugin/4.1.6_3n2x3j6farblcaf52bherr6og4: + resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==} + engines: {node: '>=6.11.5', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + dependencies: + '@babel/code-frame': 7.16.7 + chalk: 2.4.2 + eslint: 7.32.0 + micromatch: 3.1.10 + minimatch: 3.1.2 + semver: 5.7.1 + tapable: 1.1.3 + typescript: 4.8.4 + webpack: 4.46.0 + worker-rpc: 0.1.1 + transitivePeerDependencies: + - supports-color dev: true /fork-ts-checker-webpack-plugin/4.1.6_e7hrjdrs22zc4syxbltzlwluhe: @@ -7654,7 +15950,7 @@ packages: '@babel/code-frame': 7.16.7 chalk: 2.4.2 micromatch: 3.1.10 - minimatch: 3.0.5 + minimatch: 3.1.2 semver: 5.7.1 tapable: 1.1.3 typescript: 4.2.4 @@ -7664,13 +15960,142 @@ packages: - supports-color dev: true + /fork-ts-checker-webpack-plugin/4.1.6_ef2lra3u3fsnrdrpybbvbgzate: + resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==} + engines: {node: '>=6.11.5', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + dependencies: + '@babel/code-frame': 7.16.7 + chalk: 2.4.2 + eslint: 8.8.0 + micromatch: 3.1.10 + minimatch: 3.1.2 + semver: 5.7.1 + tapable: 1.1.3 + typescript: 4.8.4 + webpack: 4.46.0 + worker-rpc: 0.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /fork-ts-checker-webpack-plugin/4.1.6_whfidqbq6inl26rhdbd2ot7yoa: + resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==} + engines: {node: '>=6.11.5', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + dependencies: + '@babel/code-frame': 7.16.7 + chalk: 2.4.2 + eslint: 7.32.0 + micromatch: 3.1.10 + minimatch: 3.1.2 + semver: 5.7.1 + tapable: 1.1.3 + typescript: 4.2.4 + webpack: 4.46.0 + worker-rpc: 0.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /fork-ts-checker-webpack-plugin/6.5.2_3n2x3j6farblcaf52bherr6og4: + resolution: {integrity: sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==} + engines: {node: '>=10', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + dependencies: + '@babel/code-frame': 7.16.7 + '@types/json-schema': 7.0.11 + chalk: 4.1.2 + chokidar: 3.5.3 + cosmiconfig: 6.0.0 + deepmerge: 4.2.2 + eslint: 7.32.0 + fs-extra: 9.1.0 + glob: 7.2.3 + memfs: 3.4.1 + minimatch: 3.1.2 + schema-utils: 2.7.0 + semver: 7.3.8 + tapable: 1.1.3 + typescript: 4.8.4 + webpack: 4.46.0 + dev: true + + /fork-ts-checker-webpack-plugin/6.5.2_ef2lra3u3fsnrdrpybbvbgzate: + resolution: {integrity: sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==} + engines: {node: '>=10', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + dependencies: + '@babel/code-frame': 7.16.7 + '@types/json-schema': 7.0.11 + chalk: 4.1.2 + chokidar: 3.5.3 + cosmiconfig: 6.0.0 + deepmerge: 4.2.2 + eslint: 8.8.0 + fs-extra: 9.1.0 + glob: 7.2.3 + memfs: 3.4.1 + minimatch: 3.1.2 + schema-utils: 2.7.0 + semver: 7.3.8 + tapable: 1.1.3 + typescript: 4.8.4 + webpack: 4.46.0 + dev: true + /form-data/2.3.3: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} engines: {node: '>= 0.12'} dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 - mime-types: 2.1.34 + mime-types: 2.1.35 + dev: true + + /form-data/3.0.1: + resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 dev: true /form-data/4.0.0: @@ -7682,6 +16107,11 @@ packages: mime-types: 2.1.35 dev: false + /format/0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + dev: true + /formdata-polyfill/4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -7699,7 +16129,7 @@ packages: dev: true /fragment-cache/0.2.1: - resolution: {integrity: sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=} + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} engines: {node: '>=0.10.0'} dependencies: map-cache: 0.2.2 @@ -7711,7 +16141,7 @@ packages: dev: true /from2/2.3.0: - resolution: {integrity: sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=} + resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} dependencies: inherits: 2.0.4 readable-stream: 2.3.7 @@ -7721,10 +16151,28 @@ packages: resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} dev: true + /fs-extra/10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + /fs-extra/4.0.3: resolution: {integrity: sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==} dependencies: - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + + /fs-extra/8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.10 jsonfile: 4.0.0 universalify: 0.1.2 dev: true @@ -7734,7 +16182,7 @@ packages: engines: {node: '>=10'} dependencies: at-least-node: 1.0.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jsonfile: 6.1.0 universalify: 2.0.0 dev: true @@ -7757,7 +16205,7 @@ packages: dev: true /fs-write-stream-atomic/1.0.10: - resolution: {integrity: sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=} + resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==} dependencies: graceful-fs: 4.2.10 iferr: 0.1.5 @@ -7792,10 +16240,57 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true + /function.prototype.name/1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.19.1 + functions-have-names: 1.2.3 + dev: true + /functional-red-black-tree/1.0.1: resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} dev: true + /functions-have-names/1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /fuse.js/3.6.1: + resolution: {integrity: sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==} + engines: {node: '>=6'} + dev: true + + /gauge/2.7.4: + resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==} + dependencies: + aproba: 1.2.0 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 1.0.2 + strip-ansi: 3.0.1 + wide-align: 1.1.5 + dev: true + + /gauge/3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + dependencies: + aproba: 1.2.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: true + /gensync/1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -7814,7 +16309,15 @@ packages: dependencies: function-bind: 1.1.1 has: 1.0.3 - has-symbols: 1.0.2 + has-symbols: 1.0.3 + dev: true + + /get-intrinsic/1.1.3: + resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 dev: true /get-own-enumerable-property-symbols/3.0.2: @@ -7826,11 +16329,22 @@ packages: engines: {node: '>=8.0.0'} dev: true + /get-port/3.2.0: + resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==} + engines: {node: '>=4'} + dev: true + /get-port/5.1.1: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} dev: true + /get-stdin/4.0.1: + resolution: {integrity: sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==} + engines: {node: '>=0.10.0'} + dev: true + optional: true + /get-stream/4.1.0: resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} engines: {node: '>=6'} @@ -7855,16 +16369,16 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - get-intrinsic: 1.1.1 + get-intrinsic: 1.1.3 dev: true /get-value/2.0.6: - resolution: {integrity: sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=} + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} dev: true /getpass/0.1.7: - resolution: {integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=} + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} dependencies: assert-plus: 1.0.0 dev: true @@ -7875,21 +16389,38 @@ packages: encoding: 0.1.13 dev: true + /github-slugger/1.4.0: + resolution: {integrity: sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==} + dev: true + /gittar/0.1.1: - resolution: {integrity: sha1-1pk+phYKhsi3895yKmH3O8meFLQ=} + resolution: {integrity: sha512-p+XuqWJpW9ahUuNTptqeFjudFq31o6Jd+maMBarkMAR5U3K9c7zJB4sQ4BV8mIqrTOV29TtqikDhnZfCD4XNfQ==} engines: {node: '>=4'} dependencies: mkdirp: 0.5.5 tar: 4.4.19 dev: true + /glob-base/0.3.0: + resolution: {integrity: sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==} + engines: {node: '>=0.10.0'} + dependencies: + glob-parent: 2.0.0 + is-glob: 2.0.1 + dev: true + + /glob-parent/2.0.0: + resolution: {integrity: sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==} + dependencies: + is-glob: 2.0.1 + dev: true + /glob-parent/3.1.0: resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} dependencies: is-glob: 3.1.0 path-dirname: 1.0.2 dev: true - optional: true /glob-parent/5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -7905,6 +16436,24 @@ packages: is-glob: 4.0.3 dev: true + /glob-promise/3.4.0_glob@7.2.3: + resolution: {integrity: sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw==} + engines: {node: '>=4'} + peerDependencies: + glob: '*' + dependencies: + '@types/glob': 8.0.0 + glob: 7.2.3 + dev: true + + /glob-to-regexp/0.3.0: + resolution: {integrity: sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==} + dev: true + + /glob-to-regexp/0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: true + /glob/7.2.0: resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} dependencies: @@ -7926,6 +16475,13 @@ packages: path-is-absolute: 1.0.1 dev: true + /global-dirs/2.1.0: + resolution: {integrity: sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==} + engines: {node: '>=8'} + dependencies: + ini: 1.3.7 + dev: true + /global-dirs/3.0.0: resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==} engines: {node: '>=10'} @@ -7933,6 +16489,29 @@ packages: ini: 2.0.0 dev: true + /global-modules/2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + dependencies: + global-prefix: 3.0.0 + dev: true + + /global-prefix/3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + dev: true + + /global/4.4.0: + resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} + dependencies: + min-document: 2.19.0 + process: 0.11.10 + dev: true + /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -7945,6 +16524,25 @@ packages: type-fest: 0.20.2 dev: true + /globalthis/1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.1.4 + dev: true + + /globby/11.0.1: + resolution: {integrity: sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -7968,6 +16566,45 @@ packages: slash: 4.0.0 dev: true + /globby/6.1.0: + resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} + engines: {node: '>=0.10.0'} + dependencies: + array-union: 1.0.2 + glob: 7.2.3 + object-assign: 4.1.1 + pify: 2.3.0 + pinkie-promise: 2.0.1 + dev: true + + /globby/7.1.1: + resolution: {integrity: sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==} + engines: {node: '>=4'} + dependencies: + array-union: 1.0.2 + dir-glob: 2.2.2 + glob: 7.2.3 + ignore: 3.3.10 + pify: 3.0.0 + slash: 1.0.0 + dev: true + + /globby/9.2.0: + resolution: {integrity: sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==} + engines: {node: '>=6'} + dependencies: + '@types/glob': 7.2.0 + array-union: 1.0.2 + dir-glob: 2.2.2 + fast-glob: 2.2.7 + glob: 7.2.3 + ignore: 4.0.6 + pify: 4.0.1 + slash: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /got/9.6.0: resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} engines: {node: '>=8.6'} @@ -7991,10 +16628,6 @@ packages: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true - /graceful-fs/4.2.8: - resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==} - dev: true - /graceful-fs/4.2.9: resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==} dev: true @@ -8004,6 +16637,23 @@ packages: engines: {node: '>=4.x'} dev: true + /growly/1.3.0: + resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==} + dev: true + optional: true + + /gud/1.0.0: + resolution: {integrity: sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==} + dev: true + + /gzip-size/5.1.1: + resolution: {integrity: sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==} + engines: {node: '>=6'} + dependencies: + duplexer: 0.1.2 + pify: 4.0.1 + dev: true + /gzip-size/6.0.0: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} @@ -8015,8 +16665,21 @@ packages: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} dev: true + /handlebars/4.7.7: + resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} + engines: {node: '>=0.4.7'} + hasBin: true + dependencies: + minimist: 1.2.5 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.4.10 + dev: true + /har-schema/2.0.0: - resolution: {integrity: sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=} + resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} dev: true @@ -8029,10 +16692,18 @@ packages: har-schema: 2.0.0 dev: true + /harmony-reflect/1.6.2: + resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} + dev: true + /has-bigints/1.0.1: resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==} dev: true + /has-bigints/1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + /has-color/0.1.7: resolution: {integrity: sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=} engines: {node: '>=0.10.0'} @@ -8048,11 +16719,29 @@ packages: engines: {node: '>=8'} dev: true + /has-glob/1.0.0: + resolution: {integrity: sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g==} + engines: {node: '>=0.10.0'} + dependencies: + is-glob: 3.1.0 + dev: true + + /has-property-descriptors/1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.1.3 + dev: true + /has-symbols/1.0.2: resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==} engines: {node: '>= 0.4'} dev: true + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + /has-tostringtag/1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} @@ -8060,8 +16749,12 @@ packages: has-symbols: 1.0.2 dev: true + /has-unicode/2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + dev: true + /has-value/0.3.1: - resolution: {integrity: sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=} + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} engines: {node: '>=0.10.0'} dependencies: get-value: 2.0.6 @@ -8070,7 +16763,7 @@ packages: dev: true /has-value/1.0.0: - resolution: {integrity: sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=} + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} engines: {node: '>=0.10.0'} dependencies: get-value: 2.0.6 @@ -8079,12 +16772,12 @@ packages: dev: true /has-values/0.1.4: - resolution: {integrity: sha1-bWHeldkd/Km5oCCJrThL/49it3E=} + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} engines: {node: '>=0.10.0'} dev: true /has-values/1.0.0: - resolution: {integrity: sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=} + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} engines: {node: '>=0.10.0'} dependencies: is-number: 3.0.0 @@ -8123,6 +16816,13 @@ packages: minimalistic-assert: 1.0.1 dev: true + /hasha/4.0.1: + resolution: {integrity: sha512-+wnvroCn3pq0CAKWfItGPyl0DJOob2qs/2D/Rh0a/O90LBzmo5GaKHwIRb6FInVvmEl1mCIHL5RqlfTLvh6FoQ==} + engines: {node: '>=8'} + dependencies: + is-stream: 1.1.0 + dev: true + /hasha/5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} engines: {node: '>=8'} @@ -8131,6 +16831,68 @@ packages: type-fest: 0.8.1 dev: true + /hast-to-hyperscript/9.0.1: + resolution: {integrity: sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==} + dependencies: + '@types/unist': 2.0.6 + comma-separated-tokens: 1.0.8 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + style-to-object: 0.3.0 + unist-util-is: 4.1.0 + web-namespaces: 1.1.4 + dev: true + + /hast-util-from-parse5/6.0.1: + resolution: {integrity: sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==} + dependencies: + '@types/parse5': 5.0.3 + hastscript: 6.0.0 + property-information: 5.6.0 + vfile: 4.2.1 + vfile-location: 3.2.0 + web-namespaces: 1.1.4 + dev: true + + /hast-util-parse-selector/2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + dev: true + + /hast-util-raw/6.0.1: + resolution: {integrity: sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==} + dependencies: + '@types/hast': 2.3.4 + hast-util-from-parse5: 6.0.1 + hast-util-to-parse5: 6.0.0 + html-void-elements: 1.0.5 + parse5: 6.0.1 + unist-util-position: 3.1.0 + vfile: 4.2.1 + web-namespaces: 1.1.4 + xtend: 4.0.2 + zwitch: 1.0.5 + dev: true + + /hast-util-to-parse5/6.0.0: + resolution: {integrity: sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==} + dependencies: + hast-to-hyperscript: 9.0.1 + property-information: 5.6.0 + web-namespaces: 1.1.4 + xtend: 4.0.2 + zwitch: 1.0.5 + dev: true + + /hastscript/6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + dependencies: + '@types/hast': 2.3.4 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + dev: true + /he/1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true @@ -8140,6 +16902,10 @@ packages: resolution: {integrity: sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==} dev: true + /highlight.js/10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + dev: true + /history/4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} dependencies: @@ -8152,15 +16918,30 @@ packages: dev: false /hmac-drbg/1.0.1: - resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=} + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} dependencies: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 dev: true + /hoist-non-react-statics/3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + dev: true + + /hoopy/0.1.4: + resolution: {integrity: sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==} + engines: {node: '>= 6.0.0'} + dev: true + + /hosted-git-info/2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + /hpack.js/2.1.6: - resolution: {integrity: sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=} + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} dependencies: inherits: 2.0.4 obuf: 1.1.2 @@ -8169,11 +16950,18 @@ packages: dev: true /hsl-regex/1.0.0: - resolution: {integrity: sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=} + resolution: {integrity: sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==} dev: true /hsla-regex/1.0.0: - resolution: {integrity: sha1-wc56MWjIxmFAM6S194d/OyJfnDg=} + resolution: {integrity: sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==} + dev: true + + /html-element-map/1.3.1: + resolution: {integrity: sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==} + dependencies: + array.prototype.filter: 1.0.1 + call-bind: 1.0.2 dev: true /html-encoding-sniffer/1.0.2: @@ -8182,6 +16970,17 @@ packages: whatwg-encoding: 1.0.5 dev: true + /html-encoding-sniffer/2.0.1: + resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==} + engines: {node: '>=10'} + dependencies: + whatwg-encoding: 1.0.5 + dev: true + + /html-entities/1.4.0: + resolution: {integrity: sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==} + dev: true + /html-entities/2.3.2: resolution: {integrity: sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==} dev: true @@ -8190,6 +16989,20 @@ packages: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true + /html-minifier-terser/5.1.1: + resolution: {integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==} + engines: {node: '>=6'} + hasBin: true + dependencies: + camel-case: 4.1.2 + clean-css: 4.2.4 + commander: 4.1.1 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 4.8.0 + dev: true + /html-minifier/3.5.21: resolution: {integrity: sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==} engines: {node: '>=4'} @@ -8204,13 +17017,37 @@ packages: uglify-js: 3.4.10 dev: true + /html-tags/3.2.0: + resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==} + engines: {node: '>=8'} + dev: true + + /html-void-elements/1.0.5: + resolution: {integrity: sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==} + dev: true + /html-webpack-exclude-assets-plugin/0.0.7: resolution: {integrity: sha512-gaYKMGBPDts3Fb1WXyDEEcS/0TSRg2IDl3EsbQL2AkKWTqdjSKwfQ8Iz0RhPiWErJfqhq5/wkhoYyjQoG55pug==} engines: {node: '>=4.0.0'} dev: true + /html-webpack-inline-chunk-plugin/1.1.1: + resolution: {integrity: sha512-0cor73re8PI/BG2W+jCkZxob8duoLG1COQLFZ3cT8G1VBzyKE7wQRpLkl79BjJw3epntnVg17n2z7Y6McJijaA==} + dependencies: + lodash: 4.17.21 + source-map-url: 0.4.1 + dev: true + + /html-webpack-inline-source-plugin/0.0.10: + resolution: {integrity: sha512-0ZNU57u7283vrXSF5a4VDnVOMWiSwypKIp1z/XfXWoVHLA1r3Xmyxx5+Lz+mnthz/UvxL1OAf41w5UIF68Jngw==} + dependencies: + escape-string-regexp: 1.0.5 + slash: 1.0.0 + source-map-url: 0.4.1 + dev: true + /html-webpack-plugin/3.2.0_webpack@4.46.0: - resolution: {integrity: sha1-sBq71yOsqqeze2r0SS69oD2d03s=} + resolution: {integrity: sha512-Br4ifmjQojUP4EmHnRBoUIYcZ9J7M4bTMcm7u6xoIAIuq2Nte4TzXX0533owvkQKQD1WeMTTTyD4Ni4QKxS0Bg==} engines: {node: '>=6.9'} deprecated: 3.x is no longer supported peerDependencies: @@ -8226,25 +17063,61 @@ packages: webpack: 4.46.0 dev: true + /html-webpack-plugin/4.5.2_webpack@4.46.0: + resolution: {integrity: sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==} + engines: {node: '>=6.9'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + '@types/html-minifier-terser': 5.1.2 + '@types/tapable': 1.0.8 + '@types/webpack': 4.41.32 + html-minifier-terser: 5.1.1 + loader-utils: 1.4.0 + lodash: 4.17.21 + pretty-error: 2.1.2 + tapable: 1.1.3 + util.promisify: 1.0.0 + webpack: 4.46.0 + dev: true + + /html-webpack-skip-assets-plugin/1.0.3: + resolution: {integrity: sha512-vpdh+JZGlE1Df3IftH2gw5P7b6yfTsahcOIJnkkkj5iJU9dUStXgzgALoXWwl8+17wWgFm3edcJzeYTJBYfMAw==} + peerDependencies: + html-webpack-plugin: '>=3.0.0' + webpack: '>=3.0.0' + dependencies: + minimatch: 3.0.4 + dev: true + /htmlparser2/6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} dependencies: - domelementtype: 2.2.0 + domelementtype: 2.3.0 domhandler: 4.3.0 domutils: 2.8.0 entities: 2.2.0 dev: true + /htmlparser2/8.0.1: + resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + entities: 4.4.0 + dev: true + /http-cache-semantics/4.1.0: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} dev: true /http-deceiver/1.2.7: - resolution: {integrity: sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=} + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} dev: true /http-errors/1.6.3: - resolution: {integrity: sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=} + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} engines: {node: '>= 0.6'} dependencies: depd: 1.1.2 @@ -8268,6 +17141,30 @@ packages: resolution: {integrity: sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==} dev: true + /http-proxy-agent/4.0.1: + resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 1.1.2 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /http-proxy-middleware/0.19.1_tmpgdztspuwvsxzgjkhoqk7duq: + resolution: {integrity: sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==} + engines: {node: '>=4.0.0'} + dependencies: + http-proxy: 1.18.1_debug@4.3.4 + is-glob: 4.0.3 + lodash: 4.17.21 + micromatch: 3.1.10_supports-color@6.1.0 + transitivePeerDependencies: + - debug + - supports-color + dev: true + /http-proxy-middleware/2.0.3_@types+express@4.17.13: resolution: {integrity: sha512-1bloEwnrHMnCoO/Gcwbz7eSVvW50KPES01PecpagI+YLNLci4AcuKJrujW4Mc3sBLpFxMSlsLNHS5Nl/lvrTPA==} engines: {node: '>=12.0.0'} @@ -8282,7 +17179,7 @@ packages: http-proxy: 1.18.1 is-glob: 4.0.3 is-plain-obj: 3.0.0 - micromatch: 4.0.4 + micromatch: 4.0.5 transitivePeerDependencies: - debug dev: true @@ -8292,14 +17189,25 @@ packages: engines: {node: '>=8.0.0'} dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.14.8 + follow-redirects: 1.15.2 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + dev: true + + /http-proxy/1.18.1_debug@4.3.4: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.2 requires-port: 1.0.0 transitivePeerDependencies: - debug dev: true /http-signature/1.2.0: - resolution: {integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=} + resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} engines: {node: '>=0.8', npm: '>=1.3.7'} dependencies: assert-plus: 1.0.0 @@ -8308,7 +17216,22 @@ packages: dev: true /https-browserify/1.0.0: - resolution: {integrity: sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=} + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + dev: true + + /https-proxy-agent/5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /human-signals/1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} dev: true /human-signals/2.1.0: @@ -8330,6 +17253,13 @@ packages: safer-buffer: 2.1.2 dev: true + /icss-utils/4.1.1: + resolution: {integrity: sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==} + engines: {node: '>= 6'} + dependencies: + postcss: 7.0.39 + dev: true + /icss-utils/5.1.0_postcss@8.4.6: resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} @@ -8343,12 +17273,19 @@ packages: resolution: {integrity: sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==} dev: true + /identity-obj-proxy/3.0.0: + resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} + engines: {node: '>=4'} + dependencies: + harmony-reflect: 1.6.2 + dev: true + /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: true /iferr/0.1.5: - resolution: {integrity: sha1-xg7taebY/bazEEofy8ocGS3FtQE=} + resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==} dev: true /ignore-by-default/2.1.0: @@ -8356,6 +17293,10 @@ packages: engines: {node: '>=10 <11 || >=12 <13 || >=14'} dev: true + /ignore/3.3.10: + resolution: {integrity: sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==} + dev: true + /ignore/4.0.6: resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} engines: {node: '>= 4'} @@ -8366,8 +17307,19 @@ packages: engines: {node: '>= 4'} dev: true + /immer/8.0.1: + resolution: {integrity: sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==} + dev: true + + /import-cwd/2.1.0: + resolution: {integrity: sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==} + engines: {node: '>=4'} + dependencies: + import-from: 2.1.0 + dev: true + /import-fresh/2.0.0: - resolution: {integrity: sha1-2BNVwVYS04bGH53dOSLUMEgipUY=} + resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} engines: {node: '>=4'} dependencies: caller-path: 2.0.0 @@ -8382,16 +17334,49 @@ packages: resolve-from: 4.0.0 dev: true + /import-from/2.1.0: + resolution: {integrity: sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==} + engines: {node: '>=4'} + dependencies: + resolve-from: 3.0.0 + dev: true + /import-lazy/2.1.0: - resolution: {integrity: sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=} + resolution: {integrity: sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==} engines: {node: '>=4'} dev: true + /import-local/2.0.0: + resolution: {integrity: sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + pkg-dir: 3.0.0 + resolve-cwd: 2.0.0 + dev: true + + /import-local/3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} dev: true + /indent-string/2.1.0: + resolution: {integrity: sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==} + engines: {node: '>=0.10.0'} + dependencies: + repeating: 2.0.1 + dev: true + optional: true + /indent-string/4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -8403,7 +17388,7 @@ packages: dev: true /indexes-of/1.0.1: - resolution: {integrity: sha1-8w9xbI4r00bHtn0985FVZqfAVgc=} + resolution: {integrity: sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==} dev: true /infer-owner/1.0.4: @@ -8427,6 +17412,10 @@ packages: /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + /ini/1.3.7: + resolution: {integrity: sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==} + dev: true + /ini/1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: true @@ -8436,6 +17425,22 @@ packages: engines: {node: '>=10'} dev: true + /inline-chunk-html-plugin/1.1.1: + resolution: {integrity: sha512-6W1eGIj8z/Yla6xJx5il6jJfCxMZS3kVkbiLQThbbjdsDLRIWkUVmpnhfW2l6WAwCW+qfy0zoXVGBZM1E5XF3g==} + dev: true + + /inline-style-parser/0.1.1: + resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} + dev: true + + /internal-ip/4.3.0: + resolution: {integrity: sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==} + engines: {node: '>=6'} + dependencies: + default-gateway: 4.2.0 + ipaddr.js: 1.9.1 + dev: true + /internal-slot/1.0.3: resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} engines: {node: '>= 0.4'} @@ -8445,8 +17450,33 @@ packages: side-channel: 1.0.4 dev: true + /interpret/1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + dev: true + + /interpret/2.2.0: + resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} + engines: {node: '>= 0.10'} + dev: true + + /invariant/2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: true + + /ip-regex/2.1.0: + resolution: {integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==} + engines: {node: '>=4'} + dev: true + /ip/1.1.5: - resolution: {integrity: sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=} + resolution: {integrity: sha512-rBtCAQAJm8A110nbwn6YdveUnuZH3WrC36IwkRXxDnq53JvXA2NVQvB7IHyKomxK1MJ4VDNw3UtFDdXQ+AvLYA==} + dev: true + + /ip/2.0.0: + resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} dev: true /ipaddr.js/1.9.1: @@ -8465,12 +17495,17 @@ packages: dev: true /is-absolute-url/2.1.0: - resolution: {integrity: sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=} + resolution: {integrity: sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==} engines: {node: '>=0.10.0'} dev: true + /is-absolute-url/3.0.3: + resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} + engines: {node: '>=8'} + dev: true + /is-accessor-descriptor/0.1.6: - resolution: {integrity: sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=} + resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==} engines: {node: '>=0.10.0'} dependencies: kind-of: 3.2.2 @@ -8483,6 +17518,17 @@ packages: kind-of: 6.0.3 dev: true + /is-alphabetical/1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + dev: true + + /is-alphanumerical/1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + dev: true + /is-arguments/1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -8492,7 +17538,7 @@ packages: dev: true /is-arrayish/0.2.1: - resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true /is-arrayish/0.3.2: @@ -8502,7 +17548,7 @@ packages: /is-bigint/1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: - has-bigints: 1.0.1 + has-bigints: 1.0.2 dev: true /is-binary-path/1.0.1: @@ -8511,7 +17557,6 @@ packages: dependencies: binary-extensions: 1.13.1 dev: true - optional: true /is-binary-path/2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} @@ -8532,6 +17577,11 @@ packages: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} dev: true + /is-buffer/2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + dev: true + /is-builtin-module/3.2.0: resolution: {integrity: sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==} engines: {node: '>=6'} @@ -8544,6 +17594,11 @@ packages: engines: {node: '>= 0.4'} dev: true + /is-callable/1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + /is-ci/2.0.0: resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} hasBin: true @@ -8552,7 +17607,7 @@ packages: dev: true /is-color-stop/1.1.0: - resolution: {integrity: sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=} + resolution: {integrity: sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==} dependencies: css-color-names: 0.0.4 hex-color-regex: 1.1.0 @@ -8581,7 +17636,7 @@ packages: dev: true /is-data-descriptor/0.1.4: - resolution: {integrity: sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=} + resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} engines: {node: '>=0.10.0'} dependencies: kind-of: 3.2.2 @@ -8601,6 +17656,10 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-decimal/1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + dev: true + /is-descriptor/0.1.6: resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==} engines: {node: '>=0.10.0'} @@ -8620,7 +17679,7 @@ packages: dev: true /is-directory/0.3.1: - resolution: {integrity: sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=} + resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==} engines: {node: '>=0.10.0'} dev: true @@ -8630,12 +17689,19 @@ packages: hasBin: true dev: true + /is-dom/1.1.0: + resolution: {integrity: sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ==} + dependencies: + is-object: 1.0.2 + is-window: 1.0.2 + dev: true + /is-error/2.2.2: resolution: {integrity: sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==} dev: true /is-extendable/0.1.1: - resolution: {integrity: sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=} + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} dev: true @@ -8646,11 +17712,34 @@ packages: is-plain-object: 2.0.4 dev: true + /is-extglob/1.0.0: + resolution: {integrity: sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==} + engines: {node: '>=0.10.0'} + dev: true + /is-extglob/2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} dev: true + /is-finite/1.1.0: + resolution: {integrity: sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==} + engines: {node: '>=0.10.0'} + dev: true + optional: true + + /is-fullwidth-code-point/1.0.0: + resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} + engines: {node: '>=0.10.0'} + dependencies: + number-is-nan: 1.0.1 + dev: true + + /is-fullwidth-code-point/2.0.0: + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} + dev: true + /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -8661,13 +17750,28 @@ packages: engines: {node: '>=12'} dev: true + /is-function/1.0.2: + resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==} + dev: true + + /is-generator-fn/2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob/2.0.1: + resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 1.0.0 + dev: true + /is-glob/3.1.0: resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 dev: true - optional: true /is-glob/4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -8676,6 +17780,18 @@ packages: is-extglob: 2.1.1 dev: true + /is-hexadecimal/1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + dev: true + + /is-installed-globally/0.3.2: + resolution: {integrity: sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==} + engines: {node: '>=8'} + dependencies: + global-dirs: 2.1.0 + is-path-inside: 3.0.3 + dev: true + /is-installed-globally/0.4.0: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} @@ -8689,6 +17805,10 @@ packages: engines: {node: '>=8'} dev: true + /is-map/2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true + /is-module/1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} dev: true @@ -8698,6 +17818,11 @@ packages: engines: {node: '>= 0.4'} dev: true + /is-npm/4.0.0: + resolution: {integrity: sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==} + engines: {node: '>=8'} + dev: true + /is-npm/5.0.0: resolution: {integrity: sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==} engines: {node: '>=10'} @@ -8723,7 +17848,7 @@ packages: dev: true /is-obj/1.0.1: - resolution: {integrity: sha1-PkcprB9f3gJc19g6iW2rn09n2w8=} + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} dev: true @@ -8732,16 +17857,39 @@ packages: engines: {node: '>=8'} dev: true + /is-object/1.0.2: + resolution: {integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==} + dev: true + /is-path-cwd/2.2.0: resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} engines: {node: '>=6'} dev: true + /is-path-in-cwd/2.1.0: + resolution: {integrity: sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==} + engines: {node: '>=6'} + dependencies: + is-path-inside: 2.1.0 + dev: true + + /is-path-inside/2.1.0: + resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==} + engines: {node: '>=6'} + dependencies: + path-is-inside: 1.0.2 + dev: true + /is-path-inside/3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} dev: true + /is-plain-obj/1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + dev: true + /is-plain-obj/2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} engines: {node: '>=8'} @@ -8764,6 +17912,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-potential-custom-element-name/1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + /is-promise/4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} dev: true @@ -8783,7 +17935,7 @@ packages: dev: true /is-regexp/1.0.0: - resolution: {integrity: sha1-/S2INUXEa6xaYz57mgnof6LLUGk=} + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} engines: {node: '>=0.10.0'} dev: true @@ -8791,10 +17943,30 @@ packages: resolution: {integrity: sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==} dev: true + /is-root/2.1.0: + resolution: {integrity: sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==} + engines: {node: '>=6'} + dev: true + + /is-set/2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true + /is-shared-array-buffer/1.0.1: resolution: {integrity: sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==} dev: true + /is-shared-array-buffer/1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream/1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + dev: true + /is-stream/2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -8807,11 +17979,26 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-subset/0.1.1: + resolution: {integrity: sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==} + dev: true + /is-symbol/1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} dependencies: - has-symbols: 1.0.2 + has-symbols: 1.0.3 + dev: true + + /is-typed-array/1.1.9: + resolution: {integrity: sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-abstract: 1.20.4 + for-each: 0.3.3 + has-tostringtag: 1.0.0 dev: true /is-typedarray/1.0.0: @@ -8828,19 +18015,47 @@ packages: engines: {node: '>=12'} dev: true + /is-utf8/0.2.1: + resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} + dev: true + optional: true + + /is-weakmap/2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + /is-weakref/1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: call-bind: 1.0.2 dev: true + /is-weakset/2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + dev: true + + /is-whitespace-character/1.0.4: + resolution: {integrity: sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==} + dev: true + + /is-window/1.0.2: + resolution: {integrity: sha512-uj00kdXyZb9t9RcAUAwMZAnkBUwdYGhYlt7djMXhfyhUCzwNba50tIiBKR7q0l7tdoBtFVw/3JmLY6fI3rmZmg==} + dev: true + /is-windows/1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} dev: true + /is-word-character/1.0.4: + resolution: {integrity: sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==} + dev: true + /is-wsl/1.1.0: - resolution: {integrity: sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=} + resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} engines: {node: '>=4'} dev: true @@ -8856,7 +18071,11 @@ packages: dev: true /isarray/1.0.0: - resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + + /isarray/2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} dev: true /isexe/2.0.0: @@ -8864,14 +18083,19 @@ packages: dev: true /isobject/2.1.0: - resolution: {integrity: sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=} + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} engines: {node: '>=0.10.0'} dependencies: isarray: 1.0.0 dev: true /isobject/3.0.1: - resolution: {integrity: sha1-TkMekrEalzFjaqH5yNHMvP2reN8=} + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + dev: true + + /isobject/4.0.0: + resolution: {integrity: sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==} engines: {node: '>=0.10.0'} dev: true @@ -8885,7 +18109,7 @@ packages: dev: true /isstream/0.1.2: - resolution: {integrity: sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=} + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} dev: true /istanbul-lib-coverage/3.2.0: @@ -8904,7 +18128,20 @@ packages: resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.16.7 + '@babel/core': 7.19.6 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument/5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.19.6 + '@babel/parser': 7.19.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -8938,7 +18175,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.3 + debug: 4.3.4 istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: @@ -8953,6 +18190,17 @@ packages: istanbul-lib-report: 3.0.0 dev: true + /iterate-iterator/1.0.2: + resolution: {integrity: sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==} + dev: true + + /iterate-value/1.0.2: + resolution: {integrity: sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==} + dependencies: + es-get-iterator: 1.1.2 + iterate-iterator: 1.0.2 + dev: true + /jake/10.8.2: resolution: {integrity: sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==} hasBin: true @@ -8960,12 +18208,1040 @@ packages: async: 0.9.2 chalk: 2.4.2 filelist: 1.0.2 - minimatch: 3.0.5 + minimatch: 3.1.2 dev: true /jed/1.1.1: resolution: {integrity: sha512-z35ZSEcXHxLW4yumw0dF6L464NT36vmx3wxJw8MDpraBcWuNVgUPZgPJKcu1HekNgwlMFNqol7i/IpSbjhqwqA==} + /jest-changed-files/26.6.2: + resolution: {integrity: sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + execa: 4.1.0 + throat: 5.0.0 + dev: true + + /jest-changed-files/27.5.1: + resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + execa: 5.1.1 + throat: 6.0.1 + dev: true + + /jest-circus/27.5.1: + resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/environment': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + expect: 27.5.1 + is-generator-fn: 2.1.0 + jest-each: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + slash: 3.0.0 + stack-utils: 2.0.5 + throat: 6.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-cli/26.6.3: + resolution: {integrity: sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==} + engines: {node: '>= 10.14.2'} + hasBin: true + dependencies: + '@jest/core': 26.6.3 + '@jest/test-result': 26.6.2 + '@jest/types': 26.6.2 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.10 + import-local: 3.1.0 + is-ci: 2.0.0 + jest-config: 26.6.3 + jest-util: 26.6.2 + jest-validate: 26.6.2 + prompts: 2.4.2 + yargs: 15.4.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /jest-cli/27.5.1: + resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.10 + import-local: 3.1.0 + jest-config: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + prompts: 2.4.2 + yargs: 16.2.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /jest-config/26.6.3: + resolution: {integrity: sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==} + engines: {node: '>= 10.14.2'} + peerDependencies: + ts-node: '>=9.0.0' + peerDependenciesMeta: + ts-node: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@jest/test-sequencer': 26.6.3 + '@jest/types': 26.6.2 + babel-jest: 26.6.3_@babel+core@7.19.6 + chalk: 4.1.2 + deepmerge: 4.2.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + jest-environment-jsdom: 26.6.2 + jest-environment-node: 26.6.2 + jest-get-type: 26.3.0 + jest-jasmine2: 26.6.3 + jest-regex-util: 26.0.0 + jest-resolve: 26.6.2 + jest-util: 26.6.2 + jest-validate: 26.6.2 + micromatch: 4.0.5 + pretty-format: 26.6.2 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: true + + /jest-config/27.5.1: + resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + ts-node: '>=9.0.0' + peerDependenciesMeta: + ts-node: + optional: true + dependencies: + '@babel/core': 7.19.6 + '@jest/test-sequencer': 27.5.1 + '@jest/types': 27.5.1 + babel-jest: 27.5.1_@babel+core@7.19.6 + chalk: 4.1.2 + ci-info: 3.5.0 + deepmerge: 4.2.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + jest-circus: 27.5.1 + jest-environment-jsdom: 27.5.1 + jest-environment-node: 27.5.1 + jest-get-type: 27.5.1 + jest-jasmine2: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-runner: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 27.5.1 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: true + + /jest-diff/26.6.2: + resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==} + engines: {node: '>= 10.14.2'} + dependencies: + chalk: 4.1.2 + diff-sequences: 26.6.2 + jest-get-type: 26.3.0 + pretty-format: 26.6.2 + dev: true + + /jest-diff/27.5.1: + resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + dev: true + + /jest-docblock/26.0.0: + resolution: {integrity: sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==} + engines: {node: '>= 10.14.2'} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-docblock/27.5.1: + resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each/26.6.2: + resolution: {integrity: sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + chalk: 4.1.2 + jest-get-type: 26.3.0 + jest-util: 26.6.2 + pretty-format: 26.6.2 + dev: true + + /jest-each/27.5.1: + resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + chalk: 4.1.2 + jest-get-type: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + dev: true + + /jest-environment-jsdom/26.6.2: + resolution: {integrity: sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/environment': 26.6.2 + '@jest/fake-timers': 26.6.2 + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + jest-mock: 26.6.2 + jest-util: 26.6.2 + jsdom: 16.7.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: true + + /jest-environment-jsdom/27.5.1: + resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + jest-mock: 27.5.1 + jest-util: 27.5.1 + jsdom: 16.7.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: true + + /jest-environment-node/26.6.2: + resolution: {integrity: sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/environment': 26.6.2 + '@jest/fake-timers': 26.6.2 + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + jest-mock: 26.6.2 + jest-util: 26.6.2 + dev: true + + /jest-environment-node/27.5.1: + resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + jest-mock: 27.5.1 + jest-util: 27.5.1 + dev: true + + /jest-fetch-mock/3.0.3: + resolution: {integrity: sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==} + dependencies: + cross-fetch: 3.1.5 + promise-polyfill: 8.2.1 + transitivePeerDependencies: + - encoding + dev: true + + /jest-get-type/26.3.0: + resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==} + engines: {node: '>= 10.14.2'} + dev: true + + /jest-get-type/27.5.1: + resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true + + /jest-haste-map/26.6.2: + resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + '@types/graceful-fs': 4.1.5 + '@types/node': 18.8.5 + anymatch: 3.1.2 + fb-watchman: 2.0.2 + graceful-fs: 4.2.10 + jest-regex-util: 26.0.0 + jest-serializer: 26.6.2 + jest-util: 26.6.2 + jest-worker: 26.6.2 + micromatch: 4.0.5 + sane: 4.1.0 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-haste-map/27.5.1: + resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + '@types/graceful-fs': 4.1.5 + '@types/node': 18.8.5 + anymatch: 3.1.2 + fb-watchman: 2.0.2 + graceful-fs: 4.2.10 + jest-regex-util: 27.5.1 + jest-serializer: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /jest-jasmine2/26.6.3: + resolution: {integrity: sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==} + engines: {node: '>= 10.14.2'} + dependencies: + '@babel/traverse': 7.19.6 + '@jest/environment': 26.6.2 + '@jest/source-map': 26.6.2 + '@jest/test-result': 26.6.2 + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + chalk: 4.1.2 + co: 4.6.0 + expect: 26.6.2 + is-generator-fn: 2.1.0 + jest-each: 26.6.2 + jest-matcher-utils: 26.6.2 + jest-message-util: 26.6.2 + jest-runtime: 26.6.3 + jest-snapshot: 26.6.2 + jest-util: 26.6.2 + pretty-format: 26.6.2 + throat: 5.0.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /jest-jasmine2/27.5.1: + resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/environment': 27.5.1 + '@jest/source-map': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + chalk: 4.1.2 + co: 4.6.0 + expect: 27.5.1 + is-generator-fn: 2.1.0 + jest-each: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + throat: 6.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-leak-detector/26.6.2: + resolution: {integrity: sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==} + engines: {node: '>= 10.14.2'} + dependencies: + jest-get-type: 26.3.0 + pretty-format: 26.6.2 + dev: true + + /jest-leak-detector/27.5.1: + resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + dev: true + + /jest-matcher-utils/26.6.2: + resolution: {integrity: sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==} + engines: {node: '>= 10.14.2'} + dependencies: + chalk: 4.1.2 + jest-diff: 26.6.2 + jest-get-type: 26.3.0 + pretty-format: 26.6.2 + dev: true + + /jest-matcher-utils/27.5.1: + resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + dev: true + + /jest-message-util/26.6.2: + resolution: {integrity: sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==} + engines: {node: '>= 10.14.2'} + dependencies: + '@babel/code-frame': 7.18.6 + '@jest/types': 26.6.2 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + micromatch: 4.0.5 + pretty-format: 26.6.2 + slash: 3.0.0 + stack-utils: 2.0.5 + dev: true + + /jest-message-util/27.5.1: + resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@babel/code-frame': 7.16.7 + '@jest/types': 27.5.1 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + micromatch: 4.0.5 + pretty-format: 27.5.1 + slash: 3.0.0 + stack-utils: 2.0.5 + dev: true + + /jest-message-util/28.1.3: + resolution: {integrity: sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@babel/code-frame': 7.18.6 + '@jest/types': 28.1.3 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + micromatch: 4.0.5 + pretty-format: 28.1.3 + slash: 3.0.0 + stack-utils: 2.0.5 + dev: true + + /jest-mock/26.6.2: + resolution: {integrity: sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + dev: true + + /jest-mock/27.5.1: + resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + dev: true + + /jest-pnp-resolver/1.2.2_jest-resolve@26.6.2: + resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 26.6.2 + dev: true + + /jest-pnp-resolver/1.2.2_jest-resolve@27.5.1: + resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 27.5.1 + dev: true + + /jest-preset-preact/4.0.5_5pwcttm7mk4uq46yrrfyt2sdyu: + resolution: {integrity: sha512-MnU7mfpnwopJkdx0WoEyRmrNDIvRN+w6sOur0zEhaRYYMo0gJM7UdZHWTV8k6uo0+ypY+m0kQW6kMukUx4v8JQ==} + peerDependencies: + jest: 26.x || 27.x + preact: 10.x + preact-render-to-string: 5.x + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + babel-jest: 27.5.1_@babel+core@7.17.2 + identity-obj-proxy: 3.0.0 + isomorphic-unfetch: 3.1.0 + jest: 26.6.3 + jest-watch-typeahead: 0.6.5_jest@26.6.3 + preact: 10.6.1 + preact-render-to-string: 5.1.19_preact@10.6.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /jest-preset-preact/4.0.5_e2p4bsvs32uygapo46tobni7si: + resolution: {integrity: sha512-MnU7mfpnwopJkdx0WoEyRmrNDIvRN+w6sOur0zEhaRYYMo0gJM7UdZHWTV8k6uo0+ypY+m0kQW6kMukUx4v8JQ==} + peerDependencies: + jest: 26.x || 27.x + preact: 10.x + preact-render-to-string: 5.x + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + babel-jest: 27.5.1_@babel+core@7.17.2 + identity-obj-proxy: 3.0.0 + isomorphic-unfetch: 3.1.0 + jest: 27.5.1 + jest-watch-typeahead: 0.6.5_jest@27.5.1 + preact: 10.6.5 + preact-render-to-string: 5.1.19_preact@10.6.5 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /jest-preset-preact/4.0.5_ewndwes2ovzexxcig72sj2dfke: + resolution: {integrity: sha512-MnU7mfpnwopJkdx0WoEyRmrNDIvRN+w6sOur0zEhaRYYMo0gJM7UdZHWTV8k6uo0+ypY+m0kQW6kMukUx4v8JQ==} + peerDependencies: + jest: 26.x || 27.x + preact: 10.x + preact-render-to-string: 5.x + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + babel-jest: 27.5.1_@babel+core@7.17.2 + identity-obj-proxy: 3.0.0 + isomorphic-unfetch: 3.1.0 + jest: 26.6.3 + jest-watch-typeahead: 0.6.5_jest@26.6.3 + preact: 10.6.5 + preact-render-to-string: 5.1.19_preact@10.6.5 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /jest-regex-util/26.0.0: + resolution: {integrity: sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==} + engines: {node: '>= 10.14.2'} + dev: true + + /jest-regex-util/27.5.1: + resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true + + /jest-regex-util/28.0.2: + resolution: {integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dev: true + + /jest-resolve-dependencies/26.6.3: + resolution: {integrity: sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + jest-regex-util: 26.0.0 + jest-snapshot: 26.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve-dependencies/27.5.1: + resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + jest-regex-util: 27.5.1 + jest-snapshot: 27.5.1 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve/26.6.2: + resolution: {integrity: sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + chalk: 4.1.2 + graceful-fs: 4.2.10 + jest-pnp-resolver: 1.2.2_jest-resolve@26.6.2 + jest-util: 26.6.2 + read-pkg-up: 7.0.1 + resolve: 1.22.1 + slash: 3.0.0 + dev: true + + /jest-resolve/27.5.1: + resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + jest-haste-map: 27.5.1 + jest-pnp-resolver: 1.2.2_jest-resolve@27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + resolve: 1.22.1 + resolve.exports: 1.1.0 + slash: 3.0.0 + dev: true + + /jest-runner/26.6.3: + resolution: {integrity: sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/console': 26.6.2 + '@jest/environment': 26.6.2 + '@jest/test-result': 26.6.2 + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + chalk: 4.1.2 + emittery: 0.7.2 + exit: 0.1.2 + graceful-fs: 4.2.10 + jest-config: 26.6.3 + jest-docblock: 26.0.0 + jest-haste-map: 26.6.2 + jest-leak-detector: 26.6.2 + jest-message-util: 26.6.2 + jest-resolve: 26.6.2 + jest-runtime: 26.6.3 + jest-util: 26.6.2 + jest-worker: 26.6.2 + source-map-support: 0.5.21 + throat: 5.0.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /jest-runner/27.5.1: + resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/console': 27.5.1 + '@jest/environment': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + chalk: 4.1.2 + emittery: 0.8.1 + graceful-fs: 4.2.10 + jest-docblock: 27.5.1 + jest-environment-jsdom: 27.5.1 + jest-environment-node: 27.5.1 + jest-haste-map: 27.5.1 + jest-leak-detector: 27.5.1 + jest-message-util: 27.5.1 + jest-resolve: 27.5.1 + jest-runtime: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + source-map-support: 0.5.21 + throat: 6.0.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: true + + /jest-runtime/26.6.3: + resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==} + engines: {node: '>= 10.14.2'} + hasBin: true + dependencies: + '@jest/console': 26.6.2 + '@jest/environment': 26.6.2 + '@jest/fake-timers': 26.6.2 + '@jest/globals': 26.6.2 + '@jest/source-map': 26.6.2 + '@jest/test-result': 26.6.2 + '@jest/transform': 26.6.2 + '@jest/types': 26.6.2 + '@types/yargs': 15.0.14 + chalk: 4.1.2 + cjs-module-lexer: 0.6.0 + collect-v8-coverage: 1.0.1 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.10 + jest-config: 26.6.3 + jest-haste-map: 26.6.2 + jest-message-util: 26.6.2 + jest-mock: 26.6.2 + jest-regex-util: 26.0.0 + jest-resolve: 26.6.2 + jest-snapshot: 26.6.2 + jest-util: 26.6.2 + jest-validate: 26.6.2 + slash: 3.0.0 + strip-bom: 4.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /jest-runtime/27.5.1: + resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/globals': 27.5.1 + '@jest/source-map': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + chalk: 4.1.2 + cjs-module-lexer: 1.2.2 + collect-v8-coverage: 1.0.1 + execa: 5.1.1 + glob: 7.2.3 + graceful-fs: 4.2.10 + jest-haste-map: 27.5.1 + jest-message-util: 27.5.1 + jest-mock: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-serializer/26.6.2: + resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} + engines: {node: '>= 10.14.2'} + dependencies: + '@types/node': 18.8.5 + graceful-fs: 4.2.10 + dev: true + + /jest-serializer/27.5.1: + resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@types/node': 18.8.5 + graceful-fs: 4.2.10 + dev: true + + /jest-snapshot/26.6.2: + resolution: {integrity: sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==} + engines: {node: '>= 10.14.2'} + dependencies: + '@babel/types': 7.19.4 + '@jest/types': 26.6.2 + '@types/babel__traverse': 7.18.2 + '@types/prettier': 2.7.1 + chalk: 4.1.2 + expect: 26.6.2 + graceful-fs: 4.2.10 + jest-diff: 26.6.2 + jest-get-type: 26.3.0 + jest-haste-map: 26.6.2 + jest-matcher-utils: 26.6.2 + jest-message-util: 26.6.2 + jest-resolve: 26.6.2 + natural-compare: 1.4.0 + pretty-format: 26.6.2 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot/27.5.1: + resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@babel/core': 7.19.6 + '@babel/generator': 7.18.2 + '@babel/plugin-syntax-typescript': 7.16.7_@babel+core@7.19.6 + '@babel/traverse': 7.18.2 + '@babel/types': 7.18.4 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/babel__traverse': 7.18.2 + '@types/prettier': 2.7.1 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.19.6 + chalk: 4.1.2 + expect: 27.5.1 + graceful-fs: 4.2.10 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + jest-haste-map: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-util: 27.5.1 + natural-compare: 1.4.0 + pretty-format: 27.5.1 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util/26.6.2: + resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + chalk: 4.1.2 + graceful-fs: 4.2.10 + is-ci: 2.0.0 + micromatch: 4.0.5 + dev: true + + /jest-util/27.5.1: + resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + chalk: 4.1.2 + ci-info: 3.5.0 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + + /jest-util/28.1.3: + resolution: {integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/types': 28.1.3 + '@types/node': 18.8.5 + chalk: 4.1.2 + ci-info: 3.5.0 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + + /jest-validate/26.6.2: + resolution: {integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/types': 26.6.2 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 26.3.0 + leven: 3.1.0 + pretty-format: 26.6.2 + dev: true + + /jest-validate/27.5.1: + resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.5.1 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 27.5.1 + leven: 3.1.0 + pretty-format: 27.5.1 + dev: true + + /jest-watch-typeahead/0.6.5_jest@26.6.3: + resolution: {integrity: sha512-GIbV6h37/isatMDtqZlA8Q5vC6T3w+5qdvtF+3LIkPc58zEWzbKmTHvlUIp3wvBm400RzrQWcVPcsAJqKWu7XQ==} + engines: {node: '>=10'} + peerDependencies: + jest: ^26.0.0 || ^27.0.0 + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest: 26.6.3 + jest-regex-util: 27.5.1 + jest-watcher: 27.5.1 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + dev: true + + /jest-watch-typeahead/0.6.5_jest@27.5.1: + resolution: {integrity: sha512-GIbV6h37/isatMDtqZlA8Q5vC6T3w+5qdvtF+3LIkPc58zEWzbKmTHvlUIp3wvBm400RzrQWcVPcsAJqKWu7XQ==} + engines: {node: '>=10'} + peerDependencies: + jest: ^26.0.0 || ^27.0.0 + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest: 27.5.1 + jest-regex-util: 27.5.1 + jest-watcher: 27.5.1 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + dev: true + + /jest-watch-typeahead/1.1.0_jest@27.5.1: + resolution: {integrity: sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + jest: ^27.0.0 || ^28.0.0 + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest: 27.5.1 + jest-regex-util: 28.0.2 + jest-watcher: 28.1.3 + slash: 4.0.0 + string-length: 5.0.1 + strip-ansi: 7.0.1 + dev: true + + /jest-watcher/26.6.2: + resolution: {integrity: sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==} + engines: {node: '>= 10.14.2'} + dependencies: + '@jest/test-result': 26.6.2 + '@jest/types': 26.6.2 + '@types/node': 18.8.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest-util: 26.6.2 + string-length: 4.0.2 + dev: true + + /jest-watcher/27.5.1: + resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.8.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest-util: 27.5.1 + string-length: 4.0.2 + dev: true + + /jest-watcher/28.1.3: + resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 18.8.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.10.2 + jest-util: 28.1.3 + string-length: 4.0.2 + dev: true + + /jest-worker/24.9.0: + resolution: {integrity: sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==} + engines: {node: '>= 6'} + dependencies: + merge-stream: 2.0.0 + supports-color: 6.1.0 + dev: true + /jest-worker/26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} @@ -8975,6 +19251,52 @@ packages: supports-color: 7.2.0 dev: true + /jest-worker/27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 18.8.5 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest/26.6.3: + resolution: {integrity: sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==} + engines: {node: '>= 10.14.2'} + hasBin: true + dependencies: + '@jest/core': 26.6.3 + import-local: 3.1.0 + jest-cli: 26.6.3 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + + /jest/27.5.1: + resolution: {integrity: sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 27.5.1 + import-local: 3.1.0 + jest-cli: 27.5.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + dev: true + /js-string-escape/1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} engines: {node: '>= 0.8'} @@ -8999,7 +19321,7 @@ packages: dev: true /jsbn/0.1.1: - resolution: {integrity: sha1-peZUwuWi3rXyAdls77yoDA7y9RM=} + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} dev: true /jsdom/12.2.0: @@ -9036,8 +19358,50 @@ packages: - utf-8-validate dev: true + /jsdom/16.7.0: + resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==} + engines: {node: '>=10'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.5 + acorn: 8.8.0 + acorn-globals: 6.0.0 + cssom: 0.4.4 + cssstyle: 2.3.0 + data-urls: 2.0.0 + decimal.js: 10.4.2 + domexception: 2.0.1 + escodegen: 2.0.0 + form-data: 3.0.1 + html-encoding-sniffer: 2.0.1 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.0 + parse5: 6.0.1 + saxes: 5.0.1 + symbol-tree: 3.2.4 + tough-cookie: 4.1.2 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 2.0.0 + webidl-conversions: 6.1.0 + whatwg-encoding: 1.0.5 + whatwg-mimetype: 2.3.0 + whatwg-url: 8.7.0 + ws: 7.5.7 + xml-name-validator: 3.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /jsesc/0.5.0: - resolution: {integrity: sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=} + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true dev: true @@ -9061,7 +19425,6 @@ packages: /json-schema-traverse/0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true /json-schema-traverse/1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -9076,11 +19439,11 @@ packages: dev: true /json-stringify-safe/5.0.1: - resolution: {integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=} + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true /json5/0.5.1: - resolution: {integrity: sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=} + resolution: {integrity: sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==} hasBin: true dev: true @@ -9089,7 +19452,6 @@ packages: hasBin: true dependencies: minimist: 1.2.5 - dev: true /json5/2.2.0: resolution: {integrity: sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==} @@ -9099,12 +19461,18 @@ packages: minimist: 1.2.5 dev: true + /json5/2.2.1: + resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} + engines: {node: '>=6'} + hasBin: true + dev: true + /jsonc-parser/3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true /jsonfile/4.0.0: - resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=} + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.10 dev: true @@ -9144,21 +19512,30 @@ packages: object.assign: 4.1.2 dev: true + /junk/3.1.0: + resolution: {integrity: sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==} + engines: {node: '>=8'} + dev: true + /keyv/3.1.0: resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} dependencies: json-buffer: 3.0.0 dev: true + /killable/1.0.1: + resolution: {integrity: sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==} + dev: true + /kind-of/3.2.2: - resolution: {integrity: sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=} + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} dependencies: is-buffer: 1.1.6 dev: true /kind-of/4.0.0: - resolution: {integrity: sha1-IIE989cSkosgc3hpGkUGb65y3Vc=} + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} engines: {node: '>=0.10.0'} dependencies: is-buffer: 1.1.6 @@ -9213,13 +19590,24 @@ packages: package-json: 6.5.0 dev: true + /lazy-universal-dotenv/3.0.1: + resolution: {integrity: sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ==} + engines: {node: '>=6.0.0', npm: '>=6.0.0', yarn: '>=1.0.0'} + dependencies: + '@babel/runtime': 7.17.8 + app-root-dir: 1.0.2 + core-js: 3.26.0 + dotenv: 8.6.0 + dotenv-expand: 5.1.0 + dev: true + /leven/3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} dev: true /levn/0.3.0: - resolution: {integrity: sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=} + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} engines: {node: '>= 0.8.0'} dependencies: prelude-ls: 1.1.2 @@ -9243,6 +19631,18 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /load-json-file/1.1.0: + resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==} + engines: {node: '>=0.10.0'} + dependencies: + graceful-fs: 4.2.10 + parse-json: 2.2.0 + pify: 2.3.0 + pinkie-promise: 2.0.1 + strip-bom: 2.0.0 + dev: true + optional: true + /load-json-file/7.0.1: resolution: {integrity: sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -9254,7 +19654,7 @@ packages: dev: true /loader-utils/0.2.17: - resolution: {integrity: sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=} + resolution: {integrity: sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==} dependencies: big.js: 3.2.0 emojis-list: 2.1.0 @@ -9269,7 +19669,6 @@ packages: big.js: 5.2.2 emojis-list: 3.0.0 json5: 1.0.1 - dev: true /loader-utils/2.0.0: resolution: {integrity: sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==} @@ -9277,7 +19676,7 @@ packages: dependencies: big.js: 5.2.2 emojis-list: 3.0.0 - json5: 2.2.0 + json5: 2.2.1 dev: true /loader-utils/2.0.2: @@ -9289,6 +19688,11 @@ packages: json5: 2.2.0 dev: true + /local-access/1.1.0: + resolution: {integrity: sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw==} + engines: {node: '>=6'} + dev: true + /locate-path/2.0.0: resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=} engines: {node: '>=4'} @@ -9326,16 +19730,32 @@ packages: p-locate: 6.0.0 dev: true + /lodash-es/4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /lodash._reinterpolate/3.0.0: + resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==} + dev: true + /lodash.debounce/4.0.8: - resolution: {integrity: sha1-gteb/zCmfEAF/9XiUVMArZyk168=} + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: true + + /lodash.escape/4.0.1: + resolution: {integrity: sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==} dev: true /lodash.flattendeep/4.4.0: - resolution: {integrity: sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=} + resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} + dev: true + + /lodash.isequal/4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} dev: true /lodash.memoize/4.1.2: - resolution: {integrity: sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=} + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true /lodash.merge/4.6.2: @@ -9343,15 +19763,38 @@ packages: dev: true /lodash.sortby/4.7.0: - resolution: {integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=} + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + dev: true + + /lodash.template/4.5.0: + resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==} + dependencies: + lodash._reinterpolate: 3.0.0 + lodash.templatesettings: 4.2.0 + dev: true + + /lodash.templatesettings/4.2.0: + resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==} + dependencies: + lodash._reinterpolate: 3.0.0 + dev: true + + /lodash.truncate/4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} dev: true /lodash.uniq/4.5.0: - resolution: {integrity: sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=} + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} dev: true /lodash/4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + /log-symbols/3.0.0: + resolution: {integrity: sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==} + engines: {node: '>=8'} + dependencies: + chalk: 2.4.2 dev: true /log-symbols/4.1.0: @@ -9362,19 +19805,39 @@ packages: is-unicode-supported: 0.1.0 dev: true + /loglevel/1.8.0: + resolution: {integrity: sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==} + engines: {node: '>= 0.6.0'} + dev: true + /loose-envify/1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true dependencies: js-tokens: 4.0.0 + /loud-rejection/1.6.0: + resolution: {integrity: sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==} + engines: {node: '>=0.10.0'} + dependencies: + currently-unhandled: 0.4.1 + signal-exit: 3.0.7 + dev: true + optional: true + /loupe/2.3.4: resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==} dependencies: get-func-name: 2.0.0 /lower-case/1.1.4: - resolution: {integrity: sha1-miyr0bno4K6ZOkv31YdcOcQujqw=} + resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} + dev: true + + /lower-case/2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.4.0 dev: true /lowercase-keys/1.0.1: @@ -9387,6 +19850,13 @@ packages: engines: {node: '>=8'} dev: true + /lowlight/1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + dev: true + /lru-cache/4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -9412,7 +19882,7 @@ packages: dev: true /lz-string/1.4.4: - resolution: {integrity: sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=} + resolution: {integrity: sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==} hasBin: true dev: true @@ -9437,6 +19907,12 @@ packages: semver: 6.3.0 dev: true + /makeerror/1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + /map-age-cleaner/0.1.3: resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} engines: {node: '>=6'} @@ -9445,17 +19921,74 @@ packages: dev: true /map-cache/0.2.2: - resolution: {integrity: sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=} + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj/1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} dev: true + optional: true + + /map-or-similar/1.5.0: + resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} + dev: true /map-visit/1.0.0: - resolution: {integrity: sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=} + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} engines: {node: '>=0.10.0'} dependencies: object-visit: 1.0.1 dev: true + /markdown-escapes/1.0.4: + resolution: {integrity: sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==} + dev: true + + /markdown-to-jsx/6.11.4: + resolution: {integrity: sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==} + engines: {node: '>= 4'} + peerDependencies: + react: '>= 0.14.0' + dependencies: + prop-types: 15.8.1 + unquote: 1.1.1 + dev: true + + /markdown-to-jsx/6.11.4_react@16.14.0: + resolution: {integrity: sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==} + engines: {node: '>= 4'} + peerDependencies: + react: '>= 0.14.0' + dependencies: + prop-types: 15.8.1 + react: 16.14.0 + unquote: 1.1.1 + dev: true + + /markdown-to-jsx/7.1.7: + resolution: {integrity: sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==} + engines: {node: '>= 10'} + peerDependencies: + react: '>= 0.14.0' + dev: true + + /markdown-to-jsx/7.1.7_react@16.14.0: + resolution: {integrity: sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==} + engines: {node: '>= 10'} + peerDependencies: + react: '>= 0.14.0' + dependencies: + react: 16.14.0 + dev: true + + /marked/2.0.7: + resolution: {integrity: sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==} + engines: {node: '>= 8.16.2'} + hasBin: true + dev: true + /marked/4.1.1: resolution: {integrity: sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==} engines: {node: '>= 12'} @@ -9484,6 +20017,35 @@ packages: safe-buffer: 5.2.1 dev: true + /mdast-squeeze-paragraphs/4.0.0: + resolution: {integrity: sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==} + dependencies: + unist-util-remove: 2.1.0 + dev: true + + /mdast-util-definitions/4.0.0: + resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==} + dependencies: + unist-util-visit: 2.0.3 + dev: true + + /mdast-util-to-hast/10.0.1: + resolution: {integrity: sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==} + dependencies: + '@types/mdast': 3.0.10 + '@types/unist': 2.0.6 + mdast-util-definitions: 4.0.0 + mdurl: 1.0.1 + unist-builder: 2.0.3 + unist-util-generated: 1.1.6 + unist-util-position: 3.1.0 + unist-util-visit: 2.0.3 + dev: true + + /mdast-util-to-string/1.1.0: + resolution: {integrity: sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==} + dev: true + /mdn-data/2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: true @@ -9492,6 +20054,10 @@ packages: resolution: {integrity: sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==} dev: true + /mdurl/1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + dev: true + /media-typer/0.3.0: resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} engines: {node: '>= 0.6'} @@ -9512,8 +20078,14 @@ packages: fs-monkey: 1.0.3 dev: true + /memoizerific/1.11.3: + resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} + dependencies: + map-or-similar: 1.5.0 + dev: true + /memory-fs/0.4.1: - resolution: {integrity: sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=} + resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==} dependencies: errno: 0.1.8 readable-stream: 2.3.7 @@ -9527,6 +20099,23 @@ packages: readable-stream: 2.3.7 dev: true + /meow/3.7.0: + resolution: {integrity: sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==} + engines: {node: '>=0.10.0'} + dependencies: + camelcase-keys: 2.1.0 + decamelize: 1.2.0 + loud-rejection: 1.6.0 + map-obj: 1.0.1 + minimist: 1.2.5 + normalize-package-data: 2.5.0 + object-assign: 4.1.1 + read-pkg-up: 1.0.1 + redent: 1.0.0 + trim-newlines: 1.0.0 + dev: true + optional: true + /merge-descriptors/1.0.1: resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=} dev: true @@ -9541,7 +20130,7 @@ packages: dev: true /methods/1.1.2: - resolution: {integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=} + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} dev: true @@ -9570,12 +20159,25 @@ packages: - supports-color dev: true - /micromatch/4.0.4: - resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} - engines: {node: '>=8.6'} + /micromatch/3.1.10_supports-color@6.1.0: + resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} + engines: {node: '>=0.10.0'} dependencies: - braces: 3.0.2 - picomatch: 2.3.1 + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2_supports-color@6.1.0 + define-property: 2.0.2 + extend-shallow: 3.0.2 + extglob: 2.0.4_supports-color@6.1.0 + fragment-cache: 0.2.1 + kind-of: 6.0.3 + nanomatch: 1.2.13_supports-color@6.1.0 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2_supports-color@6.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color dev: true /micromatch/4.0.5: @@ -9594,29 +20196,15 @@ packages: brorand: 1.1.0 dev: true - /mime-db/1.51.0: - resolution: {integrity: sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==} - engines: {node: '>= 0.6'} - dev: true - /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - dev: false - - /mime-types/2.1.34: - resolution: {integrity: sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.51.0 - dev: true /mime-types/2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 - dev: false /mime/1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} @@ -9624,6 +20212,12 @@ packages: hasBin: true dev: true + /mime/2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + dev: true + /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -9639,6 +20233,30 @@ packages: engines: {node: '>=4'} dev: true + /min-document/2.19.0: + resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} + dependencies: + dom-walk: 0.1.2 + dev: true + + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /mini-css-extract-plugin/0.9.0_webpack@4.46.0: + resolution: {integrity: sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==} + engines: {node: '>= 6.9.0'} + peerDependencies: + webpack: ^4.4.0 + dependencies: + loader-utils: 1.4.0 + normalize-url: 1.9.1 + schema-utils: 1.0.0 + webpack: 4.46.0 + webpack-sources: 1.4.3 + dev: true + /mini-css-extract-plugin/1.6.2_webpack@4.46.0: resolution: {integrity: sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==} engines: {node: '>= 10.13.0'} @@ -9651,12 +20269,17 @@ packages: webpack-sources: 1.4.3 dev: true + /mini-svg-data-uri/1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + dev: true + /minimalistic-assert/1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} dev: true /minimalistic-crypto-utils/1.0.1: - resolution: {integrity: sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=} + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} dev: true /minimatch/3.0.4: @@ -9685,7 +20308,6 @@ packages: /minimist/1.2.5: resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} - dev: true /minipass-collect/1.0.2: resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} @@ -9804,8 +20426,12 @@ packages: yargs-unparser: 2.0.0 dev: true + /moo/0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + dev: true + /move-concurrently/1.0.1: - resolution: {integrity: sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=} + resolution: {integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==} dependencies: aproba: 1.2.0 copy-concurrently: 1.0.5 @@ -9829,6 +20455,10 @@ packages: resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} dev: true + /ms/2.1.1: + resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==} + dev: true + /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -9838,7 +20468,7 @@ packages: dev: true /multicast-dns-service-types/1.1.0: - resolution: {integrity: sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=} + resolution: {integrity: sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==} dev: true /multicast-dns/6.2.3: @@ -9849,18 +20479,37 @@ packages: thunky: 1.1.0 dev: true + /mustache/4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + dev: true + + /mute-stream/0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + /nan/2.17.0: resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} requiresBuild: true dev: true optional: true + /nanoclone/0.2.1: + resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} + dev: false + /nanoid/3.2.0: resolution: {integrity: sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true + /nanoid/3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /nanomatch/1.2.13: resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} engines: {node: '>=0.10.0'} @@ -9880,6 +20529,25 @@ packages: - supports-color dev: true + /nanomatch/1.2.13_supports-color@6.1.0: + resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} + engines: {node: '>=0.10.0'} + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2_supports-color@6.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /native-url/0.3.4: resolution: {integrity: sha512-6iM8R99ze45ivyH8vybJ7X0yekIcPf5GgLV5K0ENCbmRcaRIDoj37BC8iLEmaaBfqqb8enuZ5p0uhY+lVAbAcA==} dependencies: @@ -9890,6 +20558,16 @@ packages: resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} dev: true + /nearley/2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true + dependencies: + commander: 2.20.3 + moo: 0.5.2 + railroad-diagrams: 1.0.0 + randexp: 0.4.6 + dev: true + /negotiator/0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -9899,12 +20577,27 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: true + /nested-error-stacks/2.1.1: + resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} + dev: true + + /nice-try/1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + dev: true + /no-case/2.3.2: resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} dependencies: lower-case: 1.1.4 dev: true + /no-case/3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.4.0 + dev: true + /node-domexception/1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -9930,11 +20623,20 @@ packages: formdata-polyfill: 4.0.10 dev: false + /node-forge/0.10.0: + resolution: {integrity: sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==} + engines: {node: '>= 6.0.0'} + dev: true + /node-forge/1.2.1: resolution: {integrity: sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==} engines: {node: '>= 6.13.0'} dev: true + /node-int64/0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + /node-libs-browser/2.2.1: resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==} dependencies: @@ -9963,6 +20665,19 @@ packages: vm-browserify: 1.1.2 dev: true + /node-notifier/8.0.2: + resolution: {integrity: sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==} + requiresBuild: true + dependencies: + growly: 1.3.0 + is-wsl: 2.2.0 + semver: 7.3.8 + shellwords: 0.1.1 + uuid: 8.3.2 + which: 2.0.2 + dev: true + optional: true + /node-preload/0.2.1: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} @@ -9970,10 +20685,37 @@ packages: process-on-spawn: 1.0.0 dev: true + /node-releases/1.1.77: + resolution: {integrity: sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==} + dev: true + /node-releases/2.0.2: resolution: {integrity: sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==} dev: true + /node-releases/2.0.6: + resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + dev: true + + /nodent-compiler/3.2.13: + resolution: {integrity: sha512-nzzWPXZwSdsWie34om+4dLrT/5l1nT/+ig1v06xuSgMtieJVAnMQFuZihUwREM+M7dFso9YoHfDmweexEXXrrw==} + engines: {'0': n, '1': o, '2': d, '3': e, '4': ' ', '5': '>', '6': '=', '7': ' ', '8': '0', '9': ., '10': '1', '11': '0', '12': ., '13': '0'} + dependencies: + acorn: 5.7.4 + acorn-es7-plugin: 1.1.7 + nodent-transform: 3.2.9 + source-map: 0.5.7 + dev: true + + /nodent-runtime/3.2.1: + resolution: {integrity: sha512-7Ws63oC+215smeKJQCxzrK21VFVlCFBkwl0MOObt0HOpVQXs3u483sAmtkF33nNqZ5rSOQjB76fgyPBmAUrtCA==} + requiresBuild: true + dev: true + + /nodent-transform/3.2.9: + resolution: {integrity: sha512-4a5FH4WLi+daH/CGD5o/JWRR8W5tlCkd3nrDSkxbOzscJTyTUITltvOJeQjg3HJ1YgEuNyiPhQbvbtRjkQBByQ==} + dev: true + /nofilter/3.1.0: resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} engines: {node: '>=12.19'} @@ -9987,13 +20729,21 @@ packages: underscore: 1.6.0 dev: true + /normalize-package-data/2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.1 + semver: 5.7.1 + validate-npm-package-license: 3.0.4 + dev: true + /normalize-path/2.1.1: resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} engines: {node: '>=0.10.0'} dependencies: remove-trailing-separator: 1.1.0 dev: true - optional: true /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -10001,10 +20751,20 @@ packages: dev: true /normalize-range/0.1.2: - resolution: {integrity: sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=} + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} dev: true + /normalize-url/1.9.1: + resolution: {integrity: sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==} + engines: {node: '>=4'} + dependencies: + object-assign: 4.1.1 + prepend-http: 1.0.4 + query-string: 4.3.4 + sort-keys: 1.1.2 + dev: true + /normalize-url/3.3.0: resolution: {integrity: sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==} engines: {node: '>=6'} @@ -10020,6 +20780,13 @@ packages: engines: {node: '>=10'} dev: true + /npm-run-path/2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + dependencies: + path-key: 2.0.1 + dev: true + /npm-run-path/4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -10027,6 +20794,24 @@ packages: path-key: 3.1.1 dev: true + /npmlog/4.1.2: + resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} + dependencies: + are-we-there-yet: 1.1.7 + console-control-strings: 1.1.0 + gauge: 2.7.4 + set-blocking: 2.0.0 + dev: true + + /npmlog/5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + dev: true + /nth-check/1.0.2: resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} dependencies: @@ -10039,6 +20824,15 @@ packages: boolbase: 1.0.0 dev: true + /num2fraction/1.2.2: + resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==} + dev: true + + /number-is-nan/1.0.1: + resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} + engines: {node: '>=0.10.0'} + dev: true + /nwsapi/2.2.0: resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==} dev: true @@ -10084,12 +20878,12 @@ packages: dev: true /object-assign/4.1.1: - resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} dev: true /object-copy/0.1.0: - resolution: {integrity: sha1-fn2Fi3gb18mRpBupde04EnVOmYw=} + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} engines: {node: '>=0.10.0'} dependencies: copy-descriptor: 0.1.1 @@ -10101,6 +20895,10 @@ packages: resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==} dev: true + /object-inspect/1.12.2: + resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + dev: true + /object-is/1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} engines: {node: '>= 0.4'} @@ -10115,7 +20913,7 @@ packages: dev: true /object-visit/1.0.1: - resolution: {integrity: sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=} + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} engines: {node: '>=0.10.0'} dependencies: isobject: 3.0.1 @@ -10131,6 +20929,16 @@ packages: object-keys: 1.1.1 dev: true + /object.assign/4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + /object.entries/1.1.5: resolution: {integrity: sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==} engines: {node: '>= 0.4'} @@ -10154,8 +20962,8 @@ packages: engines: {node: '>= 0.8'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.3 - es-abstract: 1.19.1 + define-properties: 1.1.4 + es-abstract: 1.20.4 dev: true /object.hasown/1.1.0: @@ -10166,7 +20974,7 @@ packages: dev: true /object.pick/1.3.0: - resolution: {integrity: sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=} + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} engines: {node: '>=0.10.0'} dependencies: isobject: 3.0.1 @@ -10186,7 +20994,7 @@ packages: dev: true /on-finished/2.3.0: - resolution: {integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=} + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} engines: {node: '>= 0.8'} dependencies: ee-first: 1.1.1 @@ -10209,6 +21017,14 @@ packages: mimic-fn: 2.1.0 dev: true + /open/7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + /open/8.4.0: resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} engines: {node: '>=12'} @@ -10223,6 +21039,23 @@ packages: hasBin: true dev: true + /opn/5.5.0: + resolution: {integrity: sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==} + engines: {node: '>=4'} + dependencies: + is-wsl: 1.1.0 + dev: true + + /optimize-css-assets-webpack-plugin/5.0.8_webpack@4.46.0: + resolution: {integrity: sha512-mgFS1JdOtEGzD8l+EuISqL57cKO+We9GcoiQEmdCWRqqck+FGNmYJtx9qfAPzEz+lRrlThWMuGDaRkI/yWNx/Q==} + peerDependencies: + webpack: ^4.0.0 + dependencies: + cssnano: 4.1.11 + last-call-webpack-plugin: 3.0.0 + webpack: 4.46.0 + dev: true + /optimize-css-assets-webpack-plugin/6.0.1_webpack@4.46.0: resolution: {integrity: sha512-BshV2UZPfggZLdUfN3zFBbG4sl/DynUI+YCB6fRRDWaqO2OiWN8GPcp4Y0/fEV6B3k9Hzyk3czve3V/8B/SzKQ==} peerDependencies: @@ -10258,6 +21091,20 @@ packages: word-wrap: 1.2.3 dev: true + /ora/4.1.1: + resolution: {integrity: sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==} + engines: {node: '>=8'} + dependencies: + chalk: 3.0.0 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + is-interactive: 1.0.0 + log-symbols: 3.0.0 + mute-stream: 0.0.8 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + /ora/5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} @@ -10274,7 +21121,24 @@ packages: dev: true /os-browserify/0.3.0: - resolution: {integrity: sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=} + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + dev: true + + /os-homedir/1.0.2: + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} + dev: true + optional: true + + /overlayscrollbars/1.13.3: + resolution: {integrity: sha512-1nB/B5kaakJuHXaLXLRK0bUIilWhUGT6q5g+l2s5vqYdLle/sd0kscBHkQC1kuuDg9p9WR4MTdySDOPbeL/86g==} + dev: true + + /p-all/2.1.0: + resolution: {integrity: sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==} + engines: {node: '>=6'} + dependencies: + p-map: 2.1.0 dev: true /p-cancelable/1.1.0: @@ -10287,6 +21151,18 @@ packages: engines: {node: '>=4'} dev: true + /p-each-series/2.2.0: + resolution: {integrity: sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==} + engines: {node: '>=8'} + dev: true + + /p-event/4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: true + /p-event/5.0.1: resolution: {integrity: sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10294,6 +21170,18 @@ packages: p-timeout: 5.1.0 dev: true + /p-filter/2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + dependencies: + p-map: 2.1.0 + dev: true + + /p-finally/1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + dev: true + /p-limit/1.3.0: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} engines: {node: '>=4'} @@ -10357,6 +21245,11 @@ packages: p-limit: 4.0.0 dev: true + /p-map/2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + dev: true + /p-map/3.0.0: resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} engines: {node: '>=8'} @@ -10378,6 +21271,13 @@ packages: aggregate-error: 4.0.1 dev: true + /p-retry/3.0.1: + resolution: {integrity: sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==} + engines: {node: '>=6'} + dependencies: + retry: 0.12.0 + dev: true + /p-retry/4.6.1: resolution: {integrity: sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==} engines: {node: '>=8'} @@ -10386,13 +21286,20 @@ packages: retry: 0.13.1 dev: true + /p-timeout/3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: true + /p-timeout/5.1.0: resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} engines: {node: '>=12'} dev: true /p-try/1.0.0: - resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=} + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} engines: {node: '>=4'} dev: true @@ -10405,7 +21312,7 @@ packages: resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==} engines: {node: '>=8'} dependencies: - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 hasha: 5.2.2 lodash.flattendeep: 4.4.0 release-zalgo: 1.0.0 @@ -10434,11 +21341,18 @@ packages: dev: true /param-case/2.1.1: - resolution: {integrity: sha1-35T9jPZTHs915r75oIWPvHK+Ikc=} + resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} dependencies: no-case: 2.3.2 dev: true + /param-case/3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + dependencies: + dot-case: 3.0.4 + tslib: 2.4.0 + dev: true + /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -10456,8 +21370,27 @@ packages: safe-buffer: 5.2.1 dev: true + /parse-entities/2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + dev: true + + /parse-json/2.2.0: + resolution: {integrity: sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==} + engines: {node: '>=0.10.0'} + dependencies: + error-ex: 1.3.2 + dev: true + optional: true + /parse-json/4.0.0: - resolution: {integrity: sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=} + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} dependencies: error-ex: 1.3.2 @@ -10468,7 +21401,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.16.7 + '@babel/code-frame': 7.18.6 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -10479,6 +21412,13 @@ packages: engines: {node: '>=6'} dev: true + /parse5-htmlparser2-tree-adapter/7.0.0: + resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + dependencies: + domhandler: 5.0.3 + parse5: 7.1.1 + dev: true + /parse5/4.0.0: resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==} dev: true @@ -10487,13 +21427,30 @@ packages: resolution: {integrity: sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==} dev: true + /parse5/6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + dev: true + + /parse5/7.1.1: + resolution: {integrity: sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==} + dependencies: + entities: 4.4.0 + dev: true + /parseurl/1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} dev: true + /pascal-case/3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.4.0 + dev: true + /pascalcase/0.1.1: - resolution: {integrity: sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=} + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} engines: {node: '>=0.10.0'} dev: true @@ -10502,7 +21459,14 @@ packages: dev: true /path-dirname/1.0.2: - resolution: {integrity: sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=} + resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + dev: true + + /path-exists/2.1.0: + resolution: {integrity: sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==} + engines: {node: '>=0.10.0'} + dependencies: + pinkie-promise: 2.0.1 dev: true optional: true @@ -10525,6 +21489,15 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + /path-is-inside/1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + dev: true + + /path-key/2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + dev: true + /path-key/3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -10538,6 +21511,23 @@ packages: resolution: {integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=} dev: true + /path-type/1.1.0: + resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==} + engines: {node: '>=0.10.0'} + dependencies: + graceful-fs: 4.2.10 + pify: 2.3.0 + pinkie-promise: 2.0.1 + dev: true + optional: true + + /path-type/3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + dependencies: + pify: 3.0.0 + dev: true + /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -10558,7 +21548,7 @@ packages: dev: true /performance-now/2.1.0: - resolution: {integrity: sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=} + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} dev: true /picocolors/0.2.1: @@ -10574,11 +21564,38 @@ packages: engines: {node: '>=8.6'} dev: true + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + + /pify/3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + dev: true + /pify/4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} dev: true + /pinkie-promise/2.0.1: + resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} + engines: {node: '>=0.10.0'} + dependencies: + pinkie: 2.0.4 + dev: true + + /pinkie/2.0.4: + resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} + engines: {node: '>=0.10.0'} + dev: true + + /pirates/4.0.5: + resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} + engines: {node: '>= 6'} + dev: true + /pkg-conf/4.0.0: resolution: {integrity: sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10601,6 +21618,20 @@ packages: find-up: 4.1.0 dev: true + /pkg-dir/5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + dependencies: + find-up: 5.0.0 + dev: true + + /pkg-up/3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + dependencies: + find-up: 3.0.0 + dev: true + /plur/5.1.0: resolution: {integrity: sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10612,6 +21643,15 @@ packages: resolution: {integrity: sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==} dev: true + /pnp-webpack-plugin/1.6.4_typescript@4.8.4: + resolution: {integrity: sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==} + engines: {node: '>=6'} + dependencies: + ts-pnp: 1.2.0_typescript@4.8.4 + transitivePeerDependencies: + - typescript + dev: true + /pnp-webpack-plugin/1.7.0_typescript@4.2.4: resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==} engines: {node: '>=6'} @@ -10637,6 +21677,13 @@ packages: '@babel/runtime': 7.17.2 dev: true + /polished/4.2.2: + resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} + engines: {node: '>=10'} + dependencies: + '@babel/runtime': 7.17.8 + dev: true + /portfinder/1.0.28: resolution: {integrity: sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==} engines: {node: '>= 0.12.0'} @@ -10648,8 +21695,19 @@ packages: - supports-color dev: true + /portfinder/1.0.28_supports-color@6.1.0: + resolution: {integrity: sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==} + engines: {node: '>= 0.12.0'} + dependencies: + async: 2.6.3 + debug: 3.2.7_supports-color@6.1.0 + mkdirp: 0.5.5 + transitivePeerDependencies: + - supports-color + dev: true + /posix-character-classes/0.1.1: - resolution: {integrity: sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=} + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} engines: {node: '>=0.10.0'} dev: true @@ -10675,7 +21733,7 @@ packages: resolution: {integrity: sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 color: 3.2.1 has: 1.0.3 postcss: 7.0.39 @@ -10688,7 +21746,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 caniuse-api: 3.0.0 colord: 2.9.2 postcss: 8.4.6 @@ -10777,6 +21835,20 @@ packages: postcss: 8.4.6 dev: true + /postcss-flexbugs-fixes/4.2.1: + resolution: {integrity: sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ==} + dependencies: + postcss: 7.0.39 + dev: true + + /postcss-load-config/2.1.2: + resolution: {integrity: sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==} + engines: {node: '>= 4'} + dependencies: + cosmiconfig: 5.2.1 + import-cwd: 2.1.0 + dev: true + /postcss-load-config/3.1.2: resolution: {integrity: sha512-X1NVP1itP6VE5dDA4wR6NK1g9lNlkBx9A+tgDKb/8Mnx4HrvX6k+DcTXGelZvfp6p4zCBZjh4Gwyp4aptOUI9Q==} engines: {node: '>= 10'} @@ -10790,6 +21862,32 @@ packages: yaml: 1.10.2 dev: true + /postcss-loader/3.0.0: + resolution: {integrity: sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==} + engines: {node: '>= 6'} + dependencies: + loader-utils: 1.4.0 + postcss: 7.0.39 + postcss-load-config: 2.1.2 + schema-utils: 1.0.0 + dev: true + + /postcss-loader/4.3.0_gzaxsinx64nntyd3vmdqwl7coe: + resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} + engines: {node: '>= 10.13.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^4.0.0 || ^5.0.0 + dependencies: + cosmiconfig: 7.0.1 + klona: 2.0.5 + loader-utils: 2.0.2 + postcss: 7.0.39 + schema-utils: 3.1.1 + semver: 7.3.8 + webpack: 4.46.0 + dev: true + /postcss-loader/4.3.0_sa6x6oa3aqtj2o2n4wqcmgxr4e: resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} engines: {node: '>= 10.13.0'} @@ -10802,7 +21900,7 @@ packages: loader-utils: 2.0.2 postcss: 8.4.6 schema-utils: 3.1.1 - semver: 7.3.5 + semver: 7.3.8 webpack: 4.46.0 dev: true @@ -10831,7 +21929,7 @@ packages: resolution: {integrity: sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 caniuse-api: 3.0.0 cssnano-util-same-parent: 4.0.1 postcss: 7.0.39 @@ -10845,7 +21943,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 caniuse-api: 3.0.0 cssnano-utils: 3.0.2_postcss@8.4.6 postcss: 8.4.6 @@ -10897,7 +21995,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: alphanum-sort: 1.0.2 - browserslist: 4.19.1 + browserslist: 4.21.4 cssnano-util-get-arguments: 4.0.0 postcss: 7.0.39 postcss-value-parser: 3.3.1 @@ -10910,7 +22008,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 cssnano-utils: 3.0.2_postcss@8.4.6 postcss: 8.4.6 postcss-value-parser: 4.2.0 @@ -10936,6 +22034,13 @@ packages: postcss-selector-parser: 6.0.9 dev: true + /postcss-modules-extract-imports/2.0.0: + resolution: {integrity: sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==} + engines: {node: '>= 6'} + dependencies: + postcss: 7.0.39 + dev: true + /postcss-modules-extract-imports/3.0.0_postcss@8.4.6: resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} @@ -10945,6 +22050,16 @@ packages: postcss: 8.4.6 dev: true + /postcss-modules-local-by-default/3.0.3: + resolution: {integrity: sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==} + engines: {node: '>= 6'} + dependencies: + icss-utils: 4.1.1 + postcss: 7.0.39 + postcss-selector-parser: 6.0.9 + postcss-value-parser: 4.2.0 + dev: true + /postcss-modules-local-by-default/4.0.0_postcss@8.4.6: resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==} engines: {node: ^10 || ^12 || >= 14} @@ -10957,6 +22072,14 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-modules-scope/2.2.0: + resolution: {integrity: sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==} + engines: {node: '>= 6'} + dependencies: + postcss: 7.0.39 + postcss-selector-parser: 6.0.9 + dev: true + /postcss-modules-scope/3.0.0_postcss@8.4.6: resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} @@ -10967,6 +22090,13 @@ packages: postcss-selector-parser: 6.0.9 dev: true + /postcss-modules-values/3.0.0: + resolution: {integrity: sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==} + dependencies: + icss-utils: 4.1.1 + postcss: 7.0.39 + dev: true + /postcss-modules-values/4.0.0_postcss@8.4.6: resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} @@ -11094,7 +22224,7 @@ packages: resolution: {integrity: sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 postcss: 7.0.39 postcss-value-parser: 3.3.1 dev: true @@ -11105,7 +22235,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 postcss: 8.4.6 postcss-value-parser: 4.2.0 dev: true @@ -11173,7 +22303,7 @@ packages: resolution: {integrity: sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 caniuse-api: 3.0.0 has: 1.0.3 postcss: 7.0.39 @@ -11185,7 +22315,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 caniuse-api: 3.0.0 postcss: 8.4.6 dev: true @@ -11291,6 +22421,218 @@ packages: source-map-js: 1.0.2 dev: true + /preact-cli/3.0.5_ka7slmjrdyz4arupkjrkg4jzfm: + resolution: {integrity: sha512-Oc9HOjwX/3Zk1eXkmP7TMmtqbaROl7F0RWZ2Ni5Q/grmx3yBLJmarkUcOSKabkI/Usw2dU3RVju32Q3Pvy5qIw==} + engines: {node: '>=8'} + hasBin: true + peerDependencies: + preact: '*' + preact-render-to-string: '*' + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.17.2 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-transform-object-assign': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + '@preact/async-loader': 3.0.1_preact@10.6.5 + '@prefresh/webpack': 1.1.0_iaukxvobhnxulwhqqdnbfsnwxu + autoprefixer: 9.8.8 + babel-esm-plugin: 0.9.0_webpack@4.46.0 + babel-loader: 8.2.3_mc362qep5qjjhwk7q3m45kuizi + babel-plugin-macros: 2.8.0 + babel-plugin-transform-react-remove-prop-types: 0.4.24 + browserslist: 4.19.1 + compression-webpack-plugin: 4.0.1_webpack@4.46.0 + console-clear: 1.1.1 + copy-webpack-plugin: 5.1.2_webpack@4.46.0 + critters-webpack-plugin: 2.5.0_html-webpack-plugin@3.2.0 + cross-spawn-promise: 0.10.2 + css-loader: 3.6.0_webpack@4.46.0 + ejs-loader: 0.5.0 + envinfo: 7.8.1 + esm: 3.2.25 + fast-async: 6.3.8 + file-loader: 6.2.0_webpack@4.46.0 + fork-ts-checker-webpack-plugin: 4.1.6_3awoqomffoooecyduzzbrfpye4 + get-port: 5.1.1 + gittar: 0.1.1 + glob: 7.2.3 + html-webpack-exclude-assets-plugin: 0.0.7 + html-webpack-plugin: 3.2.0_webpack@4.46.0 + ip: 1.1.5 + isomorphic-unfetch: 3.1.0 + kleur: 4.1.4 + loader-utils: 2.0.2 + mini-css-extract-plugin: 0.9.0_webpack@4.46.0 + minimatch: 3.1.2 + native-url: 0.3.4 + optimize-css-assets-webpack-plugin: 5.0.8_webpack@4.46.0 + ora: 4.1.1 + postcss-load-config: 2.1.2 + postcss-loader: 3.0.0 + preact: 10.6.5 + preact-render-to-string: 5.1.19_preact@10.6.5 + progress-bar-webpack-plugin: 2.1.0_webpack@4.46.0 + promise-polyfill: 8.2.1 + prompts: 2.4.2 + raw-loader: 4.0.2_webpack@4.46.0 + react-refresh: 0.8.3 + require-relative: 0.8.7 + resolve-from: 5.0.0 + rimraf: 3.0.2 + sade: 1.8.1 + size-plugin: 2.0.2_webpack@4.46.0 + source-map: 0.7.3 + stack-trace: 0.0.10 + style-loader: 1.3.0_webpack@4.46.0 + terser-webpack-plugin: 3.1.0_webpack@4.46.0 + typescript: 3.9.10 + update-notifier: 4.1.3 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + validate-npm-package-name: 3.0.0 + webpack: 4.46.0 + webpack-bundle-analyzer: 3.9.0 + webpack-dev-server: 3.11.3_webpack@4.46.0 + webpack-fix-style-only-entries: 0.5.2 + webpack-merge: 4.2.2 + webpack-plugin-replace: 1.2.0 + which: 2.0.2 + workbox-cacheable-response: 5.1.4 + workbox-core: 5.1.4 + workbox-precaching: 5.1.4 + workbox-routing: 5.1.4 + workbox-strategies: 5.1.4 + workbox-webpack-plugin: 5.1.4_webpack@4.46.0 + transitivePeerDependencies: + - bluebird + - bufferutil + - debug + - encoding + - eslint + - supports-color + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /preact-cli/3.3.5_4lrkxlnsyltikzdya2zqvxqmyq: + resolution: {integrity: sha512-qtIk8WtheEoY192UoKFQD14cw5CoUnYs9A+gIL95H/WT4aw8Z4CvxsB6+eELMZnsruakkq7OQMd0xRrfyoZNgA==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + less-loader: ^7.3.0 + preact: '*' + preact-render-to-string: '*' + sass-loader: ^10.2.0 + stylus-loader: ^4.3.3 + peerDependenciesMeta: + less-loader: + optional: true + sass-loader: + optional: true + stylus-loader: + optional: true + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.17.2 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-transform-object-assign': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + '@preact/async-loader': 3.0.1_preact@10.6.1 + '@prefresh/babel-plugin': 0.4.1 + '@prefresh/webpack': 3.3.2_ev3yg7augojg2gbov5lykbl5qy + '@types/webpack': 4.41.32 + autoprefixer: 10.4.2_postcss@8.4.6 + babel-esm-plugin: 0.9.0_webpack@4.46.0 + babel-loader: 8.2.3_mc362qep5qjjhwk7q3m45kuizi + babel-plugin-macros: 3.1.0 + babel-plugin-transform-react-remove-prop-types: 0.4.24 + browserslist: 4.19.1 + compression-webpack-plugin: 6.1.1_webpack@4.46.0 + console-clear: 1.1.1 + copy-webpack-plugin: 6.4.1_webpack@4.46.0 + critters-webpack-plugin: 2.5.0_html-webpack-plugin@3.2.0 + cross-spawn-promise: 0.10.2 + css-loader: 5.2.7_webpack@4.46.0 + ejs-loader: 0.5.0 + envinfo: 7.8.1 + esm: 3.2.25 + file-loader: 6.2.0_webpack@4.46.0 + fork-ts-checker-webpack-plugin: 4.1.6_whfidqbq6inl26rhdbd2ot7yoa + get-port: 5.1.1 + gittar: 0.1.1 + glob: 7.2.0 + html-webpack-exclude-assets-plugin: 0.0.7 + html-webpack-plugin: 3.2.0_webpack@4.46.0 + ip: 1.1.5 + isomorphic-unfetch: 3.1.0 + kleur: 4.1.4 + loader-utils: 2.0.2 + mini-css-extract-plugin: 1.6.2_webpack@4.46.0 + minimatch: 3.0.5 + native-url: 0.3.4 + optimize-css-assets-webpack-plugin: 6.0.1_webpack@4.46.0 + ora: 5.4.1 + pnp-webpack-plugin: 1.7.0_typescript@4.2.4 + postcss: 8.4.6 + postcss-load-config: 3.1.2 + postcss-loader: 4.3.0_sa6x6oa3aqtj2o2n4wqcmgxr4e + preact: 10.6.1 + preact-render-to-string: 5.1.19_preact@10.6.1 + progress-bar-webpack-plugin: 2.1.0_webpack@4.46.0 + promise-polyfill: 8.2.1 + prompts: 2.4.2 + raw-loader: 4.0.2_webpack@4.46.0 + react-refresh: 0.10.0 + rimraf: 3.0.2 + sade: 1.8.1 + sass-loader: 10.1.1_sass@1.32.13 + size-plugin: 3.0.0_webpack@4.46.0 + source-map: 0.7.3 + stack-trace: 0.0.10 + style-loader: 2.0.0_webpack@4.46.0 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + typescript: 4.2.4 + update-notifier: 5.1.0 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + validate-npm-package-name: 3.0.0 + webpack: 4.46.0 + webpack-bundle-analyzer: 4.5.0 + webpack-dev-server: 4.7.4_webpack@4.46.0 + webpack-fix-style-only-entries: 0.6.1 + webpack-merge: 5.8.0 + webpack-plugin-replace: 1.2.0 + which: 2.0.2 + workbox-cacheable-response: 6.4.2 + workbox-core: 6.4.2 + workbox-precaching: 6.4.2 + workbox-routing: 6.4.2 + workbox-strategies: 6.4.2 + workbox-webpack-plugin: 6.4.2_webpack@4.46.0 + transitivePeerDependencies: + - '@types/babel__core' + - bluebird + - bufferutil + - debug + - encoding + - eslint + - supports-color + - ts-node + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + /preact-cli/3.3.5_mvcuzfcaha7xgb2vtvvy6e3v4a: resolution: {integrity: sha512-qtIk8WtheEoY192UoKFQD14cw5CoUnYs9A+gIL95H/WT4aw8Z4CvxsB6+eELMZnsruakkq7OQMd0xRrfyoZNgA==} engines: {node: '>=12'} @@ -11403,6 +22745,144 @@ packages: - webpack-command dev: true + /preact-cli/3.3.5_p6ocopta3zc4zccdbpyllaobd4: + resolution: {integrity: sha512-qtIk8WtheEoY192UoKFQD14cw5CoUnYs9A+gIL95H/WT4aw8Z4CvxsB6+eELMZnsruakkq7OQMd0xRrfyoZNgA==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + less-loader: ^7.3.0 + preact: '*' + preact-render-to-string: '*' + sass-loader: ^10.2.0 + stylus-loader: ^4.3.3 + peerDependenciesMeta: + less-loader: + optional: true + sass-loader: + optional: true + stylus-loader: + optional: true + dependencies: + '@babel/core': 7.17.2 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-proposal-decorators': 7.17.2_@babel+core@7.17.2 + '@babel/plugin-proposal-object-rest-spread': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.2 + '@babel/plugin-transform-object-assign': 7.16.7_@babel+core@7.17.2 + '@babel/plugin-transform-react-jsx': 7.16.7_@babel+core@7.17.2 + '@babel/preset-env': 7.16.11_@babel+core@7.17.2 + '@babel/preset-typescript': 7.16.7_@babel+core@7.17.2 + '@preact/async-loader': 3.0.1_preact@10.6.5 + '@prefresh/babel-plugin': 0.4.1 + '@prefresh/webpack': 3.3.2_dveknyjmyxkzkf4ybureeu5fae + '@types/webpack': 4.41.32 + autoprefixer: 10.4.2_postcss@8.4.6 + babel-esm-plugin: 0.9.0_webpack@4.46.0 + babel-loader: 8.2.3_mc362qep5qjjhwk7q3m45kuizi + babel-plugin-macros: 3.1.0 + babel-plugin-transform-react-remove-prop-types: 0.4.24 + browserslist: 4.19.1 + compression-webpack-plugin: 6.1.1_webpack@4.46.0 + console-clear: 1.1.1 + copy-webpack-plugin: 6.4.1_webpack@4.46.0 + critters-webpack-plugin: 2.5.0_html-webpack-plugin@3.2.0 + cross-spawn-promise: 0.10.2 + css-loader: 5.2.7_webpack@4.46.0 + ejs-loader: 0.5.0 + envinfo: 7.8.1 + esm: 3.2.25 + file-loader: 6.2.0_webpack@4.46.0 + fork-ts-checker-webpack-plugin: 4.1.6_whfidqbq6inl26rhdbd2ot7yoa + get-port: 5.1.1 + gittar: 0.1.1 + glob: 7.2.0 + html-webpack-exclude-assets-plugin: 0.0.7 + html-webpack-plugin: 3.2.0_webpack@4.46.0 + ip: 1.1.5 + isomorphic-unfetch: 3.1.0 + kleur: 4.1.4 + loader-utils: 2.0.2 + mini-css-extract-plugin: 1.6.2_webpack@4.46.0 + minimatch: 3.0.5 + native-url: 0.3.4 + optimize-css-assets-webpack-plugin: 6.0.1_webpack@4.46.0 + ora: 5.4.1 + pnp-webpack-plugin: 1.7.0_typescript@4.2.4 + postcss: 8.4.6 + postcss-load-config: 3.1.2 + postcss-loader: 4.3.0_sa6x6oa3aqtj2o2n4wqcmgxr4e + preact: 10.6.5 + preact-render-to-string: 5.1.19_preact@10.6.5 + progress-bar-webpack-plugin: 2.1.0_webpack@4.46.0 + promise-polyfill: 8.2.1 + prompts: 2.4.2 + raw-loader: 4.0.2_webpack@4.46.0 + react-refresh: 0.10.0 + rimraf: 3.0.2 + sade: 1.8.1 + sass-loader: 10.1.1_sass@1.32.13 + size-plugin: 3.0.0_webpack@4.46.0 + source-map: 0.7.3 + stack-trace: 0.0.10 + style-loader: 2.0.0_webpack@4.46.0 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + typescript: 4.2.4 + update-notifier: 5.1.0 + url-loader: 4.1.1_lit45vopotvaqup7lrvlnvtxwy + validate-npm-package-name: 3.0.0 + webpack: 4.46.0 + webpack-bundle-analyzer: 4.5.0 + webpack-dev-server: 4.7.4_webpack@4.46.0 + webpack-fix-style-only-entries: 0.6.1 + webpack-merge: 5.8.0 + webpack-plugin-replace: 1.2.0 + which: 2.0.2 + workbox-cacheable-response: 6.4.2 + workbox-core: 6.4.2 + workbox-precaching: 6.4.2 + workbox-routing: 6.4.2 + workbox-strategies: 6.4.2 + workbox-webpack-plugin: 6.4.2_webpack@4.46.0 + transitivePeerDependencies: + - '@types/babel__core' + - bluebird + - bufferutil + - debug + - encoding + - eslint + - supports-color + - ts-node + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + dev: true + + /preact-render-to-json/3.6.6_preact@10.6.1: + resolution: {integrity: sha512-w+guVnrKJMtSdAYKD3INih1O+XNEgJmj58r49KjjnwE1tLqzNXq1NaZXDg1DGhQ0fj67DYKdR9BqhogFI0lvsg==} + peerDependencies: + preact: '*' + dependencies: + preact: 10.6.1 + dev: true + + /preact-render-to-json/3.6.6_preact@10.6.5: + resolution: {integrity: sha512-w+guVnrKJMtSdAYKD3INih1O+XNEgJmj58r49KjjnwE1tLqzNXq1NaZXDg1DGhQ0fj67DYKdR9BqhogFI0lvsg==} + peerDependencies: + preact: '*' + dependencies: + preact: 10.6.5 + dev: true + + /preact-render-to-string/5.1.19_preact@10.6.1: + resolution: {integrity: sha512-bj8sn/oytIKO6RtOGSS/1+5CrQyRSC99eLUnEVbqUa6MzJX5dYh7wu9bmT0d6lm/Vea21k9KhCQwvr2sYN3rrQ==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.6.1 + pretty-format: 3.8.0 + dev: true + /preact-render-to-string/5.1.19_preact@10.6.5: resolution: {integrity: sha512-bj8sn/oytIKO6RtOGSS/1+5CrQyRSC99eLUnEVbqUa6MzJX5dYh7wu9bmT0d6lm/Vea21k9KhCQwvr2sYN3rrQ==} peerDependencies: @@ -11411,6 +22891,14 @@ packages: preact: 10.6.5 pretty-format: 3.8.0 + /preact-router/3.2.1_preact@10.6.1: + resolution: {integrity: sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.6.1 + dev: false + /preact-router/3.2.1_preact@10.6.5: resolution: {integrity: sha512-KEN2VN1DxUlTwzW5IFkF13YIA2OdQ2OvgJTkQREF+AA2NrHRLaGbB68EjS4IeZOa1shvQ1FvEm3bSLta4sXBhg==} peerDependencies: @@ -11419,11 +22907,14 @@ packages: preact: 10.6.5 dev: false + /preact/10.6.1: + resolution: {integrity: sha512-ydCg+ISIq70vqiThvNWStZWLRjR9U2awP/JAmGdWUKm9+Tyuy+MqVdAIyEByeIspAVtD4GWC/SJtxO8XD4knVA==} + /preact/10.6.5: resolution: {integrity: sha512-i+LXM6JiVjQXSt2jG2vZZFapGpCuk1fl8o6ii3G84MA3xgj686FKjs4JFDkmUVhtxyq21+4ay74zqPykz9hU6w==} /prelude-ls/1.1.2: - resolution: {integrity: sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=} + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} dev: true @@ -11432,11 +22923,22 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prepend-http/1.0.4: + resolution: {integrity: sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==} + engines: {node: '>=0.10.0'} + dev: true + /prepend-http/2.0.0: - resolution: {integrity: sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=} + resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==} engines: {node: '>=4'} dev: true + /prettier/2.2.1: + resolution: {integrity: sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /prettier/2.5.1: resolution: {integrity: sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==} engines: {node: '>=10.13.0'} @@ -11444,7 +22946,7 @@ packages: dev: true /pretty-bytes/4.0.2: - resolution: {integrity: sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=} + resolution: {integrity: sha512-yJAF+AjbHKlxQ8eezMd/34Mnj/YTQ3i6kLzvVsH4l/BfIFtp444n0wVbnsn66JimZ9uBofv815aRp1zCppxlWw==} engines: {node: '>=4'} dev: true @@ -11470,9 +22972,33 @@ packages: react-is: 17.0.2 dev: true + /pretty-format/27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + + /pretty-format/28.1.3: + resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/schemas': 28.1.3 + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /pretty-format/3.8.0: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + /pretty-hrtime/1.0.3: + resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} + engines: {node: '>= 0.8'} + dev: true + /pretty-ms/7.0.1: resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} engines: {node: '>=10'} @@ -11480,6 +23006,16 @@ packages: parse-ms: 2.1.0 dev: true + /prismjs/1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + dev: true + + /prismjs/1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + dev: true + /process-nextick-args/2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true @@ -11492,7 +23028,7 @@ packages: dev: true /process/0.11.10: - resolution: {integrity: sha1-czIwDoQBYb2j5podHZGn1LwW8YI=} + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} dev: true @@ -11511,8 +23047,17 @@ packages: engines: {node: '>=0.4.0'} dev: true + /promise-inflight/1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + dev: true + /promise-inflight/1.0.1_bluebird@3.7.2: - resolution: {integrity: sha1-mEcocL8igTL8vdhoEputEsPAKeM=} + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} peerDependencies: bluebird: '*' peerDependenciesMeta: @@ -11526,6 +23071,35 @@ packages: resolution: {integrity: sha512-3p9zj0cEHbp7NVUxEYUWjQlffXqnXaZIMPkAO7HhFh8u5636xLRDHOUo2vpWSK0T2mqm6fKLXYn1KP6PAZ2gKg==} dev: true + /promise.allsettled/1.0.5: + resolution: {integrity: sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ==} + engines: {node: '>= 0.4'} + dependencies: + array.prototype.map: 1.0.4 + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + get-intrinsic: 1.1.3 + iterate-value: 1.0.2 + dev: true + + /promise.prototype.finally/3.1.3: + resolution: {integrity: sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /prompts/2.4.0: + resolution: {integrity: sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + /prompts/2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -11542,6 +23116,16 @@ packages: react-is: 16.13.1 dev: true + /property-expr/2.0.5: + resolution: {integrity: sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==} + dev: false + + /property-information/5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + dependencies: + xtend: 4.0.2 + dev: true + /proxy-addr/2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -11551,11 +23135,11 @@ packages: dev: true /prr/1.0.1: - resolution: {integrity: sha1-0/wRS6BplaRexok/SEzrHXj19HY=} + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} dev: true /pseudomap/1.0.2: - resolution: {integrity: sha1-8FKijacOYYkX7wqKw0wa5aaChrM=} + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true /psl/1.8.0: @@ -11596,17 +23180,16 @@ packages: dev: true /punycode/1.3.2: - resolution: {integrity: sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=} + resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} dev: true /punycode/1.4.1: - resolution: {integrity: sha1-wNWmOycYgArY4esPpSachN1BhF4=} + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} dev: true /punycode/2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} - dev: true /pupa/2.1.1: resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} @@ -11616,7 +23199,7 @@ packages: dev: true /q/1.5.1: - resolution: {integrity: sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=} + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} engines: {node: '>=0.6.0', teleport: '>=0.2.0'} dev: true @@ -11630,6 +23213,13 @@ packages: resolution: {integrity: sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==} dev: false + /qs/6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: true + /qs/6.5.3: resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} engines: {node: '>=0.6'} @@ -11640,13 +23230,21 @@ packages: engines: {node: '>=0.6'} dev: true + /query-string/4.3.4: + resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==} + engines: {node: '>=0.10.0'} + dependencies: + object-assign: 4.1.1 + strict-uri-encode: 1.1.0 + dev: true + /querystring-es3/0.2.1: - resolution: {integrity: sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=} + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} engines: {node: '>=0.4.x'} dev: true /querystring/0.2.0: - resolution: {integrity: sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=} + resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} engines: {node: '>=0.4.x'} deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. dev: true @@ -11657,10 +23255,36 @@ packages: deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. dev: true + /querystringify/2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: true + /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /raf/3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + dependencies: + performance-now: 2.1.0 + dev: true + + /railroad-diagrams/1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + dev: true + + /ramda/0.28.0: + resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} + dev: true + + /randexp/0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + dependencies: + discontinuous-range: 1.0.0 + ret: 0.1.15 + dev: true + /randombytes/2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: @@ -11710,6 +23334,156 @@ packages: strip-json-comments: 2.0.1 dev: true + /react-colorful/5.6.1: + resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dev: true + + /react-colorful/5.6.1_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + dev: true + + /react-dev-utils/11.0.4_ef2lra3u3fsnrdrpybbvbgzate: + resolution: {integrity: sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==} + engines: {node: '>=10'} + peerDependencies: + typescript: '>=2.7' + webpack: '>=4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/code-frame': 7.10.4 + address: 1.1.2 + browserslist: 4.14.2 + chalk: 2.4.2 + cross-spawn: 7.0.3 + detect-port-alt: 1.1.6 + escape-string-regexp: 2.0.0 + filesize: 6.1.0 + find-up: 4.1.0 + fork-ts-checker-webpack-plugin: 4.1.6_ef2lra3u3fsnrdrpybbvbgzate + global-modules: 2.0.0 + globby: 11.0.1 + gzip-size: 5.1.1 + immer: 8.0.1 + is-root: 2.1.0 + loader-utils: 2.0.0 + open: 7.4.2 + pkg-up: 3.1.0 + prompts: 2.4.0 + react-error-overlay: 6.0.11 + recursive-readdir: 2.2.2 + shell-quote: 1.7.2 + strip-ansi: 6.0.0 + text-table: 0.2.0 + typescript: 4.8.4 + webpack: 4.46.0 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + dev: true + + /react-dom/16.14.0_react@16.14.0: + resolution: {integrity: sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==} + peerDependencies: + react: ^16.14.0 + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + prop-types: 15.8.1 + react: 16.14.0 + scheduler: 0.19.1 + dev: true + + /react-draggable/4.4.5: + resolution: {integrity: sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + dependencies: + clsx: 1.2.1 + prop-types: 15.8.1 + dev: true + + /react-draggable/4.4.5_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + dependencies: + clsx: 1.2.1 + prop-types: 15.8.1 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + dev: true + + /react-element-to-jsx-string/14.3.4: + resolution: {integrity: sha512-t4ZwvV6vwNxzujDQ+37bspnLwA4JlgUPWhLjBJWsNIDceAf6ZKUTCjdm08cN6WeZ5pTMKiCJkmAYnpmR4Bm+dg==} + peerDependencies: + react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 + react-dom: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 + dependencies: + '@base2/pretty-print-object': 1.0.1 + is-plain-object: 5.0.0 + react-is: 17.0.2 + dev: true + + /react-error-overlay/6.0.11: + resolution: {integrity: sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==} + dev: true + + /react-fast-compare/3.2.0: + resolution: {integrity: sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==} + dev: true + + /react-helmet-async/1.3.0: + resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.17.8 + invariant: 2.2.4 + prop-types: 15.8.1 + react-fast-compare: 3.2.0 + shallowequal: 1.1.0 + dev: true + + /react-helmet-async/1.3.0_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.17.8 + invariant: 2.2.4 + prop-types: 15.8.1 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-fast-compare: 3.2.0 + shallowequal: 1.1.0 + dev: true + + /react-inspector/5.1.1: + resolution: {integrity: sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg==} + peerDependencies: + react: ^16.8.4 || ^17.0.0 + dependencies: + '@babel/runtime': 7.17.8 + is-dom: 1.1.0 + prop-types: 15.8.1 + dev: true + /react-is/16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true @@ -11718,11 +23492,182 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true + /react-is/18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /react-lifecycles-compat/3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + dev: true + + /react-popper-tooltip/3.1.1: + resolution: {integrity: sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 + react-dom: ^16.6.0 || ^17.0.0 + dependencies: + '@babel/runtime': 7.17.8 + '@popperjs/core': 2.11.6 + react-popper: 2.3.0_@popperjs+core@2.11.6 + dev: true + + /react-popper-tooltip/3.1.1_wcqkhtmu7mswc6yz4uyexck3ty: + resolution: {integrity: sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 + react-dom: ^16.6.0 || ^17.0.0 + dependencies: + '@babel/runtime': 7.17.8 + '@popperjs/core': 2.11.6 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-popper: 2.3.0_ahsyp7paedy7ttpixxrzktxjdi + dev: true + + /react-popper/2.3.0_@popperjs+core@2.11.6: + resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} + peerDependencies: + '@popperjs/core': ^2.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + dependencies: + '@popperjs/core': 2.11.6 + react-fast-compare: 3.2.0 + warning: 4.0.3 + dev: true + + /react-popper/2.3.0_ahsyp7paedy7ttpixxrzktxjdi: + resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} + peerDependencies: + '@popperjs/core': ^2.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + dependencies: + '@popperjs/core': 2.11.6 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + react-fast-compare: 3.2.0 + warning: 4.0.3 + dev: true + /react-refresh/0.10.0: resolution: {integrity: sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==} engines: {node: '>=0.10.0'} dev: true + /react-refresh/0.8.3: + resolution: {integrity: sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==} + engines: {node: '>=0.10.0'} + dev: true + + /react-sizeme/3.0.2: + resolution: {integrity: sha512-xOIAOqqSSmKlKFJLO3inBQBdymzDuXx4iuwkNcJmC96jeiOg5ojByvL+g3MW9LPEsojLbC6pf68zOfobK8IPlw==} + dependencies: + element-resize-detector: 1.2.4 + invariant: 2.2.4 + shallowequal: 1.1.0 + throttle-debounce: 3.0.1 + dev: true + + /react-syntax-highlighter/13.5.3: + resolution: {integrity: sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg==} + peerDependencies: + react: '>= 0.14.0' + dependencies: + '@babel/runtime': 7.17.8 + highlight.js: 10.7.3 + lowlight: 1.20.0 + prismjs: 1.29.0 + refractor: 3.6.0 + dev: true + + /react-syntax-highlighter/13.5.3_react@16.14.0: + resolution: {integrity: sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg==} + peerDependencies: + react: '>= 0.14.0' + dependencies: + '@babel/runtime': 7.17.8 + highlight.js: 10.7.3 + lowlight: 1.20.0 + prismjs: 1.29.0 + react: 16.14.0 + refractor: 3.6.0 + dev: true + + /react-textarea-autosize/8.3.4: + resolution: {integrity: sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.17.8 + use-composed-ref: 1.3.0 + use-latest: 1.2.1 + transitivePeerDependencies: + - '@types/react' + dev: true + + /react-textarea-autosize/8.3.4_react@16.14.0: + resolution: {integrity: sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.17.8 + react: 16.14.0 + use-composed-ref: 1.3.0_react@16.14.0 + use-latest: 1.2.1_react@16.14.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /react/16.14.0: + resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + prop-types: 15.8.1 + dev: true + + /read-pkg-up/1.0.1: + resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==} + engines: {node: '>=0.10.0'} + dependencies: + find-up: 1.1.2 + read-pkg: 1.1.0 + dev: true + optional: true + + /read-pkg-up/7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg/1.1.0: + resolution: {integrity: sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==} + engines: {node: '>=0.10.0'} + dependencies: + load-json-file: 1.1.0 + normalize-package-data: 2.5.0 + path-type: 1.1.0 + dev: true + optional: true + + /read-pkg/5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.1 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + /readable-stream/2.3.7: resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} dependencies: @@ -11756,6 +23701,17 @@ packages: dev: true optional: true + /readdirp/2.2.1_supports-color@6.1.0: + resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} + engines: {node: '>=0.10'} + dependencies: + graceful-fs: 4.2.10 + micromatch: 3.1.10_supports-color@6.1.0 + readable-stream: 2.3.7 + transitivePeerDependencies: + - supports-color + dev: true + /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -11763,6 +23719,45 @@ packages: picomatch: 2.3.1 dev: true + /rechoir/0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + dependencies: + resolve: 1.22.1 + dev: true + + /recursive-readdir/2.2.2: + resolution: {integrity: sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==} + engines: {node: '>=0.10.0'} + dependencies: + minimatch: 3.0.4 + dev: true + + /redent/1.0.0: + resolution: {integrity: sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==} + engines: {node: '>=0.10.0'} + dependencies: + indent-string: 2.1.0 + strip-indent: 1.0.1 + dev: true + optional: true + + /redent/3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /refractor/3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + dev: true + /regenerate-unicode-properties/10.0.1: resolution: {integrity: sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==} engines: {node: '>=4'} @@ -11784,7 +23779,7 @@ packages: /regenerator-transform/0.14.5: resolution: {integrity: sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==} dependencies: - '@babel/runtime': 7.17.2 + '@babel/runtime': 7.17.8 dev: true /regex-not/1.0.2: @@ -11800,7 +23795,16 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.3 + define-properties: 1.1.4 + dev: true + + /regexp.prototype.flags/1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + functions-have-names: 1.2.3 dev: true /regexpp/3.2.0: @@ -11846,7 +23850,7 @@ packages: dev: true /relateurl/0.2.7: - resolution: {integrity: sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=} + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} dev: true @@ -11857,10 +23861,73 @@ packages: es6-error: 4.1.1 dev: true + /remark-external-links/8.0.0: + resolution: {integrity: sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==} + dependencies: + extend: 3.0.2 + is-absolute-url: 3.0.3 + mdast-util-definitions: 4.0.0 + space-separated-tokens: 1.1.5 + unist-util-visit: 2.0.3 + dev: true + + /remark-footnotes/2.0.0: + resolution: {integrity: sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==} + dev: true + + /remark-mdx/1.6.22: + resolution: {integrity: sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==} + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-proposal-object-rest-spread': 7.12.1_@babel+core@7.12.9 + '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.12.9 + '@mdx-js/util': 1.6.22 + is-alphabetical: 1.0.4 + remark-parse: 8.0.3 + unified: 9.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /remark-parse/8.0.3: + resolution: {integrity: sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==} + dependencies: + ccount: 1.1.0 + collapse-white-space: 1.0.6 + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + is-whitespace-character: 1.0.4 + is-word-character: 1.0.4 + markdown-escapes: 1.0.4 + parse-entities: 2.0.0 + repeat-string: 1.6.1 + state-toggle: 1.0.3 + trim: 0.0.1 + trim-trailing-lines: 1.1.4 + unherit: 1.1.3 + unist-util-remove-position: 2.0.1 + vfile-location: 3.2.0 + xtend: 4.0.2 + dev: true + + /remark-slug/6.1.0: + resolution: {integrity: sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==} + dependencies: + github-slugger: 1.4.0 + mdast-util-to-string: 1.1.0 + unist-util-visit: 2.0.3 + dev: true + + /remark-squeeze-paragraphs/4.0.0: + resolution: {integrity: sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==} + dependencies: + mdast-squeeze-paragraphs: 4.0.0 + dev: true + /remove-trailing-separator/1.1.0: - resolution: {integrity: sha1-wkvOKig62tW8P1jg1IJJuSN52O8=} + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} dev: true - optional: true /renderkid/2.0.7: resolution: {integrity: sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==} @@ -11878,10 +23945,18 @@ packages: dev: true /repeat-string/1.6.1: - resolution: {integrity: sha1-jcrkcOHIirwtYA//Sndihtp15jc=} + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} dev: true + /repeating/2.0.1: + resolution: {integrity: sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==} + engines: {node: '>=0.10.0'} + dependencies: + is-finite: 1.1.0 + dev: true + optional: true + /request-promise-core/1.1.4_request@2.88.2: resolution: {integrity: sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==} engines: {node: '>=0.10.0'} @@ -11922,7 +23997,7 @@ packages: is-typedarray: 1.0.0 isstream: 0.1.2 json-stringify-safe: 5.0.1 - mime-types: 2.1.34 + mime-types: 2.1.35 oauth-sign: 0.9.0 performance-now: 2.1.0 qs: 6.5.3 @@ -11946,8 +24021,19 @@ packages: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true + /require-relative/0.8.7: + resolution: {integrity: sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==} + dev: true + /requires-port/1.0.0: - resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=} + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: true + + /resolve-cwd/2.0.0: + resolution: {integrity: sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==} + engines: {node: '>=4'} + dependencies: + resolve-from: 3.0.0 dev: true /resolve-cwd/3.0.0: @@ -11958,7 +24044,7 @@ packages: dev: true /resolve-from/3.0.0: - resolution: {integrity: sha1-six699nWiBvItuZTM17rywoYh0g=} + resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} engines: {node: '>=4'} dev: true @@ -11977,10 +24063,15 @@ packages: dev: false /resolve-url/0.2.1: - resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=} + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} deprecated: https://github.com/lydell/resolve-url#deprecated dev: true + /resolve.exports/1.1.0: + resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} + engines: {node: '>=10'} + dev: true + /resolve/1.21.0: resolution: {integrity: sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==} hasBin: true @@ -11990,15 +24081,6 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /resolve/1.22.0: - resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} - hasBin: true - dependencies: - is-core-module: 2.10.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - /resolve/1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -12016,7 +24098,7 @@ packages: dev: true /responselike/1.0.2: - resolution: {integrity: sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=} + resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} dependencies: lowercase-keys: 1.0.1 dev: true @@ -12034,6 +24116,11 @@ packages: engines: {node: '>=0.12'} dev: true + /retry/0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: true + /retry/0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -12045,18 +24132,18 @@ packages: dev: true /rgb-regex/1.0.1: - resolution: {integrity: sha1-wODWiC3w4jviVKR16O3UGRX+rrE=} + resolution: {integrity: sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==} dev: true /rgba-regex/1.0.0: - resolution: {integrity: sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=} + resolution: {integrity: sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==} dev: true /rimraf/2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} hasBin: true dependencies: - glob: 7.2.0 + glob: 7.2.3 dev: true /rimraf/3.0.2: @@ -12073,6 +24160,36 @@ packages: inherits: 2.0.4 dev: true + /rollup-plugin-babel/4.4.0_n6u4oijhaodubo5gx2ecrhe56e: + resolution: {integrity: sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-babel. + peerDependencies: + '@babel/core': 7 || ^7.0.0-rc.2 + rollup: '>=0.60.0 <3' + dependencies: + '@babel/core': 7.19.6 + '@babel/helper-module-imports': 7.18.6 + rollup: 1.32.1 + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-bundle-html/0.2.2: + resolution: {integrity: sha512-nK4Z/k3MVjfCcnC5T15ksHw3JyRJx110oduy3VBW0ki2qI0tu4pLlgXyltBgtd+gpiFCPqEnfy89XRPG+eCOwA==} + dependencies: + cheerio: 1.0.0-rc.12 + hasha: 4.0.1 + dev: true + + /rollup-plugin-css-only/3.1.0_rollup@2.79.0: + resolution: {integrity: sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA==} + engines: {node: '>=10.12.0'} + peerDependencies: + rollup: 1 || 2 + dependencies: + '@rollup/pluginutils': 4.2.1 + rollup: 2.79.0 + dev: true + /rollup-plugin-sourcemaps/0.6.3_n3h7ooyjwm4phuvjpg4pqirc4i: resolution: {integrity: sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==} engines: {node: '>=10.0.0'} @@ -12089,6 +24206,19 @@ packages: source-map-resolve: 0.6.0 dev: true + /rollup-plugin-terser/5.3.1_rollup@1.32.1: + resolution: {integrity: sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w==} + peerDependencies: + rollup: '>=0.66.0 <3' + dependencies: + '@babel/code-frame': 7.18.6 + jest-worker: 24.9.0 + rollup: 1.32.1 + rollup-pluginutils: 2.8.2 + serialize-javascript: 4.0.0 + terser: 4.8.0 + dev: true + /rollup-plugin-terser/7.0.2_rollup@2.79.0: resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} peerDependencies: @@ -12101,6 +24231,21 @@ packages: terser: 5.10.0 dev: true + /rollup-pluginutils/2.8.2: + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + dependencies: + estree-walker: 0.6.1 + dev: true + + /rollup/1.32.1: + resolution: {integrity: sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==} + hasBin: true + dependencies: + '@types/estree': 1.0.0 + '@types/node': 18.8.5 + acorn: 7.4.1 + dev: true + /rollup/2.79.0: resolution: {integrity: sha512-x4KsrCgwQ7ZJPcFA/SUu6QVcYlO7uRLfLAy0DSA4NS2eG8japdbpM50ToH7z4iObodRYOJ0soneF0iaQRJ6zhA==} engines: {node: '>=10.0.0'} @@ -12109,6 +24254,18 @@ packages: fsevents: 2.3.2 dev: true + /rst-selector-parser/2.2.3: + resolution: {integrity: sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==} + dependencies: + lodash.flattendeep: 4.4.0 + nearley: 2.20.1 + dev: true + + /rsvp/4.8.5: + resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==} + engines: {node: 6.* || >= 7.*} + dev: true + /run-parallel/1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -12116,7 +24273,7 @@ packages: dev: true /run-queue/1.0.3: - resolution: {integrity: sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=} + resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==} dependencies: aproba: 1.2.0 dev: true @@ -12128,6 +24285,10 @@ packages: mri: 1.2.0 dev: true + /safe-buffer/5.1.1: + resolution: {integrity: sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==} + dev: true + /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} dev: true @@ -12136,8 +24297,16 @@ packages: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true + /safe-regex-test/1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + is-regex: 1.1.4 + dev: true + /safe-regex/1.1.0: - resolution: {integrity: sha1-QKNmnzsHfR6UPURinhV91IAjvy4=} + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} dependencies: ret: 0.1.15 dev: true @@ -12146,6 +24315,73 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /sane/4.1.0: + resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==} + engines: {node: 6.* || 8.* || >= 10.*} + deprecated: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added + hasBin: true + dependencies: + '@cnakazawa/watch': 1.0.4 + anymatch: 2.0.0 + capture-exit: 2.0.0 + exec-sh: 0.3.6 + execa: 1.0.0 + fb-watchman: 2.0.2 + micromatch: 3.1.10 + minimist: 1.2.5 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + dev: true + + /sass-loader/10.1.1_sass@1.32.13: + resolution: {integrity: sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 + sass: ^1.3.0 + webpack: ^4.36.0 || ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + dependencies: + klona: 2.0.5 + loader-utils: 2.0.2 + neo-async: 2.6.2 + sass: 1.32.13 + schema-utils: 3.1.1 + semver: 7.3.8 + dev: true + + /sass-loader/10.3.1_sass@1.32.13: + resolution: {integrity: sha512-y2aBdtYkbqorVavkC3fcJIUDGIegzDWPn3/LAFhsf3G+MzPKTJx37sROf5pXtUeggSVbNbmfj8TgRaSLMelXRA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + sass: ^1.3.0 + webpack: ^4.36.0 || ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + dependencies: + klona: 2.0.5 + loader-utils: 2.0.2 + neo-async: 2.6.2 + sass: 1.32.13 + schema-utils: 3.1.1 + semver: 7.3.8 + dev: true + /sass/1.32.13: resolution: {integrity: sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA==} engines: {node: '>=8.9.0'} @@ -12165,6 +24401,27 @@ packages: xmlchars: 2.2.0 dev: true + /saxes/5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + dependencies: + xmlchars: 2.2.0 + dev: true + + /scheduler/0.19.1: + resolution: {integrity: sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + dev: true + + /schema-utils/0.4.7: + resolution: {integrity: sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==} + engines: {node: '>= 4'} + dependencies: + ajv: 6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 + /schema-utils/1.0.0: resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==} engines: {node: '>= 4'} @@ -12174,11 +24431,20 @@ packages: ajv-keywords: 3.5.2_ajv@6.12.6 dev: true + /schema-utils/2.7.0: + resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} + engines: {node: '>= 8.9.0'} + dependencies: + '@types/json-schema': 7.0.11 + ajv: 6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 + dev: true + /schema-utils/2.7.1: resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} engines: {node: '>= 8.9.0'} dependencies: - '@types/json-schema': 7.0.9 + '@types/json-schema': 7.0.11 ajv: 6.12.6 ajv-keywords: 3.5.2_ajv@6.12.6 dev: true @@ -12187,7 +24453,7 @@ packages: resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.9 + '@types/json-schema': 7.0.11 ajv: 6.12.6 ajv-keywords: 3.5.2_ajv@6.12.6 dev: true @@ -12196,14 +24462,32 @@ packages: resolution: {integrity: sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==} engines: {node: '>= 12.13.0'} dependencies: - '@types/json-schema': 7.0.9 + '@types/json-schema': 7.0.11 ajv: 8.10.0 ajv-formats: 2.1.1 ajv-keywords: 5.1.0_ajv@8.10.0 dev: true + /script-ext-html-webpack-plugin/2.1.5: + resolution: {integrity: sha512-nMjd5dtsnoB8dS+pVM9ZL4mC9O1uVtTxrDS99OGZsZxFbkZE6pw0HCMued/cncDrKivIShO9vwoyOTvsGqQHEQ==} + engines: {node: '>=6.11.5'} + peerDependencies: + html-webpack-plugin: ^3.0.0 || ^4.0.0 + webpack: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /select-hose/2.0.0: - resolution: {integrity: sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=} + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + dev: true + + /selfsigned/1.10.14: + resolution: {integrity: sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==} + dependencies: + node-forge: 0.10.0 dev: true /selfsigned/2.0.0: @@ -12213,6 +24497,11 @@ packages: node-forge: 1.2.1 dev: true + /semiver/1.1.0: + resolution: {integrity: sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==} + engines: {node: '>=6'} + dev: true + /semver-diff/3.1.1: resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} engines: {node: '>=8'} @@ -12280,6 +24569,27 @@ packages: - supports-color dev: true + /send/0.17.2_supports-color@6.1.0: + resolution: {integrity: sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9_supports-color@6.1.0 + depd: 1.1.2 + destroy: 1.0.4 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 1.8.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.3.0 + range-parser: 1.2.1 + statuses: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: true + /serialize-error/7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} @@ -12305,8 +24615,19 @@ packages: randombytes: 2.1.0 dev: true + /serve-favicon/2.5.0: + resolution: {integrity: sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==} + engines: {node: '>= 0.8.0'} + dependencies: + etag: 1.8.1 + fresh: 0.5.2 + ms: 2.1.1 + parseurl: 1.3.3 + safe-buffer: 5.1.1 + dev: true + /serve-index/1.9.1: - resolution: {integrity: sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=} + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} engines: {node: '>= 0.8.0'} dependencies: accepts: 1.3.8 @@ -12314,7 +24635,22 @@ packages: debug: 2.6.9 escape-html: 1.0.3 http-errors: 1.6.3 - mime-types: 2.1.34 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /serve-index/1.9.1_supports-color@6.1.0: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9_supports-color@6.1.0 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 parseurl: 1.3.3 transitivePeerDependencies: - supports-color @@ -12332,6 +24668,18 @@ packages: - supports-color dev: true + /serve-static/1.14.2_supports-color@6.1.0: + resolution: {integrity: sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.17.2_supports-color@6.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /set-blocking/2.0.0: resolution: {integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc=} dev: true @@ -12347,7 +24695,7 @@ packages: dev: true /setimmediate/1.0.5: - resolution: {integrity: sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=} + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} dev: true /setprototypeof/1.1.0: @@ -12373,8 +24721,12 @@ packages: kind-of: 6.0.3 dev: true + /shallowequal/1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + dev: true + /shebang-command/1.2.0: - resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=} + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} dependencies: shebang-regex: 1.0.0 @@ -12388,7 +24740,7 @@ packages: dev: true /shebang-regex/1.0.0: - resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=} + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} engines: {node: '>=0.10.0'} dev: true @@ -12397,6 +24749,25 @@ packages: engines: {node: '>=8'} dev: true + /shell-quote/1.7.2: + resolution: {integrity: sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==} + dev: true + + /shelljs/0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + dev: true + + /shellwords/0.1.1: + resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==} + dev: true + optional: true + /shiki/0.11.1: resolution: {integrity: sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==} dependencies: @@ -12405,6 +24776,14 @@ packages: vscode-textmate: 6.0.0 dev: true + /shiki/0.9.15: + resolution: {integrity: sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==} + dependencies: + jsonc-parser: 3.2.0 + vscode-oniguruma: 1.6.2 + vscode-textmate: 5.2.0 + dev: true + /side-channel/1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: @@ -12422,11 +24801,26 @@ packages: dev: true /simple-swizzle/0.2.2: - resolution: {integrity: sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=} + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: is-arrayish: 0.3.2 dev: true + /sirv-cli/1.0.14: + resolution: {integrity: sha512-yyUTNr984ANKDloqepkYbBSqvx3buwYg2sQKPWjSU+IBia5loaoka2If8N9CMwt8AfP179cdEl7kYJ//iWJHjQ==} + engines: {node: '>= 10'} + hasBin: true + dependencies: + console-clear: 1.1.1 + get-port: 3.2.0 + kleur: 3.0.3 + local-access: 1.1.0 + sade: 1.8.1 + semiver: 1.1.0 + sirv: 1.0.19 + tinydate: 1.3.0 + dev: true + /sirv/1.0.19: resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==} engines: {node: '>= 10'} @@ -12440,6 +24834,25 @@ packages: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true + /size-plugin/2.0.2_webpack@4.46.0: + resolution: {integrity: sha512-pnPH6XX3TcdXTk6qsaKI6pXuOuKCpepJFTj96vjHcW/mY2zd1LhqNu7qsdkMnZS1LnA+PHaJ2C+uNveg32Loqg==} + peerDependencies: + webpack: '*' + dependencies: + axios: 0.21.4 + chalk: 2.4.2 + ci-env: 1.17.0 + escape-string-regexp: 1.0.5 + glob: 7.2.3 + gzip-size: 5.1.1 + minimatch: 3.1.2 + pretty-bytes: 5.6.0 + util.promisify: 1.1.1 + webpack: 4.46.0 + transitivePeerDependencies: + - debug + dev: true + /size-plugin/3.0.0_webpack@4.46.0: resolution: {integrity: sha512-RPMSkgbvmS1e5XUwPNFZre7DLqcK9MhWARIm8UmGLgbUCAs173JLI6DPHco68wvo0cUdft8+GGRaJtNl5RWfew==} peerDependencies: @@ -12449,8 +24862,8 @@ packages: chalk: 2.4.2 ci-env: 1.17.0 escape-string-regexp: 1.0.5 - glob: 7.2.0 - minimatch: 3.0.5 + glob: 7.2.3 + minimatch: 3.1.2 pretty-bytes: 5.6.0 util.promisify: 1.1.1 webpack: 4.46.0 @@ -12458,6 +24871,16 @@ packages: - debug dev: true + /slash/1.0.0: + resolution: {integrity: sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==} + engines: {node: '>=0.10.0'} + dev: true + + /slash/2.0.0: + resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} + engines: {node: '>=6'} + dev: true + /slash/3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -12468,6 +24891,15 @@ packages: engines: {node: '>=12'} dev: true + /slice-ansi/4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + /slice-ansi/5.0.0: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} @@ -12508,6 +24940,35 @@ packages: - supports-color dev: true + /snapdragon/0.8.2_supports-color@6.1.0: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + dependencies: + base: 0.11.2 + debug: 2.6.9_supports-color@6.1.0 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /sockjs-client/1.6.1_supports-color@6.1.0: + resolution: {integrity: sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==} + engines: {node: '>=12'} + dependencies: + debug: 3.2.7_supports-color@6.1.0 + eventsource: 2.0.2 + faye-websocket: 0.11.4 + inherits: 2.0.4 + url-parse: 1.5.10 + transitivePeerDependencies: + - supports-color + dev: true + /sockjs/0.3.24: resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} dependencies: @@ -12516,6 +24977,13 @@ packages: websocket-driver: 0.7.4 dev: true + /sort-keys/1.1.2: + resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} + engines: {node: '>=0.10.0'} + dependencies: + is-plain-obj: 1.1.0 + dev: true + /source-list-map/2.0.1: resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} dev: true @@ -12580,6 +25048,10 @@ packages: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} dev: true + /space-separated-tokens/1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + dev: true + /spawn-wrap/2.0.0: resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==} engines: {node: '>=8'} @@ -12592,10 +25064,45 @@ packages: which: 2.0.2 dev: true + /spdx-correct/3.1.1: + resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.12 + dev: true + + /spdx-exceptions/2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse/3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.12 + dev: true + + /spdx-license-ids/3.0.12: + resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} + dev: true + /spdy-transport/3.0.0: resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} dependencies: - debug: 4.3.3 + debug: 4.3.4 + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.0 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + dev: true + + /spdy-transport/3.0.0_supports-color@6.1.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + dependencies: + debug: 4.3.4_supports-color@6.1.0 detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -12609,7 +25116,7 @@ packages: resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} engines: {node: '>=6.0.0'} dependencies: - debug: 4.3.3 + debug: 4.3.4 handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -12618,6 +25125,19 @@ packages: - supports-color dev: true + /spdy/4.0.2_supports-color@6.1.0: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + dependencies: + debug: 4.3.4_supports-color@6.1.0 + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0_supports-color@6.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /split-string/3.1.0: resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} engines: {node: '>=0.10.0'} @@ -12626,7 +25146,7 @@ packages: dev: true /sprintf-js/1.0.3: - resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=} + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true /sshpk/1.17.0: @@ -12660,6 +25180,7 @@ packages: /stable/0.1.8: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' dev: true /stack-trace/0.0.10: @@ -12673,8 +25194,12 @@ packages: escape-string-regexp: 2.0.0 dev: true + /state-toggle/1.0.3: + resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==} + dev: true + /static-extend/0.1.2: - resolution: {integrity: sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=} + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} engines: {node: '>=0.10.0'} dependencies: define-property: 0.2.5 @@ -12682,15 +25207,19 @@ packages: dev: true /statuses/1.5.0: - resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=} + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} dev: true /stealthy-require/1.1.1: - resolution: {integrity: sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=} + resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==} engines: {node: '>=0.10.0'} dev: true + /store2/2.14.2: + resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} + dev: true + /stream-browserify/2.0.2: resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==} dependencies: @@ -12719,6 +25248,45 @@ packages: resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} dev: true + /strict-uri-encode/1.1.0: + resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} + engines: {node: '>=0.10.0'} + dev: true + + /string-length/4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-length/5.0.1: + resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} + engines: {node: '>=12.20'} + dependencies: + char-regex: 2.0.1 + strip-ansi: 7.0.1 + dev: true + + /string-width/1.0.2: + resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} + engines: {node: '>=0.10.0'} + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + dev: true + + /string-width/3.1.0: + resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} + engines: {node: '>=6'} + dependencies: + emoji-regex: 7.0.3 + is-fullwidth-code-point: 2.0.0 + strip-ansi: 5.2.0 + dev: true + /string-width/4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -12750,18 +25318,61 @@ packages: side-channel: 1.0.4 dev: true + /string.prototype.padend/3.1.3: + resolution: {integrity: sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /string.prototype.padstart/3.1.3: + resolution: {integrity: sha512-NZydyOMtYxpTjGqp0VN5PYUF/tsU15yDMZnUdj16qRUIUiMJkHHSDElYyQFrMu+/WloTpA7MQSiADhBicDfaoA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /string.prototype.trim/1.2.6: + resolution: {integrity: sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + /string.prototype.trimend/1.0.4: resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.3 + define-properties: 1.1.4 + dev: true + + /string.prototype.trimend/1.0.5: + resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 dev: true /string.prototype.trimstart/1.0.4: resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.3 + define-properties: 1.1.4 + dev: true + + /string.prototype.trimstart/1.0.5: + resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 dev: true /string_decoder/1.1.1: @@ -12786,18 +25397,32 @@ packages: dev: true /strip-ansi/0.1.1: - resolution: {integrity: sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=} + resolution: {integrity: sha512-behete+3uqxecWlDAm5lmskaSaISA+ThQ4oNNBDTBJt0x2ppR6IPqfZNuj6BLaLJ/Sji4TPZlcRyOis8wXQTLg==} engines: {node: '>=0.8.0'} hasBin: true dev: true /strip-ansi/3.0.1: - resolution: {integrity: sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=} + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} engines: {node: '>=0.10.0'} dependencies: ansi-regex: 2.1.1 dev: true + /strip-ansi/5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + dependencies: + ansi-regex: 4.1.1 + dev: true + + /strip-ansi/6.0.0: + resolution: {integrity: sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -12812,6 +25437,14 @@ packages: ansi-regex: 6.0.1 dev: true + /strip-bom/2.0.0: + resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} + engines: {node: '>=0.10.0'} + dependencies: + is-utf8: 0.2.1 + dev: true + optional: true + /strip-bom/3.0.0: resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} engines: {node: '>=4'} @@ -12822,18 +25455,47 @@ packages: engines: {node: '>=8'} dev: true + /strip-comments/1.0.2: + resolution: {integrity: sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==} + engines: {node: '>=4'} + dependencies: + babel-extract-comments: 1.0.0 + babel-plugin-transform-object-rest-spread: 6.26.0 + dev: true + /strip-comments/2.0.1: resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} engines: {node: '>=10'} dev: true + /strip-eof/1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + dev: true + /strip-final-newline/2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} dev: true + /strip-indent/1.0.1: + resolution: {integrity: sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + get-stdin: 4.0.1 + dev: true + optional: true + + /strip-indent/3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + /strip-json-comments/2.0.1: - resolution: {integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo=} + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} dev: true @@ -12842,6 +25504,17 @@ packages: engines: {node: '>=8'} dev: true + /style-loader/1.3.0_webpack@4.46.0: + resolution: {integrity: sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==} + engines: {node: '>= 8.9.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + loader-utils: 2.0.2 + schema-utils: 2.7.1 + webpack: 4.46.0 + dev: true + /style-loader/2.0.0_webpack@4.46.0: resolution: {integrity: sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==} engines: {node: '>= 10.13.0'} @@ -12853,11 +25526,17 @@ packages: webpack: 4.46.0 dev: true + /style-to-object/0.3.0: + resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==} + dependencies: + inline-style-parser: 0.1.1 + dev: true + /stylehacks/4.0.3: resolution: {integrity: sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 postcss: 7.0.39 postcss-selector-parser: 3.1.2 dev: true @@ -12868,7 +25547,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.1 + browserslist: 4.21.4 postcss: 8.4.6 postcss-selector-parser: 6.0.9 dev: true @@ -12894,6 +25573,13 @@ packages: has-flag: 3.0.0 dev: true + /supports-color/6.1.0: + resolution: {integrity: sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==} + engines: {node: '>=6'} + dependencies: + has-flag: 3.0.0 + dev: true + /supports-color/7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -12908,6 +25594,14 @@ packages: has-flag: 4.0.0 dev: true + /supports-hyperlinks/2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + dev: true + /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -12948,10 +25642,55 @@ packages: stable: 0.1.8 dev: true + /swr/0.5.7: + resolution: {integrity: sha512-Jh1Efgu8nWZV9rU4VLUMzBzcwaZgi4znqbVXvAtUy/0JzSiN6bNjLaJK8vhY/Rtp7a83dosz5YuehfBNwC/ZoQ==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 + dependencies: + dequal: 2.0.2 + dev: false + + /swr/1.1.0: + resolution: {integrity: sha512-MFL3mkl752Uap81nLA1tEu7vQmikPamSziW+6dBidYKAo4oLOlUx/x5GZy4ZCkCwfZe2uedylkz1UMGnatUX4g==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + dev: false + + /swr/1.1.2: + resolution: {integrity: sha512-UsM0eo5T+kRPyWFZtWRx2XR5qzohs/LS4lDC0GCyLpCYFmsfTk28UCVDbOE9+KtoXY4FnwHYiF+ZYEU3hnJ1lQ==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + dev: false + /symbol-tree/3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true + /symbol.prototype.description/1.0.5: + resolution: {integrity: sha512-x738iXRYsrAt9WBhRCVG5BtIC3B7CUkFwbHW2zOvGtwM33s7JjrCDyq8V0zgMYVb5ymsL8+qkzzpANH63CPQaQ==} + engines: {node: '>= 0.11.15'} + dependencies: + call-bind: 1.0.2 + get-symbol-description: 1.0.0 + has-symbols: 1.0.3 + object.getownpropertydescriptors: 2.1.3 + dev: true + + /synchronous-promise/2.0.16: + resolution: {integrity: sha512-qImOD23aDfnIDNqlG1NOehdB9IYsn1V9oByPjKY1nakv2MQYCEMyX033/q+aEtYCpmYK1cv2+NTmlH+ra6GA5A==} + dev: true + + /table/6.8.0: + resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==} + engines: {node: '>=10.0.0'} + dependencies: + ajv: 8.10.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + /tapable/1.1.3: resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} engines: {node: '>=6'} @@ -12987,11 +25726,51 @@ packages: yallist: 4.0.0 dev: true + /telejson/5.3.3: + resolution: {integrity: sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA==} + dependencies: + '@types/is-function': 1.0.1 + global: 4.4.0 + is-function: 1.0.2 + is-regex: 1.1.4 + is-symbol: 1.0.4 + isobject: 4.0.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + dev: true + + /telejson/6.0.8: + resolution: {integrity: sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg==} + dependencies: + '@types/is-function': 1.0.1 + global: 4.4.0 + is-function: 1.0.2 + is-regex: 1.1.4 + is-symbol: 1.0.4 + isobject: 4.0.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + dev: true + + /temp-dir/1.0.0: + resolution: {integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==} + engines: {node: '>=4'} + dev: true + /temp-dir/2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} dev: true + /tempy/0.3.0: + resolution: {integrity: sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ==} + engines: {node: '>=8'} + dependencies: + temp-dir: 1.0.0 + type-fest: 0.3.1 + unique-string: 1.0.0 + dev: true + /tempy/0.6.0: resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} engines: {node: '>=10'} @@ -13002,6 +25781,19 @@ packages: unique-string: 2.0.0 dev: true + /term-size/2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: true + + /terminal-link/2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + dev: true + /terser-webpack-plugin/1.4.5_webpack@4.46.0: resolution: {integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==} engines: {node: '>= 6.9.0'} @@ -13020,6 +25812,26 @@ packages: worker-farm: 1.7.0 dev: true + /terser-webpack-plugin/3.1.0_webpack@4.46.0: + resolution: {integrity: sha512-cjdZte66fYkZ65rQ2oJfrdCAkkhJA7YLYk5eGOcGCSGlq0ieZupRdjedSQXYknMPo2IveQL+tPdrxUkERENCFA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + cacache: 15.3.0 + find-cache-dir: 3.3.2 + jest-worker: 26.6.2 + p-limit: 3.1.0 + schema-utils: 2.7.1 + serialize-javascript: 4.0.0 + source-map: 0.6.1 + terser: 4.8.0 + webpack: 4.46.0 + webpack-sources: 1.4.3 + transitivePeerDependencies: + - bluebird + dev: true + /terser-webpack-plugin/4.2.3_webpack@4.46.0: resolution: {integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==} engines: {node: '>= 10.13.0'} @@ -13045,7 +25857,7 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - acorn: 8.7.0 + acorn: 8.8.0 commander: 2.20.3 source-map: 0.6.1 source-map-support: 0.5.21 @@ -13070,7 +25882,7 @@ packages: engines: {node: '>=8'} dependencies: '@istanbuljs/schema': 0.1.3 - glob: 7.2.0 + glob: 7.2.3 minimatch: 3.1.2 dev: true @@ -13078,6 +25890,19 @@ packages: resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} dev: true + /throat/5.0.0: + resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} + dev: true + + /throat/6.0.1: + resolution: {integrity: sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==} + dev: true + + /throttle-debounce/3.0.1: + resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} + engines: {node: '>=10'} + dev: true + /through2/2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} dependencies: @@ -13102,7 +25927,7 @@ packages: dev: true /timsort/0.3.0: - resolution: {integrity: sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=} + resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} dev: true /tiny-invariant/1.1.0: @@ -13113,22 +25938,31 @@ packages: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} dev: false + /tinydate/1.3.0: + resolution: {integrity: sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==} + engines: {node: '>=4'} + dev: true + + /tmpl/1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + /to-arraybuffer/1.0.1: - resolution: {integrity: sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=} + resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} dev: true /to-fast-properties/1.0.3: - resolution: {integrity: sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=} + resolution: {integrity: sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==} engines: {node: '>=0.10.0'} dev: true /to-fast-properties/2.0.0: - resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=} + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} dev: true /to-object-path/0.3.0: - resolution: {integrity: sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=} + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} engines: {node: '>=0.10.0'} dependencies: kind-of: 3.2.2 @@ -13140,7 +25974,7 @@ packages: dev: true /to-regex-range/2.1.1: - resolution: {integrity: sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=} + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} engines: {node: '>=0.10.0'} dependencies: is-number: 3.0.0 @@ -13164,15 +25998,23 @@ packages: safe-regex: 1.1.0 dev: true + /toggle-selection/1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + dev: true + /toidentifier/1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} dev: true /toposort/1.0.7: - resolution: {integrity: sha1-LmhELZ9k7HILjMieZEOsbKqVACk=} + resolution: {integrity: sha512-FclLrw8b9bMWf4QlCJuHBEVhSRsqDj6u3nIjAzPeJvgl//1hBlffdlk0MALceL14+koWEdU4ofRAXofbODxQzg==} dev: true + /toposort/2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + dev: false + /totalist/1.1.0: resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==} engines: {node: '>=6'} @@ -13186,20 +26028,71 @@ packages: punycode: 2.1.1 dev: true + /tough-cookie/4.1.2: + resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} + engines: {node: '>=6'} + dependencies: + psl: 1.8.0 + punycode: 2.1.1 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + /tr46/0.0.3: resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=} /tr46/1.0.1: - resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=} + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + dependencies: + punycode: 2.1.1 + dev: true + + /tr46/2.1.0: + resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} + engines: {node: '>=8'} dependencies: punycode: 2.1.1 dev: true + /trim-newlines/1.0.0: + resolution: {integrity: sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==} + engines: {node: '>=0.10.0'} + dev: true + optional: true + + /trim-trailing-lines/1.1.4: + resolution: {integrity: sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==} + dev: true + + /trim/0.0.1: + resolution: {integrity: sha1-WFhUf2spB1fulczMZm+1AITEYN0=} + dev: true + + /trough/1.0.5: + resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} + dev: true + + /tryer/1.0.1: + resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} + dev: true + + /ts-dedent/2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + dev: true + + /ts-invariant/0.10.3: + resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} + engines: {node: '>=8'} + dependencies: + tslib: 2.4.0 + dev: true + /ts-invariant/0.9.4: resolution: {integrity: sha512-63jtX/ZSwnUNi/WhXjnK8kz4cHHpYS60AnmA6ixz17l7E12a5puCWFlNpkne5Rl0J8TBPVHpGjsj4fxs8ObVLQ==} engines: {node: '>=8'} dependencies: - tslib: 2.3.1 + tslib: 2.4.0 dev: true /ts-pnp/1.2.0_typescript@4.2.4: @@ -13214,6 +26107,18 @@ packages: typescript: 4.2.4 dev: true + /ts-pnp/1.2.0_typescript@4.8.4: + resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} + engines: {node: '>=6'} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 4.8.4 + dev: true + /tsconfig-paths/3.12.0: resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==} dependencies: @@ -13227,13 +26132,8 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib/2.3.1: - resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} - dev: true - /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - dev: false /tsutils/3.21.0_typescript@4.8.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -13250,17 +26150,17 @@ packages: dev: true /tunnel-agent/0.6.0: - resolution: {integrity: sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=} + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 dev: true /tweetnacl/0.14.5: - resolution: {integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=} + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} dev: true /type-check/0.3.2: - resolution: {integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=} + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} dependencies: prelude-ls: 1.1.2 @@ -13292,6 +26192,21 @@ packages: engines: {node: '>=10'} dev: true + /type-fest/0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.3.1: + resolution: {integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==} + engines: {node: '>=6'} + dev: true + + /type-fest/0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + /type-fest/0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -13302,7 +26217,7 @@ packages: engines: {node: '>= 0.6'} dependencies: media-typer: 0.3.0 - mime-types: 2.1.34 + mime-types: 2.1.35 dev: true /typedarray-to-buffer/3.1.5: @@ -13312,7 +26227,33 @@ packages: dev: true /typedarray/0.0.6: - resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=} + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + dev: true + + /typedoc-default-themes/0.12.10: + resolution: {integrity: sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==} + engines: {node: '>= 8'} + dev: true + + /typedoc/0.20.37_typescript@4.8.4: + resolution: {integrity: sha512-9+qDhdc4X00qTNOtii6QX2z7ndAeWVOso7w3MPSoSJdXlVhpwPfm1yEp4ooKuWA9fiQILR8FKkyjmeqa13hBbw==} + engines: {node: '>= 10.8.0'} + hasBin: true + peerDependencies: + typescript: 3.9.x || 4.0.x || 4.1.x || 4.2.x + dependencies: + colors: 1.4.0 + fs-extra: 9.1.0 + handlebars: 4.7.7 + lodash: 4.17.21 + lunr: 2.3.9 + marked: 2.0.7 + minimatch: 3.1.2 + progress: 2.0.3 + shelljs: 0.8.5 + shiki: 0.9.15 + typedoc-default-themes: 0.12.10 + typescript: 4.8.4 dev: true /typedoc/0.23.16_typescript@4.8.4: @@ -13329,6 +26270,12 @@ packages: typescript: 4.8.4 dev: true + /typescript/3.9.10: + resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + /typescript/4.2.4: resolution: {integrity: sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==} engines: {node: '>=4.2.0'} @@ -13355,7 +26302,16 @@ packages: dependencies: function-bind: 1.1.1 has-bigints: 1.0.1 - has-symbols: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /unbox-primitive/1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 dev: true @@ -13367,6 +26323,13 @@ packages: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} dev: true + /unherit/1.1.3: + resolution: {integrity: sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==} + dependencies: + inherits: 2.0.4 + xtend: 4.0.2 + dev: true + /unicode-canonical-property-names-ecmascript/2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -13390,6 +26353,18 @@ packages: engines: {node: '>=4'} dev: true + /unified/9.2.0: + resolution: {integrity: sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==} + dependencies: + '@types/unist': 2.0.6 + bail: 1.0.5 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 2.1.0 + trough: 1.0.5 + vfile: 4.2.1 + dev: true + /union-value/1.0.1: resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} engines: {node: '>=0.10.0'} @@ -13401,11 +26376,11 @@ packages: dev: true /uniq/1.0.1: - resolution: {integrity: sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=} + resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==} dev: true /uniqs/2.0.0: - resolution: {integrity: sha1-/+3ks2slKQaW5uFl1KWe25mOawI=} + resolution: {integrity: sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==} dev: true /unique-filename/1.1.1: @@ -13420,6 +26395,13 @@ packages: imurmurhash: 0.1.4 dev: true + /unique-string/1.0.0: + resolution: {integrity: sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==} + engines: {node: '>=4'} + dependencies: + crypto-random-string: 1.0.0 + dev: true + /unique-string/2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -13427,38 +26409,130 @@ packages: crypto-random-string: 2.0.0 dev: true + /unist-builder/2.0.3: + resolution: {integrity: sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==} + dev: true + + /unist-util-generated/1.1.6: + resolution: {integrity: sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==} + dev: true + + /unist-util-is/4.1.0: + resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + dev: true + + /unist-util-position/3.1.0: + resolution: {integrity: sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==} + dev: true + + /unist-util-remove-position/2.0.1: + resolution: {integrity: sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==} + dependencies: + unist-util-visit: 2.0.3 + dev: true + + /unist-util-remove/2.1.0: + resolution: {integrity: sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==} + dependencies: + unist-util-is: 4.1.0 + dev: true + + /unist-util-stringify-position/2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + dependencies: + '@types/unist': 2.0.6 + dev: true + + /unist-util-visit-parents/3.1.1: + resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + dependencies: + '@types/unist': 2.0.6 + unist-util-is: 4.1.0 + dev: true + + /unist-util-visit/2.0.3: + resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} + dependencies: + '@types/unist': 2.0.6 + unist-util-is: 4.1.0 + unist-util-visit-parents: 3.1.1 + dev: true + /universalify/0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} dev: true + /universalify/0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} dev: true /unpipe/1.0.0: - resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=} + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} dev: true /unquote/1.1.1: - resolution: {integrity: sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=} + resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==} dev: true /unset-value/1.0.0: - resolution: {integrity: sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=} + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} engines: {node: '>=0.10.0'} dependencies: has-value: 0.3.1 isobject: 3.0.1 dev: true + /untildify/2.1.0: + resolution: {integrity: sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig==} + engines: {node: '>=0.10.0'} + dependencies: + os-homedir: 1.0.2 + dev: true + optional: true + /upath/1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} dev: true + /update-browserslist-db/1.0.10_browserslist@4.21.4: + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.4 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /update-notifier/4.1.3: + resolution: {integrity: sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==} + engines: {node: '>=8'} + dependencies: + boxen: 4.2.0 + chalk: 3.0.0 + configstore: 5.0.1 + has-yarn: 2.1.0 + import-lazy: 2.1.0 + is-ci: 2.0.0 + is-installed-globally: 0.3.2 + is-npm: 4.0.0 + is-yarn-global: 0.3.0 + latest-version: 5.1.0 + pupa: 2.1.1 + semver-diff: 3.1.1 + xdg-basedir: 4.0.0 + dev: true + /update-notifier/5.1.0: resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} engines: {node: '>=10'} @@ -13474,23 +26548,22 @@ packages: is-yarn-global: 0.3.0 latest-version: 5.1.0 pupa: 2.1.1 - semver: 7.3.5 + semver: 7.3.8 semver-diff: 3.1.1 xdg-basedir: 4.0.0 dev: true /upper-case/1.1.3: - resolution: {integrity: sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=} + resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} dev: true /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.1.1 - dev: true /urix/0.1.0: - resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=} + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} deprecated: Please see https://github.com/lydell/urix#deprecated dev: true @@ -13506,47 +26579,115 @@ packages: dependencies: file-loader: 6.2.0_webpack@4.46.0 loader-utils: 2.0.2 - mime-types: 2.1.34 + mime-types: 2.1.35 schema-utils: 3.1.1 webpack: 4.46.0 dev: true /url-parse-lax/3.0.0: - resolution: {integrity: sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=} + resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} engines: {node: '>=4'} dependencies: prepend-http: 2.0.0 dev: true + /url-parse/1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: true + /url/0.11.0: - resolution: {integrity: sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=} + resolution: {integrity: sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==} dependencies: punycode: 1.3.2 querystring: 0.2.0 dev: true + /use-composed-ref/1.3.0: + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dev: true + + /use-composed-ref/1.3.0_react@16.14.0: + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 16.14.0 + dev: true + + /use-isomorphic-layout-effect/1.1.2: + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dev: true + + /use-isomorphic-layout-effect/1.1.2_react@16.14.0: + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + react: 16.14.0 + dev: true + + /use-latest/1.2.1: + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + use-isomorphic-layout-effect: 1.1.2 + dev: true + + /use-latest/1.2.1_react@16.14.0: + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + react: 16.14.0 + use-isomorphic-layout-effect: 1.1.2_react@16.14.0 + dev: true + /use/3.1.1: resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} engines: {node: '>=0.10.0'} dev: true /util-deprecate/1.0.2: - resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true /util.promisify/1.0.0: resolution: {integrity: sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==} dependencies: - define-properties: 1.1.3 + define-properties: 1.1.4 object.getownpropertydescriptors: 2.1.3 dev: true /util.promisify/1.0.1: resolution: {integrity: sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==} dependencies: - define-properties: 1.1.3 - es-abstract: 1.19.1 - has-symbols: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + has-symbols: 1.0.3 object.getownpropertydescriptors: 2.1.3 dev: true @@ -13554,14 +26695,14 @@ packages: resolution: {integrity: sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.3 + define-properties: 1.1.4 for-each: 0.3.3 - has-symbols: 1.0.2 + has-symbols: 1.0.3 object.getownpropertydescriptors: 2.1.3 dev: true /util/0.10.3: - resolution: {integrity: sha1-evsa/lCAUkZInj23/g7TeTNqwPk=} + resolution: {integrity: sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==} dependencies: inherits: 2.0.1 dev: true @@ -13573,7 +26714,7 @@ packages: dev: true /utila/0.4.0: - resolution: {integrity: sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=} + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} dev: true /utils-merge/1.0.1: @@ -13581,6 +26722,10 @@ packages: engines: {node: '>= 0.4.0'} dev: true + /uuid-browser/3.1.0: + resolution: {integrity: sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg==} + dev: true + /uuid/3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. @@ -13596,6 +26741,15 @@ packages: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true + /v8-to-istanbul/7.1.2: + resolution: {integrity: sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==} + engines: {node: '>=10.10.0'} + dependencies: + '@types/istanbul-lib-coverage': 2.0.4 + convert-source-map: 1.8.0 + source-map: 0.7.3 + dev: true + /v8-to-istanbul/8.1.1: resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==} engines: {node: '>=10.12.0'} @@ -13605,8 +26759,15 @@ packages: source-map: 0.7.3 dev: true + /validate-npm-package-license/3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.1.1 + spdx-expression-parse: 3.0.1 + dev: true + /validate-npm-package-name/3.0.0: - resolution: {integrity: sha1-X6kS2B630MdK/BQN5zF/DKffQ34=} + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} dependencies: builtins: 1.0.3 dev: true @@ -13616,7 +26777,7 @@ packages: dev: false /vary/1.1.2: - resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} dev: true @@ -13633,6 +26794,26 @@ packages: extsprintf: 1.3.0 dev: true + /vfile-location/3.2.0: + resolution: {integrity: sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==} + dev: true + + /vfile-message/2.0.4: + resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + dependencies: + '@types/unist': 2.0.6 + unist-util-stringify-position: 2.0.3 + dev: true + + /vfile/4.2.1: + resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} + dependencies: + '@types/unist': 2.0.6 + is-buffer: 2.0.5 + unist-util-stringify-position: 2.0.3 + vfile-message: 2.0.4 + dev: true + /vm-browserify/1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} dev: true @@ -13641,16 +26822,40 @@ packages: resolution: {integrity: sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==} dev: true + /vscode-textmate/5.2.0: + resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==} + dev: true + /vscode-textmate/6.0.0: resolution: {integrity: sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==} dev: true /w3c-hr-time/1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + deprecated: Use your platform's native performance.now() and performance.timeOrigin. dependencies: browser-process-hrtime: 1.0.0 dev: true + /w3c-xmlserializer/2.0.0: + resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==} + engines: {node: '>=10'} + dependencies: + xml-name-validator: 3.0.0 + dev: true + + /walker/1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /warning/4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + dependencies: + loose-envify: 1.4.0 + dev: true + /watchpack-chokidar2/2.0.1: resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==} requiresBuild: true @@ -13664,7 +26869,7 @@ packages: /watchpack/1.7.5: resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==} dependencies: - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 neo-async: 2.6.2 optionalDependencies: chokidar: 3.5.3 @@ -13673,6 +26878,14 @@ packages: - supports-color dev: true + /watchpack/2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.10 + dev: true + /wbuf/1.7.3: resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} dependencies: @@ -13680,11 +26893,15 @@ packages: dev: true /wcwidth/1.0.1: - resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=} + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.3 dev: true + /web-namespaces/1.1.4: + resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==} + dev: true + /web-streams-polyfill/3.2.0: resolution: {integrity: sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==} engines: {node: '>= 8'} @@ -13697,6 +26914,40 @@ packages: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} dev: true + /webidl-conversions/5.0.0: + resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} + engines: {node: '>=8'} + dev: true + + /webidl-conversions/6.1.0: + resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} + engines: {node: '>=10.4'} + dev: true + + /webpack-bundle-analyzer/3.9.0: + resolution: {integrity: sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==} + engines: {node: '>= 6.14.4'} + hasBin: true + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + bfj: 6.1.2 + chalk: 2.4.2 + commander: 2.20.3 + ejs: 2.7.4 + express: 4.17.2 + filesize: 3.6.1 + gzip-size: 5.1.1 + lodash: 4.17.21 + mkdirp: 0.5.5 + opener: 1.5.2 + ws: 6.2.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /webpack-bundle-analyzer/4.5.0: resolution: {integrity: sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==} engines: {node: '>= 10.13.0'} @@ -13716,6 +26967,20 @@ packages: - utf-8-validate dev: true + /webpack-dev-middleware/3.7.3_webpack@4.46.0: + resolution: {integrity: sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==} + engines: {node: '>= 6'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + memory-fs: 0.4.1 + mime: 2.6.0 + mkdirp: 0.5.5 + range-parser: 1.2.1 + webpack: 4.46.0 + webpack-log: 2.0.0 + dev: true + /webpack-dev-middleware/5.3.1_webpack@4.46.0: resolution: {integrity: sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==} engines: {node: '>= 12.13.0'} @@ -13724,12 +26989,62 @@ packages: dependencies: colorette: 2.0.16 memfs: 3.4.1 - mime-types: 2.1.34 + mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.0.0 webpack: 4.46.0 dev: true + /webpack-dev-server/3.11.3_webpack@4.46.0: + resolution: {integrity: sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==} + engines: {node: '>= 6.11.5'} + hasBin: true + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + ansi-html-community: 0.0.8 + bonjour: 3.5.0 + chokidar: 2.1.8_supports-color@6.1.0 + compression: 1.7.4_supports-color@6.1.0 + connect-history-api-fallback: 1.6.0 + debug: 4.3.4_supports-color@6.1.0 + del: 4.1.1 + express: 4.17.2_supports-color@6.1.0 + html-entities: 1.4.0 + http-proxy-middleware: 0.19.1_tmpgdztspuwvsxzgjkhoqk7duq + import-local: 2.0.0 + internal-ip: 4.3.0 + ip: 1.1.5 + is-absolute-url: 3.0.3 + killable: 1.0.1 + loglevel: 1.8.0 + opn: 5.5.0 + p-retry: 3.0.1 + portfinder: 1.0.28_supports-color@6.1.0 + schema-utils: 1.0.0 + selfsigned: 1.10.14 + semver: 6.3.0 + serve-index: 1.9.1_supports-color@6.1.0 + sockjs: 0.3.24 + sockjs-client: 1.6.1_supports-color@6.1.0 + spdy: 4.0.2_supports-color@6.1.0 + strip-ansi: 3.0.1 + supports-color: 6.1.0 + url: 0.11.0 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-log: 2.0.0 + ws: 6.2.2 + yargs: 13.3.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /webpack-dev-server/4.7.4_webpack@4.46.0: resolution: {integrity: sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==} engines: {node: '>= 12.13.0'} @@ -13779,10 +27094,31 @@ packages: - utf-8-validate dev: true + /webpack-filter-warnings-plugin/1.2.1_webpack@4.46.0: + resolution: {integrity: sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==} + engines: {node: '>= 4.3 < 5.0.0 || >= 5.10'} + peerDependencies: + webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 + dependencies: + webpack: 4.46.0 + dev: true + + /webpack-fix-style-only-entries/0.5.2: + resolution: {integrity: sha512-BlJyvvLSmQmvVY+sWbXMoS3qkcglXDKB16sM3Mao0Ce5oeGF6goyLZ2g89gWk29pA0/CDS6En8aNAMIPMOk3wQ==} + dev: true + /webpack-fix-style-only-entries/0.6.1: resolution: {integrity: sha512-wyIhoxS3DD3Fr9JA8hQPA+ZmaWnqPxx12Nv166wcsI/0fbReqyEtiIk2llOFYIg57WVS3XX5cZJxw2ji70R0sA==} dev: true + /webpack-hot-middleware/2.25.2: + resolution: {integrity: sha512-CVgm3NAQyfdIonRvXisRwPTUYuSbyZ6BY7782tMeUzWOO7RmVI2NaBYuCp41qyD4gYCkJyTneAJdK69A13B0+A==} + dependencies: + ansi-html-community: 0.0.8 + html-entities: 2.3.2 + strip-ansi: 6.0.1 + dev: true + /webpack-log/2.0.0: resolution: {integrity: sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==} engines: {node: '>= 6'} @@ -13791,6 +27127,12 @@ packages: uuid: 3.4.0 dev: true + /webpack-merge/4.2.2: + resolution: {integrity: sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==} + dependencies: + lodash: 4.17.21 + dev: true + /webpack-merge/5.8.0: resolution: {integrity: sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==} engines: {node: '>=10.0.0'} @@ -13811,6 +27153,14 @@ packages: source-map: 0.6.1 dev: true + /webpack-virtual-modules/0.2.2: + resolution: {integrity: sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA==} + dependencies: + debug: 3.2.7 + transitivePeerDependencies: + - supports-color + dev: true + /webpack/4.46.0: resolution: {integrity: sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==} engines: {node: '>=6.11.5'} @@ -13894,6 +27244,15 @@ packages: webidl-conversions: 4.0.2 dev: true + /whatwg-url/8.7.0: + resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} + engines: {node: '>=10'} + dependencies: + lodash: 4.17.21 + tr46: 2.1.0 + webidl-conversions: 6.1.0 + dev: true + /which-boxed-primitive/1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: @@ -13904,10 +27263,31 @@ packages: is-symbol: 1.0.4 dev: true + /which-collection/1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + /which-module/2.0.0: resolution: {integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=} dev: true + /which-typed-array/1.1.8: + resolution: {integrity: sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-abstract: 1.20.4 + for-each: 0.3.3 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.9 + dev: true + /which/1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -13923,6 +27303,12 @@ packages: isexe: 2.0.0 dev: true + /wide-align/1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + dependencies: + string-width: 4.2.3 + dev: true + /widest-line/3.1.0: resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} engines: {node: '>=8'} @@ -13939,6 +27325,16 @@ packages: engines: {node: '>=0.10.0'} dev: true + /wordwrap/1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + dev: true + + /workbox-background-sync/5.1.4: + resolution: {integrity: sha512-AH6x5pYq4vwQvfRDWH+vfOePfPIYQ00nCEB7dJRU1e0n9+9HMRyvI63FlDvtFT2AvXVRsXvUt7DNMEToyJLpSA==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-background-sync/6.4.2: resolution: {integrity: sha512-P7c8uG5X2k+DMICH9xeSA9eUlCOjHHYoB42Rq+RtUpuwBxUOflAXR1zdsMWj81LopE4gjKXlTw7BFd1BDAHo7g==} dependencies: @@ -13946,21 +27342,71 @@ packages: workbox-core: 6.4.2 dev: true + /workbox-broadcast-update/5.1.4: + resolution: {integrity: sha512-HTyTWkqXvHRuqY73XrwvXPud/FN6x3ROzkfFPsRjtw/kGZuZkPzfeH531qdUGfhtwjmtO/ZzXcWErqVzJNdXaA==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-broadcast-update/6.4.2: resolution: {integrity: sha512-qnBwQyE0+PWFFc/n4ISXINE49m44gbEreJUYt2ldGH3+CNrLmJ1egJOOyUqqu9R4Eb7QrXcmB34ClXG7S37LbA==} dependencies: workbox-core: 6.4.2 dev: true + /workbox-build/5.1.4: + resolution: {integrity: sha512-xUcZn6SYU8usjOlfLb9Y2/f86Gdo+fy1fXgH8tJHjxgpo53VVsqRX0lUDw8/JuyzNmXuo8vXX14pXX2oIm9Bow==} + engines: {node: '>=8.0.0'} + dependencies: + '@babel/core': 7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/runtime': 7.17.8 + '@hapi/joi': 15.1.1 + '@rollup/plugin-node-resolve': 7.1.3_rollup@1.32.1 + '@rollup/plugin-replace': 2.4.2_rollup@1.32.1 + '@surma/rollup-plugin-off-main-thread': 1.4.2 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 8.1.0 + glob: 7.2.3 + lodash.template: 4.5.0 + pretty-bytes: 5.6.0 + rollup: 1.32.1 + rollup-plugin-babel: 4.4.0_n6u4oijhaodubo5gx2ecrhe56e + rollup-plugin-terser: 5.3.1_rollup@1.32.1 + source-map: 0.7.3 + source-map-url: 0.4.1 + stringify-object: 3.3.0 + strip-comments: 1.0.2 + tempy: 0.3.0 + upath: 1.2.0 + workbox-background-sync: 5.1.4 + workbox-broadcast-update: 5.1.4 + workbox-cacheable-response: 5.1.4 + workbox-core: 5.1.4 + workbox-expiration: 5.1.4 + workbox-google-analytics: 5.1.4 + workbox-navigation-preload: 5.1.4 + workbox-precaching: 5.1.4 + workbox-range-requests: 5.1.4 + workbox-routing: 5.1.4 + workbox-strategies: 5.1.4 + workbox-streams: 5.1.4 + workbox-sw: 5.1.4 + workbox-window: 5.1.4 + transitivePeerDependencies: + - supports-color + dev: true + /workbox-build/6.4.2: resolution: {integrity: sha512-WMdYLhDIsuzViOTXDH+tJ1GijkFp5khSYolnxR/11zmfhNDtuo7jof72xPGFy+KRpsz6tug39RhivCj77qqO0w==} engines: {node: '>=10.0.0'} dependencies: '@apideck/better-ajv-errors': 0.3.3_ajv@8.10.0 - '@babel/core': 7.17.2 - '@babel/preset-env': 7.16.11_@babel+core@7.17.2 - '@babel/runtime': 7.17.2 - '@rollup/plugin-babel': 5.3.0_lubkdqoa5gexe4rox23cswxwm4 + '@babel/core': 7.19.6 + '@babel/preset-env': 7.16.11_@babel+core@7.19.6 + '@babel/runtime': 7.17.8 + '@rollup/plugin-babel': 5.3.0_s45mkc5s7is4owdeow33qgy2s4 '@rollup/plugin-node-resolve': 11.2.1_rollup@2.79.0 '@rollup/plugin-replace': 2.4.2_rollup@2.79.0 '@surma/rollup-plugin-off-main-thread': 2.2.3 @@ -13968,7 +27414,7 @@ packages: common-tags: 1.8.2 fast-json-stable-stringify: 2.1.0 fs-extra: 9.1.0 - glob: 7.2.0 + glob: 7.2.3 lodash: 4.17.21 pretty-bytes: 5.6.0 rollup: 2.79.0 @@ -13999,16 +27445,32 @@ packages: - supports-color dev: true + /workbox-cacheable-response/5.1.4: + resolution: {integrity: sha512-0bfvMZs0Of1S5cdswfQK0BXt6ulU5kVD4lwer2CeI+03czHprXR3V4Y8lPTooamn7eHP8Iywi5QjyAMjw0qauA==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-cacheable-response/6.4.2: resolution: {integrity: sha512-9FE1W/cKffk1AJzImxgEN0ceWpyz1tqNjZVtA3/LAvYL3AC5SbIkhc7ZCO82WmO9IjTfu8Vut2X/C7ViMSF7TA==} dependencies: workbox-core: 6.4.2 dev: true + /workbox-core/5.1.4: + resolution: {integrity: sha512-+4iRQan/1D8I81nR2L5vcbaaFskZC2CL17TLbvWVzQ4qiF/ytOGF6XeV54pVxAvKUtkLANhk8TyIUMtiMw2oDg==} + dev: true + /workbox-core/6.4.2: resolution: {integrity: sha512-1U6cdEYPcajRXiboSlpJx6U7TvhIKbxRRerfepAJu2hniKwJ3DHILjpU/zx3yvzSBCWcNJDoFalf7Vgd7ey/rw==} dev: true + /workbox-expiration/5.1.4: + resolution: {integrity: sha512-oDO/5iC65h2Eq7jctAv858W2+CeRW5e0jZBMNRXpzp0ZPvuT6GblUiHnAsC5W5lANs1QS9atVOm4ifrBiYY7AQ==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-expiration/6.4.2: resolution: {integrity: sha512-0hbpBj0tDnW+DZOUmwZqntB/8xrXOgO34i7s00Si/VlFJvvpRKg1leXdHHU8ykoSBd6+F2KDcMP3swoCi5guLw==} dependencies: @@ -14016,6 +27478,15 @@ packages: workbox-core: 6.4.2 dev: true + /workbox-google-analytics/5.1.4: + resolution: {integrity: sha512-0IFhKoEVrreHpKgcOoddV+oIaVXBFKXUzJVBI+nb0bxmcwYuZMdteBTp8AEDJacENtc9xbR0wa9RDCnYsCDLjA==} + dependencies: + workbox-background-sync: 5.1.4 + workbox-core: 5.1.4 + workbox-routing: 5.1.4 + workbox-strategies: 5.1.4 + dev: true + /workbox-google-analytics/6.4.2: resolution: {integrity: sha512-u+gxs3jXovPb1oul4CTBOb+T9fS1oZG+ZE6AzS7l40vnyfJV79DaLBvlpEZfXGv3CjMdV1sT/ltdOrKzo7HcGw==} dependencies: @@ -14025,12 +27496,24 @@ packages: workbox-strategies: 6.4.2 dev: true + /workbox-navigation-preload/5.1.4: + resolution: {integrity: sha512-Wf03osvK0wTflAfKXba//QmWC5BIaIZARU03JIhAEO2wSB2BDROWI8Q/zmianf54kdV7e1eLaIEZhth4K4MyfQ==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-navigation-preload/6.4.2: resolution: {integrity: sha512-viyejlCtlKsbJCBHwhSBbWc57MwPXvUrc8P7d+87AxBGPU+JuWkT6nvBANgVgFz6FUhCvRC8aYt+B1helo166g==} dependencies: workbox-core: 6.4.2 dev: true + /workbox-precaching/5.1.4: + resolution: {integrity: sha512-gCIFrBXmVQLFwvAzuGLCmkUYGVhBb7D1k/IL7pUJUO5xacjLcFUaLnnsoVepBGAiKw34HU1y/YuqvTKim9qAZA==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-precaching/6.4.2: resolution: {integrity: sha512-CZ6uwFN/2wb4noHVlALL7UqPFbLfez/9S2GAzGAb0Sk876ul9ukRKPJJ6gtsxfE2HSTwqwuyNVa6xWyeyJ1XSA==} dependencies: @@ -14039,6 +27522,12 @@ packages: workbox-strategies: 6.4.2 dev: true + /workbox-range-requests/5.1.4: + resolution: {integrity: sha512-1HSujLjgTeoxHrMR2muDW2dKdxqCGMc1KbeyGcmjZZAizJTFwu7CWLDmLv6O1ceWYrhfuLFJO+umYMddk2XMhw==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-range-requests/6.4.2: resolution: {integrity: sha512-SowF3z69hr3Po/w7+xarWfzxJX/3Fo0uSG72Zg4g5FWWnHpq2zPvgbWerBZIa81zpJVUdYpMa3akJJsv+LaO1Q==} dependencies: @@ -14056,18 +27545,38 @@ packages: workbox-strategies: 6.4.2 dev: true + /workbox-routing/5.1.4: + resolution: {integrity: sha512-8ljknRfqE1vEQtnMtzfksL+UXO822jJlHTIR7+BtJuxQ17+WPZfsHqvk1ynR/v0EHik4x2+826Hkwpgh4GKDCw==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-routing/6.4.2: resolution: {integrity: sha512-0ss/n9PAcHjTy4Ad7l2puuod4WtsnRYu9BrmHcu6Dk4PgWeJo1t5VnGufPxNtcuyPGQ3OdnMdlmhMJ57sSrrSw==} dependencies: workbox-core: 6.4.2 dev: true + /workbox-strategies/5.1.4: + resolution: {integrity: sha512-VVS57LpaJTdjW3RgZvPwX0NlhNmscR7OQ9bP+N/34cYMDzXLyA6kqWffP6QKXSkca1OFo/v6v7hW7zrrguo6EA==} + dependencies: + workbox-core: 5.1.4 + workbox-routing: 5.1.4 + dev: true + /workbox-strategies/6.4.2: resolution: {integrity: sha512-YXh9E9dZGEO1EiPC3jPe2CbztO5WT8Ruj8wiYZM56XqEJp5YlGTtqRjghV+JovWOqkWdR+amJpV31KPWQUvn1Q==} dependencies: workbox-core: 6.4.2 dev: true + /workbox-streams/5.1.4: + resolution: {integrity: sha512-xU8yuF1hI/XcVhJUAfbQLa1guQUhdLMPQJkdT0kn6HP5CwiPOGiXnSFq80rAG4b1kJUChQQIGPrq439FQUNVrw==} + dependencies: + workbox-core: 5.1.4 + workbox-routing: 5.1.4 + dev: true + /workbox-streams/6.4.2: resolution: {integrity: sha512-ROEGlZHGVEgpa5bOZefiJEVsi5PsFjJG9Xd+wnDbApsCO9xq9rYFopF+IRq9tChyYzhBnyk2hJxbQVWphz3sog==} dependencies: @@ -14075,10 +27584,31 @@ packages: workbox-routing: 6.4.2 dev: true + /workbox-sw/5.1.4: + resolution: {integrity: sha512-9xKnKw95aXwSNc8kk8gki4HU0g0W6KXu+xks7wFuC7h0sembFnTrKtckqZxbSod41TDaGh+gWUA5IRXrL0ECRA==} + dev: true + /workbox-sw/6.4.2: resolution: {integrity: sha512-A2qdu9TLktfIM5NE/8+yYwfWu+JgDaCkbo5ikrky2c7r9v2X6DcJ+zSLphNHHLwM/0eVk5XVf1mC5HGhYpMhhg==} dev: true + /workbox-webpack-plugin/5.1.4_webpack@4.46.0: + resolution: {integrity: sha512-PZafF4HpugZndqISi3rZ4ZK4A4DxO8rAqt2FwRptgsDx7NF8TVKP86/huHquUsRjMGQllsNdn4FNl8CD/UvKmQ==} + engines: {node: '>=8.0.0'} + peerDependencies: + webpack: ^4.0.0 + dependencies: + '@babel/runtime': 7.17.8 + fast-json-stable-stringify: 2.1.0 + source-map-url: 0.4.1 + upath: 1.2.0 + webpack: 4.46.0 + webpack-sources: 1.4.3 + workbox-build: 5.1.4 + transitivePeerDependencies: + - supports-color + dev: true + /workbox-webpack-plugin/6.4.2_webpack@4.46.0: resolution: {integrity: sha512-CiEwM6kaJRkx1cP5xHksn13abTzUqMHiMMlp5Eh/v4wRcedgDTyv6Uo8+Hg9MurRbHDosO5suaPyF9uwVr4/CQ==} engines: {node: '>=10.0.0'} @@ -14097,6 +27627,12 @@ packages: - supports-color dev: true + /workbox-window/5.1.4: + resolution: {integrity: sha512-vXQtgTeMCUq/4pBWMfQX8Ee7N2wVC4Q7XYFqLnfbXJ2hqew/cU1uMTD2KqGEgEpE4/30luxIxgE+LkIa8glBYw==} + dependencies: + workbox-core: 5.1.4 + dev: true + /workbox-window/6.4.2: resolution: {integrity: sha512-KVyRKmrJg7iB+uym/B/CnEUEFG9CvnTU1Bq5xpXHbtgD9l+ShDekSl1wYpqw/O0JfeeQVOFb8CiNfvnwWwqnWQ==} dependencies: @@ -14120,6 +27656,15 @@ packages: resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} dev: true + /wrap-ansi/5.1.0: + resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} + engines: {node: '>=6'} + dependencies: + ansi-styles: 3.2.1 + string-width: 3.1.0 + strip-ansi: 5.2.0 + dev: true + /wrap-ansi/6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -14210,6 +27755,13 @@ packages: optional: true dev: true + /x-default-browser/0.4.0: + resolution: {integrity: sha512-7LKo7RtWfoFN/rHx1UELv/2zHGMx8MkZKDq1xENmOCTkfIqZJ0zZ26NEJX8czhnPXVcqS0ARjjfJB+eJ0/5Cvw==} + hasBin: true + optionalDependencies: + default-browser-id: 1.0.4 + dev: true + /xdg-basedir/4.0.0: resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} engines: {node: '>=8'} @@ -14238,7 +27790,7 @@ packages: dev: true /yallist/2.1.2: - resolution: {integrity: sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=} + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: true /yallist/3.1.1: @@ -14254,6 +27806,13 @@ packages: engines: {node: '>= 6'} dev: true + /yargs-parser/13.1.2: + resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + /yargs-parser/18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -14287,6 +27846,21 @@ packages: is-plain-obj: 2.1.0 dev: true + /yargs/13.3.2: + resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} + dependencies: + cliui: 5.0.0 + find-up: 3.0.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 3.1.0 + which-module: 2.0.0 + y18n: 4.0.3 + yargs-parser: 13.1.2 + dev: true + /yargs/15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} @@ -14339,3 +27913,20 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} dev: true + + /yup/0.32.11: + resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==} + engines: {node: '>=10'} + dependencies: + '@babel/runtime': 7.17.8 + '@types/lodash': 4.14.186 + lodash: 4.17.21 + lodash-es: 4.17.21 + nanoclone: 0.2.1 + property-expr: 2.0.5 + toposort: 2.0.2 + dev: false + + /zwitch/1.0.5: + resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} + dev: true |