diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..0acd49000 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +# Dependency directories +node_modules +client/node_modules +backend/node_modules +# .env +# webpack bundle +client/dist +client/build \ No newline at end of file diff --git a/.env.defaults b/.env.defaults new file mode 100644 index 000000000..5ae6b9476 --- /dev/null +++ b/.env.defaults @@ -0,0 +1,4 @@ +NODE_ENV= +newReg= +MONGO_URI= +REACT_APP_fbAPIid= \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..76d9185ef --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +### What is this PR for? + + +### What type of PR is it? +[Bug Fix | Improvement | Feature | Documentation | Hot Fix | Refactoring] + +### Todos +* [ ] - Task + +### What is the Jira issue? + + +### Screenshots (if appropriate) + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..af610e669 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,10 @@ +name: NTUEEPLUS + +# Trigger the workflow on push or pull request +on: [push, pull_request] + +jobs: + example: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 86ebeb559..4bb8d8963 100644 --- a/.gitignore +++ b/.gitignore @@ -3,12 +3,8 @@ # dependencies /node_modules package-lock.json - -# testing -/coverage - -# production -/build +yarn.lock +.env # misc .eslintcache diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..35d691878 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx pretty-quick --staged diff --git a/.prettierignore b/.prettierignore index 849ddff3b..4f90cb308 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,3 @@ -dist/ +./client/dist/ +./client/node_modules +./backend/routes/docTemplate/plate.md \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js index 415ca0578..66f90b933 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,7 +1,7 @@ module.exports = { semi: false, - trailingComma: "all", + trailingComma: 'all', singleQuote: true, printWidth: 100, - tabWidth: 2 -}; \ No newline at end of file + tabWidth: 2, +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..1b6457c5c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index ee4e5f2ba..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,61 +0,0 @@ -### Changelog - -All notable changes to this project will be documented in this file. Dates are displayed in UTC. - -#### [3.2.0](https://github.com/coreui/coreui-free-react-admin-template/compare/3.1.1...3.2.0) - -> 14 December 2020 - -- fix(BrandButtons): minor temp fixes with stylings [`f0c0585`](https://github.com/coreui/coreui-free-react-admin-template/commit/f0c05858329430c9487bdcfcf36d0aa98f60776d) -- refactor: polyfills cleanup [`9522ade`](https://github.com/coreui/coreui-free-react-admin-template/commit/9522ade1f1eb2c5a26d5208fe6e44be803123bd2) -- test: refactor, import temp update [`f3f91c5`](https://github.com/coreui/coreui-free-react-admin-template/commit/f3f91c5d721754a2dac26143f312415c94c7ed68) -- chore: React 17 and dependencies update [`f6a710f`](https://github.com/coreui/coreui-free-react-admin-template/commit/f6a710ffd6996c9b6b026b8eebed6ff390757fe1) -- fix(_nav): no anonymous default export [`ed83c9a`](https://github.com/coreui/coreui-free-react-admin-template/commit/ed83c9a3d4199b8c6566a6e01396cdcbca80cf42) - -#### [3.1.1](https://github.com/coreui/coreui-free-react-admin-template/compare/3.1.0...3.1.1) - -> 26 November 2020 - -- chore: dependencies update [`5b70904`](https://github.com/coreui/coreui-free-react-admin-template/commit/5b70904d8f22f61a71def3306ea96ad640573c39) -- chore: add changelog [`de605cc`](https://github.com/coreui/coreui-free-react-admin-template/commit/de605cc1aadec9fb066625c6bc7b656590efc7bb) -- chore: limit changelog to version 3 [`6ac9a1d`](https://github.com/coreui/coreui-free-react-admin-template/commit/6ac9a1d094d4d293ddacb3f3562216a98be8e129) -- chore: add migration docs [`b661344`](https://github.com/coreui/coreui-free-react-admin-template/commit/b661344485a38404b8c2b1326e3a4258009b071c) -- chore: changelog update [`f9f1927`](https://github.com/coreui/coreui-free-react-admin-template/commit/f9f19274f046cf5f11f68d6c7eb7ae04c553f219) -- chore: .gitignore cleanup [`ba21e00`](https://github.com/coreui/coreui-free-react-admin-template/commit/ba21e001944322fb48e43264719b94368c589732) -- chore: 3.1.1 release [`081b957`](https://github.com/coreui/coreui-free-react-admin-template/commit/081b957291a8020e3d2e3b42c1f0a1455a49c524) - -#### [3.1.0](https://github.com/coreui/coreui-free-react-admin-template/compare/3.0.0...3.1.0) - -> 12 August 2020 - -- docs(readme): CoreUI react theme setup with laravel tutorial link add into readme [`#226`](https://github.com/coreui/coreui-free-react-admin-template/pull/226) -- fix: fix template testing [`68ce41d`](https://github.com/coreui/coreui-free-react-admin-template/commit/68ce41db6831d6995121766a5771dc9d667cc61a) -- chore: 3.1.0 release - update dependencies [`92f55b8`](https://github.com/coreui/coreui-free-react-admin-template/commit/92f55b8cdfd748a9e72649d5da62b93015a1c8e6) -- refactor: add reusable folder with DocsLink component [`eef84db`](https://github.com/coreui/coreui-free-react-admin-template/commit/eef84dbbd770c7253080a6f69443c40e4fecefd2) -- feat: add CSwitch examples to forms [`b31e452`](https://github.com/coreui/coreui-free-react-admin-template/commit/b31e452fd0ea736763d3032d7204cd478863b505) -- refactor: add CIcon example in _nav.js [`be5d1f0`](https://github.com/coreui/coreui-free-react-admin-template/commit/be5d1f0618f981f18c45be87afb56c4409bd3389) - -### [3.0.0](https://github.com/coreui/coreui-free-react-admin-template/compare/v2.6.1...3.0.0) - -> 17 June 2020 - -- feat: update template to version 3 [`cc79542`](https://github.com/coreui/coreui-free-react-admin-template/commit/cc795425bbf610873fcdf6938b5fb0aba49a4d97) -- refactor: update folder casing to kebab-case [`75138b0`](https://github.com/coreui/coreui-free-react-admin-template/commit/75138b0d0340cc21d58bcc2f800f042f86e54346) -- refactor: temporarily delete views folder [`cb4433a`](https://github.com/coreui/coreui-free-react-admin-template/commit/cb4433a3e33cb943bc1f47199110ead28fab517b) -- docs: README update [`188e0b1`](https://github.com/coreui/coreui-free-react-admin-template/commit/188e0b1c09fd7d47dc87d0410303ae43e8ee79de) -- chore: clear packages [`e236aad`](https://github.com/coreui/coreui-free-react-admin-template/commit/e236aad4ab0129e3611adfc2127670da64696e54) -- fix: delete obsolete files, fix logos [`f479a5d`](https://github.com/coreui/coreui-free-react-admin-template/commit/f479a5dc72bb5bb75b95a4b904d1c350be8fe7bc) -- chore: 3.0.0-beta.1 release [`d940f92`](https://github.com/coreui/coreui-free-react-admin-template/commit/d940f92ef741d7eab021af4fbcf385823c80421a) -- fix: fix accordion, delete aside [`0e6506e`](https://github.com/coreui/coreui-free-react-admin-template/commit/0e6506ea3303ca30bc21ba2bcf3717a3f009dc8c) -- refactor: optimize icon bundle size, update icons [`9fed168`](https://github.com/coreui/coreui-free-react-admin-template/commit/9fed168a534b88cb27371d6364b922418a5a13b4) -- refacotor: template updates [`1df8c15`](https://github.com/coreui/coreui-free-react-admin-template/commit/1df8c15030d45779f6adc5031153eaff09701d97) -- refactor: turn logos extensions from svg to js [`8c0deee`](https://github.com/coreui/coreui-free-react-admin-template/commit/8c0deeed169267155323a5b6bdbbdfaf8a856a41) -- refactor: rename containers from 'Default' to 'The', small fixes [`bfc79da`](https://github.com/coreui/coreui-free-react-admin-template/commit/bfc79da4039dd534ee49b4526978f7b949cea90b) -- refactor: update icons to version 2, rtl fixes [`8e4fbc2`](https://github.com/coreui/coreui-free-react-admin-template/commit/8e4fbc2aa8786b00a004282260c52986e1cd2430) -- fix: delete unneded icons, aside, fix readme.md [`1ee0561`](https://github.com/coreui/coreui-free-react-admin-template/commit/1ee05619ba15d050b73df21c8d1347e8329942d5) -- chore: 3.0.0 version release - update dependencies [`fd5236d`](https://github.com/coreui/coreui-free-react-admin-template/commit/fd5236d47340b336bf641041cbf6d48ec8b1081a) -- feat: add query parameters to Users view [`98f8b67`](https://github.com/coreui/coreui-free-react-admin-template/commit/98f8b677edb96f9175b7d4c20370c3d6744543bd) -- docs: add license [`db85786`](https://github.com/coreui/coreui-free-react-admin-template/commit/db85786be465fdb7a84b7337dbe876afc5e957bc) -- chore: update react.md [`5aa0cc3`](https://github.com/coreui/coreui-free-react-admin-template/commit/5aa0cc3ce15c841032cd75392418cfeb2e4d094f) -- docs: README cleanup [`82a4351`](https://github.com/coreui/coreui-free-react-admin-template/commit/82a4351daa6c8d452e19c7141dbadecc3f721c1b) -- fix: fix Icons views [`1777a09`](https://github.com/coreui/coreui-free-react-admin-template/commit/1777a092f6444497120e85c8852a1e4779640e71) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..ae4ce1c24 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM node:14.17-alpine +WORKDIR /app +# Install app dependencies +COPY package*.json ./ +COPY client/package*.json ./client/ +COPY backend/package*.json ./backend/ +RUN yarn local-install +# Bundle app source +COPY . . +ENV PORT=3000 +ENV NODE_ENV=production +ENV newReg=false +ARG MONGO_URI +ENV MONGO_URI=${MONGO_URI:-} +ARG REACT_APP_fbAPIid +ENV REACT_APP_fbAPIid=${REACT_APP_fbAPIid:-} +RUN yarn run build-client + +EXPOSE 3000 +CMD [ "node", "index.js" ] \ No newline at end of file diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 00045c0de..000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,20 +0,0 @@ -Before opening an issue: - -- [Search for duplicate or closed issues](https://github.com/coreui/coreui-free-react-admin-template/issues?utf8=%E2%9C%93&q=is%3Aissue) -- Prepare a [reduced test case](https://css-tricks.com/reduced-test-cases/) for any bugs - - -When asking general "how to" questions: - -- Please do not open an issue here - -When reporting a bug, include: - -- Operating system and version (Windows, Mac OS X, Android, iOS, Win10 Mobile) -- Browser and version (Chrome, Firefox, Safari, IE, MS Edge, Opera, Android Browser) -- Reduced test cases and potential fixes using [CodePen](https://codepen.io/) or [JS Bin](https://jsbin.com/) - -When suggesting a feature, include: - -- As much detail as possible for what we should add and why it's important to CoreUI Admin Template -- Relevant links to prior art, screenshots, or live demos whenever possible diff --git a/REACT.md b/REACT.md deleted file mode 100644 index e3e62a422..000000000 --- a/REACT.md +++ /dev/null @@ -1,19 +0,0 @@ -# CoreUI React version - -## Intro -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) - -It uses Sass (with .scss). The styles are loaded at the template level with `node-sass-chokidar` css preprocessor - -Dependencies are handled by **npm**. - -## Usage -`npm i` - to install dependencies - -## Sctipts -`npm start` for developing (it runs webpack-dev-server) -`npm run build` to run a dev build - -## See also -[Create-React-App](CRA.md) -[Readme](./README.md) diff --git a/README.md b/README.md index d922b52e9..6c9849bff 100644 --- a/README.md +++ b/README.md @@ -1,174 +1,92 @@ -[![@coreui coreui](https://img.shields.io/badge/@coreui%20-coreui-lightgrey.svg?style=flat-square)](https://github.com/coreui/coreui) -[![npm package][npm-coreui-badge]][npm-coreui] -[![NPM downloads][npm-coreui-download]][npm-coreui] -[![@coreui react](https://img.shields.io/badge/@coreui%20-react-lightgrey.svg?style=flat-square)](https://github.com/coreui/react) -[![npm package][npm-coreui-react-badge]][npm-coreui-react] -[![NPM downloads][npm-coreui-react-download]][npm-coreui-react] -[![npm next][npm-next]][npm] - -[npm-coreui]: https://www.npmjs.com/package/@coreui/coreui -[npm-coreui-badge]: https://img.shields.io/npm/v/@coreui/coreui.png?style=flat-square -[npm-coreui-download]: https://img.shields.io/npm/dm/@coreui/coreui.svg?style=flat-square -[npm-coreui-react]: https://www.npmjs.com/package/@coreui/react -[npm-coreui-react-badge]: https://img.shields.io/npm/v/@coreui/react.png?style=flat-square -[npm-coreui-react-download]: https://img.shields.io/npm/dm/@coreui/react.svg?style=flat-square -[npm-next]: https://img.shields.io/npm/v/@coreui/react/next.png?style=flat-square -[npm]: https://www.npmjs.com/package/@coreui/react - -# CoreUI Free React Admin Template v3 - -CoreUI is meant to be the UX game changer. Pure & transparent code is devoid of redundant components, so the app is light enough to offer ultimate user experience. This means mobile devices also, where the navigation is just as easy and intuitive as on a desktop or laptop. The CoreUI Layout API lets you customize your project for almost any device – be it Mobile, Web or WebApp – CoreUI covers them all! - -## Table of Contents - -* [Versions](#versions) -* [CoreUI Pro](#coreui-pro) -* [Installation](#installation) -* [Basic usage](#create-react-app) -* [What's included](#whats-included) -* [Documentation](#documentation) -* [Versioning](#versioning) -* [Creators](#creators) -* [Community](#community) -* [Copyright and License](#copyright-and-license) - -## Versions - -* [CoreUI Free Bootstrap Admin Template](https://github.com/coreui/coreui-free-bootstrap-admin-template) -* [CoreUI Free Angular 9+ Admin Template](https://github.com/coreui/coreui-free-angular-admin-template) -* [CoreUI Free React.js Admin Template](https://github.com/coreui/coreui-free-react-admin-template) -* [CoreUI Free Vue.js Admin Template](https://github.com/coreui/coreui-free-vue-admin-template) -* [CoreUI Free Laravel Admin Template](https://github.com/coreui/coreui-free-laravel-admin-template) -* [CoreUI Free Vue.js + Laravel Admin Template](https://github.com/coreui/coreui-free-vue-laravel-admin-template) - -## CoreUI Pro - -**Only customers with [Enterpise Membership Plan](https://coreui.io/pro/#buy) have access to private github CoreUI Pro repository.** - -* 💪 [CoreUI Pro Bootstrap Admin Template](https://coreui.io/pro/) -* 💪 [CoreUI Pro Angular 9+ Admin Template](https://coreui.io/pro/angular) -* 💪 [CoreUI Pro React Admin Template](https://coreui.io/pro/react) -* 💪 [CoreUI Pro Vue Admin Template](https://coreui.io/pro/vue) -* 💪 [CoreUI Pro Laravel Admin Template](https://coreui.io/pro/laravel/) -* 💪 [CoreUI Pro Vue.js + Laravel Admin Template](https://coreui.io/pro/vue-laravel/) - -## Installation - -### Clone repo - -``` bash -# clone the repo -$ git clone https://github.com/coreui/coreui-free-react-admin-template.git my-project - -# go into app's directory -$ cd my-project - -# install app's dependencies -$ npm install -``` +# NTUEE_Plus_website -### Copy and Paste +## Run -Copy all your files to your project folder and then, +- get true .env from [here](https://drive.google.com/drive/folders/1sIbHwgsVmo1IHE-nc3OvVdYd3-haUc5N?usp=sharing) -``` bash -# go into app's directory -$ cd my-project +```bash +# whenever new package is used. +yarn local-install -# install app's dependencies -$ npm install +yarn dev +# 進入http://localhost:1993 ``` -## Create React App -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) +## Contribution -see also: -[CRA docs](https://create-react-app.dev/docs/getting-started) +please read [How to Contribute](https://github.com/NTUEE-PLUS/EndOfWeb/blob/main/doc/contribution.md) first. -### Basic usage +## 前端 coding style 規則 -``` bash -# dev server with hot reload at http://localhost:3000 -$ npm start -``` +> https://www.notion.so/Tidy-Up-Coding-style-de130c77c8654b61ba0e45c941b55ed4 -Navigate to [http://localhost:3000](http://localhost:3000). The app will automatically reload if you change any of the source files. +## 後端/api 使用方法 -### Build +> https://github.com/NTUEE-PLUS/EndOfWeb/tree/main/backend/routes -Run `build` to build the project. The build artifacts will be stored in the `build/` directory. +## EE+ MD -```bash -# build for production with minification -$ npm run build -``` +> https://hackmd.io/CSNbja7XTYCYquYxgq4Xow + +## 會議記錄 + +> https://hackmd.io/7RZ9XyL7Qa-7aFXaZSfw_w -## What's included +## 電二主機 -Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this: +- 下載[.env](https://drive.google.com/drive/folders/1wruoEuM2yG2fNlA3i5pBeXH8IoKlr9cv?usp=sharing),需要請求權限 + - 請確認 newReg 的值,false 表示直接允許註冊,true 表示照片驗證(deprecated),version3 表示寄信 or 照片驗證 +- 執行 +```bash +$ docker-compose up --build -d (噴error時取消-d可以看到完整錯誤訊息) +$ docker-compose exec web npm run reset-db +open http://localhost:3000 ``` -CoreUI-React#v3.0.0 -├── public/ #static files -│ └── index.html #html template -│ -├── src/ #project root -│ ├── assets/ #assets - js icons object -│ ├── containers/ #container source - template layout -| │ ├── _nav.js #sidebar config -| │ └── ... -│ ├── scss/ #user scss/css source -│ ├── views/ #views source -│ ├── App.js -│ ├── App.test.js -│ ├── polyfill.js -│ ├── index.js -│ ├── routes.js #routes config -│ └── store.js #template state example -│ -└── package.json + +- if docker no working well, try this in powershell + +```bash +$ Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All ``` -## Documentation +## Heroku -The documentation for the CoreUI Admin Template is hosted at our website [CoreUI for React](https://coreui.io/react/) +- visit the [website](https://eeplus.herokuapp.com/) -### :film_strip: How to setup coreui react theme in laravel. Video tutorial available [here](https://youtu.be/HVVpbpNUJ8M) +### env setting -## Versioning +- In heroku/settings, modify config vars -For transparency into our release cycle and in striving to maintain backward compatibility, CoreUI Free Admin Template is maintained under [the Semantic Versioning guidelines](http://semver.org/). +![image](https://github.com/Claude0311/EndOfWeb/blob/NTUEEPLUS-152/screenshot/heroku-arg.png) -See [the Releases section of our project](https://github.com/coreui/coreui-free-react-admin-template/releases) for changelogs for each release version. +## GCP -## Creators +- visit the [website](https://eeplus-jflswz6uxq-de.a.run.app/#/contact) -**Łukasz Holeczek** -* -* -* +### manual deploy -**CoreUI team** -* https://github.com/orgs/coreui/people +```bash +# in cloud shell +$ gcloud builds submit +``` -## Community +- This will take 5~15 mins, depends on whether package.json was changed. If package wasn't changed, cache will be used; Otherwise, yarn install will take a lot of time. -Get updates on CoreUI's development and chat with the project maintainers and community members. +### auto deploy (with secret .env) -- Follow [@core_ui on Twitter](https://twitter.com/core_ui). -- Read and subscribe to [CoreUI Blog](https://coreui.ui/blog/). +1. set **trigger** in cloud build and add arg fields + ![image](https://github.com/Claude0311/EndOfWeb/blob/NTUEEPLUS-152/screenshot/gcp-arg-step1.png) -## Copyright and License +2. In cloudbuild.yaml, use **--build-arg** to read args -copyright 2021 creativeLabs Łukasz Holeczek. + ![image](https://github.com/Claude0311/EndOfWeb/blob/NTUEEPLUS-152/screenshot/gcp-arg-step2.png) - -Code released under [the MIT license](https://github.com/coreui/coreui-free-react-admin-template/blob/master/LICENSE). -There is only one limitation you can't can’t re-distribute the CoreUI as stock. You can’t do this if you modify the CoreUI. In past we faced some problems with persons who tried to sell CoreUI based templates. +3. In Dockerfile, use **ARG** and **ENV** to set env -## Support CoreUI Development + ![image](https://github.com/Claude0311/EndOfWeb/blob/NTUEEPLUS-152/screenshot/gcp-arg-step3.png) -CoreUI is an MIT licensed open source project and completely free to use. However, the amount of effort needed to maintain and develop new features for the project is not sustainable without proper financial backing. You can support development by buying [CoreUI Pro Version](https://coreui.io/pro/). +4. If want to use .env in frontend, set **pulgin** in webpack.config.js -We're also open to conversations regarding custom sponsorship / consulting arrangements. Get in touch on [Twitter](https://twitter.com/lukaszholeczek). + ![image](https://github.com/Claude0311/EndOfWeb/blob/NTUEEPLUS-152/screenshot/gcp-arg-step4.png) diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 000000000..a1f554cc6 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,60 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +.vscode/ +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +/NTUEE-PLUS/node_modules/.cache + +#myedition +node_modules/ +.DS_Store +npm-debug.log +client/build/ +sessions/ +dist/ +tmp/ + +.env +init-mongo.sh +certificate.crt +certificate.key +routes/apidoc-out +package-lock.json +yarn.lock +**/uploads/*.xlsx + +test.js \ No newline at end of file diff --git a/backend/Procfile b/backend/Procfile new file mode 100644 index 000000000..22c517aeb --- /dev/null +++ b/backend/Procfile @@ -0,0 +1 @@ +web: yarn start-heroku \ No newline at end of file diff --git a/backend/index.js b/backend/index.js new file mode 100644 index 000000000..51760cf70 --- /dev/null +++ b/backend/index.js @@ -0,0 +1,97 @@ +//ee0125/index.js +const express = require('express') +const app = express() +const path = require('path') +const cors = require('cors') +const mongoose = require('./routes/Schemas/db') + +mongoose.connection.on('open', () => { + console.log('DB on') + const bodyParser = require('body-parser') + const session = require('express-session') + const MongoStore = require('connect-mongo')(session) + //post, get時的解碼 + app.use(bodyParser.urlencoded({ extended: true })) + app.use(bodyParser.json()) + // app.use(function(req, res, next) { + // res.header("Access-Control-Allow-Origin", 'http://localhost:3000'); + // res.header("Access-Control-Allow-Credentials", true); + // res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); + // res.header("Access-Control-Allow-Headers", 'Origin, X-Requested-With, Content-Type, Accept'); + // next(); + // }); + app.use( + cors({ + origin: 'http://localhost:3000', + methods: ['POST', 'PUT', 'GET', 'OPTIONS', 'HEAD', 'PATCH', 'DELETE'], + credentials: true, + }), + ) + //session 設定 + //參考網站https://www.cnblogs.com/chyingp/p/nodejs-learning-express-session.html + app.use( + session({ + name: 'eeplus', + secret: 'fuewhzk', // 用来對session id相關的cookie進行簽名,建議128byte亂碼 + //store: new FileStore({ logFn: function () {} }), // 本地儲存session(文本文件,也可以選擇其他store,比如redis的) + store: new MongoStore({ + mongooseConnection: mongoose.connection, + }), + saveUninitialized: false, // 是否自动保存未初始化的會話,建議false + resave: false, // 是否每次都重新保存會話,建議false + cookie: { + httpOnly: true, //false前端可read和set + maxAge: 60 * 60 * 1000, // 有效期(ms) + }, + }), + ) + + app.use('/api', require('./routes/api')) + // }) + + //frontend + if (!process.env.HERO) { + const connectHistoryApiFallback = require('connect-history-api-fallback') + app.use( + connectHistoryApiFallback({ + verbose: false, + }), + ) + const DIST_DIR = path.join(__dirname, './dist') + const HTML_FILE = path.join(__dirname, './index.html') + app.use(express.static(DIST_DIR)) + app.get('/', (req, res) => { + res.sendFile(HTML_FILE) // EDIT + }) + } + //old frontend + //Serve static files from the React app + //詳細資訊看:https://expressjs.com/zh-tw/starter/static-files.html + // app.use(express.static(path.join(__dirname, 'client/build'))) + /*app.get('*', (req, res) => { + res.sendFile(path.join(__dirname+'/client/build/index.html')); //這個缺點是react build的index不是我們寫的那個 + //res.redirect('/'); //這個按F5會亂跳,先捨棄 +});*/ + + //server on + app.listen(process.env.PORT || 1993, () => { + if (process.env.HERO) { + const { wakeDyno } = require('heroku-keep-awake') + wakeDyno('https://eeplus-back.herokuapp.com/', { + logging: false, + stopTimes: { start: '16:00', end: '00:00' }, //time zone +0,so -8hr + }) + } + console.log(`Server is up on port ${process.env.PORT || 1993}.`) + }) + //https server + // connect to https://localhost:1993 + // const fs = require('fs') + // const options = { + // key: fs.readFileSync('./certificate.key'), + // cert: fs.readFileSync('./certificate.crt') + // }; + // const https = require('https'); + // https.createServer(null, app).listen(process.env.PORT||1993, () => { + // console.log(`Server is up on port ${process.env.PORT || 1993}.`) +}) diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 000000000..1a4d85da7 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,40 @@ +{ + "name": "ee0125", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node index.js", + "watch:server": "nodemon index.js", + "test": "echo \"Error: no test specified\" && exit 1", + "dbstart": "sudo service mongodb start", + "doc": "apidoc -i routes -o routes/apidoc-out && apidoc-markdown -p routes/apidoc-out -o routes/readme.md -t routes/doctemplate/plate.md", + "insert-sample": "cd ./routes/srcs/in/study/runMatch/uploads && node writeSample.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.17.2", + "connect-history-api-fallback": "^1.6.0", + "connect-mongo": "^3.2.0", + "core-js": "^3.8.1", + "express": "^4.17.1", + "express-async-handler": "^1.1.4", + "express-session": "^1.17.1", + "express-validator": "^6.8.1", + "jquery": "^3.5.1", + "jsdom": "^16.4.0", + "mongoose": "^5.11.8", + "mongoose-type-email": "^1.1.2", + "multer": "^1.4.2", + "munkres-js": "^1.2.2", + "nodemailer": "^6.4.17", + "read-excel-file": "^5.0.0", + "xlsx": "^0.16.9" + }, + "devDependencies": { + "apidoc": "^0.25.0", + "apidoc-markdown": "^5.2.1", + "nodemon": "^2.0.6" + } +} diff --git a/backend/routes/Schemas/abroad_info.js b/backend/routes/Schemas/abroad_info.js new file mode 100644 index 000000000..01c5a112f --- /dev/null +++ b/backend/routes/Schemas/abroad_info.js @@ -0,0 +1,25 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Abroad_Info = new Schema({ + title: { type: String }, + info: { type: String }, + icon: { + data: { type: Buffer }, + contentType: { type: String }, + }, +}) + +const { buf2url } = require('./query') +Abroad_Info.virtual('iconSrc').get(buf2url('icon')) + +Abroad_Info.methods.getPublic = function () { + return { + title: this.title, + _id: this._id, + info: this.info, + image: this.iconSrc, + } +} + +module.exports = mongoose.model('Abroad_info', Abroad_Info) diff --git a/backend/routes/Schemas/activation.js b/backend/routes/Schemas/activation.js new file mode 100755 index 000000000..5f34580e9 --- /dev/null +++ b/backend/routes/Schemas/activation.js @@ -0,0 +1,11 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const activation_Schema = new Schema({ + account: { type: String, required: true }, + //newpsw: { type: String, required: true }, + active: String, //激活碼 + createdAt: { type: Date, default: Date.now, expires: 60 * 60 }, //修改這行時要刪掉collection,否則不會更新到 +}) + +module.exports = mongoose.model('Activation', activation_Schema) diff --git a/backend/routes/Schemas/column.js b/backend/routes/Schemas/column.js new file mode 100644 index 000000000..d75552f7f --- /dev/null +++ b/backend/routes/Schemas/column.js @@ -0,0 +1,13 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Column_Schema = new Schema({ + filename: { type: String }, + columnImg: { + //column 的照片 + data: { type: Buffer }, + contentType: { type: String }, + }, +}) + +module.exports = mongoose.model('Column', Column_Schema) diff --git a/backend/routes/Schemas/column_detail.js b/backend/routes/Schemas/column_detail.js new file mode 100644 index 000000000..2c3373287 --- /dev/null +++ b/backend/routes/Schemas/column_detail.js @@ -0,0 +1,52 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Column_detail = new Schema({ + top: { + name: String, + experience: [String], + hashtags: [String], + }, + body: { + body: [ + { + bigtitle: String, + bigsections: [ + { + subtitle: String, + subsection: String, + }, + ], + }, + ], + }, + annotation: { + annotation: [ + { + job: String, + contributer: String, + }, + ], + }, + id: { type: String, unique: true, index: true }, +}) + +Column_detail.statics.smartQuery = function (keywords) { + const reg = new RegExp(keywords.replace(' ', '|'), 'i') + const query = { + $or: [ + { 'top.name': reg }, + { 'top.experience': reg }, + { 'top.hashtags': reg }, + { 'body.body.bigtitle': reg }, + { 'body.body.bigsections.subtitle': reg }, + { 'body.body.bigsections.subsection': reg }, + { 'annotation.annotation.contributer': reg }, + ], + } + return query +} + +Column_detail.index({ id: -1 }) + +module.exports = mongoose.model('Column_detail_v3', Column_detail) diff --git a/backend/routes/Schemas/column_outline.js b/backend/routes/Schemas/column_outline.js new file mode 100644 index 000000000..beb0fabf8 --- /dev/null +++ b/backend/routes/Schemas/column_outline.js @@ -0,0 +1,58 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Column_Outline = new Schema({ + anno: [{ type: String }], + date: String, + title: [{ type: String }], + exp: [{ type: String }], + edu: [{ type: String }], + intro: [{ type: String }], + id: { type: String, unique: true, index: true }, + columnImg: { + //column 的照片 + data: { type: Buffer }, + contentType: { type: String }, + }, +}) + +const { buf2url } = require('./query') +Column_Outline.virtual('imgSrc').get(buf2url('columnImg')) + +Column_Outline.methods.getPublic = function () { + const { anno, date, title, exp, edu, intro, id, imgSrc } = this + return { anno, date, title, exp, edu, intro, id, imgSrc } +} + +// Column_Outline.statics.leanTrans = function ({ +// anno, +// date, +// title, +// exp, +// edu, +// intro, +// id, +// columnImg, +// }) { +// let imgSrc +// try { +// const prefix = 'data:' + columnImg.contentType + ';base64,' +// const img = new Buffer(columnImg.data, 'binary').toString('base64') +// imgSrc = prefix + img +// } catch { +// imgSrc = '' +// } +// return { anno, date, title, exp, edu, intro, id, imgSrc } +// } + +Column_Outline.statics.smartQuery = function (keywords) { + const reg = new RegExp(keywords.replace(' ', '|'), 'i') + const query = { + $or: [{ anno: reg }, { title: reg }, { exp: reg }, { edu: reg }, { intro: reg }], + } + return query +} + +Column_Outline.index({ id: -1 }) + +module.exports = mongoose.model('Column_outline_v3', Column_Outline) diff --git a/backend/routes/Schemas/db.js b/backend/routes/Schemas/db.js new file mode 100644 index 000000000..9bb5cc8c6 --- /dev/null +++ b/backend/routes/Schemas/db.js @@ -0,0 +1,25 @@ +const mongoose = require('mongoose') +const env = require('dotenv') +env.config() + +// const localDB = 'mongodb://localhost:27017/eeplus' +let DB_URL +if (process.env.MONGO_URI) { + DB_URL = process.env.MONGO_URI +} else { + console.log('using sample DBurl, contact project manager to get real DBurl') + DB_URL = + 'mongodb+srv://ntueeplus:ntueeplus2020@cluster0.fctiy.mongodb.net/heroku_kbtrwz4h?retryWrites=true&w=majority' +} +console.log('mongoose connecting to:', DB_URL) + +const options = { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true } +mongoose.connect(DB_URL, options) +mongoose.connection.on('disconnected', function () { + console.log('disconnected to', DB_URL) +}) +mongoose.connection.on('error', function (err) { + console.log(err.message) +}) + +module.exports = mongoose diff --git a/backend/routes/Schemas/googlesheet_link.js b/backend/routes/Schemas/googlesheet_link.js new file mode 100644 index 000000000..b938bf9c3 --- /dev/null +++ b/backend/routes/Schemas/googlesheet_link.js @@ -0,0 +1,13 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Study_Schema = new Schema({ + id: Number, + senior: String, + junior: String, + year: { type: Number, default: new Date().getFullYear() }, + publishTime: { type: Date }, + note: String, +}) + +module.exports = mongoose.model('Study_Link', Study_Schema) diff --git a/backend/routes/Schemas/matching_form.js b/backend/routes/Schemas/matching_form.js new file mode 100644 index 000000000..683a3bf30 --- /dev/null +++ b/backend/routes/Schemas/matching_form.js @@ -0,0 +1,34 @@ +const mongoose = require('mongoose') +const Schema = mongoose.Schema + +const sForm = new Schema({ + account: String, + name: String, + degree: String, + major: [{ type: String }], + gpa: String, + email: String, + number: String, + admission: [{ type: String }], + school: String, + junior: [{ type: String }], +}) + +const jForm = new Schema({ + account: String, + name: String, + degree: [{ type: String }], + hasPaper: String, + major: [{ type: String }], + gpa: String, + email: String, + studentID: String, + school1: [{ type: String }], + school2: [{ type: String }], + school3: [{ type: String }], + senior: String, +}) + +const seniorForm = mongoose.model('seniorForm', sForm) +const juniorForm = mongoose.model('juniorForm', jForm) +module.exports = { seniorForm, juniorForm } diff --git a/backend/routes/Schemas/matching_result.js b/backend/routes/Schemas/matching_result.js new file mode 100644 index 000000000..777ea24eb --- /dev/null +++ b/backend/routes/Schemas/matching_result.js @@ -0,0 +1,12 @@ +// const mongoose = require('./db'), +// Schema = mongoose.Schema; + +// const Matching_Schema = new Schema({ +// name1: String, +// email1: String, +// name2: String, +// Id2: String, +// email2: String +// }) + +// module.exports = mongoose.model('matching_result', Matching_Schema); diff --git a/backend/routes/Schemas/preload.js b/backend/routes/Schemas/preload.js new file mode 100644 index 000000000..c45910639 --- /dev/null +++ b/backend/routes/Schemas/preload.js @@ -0,0 +1,49 @@ +const mongoose = require('mongoose') +const models = [ + require('./user_login'), + require('./user_visual_new'), + require('./recruitment'), + require('./recommendation'), + require('./column_detail'), + require('./column_outline'), +] +const env = require('dotenv') +env.config() + +const sourceUrl = process.env.MONGO_URL +// 'mongodb+srv://ntueeplus:ntueeplus2020@cluster0.fctiy.mongodb.net/heroku_kbtrwz4h?retryWrites=true&w=majority' +const targetUrl = process.env.MONGO_URI +if (!sourceUrl || !targetUrl) throw new Error('MONGO_URI or MONGO_URL not given') + +const copy = async (sourceDB, targetDB, schema, name, ordered = true) => { + const sourceModel = sourceDB.model(name, schema) + const targetModel = targetDB.model(name, schema) + const docs = await sourceModel.find() + console.log(`${docs.length} ${name} docs found`) + await targetModel.insertMany(docs, { ordered }) //ordered true may run faster(?) but will be unordered + console.log(`insert ${name} complete`) +} + +const main = async () => { + console.log(`saving to ${targetUrl}`) + const option = { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true } + const sourceDB = await mongoose.createConnection(sourceUrl, option) + const targetDB = await mongoose.createConnection(targetUrl, option) + const dp = new Promise((resolve, reject) => { + targetDB.db.dropDatabase(function (err, res) { + if (err) reject(err) + console.log('target DB drop success') + resolve() + }) + }) + await dp + const tasks = models.map((model) => { + const collectionName = model.collection.collectionName + const Schema = model.schema + return copy(sourceDB, targetDB, Schema, collectionName) + }) + await Promise.all(tasks) + sourceDB.close() + targetDB.close() +} +main() diff --git a/backend/routes/Schemas/query.js b/backend/routes/Schemas/query.js new file mode 100644 index 000000000..563053ced --- /dev/null +++ b/backend/routes/Schemas/query.js @@ -0,0 +1,83 @@ +const { dbCatch } = require('../error') + +const updateQuery = (obj) => { + const toSet = Object.entries(obj).reduce( + (acc, [key, val]) => { + if (val === undefined) return acc + if (val === '') { + acc.$unset[key] = '' + return acc + } + acc.$set[key] = val + return acc + }, + { $set: {}, $unset: {} }, + ) + return toSet +} + +const searchQuery = (obj) => { + const query = Object.entries(obj).reduce((acc, [key, val]) => { + if (val === undefined) return acc + if (key === '_id') return [{ _id: val }, ...acc] + const reg = new RegExp(val.replace(' ', '|'), 'i') + const obj = {} + obj[key] = reg + acc.push(obj) + return acc + }, []) + console.log('search query:') + console.log(query) + return query.length === 0 ? {} : { $or: query } +} + +/** + * + * @param {Collection} Collection + * @param {json} query + * @param {Number} page (default 1) + * @param {Number} perpage (default 5) + * @returns [documents,maxPage] + */ +const findWithLimit = async (Collection, query, page, perpage) => { + const p = parseInt(page ? page : 1) + const pp = parseInt(perpage && perpage > 0 ? perpage : 5) + const totalData = await Collection.countDocuments(query).catch(dbCatch) + const maxPage = Math.ceil(totalData / pp) + if (p > maxPage) return [[], maxPage] + const toSkip = p >= maxPage ? 0 : totalData - pp * p + const toLim = p >= maxPage ? totalData - pp * (maxPage - 1) : pp + const docs = await Collection.find(query).skip(toSkip).limit(toLim).catch(dbCatch) + return [docs, maxPage] +} +/* + * trans {buffer,mimetype} to {data,contentType} + * @param {File} file format: {buffer,mimetype} + * @returns + */ +const parseImg = (file) => { + if (!file) return undefined + return { + data: file.buffer, + contentType: file.mimetype, + } +} + +/** + * trans {contentType, data} to dataurl + * @param {String} key default 'img' + * @returns + */ +const buf2url = (key = 'img') => { + return function () { + try { + return `data:${this[key].contentType};base64,${Buffer.from(this[key].data).toString( + 'base64', + )}` + } catch { + return '' + } + } +} + +module.exports = { updateQuery, searchQuery, parseImg, buf2url, findWithLimit } diff --git a/backend/routes/Schemas/recommendation.js b/backend/routes/Schemas/recommendation.js new file mode 100644 index 000000000..5c2948f8a --- /dev/null +++ b/backend/routes/Schemas/recommendation.js @@ -0,0 +1,62 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Recommendation_Schema = new Schema({ + account: { type: String, required: true }, + title: { + title: String, + name: String, + desire_work_type: String, + }, + info: { + contact: String, + email: String, + diploma: String, + }, + spec: { + experience: [String], + speciality: [String], + }, + + img: { + data: { type: Buffer }, + contentType: { type: String }, + }, + //image:eesa_icon, +}) + +const { buf2url } = require('./query') +Recommendation_Schema.virtual('imgSrc').get(buf2url()) + +Recommendation_Schema.methods.getPublic = function () { + return { + account: this.account, + _id: this._id, + title: this.title, + info: this.info, + spec: this.spec, + image: this.imgSrc, + } +} + +Recommendation_Schema.statics.smartQuery = function (keywords) { + if (!keywords) return [] + const reg = new RegExp(keywords.replace(' ', '|'), 'i') + // console.log(reg) + const query = { + $or: [ + { account: reg }, + { 'title.title': reg }, + { 'title.name': reg }, + { 'title.desire_work_type': reg }, + { 'info.contact': reg }, + { 'info.email': reg }, + { 'info.diploma': reg }, + { 'spec.experience': reg }, + { 'spec.speciality': reg }, + ], + } + return query +} + +module.exports = mongoose.model('Recommendation', Recommendation_Schema) diff --git a/backend/routes/Schemas/recruitment.js b/backend/routes/Schemas/recruitment.js new file mode 100644 index 000000000..a78930e18 --- /dev/null +++ b/backend/routes/Schemas/recruitment.js @@ -0,0 +1,62 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Recruitment_Schema = new Schema({ + account: String, + + title: { + title: String, + company_name: String, + work_type: String, + }, + info: { + salary: String, + experience: [String], + diploma: String, + }, + + spec: { + requirement: [String], + description: String, + }, + img: { + data: { type: Buffer }, + contentType: { type: String }, + }, +}) + +const { buf2url } = require('./query') +Recruitment_Schema.virtual('imgSrc').get(buf2url()) + +Recruitment_Schema.methods.getPublic = function () { + return { + account: this.account, + _id: this._id, + title: this.title, + info: this.info, + spec: this.spec, + image: this.imgSrc, + } +} + +Recruitment_Schema.statics.smartQuery = function (keywords) { + if (!keywords) return [] + const reg = new RegExp(keywords.replace(' ', '|'), 'i') + // console.log(reg) + const query = { + $or: [ + { account: reg }, + { 'title.title': reg }, + { 'title.company_name': reg }, + { 'title.work_type': reg }, + { 'info.salary': reg }, + { 'info.experience': reg }, + { 'info.diploma': reg }, + { 'spec.requirement': reg }, + { 'spec.description': reg }, + ], + } + return query +} + +module.exports = mongoose.model('Recruitment_v3', Recruitment_Schema) diff --git a/backend/routes/Schemas/time.js b/backend/routes/Schemas/time.js new file mode 100644 index 000000000..37ace9cd5 --- /dev/null +++ b/backend/routes/Schemas/time.js @@ -0,0 +1,9 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema + +const Time_Schema = new Schema({ + time: { required: true, type: Date }, + target: { required: true, type: String }, +}) + +module.exports = mongoose.model('Time', Time_Schema) diff --git a/backend/routes/Schemas/user_login.js b/backend/routes/Schemas/user_login.js new file mode 100644 index 000000000..35d62e2ca --- /dev/null +++ b/backend/routes/Schemas/user_login.js @@ -0,0 +1,47 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema +const env = require('dotenv') +env.config() + +const exportVersion = (v) => { + switch (v) { + case 'true': + return new Schema({ + username: { type: String, required: true }, //名字 + facebookID: String, + account: { type: String, required: true, lowercase: true }, //學號 + userpsw: String, //密碼 + isAuth: { type: Boolean, default: false }, + visual: { type: Schema.Types.ObjectId, ref: 'User_visual' }, + }) + case 'version3': + return new Schema({ + username: { type: String, required: true }, //名字 + facebookID: String, + account: { type: String, required: true, lowercase: true }, //學號 + userpsw: String, //密碼 + isAuth: { type: Boolean, default: false }, + visual: { type: Schema.Types.ObjectId, ref: 'User_visual' }, + }) + default: + return new Schema({ + username: { type: String, required: true }, //名字 + facebookID: String, + account: { type: String, required: true, lowercase: true }, //學號 + userpsw: String, //密碼 + isAuth: { type: Boolean, default: false }, + visual: { type: Schema.Types.ObjectId, ref: 'User_visual' }, + img: { + data: { type: Buffer }, + contentType: { type: String }, + }, + }) + } +} + +const User_login_Schema = exportVersion(process.env.newReg) + +const { buf2url } = require('./query') +User_login_Schema.virtual('imgSrc').get(buf2url()) + +module.exports = mongoose.model('User_login', User_login_Schema) diff --git a/backend/routes/Schemas/user_pending.js b/backend/routes/Schemas/user_pending.js new file mode 100644 index 000000000..1d2ac95b9 --- /dev/null +++ b/backend/routes/Schemas/user_pending.js @@ -0,0 +1,25 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema +require('mongoose-type-email') + +const User_pending_Schema = new Schema({ + username: { type: String, required: true }, //名字 + facebookID: String, + account: { type: String, required: true, lowercase: true, unique: true }, //學號 + userpsw: String, //密碼 + email: { type: mongoose.SchemaTypes.Email, required: true }, + active: String, + img: { + data: Buffer, + contentType: String, + }, + avatar: { + data: Buffer, + contentType: String, + }, +}) + +const { buf2url } = require('./query') +User_pending_Schema.virtual('imgSrc').get(buf2url()) + +module.exports = mongoose.model('User_pending', User_pending_Schema) diff --git a/backend/routes/Schemas/user_visual_new.js b/backend/routes/Schemas/user_visual_new.js new file mode 100644 index 000000000..f88bdf8f4 --- /dev/null +++ b/backend/routes/Schemas/user_visual_new.js @@ -0,0 +1,113 @@ +const mongoose = require('mongoose'), + Schema = mongoose.Schema +require('mongoose-type-email') + +const Profile_Schema = new Schema({ + account: { type: String, required: true, lowercase: true }, + username: { type: String, required: true }, + nickname: String, + profile: String, + major: String, + double_major: String, + minor: String, + master: String, + doctor: String, + publicEmail: mongoose.SchemaTypes.Email, + cellphone: String, + CC: String, //city+country + web: String, + facebook: String, + github: String, + Linkedin: String, + Occupation: [ + { + O: String, //部門 + P: String, //職稱 + C: String, //公司 + }, + ], + userimage: { + data: Buffer, + contentType: String, + }, +}) + +const { buf2url } = require('./query') +Profile_Schema.virtual('imgSrc').get(buf2url('userimage')) + +Profile_Schema.statics.smartQuery = function (keywords) { + if (!keywords) return [] + const reg = new RegExp(keywords.replace(' ', '|'), 'i') + // console.log(reg) + const query = { + $or: [ + { account: reg }, + { username: reg }, + { nickname: reg }, + { profile: reg }, + { major: reg }, + { double_major: reg }, + { minor: reg }, + { master: reg }, + { doctor: reg }, + { publicEmail: reg }, + { cellphone: reg }, + { CC: reg }, + { web: reg }, + { facebook: reg }, + { Linkedin: reg }, + { github: reg }, + { 'Occupation.O': reg }, + { 'Occupation.P': reg }, + { 'Occupation.C': reg }, + ], + } + return query +} + +Profile_Schema.methods.getPublic = function () { + const { + _id, + account, + username, + nickname, + profile, + major, + double_major, + minor, + master, + doctor, + publicEmail, + cellphone, + CC, + web, + facebook, + github, + Linkedin, + Occupation, + imgSrc, + } = this + return { + _id, + account, + username, + nickname, + profile, + major, + double_major, + minor, + master, + doctor, + publicEmail, + cellphone, + CC, + web, + facebook, + github, + Linkedin, + Occupation, + userimage: imgSrc, + } +} + +module.exports = mongoose.model('Profile', Profile_Schema) diff --git a/backend/routes/api.js b/backend/routes/api.js new file mode 100644 index 000000000..2de3d9af9 --- /dev/null +++ b/backend/routes/api.js @@ -0,0 +1,57 @@ +//routes/api.js 控管後端所有頁面部屬 +const express = require('express') +const router = express.Router() +const env = require('dotenv') +env.config() + +if (process.env.NODE_ENV === 'development') { + //test + console.log('running in dev mode') +} + +//out +//login, loginFB, register, registerFB +router.use(require('./srcs/out/account/main')) +//forget, activation +router.use(require('./srcs/out/forget/main')) + +//in +//check is user +router.use(require('./srcs/in/auth/isUser')) +//showVisual, chVisual, searchVisual +// router.use(require('./srcs/in/profile/main')) +//dashboard +router.use(require('./srcs/in/dashboard/main')) +//time +router.use('/time', require('./srcs/in/time/main').router) +//profile, searchProfile +router.use(require('./srcs/in/profile_new/main')) +//showPerson, chLogin, isLogin, logout +router.use(require('./srcs/in/account/main').router) +//column +router.use('/column', require('./srcs/in/column/main').router) +//searchJob, addJob, addRecruitment +router.use(require('./srcs/in/career/main')) +router.use(require('./srcs/in/recommendation/main')) +//study +router.use('/study', require('./srcs/in/study/main').router) +//abroadInfo +router.use(require('./srcs/in/abroadInfo/main').router) + +//check is auth +router.use(require('./srcs/in/auth/isAuth')) +//column +router.use('/column', require('./srcs/in/column/main').router_auth) +//auth +// router.use(require('./srcs/in/auth/main')) +//account +router.use(require('./srcs/in/account/main').router_auth) +//study auth +router.use('/study', require('./srcs/in/study/main').router_auth) +//abroadInfo +router.use(require('./srcs/in/abroadInfo/main').router_auth) + +//error handling, every error thrown by previous router will be catch by me +router.use(require('./error').handleError) + +module.exports = router diff --git a/backend/routes/apidoc.json b/backend/routes/apidoc.json new file mode 100644 index 000000000..40ee54d9c --- /dev/null +++ b/backend/routes/apidoc.json @@ -0,0 +1,6 @@ +{ + "name": "EEplus website api", + "version": "1.0.0", + "description": "EE+ api文件", + "title": "EE+ api" + } \ No newline at end of file diff --git a/backend/routes/docTemplate/plate.md b/backend/routes/docTemplate/plate.md new file mode 100644 index 000000000..4de81711e --- /dev/null +++ b/backend/routes/docTemplate/plate.md @@ -0,0 +1,157 @@ + +# <%= project.name %> v<%= project.version %> + +<%= project.description %> + +<% data.forEach(group => { -%> + - [<%= group.name %>](#<%= toLink(group.name.replace('/','')).toLowerCase() -%>) +<% group.subs.forEach(sub => { -%> + - [<%= sub.title %>](#<%= toLink(sub.title.replace('/','')).toLowerCase() %>) +<% })}) -%> + +___ + +<% if (prepend) { -%> +<%- prepend %> +<% } -%> +<% data.forEach(group => { -%> + +# <%= group.name %> +<% group.subs.forEach(sub => { -%> + +## <%= sub.title %> +[Back to top](#top) + +<%- sub.description ? `${sub.description.replace('

', '').replace('

', '').replace('
    ','').replace('
','')}\n\n` : '' -%> +``` +<%- sub.type.toUpperCase() %> <%= sub.url %> +``` +<% if (sub.header && sub.header.fields) { -%> +<% Object.entries(sub.header.fields).forEach(([headersGroup, headersGroupContent]) => { -%> + +### Headers - `<%= headersGroup %>` + +| Name | Type | Description | +|---------|-----------|--------------------------------------| +<% headersGroupContent.forEach(header => { -%> +| <%- header.field %> | <%- header.type ? `\`${header.type}\`` : '' %> | <%- header.optional ? '**optional**' : '' %><%- header.description.replace('

', '').replace('

', '').replace('
    ','').replace('
',''); %> | +<% }) // foreach parameter -%> +<% }) // foreach header fields -%> +<% } // if parameters -%> +<% if (sub.header && sub.header.examples && sub.header.examples.length) { -%> + +### Header examples +<% sub.header.examples.forEach(example => { -%> +<%= example.title %> + +```<%= example.type %> +<%- example.content %> +``` +<% }) // foreach example -%> +<% } // if example -%> +<% if (sub.parameter && sub.parameter.fields) { -%> +<% Object.entries(sub.parameter.fields).forEach(([parametersGroup, parametersGroupContent]) => { -%> + +### Parameters - `<%= parametersGroup -%>` + +| Name | Type | Description | +|----------|------------|---------------------------------------| +<% parametersGroupContent.forEach(param => { -%> +| <%- `${' '.repeat(param.field.split('.').length-1)}`+param.field.split('.').pop() -%> | <%- param.type ? `\`${param.type}\`` : '' %> | <%- param.optional ? '**optional** ' : '' -%><%- param.description.replace('

', '').replace('

', '').replace('
    ','').replace('
',''); -%> +<% if (param.defaultValue) { -%> +_Default value: <%= param.defaultValue %>_
<% } -%> +<% if (param.size) { -%> +_Size range: <%- param.size %>_
<% } -%> +<% if (param.allowedValues) { -%> +_Allowed values: <%- param.allowedValues %>_<% } -%> | +<% }) // foreach parameters -%> +<% }) // foreach param parameter -%> +<% } // if parameters -%> +<% if (sub.examples && sub.examples.length) { -%> + +### Examples +<% sub.examples.forEach(example => { -%> +<%= example.title %> + +```<%= example.type %> +<%- example.content %> +``` +<% }) // foreach example -%> +<% } // if example -%> +<% if (sub.parameter && sub.parameter.examples && sub.parameter.examples.length) { -%> + +### Parameters examples +<% sub.parameter.examples.forEach(exampleParam => { -%> +`<%= exampleParam.type %>` - <%= exampleParam.title %> + +```<%= exampleParam.type %> +<%- exampleParam.content %> +``` +<% }) // foreach exampleParam -%> +<% } // if exampleParam -%> +<% if (sub.success && sub.success.fields) { -%> + +### Success response +<% Object.entries(sub.success.fields).forEach(([responsesGroup, responsesGroupContent]) => { -%> + +#### Success response - `<%= responsesGroup %>` + +| Name | Type | Description | +|----------|------------|---------------------------------------| +<% responsesGroupContent.forEach(param => { -%> +| <%- `${' '.repeat(param.field.split('.').length-1)}`+param.field.split('.').pop() %> | <%- param.type ? `\`${param.type}\`` : '' %> | <%- param.optional ? '**optional**' : '' %><%- param.description.replace('

', '').replace('

', '').replace('
    ','').replace('
',''); -%> +<% if (param.defaultValue) { -%> +_Default value: <%- param.defaultValue %>_
<% } -%> +<% if (param.size) { -%> +_Size range: <%- param.size -%>_
<% } -%> +<% if (param.allowedValues) { -%> +_Allowed values: <%- param.allowedValues %>_<% } -%> | +<% }) // foreach reponses -%> +<% }) // foreach field -%> +<% } // if success.fields -%> +<% if (sub.success && sub.success.examples && sub.success.examples.length) { -%> + +### Success response example +<% sub.success.examples.forEach(example => { -%> + +#### Success response example - `<%= example.title %>` + +```<%= example.type %> +<%- example.content %> +``` +<% }) // foreach success example -%> +<% } // if success.examples -%> +<% if (sub.error && sub.error.fields) { -%> + +### Error response +<% Object.entries(sub.error.fields).forEach(([errorsGroup, errorsGroupContent]) => { -%> + +#### Error response - `<%= errorsGroup %>` + +| Name | Type | Description | +|----------|------------|---------------------------------------| +<% errorsGroupContent.forEach(param => { -%> +| <%- `${' '.repeat(param.field.split('.').length-1)}`+param.field.split('.').pop() %> | <%- param.type ? `\`${param.type}\`` : '' %> | <%- param.optional ? '**optional**' : '' %><%- param.description.replace('

', '').replace('

', '').replace('
    ','').replace('
',''); -%> +<% if (param.defaultValue) { -%> +_Default value: <%- param.defaultValue %>_
<% } -%> +<% if (param.size) { -%> +_Size range: <%- param.size -%>_
<% } -%> +<% if (param.allowedValues) { -%> +_Allowed values: <%- param.allowedValues %>_<% } -%> | +<% }) // foreach errors -%> +<% }) // foreach field -%> +<% } // if error.fields -%> +<% if (sub.error && sub.error.examples && sub.error.examples.length) { -%> + +### Error response example +<% sub.error.examples.forEach(example => { -%> + +#### Error response example - `<%= example.title %>` + +```<%= example.type %> +<%- example.content %> +``` +<% }) // foreach error example -%> +<% } // if error.examples -%> +<% }) // foreach sub -%> +<% }) // foreach group -%> diff --git a/backend/routes/error/index.js b/backend/routes/error/index.js new file mode 100644 index 000000000..cc8a273e3 --- /dev/null +++ b/backend/routes/error/index.js @@ -0,0 +1,37 @@ +class ErrorHandler extends Error { + /** + * throw Error to error router + * @param {Number} statusCode http status + * @param {String} message error message sending to frontend + */ + constructor(statusCode, message) { + super() + this.statusCode = statusCode + this.description = message + } +} + +const handleError = (err, req, res, next) => { + const { statusCode, description } = err + if (!description) { + console.log('Error:', err) + return res.status(404).json({ + description: 'unknow error', + }) + } + console.log('Error:', description) + res.status(statusCode).json({ + description, + }) +} + +/** + * throw Error 500 '資料庫錯誤' to error router + * @param {Error} e + */ +const dbCatch = (e) => { + console.log(e.message) + throw new ErrorHandler(500, '資料庫錯誤') +} + +module.exports = { ErrorHandler, handleError, dbCatch } diff --git a/backend/routes/middleware/fileProcess.js b/backend/routes/middleware/fileProcess.js new file mode 100644 index 000000000..273e67deb --- /dev/null +++ b/backend/routes/middleware/fileProcess.js @@ -0,0 +1,42 @@ +const multer = require('multer') +const { ErrorHandler } = require('../error') + +//參考網址 +//https://medium.com/%E9%BA%A5%E5%85%8B%E7%9A%84%E5%8D%8A%E8%B7%AF%E5%87%BA%E5%AE%B6%E7%AD%86%E8%A8%98/%E7%AD%86%E8%A8%98-%E4%BD%BF%E7%94%A8-multer-%E5%AF%A6%E4%BD%9C%E5%A4%A7%E9%A0%AD%E8%B2%BC%E4%B8%8A%E5%82%B3-ee5bf1683113 + +const upload = multer({ + limits: { + // 限制上傳檔案的大小為 100MB + fileSize: 100000000, + }, + fileFilter: function (req, file, cb) { + if (!file.originalname.match(/\.(jpg|jpeg|png|JPG|PNG|JPEG)$/)) { + req.fileValidationError = '檔案格式錯誤' + return cb(new Error('檔案格式錯誤'), false) + } + cb(null, true) + }, +}) + +/** + * file preprocess by multer + * @param {String||Array} filename filename in multer + * @return {List} callback functions to put in router + */ +module.exports = (filename) => { + let doUpload + if (typeof filename === 'string') { + doUpload = upload.single(filename) + } else { + doUpload = upload.fields(filename) + } + return (req, res, next) => { + doUpload(req, res, (err) => { + if (req.fileValidationError) return res.status(400).send(req.fileValidationError) + //throw new ErrorHandler(400,req.fileValidationError) + else if (err instanceof multer.MulterError) return res.status(400).send(err.message) + else if (err) return res.status(400).send('檔案讀取發生錯誤') + next() + }) + } +} diff --git a/backend/routes/middleware/mail/credential.js b/backend/routes/middleware/mail/credential.js new file mode 100644 index 000000000..0f012cb82 --- /dev/null +++ b/backend/routes/middleware/mail/credential.js @@ -0,0 +1,8 @@ +//參考資料https://oranwind.org/gmail-smtp/ +module.exports = { + // cookieSecret: 'your cookie secret goes here', + gmail: { + user: 'ntueeplus2020@gmail.com', + password: 'upvnqetmnibmnaac', //這不是真正的密碼,不用試了:) + }, +} diff --git a/backend/routes/middleware/mail/main.js b/backend/routes/middleware/mail/main.js new file mode 100644 index 000000000..bd81bc55b --- /dev/null +++ b/backend/routes/middleware/mail/main.js @@ -0,0 +1,40 @@ +const nodemailer = require('nodemailer') +const { ErrorHandler } = require('../../error') +const credentials = require('./credential') + +const transporter = nodemailer.createTransport({ + service: 'Gmail', + auth: { + user: credentials.gmail.user, + pass: credentials.gmail.password, + }, +}) + +/** + * give recipient and text then send a mail + * @param {String} recipient 收件人 + * @param {String} subject 主旨 + * @param {String} text mail content + */ +module.exports = async function (recipient, subject, text, attachment_filepath = null) { + const mail = { + // 設定寄信參數 + // 發信人 + from: '"台大電機系系學會EEPlus" ', + // 主題 + subject: subject, + // 收信人 + to: recipient, + // 信件內容,HTML格式 + html: text, + + attachments: attachment_filepath + ? [{ filename: 'result.xlsx', path: attachment_filepath }] + : undefined, + } + const info = await transporter.sendMail(mail).catch((e) => { + console.log(e.message) + throw new ErrorHandler(500, '寄信失敗') + }) + console.log('mail sent:', info.envelope) +} diff --git a/backend/routes/middleware/mail/package.json b/backend/routes/middleware/mail/package.json new file mode 100644 index 000000000..43156315a --- /dev/null +++ b/backend/routes/middleware/mail/package.json @@ -0,0 +1,3 @@ +{ + "main":"./main.js" +} \ No newline at end of file diff --git a/backend/routes/middleware/validation/Name/ConfirmPassword.js b/backend/routes/middleware/validation/Name/ConfirmPassword.js new file mode 100644 index 000000000..a4b758be7 --- /dev/null +++ b/backend/routes/middleware/validation/Name/ConfirmPassword.js @@ -0,0 +1,10 @@ +const { body } = require('express-validator') + +module.exports = ({ field = 'ConfirmPassword' }) => + body(field).custom((val, { req }) => { + if (val !== req.body.password) { + console.log(val, '!=\n', req.body.password) + throw new Error('兩段密碼不一致') + } + return true + }) diff --git a/backend/routes/middleware/validation/Name/Email.js b/backend/routes/middleware/validation/Name/Email.js new file mode 100644 index 000000000..fa17a5754 --- /dev/null +++ b/backend/routes/middleware/validation/Name/Email.js @@ -0,0 +1,3 @@ +const { body } = require('express-validator') + +module.exports = ({ field = 'Email' }) => body(field, 'invalid Email').exists().isEmail() diff --git a/backend/routes/middleware/validation/Name/account.js b/backend/routes/middleware/validation/Name/account.js new file mode 100644 index 000000000..225e73654 --- /dev/null +++ b/backend/routes/middleware/validation/Name/account.js @@ -0,0 +1,27 @@ +const { body, param } = require('express-validator') + +module.exports = ( + { field = 'account', required = true, search = false, checkAt = 'body' }, //account, for register/registerFB +) => + checkAt === 'params' + ? param(field) + .isLength({ min: 9, max: 9 }) + .withMessage('學號長度錯誤') + .matches(/^[a-zA-Z]\d{8}$/) + .withMessage('學號形式錯誤') + : search + ? body(field) + .isLength({ min: 9, max: 9 }) + .withMessage("學號長度錯誤,若要進行模糊搜尋請用x表示,例如'b079010xx'") + : required + ? body(field) + .isLength({ min: 9, max: 9 }) + .withMessage('學號長度錯誤') + .matches(/^[a-zA-Z]\d{8}$/) + .withMessage('學號形式錯誤') + : body(field) + .optional({ nullable: true, checkFalsy: true }) + .isLength({ min: 9, max: 9 }) + .withMessage('學號長度錯誤') + .matches(/^[a-zA-Z]\d{8}$/) + .withMessage('學號形式錯誤') diff --git a/backend/routes/middleware/validation/Name/facebookID.js b/backend/routes/middleware/validation/Name/facebookID.js new file mode 100644 index 000000000..8d93768ad --- /dev/null +++ b/backend/routes/middleware/validation/Name/facebookID.js @@ -0,0 +1,8 @@ +const { body } = require('express-validator') + +module.exports = () => + body('facebookID') + .isLength({ min: 1 }) + .withMessage('請輸入FBID') + .matches(/^[0-9]+$/) + .withMessage('FB的ID必須都是數字組成') diff --git a/backend/routes/middleware/validation/Name/optional.js b/backend/routes/middleware/validation/Name/optional.js new file mode 100644 index 000000000..001c043ab --- /dev/null +++ b/backend/routes/middleware/validation/Name/optional.js @@ -0,0 +1,18 @@ +const { body, query } = require('express-validator') + +module.exports = ({ field = [], type = 'string', method = 'post' }) => + field.map((f) => { + const q = (method === 'post' ? body(f) : query(f)).optional({ + nullable: true, + checkFalsy: false, + }) + return type === 'string' + ? q.isString().withMessage(`${f} must be str(if exist)`) + : type === 'object' + ? q.isObject().withMessage(`${f} must be object(if exist)`) + : type === 'array' + ? q.isArray().withMessage(`${f} must be array(if exist)`) + : type === 'email' + ? q.isEmail().withMessage(`${f} must be valid email(if exist)`) + : q + }) diff --git a/backend/routes/middleware/validation/Name/password.js b/backend/routes/middleware/validation/Name/password.js new file mode 100644 index 000000000..e9797614f --- /dev/null +++ b/backend/routes/middleware/validation/Name/password.js @@ -0,0 +1,9 @@ +const { body } = require('express-validator') + +module.exports = ({ field = 'password' }) => + body(field) + .isLength({ min: 2 }) + .withMessage(`${field} must be minimum 2 length`) + .not() + .matches(/^$|\s+/) + .withMessage(`${field} shouldn't contain white space`) diff --git a/backend/routes/middleware/validation/Name/required.js b/backend/routes/middleware/validation/Name/required.js new file mode 100644 index 000000000..2a5160c9b --- /dev/null +++ b/backend/routes/middleware/validation/Name/required.js @@ -0,0 +1,18 @@ +const { body, query } = require('express-validator') + +module.exports = ({ field = 'username', type = 'string', method = 'post' }) => { + const q = method === 'post' ? body(field) : query(field) + return type === 'string' + ? q + .exists() + .withMessage(`${field} is required`) + .isString() + .withMessage(`${field} needs to be str`) + : type === 'bool' + ? q + .exists() + .withMessage(`${field} is required`) + .isBoolean() + .withMessage(`${field} needs to be boolean`) + : q.exists().withMessage(`${field} is required`) +} diff --git a/backend/routes/middleware/validation/controller.js b/backend/routes/middleware/validation/controller.js new file mode 100644 index 000000000..fb76ef389 --- /dev/null +++ b/backend/routes/middleware/validation/controller.js @@ -0,0 +1,13 @@ +const { validationResult } = require('express-validator') +const { ErrorHandler } = require('../../error') + +const validationHandling = (req, res, next) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + console.log('validation Errors:', errors.array()) + throw new ErrorHandler(400, errors.array()[0].msg) + } else { + return next() + } +} +module.exports = validationHandling diff --git a/backend/routes/middleware/validation/main.js b/backend/routes/middleware/validation/main.js new file mode 100644 index 000000000..d66afe886 --- /dev/null +++ b/backend/routes/middleware/validation/main.js @@ -0,0 +1,11 @@ +const validList = require('./validation') +const check = require('./controller') + +/** + * generate check fileds and send 404 if error + * @param {Object[]} rules + * @return {List} callback functions to put in router + */ +module.exports = (rules) => { + return [validList(rules), check] +} diff --git a/backend/routes/middleware/validation/package.json b/backend/routes/middleware/validation/package.json new file mode 100644 index 000000000..43156315a --- /dev/null +++ b/backend/routes/middleware/validation/package.json @@ -0,0 +1,3 @@ +{ + "main":"./main.js" +} \ No newline at end of file diff --git a/backend/routes/middleware/validation/validation.js b/backend/routes/middleware/validation/validation.js new file mode 100644 index 000000000..e37f8f517 --- /dev/null +++ b/backend/routes/middleware/validation/validation.js @@ -0,0 +1,6 @@ +module.exports = (rules) => { + return rules.map((rule) => { + if (typeof rule === 'string') return require('./Name/' + rule)({}) + return require('./Name/' + rule.filename)(rule) + }) +} diff --git a/backend/routes/readme.md b/backend/routes/readme.md new file mode 100644 index 000000000..67b209511 --- /dev/null +++ b/backend/routes/readme.md @@ -0,0 +1,2683 @@ + + +# EEplus website api v1.0.0 + +EE+ api 文件 + +- [In/abroadInfo](#inabroadinfo) + - [add abroadInfo](#add-abroadinfo) + - [delete abroadInfo](#delete-abroadinfo) + - [get abroadInfo](#get-abroadinfo) + - [update abroadInfo](#update-abroadinfo) +- [In/account](#inaccount) + - [change password](#change-password) + - [show personal info](#show-personal-info) +- [In/auth](#inauth) + - [刪除用戶](#刪除用戶) + - [身分驗證](#身分驗證) + - [查看待核可帳號](#查看待核可帳號) + - [新增或刪除管理員](#新增或刪除管理員) +- [In/career](#incareer) + - [add recruitment](#add-recruitment) + - [delete recruitment](#delete-recruitment) + - [search recruitment by field](#search-recruitment-by-field) + - [search recruitment by keywords](#search-recruitment-by-keywords) + - [show all recruitment](#show-all-recruitment) + - [show my recruitment](#show-my-recruitment) + - [update recruitment](#update-recruitment) +- [In/column](#incolumn) + - [add column](#add-column) + - [delete column](#delete-column) + - [get column detail](#get-column-detail) + - [get column outline with id optional](#get-column-outline-with-id-optional) + - [search column by keywords or hashtags](#search-column-by-keywords-or-hashtags) + - [update column](#update-column) +- [In/profile_new](#inprofile_new) + - [search profile by fields](#search-profile-by-fields) + - [search profile by keywords](#search-profile-by-keywords) + - [show my profile](#show-my-profile) + - [update profile](#update-profile) +- [In/recommendation](#inrecommendation) + - [add recommendation](#add-recommendation) + - [delete recommendation](#delete-recommendation) + - [search recommendation by field](#search-recommendation-by-field) + - [search recommendation by keywords](#search-recommendation-by-keywords) + - [show my recommendation](#show-my-recommendation) + - [update recommendation](#update-recommendation) +- [In/study](#instudy) + - [填配對表單](#填配對表單) + - [拿取個人表單](#拿取個人表單) + - [清空表單資料庫](#清空表單資料庫) + - [拿取本年表單連結](#拿取本年表單連結) + - [配對](#配對) + - [寄配對通知](#寄配對通知) + - [新增本年表單連結](#新增本年表單連結) +- [In/time](#intime) + - [拿取活動時間](#拿取活動時間) + - [設定活動時間](#設定活動時間) +- [Out/account](#outaccount) + - [accountActivate](#accountactivate) + - [isLogin](#islogin) + - [login](#login) + - [loginFB](#loginfb) + - [logout](#logout) + - [register](#register) + - [registerFB](#registerfb) + - [registerStep2](#registerstep2) +- [Out/contact](#outcontact) + - [get in touch](#get-in-touch) +- [Out/forget](#outforget) + - [activation](#activation) + - [forget](#forget) +- [Out/recent](#outrecent) + - [get recent column](#get-recent-column) + - [get recent recommendation](#get-recent-recommendation) + - [get recent recruitment](#get-recent-recruitment) + +--- + +# In/abroadInfo + +## add abroadInfo + +[Back to top](#top) + +新增留學資訊 + +``` +POST /addAbroadInfo +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ----- | -------- | -------------- | +| title | `String` | 學校名稱 | +| info | `String` | 學校資料超連結 | +| file | `File` | 學校校徽 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ----- | -------- | ----------- | +| title | `String` | title | +| \_id | `String` | \_id | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## delete abroadInfo + +[Back to top](#top) + +用\_id 刪除留學資訊 + +``` +DELETE /deleteAbroadInfo +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| \_id | `String` | 要刪除的 | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | |
  • | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## get abroadInfo + +[Back to top](#top) + +拿留學資訊 + +``` +POST /getAbroadInfo +``` + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| iconSrc | `String` | 圖片 | +| title | `String` | title | +| info | `String` | info | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## update abroadInfo + +[Back to top](#top) + +給\_id 更新留學資訊 + +``` +POST /updateAbroadInfo +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ----- | -------- | -------------- | +| \_id | `String` | \_id | +| title | `String` | 學校名稱 | +| info | `String` | 學校資料超連結 | +| file | `File` | 學校校徽 | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | | | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | -------------- | +| description | `String` | \_id not given | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料不存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/account + +## change password + +[Back to top](#top) + +重設密碼 + +``` +POST /chPassword +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | -------- | ----------- | +| oldPsw | `String` | 原本密碼 | +| newPsw | `String` | 新密碼 | + +### Success response + +#### Success response - `204` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | | | + +### Error response + +#### Error response - `401` + +| Name | Type | Description | +| ----------- | -------- | ------------ | +| description | `String` | 原始密碼錯誤 | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 帳號不存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## show personal info + +[Back to top](#top) + +顯示機密資料(不想被別人看到的部份,暫無) + +``` +POST /showPersonal +``` + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| -------- | ---- | ----------- | +| username | | 使用者名字 | +| account | | 使用者學號 | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/auth + +## 刪除用戶 + +[Back to top](#top) + +刪除用戶 + +``` +POST /delUser +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| account | `String` | 帳號 | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| account | `String` | account | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## 身分驗證 + +[Back to top](#top) + +身分驗證 + +``` +POST /handlePending +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---------- | --------- | -------------- | +| account | `String` | 學號 | +| acceptUser | `Boolean` | 是否接受此用戶 | + +### Success response + +#### Success response - `204` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | | | + +### Error response + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | -------------- | +| description | `String` | user not found | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## 查看待核可帳號 + +[Back to top](#top) + +查看待核可帳號 + +``` +POST /showPending +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| x | `x` | x | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| -------------- | ---------- | ----------- | +| pendings | `Object[]` | 各個帳號 | +|  username | `String` | 名字 | +|  account | `String` | 學號 | +|  email | `String` | 信箱 | +|  imgSrc | `String` | 證件照 | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## 新增或刪除管理員 + +[Back to top](#top) + +新增、刪除管理員 + +``` +POST /manageAuth +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | --------- | ------------------------------------------------- | +| account | `String` | 學號 | +| setAuth | `Boolean` | true:加成管理員;false:從管理員移除(可以移除自己) | + +### Success response + +#### Success response - `204` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | | | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/career + +## add recruitment + +[Back to top](#top) + +新增一筆職缺 + +``` +POST /addRecruitment +``` + +### Header examples + +header-config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------------ | ---------- | ------------------- | +| title | `String` | 職缺標題 | +| company_name | `String` | 公司名稱 | +| work_type | `String` | 職位(ex.前端工程師) | +| salary | `String` | 薪資 | +| experience | `String[]` | 經驗要求 | +| diploma | `String` | 學系要求 | +| requirement | `String[]` | 技能要求 | +| description | `String[]` | 其他描述 | +| file | `File` | 照片 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---- | ---- | ----------------------------------- | +| data | | 職缺標題 | +| \_id | | 職缺\_id(用來 search,update,delete) | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## delete recruitment + +[Back to top](#top) + +用\_id 刪除職缺 + +``` +DELETE /deleteRecruitment +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---- | -------- | ------------------------- | +| \_id | `String` | 要刪除職缺的 mongodb \_id | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ---- | ---- | ------------ | +| data | | 刪除職缺標題 | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ---------------------------------------- | +| description | `String` | not authorized(僅建立者與管理員可以刪除) | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | -------------- | +| description | `String` | \_id not found | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## search recruitment by field + +[Back to top](#top) + +指定欄位搜尋職缺 + +``` +POST /searchRecruitment +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------------ | -------- | ------------------- | +| \_id | `String` | \_id (optional) | +| account | `String` | 學號 (optional) | +| title | `String` | 職缺標題 (optional) | +| company_name | `String` | 公司名稱 (optional) | +| work_type | `String` | 職位 (optional) | +| salary | `String` | 薪資 (optional) | +| experience | `String` | 經驗要求 (optional) | +| diploma | `String` | 學系要求 (optional) | +| requirement | `String` | 技能要求 (optional) | +| description | `String` | 其他描述 (optional) | +| page | `Number` | default 1 | +| perpage | `Number` | default 20 | + +### Success response example + +#### Success response example - `Success-Response:` + +```json +[ + { + "_id": "String", + "title": { + "title": "String", + "company_name": "String", + "work_type": "String" + }, + "info": { + "salary": "String", + "experience": ["String"], + "diploma": "String" + }, + "spec": { + "requirement": ["String"], + "description": "String" + }, + "image": "String" + } +] +``` + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## search recruitment by keywords + +[Back to top](#top) + +用空格區分關鍵字進行搜尋 + +``` +POST /smartsearchRecruitment +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| keyword | `String` | 用空格區分 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------------------------ | ---------- | --------------------------------------------------- | +| - | `Object[]` | 職缺們 | +|  \_id | `String` | mongodb \_id(for delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   company_name | `String` | 公司名稱 | +|   work_type | `String` | 職位(ex.前端工程師) | +|  info | `Object` | 工作資訊 | +|   salary | `String` | 薪資 | +|   experience | `String[]` | 經驗要求 | +|   diploma | `String` | 學院要求 | +|  spec | `Object` | 詳細描述 | +|   requirement | `String[]` | 技能要求 | +|   description | `String[]` | 工作的其他描述 | +|  image | `String` | 公司頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## show all recruitment + +[Back to top](#top) + +顯示所有職缺(等價於不傳任何參數的 searchRecruitment) + +``` +POST /showRecruitment +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| page | `Number` | default 1 | +| perpage | `Number` | default 20 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------------------------ | ---------- | --------------------------------------------------- | +| - | `Object[]` | 職缺們 | +|  \_id | `String` | mongodb \_id(for delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   company_name | `String` | 公司名稱 | +|   work_type | `String` | 職位(ex.前端工程師) | +|  info | `Object` | 工作資訊 | +|   salary | `String` | 薪資 | +|   experience | `String[]` | 經驗要求 | +|   diploma | `String` | 學院要求 | +|  spec | `Object` | 詳細描述 | +|   requirement | `String[]` | 技能要求 | +|   description | `String[]` | 工作的其他描述 | +|  image | `String` | 公司頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## show my recruitment + +[Back to top](#top) + +顯示我的所有職缺 + +``` +GET /recruitment +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| page | `Number` | default 1 | +| perpage | `Number` | default 20 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------------------------ | ---------- | --------------------------------------------------- | +| - | `Object[]` | 職缺們 | +|  \_id | `String` | mongodb \_id(for delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   company_name | `String` | 公司名稱 | +|   work_type | `String` | 職位(ex.前端工程師) | +|  info | `Object` | 工作資訊 | +|   salary | `String` | 薪資 | +|   experience | `String[]` | 經驗要求 | +|   diploma | `String` | 學院要求 | +|  spec | `Object` | 詳細描述 | +|   requirement | `String[]` | 技能要求 | +|   description | `String[]` | 工作的其他描述 | +|  image | `String` | 公司頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | not login | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## update recruitment + +[Back to top](#top) + +更新一筆職缺 + +``` +PATCH /recruitment +``` + +### Header examples + +header-config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------------ | ---------- | ----------------------------- | +| \_id | `String` | 要更新職缺的 mongodb \_id | +| title | `String` | 職缺標題(optional) | +| company_name | `String` | 公司名稱(optional) | +| work_type | `String` | 職位(ex.前端工程師)(optional) | +| salary | `String` | 薪資(optional) | +| experience | `String[]` | 經驗要求(optional) | +| diploma | `String` | 學系要求(optional) | +| requirement | `String[]` | 技能要求(optional) | +| description | `String[]` | 其他描述(optional) | +| file | `File` | 照片(optional) | + +### Success response + +#### Success response - `203` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | |
  • | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ------------------------------ | +| description | `String` | unauthorized(僅建立者可以更新) | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | --------------- | +| description | `String` | \_id not exists | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/column + +## add column + +[Back to top](#top) + +管理員新增文章 + +``` +POST /column/add +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---------------------------- | ---------- | ----------------------------------------------------------------------------- | +| title | `String[]` | 文章標題 (xxxx 級 xxx (公司名稱與職位))(這邊看要不要和 name,experience 合併?) | +| id | `String` | 文章的編號 (建議 yymm) | +| top | `Object` | | +|  name | `String` | 標題(xxxx 級 xxx) | +|  experience | `String` | 副標題(公司名稱與職位) | +|  hashtags | `String[]` | 文章的 hashtag (文章類別,訪問者姓名、級別、工作、相關組織與企業) | +|  body | `Object[]` | | +|   bigtitle | `String` | (一、標題,二、求學階段...) | +|   bigsections | `Object[]` | | +|    subtitle | `String` | 子標題 | +|    subsection | `String` | (文章內容) | +|  annotation | `String[]` | 參與全人員 | +|   job | `String[]` | 工作 | +|   contributer | `String[]` | 人員 | +| anno | `String[]` | [所有採訪人員姓名] | +| date | `String[]` | yyyy/mm/dd 星期 x | +| exp | `String[]` | 職位 | +| edu | `String[]` | 學歷 [學士:校系(畢業年分),碩士:校系(畢業年分),博士:校系(畢業年分)] | +| intro | `String[]` | 簡介 (1 個 element 是一段) | +| file | `File` | 封面照 | + +### Parameters examples + +`js` - Input-Example: + +```js +let input = new FormData() + +input.append('file', 採訪合照) +input.append('id', 'yymm') +input.append('title', '2008級 方劭云(當屆最年輕升遷副教授)') +input.append('top[name]', '2008級 方劭云') +input.append('top[experience]', '當屆最年輕升遷副教授') +input.append('top[hashtags][0]', 關鍵字1) +input.append('annotation[annotation][0][job]', '撰寫') +input.append('annotation[annotation][0][contributer]][]', '王曉明') +input.append('anno[]', '作者1') +input.append('date', 'yyyy/mm/dd 星期x') +input.append('exp[0]', '現任:國立臺灣科技大學電機系 副教授') +input.append('edu[0]', '博士:台灣大學電子所 (2013)') +input.append('intro[0]', '2008畢業於台大電機,目前任職於臺灣科技大學的方劭云教授...') + +input.append('body[body][][bigtitle]', '一、我的大學生涯') +input.append('body[body][][bigsections][0][subtitle]', '球隊與課業交織的辛苦大學生活') +input.append( + 'body[body][][bigsections][0][subsection]', + '因為我是排球校隊,沒能花很多時間在系上...', +) + +axios.post('/api/addColumn', input, { headers: { 'content-type': 'multipart/form-data' } }) +``` + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| id | `String` | post 的 id | + +### Error response + +#### Error response - `400` + +| Name | Type | Description | +| ----------- | -------- | -------------- | +| description | `String` | id is required | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## delete column + +[Back to top](#top) + +管理員刪除文章 + +``` +DELETE /column/delete +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---- | -------- | ---------------------- | +| id | `String` | 文章的編號 (建議 yymm) | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| id | `String` | post 的 id | + +### Error response + +#### Error response - `400` + +| Name | Type | Description | +| ----------- | -------- | -------------- | +| description | `String` | id is required | + +## get column detail + +[Back to top](#top) + +拿詳細文章內容 + +``` +GET /column/detail +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---- | -------- | -------------- | +| id | `String` | yymm(required) | + +### Success response example + +#### Success response example - `Success-Response:` + +```json +{ + "top": { + "name": "String", + "experience": "String", + "hashtags": ["String"] + }, + "body": { + "body": [ + { + "bigtitle": "String", + "bigsections": [ + { + "subtitle": "String", + "subsection": "String" + } + ] + } + ] + }, + "annotation": { + "annotation": [ + { + "job": "String", + "contributer": "String" + } + ] + }, + "id": "String" +} +``` + +### Error response + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ------------------------- | +| description | `String` | id is required/資料不存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## get column outline with id optional + +[Back to top](#top) + +拿 Outline 資料(含圖片) + +``` +GET /column/outline +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ---------------------------- | +| id | `String` | id(optional,若未給則送全部) | +| perpage | `String` | 一頁數量(optional,default 5) | +| page | `String` | 頁數(optional,default 1) | + +### Success response example + +#### Success response example - `Success-Response:` + +```json +{ + "data": [ + { + "anno": ["String"], + "date": "String", + "title": ["String"], + "exp": ["String"], + "edu": ["String"], + "intro": ["String"], + "id": "String", + "imgSrc": "String" + } + ], + "maxPage": "Number" +} +``` + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## search column by keywords or hashtags + +[Back to top](#top) + +用 keyword(空格區分)或 hashtag 搜尋 + +``` +GET /column/search +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| -------- | -------- | ---------------------------- | +| keyword | `String` | 用空格區分 | +| hashtags | `String` | 用 hashtags 搜尋 | +| perpage | `String` | 一頁數量(optional,default 5) | +| page | `String` | 頁數(optional,default 1) | + +### Success response example + +#### Success response example - `Success-Response:` + +```json +{ + "data": [ + { + "anno": ["String"], + "date": "String", + "title": ["String"], + "exp": ["String"], + "edu": ["String"], + "intro": ["String"], + "id": "String", + "imgSrc": "String" + } + ], + "maxPage": "Number" +} +``` + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## update column + +[Back to top](#top) + +管理員更新文章 + +``` +PATCH /column/update +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---------------------------- | ---------- | ----------------------------------------------------------------------------- | +| title | `String[]` | 文章標題 (xxxx 級 xxx (公司名稱與職位))(這邊看要不要和 name,experience 合併?) | +| id | `String` | 文章的編號 (建議 yymm) | +| top | `Object` | | +|  name | `String` | 標題(xxxx 級 xxx) | +|  experience | `String` | 副標題(公司名稱與職位) | +|  hashtags | `String[]` | 文章的 hashtag (文章類別,訪問者姓名、級別、工作、相關組織與企業) | +|  body | `Object[]` | | +|   bigtitle | `String` | (一、標題,二、求學階段...) | +|   bigsections | `Object[]` | | +|    subtitle | `String` | 子標題 | +|    subsection | `String` | (文章內容) | +|  annotation | `String[]` | 參與全人員 | +|   job | `String[]` | 工作 | +|   contributer | `String[]` | 人員 | +| anno | `String[]` | [所有採訪人員姓名] | +| date | `String[]` | yyyy/mm/dd 星期 x | +| exp | `String[]` | 職位 | +| edu | `String[]` | 學歷 [學士:校系(畢業年分),碩士:校系(畢業年分),博士:校系(畢業年分)] | +| intro | `String[]` | 簡介 (1 個 element 是一段) | +| file | `File` | 頭貼 | + +### Parameters examples + +`js` - Input-Example: + +```js +let input = new FormData() + +input.append('file', 採訪合照) +input.append('id', 'yymm') +input.append('title', '2008級 方劭云(當屆最年輕升遷副教授)') +input.append('top[name]', '2008級 方劭云') +input.append('top[experience]', '當屆最年輕升遷副教授') +input.append('top[hashtags][0]', 關鍵字1) +input.append('annotation[annotation][0][job]', '撰寫') +input.append('annotation[annotation][0][contributer]][]', '王曉明') +input.append('anno[]', '作者1') +input.append('date', 'yyyy/mm/dd 星期x') +input.append('exp[0]', '現任:國立臺灣科技大學電機系 副教授') +input.append('edu[0]', '博士:台灣大學電子所 (2013)') +input.append('intro[0]', '2008畢業於台大電機,目前任職於臺灣科技大學的方劭云教授...') + +input.append('body[body][][bigtitle]', '一、我的大學生涯') +input.append('body[body][][bigsections][0][subtitle]', '球隊與課業交織的辛苦大學生活') +input.append( + 'body[body][][bigsections][0][subsection]', + '因為我是排球校隊,沒能花很多時間在系上...', +) + +axios.post('/api/updateColumn', input, { headers: { 'content-type': 'multipart/form-data' } }) +``` + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| id | `String` | post 的 id | + +### Error response + +#### Error response - `400` + +| Name | Type | Description | +| ----------- | -------- | -------------- | +| description | `String` | id is required | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/profile_new + +## search profile by fields + +[Back to top](#top) + +給定欄位搜尋 porfile(OR) + +``` +POST /searchProfile +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------------ | -------- | ------------------------------------------------------------------------------ | +| account | `String` | 學號(用'x'進行模糊搜尋, ex.'b079010xx') | +| username | `String` | 名字 | +| nickname | `String` | 綽號 | +| profile | `String` | 自介 | +| publicEmail | `String` | 公開信相 | +| cellphone | `String` | 手機 | +| CC | `String` | city and country | +| web | `String` | 個人部落格 | +| facebook | `String` | facebook | +| Linkedin | `String` | Linkedin | +| github | `String` | github | +| major | `Object` | 主修 | +| double_major | `String` | 雙主修 | +| minor | `String` | 輔修 | +| master | `String` | 碩士 | +| doctor | `String` | 博士 | +| Occupation | `Object` | 工作(這裡是 obj 不是 array 喔,會用 and 搜尋)(COP3 個中也可以只填 1 個或 2 個) | +|  C | `String` | 公司 | +|  O | `String` | 部門 | +|  P | `String` | 職位 | +| page | `Number` | default 1 | +| perpage | `Number` | default 50 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------------ | ---------- | ----------------------------------------------------- | +| userimage | `String` | 大頭貼(使用<img src={userimage}/>) | +| account | `String` | 學號 | +| username | `String` | 名字 | +| nickname | `String` | 綽號 | +| profile | `String` | 自介 | +| publicEmail | `String` | 公開信相 | +| cellphone | `String` | 手機 | +| CC | `String` | city and country | +| web | `String` | 個人部落格 | +| facebook | `String` | facebook | +| Linkedin | `String` | Linkedin | +| github | `String` | github | +| major | `String` | 學士 | +| double_major | `String` | 雙主修 | +| minor | `String` | 輔系 | +| master | `String` | 碩士 | +| doctor | `String` | 博士 | +| Occupation | `Object[]` | 職業 | +|  C | `String` | 公司 | +|  O | `String` | 部門 | +|  P | `String` | 職稱 | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## search profile by keywords + +[Back to top](#top) + +給定關鍵字(用空格區分)搜尋 + +``` +POST /smartsearchProfile +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| keyword | `String` | 用空格區分 | +| page | `String` | | +| perpage | `String` | default 50 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------------ | ---------- | ----------------------------------------------------- | +| userimage | `String` | 大頭貼(使用<img src={userimage}/>) | +| account | `String` | 學號 | +| username | `String` | 名字 | +| nickname | `String` | 綽號 | +| profile | `String` | 自介 | +| publicEmail | `String` | 公開信相 | +| cellphone | `String` | 手機 | +| CC | `String` | city and country | +| web | `String` | 個人部落格 | +| facebook | `String` | facebook | +| Linkedin | `String` | Linkedin | +| github | `String` | github | +| major | `String` | 學士 | +| double_major | `String` | 雙主修 | +| minor | `String` | 輔系 | +| master | `String` | 碩士 | +| doctor | `String` | 博士 | +| Occupation | `Object[]` | 職業 | +|  C | `String` | 公司 | +|  O | `String` | 部門 | +|  P | `String` | 職稱 | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## show my profile + +[Back to top](#top) + +顯示個人 profile + +``` +GET /profile +``` + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------------ | ---------- | ----------------------------------------------------- | +| userimage | `String` | 大頭貼(使用<img src={userimage}/>) | +| account | `String` | 學號 | +| username | `String` | 名字 | +| nickname | `String` | 綽號 | +| profile | `String` | 自介 | +| publicEmail | `String` | 公開信相 | +| cellphone | `String` | 手機 | +| CC | `String` | city and country | +| web | `String` | 個人部落格 | +| facebook | `String` | facebook | +| Linkedin | `String` | Linkedin | +| major | `String` | 學士 | +| double_major | `String` | 雙主修 | +| minor | `String` | 輔系 | +| master | `String` | 碩士 | +| doctor | `String` | 博士 | +| Occupation | `Object[]` | 職業 | +|  C | `String` | 公司 | +|  O | `String` | 部門 | +|  P | `String` | 職稱 | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## update profile + +[Back to top](#top) + +更新 porfile + +``` +PATCH /profile +``` + +### Header examples + +header-config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------------ | ---------- | ----------------------------------------------- | +| userimage | `File` | 大頭貼 | +| username | `String` | 名字//account 禁止修改 | +| nickname | `String` | 綽號 | +| profile | `String` | 自介 | +| publicEmail | `String` | 公開信相 | +| cellphone | `String` | 手機 | +| CC | `String` | city and country | +| web | `String` | 個人部落格 | +| facebook | `String` | facebook | +| Linkedin | `String` | Linkedin | +| major | `String` | 主修 | +| double_major | `String` | 雙主修 | +| minor | `String` | 輔修 | +| master | `String` | 碩士 | +| doctor | `String` | 博士 | +| Occupation | `Object[]` | 工作(因為 array 運算複雜,請直接給我完整的覆蓋) | +|  C | `String` | 公司,append('Occupation[${index}][c]',vlaue) | +|  O | `String` | 部門,append('Occupation[${index}][o]',vlaue) | +|  P | `String` | 職位,append('Occupation[${index}][p]',vlaue) | + +### Success response + +#### Success response - `204` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | | | + +### Error response + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 帳號不存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/recommendation + +## add recommendation + +[Back to top](#top) + +新增簡歷 + +``` +POST /recommendation +``` + +### Header examples + +header-config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---------------- | ---------- | ----------- | +| title | `String` | 簡歷標題 | +| name | `String` | 姓名 | +| desire_work_type | `String` | 想要職位 | +| contact | `String` | 電話 | +| email | `String` | 信箱 | +| diploma | `String` | 學位 | +| experience | `String[]` | 經驗 | +| speciality | `String[]` | 專長 | +| file | `File` | 照片 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ----- | ---- | -------------- | +| title | | 簡歷標題 | +| \_id | | mongoDB 的\_id | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## delete recommendation + +[Back to top](#top) + +刪除簡歷 + +``` +DELETE /recommendation +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---- | -------- | ----------------------- | +| \_id | `String` | get 或 add 時回傳的\_id | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ----- | ---- | ----------- | +| title | | title | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | -------------- | +| description | `String` | not authorized | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | -------------------------------- | +| description | `String` | \_id not found or not authorized | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## search recommendation by field + +[Back to top](#top) + +搜尋簡歷 + +``` +GET /recommendation +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---------------- | -------- | ----------- | +| \_id | `String` | \_id | +| account | `String` | 學號 | +| title | `String` | 簡歷標題 | +| name | `String` | 姓名 | +| desire_work_type | `String` | 想要職位 | +| contact | `String` | 電話 | +| email | `String` | 信箱 | +| diploma | `String` | 學位 | +| experience | `String` | 經驗 | +| speciality | `String` | 專長 | +| page | `Number` | default 1 | +| perpage | `Number` | default 50 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---------------------------- | ---------- | ----------------------------------------------- | +| - | `Object[]` | 簡歷們 | +|  \_id | `String` | mongodb \_id(for update,delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   name | `String` | 名字 | +|   desire_work_type | `String` | 想要職位 | +|  info | `Object` | 工作資訊 | +|   contact | `String` | 電話 | +|   email | `String[]` | 信箱 | +|   diploma | `String` | 學院 | +|  spec | `Object` | 詳細描述 | +|   experience | `String[]` | 經驗 | +|   speciality | `String[]` | 專長 | +|  image | `String` | 頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| - | `String` | not login | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## search recommendation by keywords + +[Back to top](#top) + +關鍵字搜尋(空格區分) + +``` +POST /smartsearchrecommendation +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| keyword | `String` | 用空格區分 | +| page | `Number` | default 1 | +| perpage | `Number` | default 50 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---------------------------- | ---------- | ----------------------------------------------- | +| - | `Object[]` | 簡歷們 | +|  \_id | `String` | mongodb \_id(for update,delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   name | `String` | 名字 | +|   desire_work_type | `String` | 想要職位 | +|  info | `Object` | 工作資訊 | +|   contact | `String` | 電話 | +|   email | `String[]` | 信箱 | +|   diploma | `String` | 學院 | +|  spec | `Object` | 詳細描述 | +|   experience | `String[]` | 經驗 | +|   speciality | `String[]` | 專長 | +|  image | `String` | 頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| - | `String` | not login | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## show my recommendation + +[Back to top](#top) + +顯示我建立的簡歷 + +``` +GET /recommendation/mine +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| page | `Number` | default 1 | +| perpage | `Number` | default 20 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---------------------------- | ---------- | ----------------------------------------------- | +| - | `Object[]` | 簡歷們 | +|  \_id | `String` | mongodb \_id(for update,delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   name | `String` | 名字 | +|   desire_work_type | `String` | 想要職位 | +|  info | `Object` | 工作資訊 | +|   contact | `String` | 電話 | +|   email | `String` | 信箱 | +|   diploma | `String` | 學院 | +|  spec | `Object` | 詳細描述 | +|   experience | `String[]` | 經驗 | +|   speciality | `String[]` | 專長 | +|  image | `String` | 頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| - | `String` | not login | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## update recommendation + +[Back to top](#top) + +更新簡歷 + +``` +PATCH /recommendation +``` + +### Header examples + +header-config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---------------- | ---------- | ----------------------- | +| \_id | `String` | get 或 add 時回傳的\_id | +| title | `String` | 簡歷標題 | +| name | `String` | 姓名 | +| desire_work_type | `String` | 想要職位 | +| contact | `String` | 電話 | +| email | `String` | 信箱 | +| diploma | `String` | 學位 | +| experience | `String[]` | 經驗 | +| speciality | `String[]` | 專長 | +| file | `File` | 照片 | + +### Success response + +#### Success response - `203` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | |
  • | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ----------------------------------- | +| description | `String` | not valid \_id or account not match | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/study + +## 填配對表單 + +[Back to top](#top) + +填寫配對表單以提供資料供學長姊、學弟妹配對時使用 + +``` +POST /study/fillForm +``` + +### Request body + +Request body 會依據身分(學長姐 or 學弟妹)不同而有些微不同 +| Key | Type | value | description | +| -------- | ------ | ----- | ----------- | +| Identity | `String` | `"senior"` / `"junior"` | 填表單者的身分 | + +- seniorForm: 若上面的 `Identity` 值為 `"senior"`,後端會將資料填入 seniorForm + | Key | Type | value | description | + | -------- | ------ | ----- | ----------- | + | name | `String` | `"王小明"` | 填表單者的姓名 | + | degree | `Number` | `0`, `1` | 申請的學位(0: MS, 1: PhD) | + | major | `String` | `"CS"` | 專長領域 | + | gpa | `Number` | `4.3` | gpa (0 ~ 4.3) | + | email | `String` | `example@gmail.com` | 電子信箱 | + | number | `Number` | `2` | 願意提供分享的學弟妹人數 | + | admission | `String[]` | `["MIT", "Stanford"]` | 當時錄取了哪些學校 | + | school | `String` | `"MIT"` | 最後去哪一間學校 | + +- juniorForm: 若上面的 `Identity` 值為 `"junior"`,後端會將資料填入 juniorForm + | Key | Type | value | description | + | -------- | ------ | ----- | ----------- | + | name | `String` | `"王小明"` | 填表單者的姓名 | + | degree | `Number[]` | `[0, 1]` | 欲申請的學位(0: MS, 1: PhD) | + | hasPaper | `Number` | `0 ~ 3` | 0: 無論文經驗, 1: 已投稿但尚未公佈, 2: 已發表 1 篇, 3: 已發表 2 篇以上 | + | major | `String[]` | `["通信", "電磁"]` | 專長領域 | + | gpa | `Number` | `4.3` | gpa (0 ~ 4.3) | + | email | `String` | `example@gmail.com` | 電子信箱 | + | account | `String` | `"B12345678"` | 學號 | + | school1 | `String[]` | `["MIT", "Stanford"]` | 夢幻學校(沒有填 "無") | + | school2 | `String[]` | `["NTU"]` | 有把握的學校 | + | school3 | `String[]` | `["無"]` | 保底學校 | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ---- | ---- | ------------ | +| - | | "Form saved" | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ---- | ---- | ------------------------------------ | +| - | | "Encounter error when filling forms" | + +## 拿取個人表單 + +[Back to top](#top) + +拿取個人表單 + +``` +GET /study/form +``` + +### Success response + +#### Success response - `201` + +依照此人的身分(學長姐 or 學弟妹)有些許不同 + +- 學長姐 + | Key | Type | value | description | + | -------- | ------ | ----- | ----------- | + | identity | `String` | `"senior"` | 身分為學長姐 | + | name | `String` | `"王小明"` | 填表單者的姓名 | + | degree | `String` | `0` | 申請到的學位(0: MS, 1: PhD) | + | major | `String` | `CS` | 專長領域 | + | gpa | `Number` | `4.3` | gpa (0 ~ 4.3) | + | email | `String` | `example@gmail.com` | 電子信箱 | + | number | `Number` | `2` | 願意提供分享的學弟妹人數 | + | admission | `String[]` | `["MIT", "Stanford"]` | 當時錄取了哪些學校 | + | school | `String` | `"MIT"` | 最後去哪一間學校 | + +- 學弟妹 + | Key | Type | value | description | + | -------- | ------ | ----- | ----------- | + | identity | `String` | `"senior"` | 身分為學長姐 | + | name | `String` | `"王小明"` | 填表單者的姓名 | + | degree | `Number[]` | `[0, 1]` | 欲申請的學位(0: MS, 1: PhD) | + | hasPaper | `Number` | `0 ~ 3` | 0: 無論文經驗, 1: 已投稿但尚未公佈, 2: 已發表 1 篇, 3: 已發表 2 篇以上 | + | major | `String[]` | `["通信", "電磁"]` | 專長領域 | + | gpa | `Number` | `4.3` | gpa (0 ~ 4.3) | + | email | `String` | `example@gmail.com` | 電子信箱 | + | studentID | `String` | `"B12345678"` | 學號 | + | school1 | `String[]` | `["MIT"]` | 夢幻學校(沒有填 "無") | + | school2 | `String[]` | `["NTU"]` | 有把握的學校 | + | school3 | `String[]` | `["無"]` | 保底學校 | + +### Error response + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ------------------- | +| description | `String` | form data not found | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## 清空表單資料庫 + +[Back to top](#top) + +清空表單資料庫 + +``` +DELETE /study/form +``` + +### success response + +#### Success rersponse - `200` + +| Name | Type | Description | +| ----------- | -------- | ------------ | +| description | `String` | 資料庫已清空 | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 權限錯誤 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## 拿取本年表單連結 + +[Back to top](#top) + +拿取本年表單連結 + +``` +GET /study/links +``` + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------ | -------- | -------------- | +| senior | `String` | 學長姊表單連結 | +| junior | `String` | 學弟妹表單連結 | +| note | `String` | 備註 | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ------------------- | +| description | `String` | server not open yet | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | not found | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## 配對 + +[Back to top](#top) + +給學長姊跟學弟妹留學配對的.xlsx 檔,幫他們配對 + +``` +POST /study/matching +``` + +### Header examples + +config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ---- | ------ | ----------- | +| - | `File` | result.xlsx | + +## 寄配對通知 + +[Back to top](#top) + +給/study/matching 拿到的 output.xlsx 檔,並寄信 + +``` +POST /study/sendmail +``` + +### Header examples + +config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | ------ | ------------------------ | +| result | `File` | /study/match 產生的.xlsx | + +### Success response + +#### Success response - `203` + +| Name | Type | Description | +| ------ | ---------- | -------------------- | +| errors | `String[]` | 發生錯誤的寄件者姓名 | + +## 新增本年表單連結 + +[Back to top](#top) + +設定本年表單 + +``` +POST /study/addLink +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | -------- | -------------------- | +| senior | `String` | 學長姊表單連結 | +| junior | `String` | 學弟妹表單連結 | +| note | `String` | 備註(截止時間之類的) | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| x | `String` | data stored | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# In/time + +## 拿取活動時間 + +[Back to top](#top) + +拿取特定活動的時間 + +``` +GET /time/getTime +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | -------- | ----------- | +| target | `String` | 活動名稱 | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ---- | -------- | ---------------- | +| - | `String` | yyyy-mm-dd-hh-mm | + +## 設定活動時間 + +[Back to top](#top) + +設定特定活動的時間 + +``` +POST /time/setTime +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | -------- | ----------- | +| target | `String` | 活動名稱 | +| time | `Date` | 時間 | + +#### Success response - `200` + +| Name | Type | Description | +| ---- | -------- | ----------------------------------- | +| - | `String` | successfully set {target} at {time} | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ---- | -------- | ----------------------------------------- | +| - | `String` | Encounter error when settinng time: + err | + +# Out/account + +## accountActivate + +[Back to top](#top) + +註冊後用信箱寄這個連結,驗證。完成後自動導向 + +``` +GET /regact/:account/:active +``` + +### Header examples + +config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| account | `String` | 學號 | +| active | `String` | 驗證碼 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---- | ------ | ----------- | +| - | `HTML` |
  • | + +### Error response + +#### Error response - `400` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 請添加照片 | + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 帳號已存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## isLogin + +[Back to top](#top) + +檢查是否有登入 + +``` +POST /isLogin +``` + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| account | `String` | 登入者學號 | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ------------------ | +| description | `String` | "未登入" | + +## login + +[Back to top](#top) + +登入 + +``` +POST /login +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| -------- | -------- | ------------------------ | +| account | `String` | 學號 | +| password | `String` | 密碼(以後建議在前端加密) | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| -------- | --------- | ------------ | +| username | `String` | 登入者名字 | +| account | `String` | 登入者學號 | +| isAuth | `Boolean` | 是否是管理員 | + +### Error response + +#### Error response - `401` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 密碼錯誤 | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 帳號不存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## loginFB + +[Back to top](#top) + +登入 by facebook + +``` +POST /loginFB +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ---------- | -------- | ----------- | +| facebookID | `String` | facebook ID | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| -------- | --------- | ------------ | +| username | `String` | 登入者名字 | +| account | `String` | 學號 | +| isAuth | `Boolean` | 是否是管理員 | + +### Error response + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 帳號不存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## logout + +[Back to top](#top) + +登出 + +``` +POST /logout +``` + +### Success response + +#### Success response - `204` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | | | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | -------------------------------- | +| description | `String` | "session destroy 失敗" | + +## register + +[Back to top](#top) + +註冊(by 學號 & email),.env 設定 newReg=true 使用新註冊規則 + +``` +POST /register +``` + +### Header examples + +config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| --------------- | -------- | ---------------------------------------------------------------- | +| account | `String` | 學號 | +| password | `String` | 密碼(以後建議在前端加密) | +| ConfirmPassword | `String` | 二次密碼 | +| username | `String` | 使用者名字 | +| Email | `String` | 信箱 | +| isGraduated | `String` | false 則寄送 email 給account@ntu.edu.tw(newRule=version3 才需要) | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ----------- | -------- | ----------------------------------------------------------------- | +| username | `String` | 姓名(newRule=false) | +| isGraduated | `String` | isGraduated(newRule=version3) | +| email | `String` | account@ntu.edu.tw(newRule=version3 && isGraduated=false) | + +### Error response + +#### Error response - `400` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 請添加照片 | + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 帳號已存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## registerFB + +[Back to top](#top) + +註冊(by facebook ID),在.env 用 newReg=version3 + +``` +POST /registerFB +``` + +### Header examples + +config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ----------- | -------- | ---------------------------------------------------------------- | +| facebookID | `String` | facebookID | +| account | `String` | 學號 | +| username | `String` | 使用者名字 | +| file | `File` | 身分證明的照片(optional) | +| avatar | `File` | 大頭貼(optional) | +| Email | `String` | Email(newRule=true,version3 才需要) | +| isGraduated | `String` | false 則寄送 email 給account@ntu.edu.tw(newRule=version3 才需要) | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ----------- | -------- | ----------------------------------------------------------------- | +| username | `String` | 使用者名字(newRule=false) | +| isGraduated | `String` | isGraduated(newRule=version3) | +| email | `String` | account@ntu.edu.tw(newRule=version3 && isGraduated=false) | + +### Error response + +#### Error response - `400` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 請添加照片 | + +#### Error response - `403` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 帳號已存在 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## registerStep2 + +[Back to top](#top) + +註冊,新增照片(供管理員檢視) + +``` +POST /register_step2 +``` + +### Header examples + +config + +```json +{ "content-type": "multipart/form-data" } +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| account | `String` | 學號 | +| file | `File` | 身分證明 | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| - | `String` |
  • | + +### Error response + +#### Error response - `400` + +| Name | Type | Description | +| ----------- | -------- | ------------- | +| description | `String` | img not given | + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------------- | +| description | `String` | account not found | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +# Out/contact + +## get in touch + +[Back to top](#top) + +用 google 表單蒐集站內信,統整在google sheet + +``` +GET https://docs.google.com/forms/d/e/1FAIpQLSed-GXXBhqIRUBEX7-nMlwuQ3a22-Z51mtxVSlcyhWzG9TH2Q/formResponse +``` + +### Parameters examples + +`js` - Input-Example: + +```js +axios.get(url, { + usp: 'pp_url', + 'entry.1670134810': 'your name', + 'entry.302205267': 'your email', + 'entry.307115258': 'your message', +}) +``` + +# Out/forget + +## activation + +[Back to top](#top) + +檢查激活碼,忘記密碼重設 + +``` +POST /activation +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| -------- | -------- | ------------------------ | +| account | `String` | 學號 | +| active | `String` | 激活碼(附在信箱的連結裡) | +| password | `String` | 要重設的密碼 | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ---- | ---- | ----------- | +| - | | | + +### Error response + +#### Error response - `401` + +| Name | Type | Description | +| ----------- | -------- | -------------------------------- | +| description | `String` | 驗證碼已不存在,請至 forget 頁面 | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## forget + +[Back to top](#top) + +忘記密碼,寄信 + +``` +POST /forget +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------- | -------- | ----------- | +| account | `String` | 學號 | + +### Success response + +#### Success response - `200` + +| Name | Type | Description | +| ----- | -------- | ------------------------------------------------------------- | +| email | `String` |
  • 使用者填寫的 email
  • "您的私人信箱"
  • | + +### Error response + +#### Error response - `404` + +| Name | Type | Description | +| ----------- | -------- | ----------------------------------------------------- | +| description | `String` |
  • 帳號不存在
  • 未設定信箱,請聯絡管理員
  • | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | --------------------------------------------------------------- | +| description | `String` |
  • 資料庫錯誤
  • 信件範本讀取失敗
  • 寄信失敗
  • | + +# Out/recent + +## get recent column + +[Back to top](#top) + +拿 Outline 資料(含圖片) + +``` +GET /column/recent +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | -------- | --------------- | +| number | `Number` | 篇數(default:5) | + +### Success response example + +#### Success response example - `Success-Response:` + +```json +{ + "data": [ + { + "anno": ["String"], + "date": "String", + "title": ["String"], + "exp": ["String"], + "edu": ["String"], + "intro": ["String"], + "id": "String", + "columnImg": { + "data": "Buffer", + "contentType": "String" + } + } + ] +} +``` + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## get recent recommendation + +[Back to top](#top) + +搜尋簡歷 + +``` +GET /recommendation/recent +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | -------- | --------------- | +| number | `Number` | 篇數(default:5) | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ---------------------------- | ---------- | ----------------------------------------------- | +| - | `Object[]` | 簡歷們 | +|  \_id | `String` | mongodb \_id(for update,delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   name | `String` | 名字 | +|   desire_work_type | `String` | 想要職位 | +|  info | `Object` | 工作資訊 | +|   contact | `String` | 電話 | +|   email | `String[]` | 信箱 | +|   diploma | `String` | 學院 | +|  spec | `Object` | 詳細描述 | +|   experience | `String[]` | 經驗 | +|   speciality | `String[]` | 專長 | +|  image | `String` | 頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `403` + +| Name | Type | Description | +| ---- | -------- | ----------- | +| - | `String` | not login | + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | + +## get recent recruitment + +[Back to top](#top) + +顯示所有職缺(等價於不傳任何參數的 searchRecruitment) + +``` +GET /recruitment/recent +``` + +### Parameters - `Parameter` + +| Name | Type | Description | +| ------ | -------- | --------------- | +| number | `Number` | 篇數(default:5) | + +### Success response + +#### Success response - `201` + +| Name | Type | Description | +| ------------------------ | ---------- | --------------------------------------------------- | +| - | `Object[]` | 職缺們 | +|  \_id | `String` | mongodb \_id(for delete) | +|  title | `Object` | 標題相關 | +|   title | `String` | 標題 | +|   company_name | `String` | 公司名稱 | +|   work_type | `String` | 職位(ex.前端工程師) | +|  info | `Object` | 工作資訊 | +|   salary | `String` | 薪資 | +|   experience | `String[]` | 經驗要求 | +|   diploma | `String` | 學院要求 | +|  spec | `Object` | 詳細描述 | +|   requirement | `String[]` | 技能要求 | +|   description | `String[]` | 工作的其他描述 | +|  image | `String` | 公司頭像(Ex. <img src={image}/>) | + +### Error response + +#### Error response - `500` + +| Name | Type | Description | +| ----------- | -------- | ----------- | +| description | `String` | 資料庫錯誤 | diff --git a/backend/routes/srcs/in/abroadInfo/addAbroadInfo.js b/backend/routes/srcs/in/abroadInfo/addAbroadInfo.js new file mode 100644 index 000000000..f4fbd9bfc --- /dev/null +++ b/backend/routes/srcs/in/abroadInfo/addAbroadInfo.js @@ -0,0 +1,30 @@ +const { dbCatch } = require('../../../error') +const Abroad_info = require('../../../Schemas/abroad_info') +const { parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /addAbroadInfo add abroadInfo + * @apiName addAbroadInfo + * @apiGroup In/abroadInfo + * @apiDescription 新增留學資訊 + * + * @apiParam {String} title 學校名稱 + * @apiParam {String} info 學校資料超連結 + * @apiParam {File} file 學校校徽 + * + * @apiSuccess (201) {String} title title + * @apiSuccess (201) {String} _id _id + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const addAbroadInfo = async (req, res) => { + const { title, info } = req.body + const icon = parseImg(req.file) + const { _id } = await new Abroad_info({ title, info, icon }).save().catch(dbCatch) + res.status(201).send({ title, _id }) +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'optional', field: ['title', 'info'] }] +module.exports = [valid(rules), asyncHandler(addAbroadInfo)] diff --git a/backend/routes/srcs/in/abroadInfo/deleteAbroadInfo.js b/backend/routes/srcs/in/abroadInfo/deleteAbroadInfo.js new file mode 100644 index 000000000..5776560df --- /dev/null +++ b/backend/routes/srcs/in/abroadInfo/deleteAbroadInfo.js @@ -0,0 +1,25 @@ +const Abroad_info = require('../../../Schemas/abroad_info') +const asyncHandler = require('express-async-handler') +const { dbCatch } = require('../../../error') + +/** + * @api {delete} /deleteAbroadInfo delete abroadInfo + * @apiName DeleteAbroadInfo + * @apiGroup In/abroadInfo + * @apiDescription 用_id刪除留學資訊 + * + * @apiparam {String} _id 要刪除的 + * + * @apiSuccess (200) - - + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const delAI = async (req, res, next) => { + const { _id } = req.body + await Abroad_info.findByIdAndDelete(_id).catch(dbCatch) + res.status(200).end() +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'required', field: '_id' }] +module.exports = [valid(rules), asyncHandler(delAI)] diff --git a/backend/routes/srcs/in/abroadInfo/getAbroadInfo.js b/backend/routes/srcs/in/abroadInfo/getAbroadInfo.js new file mode 100644 index 000000000..358e9bae9 --- /dev/null +++ b/backend/routes/srcs/in/abroadInfo/getAbroadInfo.js @@ -0,0 +1,23 @@ +const Abroad_info = require('../../../Schemas/abroad_info') +const asyncHandler = require('express-async-handler') +const { dbCatch, ErrorHandler } = require('../../../error') + +/** + * @api {post} /getAbroadInfo get abroadInfo + * @apiName GetAbroadInfo + * @apiGroup In/abroadInfo + * @apiDescription 拿留學資訊 + * + * + * @apiSuccess (201) {String} iconSrc 圖片 + * @apiSuccess (201) {String} title title + * @apiSuccess (201) {String} info info + * + * + * @apiError (500) {String} description 資料庫錯誤 + */ + +module.exports = asyncHandler(async (req, res, next) => { + const infos = await Abroad_info.find().catch(dbCatch) + return res.status(201).send(infos.map((info) => info.getPublic())) +}) diff --git a/backend/routes/srcs/in/abroadInfo/main.js b/backend/routes/srcs/in/abroadInfo/main.js new file mode 100644 index 000000000..f0b730305 --- /dev/null +++ b/backend/routes/srcs/in/abroadInfo/main.js @@ -0,0 +1,14 @@ +const express = require('express') +const router = express.Router() +const router_auth = express.Router() +const parseFile = require('../../../middleware/fileProcess') + +router_auth.post('/addAbroadInfo', parseFile('file'), require('./addAbroadInfo')) + +router.post('/getAbroadInfo', require('./getAbroadInfo')) + +router_auth.post('/updateAbroadInfo', parseFile('file'), require('./updateAbroadInfo')) + +router_auth.delete('/deleteAbroadInfo', require('./deleteAbroadInfo')) + +module.exports = { router, router_auth } diff --git a/backend/routes/srcs/in/abroadInfo/updateAbroadInfo.js b/backend/routes/srcs/in/abroadInfo/updateAbroadInfo.js new file mode 100644 index 000000000..c903fcaf0 --- /dev/null +++ b/backend/routes/srcs/in/abroadInfo/updateAbroadInfo.js @@ -0,0 +1,42 @@ +const Abroad_info = require('../../../Schemas/abroad_info') +const { updateQuery, parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') +const { dbCatch, ErrorHandler } = require('../../../error') + +/** + * @api {post} /updateAbroadInfo update abroadInfo + * @apiName updateAbroadInfo + * @apiGroup In/abroadInfo + * @apiDescription 給_id更新留學資訊 + * + * @apiParam {String} _id _id + * @apiParam {String} title 學校名稱 + * @apiParam {String} info 學校資料超連結 + * @apiParam {File} file 學校校徽 + * + * + * @apiSuccess (200) - + * + * @apiError (404) {String} description 資料不存在 + * @apiError (403) {String} description _id not given + * @apiError (500) {String} description 資料庫錯誤 + */ + +const updateAbroadInfo = async (req, res) => { + const { _id, title, info } = req.body + if (!_id) throw new ErrorHandler(403, '_id not given') + const obj = await Abroad_info.findOne({ _id }, 'title').catch(dbCatch) + if (!obj) throw new ErrorHandler(404, '資料不存在') + + const icon = parseImg(req.file) + const toSet = updateQuery({ title, info, icon }) + await Abroad_info.findByIdAndUpdate(_id, toSet).catch(dbCatch) + return res.status(200).end() +} + +const valid = require('../../../middleware/validation') +const rules = [ + { filename: 'required', field: '_id' }, + { filename: 'optional', field: ['title', 'info'] }, +] +module.exports = [valid(rules), asyncHandler(updateAbroadInfo)] diff --git a/backend/routes/srcs/in/account/auth/deleteUser.js b/backend/routes/srcs/in/account/auth/deleteUser.js new file mode 100644 index 000000000..ab3508cd0 --- /dev/null +++ b/backend/routes/srcs/in/account/auth/deleteUser.js @@ -0,0 +1,27 @@ +const { dbCatch, ErrorHandler } = require('../../../../error') +const Visual = require('../../../../Schemas/user_visual_new') +const Login = require('../../../../Schemas/user_login') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /delUser 刪除用戶 + * @apiName delUser + * @apiGroup In/auth + * @apiDescription 刪除用戶 + * + * @apiparam {String} account 帳號 + * + * @apiSuccess (200) {String} account account + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const deleteUser = async (req, res, next) => { + const { account } = req.body + await Visual.deleteOne({ account }).catch(dbCatch) + await Login.deleteOne({ account }).catch(dbCatch) + return res.send({ account }) +} + +const valid = require('../../../../middleware/validation') +const rules = ['account'] +module.exports = [valid(rules), asyncHandler(deleteUser)] diff --git a/backend/routes/srcs/in/account/auth/handlePending.js b/backend/routes/srcs/in/account/auth/handlePending.js new file mode 100644 index 000000000..43f82fff5 --- /dev/null +++ b/backend/routes/srcs/in/account/auth/handlePending.js @@ -0,0 +1,60 @@ +const { dbCatch, ErrorHandler } = require('../../../../error') +const Login = require('../../../../Schemas/user_login') +const Pending = require('../../../../Schemas/user_pending') +const Visual = require('../../../../Schemas/user_visual_new') +const asyncHandler = require('express-async-handler') +const sendmail = require('../../../../middleware/mail') + +/** + * @api {post} /handlePending 身分驗證 + * @apiName handlePending + * @apiGroup In/auth + * @apiDescription 身分驗證 + * + * @apiparam {String} account 學號 + * @apiparam {Boolean} acceptUser 是否接受此用戶 + * + * @apiSuccess (204) - + * + * @apiError (404) {String} description user not found + * @apiError (500) {String} description 資料庫錯誤 + */ +const manage = async (req, res, next) => { + const { account, acceptUser } = req.body + const pending = await Pending.findOne({ account }).catch(dbCatch) + if (!pending) throw new ErrorHandler(404, 'user not found') + if (!acceptUser) { + await Pending.deleteMany({ account }).catch(dbCatch) + return res.end() + } + const { username, userpsw, facebookID, email } = pending + + const { _id: visualID } = await Visual({ + username, + account, + publicEmail: email, + }) + .save() + .catch(dbCatch) + await Login({ username, account, facebookID, userpsw, visual: visualID }) + .save() + .catch(async (e) => { + await Visual.deleteOne({ _id: visualID }).catch(dbCatch) + throw new ErrorHandler(500, '資料庫錯誤') + }) + await Pending.deleteMany({ account }).catch(dbCatch) + + const template = require('../mailTemplate/template_generator') + const link = `${req.protocol}://${req.get('host')}/home` + const htmlText = await template(link) + await sendmail(email, 'eeplus website account activaiton', htmlText).catch((e) => { + console.log(e) + throw new ErrorHandler(400, 'sendemail fail') + }) + + return res.send({ email, account }) +} + +const valid = require('../../../../middleware/validation') +const rules = ['account', { filename: 'required', type: 'bool', field: 'acceptUser' }] +module.exports = [valid(rules), asyncHandler(manage)] diff --git a/backend/routes/srcs/in/account/auth/manageAuth.js b/backend/routes/srcs/in/account/auth/manageAuth.js new file mode 100644 index 000000000..2f2d7351d --- /dev/null +++ b/backend/routes/srcs/in/account/auth/manageAuth.js @@ -0,0 +1,26 @@ +const { dbCatch } = require('../../../../error') +const Login = require('../../../../Schemas/user_login') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /manageAuth 新增或刪除管理員 + * @apiName manageAuth + * @apiGroup In/auth + * @apiDescription 新增、刪除管理員 + * + * @apiparam {String} account 學號 + * @apiparam {Boolean} setAuth true:加成管理員;false:從管理員移除(可以移除自己) + * + * @apiSuccess (204) - + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const manage = async (req, res, next) => { + const { account, setAuth } = req.body + await Login.updateOne({ account }, { isAuth: setAuth }).catch(dbCatch) + res.end() +} + +const valid = require('../../../../middleware/validation') +const rules = ['account', { filename: 'required', field: 'setAuth', type: 'bool' }] +module.exports = [valid(rules), asyncHandler(manage)] diff --git a/backend/routes/srcs/in/account/auth/showPending.js b/backend/routes/srcs/in/account/auth/showPending.js new file mode 100644 index 000000000..9c980a801 --- /dev/null +++ b/backend/routes/srcs/in/account/auth/showPending.js @@ -0,0 +1,27 @@ +const { dbCatch, ErrorHandler } = require('../../../../error') +const Pending = require('../../../../Schemas/user_pending') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /showPending 查看待核可帳號 + * @apiName showPending + * @apiGroup In/auth + * @apiDescription 查看待核可帳號 + * + * @apiparam {x} x x + * + * @apiSuccess (200) {Object[]} pendings 各個帳號 + * @apiSuccess (200) {String} pendings.username 名字 + * @apiSuccess (200) {String} pendings.account 學號 + * @apiSuccess (200) {String} pendings.email 信箱 + * @apiSuccess (200) {String} pendings.imgSrc 證件照 + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const manage = async (req,res,next)=>{ + const pendings = await Pending.find({},'username account email img').catch(dbCatch) + const pend = pendings.map(({username, account, email, imgSrc})=>({username, account, email, imgSrc})) + return res.send({pendings:pend}) +} + +module.exports = asyncHandler(manage) \ No newline at end of file diff --git a/backend/routes/srcs/in/account/chPassword.js b/backend/routes/srcs/in/account/chPassword.js new file mode 100644 index 000000000..1340acb63 --- /dev/null +++ b/backend/routes/srcs/in/account/chPassword.js @@ -0,0 +1,42 @@ +//srcs/chLogin.js +const { dbCatch, ErrorHandler } = require('../../../error') +const crypto = require('crypto') +const Login = require('../../../Schemas/user_login') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /chPassword change password + * @apiName chPassword + * @apiGroup In/account + * @apiDescription 重設密碼 + * + * @apiparam {String} oldPsw 原本密碼 + * @apiparam {String} newPsw 新密碼 + * + * @apiSuccess (204) - + * + * @apiError (401) {String} description 原始密碼錯誤 + * @apiError (404) {String} description 帳號不存在 + * @apiError (500) {String} description 資料庫錯誤 + */ +const chPsw = async (req, res, next) => { + const session_account = req.session.loginAccount + + let { oldPsw, newPsw } = req.body + oldPsw = crypto.createHash('md5').update(oldPsw).digest('hex') + newPsw = crypto.createHash('md5').update(newPsw).digest('hex') + + const obj = await Login.findOne({ account: session_account }).catch(dbCatch) + if (!obj) throw new ErrorHandler(404, '帳號不存在') + if (obj.userpsw !== oldPsw) throw new ErrorHandler(401, '原始密碼錯誤') + + await Login.updateOne({ account: session_account }, { $set: { userpsw: newPsw } }).catch(dbCatch) + return res.status(204).end() +} + +const valid = require('../../../middleware/validation') +const rules = [ + { filename: 'password', field: 'oldPsw' }, + { filename: 'password', field: 'newPsw' }, +] +module.exports = [valid(rules), asyncHandler(chPsw)] diff --git a/backend/routes/srcs/in/account/mailTemplate/accountActivate.html b/backend/routes/srcs/in/account/mailTemplate/accountActivate.html new file mode 100644 index 000000000..f9c3c4332 --- /dev/null +++ b/backend/routes/srcs/in/account/mailTemplate/accountActivate.html @@ -0,0 +1,355 @@ + + + + + + activate account + + + + + +
    + a password reset link is auto sent from NTUEEplus website. +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + +
    + + + Logo + +
    + +
    + + + + + +
    +

    + Your EE+ Account Has Been Activated +

    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    +

    + The identity verification is completed, and your EE+ account has already been + activated. +
    + If you didn't register for EE+ website, please ignore this email. +

    +
    + + + + +
    + + + + +
    + Start Now +
    +
    +
    +

    Sincerely,
    NTUEE Plus Web Team

    +
    + +
    + + + + + + + + + + + + + +
    +

    + You received this email because we received a request for [register] for your + account. If you didn't request [register] you can safely delete this email. +

    +
    + +

    +
    + +
    + + + diff --git a/backend/routes/srcs/in/account/mailTemplate/template_generator.js b/backend/routes/srcs/in/account/mailTemplate/template_generator.js new file mode 100644 index 000000000..dea3474b8 --- /dev/null +++ b/backend/routes/srcs/in/account/mailTemplate/template_generator.js @@ -0,0 +1,20 @@ +const { JSDOM } = require('jsdom') +const jquery = require('jquery') +const path = require('path') +const { ErrorHandler } = require('../../../../error') +/** + * generate email with beautiful button + * @return {String} html text + */ +module.exports = async (link) => { + const DOM = await JSDOM.fromFile(path.join(__dirname, './accountActivate.html'), { + contentType: 'text/html', + }).catch((e) => { + console.log(e.message) + throw new ErrorHandler(500, '信件範本讀取失敗') + }) + const { window } = DOM + const $ = jquery(window) + $('#start').attr('href', link) + return window.document.documentElement.outerHTML +} diff --git a/backend/routes/srcs/in/account/main.js b/backend/routes/srcs/in/account/main.js new file mode 100644 index 000000000..402990472 --- /dev/null +++ b/backend/routes/srcs/in/account/main.js @@ -0,0 +1,13 @@ +const express = require('express') +const router = express.Router() +const router_auth = express.Router() + +router.post('/showPersonal', require('./showPersonal')) +router.post('/chPassword', require('./chPassword')) + +router_auth.post('/manageAuth', require('./auth/manageAuth')) +router_auth.post('/handlePending', require('./auth/handlePending')) +router_auth.post('/showPending', require('./auth/showPending')) +router_auth.post('/delUser', require('./auth/deleteUser')) + +module.exports = { router, router_auth } diff --git a/backend/routes/srcs/in/account/showPersonal.js b/backend/routes/srcs/in/account/showPersonal.js new file mode 100644 index 000000000..b45a48c22 --- /dev/null +++ b/backend/routes/srcs/in/account/showPersonal.js @@ -0,0 +1,28 @@ +//srcs/login.js +const { dbCatch } = require('../../../error') +const Login = require('../../../Schemas/user_login') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /showPersonal show personal info + * @apiName ShowPersonal + * @apiGroup In/account + * @apiDescription 顯示機密資料(不想被別人看到的部份,暫無) + * + * @apiSuccess (201) username 使用者名字 + * @apiSuccess (201) account 使用者學號 + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const shPsw = async (req, res, next) => { + const session_account = req.session.loginAccount + + const obj = await Login.findOne({ account: session_account }, 'username account').catch(dbCatch) + + if (!obj) return //throw new ErrorHandler(403,'帳號不存在') + return res.status(201).json({ + username: obj.username, + account: obj.account, + }) +} +module.exports = asyncHandler(shPsw) diff --git a/backend/routes/srcs/in/auth/isAuth.js b/backend/routes/srcs/in/auth/isAuth.js new file mode 100644 index 000000000..3b4f4afc2 --- /dev/null +++ b/backend/routes/srcs/in/auth/isAuth.js @@ -0,0 +1,14 @@ +const { ErrorHandler } = require("../../../error"); +const env = require('dotenv') +env.config() + +module.exports = (req, res, next) => { + const session_isAuth = req.session.isAuth; + if(session_isAuth){ + next() + }else{ + console.log("not auth.",process.env.NODE_ENV==='development'&&'dev mode,accept') + if(process.env.NODE_ENV==='development') next() + else throw new ErrorHandler(403,'not auth') + } +} \ No newline at end of file diff --git a/backend/routes/srcs/in/auth/isUser.js b/backend/routes/srcs/in/auth/isUser.js new file mode 100644 index 000000000..964089caa --- /dev/null +++ b/backend/routes/srcs/in/auth/isUser.js @@ -0,0 +1,18 @@ +const { ErrorHandler } = require("../../../error"); +const env = require('dotenv') +env.config() + +module.exports = (req, res, next) => { + const session_account = req.session.loginAccount; + if(session_account){ + next() + }else{ + console.log("not login.",process.env.NODE_ENV==='development'&&'dev mode,accept') + if(process.env.NODE_ENV==='development'){ + req.session.loginAccount = 'b07901029' + req.session.loginName = '測試用戶' + next() + } + else throw new ErrorHandler(403,'未登入') + } +} \ No newline at end of file diff --git a/backend/routes/srcs/in/career/addRecruitment.js b/backend/routes/srcs/in/career/addRecruitment.js new file mode 100644 index 000000000..a225bd142 --- /dev/null +++ b/backend/routes/srcs/in/career/addRecruitment.js @@ -0,0 +1,70 @@ +const { dbCatch } = require('../../../error') +const Recruitment = require('../../../Schemas/recruitment') +const { parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /addRecruitment add recruitment + * @apiName AddRecruitment + * @apiGroup In/career + * @apiDescription 新增一筆職缺 + * + * @apiHeaderExample {json} header-config + { "content-type": "multipart/form-data" } + * + * @apiparam {String} title 職缺標題 + * @apiparam {String} company_name 公司名稱 + * @apiparam {String} work_type 職位(ex.前端工程師) + * @apiparam {String} salary 薪資 + * @apiparam {String[]} experience 經驗要求 + * @apiparam {String} diploma 學系要求 + * @apiparam {String[]} requirement 技能要求 + * @apiparam {String[]} description 其他描述 + * @apiparam {File} file 照片 + * + * @apiSuccess (201) data 職缺標題 + * @apiSuccess (201) _id 職缺_id(用來search,update,delete) + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const addRecru = async (req, res) => { + const account = req.session.loginAccount + + const { title, company_name, work_type, salary, experience, diploma, requirement, description } = + req.body + const img = parseImg(req.file) + + const { _id } = await new Recruitment({ + account, + title: { + title, + company_name, + work_type, + }, + info: { + salary, + experience, + diploma, + }, + spec: { + requirement, + description, + }, + img, + }) + .save() + .catch(dbCatch) + + res.status(201).send({ data: title, _id }) +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['title', 'company_name', 'work_type', 'salary', 'diploma', 'description'], + type: 'string', + }, + { filename: 'optional', field: ['experience', 'requirement'], type: 'array' }, +] +module.exports = [valid(rules), asyncHandler(addRecru)] diff --git a/backend/routes/srcs/in/career/deleteRecruitment.js b/backend/routes/srcs/in/career/deleteRecruitment.js new file mode 100644 index 000000000..c073320cf --- /dev/null +++ b/backend/routes/srcs/in/career/deleteRecruitment.js @@ -0,0 +1,39 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recruitment = require('../../../Schemas/recruitment') +const asyncHandler = require('express-async-handler') + +async function deleteRecruitment(req, res, next) { + const { _id } = req.body + const delRec = await Recruitment.findById(_id, 'account title').catch(dbCatch) + if (!delRec) throw new ErrorHandler(404, '_id not found') + if (delRec.account !== req.session.loginAccount && !req.session.isAuth) + throw new ErrorHandler(403, 'not authorized') + + await Recruitment.findByIdAndDelete(_id).catch(dbCatch) + res.status(200).send({ data: delRec.title.title }) +} + +/** + * @api {delete} /deleteRecruitment delete recruitment + * @apiName DeleteRecruitment + * @apiGroup In/career + * @apiDescription 用_id刪除職缺 + * + * @apiparam {String} _id 要刪除職缺的mongodb _id + * + * @apiSuccess (200) data 刪除職缺標題 + * + * @apiError (500) {String} description 資料庫錯誤 + * @apiError (403) {String} description not authorized(僅建立者與管理員可以刪除) + * @apiError (404) {String} description _id not found + * + */ + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'required', + field: '_id', + }, +] +module.exports = [valid(rules), asyncHandler(deleteRecruitment)] diff --git a/backend/routes/srcs/in/career/main.js b/backend/routes/srcs/in/career/main.js new file mode 100644 index 000000000..938cd23c8 --- /dev/null +++ b/backend/routes/srcs/in/career/main.js @@ -0,0 +1,13 @@ +const express = require('express') +const router = express.Router() +const parseFile = require('../../../middleware/fileProcess') + +router.post('/addRecruitment', parseFile('file'), require('./addRecruitment')) +router.post('/showRecruitment', require('./showRecruitment')) +router.post('/searchRecruitment', require('./searchRecruitment')) +router.post('/smartsearchRecruitment', require('./smartSearch')) +router.delete('/deleteRecruitment', require('./deleteRecruitment')) +router.get('/recruitment', require('./showMyRecruitment')) +router.patch('/recruitment', parseFile('file'), require('./updateRecruitment')) + +module.exports = router diff --git a/backend/routes/srcs/in/career/preload/preload.js b/backend/routes/srcs/in/career/preload/preload.js new file mode 100644 index 000000000..1769145b4 --- /dev/null +++ b/backend/routes/srcs/in/career/preload/preload.js @@ -0,0 +1,128 @@ +const mongoose = require('../../../../Schemas/db') +const imgPath = '../../../../../../../NTUEE_PLUS_WEBSITE/client/src/images' +const fs = require('fs') +const Recruitment = require('../../../../Schemas/recruitment') +const toInsert = [ + { + title: { + title: '資深前端工程師職缺', + company_name: '均一平台教育基金會', + work_type: '資深前端工程師', + }, + info: { + salary: '月薪 70000元', + experience: '無工作經驗限制', + diploma: 'NTUEE', + }, + spec: { + requirement: + '熟悉 HTML, CSS, JavaScript 與 Browser API\n熟悉 RWD, SPA 開發\n熟悉前端開發工具如 webpack, ESLint\n熟悉常見 Design Patterns\n熟悉 React 及其 ecosystem 例如 redux, react-router, styled-components\n具備 CSS Frameworks 使用經驗,例如 Bootstrap, Material-UI\n熟悉 Git 開發流程', + description: + '網站前端需求設計與開發\n與PM和Designer合作規劃新功能,迭代產品設計\n前端架構設計與優化,確保系統可維護性與擴展性\n提升團隊前端技術能力', + }, + image: 'eesa-icon', + }, + { + title: { + title: '軟體工程師職缺', + company_name: '均一平台教育基金會', + work_type: '軟體工程師', + }, + info: { + salary: '月薪 50000元', + experience: '無工作經驗限制', + diploma: 'NTUEE', + }, + + spec: { + requirement: '熟悉 HTML, CSS, JavaScript\n熟悉 Python\n有 Git 開發流程經驗', + description: + '網站功能全端開發與維護\n根據 UI/UX 規格,開發產品網頁外觀與互動介面 設計與開發 API 與後端邏輯\n撰寫相關自動化測試,確保程式碼品質', + }, + image: 'eesa-icon', + }, + { + title: { + title: '正職/實習職缺', + company_name: 'KRONOS 麒點科技', + work_type: 'C++ Developer', + }, + info: { + salary: '年薪 1M ~ 2.5M 元', + experience: '無需工作經驗限制', + diploma: 'NTUEE', + }, + spec: { + requirement: + '物理/資工/電機背景尤佳\n不排斥投資交易相關領域或研究\n對自己的C++ / Python能力有自信', + description: + '聽過WorldQuant & Point72 嗎?\n現在台灣也有類似的新創公司! – KRONOS\nKRONOS是專注於加密貨幣交易市場的FinTech公司。\n公司希望招募更多電資背景的人,只要您對計量/程式交易領域好奇或有興趣,都歡迎申請,薪水相當優渥!', + }, + image: 'kronos', + }, + { + title: { + title: '徵軟體專案經理', + company_name: '美商麥肯錫公司台北辦公室', + work_type: '3~4個月 一週2天兼職軟體專案經理 視情況延長', + }, + info: { + salary: '誠徵 可議', + experience: '無需工作經驗限制', + diploma: '', + }, + spec: { + requirement: '1) 具軟體業界實作經驗\n2) 懂computer vision與image processing\n3) 英文讀寫流利', + description: + '領導軟體工程師團隊,設計與實作電腦視覺解決方案。算法與代碼已具高度基礎,待最佳化', + }, + image: 'McKinsey', + }, + { + title: { + title: 'C++/Linux Software Engineer', + company_name: 'Riedel Communications GmbH & Co.', + work_type: '', + }, + info: { + salary: '誠徵 可議', + experience: '無需工作經驗限制', + diploma: '', + }, + spec: { + requirement: '', + description: + '一個德國的工作機會推薦給大家,公司是全球 Top.1 的專業通訊公司 Riedel Communications GmbH & Co.,主要提供「即時溝通」的解決方案。曾經服務過世界盃足球賽、奧運、F1 方程式賽車等等.地點在德國的烏珀塔爾 (Wuppertal),語言要求只需英文流利即可。德文為加分項目,但不必一定具備。德國稅負較高,如年薪 8萬歐元,稅率接近40%,可看作稅後 5萬歐元。5萬歐元以匯率計算,為180萬台幣。然而烏珀塔爾的物價與台北相仿,故消費力也相當於在台北的180萬元台幣。詳細職缺內容如連結 (另有 Frontend Engineer),對這個職缺有興趣的請私訊我,我會直接幫你推薦!', + }, + image: 'Riedel', + }, +] + +const path = require('path') + +const main = async () => { + toInsert.forEach( + ({ + title: { title, company_name, work_type }, + info: { salary, experience, diploma }, + spec: { requirement, description }, + image, + }) => { + const rec = new Recruitment({ + account: 'b07901029', + title: { title, company_name, work_type }, + info: { salary, experience: experience.split('\n'), diploma }, + spec: { requirement: requirement.split('\n'), description: description }, + }) + filePath = path.join(imgPath, image + '.png') + rec.img.data = fs.readFileSync(filePath) + rec.img.contentType = 'image/png' + console.log(rec.title.title) + rec.save() + }, + ) +} +mongoose.connection.on('open', () => { + console.log('DB on') + main() +}) diff --git a/backend/routes/srcs/in/career/searchRecruitment.js b/backend/routes/srcs/in/career/searchRecruitment.js new file mode 100644 index 000000000..498f94211 --- /dev/null +++ b/backend/routes/srcs/in/career/searchRecruitment.js @@ -0,0 +1,97 @@ +const Recruitment = require('../../../Schemas/recruitment') +const { searchQuery, findWithLimit } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +const searchRecuitment = async function (req, res, next) { + const { + _id, + account, + title, + company_name, + work_type, + salary, + experience, + diploma, + requirement, + description, + } = req.body + const keys = { + _id, + account, + 'title.title': title, + 'title.company_name': company_name, + 'title.work_type': work_type, + 'info.salary': salary, + 'info.experience': experience, + 'info.diploma': diploma, + 'spec.requirement': requirement, + 'spec.description': description, + } + const query = searchQuery(keys) + const { page, perpage } = req.body + const [recrus, maxPage] = await findWithLimit(Recruitment, query, page, perpage || 20) + return res.status(201).send(recrus.map((recru) => recru.getPublic()).reverse()) +} + +/** + * @api {post} /searchRecruitment search recruitment by field + * @apiName SearchRecruitment + * @apiGroup In/career + * @apiDescription 指定欄位搜尋職缺 + * + * @apiparam {String} _id _id (optional) + * @apiparam {String} account 學號 (optional) + * @apiparam {String} title 職缺標題 (optional) + * @apiparam {String} company_name 公司名稱 (optional) + * @apiparam {String} work_type 職位 (optional) + * @apiparam {String} salary 薪資 (optional) + * @apiparam {String} experience 經驗要求 (optional) + * @apiparam {String} diploma 學系要求 (optional) + * @apiparam {String} requirement 技能要求 (optional) + * @apiparam {String} description 其他描述 (optional) + * @apiparam {Number} page default 1 + * @apiparam {Number} perpage default 20 + * + * @apiSuccessExample {json} Success-Response: + * [{ + * '_id': 'String', + * 'title': { + * 'title': 'String', + * 'company_name': 'String', + * 'work_type': 'String' + * }, + * 'info': { + 'salary': 'String', + 'experience': ['String'], + 'diploma': 'String' + * }, + * 'spec': { + 'requirement': ['String'], + 'description': 'String' + * }, + 'image': 'String' + * },] + * + * @apiError (500) {String} description 資料庫錯誤 + */ + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: [ + '_id', + 'account', + 'title', + 'company_name', + 'work_type', + 'salary', + 'experience', + 'diploma', + 'requirement', + 'description', + ], + type: 'string', + }, +] +module.exports = [valid(rules), asyncHandler(searchRecuitment)] diff --git a/backend/routes/srcs/in/career/showMyRecruitment.js b/backend/routes/srcs/in/career/showMyRecruitment.js new file mode 100644 index 000000000..631e341e5 --- /dev/null +++ b/backend/routes/srcs/in/career/showMyRecruitment.js @@ -0,0 +1,37 @@ +const Recruitment = require('../../../Schemas/recruitment') +const asyncHandler = require('express-async-handler') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {get} /recruitment show my recruitment + * @apiName ShowMyRecruitment + * @apiGroup In/career + * @apiDescription 顯示我的所有職缺 + * + * @apiparam {Number} page default 1 + * @apiparam {Number} perpage default 20 + * + * @apiSuccess (201) {Object[]} - 職缺們 + * @apiSuccess (201) {String} -._id mongodb _id(for delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.company_name 公司名稱 + * @apiSuccess (201) {String} -.title.work_type 職位(ex.前端工程師) + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.salary 薪資 + * @apiSuccess (201) {String[]} -.info.experience 經驗要求 + * @apiSuccess (201) {String} -.info.diploma 學院要求 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.requirement 技能要求 + * @apiSuccess (201) {String[]} -.spec.description 工作的其他描述 + * @apiSuccess (201) {String} -.image 公司頭像(Ex. \) + * + * @apiError (403) {String} description not login + * @apiError (500) {String} description 資料庫錯誤 + */ +module.exports = asyncHandler(async (req, res, next) => { + const account = req.session.loginAccount + const { page, perpage } = req.query + const [recs, maxPage] = await findWithLimit(Recruitment, { account }, page, perpage || 20) + res.status(200).send(recs.map((obj) => obj.getPublic()).reverse()) +}) diff --git a/backend/routes/srcs/in/career/showRecruitment.js b/backend/routes/srcs/in/career/showRecruitment.js new file mode 100644 index 000000000..f2e9baad6 --- /dev/null +++ b/backend/routes/srcs/in/career/showRecruitment.js @@ -0,0 +1,35 @@ +const Recruitment = require('../../../Schemas/recruitment') +const asyncHandler = require('express-async-handler') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {post} /showRecruitment show all recruitment + * @apiName ShowRecruitment + * @apiGroup In/career + * @apiDescription 顯示所有職缺(等價於不傳任何參數的searchRecruitment) + * + * @apiparam {Number} page default 1 + * @apiparam {Number} perpage default 20 + * + * @apiSuccess (201) {Object[]} - 職缺們 + * @apiSuccess (201) {String} -._id mongodb _id(for delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.company_name 公司名稱 + * @apiSuccess (201) {String} -.title.work_type 職位(ex.前端工程師) + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.salary 薪資 + * @apiSuccess (201) {String[]} -.info.experience 經驗要求 + * @apiSuccess (201) {String} -.info.diploma 學院要求 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.requirement 技能要求 + * @apiSuccess (201) {String[]} -.spec.description 工作的其他描述 + * @apiSuccess (201) {String} -.image 公司頭像(Ex. \) + * + * @apiError (500) {String} description 資料庫錯誤 + */ +module.exports = asyncHandler(async (req, res, next) => { + const { page, perpage } = req.body + const [recs, maxPage] = await findWithLimit(Recruitment, {}, page, perpage || 20) + res.status(200).send({ data: recs.map((obj) => obj.getPublic()).reverse(), maxPage }) +}) diff --git a/backend/routes/srcs/in/career/smartSearch.js b/backend/routes/srcs/in/career/smartSearch.js new file mode 100644 index 000000000..7bbca4df9 --- /dev/null +++ b/backend/routes/srcs/in/career/smartSearch.js @@ -0,0 +1,39 @@ +const Recruitment = require('../../../Schemas/recruitment') +const asyncHandler = require('express-async-handler') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {post} /smartsearchRecruitment search recruitment by keywords + * @apiName ShowRecruitment + * @apiGroup In/career + * @apiDescription 用空格區分關鍵字進行搜尋 + * + * @apiParam {String} keyword 用空格區分 + * + * @apiSuccess (201) {Object[]} - 職缺們 + * @apiSuccess (201) {String} -._id mongodb _id(for delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.company_name 公司名稱 + * @apiSuccess (201) {String} -.title.work_type 職位(ex.前端工程師) + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.salary 薪資 + * @apiSuccess (201) {String[]} -.info.experience 經驗要求 + * @apiSuccess (201) {String} -.info.diploma 學院要求 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.requirement 技能要求 + * @apiSuccess (201) {String[]} -.spec.description 工作的其他描述 + * @apiSuccess (201) {String} -.image 公司頭像(Ex. \) + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const smartSrh = async (req, res, next) => { + const { keyword, page, perpage } = req.body + const query = Recruitment.smartQuery(keyword) + const [recrus, maxPage] = await findWithLimit(Recruitment, query, page, perpage) + res.status(201).send({ data: recrus.map((recru) => recru.getPublic()).reverse() }) +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'optional', field: ['keyword'] }] +module.exports = [valid(rules), asyncHandler(smartSrh)] diff --git a/backend/routes/srcs/in/career/updateRecruitment.js b/backend/routes/srcs/in/career/updateRecruitment.js new file mode 100644 index 000000000..26305c8ea --- /dev/null +++ b/backend/routes/srcs/in/career/updateRecruitment.js @@ -0,0 +1,68 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recruitment = require('../../../Schemas/recruitment') +const { updateQuery, parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +const updateRecruitment = async (req, res, next) => { + const toUpdate = await Recruitment.findById(req.body._id, 'account').catch(dbCatch) + if (!toUpdate) throw new ErrorHandler(404, '_id not exists') + if (!req.session.loginAccount || req.session.loginAccount !== toUpdate.account) + throw new ErrorHandler(403, 'unauthorized') + + const { title, company_name, work_type, salary, experience, diploma, requirement, description } = + req.body + const keys = { + 'title.title': title, + 'title.company_name': company_name, + 'title.work_type': work_type, + 'info.salary': salary, + 'info.experience': experience, + 'info.diploma': diploma, + 'spec.requirement': requirement, + 'spec.description': description, + img: parseImg(req.file), + } + const toSet = updateQuery(keys) + + await Recruitment.findByIdAndUpdate(req.body._id, toSet).catch(dbCatch) + res.status(203).end() +} + +/** + * @api {patch} /recruitment update recruitment + * @apiName UpdateRecruitment + * @apiGroup In/career + * @apiDescription 更新一筆職缺 + * + * @apiHeaderExample {json} header-config + { "content-type": "multipart/form-data" } + * + * @apiparam {String} _id 要更新職缺的mongodb _id + * @apiparam {String} title 職缺標題(optional) + * @apiparam {String} company_name 公司名稱(optional) + * @apiparam {String} work_type 職位(ex.前端工程師)(optional) + * @apiparam {String} salary 薪資(optional) + * @apiparam {String[]} experience 經驗要求(optional) + * @apiparam {String} diploma 學系要求(optional) + * @apiparam {String[]} requirement 技能要求(optional) + * @apiparam {String[]} description 其他描述(optional) + * @apiparam {File} file 照片(optional) + * + * @apiSuccess (203) - - + * + * @apiError (404) {String} description _id not exists + * @apiError (500) {String} description 資料庫錯誤 + * @apiError (403) {String} description unauthorized(僅建立者可以更新) + */ + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['title', 'company_name', 'work_type', 'salary', 'diploma', 'description'], + type: 'string', + }, + { filename: 'optional', field: ['experience', 'requirement'], type: 'array' }, + { filename: 'required', field: '_id' }, +] +module.exports = [valid(rules), asyncHandler(updateRecruitment)] diff --git a/backend/routes/srcs/in/column/addColumn.js b/backend/routes/srcs/in/column/addColumn.js new file mode 100644 index 000000000..ddb6a7343 --- /dev/null +++ b/backend/routes/srcs/in/column/addColumn.js @@ -0,0 +1,95 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Column_detail = require('../../../Schemas/column_detail') +const Column_outline = require('../../../Schemas/column_outline') +const { parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /column/add add column + * @apiName addColumn + * @apiGroup In/column + * @apiDescription 管理員新增文章 + * + * @apiParam {String[]} title 文章標題 + * (xxxx 級 xxx (公司名稱與職位))(這邊看要不要和name,experience合併?) + * @apiParam {String} id 文章的編號 + * (建議yymm) + * @apiParam {Object} top + * @apiParam {String} top.name 標題(xxxx 級 xxx) + * @apiParam {String} top.experience 副標題(公司名稱與職位) + * @apiParam {String[]} top.hashtags 文章的hashtag + * (文章類別,訪問者姓名、級別、工作、相關組織與企業) + * @apiParam {Object[]} body.body + * @apiParam {String} body.body.bigtitle (一、標題,二、求學階段...) + * @apiParam {Object[]} body.body.bigsections + * @apiParam {String} body.body.bigsections.subtitle 子標題 + * @apiParam {String} body.body.bigsections.subsection (文章內容) + * @apiParam {String[]} annotation.annotation 參與全人員 + * @apiParam {String[]} annotation.annotation.job 工作 + * @apiParam {String[]} annotation.annotation.contributer 人員 + * + * @apiParam {String[]} anno [所有採訪人員姓名] + * @apiParam {String[]} date yyyy/mm/dd 星期x + * @apiparam {String[]} exp 職位 + * @apiParam {String[]} edu 學歷 + * [學士:校系(畢業年分),碩士:校系(畢業年分),博士:校系(畢業年分)] + * @apiParam {String[]} intro 簡介 + * (1個element是一段) + * @apiParam {File} file 封面照 + * + * @apiParamExample {js} Input-Example: + * let input=new FormData() + * + * input.append("file", 採訪合照) + * input.append("id", "yymm") + * input.append("title", "2008級 方劭云(當屆最年輕升遷副教授)") + * input.append("top[name]", "2008級 方劭云") + * input.append("top[experience]", "當屆最年輕升遷副教授") + * input.append("top[hashtags][0]", 關鍵字1) + * input.append("annotation[annotation][0][job]", "撰寫") + * input.append("annotation[annotation][0][contributer]][]", "王曉明") + * input.append("anno[]", "作者1") + * input.append("date", "yyyy/mm/dd 星期x") + * input.append("exp[0]", "現任:國立臺灣科技大學電機系 副教授") + * input.append("edu[0]", "博士:台灣大學電子所 (2013)") + * input.append("intro[0]", "2008畢業於台大電機,目前任職於臺灣科技大學的方劭云教授...") + * + * input.append("body[body][][bigtitle]", "一、我的大學生涯") + * input.append("body[body][][bigsections][0][subtitle]", "球隊與課業交織的辛苦大學生活") + * input.append("body[body][][bigsections][0][subsection]", "因為我是排球校隊,沒能花很多時間在系上...") + * + * axios.post("/api/addColumn", input, {headers:{'content-type': 'multipart/form-data'}}) + * + * @apiSuccess (201) {String} id post的id + * + * @apiError (400) {String} description id is required + * @apiError (500) {String} description 資料庫錯誤 + */ + +const addCol = async (req, res) => { + const { id, top, body, annotation, anno, date, title, exp, edu, intro } = req.body + const columnImg = parseImg(req.file) + + await new Column_detail({ id, top, body, annotation }).save().catch(dbCatch) + await new Column_outline({ anno, date, title, exp, edu, intro, id, columnImg }) + .save() + .catch(dbCatch) + res.status(201).send({ id }) +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['top', 'body', 'annotation'], + type: 'object', + }, + { + filename: 'optional', + field: ['date'], + type: 'string', + }, + { filename: 'optional', field: ['anno', 'title', 'exp', 'edu', 'intro'], type: 'array' }, + { filename: 'required', field: 'id' }, +] +module.exports = [valid(rules), asyncHandler(addCol)] diff --git a/backend/routes/srcs/in/column/delete.js b/backend/routes/srcs/in/column/delete.js new file mode 100644 index 000000000..fb8ecfaa4 --- /dev/null +++ b/backend/routes/srcs/in/column/delete.js @@ -0,0 +1,29 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const ColOut = require('../../../Schemas/column_outline') +const ColDet = require('../../../Schemas/column_detail') +const asyncHandler = require('express-async-handler') + +const deletCol = async (req, res, next) => { + const { id } = req.body + await ColDet.deleteOne({ id }).catch(dbCatch) + await ColOut.deleteOne({ id }).catch(dbCatch) + res.status(203).send({ id }) +} + +/** + * @api {delete} /column/delete delete column + * @apiName deleteColumn + * @apiGroup In/column + * @apiDescription 管理員刪除文章 + * + * @apiParam {String} id 文章的編號 + * (建議yymm) + * + * @apiSuccess (201) {String} id post的id + * + * @apiError (400) {String} description id is required + */ + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'required', field: 'id' }] +module.exports = [valid(rules), asyncHandler(deletCol)] diff --git a/backend/routes/srcs/in/column/getDetail.js b/backend/routes/srcs/in/column/getDetail.js new file mode 100644 index 000000000..9afc5f703 --- /dev/null +++ b/backend/routes/srcs/in/column/getDetail.js @@ -0,0 +1,53 @@ +const asyncHandler = require('express-async-handler') +const { dbCatch, ErrorHandler } = require('../../../error') +const Column = require('../../../Schemas/column_detail') + +/** + * @api {get} /column/detail get column detail + * @apiName GetDetail + * @apiGroup In/column + * @apiDescription 拿詳細文章內容 + * + * @apiparam {String} id yymm(required) + * + * @apiSuccessExample {json} Success-Response: + * { + * 'top':{ + * 'name':'String', + * 'experience':'String', + * 'hashtags':['String'] + * }, + * 'body': { + 'body': [ + { + 'bigtitle': 'String', + 'bigsections': [ + { + 'subtitle': 'String', + 'subsection': 'String', + },], + },], + }, + 'annotation': { + 'annotation': [ + { + 'job': 'String', + 'contributer': 'String', + },], + }, + 'id': 'String', + * } + * + * @apiError (404) {String} description id is required/資料不存在 + * @apiError (500) {String} description 資料庫錯誤 + */ +const getDetail = async (req, res, next) => { + const { id } = req.query + const objDetail = await Column.findOne({ id }).catch(dbCatch) + if (!objDetail) throw new ErrorHandler(404, '資料不存在') + return res.status(201).send(objDetail) +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'required', field: 'id', method: 'get' }] +module.exports = [valid(rules), asyncHandler(getDetail)] diff --git a/backend/routes/srcs/in/column/getImg.js b/backend/routes/srcs/in/column/getImg.js new file mode 100644 index 000000000..41b6dd66e --- /dev/null +++ b/backend/routes/srcs/in/column/getImg.js @@ -0,0 +1,7 @@ +// const getColumn = require('./imgProcess/getImg'); +// const asyncHandler = require('express-async-handler') + +// module.exports = asyncHandler(async (req, res, next)=>{ +// const getDone = await getColumn(req.body.filename) +// return res.status(201).send({data:getDone}) +// }) diff --git a/backend/routes/srcs/in/column/getOutline.js b/backend/routes/srcs/in/column/getOutline.js new file mode 100644 index 000000000..303b4704c --- /dev/null +++ b/backend/routes/srcs/in/column/getOutline.js @@ -0,0 +1,57 @@ +const asyncHandler = require('express-async-handler') +const { dbCatch } = require('../../../error') +const Column_Outline = require('../../../Schemas/column_outline') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {get} /column/outline get column outline with id optional + * @apiName GetOutline + * @apiGroup In/column + * @apiDescription 拿Outline資料(含圖片) + * + * @apiparam {String} id id(optional,若未給則送全部) + * @apiparam {String} perpage 一頁數量(optional,default 5) + * @apiparam {String} page 頁數(optional,default 1) + * + * + * @apiSuccessExample {json} Success-Response: + * {'data': + * [{ +* 'anno': ['String'], + 'date': 'String', + 'title': ['String'], + 'exp': ['String'], + 'edu': ['String'], + 'intro': ['String'], + 'id': 'String', + 'imgSrc':'String' + },], + * 'maxPage':'Number'} + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const getOut = async (req, res, next) => { + const { id, page, perpage } = req.query + const query = id ? { id } : {} + const [columnOulines, maxPage] = await findWithLimit(Column_Outline, query, page, perpage) + return res + .status(201) + .send({ data: columnOulines.reverse().map((col) => col.getPublic()), maxPage }) +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['id'], + type: 'string', + method: 'get', + }, + { + filename: 'optional', + field: ['perpage', 'page'], + type: 'any', + method: 'get', + }, +] +module.exports = [valid(rules), asyncHandler(getOut)] diff --git a/backend/routes/srcs/in/column/main.js b/backend/routes/srcs/in/column/main.js new file mode 100644 index 000000000..4eefcb461 --- /dev/null +++ b/backend/routes/srcs/in/column/main.js @@ -0,0 +1,23 @@ +const express = require('express') +const router = express.Router() +const router_auth = express.Router() +const parseFile = require('../../../middleware/fileProcess') + +// router_auth.post('/saveImg', +// ImgGet('file'), +// require('./saveImg')) + +// router.post('/getImg', +// require('./getImg')) + +router.get('/detail', require('./getDetail')) + +router.get('/outline', require('./getOutline')) + +router_auth.post('/add', parseFile('file'), require('./addColumn')) +router_auth.patch('/update', parseFile('file'), require('./update')) +router_auth.delete('/delete', require('./delete')) + +router.get('/search', require('./search')) + +module.exports = { router, router_auth } diff --git a/backend/routes/srcs/in/column/preload/detail_preload.js b/backend/routes/srcs/in/column/preload/detail_preload.js new file mode 100644 index 000000000..38b584f24 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/detail_preload.js @@ -0,0 +1,63 @@ +const mongoose = require('../../../../Schemas/db') +const Column_detail = require('../../../../Schemas/column_detail') + +const toInsert = [ + require('./details/1601'), + require('./details/1602'), + require('./details/1603'), + require('./details/1604'), + require('./details/1605'), + require('./details/16052'), + require('./details/1606'), + require('./details/1805'), + require('./details/1806'), + require('./details/1807'), + require('./details/1808'), + require('./details/1907'), + require('./details/1908'), + require('./details/1909'), + require('./details/1910'), + require('./details/1912'), + require('./details/2001'), +] + +const main = async () => { + for (let i = 0; i < toInsert.length; i += 1) { + const { title, hashtags, sections, annotation, id } = toInsert[i] + const data = { + top: { + name: title.map((til) => til.split(/\(|(|)|\)/)[0].trim()).join(' & '), + experience: title.map((til) => til.split(/\(|(|)|\)/)[1].trim()), + hashtags, + }, + body: { + body: sections.map(({ bigtitle, sections }) => ({ + bigtitle, + bigsections: sections.map(({ title, section }) => ({ + subtitle: title, + subsection: section.replace(/ | /g, '').replace(/\n/g, '\n '), + })), + })), + }, + annotation: { + annotation: annotation.map((jbp) => ({ + job: jbp.split(':')[0], + contributer: jbp.split(':')[1], + })), + }, + id: id.slice(7), + } + try { + const column_detail = new Column_detail(data) + await column_detail.save() + console.log(id, 'done') + } catch (e) { + // console.log(e) + console.log(`id ${id} duplicate`) + } + } +} +mongoose.connection.on('open', () => { + console.log('DB on') + main() +}) diff --git a/backend/routes/srcs/in/column/preload/details/1601.js b/backend/routes/srcs/in/column/preload/details/1601.js new file mode 100644 index 000000000..1a3014893 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1601.js @@ -0,0 +1,137 @@ +module.exports = { + title: ['1995級 徐瑞廷(BCG 波士頓顧問公司董事總經理)'], + hashtags: [ + '系友專訪', + '徐瑞廷', + '1995級', + 'B80', + 'StanfordUniversity', + 'SantaClaraUniversity', + 'MBA', + '史丹福大學電子工程學碩士', + '聖塔克拉拉大學MBA', + 'BCG', + '波士頓顧問公司合夥人暨董事總經理', + 'BCG台北辦公室負責人', + '策略顧問方法與實務', + 'StrategyConsulting', + '探索自己', + '多想下一步跟下下步', + '獨立思考', + ], + sections: [ + { + bigtitle: '一、前言', + sections: [ + { + title: '致謝', + section: + '系學會很感謝 1995級林哲立系友 與 1995級丁建均教授的大力幫忙,讓我們邀請到徐瑞廷系友參加本次專訪。徐學長是史丹福大學電子工程學碩士 / 聖塔克拉拉大學 MBA,畢業後在BCG 波士頓顧問公司擔任台北辦公室負責人、合夥人暨董事總經理;除此之外,學長和 台大國企系李吉仁教授 也曾在2016年九月共同開授選修課程「策略顧問—方法與實務」 (Strategy Consulting: Approach and Practice, https://www.facebook.com/BCGinTaiwan/posts/1110247705713105 )現在讓我們一起看看徐學長的分享與心路歷程。', + }, + ], + }, + { + bigtitle: '二、碩士時期', + sections: [ + { + title: '畢業後選擇的領域', + section: + '從台大電機畢業以後,當時對我來說最直接的選項就是出國念研究所,就是想出國看看。我希望申請到一流的學校,所以同樣申請電機系所。在申請國外學校時,並沒有細分電磁、自動控制等領域,是在Stanford讀研究所時才確定領域。我後來是攻讀IC design,主要也是因為IC design在當時的矽谷是熱門的領域。在Stanford上課和我在台大的經驗最大的不同,是很多上課的教材直接從業界取材。比如說上電路設計的時候,我們研究的電路樣本就是來自於幾家真實的公司,像是HP、DEC(Digital Equipment Corporation),對我的衝擊很大。我了解到原來業界很多領先的電路是長這樣子,你可以想像那跟教科書有很大的不同。同時我也認識到,畢業後如果去這些公司,大概會是做哪些事情、我需要那些技能。加上IC design本身就很有意思,後來我在研究所花了很多時間研究IC design的課,畢業以後就直接去DEC設計CPU。大概是這個樣子,可以說我的選擇是受到很多研究所課堂的影響。', + }, + { + title: '當年最尊敬的恩師', + section: + '我的恩師有兩位教授:其中一位教授上的是電機系一門很難的課,卻可以把很難的課用很白話的方式講解,不會一開始就進入技術的細節,而是先給大家一個大的概念框架,再深入淺出的說明。我對他印象很深刻也很喜歡聽他講課。另外一位老師則是非常關心學生,從一些小的互動可以看出他有在觀察學生,然後在其他地方顯現他的觀察並投射到他的互動中。', + }, + { + title: '大學的成就', + section: + '我必須承認,大學時候的環境對我來說還是相對封閉,我也不知道畢業以後要做什麼、職場的要求是什麼,所以選擇的領域主要是當時學業上比較熱門的話題。大學期間選的專題是比較像無線通訊的,在90年代,行動通訊被認為是未來的趨勢。當初選擇的原因只是因為熱門,並沒有太多的想法。', + }, + { + title: '出國念研究所的意義', + section: + '這問題其實不太容易回答。很多事情沒有自己經歷過,我是不太能評論的,畢竟我只看到結果,卻沒有看到過程。我想將重點放在我自己在美國就讀研究所的過程中,遭遇到的幾個衝擊:第一個衝擊是,美國研究所的課程題材十分實用,就如先前所提,基本上是跟業界緊密相連。我後來才明白箇中緣故:因為這些研究所,尤其如Stanford,課程的修習者不單只有學生,也包括了很多業界人士。像我們當時修習的某門課程,現場修課人數約有100多位,但其實還即時與矽谷幾個大企業(如:HP辦公室)連線同步授課,因此老師實際上正向數百人授課。這個例子的意義何在呢?老師教課時傳授的知識與技術有用與否,會直接反映在學生對他的回饋上;換句話說,一門課的課程題材實用性不高,很可能會在開課幾次後因為評分過低,遭到淘汰。我覺得這樣的互動對師生都是有益的:對學校老師而言,他課上傳授的一定得朝著實用的方向去;然而在平常與學生的互動中,老師也得以了解哪些事是相關行業人士所注重的。而我們這類剛從大學部上來的學生,也能馬上跟業界接軌。總結以上所述,在我的認知中,與業界連結緊密是最大的不同。第二個衝擊則是,當時在美國念研究所,並非每間研究所都要求寫論文,甚至可以說大部分的研究所皆沒有寫論文的要求;需要寫論文的人,多半是在攻讀博士學位。所以我當時在Stanford讀了九個月左右,就拿到了碩士學位;但若在台灣的話,無論怎麼規劃,沒花上兩年大概不太容易畢業。還有很多尚未提及的差異,或大或小,畢竟美國與台灣之間有太多的不同了:地域上的變數、文化上的變數、校風的不同等,要仔細比較可能花上三個小時都還講不完呢!在我的印象中,最大的差異大概就是這兩項了。噢...對了,再提個也是令人印象深刻的差異好了!但我要再重申一次,我不知道這類的比較是否有意義。提到在美國念研究所,應該不難想像同學來自世界各國,對吧?更有趣的是,他們甚至還來自各年齡層:我那時有個同學,當時應該已經40多歲了,我跟他交情很好。他是位HP的工程師,之所以當個全職學生,來Stanford就讀研究所,純粹是因為已經在公司工作好一段時間,公司便送他來進修。相對地,我最年輕的同學似乎是15歲吧,來自中國的天才少年班。總之,同學之中有來自不同的國家、不同的年齡層,雖然可以說是反映了美國本身在人口組成上的多樣性,但也帶來了一定的刺激。然而這些差異是否會對職業生涯造成不同的影響呢?如果從結果來看,我倒不覺得有。對我自己來說,在畢業後跟行業接軌這方面,我確實是從Stanford畢業以後,才真的有一定可以找到工作的自信;相較之下,我從台大畢業時,並不是很確定能擔任哪些工作。當時還沒有104人力銀行,只能看報紙找工作。報紙上會寫出哪些大廠在徵工程師、需要哪些條件、要求懂哪些東西。我是大四時開始在關注,雖說沒有立即就業的意願,但出於好奇,都會看一下我符不符合這些企業招募人才的需求。那時候一看,感覺沒有一份工作我可以勝任,因為對於是否學過上面條列的東西,我不能確定,自然也不清楚自己是否高於徵才門檻。 但在Stanford待九個月後,情況就不同了:那時我習得的知識與技術都是行業會用到的。所以我一畢業馬上對「能去哪些公司」、「找到怎樣的工作」...等,抱有大致的了解以及強烈的預感。但是找工作只是職場上的第一步,而這第一步我到底踏得順不順、有沒有留下甚麼深遠影響,我如今已經自電機系畢業接近二十年了,現在回首仍是不好評估咧!依結果論-當然我得再三強調,以下純屬個人觀察,並非固定的通則-大致上是這樣:比較我這幾屆當初去美國念研究所的這幫人與留在台灣的人,後者反而在畢業以後有更多的機會進入上升氣流強烈的企業。譬如:聯發科大概是從1990年代後半開始崛起,我們同屆當時就有好幾位較早進入聯發科服務,甚至有人的工號是在100號以前。你可以想像他們之中,有人現在大概累積了不少的積蓄。這種實例一部分肯定得歸功於他們當初的判斷:認定那些公司前景看好,於是跟著公司一同崛起;但另一部分也不得不承認裡面有許多人生境遇與造化。回首看一看,留在台灣的人以及比較早回台灣的人,有一定比率在職場上確實取得滿大的成就,如果這問題是想要這類比較淺層的回答...。畢竟成就這件事,要看各人怎去定義與詮釋;我不想給你們不當的定義,因為人生成就不一定只能從職場獲得。雖說這類比較稍嫌淺層而狹隘,但如果繼續從職場切入,不少像我們讀完研究所後留在美國發展的人,總會遭遇一些亞洲人常常面臨的問題,這些問題不能完全貶為劣勢,但肯定讓人難以如魚得水:身處一個新的環境,語言的不通順、文化上的差異都需要你花上時間與精力來磨合。而在追求晉升的過程中,又會遇到一波障礙,像是不可避免的辦公室政治、揣摩上司與客戶的心意......等。很有可能今天同樣狀況發生在台灣,我們更容易發揮所長;但在美國卻會受到前述問題的侷限,手腳受縛。尤其身處在管理階級時,更能體會這些阻礙。所以我們這些當時在美國念了研究所,直接畢業進美國職場的人呢,隔了二十年左右來看,在企業裡很容易遇到瓶頸。在這些分享的實例中,哪些因素對於生涯有深遠影響、影響是好是壞,我始終認為不能輕易定論。因為如果單看這一代在職場上的境遇,我們很容易忽略美國本身有許多其他的優點:舉例來講,後代的教育環境、生活成長的環境...等,這方面台灣實在不能以同等級相比。所以回歸這問題的敘述,「會對生涯造成那些影響」要看你從哪些維度來觀察,但我認為在較狹隘的職場發展上,結果確實與當初所預想的不太一樣。本來想像去了國外,拿到了金飯碗,到了後來可能有更好的待遇,但是這種預期在我們這一屆看來,反而不太是這麼一回事。', + }, + ], + }, + { + bigtitle: '三、初入職場', + sections: [ + { + title: '第一份工作', + section: + '很幸運地,在好學校,自然常會有公司來找好人才。我的第一份工作是在DEC做工程師,主要是負責ALPHA系列CPU。當時因為Digital是很大的趨勢,在課堂上也看過DEC的設計,因緣際會得到了這個工作。做了幾個月,我才發現設計這種大電路的分工非常細,和一開始的想像不太一樣,以為能有很大的彈性,可以設計出大電路,後來才發現是做小模組中的小模組。那時做的是DLL。大電路裡面有很多clock,要想辦法讓這些clock 同步,也就是DLL的功能。我離開的原因,主要是對工作的期待不太一樣。那也是我第一次面對自己:我到底喜不喜歡當工程師?我當時也是天人交戰,因為一路求學上來,很常都是因為成績考到,就去念了大家公認的好學校、好科系,沒有認真面對自己。我一直在思考到底自己要不要當工程師,後來經過很多考慮才決定離開,大概是這個樣子。', + }, + { + title: '創業經歷', + section: + '我在矽谷創過幾家Internet 的公司和一些e-commerce的公司,後來不再繼續的主因是2000年的泡沫。泡沫發生後資金很難募到,氛圍也滿恐慌的,不知道Internet公司是否能繼續存活、創造價值,還是只是一個風潮、泡沫。我從工程師跳到新創公司,才發現企業的經營有太多值得學習的地方,也才發現自己沒有準備好,索性冷靜一下,重新沈澱自己。', + }, + { + title: '您是一個好的工程師嗎?', + section: + '這個問題不容易回答。是不是一個好的工程師要從各種方面來看。從結果而言,應該不是太好,因為後來就沒再繼續鑽研。其實我覺得工程師最重要的就是解決問題的能力吧,只是大部分是別人已經定義好的問題,可能是PM、你的老闆給你的spec,而你的工作就是在專案裡讓產品達到這個spec。但我為什麼不是一個太好的工程師呢?我可以想像我如果在這個領域繼續待下來,應該會越來越感到不滿足。我比較喜歡看big picture的東西,看產品對客戶的影響,我比較喜歡這種東西,所以站在這角度來看,工程師可能無法滿足我這樣的需求,這是比較大的矛盾。', + }, + { + title: '曾經做過最好的專案', + section: + '這個很多,但是我不太能夠講細節。我們做過的專案常常可以創造重大的impact,很多都是上頭版新聞的。但是因為我們沒辦法透露客戶資訊,我只能描述大致的內容。譬如,有客戶想知道是否應繼續投資在原本的技術, 還是改投資在新技術上,這樣的決定牽涉的投資量是極大的。我們做了很多深入的分析,與客戶進行多輪討論後,說服客戶朝往某方向走,最後客戶也往該方向走,在新技術上有很多斬獲。這不完全能歸功於顧問公司,但我相信我們的分析與討論有重大的貢獻。也有一些案例是,成立了幾十年的大型組織,公司第一次想做大型的轉型,請我們幫他們做組織設計。因為是老牌公司,組織對轉型極為抗拒,我們用很多方法,對各部門和管理層做溝通,同時列舉其他遇到相同問題而轉型成功與失敗的案例,花了很長時間讓組織認可轉型的必要,當時也上了媒體。另外有很多企業會請我們幫他們思考未來三到五年的策略。舉例來說,一家公司一直認為自身的成長來自展店,因此不斷的展店,但根據我們的分析,才了解到展店反而造成了效益遞減,他們應該耕耘其他的東西,才能在三年內保持領先。後來經過討論也說服轉型,導致今天他還是保持領先地位。我在顧問產業十年多了,很榮幸能夠為很多企業創造價值,不僅對投資人,還有員工和成千上萬的家庭,這可能是我當工程師沒辦法做到的。', + }, + ], + }, + { + bigtitle: '四、跨領域顧問生涯', + sections: [ + { + title: '從工程師進入到顧問業:面臨的考驗', + section: + '不知道各位對顧問這個行業理解多少?顧問跟工程師一樣,每天的工作都是在解決問題,不過解決問題的角度不太一樣。工程師大多是為了達到 spec ,而顧問要解決的是客戶的問題,也就是經營者的問題。經營者的問題很活,有時候是組織,有時候是利潤,也有時候是各種問題的綜合。例如,兩個公司合併了,然後兩邊都有不同的文化,誰都不服誰,該怎麼辦?有點像你在家裡遇到柴米油鹽醬醋茶,什麼都來。對我來說,剛進公司的大挑戰有幾個。第一個,我一開始是在北京辦公室,對我來說有很多新的東西,包括新的文化-我沒有在中國大陸工作過,一下子到北京這個全新的環境,同事大多是中國人,客戶也是,而且是國營企業的客戶,但是當時北京辦公室的老闆是個美國人。所以我一開始要學習新的文化,不僅是地區的文化,還有人的文化。我要很快速去適應那個環境,這是蠻大的挑戰。舉一些小東西,例如,打字、用詞、用語和講話的口氣,你可以想像我有多少東西要適應。再加上顧問業的步調非常快,一開始就預期你要很快有表現,就很快把你丟到很有挑戰性的專案裡,你要馬上證明你在整個專案團隊裡不是累贅,有辦法創造價值,有辦法幹活。可以想像,面對巨大的文化衝擊,還要一邊跟上工作的快節奏,你說有什麼挑戰?全部都是挑戰。一轉頭要講大陸中文,另一邊還要用英文跟老外老闆將客戶談論的東西精準傳達,左右都不是我習慣的環境。此外,身為顧問,每次的專案、每次服務的客戶,面對的問題都是全新的,我要有辦法要很快 pick up 新的知識,對工程師而言是很不太容易適應的。以前,我可以花比較長的時間針對一個問題做深入的研究,現在第一週就要你研究 供應鏈中的某個問題,你可能只看過書,沒有實務經驗,但下週就要跟客戶開會,短期內就要提出建議,這個壓力相當大,卻只是顧問業裡的常態。因此,對我來說,除了文化環境之外,顧問跟工程師工作內容的巨大差異是最大的挑戰。', + }, + { + title: '顧問業特質', + section: + '我覺得在顧問業,有件事情是恆久不變的,那就是學習曲線非常陡。不管你在哪裡,無論在哪一個位階,就算我今天做到這個位置,我還是要學習,我每天都在大量學習。只是我們學習的管道多一些,公司內部的知識共享做得比較好。我們公司在全球各地服務上千家公司,專案中的學習都會經過機密處理後在內部分享,也讓我們了解在各行各業、各個經營層最關心的議題是什麼,我們可以如何去幫助這些客戶。除了內部大量的學習途徑,我自己每周五會和很多組織領導人,包括來自企業、自政府等的領導者交流意,這些機會對我來說也是很好的學習。有些時候,和這些檯面上的人物交換意見,聽取他們的想法,和這些一流經營者面對面學習,超過讀十本經營的書。再加上公司同事其實都很聰明,內部不斷會研究最新的趨勢,對不同行業的衝擊提出看法,我有時也會加入討論去跟全世界一流的經營人才學習。所以有太多太多的機會可以學習。只要待在這個行業一天,你就不會停止學習,你可以說這一個挑戰,這也是一個好處。', + }, + { + title: '工作、家庭與娛樂', + section: + '關於work life balance,每個人怎麼去抓工作和家庭的平衡,因為每個人的環境不同,我不太有辦法去分享什麼。當然, work life balance 肯定是很重要的,每個人必須要花力氣想。我覺得電機系有一些人可能不喜歡輸,所以在職場上過度投入,忽略家庭這個要素。我不知道你們身旁有沒有這些人,這不能小看,一定要花心思時間去做,至於要怎麼做,我只能就我的狀況做分享,不確定能不能對大家帶來幫助。但是蠻奇妙的一點,有時候工作上無法突破之處,他的答案會在家庭,我的意思是說把這個平衡做好對你是有幫助的。可能你在家庭中,跟家人小孩相處的過程裡面,可以對公司的一些事情看得更清楚。有些時候是心態的轉換,對工作不這麼執著,反而能夠度過難關。工作和家庭是一體兩面,要花一點心思去想要扮演什麼角色,家庭可以給什麼樣的支援,你在工作之餘可以給家庭什麼。', + }, + { + title: '給學弟妹想要進入顧問業的建議', + section: + '首先,我們這行主要分三個主軸,我大概簡單講一下。第一個就是區域軸,我們在全球有超過80幾個辦公室,包括像台北、香港、北京、東京、名古屋等。另一個是行業軸,像我專精於科技業,有些人主要看消費品產業,有些人是工業品。還有一個軸是功能軸,與企業內部的職能較相關。我在功能軸上的專業是策略,有些人是組織,也有人是銷售與行銷,其中,有人更專精在更細的主題上,專門在做定價。另外也有科技優勢小組,主要與 IT 、數位相關的。這是比較大型的顧問公司裡常見的分工。在這個分工之下,台灣的求職者有什麼優勢與劣勢呢?我覺得實際上是沒有直接關係的, 因為這個行業要求的,以一個單純的管理諮詢顧問來講,不一定要求在某個地域、某個行業,或是某個功能裡面有深入的研究才能進來。我們其實要求在決問題能力上或領導力上或是團隊能力上要有很大潛力,再加上要跟我們公司的文化相適應。 當然前提是過去的學業及職場表現頂尖。在這個前提之下,能夠展現團隊、領導及問題解決能力,這些人我們都很歡迎。你說台灣求職者有什麼優勢劣勢,我覺得很多時候是個人。因此台灣求職者不是有太多優勢劣勢。當然,我是以顧問業的角度來回答的。', + }, + { + title: '顧問業近五年來的變化', + section: + '過去這幾年來最大變化在於,第一,環境變動越來越大,各行各業的環境變動劇烈。第二個就是我們的客戶進化很多,越來越強。客戶的公司裡有越來越多頂尖的 MBA人才進入,我們公司的顧問也有許多後來進入客戶的公司工作。現在管理顧問業越來越難靠方法論、靠工具在賣錢。 基本上,我們的方法論和工具都是半買半送,我們真正為客戶創造的價值反而在於剛剛講的那三個軸的深度。這個深度就是說,你是不是對某個議題有深入的看法,可能是某個產業的議題、某個功能或是某個區域的議題,這種深度是非常重要的。換句話說,你今天一定要 outsmart 你的客戶,你一定要比你的客戶更懂。舉例來說,假設你今天的客戶是一家中國的互聯網公司,他很懂中國互聯網。你如果要有辦法幫他做事情,你也要懂更多,比如美國的網路發展,或是你還懂日本和其他先進國家的發展,甚至印度、東南亞等比中國落後地區的互聯網發展。你的客戶今天可能在某個特定的領域是全世界最懂的,你很難outsmart 他,但是你要在其他地方 outsmart 他,才有辦法讓他覺得請你來有價值。 這種對知識寬度與深度的要求,是過去十年很難想像的。這對我們這個行業的影響就是,你今天的公司規模一定要夠大,才能夠在各種議題上面涵蓋地更廣更深。這對我們業界最立即的衝擊,就是對中小型的顧問公司帶來極大的壓力極大。最近幾年,很多小型的顧問公司被併購,成為大者恆大的趨勢,這是目前為止發生較大的變化。對於五年後十年後的未來,有很多不確定因素。另外很明顯的趨勢就是新科技的興起,包括大數據、AI 。很多人在思考是不是以後這些新科技會取代諮詢顧問?我個人是覺得機會不大。當然科技的某些部分對我們來講是威脅,但是更大的部分對我們來講是機會。我們有更強的定位基礎來利用這些科技幫助客戶解決問題。那客戶是不是未來就不需要管理顧問,而可以直接利用這些技術來解決問題呢?這個可能性不是不存在,但是比較小。原因是,在很多客戶縱使已具備這些科技,有很多基本的問題是不能用科技解決的。我隨便舉個例:透過不同的部門之間或人跟人之間的競爭、互動,怎麼樣讓一個組織更有效率,很難純粹從科技來解決。另外要怎麼去駕馭這些科技,不管是 AI 、 VR 、 AR,到底怎麼用,從哪個應用下手,要怎麼架構你的組織,才能讓你的組織接受這些科技。這是科技不會告訴你答案,需要人去思考的問題。此外,因為我們做了大量的研究,也和互聯網公司有大量的互動,我們知道這些新型的公司怎麼運用這些科技,可以將這些經驗應用在比較傳統的產業裡。另一方面,顧問公司身為公正的第三方,我們在幫客戶做組織變革的時候,會比較有超然的立場。如果今天是人事部來做變革,通常會遇到最大的阻礙就是,人家會說「你今天為什麼不先刮自己的鬍子,為什麼來刮我這個部門的」,這提供了外部顧問公司一個進入的機會。只是以上所述的趨勢,對顧問公司也是挑戰。如果你還是套用傳統架構來解決問題,不一定可以成功運用這些新型的科技。以 BCG 的例子來講,這幾年其實為了未來做了很多佈局。我們在建立很多傳統沒有的能力。例如,公司內部成立了數據分析團隊,這個團隊現在以一個瘋狂的速度在成長,其背景和我們傳統的諮詢顧問有很大的不同,很多是數學相關領域或是數據科學家出身的,也有些人是在搞新創的。你可以想像我們公司裡面有些像矽谷裡面的創業家,在幫助我們大型的客戶創業。我們也有一幫人在搞投資的。像這種跟傳統顧問不一樣 背景的人才,是我們現在透過各種組織一直在建立的。可以想像五年後、十年後的 BCG 會長什麼樣子,一定會很不同。 現在是以諮詢顧問為中心,身旁有很多組織來支持核心,讓我們來服務客戶,像是一個同心圓。十年後公司更像聯合國,有一幫人專門做顧問,跟客戶接觸然後解決問題的,一些人做分析等等,會像我剛剛講的聯合國的概念。 我認為必須這樣做,未來才能適應這些新科技、不確定性的環境,繼續幫助企業客戶成長,這是我的看法。', + }, + { + title: '是否該繼續進修', + section: + '這個問題很難回答。像我剛剛說的,因為公司越來越像聯合國,我們招募人才時很看重diversity 。我們非常需要各種不同背景的人進來,這裡面包括 PhD 、在工作職場上有五到十年經驗的,甚至有些是在職場上待了二三十年的資深專家,我們也很歡迎。我們雇用的人才背景越來越多樣化。你說 PhD 還是職場經驗重要,兩者對我們來說都重要,只是進來後的角色會不一樣,職涯發展和訓練也會不一樣。博士生可能頭腦非常好,思路非常清晰,邏輯能力非常強,但是對產業背景相對不熟悉,可能需要商業上的培訓,或是給他更長的時間去適應整個顧問環境。另外針對一些資深的職場人士,他可能花了十五年在做數位行銷,他進來我們可能會給他比較專職的,可能一進來就是數位行銷專家,而不是單純的顧問,會有不同的職涯路徑,扮演不同角色。因此你問題哪個重要,其實都很重要,只是不同背景的人,他的職涯發展、角色以及受到的訓練等都不同。', + }, + ], + }, + { + bigtitle: '五、結語', + sections: [ + { + title: '給大學部的建議', + section: + '可以說是我的私心的偏見,對我來說,對於台大電機,我還是相信你們是全台灣最優秀的其中一群人,不是說其他人不優秀,只是你們一定是裡面最聰明的一部份。我不知道你們這屆是不是還有一些人,是因為分數才選擇電機的。我假設還是有一部份的人,並不是因為真的很愛電機才進來的。我想經過高中的教育,大概很多人都還不清楚什麼是電機,我想你們不是每個人都是經過深思熟慮的結果,是分數夠就進來了。我的意思是,你們其實可以在大學階段多去探索不同的路,可以是在電機領域,或這個領域之外,早點去面對自己。現在很多學生會延畢、出國交換,也許就是其中的一個表現。 我會蠻鼓勵你們多去探索自己,不要太受限於我們傳統教育體制之下給我們的結果,因為到一個時間點,一定會後悔,不知道是什麼時候,也可能是三四十歲,你開始想「我的人生到底在幹嘛?」,那個時候才要把過去的人生打翻,我覺得就有點可惜了。這是我的建議,多去探索自己。第二步就是,提早去為下一步或下下步打算。大學時期,很容易被眼前的課業限制住。大家都是好學生,不想輸別人,因此花很多時間把眼前的功課弄好,可是沒有花時間思考自己下一步是什麼。下一步是什麼?中期可能就是研究所,長期可能就是你的職涯選擇。以中期來說,我舉例,假設你大一大二 就決定要去 MIT 或是某學校繼續念電機,為了達到這個目標,你需要做什麼事情,大學的成績是怎麼算的,學校的要求是什麼,這類的事情你要提早弄清楚。像我們當年算 GPA ,發現國外的學校只看 GPA ,不看原始分數,然後我們那個時候 80 分以上就是 4.0 ,下面開始有 3.7 , 3.3 等等。我發現原來我只要每科都考 80 分以上就好,但我自己雖然有不少科目90幾分,卻因為一些沒有興趣的科目只有60分而拉低平均。我的重點是,早點知道遊戲規則,你就會有更多的時間去準備,而非時間快到了才趕著去看。我後來最大的反省就是,我當年花太少時間去研究我下一步要幹嘛。 現在我在世界各地做招募活動,發現很多大學生從大一大二就開始思考他們的職涯,而我也遇過一些台大學生已經大四了,都還沒開始想這個問題。這是一樣的邏輯。舉例來說,如果你對顧問業有興趣,你可以先去了解這個行業在人才招募上在乎的因素是什麼,如果顧問公司看重實習經驗,你卻在大四時才發現這件事情,你就很難去和同儕競爭,很可惜。所以我給的第二個建議就是,提早為下一步做準備。這不會花很多時間,早點去探索未來職業的要求是什麼,早點去準備。最後,我覺得電機系的學生,相較於其他系的學長比起來,就是 … 比較乖了些。好學生嘛。我不是要你們做壞學生,但是我要建議的事情跟你們做壞學生有點類似。不要去太相信一些人家給你的規則。電機系很強的一點,就是你把規則講出來,他可以在規則之內做的最好。但是有些特質你們是沒有被測試的,就是破壞規則的能力。放眼望去,那些在科技行業成功的人,每個人都是破壞規則才成功的 。我覺得你們在學生時代,可以開始思考,並不是要你們到處破壞系上的規則,甚至上街頭抗議什麼的,我不是這個意思。我覺得要做的是獨立思考,你要去挑戰很多事情,包括學業,學校的導師跟你講的規則,你的學長跟你講的規則,譬如說潛規則是怎麼樣,怎麼樣才有辦法進哪個學校,怎麼樣才能進哪間公司,都聽一半就好。聽一半的意思不是不尊重對方,而是你要思考,真的是這個樣子嗎,為什麼是這樣子呢,是不是有其他方法等等,就是獨立思考的能力。我覺得電機系的同學腦筋都很好,只是有時候你的思考結果好不好,不是在於你的腦筋好,而是在於心態。如果覺得老師講的就是王道,學長講的就是王道,就像一隻牛,從小把牠放在牛圈裡面,將圍籬通電。老牛被電了幾次後,跟小牛說,欸這個圍籬不要碰,會被電到。然後你就相信了,你沒有去嘗試被電過,怎麼知道這個電了會不會痛,或者這個圍籬是不是 24 小時都通電?說不定被電的同時你能突破,衝出圍籬獲得自由,有自己的一片天地。很多時候時空環境在變,只會給不斷挑戰現狀的人機會, 挑戰現存的事實跟常識,然後大步的向前走。這個當然也是管理顧問其中一個挑戰,因為我們工作的一部分就是去挑戰客戶的常識,所以我們的行業經常得這麼做。不過另外一方面,我會希望跟你們共勉,就是我相信在電機系,很多人有機會在本行領導台灣,走向下一個階段,而不是背負著很大的 legacy,覺得台灣的行業一直走向 成本削減。台灣的電機行業走向低成本代表著一個階段,理論上這是你們的機會,你們是有機會跟未來的,你們應該反過來看這件事情,前提是不能被過去的常識、慣習,或是大家認為的東西綁住。所以第三個就是獨立思考。所以,我的建議總共有三個,一、探索自己,二、多想下一步跟下下步,三、獨立思考。主要就是這三個吧,希望對你們有幫助。', + }, + { + title: '一句話談談電機系給您的歸屬感', + section: + '歸屬感不敢說,但是我以身為台大電機的一份子為榮。我真心覺得台大電機得天獨厚。以台灣來說,電機是特別值得向世界驕傲的產業,那麼在電機領域,全台灣最好的學校就是台大電機。我相信現在台灣在世界上取得的很多地位,跟台大電機密不可分,我想我不需要一一去談有哪些偉大的前輩為我們打下的基礎。總之,能夠沾點光,能夠身為台大電機一份子,是我一輩子的榮耀,哪怕我現在做的事情跟電機沒有直接的關係,我覺得這個是我永遠不會忘記的。', + }, + ], + }, + ], + + annotation: ['特別感謝:徐瑞廷', '撰寫:廖宜倫 黃暐倫 楊耀程 孫裕修 林恆陞'], + id: 'column_1601', +} diff --git a/backend/routes/srcs/in/column/preload/details/1602.js b/backend/routes/srcs/in/column/preload/details/1602.js new file mode 100644 index 000000000..3a223e1da --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1602.js @@ -0,0 +1,95 @@ +module.exports = { + title:['1992級 梁維仁(元大證券香港區總經理、花旗環球證券臺灣區總經理)'], + hashtags:['系友專訪', '梁維仁', '1992級', 'B77', '金融工程', 'CarnegieMellonUniversity', 'ComputationalFinance', 'MS', 'MBA', '元大證券', '花旗環球證券', '總經理', '外資交易', 'optionpricingmodel', '跨領域', 'justdoit' ], + sections:[ + { + bigtitle:'一、Emphasize', + sections:[ + { + title:'多才多藝的人', + section:'我從台南北上讀書,鄉下人進城,覺得系上每個人都好厲害。很會讀書是基本功夫,其他課外活動例如搞社團的、運動的,連打電動都打到極致,覺得系上的高手實在太多了,讓我大開眼界,這也是念電機最大的收穫,每個人在不同領域都很厲害,真的蠻佩服同學們的。台大是一個非常棒的地方,高手雲集,從別人身上看到自己不足的地方,也更有機會找到自己的優勢。“人”是我覺得念電機系這段時光最寶貴的經驗。' + }, + { + title:'各式各樣的課', + section:'我現在的專業算是金融業,在大學時的課外活動跟金融都非常遙遠,電機系本身的課業就已經很繁重。當時有參加一些社團,基本上是吃喝玩樂團之一。不過多辦活動,觸及到流程的安排與管理也對於自己的成長有所幫助。大三大四的時候已經打算出國,不像其他同學需要全心準備碩士班考試,於是我花了很多時間去修了其他學院開給自己系上的課,例如經濟系的經濟學原理跟個經,心理系的普通心理學、社會心理學,財金系的投資學,擴增了自己的眼界。 至於為什麼會走到金融這一行,說來也是誤打誤撞。大四時去修了財金系必修的投資學,開始接觸到例如CAPM等財金學的重要理論,需要統計與其他數理概念。我發現我相當喜歡運用數學去解決財金領域的問題,也就帶領我先走上碩士班雙修MBA與MS in Computational Finance的路子。衍生性金融商品需要處理許多非線性的問題,同時需要數理和金融知識,畢業後也就順理成章地走上當時投資銀行界中最夯的幾個領域之一,數理也成了我最重要的核心技能,電機系的訓練非常的 crucial。隨著年資的增長逐漸走上管理職,還是深覺除了數理的的核心技能之外,培養全方位能力的重要性,變得更為明顯。至於大學推薦的課程,我個人覺得理則學或是語言邏輯之類的很值得推薦。過去我們年代有哲學系老師開的課,不知道現在還有沒有。歷史系呂世浩老師的課非常熱門,引導大家如何思考與判斷,都是屬於這類的基本功夫。無論科學或文史,邏輯思考都會是一個最基本的訓練,萬流歸宗。最後,有一句廣告標語我相當認同,「 just do it 」,不要「 just talk 」。' + } + ] + }, + { + bigtitle:'二、Explore', + sections: + [ + { + title:'畢業方向', + section:'其實那時候也沒有很確定。我知道自己的抽象思考 (電磁學,哈!) 比較弱,實作是明顯的強項,個性上比較喜歡廣而淺、而不是鑽而深。所以那時候決定去讀商學院,不過又選擇了數理比較強的CarnegieMellon,拿了兩個碩士,除了MBA,另外一個degree是財務數學,發揮我們在數理的所長。幸好這樣的academic background支持我進入金融業,說來也是有些運氣成份。' + }, + { + title:'申請困難', + section:'CMU接受了我的申請,我還是被一些名校例如MIT,Stanford,Harvard 等reject。我覺得很公平,畢竟很多申請人的學經歷都超強的,GPA 4.0、GMAT 780、工作超過五年的,只能儘量修練自己。' + }, + { + title:'跨領域', + section:'專領域跟跨領域都要克服很多挑戰。跨領域中,整合可以算是最大的挑戰。儘管無法所有相關的領域都能精通,但至少有幾個領域必須是自己的核心技能。其他領域至少也必須能夠與專業人士溝通。' + }, + { + title:'出國環境', + section:'融入當然還是會有一定難度啦,我們本來就是外國人的身分,不過還是個性因素主導。脫離了舒適圈,大家還不都適應過來了,時間一久自然就習慣了。缺點就是花很多錢吧,優點的話主要還是眼界。我們電機系真的很棒,有一流的師資陣容跟研究環境,全球的排名也是很前面。留在系上繼續深造,一來省錢,二來可以跟著熟識的老師不間斷地繼續深入作研究。但是國外唸書的話,可以體驗不同的文化、同學,然後又遇到一群也是很厲害的人。外國的好手也多,到美國之後也能了解不同環境做人做事的方法。只能說各有優缺點,有時還是要靠一點運氣,可能幾年後或幾十年後才會證明當初的選擇。' + }, + { + title:'先備知識', + section:'書本上資源很多,不過以初學而言,自修的方式比較不容易有系統性。因此我更推薦去修課,例如修財金或經濟系必修的核心課程,包括投資學、公司理財、經濟學原理這類課程。我大學時期其實不太認同“玩”股票,但卻投入了證券業,依靠的還是金融相關學歷跟工程上興趣。我在美國讀書時,一時覺得好玩,用C++ coding了option pricing model,再用當時流行的JavaCafe設計了介面將這個model放到網路上。畢業前曾飛到新加坡面試工作,看到主管桌上厚厚一疊履歷原本覺得沒有希望了,但是跟主管吃飯的時候突然想到自己寫過一些程式,在公司電腦上的IE示範給主管看,主管眼睛一亮,他也成了我第一位主管。基於興趣,我努力的把一件事做好,幸運降臨,就這麼簽了我第一份聘僱合約。' + } + ] + }, + { + bigtitle:'三、Establish', + sections: + [ + { + title:'金融工程概念', + section:'金融工程是用數學方法去解決金融界實際遇到的問題。以選擇權 (認購權證) 為例,選擇權的payout是非線性的,要做定價時需要一些數學模式,像是stochastic calculus、boundary condition。這些演算也會牽涉到programming、algorithm。如同我們用數學解決電機中的問題,金融工程是以數學與電腦解決金融上的問題。' + }, + { + title:'金融工程前景', + section:'很可惜,我覺得在台灣前景不佳。主要是規管限制金融商品在台灣的發行,因此金融工程人員多數在香港、新加坡、東京、上海等地發展。台灣的金融業還停留在手工階段,沒有連結科技、效率、與系統化,所以常常出紕漏。這方面要進步不是那麼容易,特別是已經落後了許多,而全球各金融中心也持續進步。這是對於台灣本地的金融工程發展前景而言。對於對金融工程有興趣的同學,發展的前景還是不錯的。' + } + ] + }, + { + bigtitle:'四、Expand', + sections: + [ + { + title:'工作態度', + section:'進入職場之前,我對於股票債券期貨等等金融商品完全沒有經驗,進入職場後才開始學習如何當交易員。對新入行員工面對一個新工作,態度還是最重要,必須要努力創造自己的價值。我在做第一份工作的時候,覺得公司真好,竟然付我薪水讓我進公司來學習,當然很快地我也開始直接幫公司增加收入,相對應地公司也很慷慨地支付我工作獎金,這完全是一個互贏的概念。' + }, + { + title:'累積經驗', + section:'在證券公司工作,每個部門的專業分界非常大。我覺得總經理只是一個管理職的頭銜,以調合部門運作為主。在管理職之前的交易員資歷,在業界的影響力其實更大。當時我手上的台股交易量,大到全市場的百分之五。當時的主管也很信任,在買賣的時候他不會過問,就是一個互相的信任。但是我也必須要完全按照公司的規範去進行,每年的利潤相當不錯,也很穩定。大家如果真的有心要往金融界發展,還是要找到自己的興趣。總經理這種職位無法強求,價值也比不上某個領域的專精。當初剛入行時對於這個行業也是一知半解,進去之後才慢慢學,積極去expose yourself讓別人來認可自己的價值,要differentiate yourself。' + }, + { + title:'適應壓力', + section:'金融交易是個壓力極大的工作,如果神經太小條,太容易被情緒影響,做這行也許不是那麼適合。做事要仔細,心則要放寬一點,看到漲跌不能太緊張,不然身體受不了。交易員這種高壓行業也做不了多久,大概就是十幾二十年了。外資交易員手頭上都有幾十億幾百億的資金,純粹是按照我們的判斷去建立投資部位,目標在儘量降低風險的情況之下增加交易獲利。當然這樣的授權金額相當大,在台灣的公司不太可能發生。' + }, + { + title:'享受辛苦', + section:'1997年來到香港,剛去的時候舉目無親,廣東話不會講,一直到一年後才第一次跟在港台灣人一起吃飯聚會,和現在香港到處都是台灣人的情況是完全不同。每天的生活就是早上七點出發,路上買一個麵包和咖啡到公司;中午在公司吃便當;晚上八點下班,路上順便買個便當打發晚餐,每天就這樣的routine。連週末都拼命看書補充不足,研究公司的文件。辛苦是辛苦,不過還是很enjoy這份工作,值得付出很多的心力。' + } + ] + }, + { + bigtitle:'五、Encourage', + sections: + [ + { + title:'大學部建議', + section:'基本上還是希望能夠 Follow your heart 之餘,還是要 Just do it,不要虛擲光陰。再來就是健康很重要,無論是什麼工作,心臟要夠強、體力要夠好。' + } + ] + } + ], + + + annotation:['特別感謝:梁維仁','撰寫:藍珮瑜 Steven Huang 林昱嘉 陳柏瑞'], + id:'column_1602' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1603.js b/backend/routes/srcs/in/column/preload/details/1603.js new file mode 100644 index 000000000..19f7e4a96 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1603.js @@ -0,0 +1,63 @@ +module.exports = { + title:['2008級 趙式隆(台灣矽谷創業家協會理事長)'], + hashtags:['系友專訪', '首PO', '趙式隆', '2008級', 'B93', '創業就像搶銀行', '台灣矽谷創業家協會理事長', '學悅科技Zuvio創辦人', '台灣虛擬及擴增實境產業協會共同發起人', '網路與多媒體實驗室助教', '台大創創學程', '保持好奇心勇於嘗試' ], + sections:[ + { + bigtitle:'一、談創業經驗', + sections:[ + { + title:'台灣矽谷創業家協會', + section:'台灣矽谷創業家協會,是一個非營利組織。過去台灣有很多人到了矽谷去,然後在RCA(編按:Radio Corporation of America,美國無線電公司)的訓練下回來,創造了今天的新竹科學園區。那當然現在的台灣,其實在新創產業這一塊遇到了一些瓶頸,就是我們有人才可是沒有市場跟資金。在現在這個時代,政府也送一些團隊到矽谷去,但是到達矽谷的時候我們就會發現,台灣團隊要存活下來不是那麼容易。所以我們希望能夠做出一些橫向連結,也許可以增強台灣在矽谷的生存機會。另一方面我們也希望,不見得能夠活下來的團隊,可以將一些矽谷的精神帶回台灣。例如,在矽谷他們非常鼓勵失敗的經驗,然後你再重新站起來,但在台灣其實沒有成為一個共識,這是在這個環境上面一個很辛苦的地方。我現在做的事情蠻雜的,基本上就分成這幾個面向。' + }, + { + title:'Zuvio', + section:'Zuvio做的是一個教學互動系統。一開始,大概三四年前在葉丙成老師協助下和幾個學弟一起做的一個專案,這專案主要解決的問題是,上課時老師和同學的互動問題,老師在上課問問題,學生都不見得會回答;或者學生心裡可能有些想法,但是不敢跟老師說。那時候我們就想說讓學生可以透過他們的手持裝置跟老師互動,從原本一個OPEN-SOURCE project做到現在。這間公司最開始的時候狀況是這樣:就是當年我在教的一些課遇到一些問題,可能當我在帶各式各樣的課程,例如機率與統計、微分方程,我問學生:有沒有人有問題?即使在教了幾年經驗之後,知道這個段落大家都會有問題,但大家也都不會發問。一到下課的時候,你知道台灣人就很喜歡偷偷來,然後大家都會圍過來,結果每個人問的問題都一樣,然後助教我就走不了,就會很不爽!很多人心裡有問題不問,考試的時候才不會寫,我覺得這件事情非常不行。當時我們從來沒有想過這個事情可以成為一個business。直到第一個學弟跟我們說,他要離開去上班了,但可能還是可以fully-support這個project。那時候我們就說:「好啊、你就去上班、有空來support這個project。」然後你會發現當人去了台灣的科技公司上班之後,上班以外的時間不是在加班就是在睡覺,所以基本上他是沒有時間來support這個事情的。那後來第二、第三個學弟再跟我們提一樣的事情的時候,我就知道這時候不行了,我們要有一個若干的Business model。所以那時候我們就把公司開起來,很幸運地一直做到今天。我們的初衷很簡單,就是解決老師和學生之間的互動問題。當學生和老師之間沒辦法透過語言來做很自然的溝通的時候,我們希望透過手持裝置來改善。我們現在仍然一直專注在這個點上面,這是一個問題,我們相信我們能提供最好的一個解決方案。' + }, + { + title:'創業的挫折', + section:'我覺得正常在創業的人不會講挫折,因為創業必然要是樂觀的。每天都會遇到一堆很糟的事情,如果沒有很好的調適,通常無法繼續往下。我們公司有遇到一些小挫折,就是基本上是獲利了,但主要的客戶是學校和大型公司,開票期的匯款interval是很長的。這就很常遇到一件事情:下個月大家要領薪水,沒有錢怎麼辦?這時候你就要自己從口袋掏錢出來把這個事情墊掉,那你有沒有這個Guts?這種事情一開始做會覺得天啊!怎麼是這樣。但後來久了之後會覺得創業這樣是常態嘛。也常常會被罵,被員工、使用者、各式各樣的人罵,但被罵久你就會習慣了。剛開始會覺得說生活中的每件事都是挫折,而現在已經是生活的一部份了。' + }, + { + title:'創業像什麼?', + section:'創業這件事情很像搶銀行。怎麼說?大家應該看過一些電影,就籌備過程而言,通常主角需要很棒的團隊,技術高超的駕駛、開鎖專家、爆破專家...各式各樣的人;再來會需要一個很縝密的計畫,去想每一步要幹嘛;第三件事情是計畫永遠趕不上變化,當企劃看似完美的時候,永遠不知道中間會遇到哪些突發狀況。就結果來看,創業跟搶銀行一樣,如果成功,會變得很有錢,當你的公司被併購或上市的時候,你會變得很有影響力,做到任何你想做的事情…但是,他也很容易失敗。我覺得創業跟搶銀行最像的事情是,他的成功機率非常非常低,而且創業成功的機率可能不比搶銀行的高。我們很常看到非常有潛力的新創團隊,在短短三年內就消失了。' + }, + { + title:'電機系學生特質與創業', + section:'我自己最欣賞的一類創業者是所謂技術型創業者,他們有著一個靈魂,叫做駭客精神。我講的駭客不是這些搞破壞的駭客,駭客是一群能自己打造工具的人,當一個問題在你前面時,你又沒有工具可以用的時候,你會去想怎麼樣有能力去把它打造出來,所以我最欣賞的是有技術背景的一群人,可以自己打造工具。我想電機系的人大部分都有這樣的條件,為什麼?因為我們都看到電機系的學生在這裡受到四年的摧殘,如果能順利畢業的話,這群人就算一開始不聰明,到最後你也必須學會在很短的時間學會一樣東西,即使你學不會也要有辦法在答案上寫出來。我覺得在臺大電機系這個高壓力的環境下,人的能力是志不在話下。還有第二個很大的優勢,我們有非常多優秀的同學,在一起可以做很酷炫的事情。我們每一年電機之夜就是辦得超盛大,讓大家以為我們花很多錢(還是真的花很多錢這我不清楚)。最後一個事情是,在這個人脈網絡裡面,台大電機系佔了一個很重要的角色。很多人大學的時候功課非常爛,大學畢業之後轉換跑道,我們有人在華爾街、在矽谷、在各式各樣的產業,甚至有人是書法家或作家,台大電機系有非常多,你可以想到做各式產業的人都有。甚至像我今天早上跟我們配合的會計師事務所開會,那邊的會計師就是台大電機系畢業的學長。所以我覺得,台大電機系有各種面向,當你想要走任何一條路的時候,都有一個模範可以去參考。回頭說明,為什麼這對創業有幫助?我覺得創業有兩個要件:第一個是所有的事情都有一個死線。你必須要在死線之前完成所有事情,所以你快速學習的能力很重要,而抗壓的能力很重要。十天前公布的作業,你可能前面九天都在打電動,最後一天要把事情做完,當有個死線、你就可以把事情完成、而且是完美的完成,而你前天晚上沒睡覺,隔天上去發表,還是可以講得像前一天睡得很飽一樣。我覺得在臺大電機系這個訓練在人的素質上是一個非常重要的要件。再來是一個統合型的能力,因為任何一間startup都跟大公司一樣,只是startup比較小。大公司每一件事情都不能省略,法務、財務、稅務...所有事情,只是startup人比較少,所以每個都要會。那在台大電機系,你有豐富的學長資源,或是你在大學的時候,如果你願意你可以做很多的事情,在大學就可以很早的體驗到這些事情的重要性,特別像台大電機系活動辦得這麼多,可能是拉贊助之類的,我覺得這些事情都非常重要。我覺得這些都是台大電機系未來要創業一個很重要的基礎,因此在這邊我不會不鼓勵大家創業,如果連台大電機系都不創業,那其實台灣沒有希望了。' + } + ] + }, + { + bigtitle:'二、人生迄今的里程碑', + sections: + [ + { + title:'求學時期', + section:'我現在三十歲,過去不同階段有不一樣的想法。小學的時候打籃球,而且打到全國冠軍,以為未來會成為運動員。後來家裡要我讀書,我就讀書。從國中讀到建中資優班,比奧林匹亞保送進電機系,中間的過程也有很多想法,像比奧林匹亞會覺得自己是不是該對基礎科學有偉大的貢獻。可是那時也學習寫程式,覺得寫程式也是超級有趣的事情。後來在家教的過程,開始對於教書有興趣,教到別人學會是非常有成就感的,所以那時候我想當大學教授。當時聽說要出國回來才能當,所以那時候有準備出國,但因為家裡有點狀況,最後就留在這裡讀博士班,希望留在這裡當教授。但是當真的開始在學術界,我前前後後在台大電機系待了12年,我發現所謂這樣的學術圈跟我想像的不太一樣,我就開始想像這到底是不是我要的生活?' + }, + { + title:'創業與目標', + section:'因為Zuvio也開始在動,我就想說不如就先作為一個創業家、繼續做這樣的事情。但是開始作為創業家這個事情後,我就回歸到去思考我這個人想做什麼事情,我覺得我終其一生在追求的目標,其實是讓這個世界不要再有階級複製,去解決階級複製這個問題。當一個社會沒有辦法提供一個扭轉的機會,沒有辦法讓社會底層的人能抓到一條線去提升自己的社會地位是不合理的。舉例來說,賈伯斯是來自於相對地位比較低的地方,但他在美國能夠成功,於是很多人講美國夢,講得就是當下在美國這樣的環境,不管你的出生是什麼,都有機會可以成功。我就開始思考,這背後的原因到底是什麼?我發現,其實要打破階級複製的兩個最重要的要素:第一個叫做教育,所以那時候我想要教書,現在創業也是做教育相關的;第二個叫做醫療,當大家有健康身體的時候,才能夠在一個合理的水平線上做競爭,但醫療是一個資金密集的產業,我自己的想法是,當我把現在做的這些微創新、小玩具,得到初步的成功之後,我希望把這個東西投注在醫療上面,試著去改變、讓這個世界變得更好。這是我最想要做的事情,人生是個最佳化的過程,你永遠不知道絕對的最佳化是什麼,但當你走每一步的時候,你可以針對下一步做最佳化,我們叫他做Greedy Algorithm,其實你走出來的路不會太差。這是我現在三十年一點點的經歷、能夠分享的差不多是這樣。' + } + ] + }, + { + bigtitle:'三、給大學部的建議', + sections: + [ + { + title:'保持好奇心、勇於嘗試', + section:'對於一般的大學生,我通常會給一個建議「保持好奇心、勇於嘗試」。大學是人生中成本最低的階段,過了這階段,會發現每一件事的成本都非常高。假如你已經結婚了,還有小孩,你敢不敢再去做一件很冒險、可能讓你傾家蕩產的事情?不見得敢。當你年紀越大,擁有得越多,就會越害怕。特別是在這個資訊爆炸的時代,所有的事情都是透明的,可以看到很多機會,但你會不知道這些是不是真正很好的機會,不知道自己是不是真正喜歡,那我覺得應該是加法模式,而不是減法,看到這東西覺得喜歡,就嘗試看看,覺得不好就趕快換掉,在這一個過程裡面,保持對很多事的好奇心是很重要的,很多事情不試著做,就不會知道事情最後會怎樣。' + }, + { + title:'一句話談談電機系?', + section:'對我來說,電機系像是一個家,我在電機系待了非常久,從大學到現在12年,幾乎是我人生一半的時間了。所以,這邊的每一樣東西我都很熟悉,哪邊改變過了我都知道,看過很多很多事情。這裡對我來說是一個歸屬感很強的地方、我覺得對於很多電機系的學生來說,在這邊待了大學四年,其實你在系館花掉的時間遠比其他系的人多,因為你熬夜的時間很多、你在這邊半夜做作業的時間很多、然後你在這邊為了要跟同學討論、你不得不去學校做很多很多事情。我覺得相較於很多系來說,電機系是個向心力很強的地方。即使我在大學的時候不太參加系上活動,到現在這邊畢業的每一個學弟妹,名字可能叫不出來,但是臉都熟悉啦。我覺得自己對這裡有非常深刻的感情。' + } + ] + } + ], + + + annotation:['特別感謝:趙式隆','撰寫:許秉鈞 袁培傑 劉禹辰 曾耕森'], + id:'column_1603' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1604.js b/backend/routes/srcs/in/column/preload/details/1604.js new file mode 100644 index 000000000..d3ac20038 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1604.js @@ -0,0 +1,90 @@ +module.exports = { + title:['2000級 胡一天(源鉑資本Kyber Capital執行長,專業投資人與金融觀察家)'], + hashtags:['系友專訪' ,'胡一天', 'ThomasHu', '2000級', 'B85', 'Blockchain區塊鏈', 'Ethereum以太坊', 'KyperCapital', '源鉑資本執行長', 'InfiniteStudio無限工作室', 'FinancialEingeering', '哥倫比亞大學金融工程', 'ColumbiaUniversity', '電機系與法律系財經法學組雙主修', '跨領域', 'Hongkong', '金融觀察家', '專業投資人', '先進到適合發展的環境再積極創造機會' ], + sections:[ + { + bigtitle:'一、求學相關', + sections:[ + { + title:'Why NTUEE?', + section:'喜歡人文科學,也認為了解基礎科學和新科技很重要(民國96年入學時,互聯網、高科技已開始發展)所以選擇了綜合型大學的台大。而在電機系的選擇很多、可以認識的人也很多。電機系跨界的特性,可以在廣泛接觸各種知識後,深入研究。' + }, + { + title:'目標:投資強者同學', + section:'自己在電機系不一定能特別出類拔萃(成績、興趣…),而大學時認識很多強者同學,將來很多會研發東西、發展事業,我就作投資他們的人。因此,大學時的關鍵放在找到有革命情感、志同道合的同學。不斤斤計較分數,也不以到園區當科技新貴為目標。' + }, + { + title:'大學生活:雙主修電機、法律', + section:'自己有英語優勢,當時想多修個財金方面的專業,(但是財金系無法雙修,自己又不想只有輔系),所以選了法律系財法組。當時讀了華爾街Robert Rubin的故事:Robert Rubin耶魯大學法學院畢業後,當了兩年律師便進入Goldman Sachs從riskarbitrage 做起,待了二十七年,之後在柯林頓時期擔任財政部長。有了這個故事在心裡後,更加堅定自己念法律系的動機。法律系的課程多以面對國考為教學目標,自己的目標不是國考,所以在念的時候會多搭配相關的資料閱讀。例如修憲法課的時候,自己閱讀政治學、歷史、英美憲法相關的文獻。(P.S. 美國憲法起草人之一Hamilton是Columbia University的校友)大學時用空閒時間多學了一些法律知識,多了這方面的素養。現在做生意、投資、收購融資的時候,自己就有相關的法律概念。像是之前在香港做收購、在紐約做融資看一家公司的時候,看上市公司的財報,決定在什麼時間點買,需要相關的金融常識、法律素養,綜合判斷。' + }, + { + title:'系上印象最深刻的課:微分方程─以物理模型思考', + section:'系上印象最深的一門課,是「工程數學─微分方程」(林清富教授)。修習這門課時,我和另一位同學找了兩本教科書自行研讀。其中一本是關於微方題目與物理意義的連結。印象最深刻的是學習的態度:課堂上教了很多解題技巧,所以個人更要重視學習的紮實度,例如了解微方背後物理模型的建構。將「空的」數學賦予實質的物理意義,才是學工程的人該有的態度。其實金融中可以被量化的面向,也可藉由這類物理模型,加入邊界條件 (Boundary Conditions)解決。但商業投資上更看重直覺與悟性。' + }, + { + title:'探索興趣:Keep yourself open-minded, no stereotypes', + section:'保持開闊的心胸接受事物,不要受到主流價值觀、刻板印象影響,十年前、十年後流行的東西不一樣。台積電很熱門、可以賺不少錢,但不是大家都需要去台積電。所以不要為自己設下限制,大學時多方嘗試,找想做的事。讀完該讀的基本資料,問不懂的問題,找出答案,進而為自己創造機會。' + } + ] + }, + { + bigtitle:'二、台大電機畢業之後', + sections: + [ + { + title:'Going abroad: 接近國際金融市場', + section:'金融中心是一個情報集散地(像是臺灣的新竹科技園區,相同類型的產業高密度聚集)。我前兩個工作的地點(紐約、香港)便是世界的金融中心。讓自己置身國際大市場,獲得的體驗跟在臺北差異甚大。以國際大市場而言,身在臺北有個先天的劣勢,現在跨國金融機構很難把臺北年輕的學生視為招募高端的金融、商管儲備人才的首要目標。也建議未來想從事傳統金融產業(即不包含Fintech等新興產業)的新鮮人,不要將自己侷限在台灣。' + }, + { + title:'Why Columbia University?', + section:'退伍後申請了五所學校(Stanford, MIT, Cornell, U Penn, Columbia)的電機研究所,Columbia當初不是我的首選,因為他的電機不是最好的。不過電機不是我考量的重點,關鍵是要出國,先到那裡再說。我想去紐約,所以就選了Columbia。事實上,當初申請U Penn上的System Engineering,Cornell沒有拿到獎學金,而Columbia本來沒有上,但是另外寫信給教授後,教授看了我的背景,覺得我是個有趣的人,決定讓我去當PhD學生。開學前去辦公室找他,教授表示他當時對電機沒有興趣,正在研究神經科學相關的領域,他說我先看看,一年之後再決定是否跟他做研究,想轉到別的地方他也會支持。Columbia的制度是PhD student學籍是掛在GSAS (Graduate School of Arts andSciences),而非自己原本的工學院。只要能負荷,修什麼課都可以。我利用這段自由的時間,大量地累積金融相關的知識與人脈,也積極參與大型金融機構的校園徵才。在GSAS的一年,我努力轉到Financial Engineering,因為知道華爾街是很現實的,沒有對的資歷、關係,連面試的機會都不會有。像我這樣剛來到美國,沒有身分,大學部也不是唸本科,一定要有別的策略。所以我決定拉長戰線,除了學習之外,更要多接觸可以幫助自己找到工作的人。這一年修了很多FE領域博士班等級(比較難)的課,想說對之後轉系可能有幫助。後來我也以財務工程碩士畢業。總而言之,學校、都市、所選的內容,跟你的個性、能力是否匹配,非常重要。在對的環境,自然會遇到對的人,而這些好的關係也能從學校延續到職場上。我在紐約的第一份工作,也是哥倫比亞大學一個教授推薦的。' + }, + { + title:'Why Financial Engineering?', + section:'大學時買過國外股票、基金,在dot com泡沫爆破前有賺到一些錢,覺得投資有趣。金融 (Finance) 是一個可以學以致用的領域,學原理、實戰、有基本素養後入行。金融業的本質其實也是科技業,因為科技的進步,創造出新的金融服務。此外,金融很刺激,每天都有新的事情發生,每天都要閱讀大量資訊、快速學習,否則難以應付競爭激烈的市場。' + }, + { + title:'同學的互動', + section:'經常追蹤同學們做的領域、事情,知道現在最新的東西是什麼,以及他們對這個領域的其他想法。另外,自己本身的知識背景,也能容易的了解他們討論的內容,對選擇投資目標幫助很大。' + } + ] + }, + { + bigtitle:'三、工作相關', + sections: + [ + { + title:'畢業後第一份工作─紐約', + section:'我在商學院修了好幾門,其中一門是關於槓桿收購,學到很多與華爾街相關的實務知識,很實用。當時的教授的老朋友在紐約剛成立了新的基金公司,教授推薦我去試試,後來面試很順利,先從實習開始,後轉正職。一開始是做計量選股模型的工作,後來開始參與基金操盤。一開始是專注全球價值股,2008年時與另一名資深分析師接手一檔中小型成長股基金負責看全亞洲(包含日本)的股市,也有參與大老闆的旗艦基金操盤,最高曾負責過2億5千萬美元的部位,也親身經歷了百年難得一次的全球金融海嘯。' + }, + { + title:'從紐約到香港', + section:'當初覺得在紐約繼續做的話,薪水越來越高,但人生不會有太多變化。後來因緣際會,認識了中華開發的高層,決定在2011年離開紐約,加入開發在香港成立的新部門,與另一位新加入的香港同事一起經營高收益收購融資的業務。從2011年到2015年,陸續評估了一百多個融資案,做了十二件,覺得自己在公開市場與私募市場都有相當程度的歷練了,而且自己一直有在關注新創投資,也有天使投資出場的經驗(台大電機學弟沈育德創辦的StorySense Computing),看到金融科技與區塊鏈(blockchain)應該會有巨大商機,於是跳出來成立源鉑資本。' + } + ] + }, + { + bigtitle:'四、給學弟妹的建議', + sections: + [ + { + title:'培養跨界能力', + section:'「跨界」愈來愈重要,例如:區塊鏈(Blockchain)需要懂法律、財金、科技,非常值得關注。職場分工因應工業走向後工業的過程,資源不夠,為了效率而集中化管理。然而現在環境改變,教育模式卻沒變,職場分流沒變,人的觀點也沒變,就會被時代拋棄。台灣目前需要突破的就是這類思維的問題。大學時要花時間做紮實的事,在一兩個領域紮根,也不要侷限在電機系。電機系課程設計使我們可以看更多的領域,雖然都是在電機的領域,也需要多用心才能看出課程間連結的意義。此外,修課需有長期的策略,但仍需留有彈性,隨著時間修正,因為自己的預測不一定正確。' + }, + { + title:'累積實戰經驗', + section:'台灣學生基礎很好,但需要更多實戰經驗。進入好的公司,會給你很多學習資源,然後拋問題給你想。以石化業為例:怎麼搞懂化工廠?業界怎麼看、投資界怎麼評估?要看一整個產業的股票,包含同行當中的競爭者,也需要分析原料成本。每個地區(北美、中國、日本...)都有它自己的邏輯。這些無法透過課本得來,需要實際跟業界專家與資深分析師學習,自己再多做功課。' + }, + { + title:'一定要去美國?', + section:'金融的話,第一份工作在面向大型市場(e.x. 中國或美國)、跟國際接軌(e.x. 紐約或香港)的金融中心做,會比較好。台灣公司無論是本土或外商,對國際市場的關注不夠,專案接觸面不夠廣,不容易學到正確的基本動作。有心在中國發展的話,到北美去留學、工作有好有壞。中國大陸市場瞬息萬變,很多訊息需要在當地才知道。紐約的高度比較高,是好平台,但真的要做到很「接地氣」( on the ground) 的東西、做大事業,比較難。大部分人在那邊可能當小螺絲釘,但是能有相對單純的工作環境貢獻專業。亞洲市場複雜很多,有機會迅速竄升。上海以後會更加重要,香港有獨特地位,但重要性逐漸下降,北京平衡兩者,都是不錯的選擇。' + } + ] + + } + ], + + + annotation:['特別感謝:胡一天','撰寫:吳奕萱 楊程皓 毛弘仁 江庭瑋 蘇峯廣'], + id:'column_1604' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1605.js b/backend/routes/srcs/in/column/preload/details/1605.js new file mode 100644 index 000000000..4ef2c80fb --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1605.js @@ -0,0 +1,121 @@ +module.exports = { + title:['2006級 高奕豪(Quantitative Researcher @Two Sigma Investments)'], + hashtags:['系友專訪' ,'高奕豪', '2006級', 'B91', '計量金融', 'Stanford', 'InformationSystems', 'TwoSigmaInvestments', 'QuantitativeResearcher', 'Google', 'Yahoo', 'McKinsey', 'GoldmanSachs', 'ACM_ICPC', '數學與資訊奧林匹亞競賽' ], + sections:[ + { + bigtitle:'一、序言', + sections:[ + { + title:'序言', + section:'因為台灣的產業結構和升學制度,使得電機系收納了全國理工科學生中的菁英。這種同儕關係是個得天獨厚的資源、讓我在電機系四年從同學身上學到的不比從教授身上學到的來得少。到了美國後,在史丹佛求學的日子,又讓我再次見識到台大電機四十年來累積的龐大人際網路,也因此不管是在校內校外都得到很多系友前輩的幫助提攜,受益非常多。' + } + ] + }, + { + bigtitle:'二、大學時期', + sections: + [ + { + title:'大學時期的成就', + section:'我在大二那年跟兩位資訊系的朋友參加ACM主辦的程式設計比賽,一路拿到了全球第六名。這個過程中讓我學到不少團隊合作的技巧,也讓我看到世界之大,加深了我日後想要出國唸書的決心。另外就是我很會考試,所以成績很好,大學拿了八次書卷,但這是一個出了社會就沒用的技能。事實上,這是個上了研究所就沒有用的技能。(問:所以,做研究的能力相較比較重要嗎?)是的。還有我是電機營的教學組組長,我認為是一個滿好玩也滿有收穫的經驗。另外我是系泳的,那時候有一群熱血的人在弄,我們也在想萬一有一天這一群熱血的人不見了,那還會不會有系泳。' + }, + { + title:'最深刻的一門課', + section:'因為後來博士研究的關係,大學部對我而言最有用的是工程數學方面的訓練,尤其是線性代數和統計機率。這種理論的課,通常比較雋永,不太會隨時代而汰換。另外就是我修了當年的網路與多媒體實驗。那是個非常辛苦的過程,但我學到了兩樣很重要的東西。一個是上網讀說明文件來寫程式,另一個是工程團隊的分工。這兩者讓我後來受用很多。外系方面,我修了不少資訊系的課,都非常有用,但那應該不能算外系。真正的外系,我想到的是我修了經濟學當通識,這個在生活中非常實用。此外就是我去修了國企系的管理學。那門課雖然沒有太多硬知識,但讓我見識到了另一種思維,並結識了很多商學院的朋友,我從他們身上學到很多。我在大學的最後一年有幸被選進一個McKinsey的訓練計畫,也是受了他們的啟發。不過如果能讓我再來一遍的話,我會想多修一些理學院和文學院的課。我當年修課太重實用性,其實很多文理方面的知識對人生影響很深遠。我一直覺得身在台大最大的好處就是我們是一個綜合性的大學,什麼系都有,而且幾乎都是全國最好的。當年應該多把握這方面的資源。我後來選擇去Stanford唸博士,也有部份是受此啟發,希望能到一個綜合大學去繼續學習。' + }, + { + title:'關於專題研究', + section:'我當年做過兩個專題研究。第一個是跟 李琳山教授做的,用機器學習的方法來判斷中文語音中的情緒。第二是個和 陳宏銘教授做的,如何用光場信號來偵測實景中的深度。兩個都是非常好的經驗,讓我在畢業前就大概知道做研究是什麼樣子。跟一般修課寫作業考試不一樣,做研究沒有明確的路徑,也沒有明確的終點,投入的時間也可以沒有止境。我後來博士論文做的是機器學習,現在的工作是在做統計信號處理,都跟大學專題有關。' + }, + { + title:'國外研究所 CS v.s. EE', + section:'當初的考量除了申請難易度外,主要是我還沒有確定要專攻的子領域,而覺得選擇EE可以保持更大的彈性。但其實美國科系間分界比較模糊,所以選哪個系影響不大。我當年的指導教授有三個系的身份,分別是電機系、工管系和資訊系,同時還指導過統計系的博士生,所以非常多元。' + } + ] + }, + { + bigtitle:'三、碩士博士時期', + sections: + [ + { + title:'PhD及MS間做選擇?', + section:'在我那個年代,我非常崇拜Google的兩個創辦人Larry Page和Sergey Brin。他們把在Stanford念博士時的研究拿來創業,我當時非常崇拜這種路線。所以覺得如果我念博士也可以做到這樣就太好了。(問:可是博士不是會念比較久嗎?有些人會因為花比較多時間(在唸書)就比人家晚幾年出去工作。)這是沒錯。如果是從長遠來考量,你可以想像這是一個比較長期的投資,希望十年二十年以後能夠回報。但這的確是一個有風險的投資,畢竟你比別人多花了3、4年(一般碩士1~2年,博士5~6年),要有把握這3~4年對於你的職涯是有加分的,要真的學到東西,而不是在混學位。' + }, + { + title:'對Stanford的印象', + section:'首先是,這裡的教授非常沒架子。舉例來說,有些大牌教授會去旁聽其它系的課。這種精神我覺得在亞洲文化裡比較難見到。第二是見識到東西教育著重的不同。舉例來說,我當年修機器學習這門課,我發現考試的時候亞洲來的留學生都可以輕鬆電爆美國同學,但到期末專題時美國同學的成果卻可以狂電我們。這反應出兩者著重的地方不一樣。另外就是對研究興趣的強調。有一位我很尊敬的教授跟我說,如果你找到一個適合你的博士論文題目,你應該要常常嘆口氣說,唉,人為什麼要吃飯睡覺上廁所?要是可以不用做那些事,讓我專心研究這個題目,該有多好啊。至於Stanford,我前面有提到它是個綜合大學,而我很喜歡這樣的環境,在這種環境裡你可以認識很多領域的人才,有時候我甚至覺得,在宿舍餐廳裡面聊天所學到的不比在教室裡面上課來的少。(問:那會不會有人多花好幾年在探索要做什麼題目,就念很久?)會,所以我也有認識幾個待比較久的博士生,因為他們想多做點事情。(問:那學長是領獎學金出去的,會不會在探索過程中花太多時間,最後學校就不給你獎學金?)在美國他們會覺得,收一個博士生其實有點像是僱傭的關係,而不只是師生關係,所以你必須要有產出、要有表現,他們才會願意維持你的獎學金資格。' + }, + { + title:'赴美留學的文化衝擊', + section:'其實我待在美國那麼久,基本上沒感覺到我有被歧視過,倒是有發生過一件事情讓我見識到美國人對於「尊重不同族群」是很堅持的:有一次耶誕節的前夕去學校餐廳買午餐,為了要增添過節的氣氛,那位結帳的阿姨戴了一個麋鹿的帽子。要結帳的時候,她跟我說:「Merry Christ…」在還沒講完 Merry Christmas 之前,她就改口說「Happy Holiday」,為什麼呢?後來我跟美國朋友討論,覺得應該是她看到我是亞洲人,可能不是基督教徒,她如果祝我耶誕節快樂,其實是有可能冒犯我的,所以她改變成祝我佳節快樂,她覺得這樣才是一個尊重不同文化的做法。(問:會不會因為學長是在Stanford這個充滿知識份子的環境,所以沒感受到歧視?)的確有美國朋友跟我說,我覺得沒有受到歧視是因為我在Stanford,周遭大多知識份子,與人相處比較溫和,他說如果我到別的地方就不一定了。' + }, + { + title:'求學與工作', + section:'我在畢業前總共做過三個實習,一個在台灣兩個在美國,一個在金融業兩個在網路業,所以其實畢業前就大概知道職場長什麼樣。現在台灣的暑期實習機會比我們當年多很多,我覺得學弟妹可以多試試。另外在美國唸博士本身就蠻像一種工作,再加上我後來從事的工作也是研究導向,所以兩者間的差距不大。(問:那在讀博士的過程中去實習,會不會對學校進度有影響?)會,所以都要先和指導教授討論,有些指導教授很能夠接受學生做實習,有些不能接受,這跟科系也很有關係,像工學院都還滿鼓勵學生做實習,在博士的五六年間至少會做一次吧,但在理學院就不太流行,生醫方面也比較不喜歡學生做實習,這跟老師和科系都有關係。但的確在博士生的時候,最重要的工作還是把研究做好,這是一種長遠的投資。' + }, + { + title:'當初在那麼多教授中,是怎麼作選擇的?', + section:'我這個指導教授非常吸引我是因為他的背景非常多元,他是電機系、資工系、工管系的合聘教授,也有收過統計系的學生,專門研究數學模型在各方面的應用,我覺得跟他會學到很多很廣的東西。另外我印象滿深刻的是,當我還在台灣申請的時候,他曾打手機給我,讓我覺得他很沒架子,我們也很有緣分。只是到Stanford之後,也不是說想跟他就能跟他。他說那陣子大概有30位學生找他,但他只能收1~2個,不然我們先試做看看。這有點類似試用期,大約試用一學期左右,這是滿常見的模式。(問:30個只能收1~2個,那會不會有人一直換,最後找不到教授?)這也算是Stanford教我的第一件事情,就是他們很鼓勵這種開放的競爭,他們覺得如果你有本事就會找得到教授,到現在Stanford電機系還是維持這種模式。不過有些學校不會喜歡這種模式,而希望進去之前教授學生就配對好。(問:學長之前在台大電機系找的教授,也是對他們的研究領域比較有興趣?)對,我記得一方面我對他們研究領域有興趣,另一方面那時候陳宏銘教授特別幫我挑了一個數學比較重的題目讓我研究。電機系是一個非常大的領域,有偏應用物理的一端和偏應用數學的一端,那我興趣就是比較偏向應用數學的一端。' + } + ] + }, + { + bigtitle:'四、就業後', + sections: + [ + { + title:'轉換跑道', + section:'(問:實習生涯中,是否遇到一些挫折?從2007年在McKinsey,2009年去Yahoo,2010年再去Goldman Sachs實習,是否有些關於跑道轉換的心路歷程能夠分享?)其實這些公司對實習生都很好,所以我並沒有遇到什麼挫折。不過我在2009年實習的中間Yahoo決定要把搜尋外包給微軟,而2010年時Goldman被美國證交會指控詐欺。這兩件事都對士氣有不小的影響,也讓我學到很多經驗。至於轉換跑道,其實我覺得我的跑道一直都是同一條,也就是以數學和寫程式的技巧為基礎,建構統計模型的能力。這在不同的產業有不同的應用,但核心知識是差不多的。' + }, + { + title:'接觸到金融業的契機', + section:'其實在美國的理工博士中,從事計量金融的比例一直都不低。舉例來說,我在Stanford的那間實驗室裡,我是第三位加入我現在這家公司的博士畢業生,所以口耳相傳自然會接觸到。不過這幾年因為矽谷景氣很好,科技股高漲,所以進入金融業的人少了很多。(問:那些金融知識呢?畢竟是兩個不同專業領域,你原本是讀EE博士,後來才進金融業。)如果你是要走計量金融的話,大部份在招計量金融人才的公司,其實不太在意你有沒有金融背景,比較在乎的是你數理的背景有沒有很紮實,以及會不會寫程式。' + }, + { + title:'金融業 - 理工背景的優勢與劣勢', + section:'我所知道理工背景進入金融業的大概分兩種:一種是以他對科技的了解,轉化為對產業的投資判斷,例如在投資銀行研究哪家半導體公司的前景比較好。對於這種路線,理工人的優勢在於把技術的了解轉化為對產業的了解。第二種是做計量金融,也就是用他的理工背景,去建構各種數理模型來分析金融市場的走勢。對於這種路線,最重要的就是數學技術和寫程式的能力。但不管是哪一種路線,很多理工背景的人容易執著於細節,而見樹不見林。(問:那金融市場還是有很多人為操作因素,建構出來的數學模型會不會和它相左?)我想你的意思是說,不同領域有他所謂的domain knowledge,譬如說你在農業用的模型和在金融業用的模型絕對是不一樣的,這是合理的。所以進入這個領域後,不可避免的你還是要去了解這個產業、這個領域到底是在關注什麼樣的問題、他們有什麼樣的假設,這都是進去產業後要努力的部分。' + }, + { + title:'華爾街的印象', + section:'(問:有不少人對於華爾街的印象就是「黑」,例如電影《The Big Short》裡所描繪(若電影內容屬實):CDO本身就有問題,市場開始瓦解後CDO價值急速下降,銀行仍維持原本報價,想把它們賣出去,然後去另一間銀行做空他們剛賣出去的CDO(希望沒表達錯誤,btw我們很喜歡這部電影),請問學長對於這既定印象有什麼看法?)我想到之前高盛的CEO在接受訪問的時候說過一段話:「有人覺得我們高盛是開賭場的,我不同意,但如果你們硬要說我們是賭場,那我們也是一個對社會很重要的賭場。」這句話我自己的解讀是說,金融是現代經濟的潤滑劑,其中的投機性又是難以避免的必要之惡,我覺得是需要靠嚴格的監管機制來讓過程更公平透明,但沒有辦法讓它從現代經濟中消失。我之前還聽過一種有趣的理論,聲稱房貸債券促進了美國的地方自治。為什麼會這麼說呢?因為這種債券的發行,大幅增加了債權的流動性,降低了放貸銀行的風險,進而壓低了房貸利率,而使房屋自有率提高很多。而通常當一個人在一個地區擁有了房產後,會更關心這個地區的與衰,也更熱衷參與當地的公共事務。這是一種金融對社會的正面影響。我之前的博士指導老師,還跟我講過一種很特別的觀念來比較金融跟科技業:假設今天你想改善世界上缺水的問題,你可以發明一種新的水龍頭技術,使得全世界的每個水龍頭都省了一滴的水,加起來就省了幾十萬公噸的水,這是一種貢獻。或者,你可以選擇去建一個水庫,水庫建完可以多生產幾十萬公噸的水,這也是一種貢獻。前者屬於金融業的做法,後者屬於科技業的做法。金融業的做法比較著重效率,改善小小部分,但當它放大之後,其實對於社會的影響力也滿大的。這就是解決問題時兩種不同的想法和思維。' + }, + { + title:'計量金融的故事', + section:'因為我本身從事計量金融,就跟你們分享幾個我在計量金融所看到的故事。首先,這個領域充滿了科學家,有很多理工博士。我之前聽過一個有趣的故事, 2012年時歐洲有人宣稱他們找到比光速更快的粒子,後來雖然證明是實驗錯誤,但那時候就有人說,如果這個發現是真的,真的找到了比光速更快的粒子,那華爾街會人才大失血,因為會有非常多華爾街的物理博士離開計量金融,回去做物理學研究,因為一個新的物理時代開始了。所以你可以想像在這邊有很多人沒有忘記他們做為科學家的本心。在美國有一家很有名的計量型避險基金叫Renaissance Technologies,他們的創辦人James Simons投入了非常多的金錢做自閉症的研究,MIT的認知科學中心就大大受益於很他的捐獻。2012年時,它們公司還有一個員工在紐約成立了全國唯一一家數學博物館,推廣數學知識。另外還有一位有名的計量型避險基金創辦人叫David Shaw,他目前已經很少參與金融領域,而是專心在做分子生物學的研究。他希望以後所有分子生物學的研究都能以電腦模擬來完成,在這方面也投入非常多金錢。這些人為什麼要做這些研究?很明顯和金融業無關,但他們在功成名就後沒有忘記科學家的本心,希望能繼續推動人類知識前進。(James Simons本來是位數學教授,而David Shaw本來是位電腦教授)(問:所以華爾街其實有不少科學家?)計量金融的領域是這樣沒錯,但這是華爾街的小部分,華爾街最常見的人,還是商管背景的人。' + } + ] + + }, + { + bigtitle:'五、給學弟妹的建議', + sections: + [ + { + title:'給學弟妹進入金融業的建議', + section:'這取決於你想走我剛剛提到的那兩種路線中的哪一種。如果是第一種比較常見的市場研究路線,這種路線最重要的是把基礎的東西搞懂,至少要知道什麼是半導體,無線通訊在幹麻,如果把這些東西搞懂的話,以後在研究產業趨勢時會有紥實的技術底子。另外,我覺得如果有機會,可以接觸一些商學院課程,例如會計學或經濟學,學習別人對於商業判斷是抱什麼樣的觀點。如果是要走第二種路線,也就是計量金融這塊,就還是數學跟寫程式。在這個領域裡數學程度決定你可以走多遠,而寫程式的能力決定你可以走多快。程式語言部分我大學主要是用C跟C++,研究所就用很多Matlab,但其實我覺得如果你能把C++練熟的話要學其它語言都很容易。(問:那電磁學呢?)電磁學有很多物理跟數學,而計量金融業有非常多物理學家,他們很懂得如何描述世界運行的方式,而電磁學會培養你這方面的直覺。' + }, + { + title:'財金知識來源', + section:'(問:有推薦的財金知識補充來源嗎?如Bloomberg、Harvard Business Review等)我自己每天早上都會一邊吃早餐一邊看Bloomberg新聞台。除此之外我有時候也會看經濟學人和時代雜誌。前者比較全球也比較財經,後者比較美國中心也比較廣。另外如果對於科技創投有興趣的話,可以看一下TechCrunch。這些資源在中文的世界裡其實都有相對應的網站,所以對於剛入門的學生而言,看那些相對應的中文網站也不錯。' + }, + { + title:'金融業需要做的心理建設', + section:'工程師講究和諧文化,非常重視團隊合作,因為再厲害的工程師也無法離開團隊而做出好的成績。這跟金融業有些不一樣,金融業在個性上比較現實,很多零和思維,這是需要適應的文化。' + }, + { + title:'給大學部的建議', + section:'利用台大是綜合大學的優勢,多增廣見聞,多接觸外系知識。近年來很流行一個詞叫「T型人才」,指的是對於一個專業非常深入了解,同時也對很多領域有初步認識的人才。如果你具備這樣的條件會非常吃香。但電機系本業其實很重,所以要去接觸系外的話就要額外花一些時間。(問:那學長大學的時候有考慮雙主修嗎?)沒有,我那時候修滿多資工系的課,但沒有考慮雙主修。另外也有修很多其他的系的課,但都沒有雙主修,我當時覺得這樣修課比較有彈性。' + }, + { + title:'一句話,談談電機系給的歸屬感', + section:'這是我想最久的一題。我覺得台大電機系可能是目前台灣最有國際影響力的校系,而身為其中一份子,我不管在知識上或是事業上都得到很多前輩的幫助。我也是到美國之後才知道台大電機系在美國的人際網絡滿有影響力的。這當然是來自於很多前人的努力。我覺得自己很幸運,也希望能繼續努力維持這個聲譽。' + } + ] + } + + ], + + + annotation:['特別感謝:高奕豪','撰寫:蔡忠紘 林怡廷 陳鴻智 蔡承佑'], + id:'column_1605' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/16052.js b/backend/routes/srcs/in/column/preload/details/16052.js new file mode 100644 index 000000000..b246675cc --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/16052.js @@ -0,0 +1,79 @@ +module.exports = { + title: ['2014級 洪銘駿(RobotArt 國際機器人藝術大賽首獎)'], + hashtags: [ + '系友專訪', + '洪銘駿', + '2014級', + 'B99', + 'RobotArt', + 'Painting', + 'ArtandTechnology', + 'Robot', + '機器人繪圖', + '藝術', + '台大山服', + '系藍', + '臺大電機所控制組', + '不要想太多全心全力的投入', + '多嘗試抬起頭來看外面的世界', + ], + sections: [ + { + bigtitle: '', + sections: [ + { + title: '當初對電機的想法以及改變', + section: + '其實高中的時候也不知道大學科系是在幹嘛,也沒有認真去了解。然後那時候對理工蠻有興趣也看到一些很崇拜的學長往這個目標邁進。比如説有一個學長他是建中熱音社,打鼓的,成發完然後只用一年就上台大電機,就覺得這樣真的太猛了。因此也以台大電機當一個目標。\n在讀了之後,覺得最有興趣的是可以自己做一些東西這樣。理論上的東西可能就不太有興趣,比較喜歡實作。比如説有一些project,想一些點子然後把他實做出來。', + }, + { + title: '最深刻的一門課', + section: + '系上的話,比如説大三的資料結構,很痛苦。每天作業都要寫幾十個小時,期末project甚至不眠不休寫了七十幾個小時,但就是一個印象深刻的過程。反正都過去了,那時候大家都覺得很痛苦,但是現在就覺得反正都過去了。修完對你的程式一定是有幫助的。\n系外的就還蠻多的,我修過一門「電影藝術與當代議題」,就是你要看一部電影,然後去分析它的拍攝手法、光線、或是劇情、或是從各個面向去分析一部電影,那是期末作業,要寫七千字以上。因爲我也很喜歡看電影,所以你就可以挑一部你喜歡的電影然後好好的去研究這些,就要去想它鏡頭、場景的轉換,背後有什麽可能的想法,或是他這個光線怎麽打會有什麽意涵這樣。', + }, + { + title: '機器人繪圖及其商業應用', + section: + '機器人繪畫其實就是從我繪畫的經驗來的。我想要做出一個像人一樣的繪畫機器人。人在作畫的時候會用眼睛看,然後腦袋會去思考怎麽畫。可能第一次畫不會成功,但是你可能會去修正它。經過修正的這些動作,再把它畫出來。\n機器人看、感知的部分就是需要sensor,利用相機去偵測影像。然後機器人要用它的腦袋去想要怎麽畫,就是我們需要設計一套演算法。那這些演算法的概念大概就是從我自己繪畫的經驗去想的,比如説調色只需要五種顔色,就可以混合出大部分的顔色。這其實也是因為我在學畫畫的時候老師一開始只給我用基本的顔色,所以看到這個顔色你就要去想,它是怎麽組成的。\n然後如果比例不能一次做到位,就需要,一直去修正他、補償他。那機器人其實也是在做這件事情。對,所以這就是一種視覺的回授這樣子。\n我自己認定這是一個科技跟藝術結合的例子。現在有一些公司有在做一些科技跟藝術的展覽。以往這兩個領域是分得蠻開的,藝術純藝術,繪畫、雕刻那些的,然後科技就是科技產品,然而現在有一種趨勢就是把這兩個領域做整合,那我現在做的可能就是類似這種概念,可能是一種形式的娛樂吧。', + }, + { + title: '藝術學習歷程', + section: + '畫畫的學習就是一種感受吧,比如説顏色這件事情,老師不會跟你說這個是什麽加什麽,你就是要去感受,這可能有什麽顔色,然後去嘗試。那可能這個顔色可能這次調不對,那下次怎麽調,就是一個慢慢摸索的一個過程。剛開始學的時候,看的東西很少,一開始在學的時候,可能看到一張照片就想要把它畫得很細很像這樣。但是學到後來會看到更多,比如説我要表達什麽樣的感覺,不一定要畫得很像,可能會用一些筆法讓它變得有我要的感覺這樣子。那可能到後來會有一些取捨,就是重要不重要的,慢慢有自己的特色出來。\n我是很享受這個過程。如果你今天在學一個興趣,不要給自己太大壓力,例如說我畫畫一定要當畫家這樣。我是沒有這樣給自己壓力,畫畫的時候是很輕鬆的,因此在累的時候也不會說我要放棄。如果今天不想畫,那就不要畫啦!今天想畫我就去畫啊!就是一個我想要做什麽就做什麽,不用給自己壓力。\n其實我畫室的夥伴年紀都比我大,蠻多長輩的。我們畫室裡面有一個長輩就是八十歲了,是個很酷的人。他一生不斷地去嘗試各種的東西,還學國畫,也是書法老師。他畫的時候還是不斷的在嘗試新的東西,他已經八十幾歲了,然後可能身體也不是那麽好,手可能也不太能舉,所以他有時候畫一畫可能就休息一下,但他就是一生不斷地去嘗試,他説畫畫是一個建構再破壞的過程,比如説你在畫油畫,你把它畫上去就是在建構。那破壞是你又加了另外一層把它蓋過去或是什麽的,慢慢的去疊到最後的樣子。我自己覺得人生可能也就是這樣,去建構一些你的人格特質,並破壞一些你覺得不好的或是不重要的東西。然後慢慢的,這些過程一直重複之後就會變成你這個人。 ', + }, + { + title: '山服社團 ', + section: + '其實在參加社團之前大概就是一個電機系學生,看到的很小。機緣下才去參加山服。但我去參加服務性社團,也不是像一些很有服務熱忱的人,一開始就是要去幫助小朋友。我其實是因為服務學習缺學分,便跟朋友一起去,在山服體會到的影響我很大。都是一個機緣之下這樣。\n其實我們就是寒暑假辦夏令營跟冬令營,會有一些課程,要準備教案,比如説數學的教案,大家就可能就會想一些有趣的方法讓小朋友去學數學、英文或是學語文那類的。主要是啓發他們對學習的興趣,還有陪伴他們長大這樣子。\n其實我在研究所還是常常會回去幫忙啊!沒辦法整個營期全部參與,但有機會就會回去。也不一定要跟著社團回去,有時候跟朋友就是想要回去山上看那邊的人那我就回去。其實對我們來説那是另外一個很像家的地方。\n我覺得參加社團是讓我變成是一個完整的人一個很重要的過程。因爲我覺得一個完整的人不是只有你學到的知識那部分,是你懂得關心別人或是懂得去照顧其他需要幫助的人。這個是我覺得很重要的人格。比如説山上的小朋友,他們其實非常天真,很直覺、真誠地去對待人。有什麽想法就展現出來,那對我來説是蠻感動的。因爲我第一次去山上的時候很緊張,可是看到小朋友他們就直接很熱情的爬到你背上然後跟你玩。那時候我覺得非常感動這樣。', + }, + { + title: '關於興趣&人格特質', + section: + '比如説更懂得去關心身邊的人之類的。多去感受身邊的事情,這是一個我覺得很重要的一個特質,同理心。可能就是需要經過一些事情,你就漸漸覺得這個很重要。我覺得我很幸運有這方面的感覺、感知,而我會希望朝這個方向繼續努力這樣。\n這其實蠻困難的,但是你可以挑出你不喜歡的。這個領域我絕對不行,那就找一個不是這樣的。那其他部分的話,除非你很確定嘛,不然的話你不試你就不知道喜不喜歡啊。如果你覺得好像不排斥,就可以再繼續嘗試這樣。我自己也是覺得很難很快的就知道自己怎麽做才是對的。我自己是覺得不一定一定有一條最完整的,或是最完整的路或是最好的路。或是你其實你已經在完整的路上,可是你覺得你會不會有其他更好的選擇。因爲你沒有那麽多的時間,所以就是你嘗試的這條路你可以接受,那你就走。那如果你想換跑道那你就換。就是找一個相對正確的路這樣子。我的感覺啦。\n多去體驗不一樣的東西,多去嘗試。因爲不管你做哪一件事情你一定都會有一些收穫。那這些收穫拼凑起來就是變成一個完整的。你在做某一些事情的時候就是很認真的去做,然後很認真地去感受,你就會有收穫這樣子。多嘗試你就會有不一樣的可能。\n-就不要想太多,就全心全力的投入', + }, + { + title: '求學瓶頸', + section: + '瓶頸...倒還好,但一定有挫折。比如說,我有一個很好的麻吉,我們常常一起讀書,一起玩,但他就是天才。我們明明讀書的時間都差不多,喔沒有,他應該比我少,但他就是很強,這種人就超討厭的。然後我自己覺得我不是很聰明啦,相對那些天才而言啦,但就是換個心態啦!幹嘛跟他們比呢(笑)。每個人都有他比較擅長的部分啦。', + }, + { + title: '未來願景', + section: + '我想弄一些結合科技跟藝術的project。然後我也想把一些我覺得重要的價值觀,透過這樣的方式去呈現。譬如說,環境保育,覺得很重要,就想說怎麼把他放進去,還有教育,小孩教育的那部分,我能不能用這東西去給小朋友去學一些東西這樣。我有跟朋友在想一些project,想弄一個互動方式讓小朋友知道,種樹會讓地球變得更好之類的。那我可能就會透過一些,比如說,sensor讓他能夠有互動,然後有,種樹,地球會變得很美好,這個概念把它顯現出來。', + }, + { + title: '給大學部的建議', + section: + '多嘗試,抬起頭來看外面的世界,這很重要,看你周遭的人,不要只有看到自己的事。然後也不要太急,人生很長,你有時候太趕,我這個階段一定要做什麼事情,我下個階段一定要做什麼事情,太急或是太確定,也不是說太確定不好,就是給自己的範圍太小了,那你可能就會錯過一些體驗其他事情的機會,那我覺得很可惜。所以,我自己是覺得,人生很長,我之前修的那門課的老師有講,他說:『人生很長,給自己三五年去探索自己,或是摸索人生,是沒有關係的。』你不用一定找到一個最正確的路,但你用這個時間更了解自己的話,那你之後可能會走得更明確。\n另外如果你會想要做類似我在做的這件事情的話,一定就是你對藝術是有興趣的嘛,通常你自己有一些背景,比如説像我有一個同學他鋼琴彈得很强,他其實也想要做一些音樂跟科技結合的技術這樣子。學習上的建議,就是你盡可能把你自己的一些專業知識結合你現在學的東西,然後往這個方向去想去發展。', + }, + { + title: '一句話,談談電機系給的歸屬感', + section: + '在電機系真的是認識到一群很厲害的人,很有想法,可以從他們的一些觀點去看同一件事情的各個面向,認識這群朋友本身就是很大的收穫。我覺得電機系對我來說最有印象的歸屬感應該是系籃吧,大家能夠這樣一起走下來,感情變得非常好,很難得。', + }, + ], + }, + ], + annotation: ['特別感謝:洪銘駿'], + id: 'column_16052', +} diff --git a/backend/routes/srcs/in/column/preload/details/1606.js b/backend/routes/srcs/in/column/preload/details/1606.js new file mode 100644 index 000000000..c6eb0e12b --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1606.js @@ -0,0 +1,175 @@ +module.exports = { + title:['2007級 謝沛倫(Ambidio Inc. 共同創辦人)'], + hashtags:['系友專訪', '謝沛倫', '2007級', 'B92', 'Ambidio', 'AdventuresInSound', 'LosAngelesStartup', '聲音革命', 'Bloomberg媒體報導', 'Billboard', '讓筆電喇叭變百萬音響', '80年來最偉大的聲音革命', '李嘉誠與黑眼豆豆搶投資', 'HorizonsVentures', 'ColumbiaUniversity', 'ComputerGraphics', 'MusicTech', '大腦和耳朵重新連結的Ambidio時代'], + sections:[ + { + bigtitle:'一、Ambidio快問快答', + sections:[ + { + title:'Ambidio有何神奇?', + section:'使用者只要一指按下電腦、手機等播放鍵,便可享有具空間感的聲音效果。有效音場寬度是目前市面上其他技術的至少三倍以上。' + }, + { + title:'Ambidio的好處?', + section:'重視音效的人不必再買一堆設備,就可用最低成本,體驗沉浸式音效。' + }, + { + title:'Ambidio運用方式?', + section:'有兩種,一是直接將Ambidio技術直接嵌入影音或聲音檔案中,另一是透過程式即時將技術加入正在播放的聲音中。' + }, + { + title:'Ambidio運用管道?', + section:'可廣泛應用在任何有立體聲揚聲器或耳機的硬體設備,包括筆記型電腦、電視及部分手機和平板電腦等,已有美國電影採用。(資料來源:BUSINESSDREAM經營夢想, https://goo.gl/T32Zyt)' + } + ] + }, + { + bigtitle:'二、前言', + sections: + [ + { + title:' 前言', + section:' 「想像一下,打開電腦或是手機觀看影片或玩電動,就能感受到360度逼真的立體音效,彷彿賽車就從身旁呼嘯而過、恐龍就從眼前磅礡踩過的震撼聲音。」創辦於2014年,四名來自台灣的年輕人在洛杉磯,把心力傾注在聲音科技公司Ambidio上,得到七屆葛來美獎得主歌手will.i.am及李嘉誠先生旗下維港投資挹注支持。結合大腦感知模式和聲音科技,重塑大腦對聲音的解碼功能,創造出高質的聽覺體驗,透過一般立體聲揚聲器的平板電腦和手機,也能享受更勝環繞音效的聲音饗宴,如同身歷其境。Ambidio透過兩種方式改變對聲音的體驗:一種是直接將Ambidio技術直接嵌入影音或聲音檔案中,另一種是透過程式即時將技術加入正在播放的聲音中。一旦配備Ambidio技術,只需按下播放鍵,便可享擁奇幻兼具空間感的聲音效果。Ambidio可廣泛應用在任何有立體聲揚聲器的硬體設備,包括筆電、電視及部分手機和平板等。系學會很榮幸能夠聯絡上學長,讓我們來聽聽Ambidio Inc. 的Co-founder 謝沛倫學長為我們帶來的分享~' + } + ] + }, + { + bigtitle:'三、Ambidio 創業甘苦談', + sections: + [ + { + title:'您是如何與其他co-founders相識並有共同創業的idea?可以敘述一下這段歷史嗎?', + section:'Co-founders認識很久,在台灣就認識了。我們不是為了創業而想idea,而是有另外一個co-founder之前就在做這方面的研究,而且成果不錯,想讓更多人使用和體驗。我也有幫他debug和brainstorm,之後思考如何推廣並且問其他人的意見。每個人因為過去經驗不同,都有不同的想法,但這些人的意見的共同交集就是成立一個公司,再想辦法搞定專利,之後甚麼都好說。成立公司比較簡單,但專利就需要寫一堆繁瑣的文件。' + }, + { + title:'做研究與創業有什麼不同?', + section:'做研究比較focus在跟專業領域有關的事情,但成立公司就會需要處理很多雜事,我們每個月都在處理不同的事情,每件都是新的,只要發生事情就要趕快學,每週都有不同的情況。' + }, + { + title:'Ambidio跟別人做的環繞聲是差在哪裡呢?', + section:'我們的優點在於,有許多Artist在做創造聲音,他要作混音、幫電影配聲音,要花很多時間。我們能夠打破物理的限制,就像在自然的情況下聲音是從四面八方來的一樣。我們不會讓聲音失真,不管用耳機或喇叭都可以,但耳機本來就滿有環繞感。如果你看到兩個喇叭在前面,但發現聲音從不同方向來,感覺會很神奇。' + }, + { + title:'目前還沒有看到Ambidio出產品,只有看到demo,請問大約何時會出呢?你們未來的策略是?', + section:'目前ambidio會跟製作content的公司合作,像是做電影或音樂,所以我們做的東西通常不會直接deliver到個體客戶的手上,但我們客戶的產品你們會看到,只是目前還沒有,希望可以讓你們趕快看到。' + }, + { + title:'有沒有因為這樣的產業,認識音樂人?', + section:'認識黑眼豆豆的Will.I.Am,他是我們的投資人。在我們的投資顧問室skywalker sound,也就是做星際大戰的公司,可以看到很多人,像是侏儸紀的混音師、星際大戰的混音師,在交流的時候會認識到很多音樂的魔術師。他們或許大眾面前不有名,但在幕後混音界很厲害。' + }, + { + title:'創業至今遇到最大的挫折?', + section:'創業至今覺得會的東西太少,因為新開的公司會有很多方面的事,像是法律和會計,哪有可能知道那麼多事情,或是在專業領域上,其實也是學不完的,所以要趕快學然後趕快趕上。另外就是花太多時間在等待,就像是你等學校申請也要等待,我們創業要等專利等等。' + } + ] + }, + { + bigtitle:'四、大學時期', + sections: + [ + { + title:'談談電機系的歸屬感', + section:'我不太確定什麼是歸屬感,不過在國外留學會覺得很多人是從同一個地方來的,比方說從台灣、台大或是電機系,比較會相互照應。除了生活以外,在工作上也會因為做的領域比較接近,可以相互建議。可能台大電機出國的比例比其他學校或學系還高一點,所以在這邊會有比較多同學。不過在台灣應該也是這樣子吧,很容易跟大學同學hangout。' + }, + { + title:'當年最喜歡的教授', + section:'資工系的莊永裕老師。我上過他兩門課,分別是數位視覺效果與數位影像生成。那時候就啟發了我對computer graphics這個領域的興趣,想深入瞭解做特效、動畫背後的原理是什麼。電機系的話,應該是李琳山吧!我沒有上過他的信號系統,我上的是數位語音處理。他不是只有傳授這個學科的知識,他對學生怎麼做研究或做事的方法比較注重。我印象中他的課程設計就是以這個為出發點,並不只是把數位語音處理的不同component教給你。' + }, + { + title:'當年電機系系上的神人?', + section:'我也是想了一下。其實應該大家都是奇才啦,有點官方說法。不過我想到的是會一邊打太極拳一邊踢足球的同學。' + }, + { + title:'大學時期特別的成就或經歷', + section:'呃...沒有成就(笑)我沒參加系學會,沒拿書卷獎也沒社團。我大部分時間都在打系籃啦!對未來的影響就是到現在還是只會交籃球場上認識的人當朋友而已,沒有什麼正面的影響。' + }, + { + title:'系上 / 系外對你最有幫助的一門課', + section:'系外就是剛剛說的數位視覺效果,系上是線性代數。我覺得工程數學對之後的很多進階學科都很有幫助。後來在學其他的東西的時候都會發現:哦!原來是這樣!可是真的要用的時候才發現沒學好然後又要複習。我的教授應該是馮蟻剛。也是要看你之後做什麼啦!如果你要做電波的話,那可能就是微方比較重要。' + }, + { + title:'關於專題選擇', + section:'我跟過兩位老師。一位是鄭士康,當時我做電腦音樂相關的專題。因為我之前彈吉他所以就研究效果器的原理,再用code寫出來。另外一位是簡韶逸,那時候是做image based rendering。比如說你在動畫裡面看到的東西都是你有一個model然後貼一些texture上去然後把他render出來。那image based就是他可以拍很多很多的照片,然後用一些數學進行處理。比如說某一個視角的時候是用某一張圖的某些東西。可能跟幾何比較有關吧!但是也是多少有點關係啦。不過我的專題就是把當時已存在的方法實做出來。' + } + ] + }, + { + bigtitle:'五、美國念研究所時期', + sections: + [ + { + title:' 留學讀研究所', + section:'我覺得最大的差別大概就是在美國唸研究所比較多就是在美國工作,多數人的目的一開始也是想在美國工作才來念研究所。我們那屆出國的比例、我猜大概有三四十個人吧,沒有非常多,可是到今年都還有人出國,就是工作了一下,但還想再念一點書,就再申請出國唸書,所以不一定要一畢業就出國。' + }, + { + title:'出國前準備', + section:'(從台大電機畢業到去哥倫比亞大學中間隔了三年,請問除了當兵跟準備出國考試資料等等,還有在做什麼嗎?)我當時去中研院做研究,中研院那時好像是快要出國之前的人的臨時棲息地,因為像剛剛說大學專題就是實作一些已存在的東西,好像沒有一些厲害的paper,所以想說去中研院做一下研究,除了要增加publication之外,也學習做研究。其實我也不太確定幫助有多少,因為那時候我原本要念computer graphics,但我大學念電機系,關聯不大,所以我申請國外的CS沒上而EE有上。所以不敢說那段時間對申請多有幫助,不過對自己的學習是很有幫助的。' + }, + { + title:'中研院的研究內容', + section:'我那時候去中研院是去資訊所、做跟電腦圖學相關的東西。那時候是跟廖弘源老師做偏視覺的研究,他做比較多視覺多媒體方面的research。(所以學長還是心繫CS嗎?)對,最後博班還是去念cs了(笑)因為在columbia念EE master的時候,他們很鼓勵去修一些其他所的課。畢業標準是修30學分,我那時只修了10學分電機所的課,其他都跑去修資工所的。Columbia畢業標準不需要一些發表,其實滿多學校不用的,我自己認為美國滿多學校對master的定位只是修一些比較進階的課,並不需要有論文。台灣就比較不一樣,有點像phd之前的預備,而美國就比較像就業導向,沒有很注重論文。' + }, + { + title:'在美國的RA經驗', + section:'我有待過兩個實驗室,比較有趣的是我有做一個physical simulation,就是電腦動畫模擬衣服。那個時候他們有一個現成的系統,可以real time design衣服,像是可以改肩線等等,改2d的design時可以即時轉換成3d的圖show出來。以前的作法可能就是真的找一個假人偶,然後把布料釘上去。那樣子很麻煩,有個模擬軟體就比較好。(聽到現在感覺學長的經歷都跟聲音方面的研究沒什麼相關?比較多影像耶)剛剛那個鄭士康老師的專題,他其實就是跟聲音相關的,而且我在columbia也有修些跟聲音相關的課,不過沒有這方面的研究就是了。' + }, + { + title:'Gramercy Surgery Center 工作經驗', + section:'(看到學長有在Gramercy Surgery Center當軟體工程師。Gramercy Surgery Center 是個醫療急救單位,那軟體工程師的任務為何?)它就是手術中心,那裡跟台灣的醫療系統不太一樣。在美國,醫生判斷你要做手術,那個醫生可能會在那個醫院幫你做,或是病人自己去外面的手術中心找外科醫生幫你做。那這種手術中心可以提供器材、護理人員等等。因為有各種不同的醫生來request他要在什麼時間做什麼手術,可是他們現成的管理系統很不方便,比較偏像會議管理那種,我就幫他們重寫一個系統安排手術時間。(那學長在美國是看到有一個機會,就去做,還是說這些研究助理,幫手術中心寫系統都是計劃好的?)當然不是,如果有計畫就不會做這些了(笑)。這些是剛好有認識的人介紹,然後就去了。' + }, + { + title:'Disney Research - Capture & Effect Internship', + section:'(學長有在disney research 的Capture and effect部門實習過,請問大概是在做什麼?)Disney下面很多不同公司,有做特效、電影或disney world的等等,而disney research就是做很多公司需要的東西,所以做的東西很雜。我加入的是capture and effect,他們有一個系統可以拍很多照,然後reconstruct被拍的東西的3D model,接下來就是我就不能講了。' + } + ] + }, + { + bigtitle:'六、創業時期與未來展望', + sections: + [ + { + title:'電機系學生 v.s. 創業', + section:'(電機系的學生需要什麼特質,才適合創業?何時開始?又,怎樣的個性不適合創業?)要喜歡自己做的事,因為會把所有時間都花在上面。(平常如何分工呢?)沒有明確的分工模式,我們有辦公室,但我們沒有住在一起,還有一個人在台灣。(假設有學弟妹很早就決定要創業,怎麼學習創業?)我自己是沒有準備過創業。我是在過程中學習,但是我也不能說你不需要準備,現在不是有什麼創業學程嗎?不過我也沒有上過這個學程,所以我也沒辦法說什麼。' + }, + { + title:'未來聲音科技的突破?', + section:'其實現在很多聲音的公司,趨勢都是希望可以用更方便的方式讓客戶體驗更immersive的東西,可能還要考量到大家都在mobile上面使用。對我們來說就是希望可以成為下一個大家體驗聲音的方式。我們認為單聲道(mono)發明了很久,過了二三十年之後變成stereo,照理說接下來大家會想要環繞聲(surround sound),然而卻沒有很普及。這個東西並不是每個人都會一直在使用,但我們希望能夠把體驗聲音的方式從stereo變成ambidio。' + }, + { + title:'未來規劃', + section:'希望是可以繼續做自己喜歡的事,並沒有說幾年之後在變什麼,幾年之後要變什麼。Ambidio短期的計畫就是要讓大家都聽到。(像是現在這樣每天都聽ambidio的聲音出去外面聽會不會覺得怪怪的?)沒有啊,目標就是要讓聽的人回到原始聲音該有的方式,所以如果我的目標就是要讓你自然的聽聲音,那不就和你平常走在馬路上聽聲音一樣嗎?' + }, + { + title:'在美國和台灣軟體工程師的差別', + section:'薪水是不一樣,但花費也不一樣。最主要還是看自己喜歡怎麼樣,並不是說你一定要去美國。最大的不同應該還是美國講英文吧。總之還是要做自己喜歡的事情,像是喜不喜歡在美國生活也很重要。(那學長你認為你會一直待在美國嗎)短期之內應該會吧。' + }, + { + title:'多媒體產業', + section:'學弟妹想走這行的話,有需要特別增進哪方面的知識嗎?就是,假設想要做類似ambidio這種事情的話,除了顧本科之外,還要做什麼?)這很難講出來,我自己覺得你吸收的知識越廣越好,然後你可以很快的在不同的主題之間移動。(那應該問說,要怎麼吸收這些知識?就是在求學,大學的階段的時候,可能是有什麼雜誌嗎?網站,還是要看論文?)如果是要找新的研究,那就是看最新的論文,例如一個陌生的領域,你會去找這個領域大家最新都在做什麼。當然你很可能看不懂,看不懂的時候就會去找它引用的東西,慢慢一個一個往前找,其實李琳山老師教的課大概就是這個概念。這些知識就是一個金字塔,新的東西都在上面,那你可能會需要很多很多基礎知識才可以達到最上面的東西,但是你沒有辦法去準備把所有基礎知識都弄好。當然基礎知識是學越多越好,不過學最新的科技的方式,就是你要會去找最新的東西,然後慢慢的往回,找出一條路。' + }, + { + title:'如果再讀一次大學?', + section:'我如果再讀一次大學,老實說我就不會念電機系,因為如果我知道之後的路,我就不會去念。不過我覺得還是要增加基礎能力,像我剛才講的,你需要有足夠的基礎能力,你會比較容易pick-up之後的東西,因為現在的科技變化很快,但是像數學物理或是邏輯等等是不會改變的,可能就是要更廣泛接觸吧。(所以你想要做computer graphics是大學之後才確定的嗎?)是的。(有考慮過轉去資工系嗎?)可是我那時候已經大三了,來不及了,只好研究所再轉。' + } + ] + }, + { + bigtitle:' 七、結語', + sections: + [ + { + title:'往後5-20年有哪些產業是值得學弟妹投入?', + section:'我覺得這個有點難預測,如果你現在去看二十年前的預測就知道了,所以我也不想要二十年後回頭來看,才發現當初自己亂講話。' + }, + { + title:'一句話談談電機系給您的感覺', + section:'我覺得同學都非常厲害,所以你可以從同學中學到很多。聽起來很勢利,但未來對你的生涯都有幫助。' + } + ] + }, + ], + + + annotation:['特別感謝:謝沛倫','撰寫:鍾興寰 Paulsu Su 陳威成 孫凡耕'], + id:'column_1606' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1805.js b/backend/routes/srcs/in/column/preload/details/1805.js new file mode 100644 index 000000000..091b36933 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1805.js @@ -0,0 +1,161 @@ +module.exports = { + title:['2014級 黃柏源(Princeton EE PhD)'], + hashtags:['系友專訪', '黃柏源', '2014級', 'B99', 'Princeton', 'EE', 'PhD', 'EDA', 'TSMC', 'Intel', 'IEEE_paper_reviewer'], + sections:[ + { + bigtitle:'一、前言', + sections:[ + { + title:'簡短自介', + section:'我沒有在台灣讀碩士,大學畢業再當兵,然後就出國念博士。美國有些有些學校是分program,可能有些電機系底下會分你是做CV的,你是做電路的。學生的畢業條件會不一樣,修的課會不一樣,但出來的證明上是長一樣的。我們princeton就沒有分program,不過還是有非正式的分學生是做電路的,這些是做information theory的或是物理device的等等...,不過就沒有像台灣明顯的分出一個研究所裡面有某一個組。' + } + ] + }, + { + bigtitle:'二、求學時期', + sections: + [ + { + title:'聽說你拿了好幾次書卷獎,請問你的學習方法?感覺系上很多拿書卷獎的同學也都有在培養興趣而不只是在念書,學長有沒有什麼興趣呢?', + section:'我個人是覺得效率比較重要,花在上面的時間倒是其次。我工作或讀書一陣子之後如果有點想要耍廢,卻一直強迫自己應該要認真,其實我也會一直分心,然後效率會變很低。所以我就會徹底的去做我想做的事情,等到心情回復之後,我可以再進入認真的模式用高效率做該做的事。一來你做事都會是高效率的,二來你耍廢就會產生罪惡感,然後你就會更認真做事。不過我覺得沒有適合每一個人的方法啦,你大學要花時間尋找適合自己的方法。至於興趣的話,我大學都在打網球還有社團,陽光風箏關懷服務社,不過社團比較是因為朋友的關係,倒不是因為什麼興趣。' + }, + { + title:'您是如何接觸EDA這個領域?又為什麼會想要深入研究?怎樣的學生也適合這個領域呢?', + section:'最一開始接觸eda應該是修張耀文教授的演算法,因為他本身就是專攻eda的,所以他常常會在上課的時候講到一些他做的內容然後閒聊到一些事情。但真正接觸到更多是因為大四跟江介宏老師做專題,也有修他的課。我是大三上修演算法,然後大三下修江介宏的eda導論,這大概就是接觸eda的開始。至於為什麼會想深入研究,我大三的時候專題不是做eda,是做通訊的,但是做了一年後個人覺得沒有興趣。同時,我在修了那些eda相關的課之後,我就蠻喜歡邏輯這一塊領域,然後就打算跟江介宏老師做做看。其實EDA蠻廣的,EDA就是整個電機領域的輔助角色。電機可以從做device到很上層做system的,每一塊都有一個相對應的EDA的領域在裡面,EDA跟其他領域的差別是它在研究一個方法。那如果你喜歡當一個輔助的角色的話,你可能蠻適合做EDA的。這比較不像是你做出一個東西,然後是商業上的使用者會用到的,不是設計一個CPU然後...噢!電腦裡面的CPU是我做的。EDA比較像是弄一個tool,想一個方法,讓那些真正在design產品的人更方便地去design。有些人會希望他做的東西可以真正被應用出來,然後可以被看到實體,看到他在社會上有很明顯的影響。那我覺得這種人就不適合做eda,因為你永遠看不到你的東西被一般人(工程師除外)用到。但你如果比較喜歡默默地覺得自己對這個社會有貢獻,那我覺得你還蠻適合的。' + }, + { + title:'獲得精專首獎的專題內容?有什麼困難?又該如何解決?從做專題學到什麼?做專題應該要抱持什麼樣的態度和觀念?', + section:'那就是我跟江介宏老師做的專題,做的是非同步電路的合成,也就是你給一些描述,希望這個system有什麼表現,它能夠有什麼樣的功能,我就幫你自動合成出一個非同步的電路,然後符合你要的這些性質。至於困難,其實這個困難你在做research都會遇到,就是當你開始一個新的題目,你一定是不懂。那你就要去學很多東西,每當你越學就會發現你有越多不會的,越覺得自己弱。那解決方法也是只能靜下心來去把他們一個一個學會。我其實在這個專題學到蠻多東西,像是不要得失心太重。我看到蠻多人會有的一個困擾,可能是我同學、朋友或現在這邊lab的人的困擾。他們每個禮拜要跟老闆meeting或是record之類的,他們就會覺得自己每個禮拜都要有一些進度,一定要做出什麼東西來。這個禮拜跟老闆meeting完,下一個禮拜就要跟他說我解決了。但要是真的每個禮拜都能有很多的進度,還有問題每個禮拜都能解決的話,我覺得這很有可能是不怎麼有趣的研究。因為你在做research就代表這個東西是沒有人會,沒有人想過,沒有人做過的,你其實應該是正在拓展人類智慧的極限。那很多人,尤其是第一次做專題或research的時候,他們都會預期跟以前一樣只要花時間去學就可以學會,但專題我們遇到的通常是新的研究題目,沒有人會。我覺得很重要的是你的心態,當你做不出來,那是很正常的事情,你不要從此就覺得說自己不是做這個領域的料,是不是沒有這個資質去做研究。這些就是我跟江介宏老師做專題學到的東西,他帶領學生的時候,會跟你講這些事情,在做事情心態上面的影響我覺得還不錯。尤其是當你一個大學生第一次做專題,你甚至不要覺得自己只是一個研究助理,你就是在自己做研究。專題就是嘗試在做研究,看看做研究是什麼樣子,然後找什麼是你真正有興趣的,因為專題跟上課最大的差別是:一個是寫在書裡面的,一個是沒有人知道怎麼做的。然後你可以自己選要跟哪個老師,哪個領域的,比方說我大三是跟魏宏宇老師做通訊,就是因為這個專題我才知道我對這個領域沒有興趣,那不是我想要做的事情。其實我們那個時候,通訊是一個很熱門的一個領域。很多人就會覺得,通訊看起來是不錯的領域,那我也走通訊好了。如果沒有專題的話,我很可能大四畢業後就去找一個通訊研究所或是去做通訊的工作,那其實就可能不是最適合我的路。我覺得做專題給我最大的好處就是他讓我知道我對這個東西是真的有興趣,而且給我一個做一學期沒興趣就放棄,有興趣就繼續的機會。如果真的要跟學弟妹說什麼的話,我覺得真的可以去多多找不同老師做專題,然後不要想說一定要在這個專題裡面做出什麼很屌的東西,因為最重要的是你找到你最想要的是什麼。做出來當然很棒,沒有也沒關係,這就是研究嘛!' + }, + { + title:'聽說您大四當過IEEE 通訊 paper reviewer,這個工作在做什麼?有什麼樣的收穫?', + section:'Paper reviewer 其實就是幫別人submit的一份paper打分數,然後要寫一些 critical summary,就是說這篇paper有什麼好處有什麼缺點,contribution是什麼,它是literature review(文獻評論)還是真的有想出什麼很屌的東西,還是他只是一個實驗。通常一篇paper會被五到六個reviewer去審核,conference或是journal的主編就會根據reviewer寫的評論跟分數去決定能不能被accept。那reviewer的工作其實就是去讀一篇paper,但不是一般的讀,可能要讀到五六次,讀到很深。因為要真的了解他在做什麼,真的知道他的contribution是什麼,優缺點是什麼。至於收穫就是會很了解別人的work,因為一般讀paper不會讀到這麼深,可能只知道他做了什麼,怎麼做,但不會去想很多。那reviewer其實不會太難當,你只要發過paper而且被accept,它就會覺得你就是這個community裡面的人,你有在參與到這個領域的研究,就有機會被找到當reviewer。不過它會看你之前做的主題和你要review的主題有沒有相關,有的話它就會覺得你有資格當reviewer。(ps. 那如果評出來有懸殊差距,會去掉最高最低嗎?)當然會去最高最低啊~不過最後決定的還是editor。' + }, + { + title:'系上對您影響最大的教授是誰?從他身上學到什麼?', + section:'影響最大當然就是江介宏老師,學到一些做研究的態度吧(如上上題)。影響倒是還有另一方面吧,就其實我現在的老師跟江介宏老師是師出同門,我的師公其實是我們這個領域的一個大頭。所以人脈這點不只是學長姐對你會有很大的幫助,你的老師對你也會有很大的幫助。如果你要出去讀書,老師的人脈也很重要,他能夠影響到你找到好的老闆。' + }, + { + title:'有沒有上過讓你很受用的系外非電機專業科目?', + section:'我覺得台大還蠻多好課,像高等微積分就是很棒的一門課,他可能不是很直接地對你有幫助,但是他可以訓練你的思考能力。如果不是這一方面的話,我覺得應該就是呂世浩教授的中國古代歷史與人物吧。如果別人問台大我最推什麼非電機系的課,我應該會是說這兩個。' + }, + { + title:'大學最後悔的事情?又有過什麼樣的徬徨嗎?', + section:'沒有吧。有可惜的事情啦但是沒有後悔的,像中國古代歷史與人物,很可惜的是我都只能去旁聽因為我選不上,我去要加簽單都加不上。但沒什麼好後悔的,我也沒辦法。' + } + ] + }, + { + bigtitle:'三、畢業 &申請留學', + sections: + [ + { + title:'學長當兵的時候會覺得大學學的東西快忘掉嗎?', + section:'會啊,就跟當白痴一樣,這是絕對會的(如果不是當替代役,因為替代役可以讀書)。那如果是當兵或是像我當預官的話,退伍後就是個白癡,這是真的。我退伍後來到美國,前面真的很辛苦。一年沒有動腦,除了變笨之外英文也變很差。一來這裡就要上課,就要用英文跟老闆討論,跟別人討論,去想一個用中文可能都想不出來的事情。這件事真的很痛苦。因為上課的時候聽到的不是你平常聽到的那種英文,都是印度腔或是其他你沒有練過的腔調,真的很辛苦。我那時花了至少三個月才開始覺得能夠掌控我的步調、我的生活。前面真的非常的混亂,一切都是在覺得自己跟不上的那種感覺。' + }, + { + title:'申請國外學校有沒有遇到困難?出國唸書需要什麼條件(成績要多好、經濟負擔)?為何選擇Princeton?', + section:'我覺得依我現在看到的一些例子,其實非常靠運氣。當然成績或是經歷很重要沒有錯,但運氣也是很重要的一件事,所以有些人申請的時候會覺得他申請書的強度可能不夠申請到某一所學校,那到底要不要去試。那我覺得真的就去試,因為我看過很多很多條件很好的學生但有些學校他們沒有上。相反地,有些人條件沒有那麼好,但是他們還是上了。然後絕對絕對不要想說那個申請費很貴就不申請,因為他一點都不貴,你出來唸書才真的貴,不要為了省那一點錢然後少掉一個學校。如果那一所學校真的是你想要上的,他的老師,他的課程安排之類是你想要的,你就一定要試,因為你就只有這次機會了,你不試一定沒機會啦!成績這方面,我必須要說美國這邊學校在看,美國本土學生會比台灣學生有優勢,但我想我們也沒辦法做什麼。你能做的就是盡可能把自己寫得很好,因為他們有不同的算法。你可以說你在你的主科方面平均多少,你在你三四年的時候平均多少。另外,你可以把A-, A+全部放A,B-, B+全部放B,搞不好算起來會比別人好一點。這個是完全可以做到的事情,而且大家都在這樣做,你不做就是吃虧。就是一些小技巧,不要覺得這是什麼不好的事情。然後在找教授的時候,我覺得亞洲人往往會覺得教授很高不可攀,要使用非常尊敬的語氣,把自己講的很低一樣。但其實在美國這邊,教授跟研究生的地位是平等的,你平常或是寫信的時候都是直接叫對方名字。然後你們在做研究的時候,他們都是講work with,他是跟你一起研究,他不是教你也不是以一個上對下的概念。那很多人在申請的時候會覺得先去跟某些老師聯絡,然後說我看過教授幾篇paper然後很想要進這個學校,進教授的lab去做事。這是好的,但是不要把自己寫的太低,好像什麼都不會,要從頭努力學。我有時候幫別人看他們的sop或他們申請的東西,依我在這邊的經驗,會建議有自信一點比較有利。' + }, + ] + }, + { + bigtitle:'四、工作經驗', + sections: + [ + { + title:' 學長有在台積電做過intern,但台積電是跟半導體相關,而學長的領域不在這,和台積電有什麼關係?和之前做的專題有相關嗎?工作內容?', + section:'台積電是在做半導體沒錯,但是EDA最大的客戶其中一部分就是半導體廠,台大做EDA的老師至少有一半都是跟台積電合作,我那時候不是在半導體的部門,是去幫他們寫tool,現在的design已經不是人力可以完成的,勢必需要更多的工具,像是分析…等,我那時主要的工作就是,有一些pattern台積電做不出來,那我就幫他們寫工具,分析為什麼他們做不出來這樣,到底是哪些原因導致製程做不出來。(ps. 所以學長也要了解製程相關的?)稍微了解就好,我目前做的就完全不需要,台大的話,張耀文老師他們都要對製程有些許了解,那如果跟江介宏老師,就不太需要。台積電做半導體沒錯,但是他們是很需要懂EDA的人。(ps. 所以台積電有EDA的部門?)有EDA的部門,不過人數當然比不上其他部門,竹科也有很多EDA的公司 。(ps. 台積電這麼國際化的公司,公司氣氛是否有不一樣?)大公司基本上就是制度化,氣氛主要還是看帶你的group的老闆,我覺得跟公司大不大沒什麼關係。' + }, + { + title:'學長也有在intel當過intern,那是怎麼找到的?又是在做些什麼東西?我知道intel也是半導體相關的公司,那可以分享一下台積電跟intel的差別?', + section:'我進intel是因為我現在的lab和intel有一個合作計畫,每年暑假會叫人去intern,intel也是在做製程沒錯,不過他們現在有很大一塊在做IOT,我在那邊也不是做跟製程相關,我是在security team,在IOT裡面,他們有很多security的問題,你要怎麼確保給客戶的東西是安全的,不會用一用資料流出去,我做的主要是hardware security verification的部分。台積電跟intel的差別我個人覺得沒什麼差。(ps. 如果不說工作內容,而是公司文化呢?)公司策略應該有,台積電畢竟是亞洲公司,策略上會比較保守,intel錢很多,project可以投很多錢進去,沒機會,就直接砍掉,在這種high level上比較活,公司制度的話我覺得差不多,都是大公司,制度都訂得很死。' + }, + { + title:'學長實習經驗豐富,感覺印象中,書卷獎的同學很大一部分最後是走學術,不知道學長是要學術還是在業界工作?', + section:'我兩個都不太想,我個人想在業界的research lab,是不排斥走學術界,但是我會希望先找業界的research lab。(ps. 是像Bell lab?)恩,還有很多像是microsoft的msr,這些單位不是幫公司做產品,他有點像學界做研究,但是他們處理的問題不是學界的,是更貼近現實的業界問題,他們沒有做這東西一定要賺錢的壓力,也沒有時間的壓力,這些部門就是很研究性質,但是可以處理很現實的問題。' + } + ] + }, + { + bigtitle:'五、台灣 v.s. 美國', + sections: + [ + { + title:'請問在美國讀博士和在台灣讀有什麼不同,根據你自己的觀察和經歷,或是和在台灣的同學們相比?', + section:'在美國讀博士就是比較孤單,其他方面其實都差不多。因為你的朋友,家人們都在台灣,這是生活層面乃至於心情的影響。但是在環境,制度,學校方面的話,不會差太多。但是其中一個很重要的是,在美國讀博士的話,你跟你的老闆是平起平坐的,你們是以一個合作的關係在做研究的,除了最後的defense,他要決定你能不能畢業之外,並沒有上下關係。' + }, + { + title:'那生活上有什麼比較難適應的地方嗎?', + section:'東西很難吃,然後很冷。當然生活上很多細節都不一樣,像是台灣不會開暖氣,或是一開暖氣就很乾,乾到你必須去買一個加濕機。當然平常你晚上餓了你可以出去吃宵夜,但這邊沒有這種東西,不會一出去就便利商店,或超市,你一定要開車去幾十公里以外的大賣場買東西。生活中就是差蠻多的拉,但是這些都還算小,我覺得影響最大的還是人與人之間。因為你的朋友都不在,你那些過去二十幾年的東西都留在台灣了,不過現在比以前好很多的是你還有網路(不過還有時差問題)。那這些是我覺得你來美國讀博士必須要克服的一堂課,你要怎麼去適應一個人在一個你完全不熟悉的地方,還要同時去兼顧做研究。另外一點的是,你作研究會常常需要去conference的話,美國的好處就是很方便,就是國內線飛一下就到了,這真的是在美國讀書的好處。Connection也是,因為很可能同個領域的其他人很多也是在美國其他學校,因此有比較多的機會去建立人脈。' + }, + { + title:'根據您的個人網站上的資料,您似乎有在Princeton當助教或任課老師等等,請問根據此經驗和在台灣求學的經驗,你覺得美國教育和台灣教育的優劣之處?尤其是大學教育。', + section:'單論學生程度,如果以台大電機跟這邊的電機系比,我覺得台大電機比較好,但如果不管程度的話,我覺得不會有優劣之分,因為很多人說台灣的教育就是老師講,學生聽,學生不太會有意見,不太會問問題,這是事實麻。那在美國當然是非常主動問問題,也非常善用助教這個資源,如果在台灣,助教通常都是改作業,要分數。那在美國就是我有問題就可以問助教,如果我有任何不懂,或是任何我覺得需要,有office hour我就去,沒有我就寄信,那我就是去助教那邊,把我要問的問題把他問完,我不會的東西你必須一次把我教會。這我個人覺得有好有壞,好當然是他們很懂的去利用這些資源,可能可以很快的解決一個問題,但另外一方面我覺得某種程度上自己解決問題的能力會比較弱。就像你上課老師講一講,他如果沒有馬上catch到,他就會問問題,那在台灣,我可能在多想個十秒,就知道這不是個問題,或是想通了,但他們可能會少了那十秒思考,是好是壞我不知道。會讓上課的進度變得沒有那麼有效率,因為他們動不動就打斷,然後問一個其實沒有這麼有意義的問題,而且我真的常會覺得他們會因此減少自己解決問題的能力。' + }, + { + title:'是否曾經在研究的路上遇到困境,例如找不到研究目標、研究進度停滯不前、或失去研究的熱情等等? 如果有,又是如何克服的?', + section:'還是會拉。我個人覺得找研究目標這件事情,你如果去實習是一件很好的一個解決方法。像我現在自己的問題,或是我想要作到的實情,就是由實習開始的。我看到很多別人的paper,就是看到別人有興趣的、熱門的topic,但去業界裡面的實習,可以看到現在真的遇到是什麼樣的問題,跟真正的engineer討論時,你會知道什麼樣東西是有用的,是人們想要的。所以我覺得如果你真的找不到研究目標,跳脫學術界,去業界是很好的作法。進度停滯不前,我覺得是很常見的事,有時候你就去休息一下,去換一下心情,回來搞不好你就會有想法。失去研究熱情也有過,但是我自己的話,像我只要每次去參加conference,都會覺得充滿研究的熱情。像我剛才講到的優點,在美國讀博士,你會參加很多conference,假設在台灣,那些機票錢,住宿錢是很大的問題,老闆通常只會補助你有上paper,要去報告的conference,如果沒有,想要去的話就得自己想辦法,但是在美國這邊,即使你沒有要報paper,你還是有辦法找到錢的來源讓你去參加conference,所以你就去參加、去聽,但沒有報告。那我覺得這是還蠻棒的一件事,因為如果你有更多機會去參加conference,你會覺得你更involve在這個community裡面,然後你可以看到很多人現在他們有什麼突破,遇到了什麼問題,你會覺得很多人是跟你在這個領域奮鬥,那相對如果你每一天都在自己的地方做研究,又停滯不前,你也不知道外面發生什麼事情,就很容易失去那個動力。我自己如果參加conference,回來之後我會覺得,我會想要繼續在這個地方努力這種感覺。' + } + ] + }, + { + bigtitle:'六、未來規劃', + sections: + [ + { + title:'從Princeton畢業之後,您未來的規劃?要回國或是待美國?就業或是研究?', + section:'在Research Lab 待個5到10年然後回台灣,如果可以的話想要回台大,能夠影響些甚麼!' + }, + { + title:' 讀電機到現在設定的終極夢想?', + section:'還沒有想那麼多。' + }, + { + title:'EDA適不適合台灣發展,未來會遇到甚麼瓶頸?', + section:'台灣一直有在發展EDA,因為有台積電和其他IC design的公司。過去的EDA比較偏向硬體,近年CS越來越熱門,EDA會偏向軟體的automation,新型態的EDA不只硬體,還有軟體,至少在目前而言,台灣的軟體環境不像美國那麼好,如果要走新型態的EDA,可能會比較吃虧。如同先前說的,design automation是support的工具,需要被support的產業在哪,發展就在哪,但說實在環境也不好預測。' + } + ] + }, + { + bigtitle:'七、給學弟妹想要進入這個行業的建議', + sections: + [ + { + title:'對於擔任Princeton的Lab Instructor,學弟妹若要當助教可以注意甚麼?', + section:'我自己當助教注重的點是:不是想要教會他甚麼,而是讓他覺得有趣,好的助教會影響學生對於功課的興趣,當助教不只拿錢辦事,要想像自己當學生時會希望得到甚麼協助。' + }, + { + title:'申請實習,要如何選擇公司或部門,事前如何準備?', + section:'過去當實習時是直接被指派部門,沒有這方面的經驗。' + }, + { + title:'對於電機系畢業直接簽博,要有甚麼樣的特質或決心?', + section:'需要確認對研究是有興趣的,因為每個人不一樣,舉例來說,如果是對自己做的東西有明確的目標,喜歡實踐的成就感,可能就比較不適合讀博班,研究就是不知道未來長甚麼樣子,需要喜歡探索的樂趣。如果不喜歡研究或是沒有方向,不知道想做甚麼,就不適合讀博班。可以從專題探索,雖然專題處理的問題比較容易,但這是比較接近研究的東西。' + }, + { + title:'談談電機系的歸屬感', + section:'像我現在在國外,感受到的幫助蠻多的。一個人出來,其實沒有太多資源。一來學校的人,很多資訊都是要找高年級的學長姐。找高年級的台灣學長姐,其實就是找高年級的台大電機學長姐,因為我們學校台灣同學會大概有三分之二是台大電機的。不管是生活上、學業上還是要找工作,學長姐的幫助很大。你剛進來要找老師,但你不知道那麼多資訊,你的人脈還是要透過學長姐。那如果你要找實習,甚至是找工作也都是如此。在美國的公司,投履歷沒有太大機會,找工作很需要依靠裡面的人內部推薦給他們的hr。這時候裡面有人脈就有很大的差別,裡面的學長姐寫封信就可以讓你有很大的機會可以做到你想做的工作。我們Princeton這邊有幾個是交大來的,他們就常常會說在美國沒有太多的人脈,所以在很多方面他們就少些幫助。' + } + ] + }, + { + bigtitle:'八、結尾', + sections: + [ + { + title:'給大學部的建議(如果再重來一次,你會怎麼做?)', + section:'兩個部分,一是好好玩,大學認識的朋友是人生重要的夥伴,不管是出國或進公司,影響都會很大;二是在這四年內,找到自己想做的事情。' + }, + { + title:'「一句話」談談電機系給您的歸屬感?', + section:'難以言喻。' + } + ] + } + ], + + + annotation:['特別感謝:黃柏源','撰寫:鍾興寰 陳威成 孫凡耕 蘇柏元'], + id:'column_1805' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1806.js b/backend/routes/srcs/in/column/preload/details/1806.js new file mode 100644 index 000000000..318d0a0dc --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1806.js @@ -0,0 +1,125 @@ +module.exports = { + title: ['2000級 陳俊仰(Grindr President & CTO)'], + hashtags: [ + '系友專訪', + '陳俊仰', + '2000級', + 'B85', + 'Caltech', + 'Facebook', + 'Instagram', + 'Grindr', + 'President', + 'CTO', + ], + sections: [ + { + bigtitle: '一、前言', + sections: [ + { + title: '', + section: + '陳俊仰學長在從台大電信所(通訊組)畢業後,在 Caltech 唸了五年 PhD,研究領域是通訊和雷達的信號處理。從 Caltech 畢業後進入 Facebook,目前在世界最大的同志交友平台 Grindr 擔任總裁兼技術長。學長經營了一個部落格,紀錄了唸 PhD 時的生活點滴,以及進入職場後的觀察。 ', + }, + ], + }, + { + bigtitle: '二、Life at Facebook', + sections: [ + { + title: ' 契機:「誤打誤撞」進入軟體業第一志願', + section: + '2009 年博士班畢業時,美國處於全球金融危機的浪潮中,以外國人的身份在當地找工作格外困難。那時候同樣在 Caltech 有四位台灣同學,後來三位回去台灣(其中兩位目前從事教職)。當時我的目標很明確──留在美國工作,所以去應徵了很多家公司。我的專長是通訊和雷達的信號處理,最初找工作時也想進入相關的產業,但是通訊方面(例如:無線通訊 IC 廠)職缺不多,新鮮人的工作機會更少,而雷達方面雖較不受景氣影響,但是國防相關的工作外國人無法從事。了解就業市場中愈偏應用層工作機會愈多,軟體又比硬體多的事實後,我下定決心,花兩個月學習演算法和資料結構等知識,也解一些 puzzles 練習寫些演算法類的程式最後有收到錄取通知的包括:Google、Facebook、Microsoft 三家。選擇 Facebook 其實單純就是底薪比較多,當時還看不懂股權,後來才知道那是最有價值的,單純就只看了底薪就做了這個決定,幸好沒有選錯!我還不知道 Facebook 當時在美國已經是軟體工作的第一志願。', + }, + { + title: '從 Data Infrastructure 到 People Search,Instagram 到 AI Team', + section: + '我在 Facebook 的經驗非常的多樣性,從最底層的大數據基礎一直到針對用戶的產品和用戶增長我都接觸過。剛加入 Facebook 時的第一個組是 Data Infrastructure Team,這組管的是大數據的存儲和計算,我參予了數據可靠度和分散式計算的開發。當時 Hadoop 有一個主節點負責分發計算任務,在 Facebook 的超大規模之下,一個主節點要管理三、四千台計算節點,當時這是最大的 Hadoop 機群了,這使得主節點因負荷不了而形成瓶頸。於是我們在上面多分一層管理資源,使得計算分派可以用多個節點實現,開發這個專案讓我寫了很多程式,深度了解了分散式系統,很有成就感。之後我轉組到 Search 團隊,因為我想要做一個接近用戶的產品,這時候我也開始從技術慢慢轉型成管理職,最後我成了 People Search 團隊的經理,在這段期間我學到搜索系統的實現:從怎麼用數據建立索引,一直到怎麼用機器學習優化排序,也學到怎麼開發一個針對用戶的產品,我還開發了給Search 用的 AB 測試平台,我在這個團隊的時候還帶領團隊來過台灣做用戶研究,看台灣人怎麼用Facebook找人。後來在我管的 Search 團隊中有個人轉組去剛剛收購的 Instagram,從他得知 Instagram 正在招一名廣告工程的經理,我一直很想學習廣告,也就是 Facebook 這家公司的核心業務,加上 Instagram 正在初始階段,我就從 Facebook Search 離開,加入了 Instagram。在 Instagram 期間人事變動非常快速,我上半年管的是廣告工程團隊,下半年改成管理用戶增長團隊。用戶增長的知識對後來加入其他公司非常有幫助。最後在 Facebook 待的團隊是 Facebook Core Machine Learning Team。這跟另外一個 Facebook AI Research Team, FAIR (led by Yann LeCun)最大的差別是前者主要做應用,後者以發表學術論文為目標。在這個團隊,我才真正用上 PhD 習得的知識,每天都會讀幾篇機器學習的論文,不停的學習。這團隊在 Facebook 中是非常關鍵的,負責開發廣告和動態消息的機器學習演算法,常常一個小改變就可以幫公司帶來上億美金的收入增加,非常受到重視,當時我的座位就在 Sheryl Sandberg 隔壁桌,可見團隊的重要性,團隊之中有很多學問和實作能力都非常強的人,讓我學到很多。', + }, + { + title: '「換組」:看遍公司主要業務,提升溝通基礎', + section: + '在 Facebook 換組很正常,好處是可以了解各項主要業務,多元的經驗讓與不同組同事溝通的過程更加順利。早年 Facebook 規模比較小,組織較扁平,現在規模變大後分工也變得比較細,以前一個人要做的事,現在有十個人一起做。隨著公司大規模成長,要在不同部門間轉換,熟悉多種業務的機會也變少了。當初在 Facebook 不同組學到的事情,對我後來的工作也很有幫助。現在工作上不論是用戶增長、廣告、做實驗、用戶研究、甚至是大數據或是運維,我都能和團隊溝通和討論,靠的就是當初在 Facebook 的歷練。', + }, + ], + }, + { + bigtitle: '三、在CS組的經驗分享', + sections: [ + { + title: 'Why CS?', + section: + '在過去的世代,最大的企業是生產油公司和生產車的公司,而這個世代市值最高的前幾家公司都是軟體服務公司。原因是網路在各方面大幅度增加了人們的效率,人們現今可以在網路上上快速且大量地交換資訊。網路也讓各類交易成本大幅下降,讓人類的生活變得更有效率。我估計未來幾年對人類生活大幅影響的還是軟體公司,媒體和娛樂、電子商務、交通和住宿,這一波革命還會持續一段時間。', + }, + { + title: '軟體業求職建議', + section: + '台灣的學校環境很奇妙,科系要招多少人,不是由市場決定的,所以經常與產業發展近況脫節;很多教授沒有業界經驗,只重視發表學術論文。學生要藉由實習或出國,才會看到業界較多的面向。不過美國產學脫節的情形也跟台灣差不多,尤其 PhD,我自己做研究的時候,我感覺學術界和產業界是非常不相關的(至少在我在的通訊領域是這樣)。Facebook 很重視實習生,會讓他們解決 bug、做實驗、最後他們的成果會直接到用戶手中。很多實習生都以去我那組為第一志願(Core Machine Learning Team)。但是我覺得進 Facebook 應該把握機會去看其他的業務,像是用戶增長或廣告。雖然這些工作用到的技術層面沒有那麼高,但是能學習到一家公司的商業邏輯和核心的產品,我覺得比學習機器學習更難得。', + }, + ], + }, + { + bigtitle: '四、給學弟妹的建議', + sections: [ + { + title: ' 在FB 工作的人有很多人是 PhD 嗎?大概有多少人是研究所或大學畢業?', + section: + '每個組不一樣,像 ML、search team 就很多 PhD,但我第一個待的 data infrastructure 組大約三成,主要看那個組的數學與技術難度。用戶推廣、廣告組這類較倚賴生活經驗的組別,PhD 就比較少。至於要不要唸 PhD,首先我們需要了解 PhD 不是要解決問題,是要做出新的東西,但那些東西不一定有用。當年博班畢業時我也有考慮過教職,但總覺得相較於工程師做的通常是應用價值較高的東西,博士以降的研究產出對其他人的直接影響往往較少,所以需要更大的熱忱,不是每個人都適合。', + }, + { + title: '學長的 PhD 跟找到的工作內容沒什麼相關,會有什麼影響嗎?', + section: + 'PhD 主要影響職涯的一開始跟後段。「一開始」是起薪會比較高,但是想想跟碩士一畢業就在工作的人相比,博士學位可能還是贏不過五年的工作經驗。「後段」則是比較有機會當上主管,或是晉升更高的職位(如: Director),因為你的資歷比較好看。PhD 的訓練讓你講出來的話都是有根據,就像寫 paper 時每一句話都要有 reference,不能瞎扯。我其實不後悔讀 PhD,那段時間其實滿開心的,但對我來說經濟上沒有太大的好處。', + }, + { + title: '研究所選擇走通信的動機 ', + section: + '高中很喜歡物理,大學時選了系上半導體、物理相關的課,也有修物理系的量子物理。但是後來修到半導體工程時,覺得有點無聊繁瑣,決定換一個領域試試。當時覺得通信的數學很漂亮,Fourier Transform、Z Transform、線性代數的邏輯性充滿美感。研究所畢業後本來也沒打算出國,但我的指導教授馮世邁說出國對未來的發展比較好,所以後來還是出國了。申請 PhD 時雖然我的成績不算最頂尖,但因為研究做得頗有成果(碩士發了五篇 paper),就成功去了 CalTech。', + }, + { + title: '如果研究所在台灣唸,但想去美國找工作,難度會不會很高?', + section: + '有先例,但門檻相對高很多。公司要在不一定有足夠資訊了解你這個人的情形下,幫你拿到 H1B 簽證。台大在台灣可能很有名,但美國的公司不一定會覺得這是一個很好的保證(相較於美國本地的大學畢業生)。也有些案例在大學或碩士到美國的 FB 實習,後來轉正職,這或許是台灣學生可以走的一條路。另外也可以多參與 open source 專案。之前有位中國的 PhD 做 Hadoop Hive,貢獻在專案中高居前三名,我們就直接把他招募進來。由此可見在 open source 專案有清楚可見的產出,也是獲得聘用的一條路。', + }, + { + title: '準備 FB 面試所需的演算法跟程式', + section: + '那時去做 FB 的 puzzle 題目去練 (現在收掉了),而且之前在用 ASP 寫計中寫借教室平台,也有去接 php 的案子,在面試 FB 的時候面試官有關注到,算是無心插柳。', + }, + { + title: '有關 open source', + section: + '假如有看到一個看好的 open source project,可以去嘗試在上面解 bug、issue,這個跟公司面試的那種小演算法題目差很多,倒是跟公司裡叫你做的事比較像一點,通常都是幾十萬行的 code,要去解一些奇怪的問題或是實現一個新的 feature。通常公司裡的那些東西都是幾個月的時間就把大架構刻出來,接下來幾年的時間就在裡面 iterate。所以通常在裡面只有 5% 的機會把一個東西從零開始寫起來,其他都是在大架構下面改小東西。也是有很會做這種事的人,但不會寫像 ACM 這種智力測驗跟從零開始寫一些小架構跟邏輯和一些精妙的演算法,這種事情跟寫演算法題目用的腦是不太一樣的。', + }, + { + title: '現在各國都在往 CS 領域發展,這樣會有人才飽和的現象嗎,還是這個領域其實也在成長?', + section: + '這個領域其實也一直在變大,當然也需要更多人。但是也有可能供給會跑過需求,像前幾年 FB bootcamp 出來的人都說要做 Big Data,現在是都要做 Machine Learning。但我覺得不是大問題,因為 Software 的東西很容易轉。我覺得對你們沒有什麼關係,因為你們很聰明,很容易站到一個不錯的位子,CS目前是很有經濟價值,也還會再推動一陣子。', + }, + { + title: 'FB 發 paper 對公司有什麼好處,是隱藏利益,還是純粹一起研究進步?', + section: + '有些東西很難學,但也有些是會先申請專利再發 paper,而且 Pull Request 數會對公司的對外形象有影響 (美國會統計每個公司的這種創新成分等等),對某些人而言可能是衡量要不要加入這家公司的關鍵。', + }, + { + title: 'FB 招募 Master, PhD 的標準有不一樣嗎?', + section: + '有可能會問 PhD 的研究,但主要標準可能差不多,大部分應該是看你會不會寫 code,因為找人進來就是要做事,能不能持續產出。出 paper 的部門就不太一樣,有些人招之前就知道他們是誰,所以已經要他們了,面試內容就不太一樣,但如果是大學畢業的話應該也是會問 coding,跟對 Machine Learning 的理解之類的。', + }, + { + title: '“人的身段要軟,不要怕學習新的東西,才是成功之道 ”', + section: + '人能一直轉方向滿重要的,即使已經讀到 PhD 那麼多的,因為現在東西變太快了,很多都是紅一下就消失了,所以你沒辦法死守一個東西,就做一輩子,很難找到一個東西一直具有經濟價值,可以一直做下去。', + }, + { + title: '給大學部的學生什麼建議?尤其是對電機系的東西沒有太大興趣,GPA也不是很漂亮的人?', + section: + '對 CS 有興趣的話,可以去做 open source, 想辦法找實習,或做 ACM 競賽題,把那些學紮實一點。對大學部的話,其實我覺得應該多玩一點,專題也就找比較有興趣的來做,我以前就是很認真在圖書館讀書,現在想想那些時間如果拿去玩也不會怎麼樣。人的機遇都是很 non-linear,陰錯陽差就走到某些地方,所以如果可以比較 diverse,就比較有機會在某些地方開花。我以前修過很多專題,大概做過八個,有李琳山的,貝蘇章的,鍾孝文的,賴飛羆的,就可以多方嘗試,有聯誼之類的或好玩的事情就去做,盡量豐富一點。', + }, + ], + }, + ], + + annotation: ['特別感謝:陳俊仰', '撰寫: 楊程皓 吳奕萱 毛弘仁'], + id: 'column_1806', +} diff --git a/backend/routes/srcs/in/column/preload/details/1807.js b/backend/routes/srcs/in/column/preload/details/1807.js new file mode 100644 index 000000000..c71645278 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1807.js @@ -0,0 +1,95 @@ +module.exports = { + title:['2006級 楊奕軒 (研究員/教授 @ 中研院資創中心)'], + hashtags:['系友專訪', '楊奕軒', '2006級', 'B91', '台大電信所博士', '中央研究院', '資訊科技創新研究中心', '研究員', '教授', '音樂資訊', '音樂生成', '音樂情緒', '音源分離'], + sections:[ + { + bigtitle:'一、生涯規劃', + sections:[ + { + title:'在學時什麼時候開始決定投入音樂資訊這個領域的?為何會想要投入?', + section:'我剛進入電機系時,覺得自己來到最強的地方,大家都很厲害,發現很多人高中就會寫程式,普物、微積分也整本都讀完了。那時就拼命一股腦地努力追上去,尤其是大一那時,特別在乎成績,想把每一個科目都兼顧好。但是後來漸漸地發現我對電子學比較沒興趣,剛好在大二升大三的暑假,申請到系上的獎學金,去美國密西根州立大學 (Michgan State University)做兩個月資工領域的交換學生,在那邊進行一些研究之後,理解到IC產業並非電機系出路的唯一解。原本曾因為不熟悉程式,而有些下意識地排斥寫程式,結果在這次難得的機緣下開始感到興趣,也在大三上開始選修許多資工系的課,並前後加入李琳山老師、陳宏銘老師的實驗室參與專題研究,並在後者一直做到博班畢業。其實我國高中時是學美術的,不是學音樂的,從小沒有機會去學音樂,會做音樂對我來說完全是個意外。剛好大四上時陳宏銘老師實驗室剛好有了個與音樂情緒相關的新研究題目,做到後來蠻有心得的,就一直做音樂的題目了。我在想,大家如果要做多媒體相關的研究,第一個想到的應該是電腦視覺 (computer vision),因為應用廣、出路也多。做影像的話,別人比較不會覺得你在做冷門的題目。但是我是想法很多的人,就覺得說大家都在做影像,就不想跟著做一樣的。雖說如此,我在博二、博三也有猶豫過,也有想過要不要做一些影像相關的,就也發了一些影像的論文。後來出國開會,遇到一位國外音樂資訊的資深教授,跟他聊了一下這個問題,那時他說做有興趣的東西就好了,不用一定要跟風做比較多人做的研究領域。' + }, + { + title:'為何會踏入學界並選擇在中研院當教授?', + section:'剛上大學的時候,覺得讀電機系好像就應該去業界賺錢。記得有次系上有個「 與教授有約」的活動,有位教授問底下二三十位參加的同學說「誰之後想當教授?」結果好像只有一兩個人舉手。那時我心裡還覺得很奇怪,怎麼會有人想當教授。但後來做研究越來越有興趣,才發現學術之路也許比想像中更適合自己。之所以選擇在中研院,主要是因為我很喜歡中研院的氣氛。雖然以外界的眼光來看,中研院研究員的名氣不比大學教授響亮,但在中研院工作可以幫助很多想出國的人。而且在我實驗室的學生(或是研究助理)大多是對音樂有所興趣、且想讓自己變得更強才加入的,並不是以拿到文憑為目的。我不需要費力地去推他們、或是勉強他們做他們不喜歡做的事,而可以讓他們以自己的興趣和熱誠來做研究。我也不覺得我自己是個老闆,而是跟大家一起努力的夥伴。我覺得這種感覺很棒。另外在學術界工作主要的好處就是自主權,可以自己決定自己想做什麼。研究跟生活也有適當的平衡。' + }, + { + title:'對大學部學生的修課、以及領域選擇建議?', + section:'老實說因為每個都有自己不同的考量,很難說甚麼課比較適合誰,但是建議大家多修課摸索自己的興趣,而且避免修修營養學分浪費時間。雖然因為剛從高中出來仍保有競爭意識,覺得自己和別人還在賽跑,會在意其他人在幹嘛、修了甚麼課,這樣的想法都很難免。但是,過幾年後回過頭來看,會發現這其實沒甚麼意義的,只要選擇自己所愛就好。你的選擇還會受制於家庭背景、價值觀。以家庭背景而言,有些人爸媽是教授、有些人爸媽是在業界工作,很自然會耳濡目染類似的價值觀。有些人希望追求經濟自由、有些人希望擁有自己工作的自主權、有些人則希望工作與生活能有適當的平衡,真的會差滿多的。但是,也沒必要受到原生家庭的影響,而是可以在大學期間多接觸人,多接觸不同的想法,並讓自己有機會去嘗試看看。只是一直念書的話真的太可惜了。其實大學期間最重要的是知道自己要甚麼,就算發現有路走錯了、好像落後同學了,也沒關係,大不了再走回來,都來得及。我很喜歡我以前一位交大大學部畢業的研究助理。他大學時間花了很多時間在玩音樂跟賽車,導致GPA並不是很好看。但是他在我實驗室擔任研究助理的那一年半的時間裡,他發現音樂研究與他的興趣很相符,因此他做起研究來比許多人花上更多時間,後來的表現甚至比很多臺大畢業來我實驗室的學弟妹還好。而且,由於他不是那種讀書競爭的個性,他也比一般人懂得去照顧實驗室後進的學弟妹。現在他在美國名校念碩士,並得到在知名企業實習一年的機會,我覺得除了努力之外,這也需要歸功於他知道、或是願意去了解自己想要甚麼。在中研院,有些人會來這邊一兩年轉換跑道 (例如由IC產業離職),之後再重新去國外念碩士。我們的人生不是線性的,你不用一直想說要跟別人比,意義並不大。重點是有沒有給自己機會,去找到自己喜歡做的事情。一旦找到之後,再去做其實都還來得及。雖說如此,我覺得我好像也不是很知道自己想要什麼,就是碰到什麼就去把它做好,這或許也是一種類型的人吧。' + }, + { + title:'回到大學畢業當時如果可以出國會想出國嗎?', + section:'我會覺得應該要出國阿,離開熟悉的環境去到一個新的地方,總是能讓自己更加地成長。臺灣也需要更多的人走出去,把新的東西帶回來。我自己當初是因為女朋友(現在的老婆)的關係,選擇留在臺大並直攻博士。我並不後悔這樣的決定,因為留在臺灣讓我有更多的時間陪自己的家人。但是客觀來說,能夠有機會出國總是件好事。我當時如果單身的話,應該也會出國。出國不代表你就不回來了,我是希望有出國的人將來能考慮回來,幫助我們臺灣。像我自己沒有特別喜歡去美國,因為我每次去歐美一些國家,會覺得生活環境很好,他們的人民也都過得很好的生活,可是那就不是我的國家,他們憑什麼過那麼好(笑)。一個國家要強,首重教育跟技術創新。如果最強的人都想留在美國生活,那美國當然是越來越強。' + } + ] + }, + { + bigtitle:'二、音樂資訊領域詳談', + sections: + [ + { + title:' 教授做的音樂是純音樂還是有歌詞的音樂呢?', + section:'我們主要是做有歌詞的音樂為主。過去比較多做音樂當中聲音信號的分析,近期比較多做跟樂譜有關的「音樂生成」 (music generation)的研究。做音樂研究的好處是可以聽很多音樂,而且建出來的模型 (model)的結果是可以聽的。過去做音樂情緒辨識的時候,就會同時考慮聲音和歌詞。近來在做音樂生成,也很有興趣能同時產生詞曲,或是給詞填曲、給曲填詞。(問:音樂情緒辨識是指電腦聽了之後可以判斷音樂是屬於憂傷的還是快樂的嗎?)對,那時候就是做這樣的題目。' + }, + { + title:'用機器生成音樂有沒有可能超越人類呢?對這件事的看法?', + section:'現在大家在講AI,會分強AI、弱AI,那我們現在在做的這個東西可能比較像是弱AI,它只是一個工具、或說是一個玩具。你們對機器學習 (machine learnig)稍微有了解的話就可以理解,我們只是給它聽很多很多的音樂,然後讓它去學其中輸入(input)與輸出(output)之間的規則,並沒辦法讓電腦像人腦那樣思考。以音樂生成來說,大家會覺得這是個聽起來滿AI的題目。但我們現在在做的東西是這樣:以前音樂人在作曲的時候,他是打開一個作曲軟體的介面,然後就寫,慢慢寫慢慢作曲。那我們是希望他寫了一些些之後,我們可以給他自動產生一些feedback。譬如說他寫了主旋律,我們就推薦這個音樂人伴奏、和弦要怎麼配;如果他寫了前面幾個小節,我們幫他接下去。可是在幫他接的時候,可能會提供他很多種各式各樣的建議,不是說就只有一種答案而已。因為我們是用機器學習去做的,所以能給音樂人很多種建議,然後他自己再去挑,等於我們只是輔助他去做這個事情。能夠達成這樣的輔助,是因為機器已經在資料裡面聽過很多音樂,所以電腦可以去學樂器跟樂器之間的和聲、樂器跟樂器間的關係、前面跟後面音樂是怎麼發展的一些關係,所以它有辦法去模仿、學到人類作曲時後的一些規則,然後去創作。可是以目前的技術而言,電腦就只是這樣子而已,它沒有辦法做到所謂的強AI。我們在說的AI是指要讓電腦像人類一樣,我們人透過五官去接受一些信號、做一些處理,然後再做一些判斷。人類會有我們的意志、情感、個性等等,這些東西在我剛剛講的AI作曲裡面都還沒有,它比較像是一個工具,就是你給它一些輸入,它就吐給你一些輸出。它並沒有意志,沒有辦法知道這個作曲家在做這個音樂的時候,他在想些什麼,或是他是為了達成什麼什麼目的。像是我們人活在世上其實是有很多動機的,比如說生存的動機;或是social的動機,譬如說我們想要受歡迎、想要被愛、或想要被尊敬;或者是認知上面的動機,譬如說我想要追求成就感、我想要獲得知識,可是這些東西電腦都沒有。電腦其實就只是你怎麼program它,它就怎麼去做事情。所以,以工具而言,電腦會比人類強,就像AlphaGo一樣。一部分原因是因為電腦不會累,而且記性極佳,可以較容易地去從數據資料裡面去找出一些軌跡。可是如果是音樂的話,涉及到一些創意,或是要有「溫度」,就是這個音樂是你感覺得到那個作曲家在想些什麼。但是,我們資料裡面並沒有這些標註,資料就只是原譜而已,並不知道作曲家當初在想些什麼。或是說,這些東西很難讓電腦去學得到。所以我的意思是,以目前音樂作曲的研究來講還很難做到,它只是個工具,它不是真的是個AI。它能不能幫助作曲家呢?有可能可以。可是能不能取代?我覺得應該還很難。目前看到許多用AI輔助做出來的專輯,其實人的參與還是占了大部分。(問: 如果做過音樂情緒辨識,那就算是強AI了嗎?)那也不是,音樂情緒辨識一樣就只是判斷什麼輸入要給什麼輸出而已。我們的訓練資料裡面會告訴你說這首歌是什麼情緒,那首歌是什麼情緒,所以電腦就去抓這些音檔裡面的一些什麼特徵會跟目標的輸出有連結。所以你給它一首新的歌,它就可以估測情緒。它其實只是在抓那個pattern而已,它自己本身並沒有意志。' + }, + { + title:'做音樂生成是如何做evaluation的呢?', + section:'(註:我們現在大部分人類的問題都是有答案的,所以可以很清楚的知道一個generation的結果是好還是不好。但問題是,在音樂創作的那種部分,沒辦法直接比分數。大家現在的研究都很喜歡比performance、比數字。那如果現在我們要做這種研究時,除了做問卷、或是讓使用者現場來體驗,不然也沒辦法告訴其他人這有多好,那要怎麼辦?)音樂創作的話一定要讓人來聽才可以。可是人來聽的話很花時間,所以我們會設計一些量化的、可以算出來的指標。比如說,我們可以從真實音樂裡面去算一些統計量,例如什麼音後面容易接什麼音…等等,這種統計量,然後再去看產生出來的音樂的統計分布跟那個像不像。又或者,我們可以去計算生成的音樂是否有重複性,再去看這樣的重複性是否跟真實音樂的重複性的等級接近:一方面不同小節的音樂應該要有些重複、才會有連貫性;但另一方面有不能完全一樣,否則會很無聊。所以重複性不是越高或越低越好,而是要跟真實音樂的分布接近。透過這些量化的指標,我們可以觀察模型訓練的狀況,然後把一些不好的過濾掉。但這些指標無法反映人在真實聽得時候的感覺,因此最後還是必須給人聽,才能評估好壞。我們實驗室目前有跟KKBOX合作,與他們的的音樂人和製作人一起討論,以改進我們的模型。以音樂作曲來講,它成功與否應該是看開發出來的作曲程式是否真的幫助作曲人作出會暢銷的音樂,不過是減輕他們的工作、或是幫忙提供靈感,可能都是貢獻。如果我們的目標定得更明確,比如說你想要創作出新的音樂,因為目前我們的創作音樂都是從既有音樂學的,那就有可能跳脫不出那個框框。如果是這麼明確的一個評量標準的話,有一種方法是,譬如說可以先把既有音樂分成好幾個類別,然後去訓練一個分類器,分類器在一首音樂輸入時,可以去分類它是屬於哪一個流派、哪一個人的風格。如果我們創作出來的音樂,會使得這個分類器完全無法分辨,譬如說分類器覺得預先設定的十個類別都equally likely,那就代表它可能是一個新的類別,或是介於中間。諸如此類針對novelty的評估也是有人在研究。另外我們有在做「聲源分離」( source separation),把音樂裡面的人聲跟非人聲的部分分開,它也有一些算準確率的方法,但是那一樣不能反映人的聽覺。所以模型訓練好了之後,我們會用我們的耳朵去聽。有時候數字上會看到有差別、聽起來卻沒差;或是數字上沒差、聽起來卻有差。所以我們的研究就是要常常要親自去聽。而且機器學習會有訓練資料(training data)跟測試資料(test data)。像我們的data如果是訓練在英文歌的,我們就會故意把它測在中文歌、日文歌上面,去看它是否真的所有的歌都表現很好。這些都是屬於做研究的一些眉角,你們以後應該就會慢慢學到。指導教授或學長姊會跟你們講怎麼去設計實驗、怎麼去評估、怎麼去改進研究。' + }, + { + title:'教授於deep learning 就像煉金術有甚麼看法?', + section:'(註:可能修改一個參數就會讓model無法train起來?)機器學習其實分理論派和應用派,我們是做比較應用方面的。做理論的人會去改進deep learning裡面的東西,他們就不是把 deep learning 當煉金術在看吧。然後我覺得講deep learning 像煉金術的人,可能多少本來就對 deep learning 有些反感吧。我覺得真的要會用 deep learning ,你必須了解它背後的原理是甚麼,調參數很重要,但那只是門檻而已。就是「會用」跟你「能夠設計出新方法」也是存在著一定的gap的。我覺得需要能夠知道那些類神經網路架構的特性是甚麼,例如當你加一些不同的設計進去的時候(像是 skip connection 或是 residual),你有辦法知道為甚麼會成功、為什麼會失敗,並且跟你跑出來的結果去做連結。也就是說,必須要去懂它背後的原理,那些東西也是知識,所以我並沒有排斥 deep learning,也不覺得他只是煉金術。' + } + ] + }, + { + bigtitle:'三、做研究的經驗與建議', + sections: + [ + { + title:'教授曾經和國外許多之知名的教授和學者一起做研究,這些經歷帶給教授哪些收穫?', + section:'透過這些經歷,我會期待自己用國際級的標準來做研究,而不是當國內王。會想要讓其他國家的學者,聽到你的名字就知道你在做什麼,甚至想到一個研究題目就會想到你的名字。有這樣的目標,自然而然會去把研究做好。如果有機會參與一些大型計畫就參與,沒有也沒關係。不需要特別去追求些什麼曝光度、或是名利。把自己有興趣的事情做好,盡量花足夠的時間跟實驗室的人討論他們的研究,幫助他們變得更好。' + }, + { + title:'教授的研究成果非常的多產及豐富,我們好奇教授您有經歷過低潮期嗎?', + section:'其實還好耶。我覺得一來是抗壓性、二來是正面思考。人生要做很多選擇,我會確認我做的選擇是我喜歡的,但如果後來覺得過程或是成果沒那麼好,我會找些理由去安慰自己說「嗯,這個東西應該還是有些價值在裡面,其實也沒那麼差」,就是去看正面的部分。以研究來說,正面結果和負面結果有時候是一樣有趣的,研究論文也不能只是報喜不報憂。' + }, + { + title:'教授是怎麼找到研究的靈感?', + section:'研究通常是越做會越有心得。剛開始比較不熟的時候就是靠多跟老師或是學長姐討論,千萬不要悶著頭自己蠻幹。學會與別人討論、與別人分享是很重要的,需要建立表達的能力。慢慢有一些自信心之後,就會越來越有想法想試。如果你對某個題目真的有興趣,就可以多找論文來看。我發現我實驗室裡面,研究做得特別好的,他們會主動去抓論文來看,而且常常看得比我還快。也就是說,研究做得好的人通常也看很多論文,有時候靈感就是從那邊來的。我自己當初在做音樂情緒的研究的時候,很多靈感也是從心理學那邊的論文來的,所以也不一定是只讀自己研究領域的論文。' + }, + { + title:'教授有曾經考慮過音樂資訊以外的領域嗎?', + section:'這前面有稍微提過了,我曾經有為了自己畢業之後可否順利找到工作擔心過。因為音樂的研究確實比較小眾。來了中研院之後,也會覺得把我們這些如果機器學習的知識應用在其他領域 (例如綠能、或是氣候變遷),或是會有更大的影響力。但一方面這樣想,另一方面又覺得音樂研究很有趣,彷彿有種使命感,就覺得應該繼續做下去,所以目前還沒有想要換領域。另外,也可能由於我是一位基督徒的關係,並不會特別想汲汲營營追求什麼。我珍賞心中這種平安的感覺。' + }, + { + title:'教授認為研究對您做重要的是什麼?', + section:'應該是成就感、以及好奇心被滿足的感覺吧!能發現一些新的東西、或是做出一些被其他人覺得有用或是有趣的研究真的很好。這或許是大部分喜歡學術研究的人的通貌。' + }, + { + title:'假設我們在做專題的時候,自己的想法跟教授的想法有點出入,像是教授不支持你的研究方法等等,應該要怎麼辦?', + section:'首先要試著溝通。表達與溝通能力是我們成長過程中比較沒有被訓練到的,但出社會之後充滿許多人與人意見不同的時候,就需要表達與溝通能力。再者,也許可以考慮換實驗室。以大學專題生來說,因為重點是發現自己的興趣,所以應該是希望可以盡量從那個實驗室學到東西。要是覺得不適合,其實不用勉強。我這裡說的不是淺嘗則止、說換就換,而是要誠實地評估自己的表現,找出能夠給實驗室貢獻、又能讓自己成長的方式。如果用單純只是拿學分的心態在進行專題研究,就實在是太可惜了。通常會建議做老師或是實驗室擅長的題目。有些老師會願意讓專題生去做一些比較不確定的題目,若對自己有自信、也覺得還能跟老師或學長討論,那也是可以試試看。一樣,這沒有標準答案,完全因每個人的興趣和個性而定。不過,在進一間實驗室前的確可以多蒐集一些情報,確定實驗室的風氣與自己期待的是否相符。以個性而言,有人喜歡自己獨立一些、有人喜歡跟很多人討論。基本上我覺得多討論總是好的,所以我建議去討論風氣比較興盛的實驗室。在實驗室裏面,就是儘量找人討論,他剛開始不理你也沒關係,就是找他討論,然後一起去吃飯之類,拉近關係,這樣會學得比較快。不要只是想著自己、也可以想想怎麼樣幫助實驗室的其他人。自己若將來成為實驗室比較資深的學長姊了,也要樂於與學弟妹分享,帶動討論的氣氛。然後修課成績其實不是很重要,除非你們想出國去一流名校念書,不然的話多嘗試一些東西其實比較重要。我知道很多人甚至會修兩個專題,或是在台大修一個專題、到中研院再找一個實驗室做(沒有學分的)專題研究。也有人花在專題上的時間比修課的時間還多。我覺得這是量力而為,不過有時候動手做真的滿有趣的。做研究其實並不是以發論文為目標,而是以讓自己成長為目標。發論文是種公認的量化的指標,但也不用因為自己沒發論文、同學有發論文,而否定自己。重點還是看自己是否有成長。在找研究所或是工作面試的時候,能不能答得出自己做過些什麼、喜歡什麼、曾經解過怎麼樣的難題,有時候反而是更重要的。' + }, + { + title:'大學部做專題這件事情,您覺得它最重要的收穫是甚麼?', + section:'我覺得以career來說,能夠做一些跟研究相關、開創性的事情,會比較有趣。就算是去公司,也可以參與一些比較有研發性質的題目,並不一定都只是routine的工作。大學生參與專題研究,就是給你這麼一個機會,讓你經歷研發的過程。因為書本上的東西都是以前人研究的成果,那都是人家已經知道的,做研究就是要做沒人做過的研究。當你研究做到一個程度之後,你會發現前面已經沒什麼人了。單看你在研究的題目,全世界鑽的比你深的人沒幾個,到了那個時候就會覺得很有趣。就算還不到那個程度,但光你寫的東西能夠動起來,也是蠻厲害的。再來,會不會做研究跟會不會念書是兩回事,所以不要因為課業成績不好就覺得悲觀。若因為自己的成績落在班上排名的後面就喪失自信,那實在是太可惜了。長遠來看,大家都是同輩當中最聰明的一群人,所以就嘗試看看吧。當然,做研究只是人生諸多選項一個而已,不喜歡做研究的話,也完全沒有問題。' + }, + { + title:'要怎麼樣不為了發paper做研究?', + section:'(註:假如你想做的題目不是很好發paper,是不是應該改做不是很有用卻可以發一堆小paper的題目?)剛開始很難免會為了發論文而做研究,因為大家難免希望從發表論文中得到肯定和信心。但是,有些研究難度就是比較高,需要較多的時間研究、其中也會遭遇許多挫折。這種時候,就要能夠忍受一些寂寞,也就是研究時間會拉比較長。要是覺得有趣、也覺得自己能力夠強,那還是可以挑戰看看。你自己都知道題目夠難,能做出來的人都還是能夠發論文,只是發的快或慢的問題。我很喜歡一句話,「跑得快不代表跑得遠」,就看你要當跑得快的人還是跑得遠的人。所以這很難講。你可能跑得很快、發了些還OK的論文;你也可以選擇耕耘久一些,發一篇高水準的論文。那到底誰比較厲害?其實很難講,不一定。比較沒把握的話,可以從一些較小的題目開始,慢慢有經驗之後再挑戰大的。或者,可以先設定一些目標,例如要投稿哪個會議,再根據該會議地截稿時間往前推算,看可以做哪些事情。這都可以跟指導教授討論,但每個實驗室的風氣可能不同,建議還是要找自己合得來的實驗室。' + }, + { + title:'教授是如何訓練您的學生?', + section:'如果你是問我怎麼訓練學生的話,我會說國際觀很重要,所以我會常常讓他們去參加會議。如果是博士生的話,我會幫他們找機會去國外待一陣子,不要博士期間整段時間都待在臺灣。 反過來說,如果你的問題是:做為一個學生,我如果將來進一個實驗室,要怎麼樣可以比較快融入那個實驗室,比較快做的好?則我有兩個建議:第一個是,你們從高中一直到大學,其實都沒有面對過所謂的老闆。所以進到一個實驗室,當你老闆會對你有進度的要求的時候,你會有些壓力,可能會體驗到一些出社會的感覺。你在學校修課教授又不會拿你怎麼樣,頂多把你當掉而已,那是你念書不念書的問題。可是做研究的時候你需要直接面對你的指導教授,以及從他而來的壓力。像我之前說的,每個人適合的老師不一樣,要求高的老師可能可以讓你學到比較多東西,但你要學會與這樣的壓力共處。在一個氣氛較輕鬆的實驗室也是可以做好研究,但那就要學會自我要求。總之是抱著上進的心情,不求只是利己,而是希望自己跟實驗室可以一起變得更好。分享真的是很重要的。大家都生長在臺灣,都是臺灣未來的棟樑。可以的話不要一直存著競爭心態,而是大家有什麼訊息或收穫,就儘量多分享。同儕間、學長姐學弟妹間、甚至將來出社會之後跟同事之間,都懷抱著這樣的心情,而不是只是把別人壓過去讓自己出頭。不吝於分享、也不吝於尋求別人的幫忙。大家一起變好,那我們臺灣就會跟著變的更好。' + } + ] + } + ], + + + annotation:['特別感謝:楊奕軒','撰寫: 莊永松 莊鎧爾 琉琉 何驊凌 蔡易霖'], + id:'column_1807' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1808.js b/backend/routes/srcs/in/column/preload/details/1808.js new file mode 100644 index 000000000..d638bae74 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1808.js @@ -0,0 +1,122 @@ +module.exports = { + title: ['1994級 陳維超 (Skywatch Inc. Co-founder、英業達 Head of AI Center)'], + hashtags: [ + '系友專訪', + '陳維超', + '1994級', + 'b79', + 'Skywatch', + 'Inventec', + 'NVIDIA', + 'Computer_Graphics', + 'Nokia_Research', + 'GPU_Programming', + '電腦繪圖', + '電腦視覺', + '虛擬實境', + '雲端視訊監控', + 'IoT', + ], + sections: [ + { + bigtitle: '一、跟求學時有關的問題', + sections: [ + { + title: '談到電機系的歸屬感,請問對當年的電機系有什麼樣的回憶呢?', + section: + '老實說,對大學校內的印象比較模糊。\n高中到大學有很大的轉變,很多東西變成要自己去主動尋找,而不是被動接受(pull v.s. push)。至少大學前三年,感覺很多同學還沒有找到主動尋找的習慣,包含我自己也不是很確定想做什麼。因為這樣,在系外做的事情反而比較有感,像是騎腳踏車環島,或是寫程式打工的經驗。還有,系上似乎呈現 M 形分佈:有幾個人超愛讀書,非常認真,就是大家口中的「神」,也有一些人超愛玩,玩得很認真。這兩類的人加起來大概佔 10~15%。除此以外的同學,普遍來說比較害羞一點。', + }, + { + title: '大學時有什麼志向或興趣嗎?', + section: + '大一大二時對音樂很有興趣,常常在練鋼琴,自認練到跟音樂系程度差不多,反而沒有動機想把修習的課程做得很完美。\n而大三大四開始修習專題,開始有一些啟發。以前常常解短而簡單的問題,如同考試題目之類的,沒幾分鐘就解完了,而研究卻是會花上好幾年的時間。考試時的常見心態就是先答簡單的問題,放棄難的,研究反而是相反的。這種心態如果沒轉換過來,人生可能就會一直做簡單的選擇,例如找挑戰性低的工作。開始做專題以後,發現我喜歡解大問題,也發現自己有耐力把較大的問題,切成一塊塊的小問題來解。', + }, + { + title: '當時做過那些方面的研究?有什麼樣的學習心得嗎?', + section: + '大三時,我跟我的導師陳秋麟教授做電力相關的專題,寫程式做出電力系統的介面來,成效似乎不錯。還有,之前覺得很權威的高端研究,透過專題發現研究變得其實不那麼難親近;雖然還不一定能馬上發明出東西來,但至少看得懂。\n大四跟著很聰明的賴飛羆(ㄆㄧˊ)教授做專題,那時其實是因為老師人好就加入了,所以親民很重要!實驗室有各種專題,但主要是 IC 設計、CAD 電路設計類的,會用到很多電子電路知識。讀碩士時繼續跟著賴教授做電路,結果做了一年覺得動機不強,暑假就出國流浪了兩個月,回台灣時腦子突然變得比較靈活了,然後就在碩二換了研究方向,我就以電腦繪圖結合相對論,在ACM Transactions on Graphics 發表了一篇論文。', + }, + ], + }, + { + bigtitle: '二、跟從台大電機畢業時相關的問題', + sections: [ + { + title: '教授是先在台大讀完 MS 才去北卡攻讀 Ph.D.,選擇這樣做,有什麼考量嗎?', + section: + '有句話說:「出國就像出麻疹,一輩子出過一次就好了」!其實當時想出國,源於碩士時幾個月出國流浪,想要更認識這個世界,另外在大學時旁聽過資工系歐陽明教授的課,受過一些啟發,後來決定走上電腦繪圖的領域,歐陽明教授推薦北卡,因為那邊的教授與校友們,有許多是在電腦繪圖領域擁有很高地位的權威。', + }, + { + title: '在北卡羅萊納讀 Ph.D. 的歷程中,影響您很多的有什麼呢?', + section: + '我對於在北卡讀博士時的印象相當深刻。可能一方面由於隻身一人在國外生活,需要打開全部的感官去認識與適應新世界;另一方面也是因為讀博士時做的研究比較吸引人,能感覺到修課是為了要解決問題所需獲得的技能,而非只為了滿足畢業學分。有一些科目過去在大學時學得不夠扎實,到了美國因此再去修一次,才覺得有學好的感覺。在那裡修課時,不會被要求做大量的作業,但是需要更多的時間去完成,學習規劃並且漸次解決問題,還要能團隊合作。相對而言,他們的文化上對於作弊抄襲等就幾乎不能容忍;對於分數,可以寬鬆,但對於道德,就十分嚴格了。這種榮譽制度(Honor Code)由相信人的角度出發,然後犯錯時給予嚴重的逞罰;我們台灣則是以不相信人的角度出發,訂定嚴格的制度,但遇到有道德瑕疵時,反而不大逞罰。美國這方面或許這是我們可以學習的地方。', + }, + { + title: + '教授畢業後曾先在美國矽谷工作(NVIDIA系統架構師、Nokia Research資深研究員),那時的經驗對您的影響是甚麼?有無一些印象深刻的事?', + section: + '和求學時不同,在NVIDIA雖然待遇優渥,但和研究所相比,當下覺得成長速度有限。剛進入NVIDIA時,分配到測試晶片的工作,經常半夜還要跑測試程式,十分辛苦。審慎考慮之後,決定繼續待在這家公司,為了減輕負擔,想到一個辦法,做了個系統將電腦測試自動化,這樣就可以減輕工作的繁忙程度了,最後這系統還變成了公司重要的系統,由一個團隊來接續維護。我覺得這件事情有趣的地方是體會到,懶惰是發明之本,因為真正的工程師是無法忍受無聊的工作的,一定要發明工具來讓自己懶惰一點的。後來擔任了較高的職位,開始帶領團隊把GPU 做成運算的工具(GPGPU),因為 GPU 很快,只用來跑遊戲有點可惜,當時團隊就將 GPU 擴充成可使用程式語言來控制 GPU 的運算,從最初的 Cg 到一起開發出前幾代支援 CUDA 的晶片。\n讀完博士之後,總覺得應該要繼續發表論文,否則就好像不是研究員了。但在 NVIDIA比較難有發表論文的機會,只能做一些規模比較小的東西。因此後來在朋友介紹之下,成為第一批進入矽谷 NOKIA Palo Alto 研究室的人。當時的自由度相當高,行動運算在那個時候相當熱門,我也投入了相關研究,做影像辨識、和GPS有關的 location-base services 服務等等,是很有趣的經驗。經過兩年半,寫了很多的論文,反而覺得專注研究、不做產品有點空虛了。不過這段時間的經驗又讓我更知道怎麼帶學生發表論文、怎麼做系統、怎麼把系統和研究結合、如何帶團隊等等。每個階段自己有明顯的成長,感覺還不錯!', + }, + { + title: '碩士畢業跟博士畢業,這兩者在業界做的工作會有什麼差異嗎?', + section: + '我覺得在矽谷沒有太大的差異,但在台灣差異就大了,會稱呼「X博」、「Y博」,名片也常印出 Ph.D. 經歷,好像唸了博士就變成不一樣的人,這我比較不理解。讀不讀 Ph.D.,跟職場表現也不一定有正相關,但唸博士一定會改變、影響到自己,例如我自己就會對發表的文章精確度要求比較高,比較喜歡技術論文,而比較不會想出現在新聞稿裡面。', + }, + ], + }, + { + bigtitle: '三、跟從事現在這個工作時有關的問題', + sections: [ + { + title: + '當初是怎麼創辦 Skywatch Inc. 的呢?是否願意分享雲端視訊監控與 IoT結合的潛力所在?', + section: + 'Skywatch 當時是在與共同創辦人楊吉評(台大電機系同屆同學)討論後,決定結合自身影像分析領域的專業,與吉評在台灣硬體產業累積的人脈與資源,一同來創立的。我本來初始的想法是想要專注於影像分析與相關的服務,但評估世界的情勢與本地的優勢後,我們決定善用台灣硬體資源優勢,不侷限在影像分析的部分,使 Skywatch 結合軟硬,提供整體的分析、儲存與影像管理系統服務。', + }, + { + title: '相較於其他專做智慧家庭的公司或是其他新創公司,為什麼 Skywatch 會具有優勢?', + section: + '我認為有兩個主要因素使 Skywatch 能以相對精簡的規模,完成許多大公司難以達到的目標。第一點是策略靈活度與執行效率,讓我們能夠發揮較少的時間與人力,並借重本地硬體的優勢,來完成高品質的產品;第二點是 IoT 產品需要時間部署,並需與客戶成長與調整。近來的新創公司多仰賴創投資金大量挹注,但短時間若無法爆炸性成長的話,可能在產品成熟前,無法轉型為穩定成長的公司。Skywatch 是在穩定成長期,就沒有這類問題了。', + }, + { + title: '現今台灣創業環境有什麼問題?', + section: + '目前最大的問題在於傳統投資人對現今新創公司模式的不熟悉與不信任,新創生態系統不完善。上一波成功的新創公司常是由大公司底下的部門獨立後再上市的。而近期的新創公司則師法矽谷模式,在創業前期需要挹注足夠的資金,才有存活的機會。如果缺乏成功案例的話,投資人卻步不敢再進一步投資,就更無法再孕育新產業,陷入投資人缺乏標的、新創團隊缺少資金的惡性循環。', + }, + { + title: + '好像也有其他做智慧監視器的新創(例如 UmboCV),Skywatch 如何面對競爭呢(查到有篇報導說技術比別人傑出)?以及有什麼樣的合作夥伴(例如硬體廠商)?', + section: + '新創公司的產品之間通常不會、也不需要相互競爭。我認為創業的思維在於考慮全市場的需求,包括中國、歐洲、美國,之後決定產品定位,並專注於提升自己的產品,最終決定該如何服務客人。如果只在台灣與相關的公司相互競爭,是會容易迷失產品與研發方向的。', + }, + { + title: '教授在創業之後的主要工作內容是什麼(例如:技術研發或是管理等)?', + section: + '我同時是技術長和董事長,在技術方面還是會做一些研究、算點數學,但已經不太有機會寫 production code 了,因為 too old, too slow (笑),而且常常管理的急迫性是比較大的。管理當然不是那種「你去做這個、做那個」命令式的管理;我認為主要工作其實是 unblock 和 align。Unblock 就是幫團隊排除阻礙,並提供需要的幫助,同時自己也不該成為團隊的阻礙。Align 就是協助團隊不同成員的想法思維達成合理的一致性與協同性。團隊都是聰明人,沒有誰一定要聽誰的,但一定要協商,確保做出的結果可以同步。', + }, + { + title: '電腦視覺在學術界以及產業界有什麼重要但還沒突破的點?', + section: + '目前在做的比較多是由機器取代一個人類,例如物體辨識等。當然有些像是自駕車的應用,因為本身有很大的價值,是一定要努力突破的。除此之外,我覺得更有潛力的應該是超越人類的 (super human) AI ,例如前一陣子有入侵校園的案件,那 AI 應該要可以即時在大量影像串流裡,偵測出可能的入侵者並予以追蹤。另外一點是,現在人對 AI 的恐懼是因為不確定 AI 是不是可以負責或解釋的(accountable & explainable),像是自駕車撞到東西,要能知道為什麼發生這種事故,這時可解釋的 AI 就很重要。另外當然是有常識的、與符合人類共存的 AI (例如符合阿西莫夫機器人三定律等)的新道德規範等等,這些也都還有很長的路要走。', + }, + ], + }, + { + bigtitle: '四、結尾問題', + sections: [ + { + title: '學長對大神有什麼看法', + section: + '大神同學當然都很厲害,頭腦很好,但常有個共通點,就是講話大多數人聽不懂。IQ 雖然高,但是溝通能力有時候成為與其他人合作、或是發表研究成果時的阻礙。我在台灣的碩士論文有上百條公式,美國寫的博士論文反而只有3條 —— 這不是哪篇比較難,而是在台灣寫的時候搞得艱深,同學會「看不懂->有神快拜<(_ _)>」,但在美國時就被教育成發表就是要讓別人看懂,爭取和別人合作的機會,這樣影響力(impact)才會大。因此表達能力真的很重要,這是我認為台灣學生普遍較為缺乏、可以想辦法成長的部分。', + }, + { + title: '給大學部學弟妹的一些建議?(有無後悔在大學時來不及做的事情?)', + section: + '我只有一個比較廣泛性的建議。我發現在台灣當大學生,似乎都比較「混」:讀書做研究不起勁,連玩起來也不認真。國外的教育可能比較注重運動,年輕人不管做什麼事,認真起來能量都滿大的,所以鼓勵同學把身體搞好,work hard, play harder。', + }, + ], + }, + ], + annotation: ['特別感謝:陳維超'], + id: 'column_1808', +} diff --git a/backend/routes/srcs/in/column/preload/details/1907.js b/backend/routes/srcs/in/column/preload/details/1907.js new file mode 100644 index 000000000..d2a749f72 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1907.js @@ -0,0 +1,115 @@ +module.exports = { + title:['2012級 王易如 (行動貝果共同創辦人)'], + hashtags:['系友專訪', '王易如', '2012級', 'B96', 'Stanford', 'MoBagel', 'Silicon_Valley', 'Automatic_Machine_Learning', 'Advanced_Preprocessing_Unit', '行動貝果', '自動化機器學習引擎', '資料前處理'], + sections:[ + { + bigtitle:'一、大學生涯', + sections:[ + { + title:'跨出電機,創業心的萌芽', + section:'與歷屆學生相同,我在大學期間參與了電夜、系學會等系上活動,然而在加入台大創意創業學程 (NTUCEP) 後,我有了不同的視野。相較於電機系上的強人文化,埋頭苦幹為了學業拚搏,在創創學程來自各系的人中,我彷彿看到社會的縮影,學習如何溝通協調、引用各方資源、完成專題。從一開始完全不知道“創業”是什麼,單純被“創意”所吸引,到後來經過學程中黃鐘揚教授與陳良基教授的不斷創業灌輸下,我開始有了創業的想法。這時的想法很簡單:我要改變世界,我要創業。' + } + ] + }, + { + bigtitle:'二、赴美攻讀碩士', + sections: + [ + { + title:'選擇MS或PhD?', + section:'當初申請研究所時為了選擇Stanford MS 自費還是Princeton PhD全獎而與家人有一番激烈的爭執。雖然家人尊重我的決定,金錢上支持我去Stanford,但其實至今家人依然無法理解。因為金錢的因素,我只能加倍的努力,學期間在NVIDIA暑期實習,並且最後一學期找到TA獎學金,這才減少家裡的經濟壓力。除了因Stanford為創業聖地而選擇它以外,選MS也是因為覺得自己個性不適合長時間做研究。我喜歡跟人接觸溝通、進行team building、打造團隊合作;而PhD必須抱持著要做到某個領域的頂尖的心,埋首於研究多年。因此,MS跟PhD出社會後做的工作性質也差很多,MS畢業後主要是做engineer,而PhD主要是做research。' + }, + { + title:'在美國經歷的文化衝擊', + section:'在臺大電機裡,充斥著強人文化,大家都會去跪拜所謂的神人,寫作業讀考試時也是一直圍繞在那些神人旁邊,不斷的抱大腿、問問題,大家一起寫作業,所以感情很好。但在Stanford不一樣,那裡主要的校規就是honor code – 用榮譽感來規範。他們不太抱大腿,堅持要自己完成作業、不要求分數一定要很高,所以會花大量時間做一個作業。最令我震驚的是,在Stanford期末考時,助教發完考卷便直接離開教室了,完全沒有要監考的意思,而我也從來沒聽過有人作弊。因為堅持每一份作業全部都要自己寫,大部分學生週一到週五幾乎全部在圖書館或是宿舍讀書、寫作業,沒時間社交,導致週末便要辦party大放縱。Stanford學生報紙指出:大部分學生不想交男女朋友,因為覺得交男女朋友的時間成本太高,需要花大量時間維繫感情。週末一夜情是常態,很多人都會交砲友。另外也觀察到他們非常強調團隊合作,不過一旦任務結束,就不太會聚在一起,所以他們會覺得亞洲人很像sticky rice (天天黏在一起)。對於課程內容,Stanford的程式課程基礎打得非常好,我原本沒有打算走CS,因為大學時期的強人文化,讓自己對寫程式這塊非常沒信心,但進入Stanford之後我把CS的課重新修了一次,對寫程式這塊也改觀了。' + }, + { + title:'文化衝擊後的改變', + section:'臺灣和美國的法律制度是不同的,臺灣是以法條為主,法條沒有寫大家就會想怎麼鑽漏洞,而美國是強調case by case,凡事追求有正確的初衷、有榮耀感。不需太多獎懲制度,也沒有要一定去遵守什麼規定。這方面讓我學會突破框架思考。原本的我,很容易被考試的標準答案所束縛,遇到問題的解決辦法就是想到訂規則。在這些衝擊之後,我學習用初衷、榮譽來解決問題。我帶著這個榮譽制度進入我們公司讓大家守時,結果是非常驚人的,沒有任何獎賞或懲罰,大家便準時出現在會議室裡。這世界不是什麼都可以拿法條來定義與衡量的,要生存在這世界重要的是人與人之間的溝通,所以不要跟著別人的期待走,或許有更好的方式是別人沒有想到的。' + }, + { + title:'Stanford培養的思考模式', + section:'Stanford的Design School(創意思考學院)對我影響很大,他們強調短時間的Brainstorm不做任何Judgement。在提出問題或解決上,先有一段時間發散思考,最後收斂階段時再剃除不好的選項。如此我們比較容易在發散階段逐漸找到global maxima,如果一開始直接否定掉一些可能性,最後討論的共識通常就會只是大家尚能接受的local maxima。這套發想的思考模式也應用在我們公司每季訂定的目標和關鍵結果(OKR),如此可以讓所有與會員工參與並提出最好的方案。' + } + ] + }, + { + bigtitle:'三、共創MoBagel', + sections: + [ + { + title:'MoBagel團隊的建立', + section:'我原先與一些朋友著手Social App時有認識一個台灣在矽谷的天使投資人。有次投資人說有一群前來矽谷參Salesforce $1Million Hackathon的年輕人,他們需要英文上的協助,希望可以找一個能幫忙確認賽會要求與發表成品的成員,於是我加入了他們。在比賽現場我才認識後來MoBagel的CEO與CIO,那時我們做的主題與現在完全不同,那是一個設計師供應鏈廠商的平台,幫助hardware designer找到合適的suppliers。比賽中我們得到評審青睞拿到第六名,也認為團隊默契絕佳,向性挺合的,決定一起創業。這場比賽無疑是我人生的轉捩點!' + }, + { + title:'MoBagel團隊的運作', + section:'「人」其實是創業過程中最重要的,要有一個團隊共同相信的東西(相信商業、相信邏輯、相信技術),也要有相應性,我們的CIO的技術很強,CEO的資源多,因為他的家人開工業傳產上市公司,比較知道投資人想聽什麼。而我比較想認識了解一個人知道每個人的熱情在哪裡,什麼核心持續驅動人去做一件事,這麼多人又如何組成交響樂奏出和諧的曲子。我很在乎找到團隊中每個人的核心,然後怎麼驅動這個團隊持續營運,所以現在擔任的是COO(營運長)。' + }, + { + title:'物聯網起家,逐步轉型成自動機器學習平台', + section:'我們最初是物聯網數據分析平台。自詡為Google Analytic for IoT,每個物聯網設備都可以串接到此平台。我們有一個dashboard可以呈現每個設備的狀態,也有偵錯系統。後來客戶希望我們能預測每個設備接下來可能發生的事情,希望不要僅限於一般的數據分析,如此便需導入Machine Learning與Time Series的預測。在這塊投入越來越多資源後,發現我們的預測引擎(Automatic Machine Learning,給Data就能自動建模型)其實不用侷限於物聯網,可以跨足更多應用場域。即是我們現在做的:自動機器學習預測引擎。' + }, + { + title:'讓機器學習落地,人工智慧普及化', + section:'我們與微軟Azure的願景不同,更注重應用。我們將 AI 的演算法實際落地到生活中,像是預測大樓用電量來制定省電策略。KDNuggets指出,世界上僅有20萬人真正了解ML,這與世界需要的大量AI人才有很大的落差,因此很多應用無法實現,解決這個問題是我們的使命!我們正在做APU (Advanced Preprocessing Unit),根據AI的應用類別來進行資料前處理,增加預測準確度,並且跟產業有更深的合作。我們的工具也比Azure更簡單、快速與準確!' + }, + { + title:'新創的困難與學習', + section:'我們的強項大多在技術方面,相對上商業即是罩門,團隊一開始對業務部分比較沒經驗,不了解如何讓商品落地,做了很多的嘗試,目前我們已經打入台灣產業龍頭如中華電信、友達等。歷經15年創立到18年才逐漸商業落地,現在的小成功是因為不停trial and error,才找到合適業務。' + }, + { + title:'新創V.S.大公司', + section:'我不是鼓勵大家都去創業,是希望大家可以培養leadership,組織團隊一起完成一個目標,我認為這是在大公司或新創都可以學習與挑戰的。一般而言,在大公司工作相對穩定且分工明確,大家的工作範圍是一個個方塊,拼起來就是全部的事情,所以只要每個人做好份內的事情,產出就會完整;而新創在高速成長期,相對分工較不明確,所以大家的工作範圍較從自己出發,是一個個圓圈,拼起來時中間會有縫隙,能夠接受這樣的情況並且帶領團隊將縫隙填滿是成功的關鍵。當然在大公司也有新創事業群,所以上述情況也要看單位。建議依照你對自己的了解來決定去大公司或是新創工作。在大公司通常會有mentor可以帶你,而在新創有自己摸索的挑戰。不過只要在學習,之後轉換跑道都是有機會的。新創也需要待過大公司的資深人才,同樣地,大公司也有新創事業群尋找愛冒險的人才。' + } + ] + }, + { + bigtitle:'四、人生規劃', + sections: + [ + { + title:'學姐的人生哲學', + section:'年輕的時候,往往會看到別人做得比自己好的一面,便開始有比較的心態。這樣的心態可以成為一種驅動力,讓自己不斷地進步。然而,我曾經深陷於比較之中,而變得憤世嫉俗。柯文哲說:「不要羨慕別人的成功,因為你付不起那個代價。」後來才發現陷於比較是沒有意義的,因為大家終究也只看到表面,並沒有看到成功人士的背後到底犧牲了甚麼。現在回頭看年輕的自己,我希望更早的我內心能更定地好好做事情,不要太浮躁。「每個人都有自己的時鐘。」我覺得人生應該視為一種累積,不管失敗或成功都會成為生命中成長的養分,不要太在意當下前進幾步、後退幾步。' + }, + { + title:'對於MoBagel與臺灣新創的期許', + section:'對於未來,我希望能讓MoBagel成為臺灣軟體大廠的模範,並且帶動臺灣人投入創業與領導能力的培養。如果只是著眼於眼前的工作,往往就只會成為一個小螺絲而已,應該要學習怎麼看得更長遠,因此領導力是非常重要的。領導能力可以透過投入project來培養,過程中你可以練習怎麼說出自己的願景、找到自己需要的夥伴,甚至像是負責系上的活動也可以培養領導能力,而這些練習的機會可以靠自己去創造。' + }, + { + title:'給學弟妹規畫人生目標的方向', + section:'談到如何規劃自己的人生,我覺得有兩個方向。第一,相信自己獨一無二,不要害怕挫折。每個人的出發點不同,特質也不同,因此要相信自己的與眾不同。世界上有很多創業成功的例子,這些人會成功的原因就在於,他們不會因為一個題目已經有其他人做了而認為機會消失,而是相信自己能夠做出與他人不一樣的風格。事業有很多可能,不要受限於框架內。第二、有個值得一生去追尋的志業。如果有一件事值得用一生去追尋,我覺得那會是很棒的事情,因為那就是一個人的使命。如果有,千萬不要放棄它。沒有也沒關係,可以先著眼於比較短暫的目標,重點是知道活著的意義與價值。' + } + ] + + }, + { + bigtitle:'五、給大學部的建議', + sections: + [ + { + title:'假如重新進入大學生涯,學姊會如何規劃?', + section:'我想要在大學的時候接觸更多人並更深入的了解他們,因為其實人脈最後都會變成你的資產。所以如果時間能倒流,我會去組織一些我在乎的事情,然後認識一些有才幹的人。' + }, + { + title:'學姊推薦畢業生投入的領域', + section:'我覺得還是要看自己喜歡什麼,如果已經有志業的話,就follow你的志業,勇敢投入、不要怕。如果沒有的話,那大方向的話學習AI、機器學習基本上還是不會錯,因為現在會的人還是太少,而且目前各行各業大公司都在招募這樣的人才。所以如果是快要或剛畢業,我覺得找個可以學習這些事的工作會很棒。' + }, + { + title:'有什麼座右銘或可以提點學弟妹的嗎?', + section:'不要活在過去,不要活在未來,活在當下。不要活在過去意思是,不要困在過去的成功或失敗中走不出來;不要活在未來,則是很多人常常會有一個虛幻的目標,但是覺得時間過去目標自動就會達成,造成現階段卻擺爛、沒有在做能幫助達成目標的事情。若有現在讓你非常不舒服的事情,請改變它,不然就是接受它,並好好活出當下。' + }, + { + title:'最後,一句話談談電機系給您的歸屬感?', + section:'每個人都有追求卓越的心,會把有興趣的事情做到最好,且沒那麼功利主義,這樣的電機魂是我所感到共鳴的。' + } + ] + + } + + + ], + + + annotation:['特別感謝: 王易如','撰寫: 鄭謹譯、李筠婕、戴慕潔、吳建翰','校稿彙整: 鄭謹譯'], + id:'column_1907' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1908.js b/backend/routes/srcs/in/column/preload/details/1908.js new file mode 100644 index 000000000..6ed4e055c --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1908.js @@ -0,0 +1,131 @@ +module.exports = { + title:['2008級 方劭云(當屆最年輕升遷副教授)'], + hashtags:['2008級', 'B93', 'NTUST', 'EDA', 'VLSI', 'IEEE', 'ACM', '電子設計自動化', '超大型積體電路', '台灣科技大學', '演算法'], + sections:[ + { + bigtitle:'一、我的大學生涯', + sections:[ + { + title:'球隊與課業交織的辛苦大學生活', + section:'因為我是排球校隊,沒能花很多時間在系上,但電機營電機之夜年年參加,這些活動是我跟系上相處最多的時間。大學時候我覺得讀電機系太痛苦了,常常嘴邊掛著畢業後就要嫁了這句話。因為練完球已經十點,回家從12點一直唸到4點,早八又要應付系上各大小考試,跟其他系的球隊同學比起來差很多。另外我常常覺得自己腦袋轉的沒有男生快,沒辦法贏過男生,但我好勝心很強不喜歡事事依賴同學,所以特別辛苦。因此我大學的底子打的不是很深,都是為了考試而唸書,也很不會寫程式。' + }, + { + title:'求學生涯印象最深刻的事', + section:'雖然有嘗試做過專題,但一直到大四要推甄的時候我還是很徬徨,跟隨同學推了ICS(當時熱門科系)與EDA,最後進了EDA組,赫然發現EDA需要用到大量程式。我跟隨的是張耀文教授,一開始能力不太受教授肯定,但因為我好勝心很強,因此開始努力學習,也發現自己其實不會排斥程式,甚至慢慢從中得到樂趣,越做越好也得到教授的青睞,這也是我在台大九年印象最深刻的事。' + }, + { + title:'過往經驗而成為良師', + section:'因為我的成長過程,現在身為老師,我不會用分數評論學生好壞,畢竟學生都是為了考試而唸書,等當你需要某項能力而自發性學習時,那些東西才會是你的。而且我一直不是電機系裡最聰明的那一群,所以我比較知道為什麼學生這裡不懂,比較能夠照顧80%的學生,而不僅限於Top10的菁英。' + }, + { + title:'不侷限於課業,拓展更大的眼界', + section:'多方嘗試,像我打了9年的球隊,如果能參加例如之夜、營隊我也都會參加。這些事情對我的幫助可能比大學時期在圖書館讀書來得更大。很多事情必須要多方嘗試才能獲得更多,參加球隊對我最大的幫助是當我在做事的時候效率比別人高很多,在僅剩的短暫時間,我會拚了命的完成事情。這樣的生活訓練了我的高密度集中力,往後幫助我的工作很多,例如趕Deadline。另外參加活動可以提高與人相處的能力,讓自己更圓融,多遇見各種人,每個系的人都非常獨特,所以我很鼓勵大家往外參加活動,尤其電機系有些人在與人相處這件事上是短板,畢竟出了社會常需要團體合作,這些經驗對人生幫助很大。最後參加活動也可以建立自己的人脈,當後半段的人生回歸生活而不是專業領域,更廣泛的人脈可以帶給自己生活上更大的幫助。' + } + ] + }, + { + bigtitle:'二、攻讀碩士博士', + sections: + [ + { + title:'漫長的研究所生涯', + section:'我讀完一年碩士之後就直升攻讀博士,再花四年拿到博士學位。在臺灣,很少有人大學畢業就直接讀博士,有些人讀了一年碩士後選擇直升,也就是逕讀,直接成為博士班學生,並且拿博士畢業證書。這與按部就班讀完碩士的差別是碩士和博士的畢業論文不能互用,但選擇直升碩士時期的論文和研究主題可以成為博士論文的一部分,讓研究生能夠往更加一致的目標、主題鑽研。' + }, + { + title:'面對抉擇—是否要選擇讀博士', + section:'如果畢業生想去的是一間新創公司,上班時寫的是應用程式、網頁、App,那沒有讀到博士的必要。但在一些大公司內,博士學位有一些優勢,例如職等、升遷機會等。拿到博士學位後也可以申請教職,但還是以有教學熱忱者為佳。我常建議學生不要隨波逐流,要選擇你真正想走的道路,畢竟在現在的社會,隨波逐流是一件挺危險的事,所以我希望學生們能自己衡量什麼方式是對你自己的未來比較有利的。就算是電機系的博士,不同領域也有差異,但最重要的還是指導教授本身。像我的教授他就算已經非常德高望重,仍然非常積極在從事研究,凡事親力親為,發論文也是十年內從不間斷,數十年如一日。他也認為博士學位應該在四年內讀完,因此會一直督促我做研究和發論文。所以跟對適合自己的教授是很重要的,不要隨波逐流!另外當訪問一個碩士生,你得到的會是「不想讀博士的原因」,但當訪問一個博士生時,你得到的會是「為何讀博士的原因」。因此,不要因為一個學長姐的意見就決定自己未來的道路,而是應該為自己的未來做打算。' + }, + { + title:'臺灣和國外的MS與PhD', + section:'部分國外大學的MS只是做個小專題或甚至修完課就能畢業並取得學位,而臺灣的MS比較精實,畢業還要交一篇碩士論文。所以在臺灣大部分學生都是先讀完MS再攻讀PhD,所以國內外的碩博士制度是有很大的不同的。' + }, + { + title:'實習經驗', + section:'我的題目是半導體製程的電子設計自動化,因為實習內容和我的博論內容相關,我才會選擇去台積電的OPC部門實習的,這個領域叫DFM (Design for Manufacturability),製造可行性設計。這次經驗對我幫助很大,除了研究方面的幫助外(直到現在我都還有在做這個議題),也讓我瞭解業界文化,成為我未來做決定時的重要參考。' + }, + { + title:'最終抉擇,踏入教職', + section:'如果你是一個比起其他人更喜歡面對電腦的人,那進入科技公司是個不錯的選擇。但我比較喜歡跟人互動,同時這也是我擅長的地方,再加上我比較不喜歡業界文化,因此我才會選擇投入教職。同時,做教職的時間比較自由,雖然薪水較少,但我可以做自己時間的主人。' + } + ] + }, + { + bigtitle:'三、悠遊於產、學、教職之間', + sections: + [ + { + title:'教學之路的體悟', + section:'我在台科大學生身上看到了一些很多台大學生沒有的特質,不是我不喜歡台大學生的特質,只是我在台大待了九年,所以在台科大更能和學生有不一樣的火花。會選擇教職不是想要成為學術上很厲害的人,而是我想要帶給學生不一樣的東西或幫助到學生,讓學生有機會憑藉自身努力改變家庭狀態,這同時也是讓我蠻有成就感的部分。勉勵不論哪裡的學生,都不應安於現狀,持續自我學習,才能在快速變動的EE領域歷久不衰!' + }, + { + title:'參與競賽自我提升', + section:'學生研究競賽對於我教職申請有一定幫助,這類型競賽很重視Presentation的技巧,其中包括口語表達、slides、台風都是重要的一環,然而不可忽略的是比賽運氣成分很大!建議電機系的學生,學術能力是基礎,但如何自我行銷也是重要的課題,這在往後進公司、職場也是必要的。競賽是一種高密度的能力提升,因為是實際所需,而非針對考試,學生更能學到東西,所以很多EDA老師都會推薦學生參加競賽!若要給也想參與競賽的學生甚麼建議,我認為現在的學生實在都太忙了,有各種活動可以選擇,但要懂得取捨,無關對錯。只有當你投入身心靈、好好參加競賽,才能從競賽中學習到東西及得到名次,在參加前應好好考慮自己有多少時間可以投入競賽。' + }, + { + title:'機器學習、人工智慧與EDA', + section:'AI無疑是時下最火紅的炸子雞,各領域都大有人在試圖將AI整合,但AI並非萬能,以EDA來說,加入AI來輔助目前還是以學術論文為主;業界而言,AI具有精準度的限制,精準度方面,例如晶片繞線與設計需要百分之百的正確度,而這是目前AI無法做到的,因此目前AI主要運用還是針對EDA中需要預測的應用上。目前運用主要可分為EDA for ML及 ML for EDA,前者是透過EDA設計AI所需晶片,後者是運用AI來輔助電子設計自動化。總的來講,AI的優勢在於大量data的analysis,如alpha go、image processing、語音處理,而目前弱勢則是prediction的精準度與無法取得大量既有資料時(training data)的情況。' + }, + { + title:'EDA目前領域限制與未來發展', + section:'我認為EDA短期之內絕對不會衰退,而且應該還可以持續一陣子。在台灣EDA人才需求非常大,也沒有所謂飽和問題,只要半導體走得下去,EDA就永遠走得下去,半導體製程只要有新的技術,EDA就需有新的軟體來Support;只要摩爾定律走得下去,EDA仍然還有很多發展空間。目前瓶頸在於Problem Size,因為晶片的邏輯匝太多,通常數量級百萬起跳,要處理這麼大的問題,演算法、資料結構、平行運算與分散運算變得更加重要,運算資源也相同。' + }, + { + title:'全球性對台灣EDA產業的影響', + section:'美國EDA逐漸往台灣走,而且不僅是EDA公司,所有半導體公司也都需要EDA人才。我覺得軟體都很重要,除了純類比以外未來都會用到。' + } + ] + }, + { + bigtitle:'四、我的未來規劃', + sections: + [ + { + title:'傾力鑽研專長領域', + section:'我目前傾向不轉領域。一來部分老師轉換領域,是為做出與以往博士班不同的成果,有助於工作上的晉升,但我個人重視興趣勝於追求工作表現。二來由於美國近年願意在EDA領域投注精力的人變少,多數都嚮往進入如Google、Facebook等等的公司,於是企業在台灣的EDA人才需求日漸龐大,然而台灣專精EDA的老師不多,造成培育出的領域人才遠少於企業需求。綜合以上興趣與產業的原因,我並沒有轉換領域的規劃,但對於各式新興主題,如近年的Machine Learning,我們依然樂意接觸,但重心還是會放在EDA。' + }, + { + title:'興趣與企業合作', + section:'就我個人而言,是以興趣為主要考量,但當學校與企業合作時,就會做出企業指定的項目,當然也有完全不與企業合作的老師,但透過這樣的契機,學生們能面對企業中正在處理的問題,了解產業的真實面貌,有利於將來的實習與就業。' + }, + { + title:'學術團體之於我', + section:'而在學術界裡,我的中心思想還是希望可以忠於自我,我並不期許能夠在IEEE/ACM中扮演怎樣舉足輕重的角色,但當我認為在目前的崗位上,只要仍然是自在且能力許可,我就會持續參與組織,接觸各樣領域最前端的研究,掌握學界的走向和脈動。' + }, + { + title:'學術界和業界間的掙扎', + section:'雖然我也曾經起過到企業裡過著朝九晚五、坐擁高薪生活的念頭,但後來還是決定續留教育界。教職其實是一份自由度極高的工作,能夠自行安排一切行程,尤其擁有家庭之後,這樣的彈性是非常可貴的。過去三度至企業實習的經驗,也給了我一些啟示,讓我深信教育界是與我最投緣的環境。' + }, + { + title:'面對瞬息萬變的電機產業,大學生該...', + section:'我認為是沒有必要進程遠程規劃的,電機產業的確瞬息萬變,在這樣計畫趕不上變化的環境裡,你甚至無法確知在現在當紅的AI,在畢業時是不是仍獨領風騷。只要在大一大二的修課過程中,把每一門課都學好,並留心自己感興趣的領域,機緣下那些看似死板的科目,或許會成為左右生涯規劃的重要元素,而求學過程中獲得的自學能力,也是比任何知識都來得有價的。' + } + ] + + }, + { + bigtitle:'五、回饋電機人的大家', + sections: + [ + { + title:'給大學部同學的建議', + section:'希望你們在大學部的時候就多去瞭解其他領域的人在做什麼。並思考自己對未來的打算;有興趣的領域、科目有哪些,在認真查過資料後,對研究所的生態就會更有概念。演算法這門課一定要認真學,對未來非常有幫助。' + }, + { + title:'一句話勉勵大家', + section:'事有輕重緩急,能忠於自己,不隨波逐流,知道當下最重要的事情,把它做到最好。' + }, + { + title:'台大電機帶給我的歸屬感', + section:'我誠實地說其實大學時代沒什麼歸屬感,反而是出社會後發現在台大電機所認識的人力資源非常多,畢業之後覺得能成為台大電機的一份子是非常幸運的!' + } + ] + + } + + + ], + + + annotation:['特別感謝:方劭云','撰寫: 鄭謹譯、施彥宇、王德宇、曾晴','校稿彙整: 鄭謹譯'], + id:'column_1908' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/1909.js b/backend/routes/srcs/in/column/preload/details/1909.js new file mode 100644 index 000000000..3fc028f54 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1909.js @@ -0,0 +1,135 @@ +module.exports = { + title: ['2012級 李昀樵(17直播 技術副總)'], + hashtags: [ + '系友專訪', + '李昀樵', + '2012級', + 'B97', + 'Harvard', + 'MBA', + '新創', + '創業', + '語音處理', + '17直播', + ], + sections: [ + { + bigtitle: '一、大學時期', + sections: [ + { + title: '學長在大學時期就對語音產生興趣了嗎?是甚麼原因讓您有興趣呢?', + section: + '我大學的時候修了語音實驗室的專題,就覺得對語音相關的資訊很感興趣,事後看來這是一個蠻好的決定,因為碩一時剛好是deep learning在語音開花的時候,也讓我多接觸到 deep learning 這個領域。', + }, + { + title: '在語音實驗室時做過哪些研究?請問內容與日後工作是否有相關性,以及對您的影響?', + section: + '在語音實驗室時主要是做retrieval相關的東西,例如語意檢索(Semantic Retrieval),也就是當你想要尋找的term不存在在文章中但該文章仍跟這個term 有關時,該如何尋找到這篇文章。\n至於內容是否跟日後工作有關,其實碩士的研究通常跟未來的工作沒有直接的關係,除非做PhD,研究的東西才會跟工作比較有關聯性。碩士主要是培養做研究的能力,也就是教你如何藉由大量閱讀 Paper 和重置他人實驗來學習某個領域最尖端的知識,這其實是很重要的能力。', + }, + { + title: '為何會對新創事業感到有興趣?', + section: + '大三的時候加入了創創學程,當時在創創學程開了非常多的眼界,然後認識了台大各系關於這方面的人才,日後發現這些人在今日的新創產業中仍非常的活躍。大四時跟外系的幾個朋友參加了一個創意競賽,當時其他同學有很好的發想,但不知道如何用程式寫出來,所以就找了身為電機系的我,最後大家一起完成了一個 prototype。之後同一群人也一起做了幾個 app 和產品,因為有各系各個領域的人,所以大家分工,我負責開發,有人負責宣傳。最後甚至跟這群人一起開了公司,甚至在學期中請假到矽谷募資,是個很有趣的經驗。', + }, + { + title: '系上的課業繁重,有什麼是鼓勵學弟妹在大學中一定要嘗試的事情?', + section: + '我會鼓勵大家在各方面都要勇敢嘗試,不能只顧課業。事後看來,大學期間對我幫助最大的事情,在當時看來都是不知道對未來有什麼幫助的,只是覺得好玩就決定試試看。比如參加陽光椰子社、台大藝術季、創創學程、參加創意競賽、做 Side Project、開公司等等。另一方面對我幫助最大的是大學期間認識的朋友,每個人都有獨特的背景、故事和能力,從他們身上我得到了很多啟發。', + }, + ], + }, + { + bigtitle: '二、生涯規劃', + sections: [ + { + title: '對於出國讀研、在臺讀研、直接工作的選擇?', + section: + '其實在大學時,我對於這些路都有事先做一部分的準備,例如有當兵、準備研究所推徵,出國的部分也有去考 TOFEL、準備 GRE 等等。我最後選擇留在臺灣,很大的原因是我希望能夠陪著家人。另外,因為讀研究所可以培養與實際上的知識接軌的能力,而我當時知道自己仍需要培養這種能力,因此選擇讀碩士。\n然而,我覺得在大學時自己沒有很積極地找已經出社會的學長姐詢問相關的建議。我也建議學弟妹可以多找大約 30 歲左右的學長姐諮詢,他們可以給予較為成熟的建議。我在申請哈佛 MBA 的時候發現管理學院的學生,會時常向年紀較近,或是已畢業一段時間的學長姐諮詢自己的職涯規劃,我覺得這是一個蠻好的決策輔助。', + }, + { + title: '當時是否曾考慮繼續就讀博士?', + section: + '我當時沒有考慮讀博士,因為自己並非很想走純研究路線,我的興趣主要是在實務中應用這些前端知識。另一個考量是時間,博士通常會讀 4、5 年,這個機會成本也是要一起考量的。', + }, + { + title: '畢業後自行創業的考量?', + section: + '其實在讀碩士的時候我就已經有創業的念頭,那時候已經和許多朋友進行很多 side projects,甚至還大膽地在學期中請假飛到美國矽谷募資、面試美國加速器等等,現在回想起來當時真的是一股衝勁。我覺得人生應該多多嘗試,才會知道自己到底想要什麼,尤其現在資源很多,想試就去試。若你覺得非做不可,那就放手去做吧!\n但相反地,不要一味地跟風,要思考好再做決定。我覺得對學生來說,直接去風險太高。比較好的方法是做 side projects,如果這個 project 能夠做起來的話,再嘗試全職來做,我自己現在也一直都還有在做 side project。另一方面,我覺得學生通常在經驗和能力方面通常都較為不足,所以如果對新創感興趣的話,先加入一個有潛力的新創學習一下,也是一個好方法。', + }, + { + title: '今年選擇去哈佛念MBA的考量?', + section: + '我覺得這是個增廣眼界的過程,回想起過去,常常人生中重要的轉捩點是在不同環境下遇到不同的人而產生的,而 MBA 就提供你一個不同的環境並能夠跟一群特別的人相處。\n再來就是在亞洲創業的話,常常會覺得台灣新創產業有個天花板,因此覺得需要跟國際接軌,要學習如何去獲得國外的資源,例如眼界、經驗與資本,來突破這個天花板。', + }, + { + title: '可以說說現在對未來的規劃嗎?', + section: + '下一個目標就是希望創業成功,並且有機會的話能夠回饋社會,找到自己能夠貢獻並幫助社會的地方。', + }, + ], + }, + { + bigtitle: '三、在17直播的經歷', + sections: [ + { + title: '加入 17 直播的考量?', + section: + '我會想加入 17 直播的原因有三個。第一是技術層面,我想加入的是有足夠技術深度與挑戰的公司,而 17 直播是臺灣使用人數最多的直播平台,再加上直播本身也是個有足夠技術挑戰的領域,所以 17 直播是個能提供足夠技術挑戰的公司。第二是創辦人,加入之前我與 17 的創辦人之一黃立成聊過幾次,我覺得他的想法、經驗與視野都有很值得學習的地方。第三是公司文化,我很喜歡新創公司那種自由、自治的風氣,每個人都把公司當成是自己的,大家都盡全力讓公司發展得更好,這是我很喜歡 17 這間公司的地方。', + }, + { + title: '在 17 直播中扮演的角色?', + section: + '我在公司的職位是產品和技術副總,負責規劃產品方向、規劃技術架構和確保產品品質等等,也要負責管理團隊、建立團隊文化等等。', + }, + { + title: '聘用員工時如何判斷一個人的能力?', + section: + '要判斷這名員工是否是公司想要的人才,我大概會從三個方向著手。\n第一,是否具備硬實力,我通常會列出這個職位必須的技能,再逐項驗證。例如他如果是程式工程師,最重要的技能就是寫程式,我的做法通常是給他幾個程式題讓他當場寫。\n第二,團隊合作,我必須知道這個人如何與團隊合作,以及判斷這個人是否好相處。我會觀察他在面試中的應對進退和過去經驗,比如是否有團隊精神,是否會太自我或是無法接受他人意見等等。\n第三,如果是需要帶人的職位,我會確認領導能力,主要會問他一些過去建立與帶領團隊的經驗,以及問對方一些情境題,例如與老闆或員工意見不合的狀況,他如何解決。', + }, + { + title: '大學、碩士與工作的差距?', + section: + '加入 17 直播之後所做的東西大部分是大學或碩士時沒有教過的,也就是說要從零開始學,自己找國外的資源閱讀或參加 conference,跟產業中厲害的人學習,比如說跟這領域的意見領袖聊天理解這個領域的大方向。\n17直播剛開始做的時候主要是兩個領域,media streaming和distributed system(分散式系統),這兩個當初在台大時沒有太多接觸,只能自己從網路上學,或者找會這些東西的人學,當時幾乎是邊學邊做,系統每幾天就會超載一次,壓力還蠻大的,但我覺得在大學基礎有打穩的話,這些技能學起來還蠻快的。建議學弟在大學時要把核心能力打穩,比如說如果未來要走 CS 的話,那現在應該把作業系統、資料結構、演算法、基本的幾種程式語言等等都學好,至於其他比較細節的東西,之後學起來會很快。至於哪些是核心能力,可以向業界的人聊天,請教學長姐或教授。', + }, + ], + }, + { + bigtitle: '四、給學弟妹的建議', + sections: [ + { + title: '給未來想要進入新創產業的學弟妹的建議?', + section: + '我覺得有三個面向可以講:興趣、技能、選擇。\n首先就是你要有興趣,並且必須知道新創產業是有風險的,不要一窩蜂覺得新創很好就去加入。\n再來是技能層面,加入新創產業前,自己要先想好要具備並培養怎麼樣的技能,如果你想進大公司 Google、Facebook,那你應該把大部分的時間投資在 coding 上。但是在新創產業裡,可能要稍微分散一點,對各方面都要略有了解,像是 coding 能力、溝通合作能力、對產品的 sense、對公司策略也要有想法,要能在公司裡找到自己的定位做出最大的貢獻,幫助公司成功。對個人而言,你要為自己未來的發展做規劃,如果創業失敗了,要確保自己這樣的技能在市場上是有退路的。\n再來是選擇,選擇自己想要加入怎麼樣的新創公司,你可以想像你自己是創投公司。概略來講,他們首先可能會評估市場,評估市場對這個產品的需求、有無競爭力等等,再來要評估創辦人是否足夠優秀,跟團隊有無足夠的能力持續地走下去。而你自己在做選擇時,就是要像創投公司一樣思考值不值得把自己未來幾年的時間投資在這間公司上面。', + }, + { + title: '環境衝擊與建立新的交友圈', + section: + '我覺得在大學時,建立多元交友圈是很有幫助的,每個學院的學生都有自己的文化、思考和做事的方式,在大學的時候多跟不同學院的同學接觸,對於開拓眼界是很有幫助的。台大在這方面提供蠻多方式的,比如修學程、跨系修課,參加各種社團等等。\n比如我之前暑假到 Boston Consulting Group (BCG) 做 Part Time Assistant,就受到很不一樣的文化衝擊,在電機系我們通常把重點放在 code 的品質,但在 BCG 要學習如何思考商業問題、如何為公司創造價值、和如何跟主管與同事溝通等等,這些不一樣的思考方式後來在職場上對我幫助很大。', + }, + { + title: '您覺得您成功的關鍵是?', + section: + '首先我是覺得我離成功還有蠻大一段距離。如果說回頭看過去幾年的話,我覺得在做決定時,比起選擇可預期的道路,我更喜歡多方嘗試各種方向,其中不乏風險高的方向,比如說加入早期的新創公司。我的感覺是按部就班地規劃未來的人生,反而無法跳脫現有的眼界,多方嘗試才更容易找到新機會和新方向。', + }, + ], + }, + { + bigtitle: '五、與台大電機系的連結', + sections: [ + { + title: '電機系給您的歸屬感?', + section: + '很多好朋友甚至現在一起合作的夥伴都是電機系同屆或上下屆的同學,電機系的老師也一直給我很多幫助,我現在也還是有跟比較熟的老師保持聯繫。', + }, + { + title: '覺得電機系的有什麼共同特質?', + section: + '我覺得電機系的同學非常優秀,在各方面學習理解和創新能力都很強,在我們公司的電機系同學表現也很傑出。我覺得學弟妹們如果能更勇於多方嘗試、不要只走成功率高、低風險的路,電機系的同學能夠在更多元的領域做出好成績。', + }, + ], + }, + ], + + annotation: ['特別感謝:李昀樵', '撰寫:吳建翰、余欣澄、莊永松、鄭謹譯', '校稿彙整:李筠婕'], + id: 'column_1909', +} diff --git a/backend/routes/srcs/in/column/preload/details/1910.js b/backend/routes/srcs/in/column/preload/details/1910.js new file mode 100644 index 000000000..09b14a80d --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1910.js @@ -0,0 +1,117 @@ +module.exports = { + title: ['2016級 林奕辰 (Bravo AI 洽吧智能執行長)', '2014級 沈昇勳 (Bravo AI 洽吧智能技術長)'], + hashtags: [ + '系友專訪', + '林奕辰', + '沈昇勳', + '2016級', + '2014級', + 'BravoAI', + '洽吧智能', + 'MachineLearning', + '核保理賠自動化', + '電腦視覺', + '自然語言處理', + '深度學習', + ], + sections: [ + { + bigtitle: '一、對大學的反思與日後影響', + sections: [ + { + title: '回頭看大學經歷與對於未來的影響', + section: + '現在回頭來看,覺得當時可能會很熱血於某一件事情上,但老實說會反思那時能夠以更有效率的方式如何做得比當時更好。現在來看也會比較注重大家對於一個企劃的在意程度,來決定是否花時間在這件事上。不過,參加各式各樣的活動也使我能夠有機會與不同思維的人合作,每個人都會有自己擅長的事情,比如說寫文章、技術層面、人際溝通等。在工作時,會遇到更多有不同個性與思維的人,大學時這些經歷在我工作上很有幫助。', + }, + { + title: '大學時期實習的建議', + section: + '我覺得這是個人的選擇,在我們的年代,實習並不是那麼流行,因此只有一部分同學會有這個經驗。但我認為,當年紀越來越大,會發現自己的暑假越來越少,因此其實如果能在年輕的時候把握暑假長達兩個月的假期,去做些不一樣的事情,我覺得是很棒的一件事。當然實習也很好,但你同時犧牲了一個假期,就看你想要過什麼樣的生活。', + }, + { + title: '培養成具有多元能力的T型人才', + section: + '我在大學時期有修像是社科院或是管理學院的課程,在這些課程中,其實會發現每個科系都有不同注重的地方,這些不同的思維也許對一個工程師來說沒有幫助。然而,在未來的工作中有時碰到的問題並不能單純只以工程思維來解決,其中可能包含了更多像是人與人之間互動的細節。我覺得跨科系的課程可以讓你對世界的看法不同,面對問題時也能夠降低出錯的機率。至於是否一定要去修課?我覺得也是一種選擇。如果你比較喜歡接觸多元面相的工作,那麼我很建議要修這些課程。當然,如果你只是想成為一個工程師,那倒可不必。我認為在電機系的我們有很多選擇籌碼,有太多機會可以選擇。因此想清楚,自己要發展成怎樣的人,不要後悔就好。', + }, + { + title: '是甚麼樣的契機開始做AI?', + section: + '沈昇勳:我是因為修了李宏毅教授的專題,當時就有接觸到像是ML之類的技術等,因此這個經驗也讓我之後工作往這個方向發展。林奕辰:我認為AI在當時是一個具有突破的技術,我不見得很喜歡它,但我認為我能夠將這個技術做得更好、能夠更滿足大眾的需求。', + }, + ], + }, + { + bigtitle: '二、讀研究所的必要性', + sections: [ + { + title: '研究所經驗對創業影響(沈昇勳)', + section: + '就讀研究所,學到的能力主要有兩個:一是獨立解決問題的能力。這是在專題研究的過程中學到的,從自己找題目、摸索、開發到解決問題,能夠不依賴他人完成一份研究是過程中能夠學到最多的部分,而論文反而只是一種形式。二是組織已知並表達的能力。研究所學生通常身兼TA的工作,或是帶學弟妹做專題,經由從事這些工作,能訓練整合知識的能力。教學的過程,也能增進表達這些知識的能力。', + }, + { + title: '大學畢業後直接創業的心境(林奕辰)', + section: + '對於當下的我來說,選擇創業,並不是一個會失去什麼的決定,如果公司運營的不順利,那即便再申請一次碩士,我也能申請到。以整個職涯來看,早一年讀或晚一年讀碩士其實影響不大。也因為對創業有些想法,而對當下的我而言,創業是一個機會成本趨近於零的選擇。想不到明確不能做的原因,那就試試看吧。', + }, + ], + }, + { + bigtitle: '三、Bravo AI與自動理賠', + sections: [ + { + title: '從聊天機器人變成核保理賠自動化', + section: + '當初Bravo AI的第一個產品是聊天機器人,因為聽到報章雜誌說這個領域很紅,便決定試著做做看。做了之後才發現其實沒有太多利潤。聊天機器人在技術層面上有太多東西要去調控,也不確定在花了這麼多成本後會增加多少產品價值。相反的,把原本以人力去評估是否該核保的工作交給AI來做,很明確地可以知道省下多少人力,也就知道這個產品的價值。', + }, + { + title: '新創公司的挑戰', + section: + '做新東西的挑戰就是不知道構想產品的可行性。以通路為例,貿易公司需要擔心的大多不是產品本身好不好,而是銷售手段夠不夠精緻;新創軟體公司擔心的則大多是產品究竟做不做得出來,而且沒有先行者可以當作範本,很多細節都要一步一步去試,但一旦做出來很可能變成為該領域業界的領頭羊。不同類型的公司都會有各自的難處,而新創公司就是一旦搶下市占那就成功了,但在沒有穩定現金流錢就是虧錢的一天。', + }, + { + title: '參與新創公司所需特質', + section: + '在有限的時間把事情完成,也就是要有快速解決問題的能力。不一定要是最佳解,但很多時候就像奧卡姆剃刀(簡約法則)所說,最簡單的解通常就是最佳解。在新創公司這樣的要求尤其重要,每個產品都有他的生命週期。每晚一天完成就是可以產生利潤的時間就少一天。所以能在倒數計時結束前完成任務對前期的新創公司非常重要。', + }, + { + title: '讓機器取代人力,Bravo AI 扮演的角色', + section: + 'Bravo AI 主要做的就是AI 在保險方面的應用,最經典的案例就是自動理賠。傳統上這些工作都是人在處理,但是人在處理這些問題的時候可能會有一些瑕疵,例如對醫療知識的不足可能會造成判定錯誤,而且人處理問題的速度也比較慢。如果整個流程自動化就能有系統地解決很大一部份的問題,也可以大幅度地壓縮處理的時間。', + }, + { + title: '機器學習發展到了極限?從學界走到業界', + section: + '如果你看那些影像處理和辨識的論文,每個都達到了非常高的準確率,你或許會覺得這些領域已經進展到了極致,但這代表這樣的技術已經成熟了嗎?如果是的話,為甚麼市面上很少有應用這些技術的產品呢?我想這是學界與業界觀點的不同。大多數的論文只需要追求精準度的提升。但在應用的時候其實還有很多問題要解決,做為一個應用工程師,我覺得你要做的是把那些在學界看起來已經成熟的技術,讓它們可以在實際情境當中被使用。當你準備實際應用在應用端的時候,可能會出現其他的問題,比方說你要讓業界的人知道要如何使用學界有什麼新知,這是一個很大的溝通門檻;或是在應用端會做很多學界不會做的考量,比方說成本、圖片大小與處理速度等,所以我想這都還有很大的發展空間。', + }, + { + title: '機器學習的未來,走出一條別人不會走的路', + section: + '觀察那些美國的成功案例,像是siri。他們通常都有很大量的資料,而且他們做這樣的產品有很明確的目的,就是這些東西可以被實際應用在他們的其他產品上。對台灣的公司來說,最大的挑戰便是:要如何以少量的資料做出有價值的東西。當然,從資料的角度來看,我們不一定能跟Facebook 或Google 這類擁有大量資料的公司競爭,但是實際上還有很多應用的場域是沒有人做的,比方說AI 在製造業與金融業的應用。有很多其他的產業會需要AI,如果你想做AI,可以往這些方向去發展。就好比美國很多大公司都在發展自駕車,但是長時間發展自駕車需要大量的資金資源,這在台灣不一定有辦法做到。但是如果往其他破碎、封閉的應用場景,我想在台灣還是有一些機會的。', + }, + ], + }, + { + bigtitle: '四、未來規劃與給大家的建議', + sections: [ + { + title: '在未來事業中會繼續走AI或嘗試不一樣的領域?長期的目標是?', + section: + '畢竟這間公司其實是在做保險相關的應用,做這項工作的成就感是來自於如何去解決問題,而不是著重在使用什麼工具,可以解決問題的方式就是好方式,所以不會特別執著在使用AI。所以如果說會不會從AI轉換成其他領域,其實這天天都在做,但如果是說會不會從保險業轉換其他跑道也許是可能的,只不過應該還是相近的領域例如金融業銀行業等等。而公司的長期目標當然是穩定賺錢,然後思考如何作出一個可以永續經營的東西。', + }, + { + title: '訂定人生目標及規劃人生的建議', + section: + '當過了一段時間回頭檢視自己的時候,能不能知道自己在做什麼或要什麼是非常重要的事。雖然思考未來的方向可能受限於自己對於社會的無知,而不會是一個成熟的想法,也會因為自己的無知,而感到恐懼,但追求看似自己想要的模糊的目標,才有可能真的完成這樣的目標,也才有機會去審視自己的下一步。千萬不要人云亦云,只追隨那些現階段當紅的產業,多一些自己的想法。當因為學校課堂、社群媒體、同儕團體之間的影響產生了一些想法,不要直接接受別人所說的。如果當下覺得什麼東西是有趣的,可以去試試看,但絕對要知道自己為什麼去做、是不是真的喜歡做這些事,而不是只是因為社會價值觀或是受到某些人的慫恿。不要因為隨著時間的推進而有壓力現在必須做什麼,就倉促的下決定。找到方向停下來好好想清楚自己要的是什麼,專注去做。即使晚了一點出發,也能走的踏實。', + }, + ], + }, + ], + + annotation: [ + '特別感謝:林奕辰、沈昇勳', + '撰寫:何俊緯、周子庭、謝承霖、吳建翰、余欣澄', + '校稿彙整:翁瑋襄、鄭謹譯', + ], + href: ['上一篇', '下一篇', '返回專欄'], + id: 'column_1910', +} diff --git a/backend/routes/srcs/in/column/preload/details/1912.js b/backend/routes/srcs/in/column/preload/details/1912.js new file mode 100644 index 000000000..96641a5ac --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/1912.js @@ -0,0 +1,98 @@ +module.exports = { + title:['2008級 鄭恆之(Technical Lead Manager @ Google Brain)'], + hashtags:['系友專訪' ,'鄭恆之' ,'2007級' ,'B93' ,'Google' ,'MachineLearning' ,'SiliconValley' ,'CMU' ,'PhD'], + sections:[ + { + bigtitle:'一、大學時期', + sections:[ + { + title:'大學中印象最深刻的經驗及對未來的影響', + section:'先從社團說起,我大學時參加台大合唱團,大二時擔任男高音聲部部長,大三開始擔任指揮。每週都要帶領約一百人的團練習,訓練到領導能力及溝通能力(鼓勵團員一起來練習、向團員傳達自己對於音樂詮釋與團練模式的想法)。比較特殊的經驗是在國家音樂廳當指揮,有了這種大規模presentation的經驗,讓我之後都不容易怯場。\n      另外,我在合唱團交到女朋友,也就是現在的老婆。從大學的時候,我們就常常討論彼此的興趣、課業,後來討論到生涯規劃,就包括去美國念博士班,雖然到美國之後相隔兩地長達五年,但是仍互相鼓勵,最後一起到加州工作、結婚,她在Facebook工作,我在Google工作。所以情場也是很重要的,鼓勵大家嘗試。\n      除了社團以外,我也參加許多系上的活動。參加過三次野台、擔任過一次電機音樂會總召,也參加過電機之夜和電機營。大學參加這些活動帶給我最大的收穫,首先是表達與溝通能力,這是在進入研究所之後,尤其是到美國進入職場之後相當重要的核心能力,但是台灣的教育常常缺乏這方面的訓練,在社團尤其是當幹部的話,較能夠訓練到這部分。有了表達與溝通能力之後,要進一步擁有領導能力,包括如何在團體中具有影響力,還有如何讓大家目標一致,一起完成一件事情。最後是創意,多嘗試不同面向的事情可以增進自己的創意。' + + }, + { + title:'決定研究主題及未來職涯方向', + section:'這跟修課及所做的專題息息相關。我當初修信號與系統時,就覺得很喜歡,這門課最神奇的就是可以把很多資訊量儲存在信號中藉此傳遞、解讀。由於修了李琳山教授的信號與系統,所以就跟著琳山教授做專題,主題是語音辨識。後來在Homer做的主題是音樂信號處理,這可以跟我對音樂的興趣相結合,把研究與自己的興趣相結合,會讓研究比較具有獨特性,因為別人不一定想做這主題,而且做自己喜歡的事總是較有動力。\n      在Homer的時候,我曾跟楊奕軒學長合作過,他幫助我很多,讓我學到,獨立做研究和做已經設計好的實驗非常的不同。那時候常常做一些未知的題目,像是一開始做和弦辨識,這跟我當時喜歡彈吉他有關,和弦影響一首歌給人的感覺,所以我想要從這個小題目做起,也是那時接觸到machine learning ,覺得很喜歡,我對人類如何接收訊息並學習,然後教機器學習非常有興趣,所以就愈來愈往那方向走。' + } + ] + }, + { + bigtitle:'二、博士時期', + sections: + [ + { + title:'美國與台灣的文化衝擊', + section:'美國相對台灣而言對於師生之間的輩分差異不會太過拘泥,以課堂演講為例:當學生已經將教室的空位坐滿後,即使是 60 歲以上高齡的資深教授晚到也必須和其他遲來的學生一起席地而坐,雖然心中有想要讓座的想法,不過在觀察週遭同學和教授的反應後,發覺在美國先來後到的觀念還是大於師生間的輩分關係。模糊的輩分關係更讓我之後在與教授和公司上級交流時,以平等的地位溝通,更能直截了當的表達自己的想法,不需要特別顧慮輩分而保留心中想說的話。尤其在職場上,清楚、簡短而且明確的表達想法,對於自己在公司決策的影響力非常重要。' + }, + { + title:'業界和學界的研究內容差異', + section:'兩者在於研究方法是相同的,最主要的差異在於「目標的定義」和「對於成功的衡量標準」,因為這兩項的不同會造成最佳化的方向不同。\n      學界主要重視在領域的創新程度,而業界以 Google 為例,最重要的是研究的結果對於產品是否具有決定性的影響力。我自己的專業領域是機器學習,有些學界研究可能會以增加模型的複雜度,來達到某些程度的創新;然而在業界會同時實用性,若複雜模型需要耗費過多資源和時間來維持運作,那相對於簡單的模型就不會是好選擇。\n      總而言之,在不同地方研究會受到目標定義而依循不同的研究方向和方法。' + } + ] + }, + { + bigtitle:'三、工作時期', + sections: + [ + { + title:'留在美國工作的決定', + section:'對於是否留在美國其實不斷的改變,大學的時候不清楚美國的狀況,很難去做規劃,因而沒有預設。後來才看出要做Machine Learning、Research等在美國機會很多,相較而言在台灣蠻缺乏的。雖然台灣有很優秀的Startup從事Machine Learning,但真的與矽谷難以比擬。是否留在美國還是要看領域,當時大學沒有事先規劃,但接觸越多,知道產業狀態,也就能給自己目標與做決定。\n      另外,在美國工作,要確保拿到簽證,主要是自身能力的特殊性。一個國家給外國人工作簽證,通常是因為他的能力本國人難以取代,有需求去發簽證雇用他。像在美國STEM,Science, Technology, Engineering and Math,這種理工科人才會在學生簽證後給比較長的實習時間,申請到的工作簽證機率也比較高。另外薪水超過一定門檻的工作拿到的機會也較高,因為薪水高大致上代表在當地就業市場的價值高,意即這樣的人才比較難尋。未來像是入門程式設計這類越來越多人學習的技能,自己可以貢獻的特殊性可能會越來越小,所以最好除了程式外,還要有其他領域的專業會更好。' + }, + { + title:'進入 Google 的契機和工作內容', + section:'一開始我是藉由實習得到轉正職的機會,並且參與 Google Brain 的研究計畫。工作內容分成主要分三個階段,第一階段在 2014 年,此時在建構 Large-Scale Machine Learning Platform,實際內容涵蓋 Data Pipeline、利用分散式系統建立快速訓練的平台、利用平行化的技巧做快速的預測和推論。\n      第二階段是從 2015 年到 2017 年,此時將 Deep Learning 導入原先沒有使用機器學習的產品上,例如 Google Search、Google Play、Google System 和 Youtube。除此之外還有從自己的專案「Wide & Deep Learning」開始研究,後來有實際應用在產品上,最終對外界開源 Tensorflow 1.0,被其他學術單位和公司所使用。這個時期讓我發現在 Google 內部做的研究,事實上不只能影響公司產品也能遍及全世界。\n      第三階段主要在做「Neural Sequential Interaction Modeling」,也就是透過一連串使用者和系統的互動來學習,例如對話系統、Google Duplex Assistant。另外,雖然很多產品都開始導入 end-to-end 的學習方法,但考量到 Rule-based method 能精準控制的特性,仍會採用能確保特定嚴重錯誤不會在系統中發生,整體而言是雜揉 Deep Learning 和 Rule-based method 的混合系統。' + }, + { + title:'Google技術突破的浪潮發展方向', + section:'近年發展的 Deep Learning 能從 Data 裡面自動抽取出很好的 high level feature,做 Perception 的任務作得特別好,而接下來的挑戰是從這些feature當中去做reasoning,如:從對話中抽出feature後去回答問題。由於reasoning是需要common sense的,亦即對世界有基本瞭解(如:影像需符合物理規則,人說的話必須make sense),知道什麼是合理/不合理,這如果能藉由人類給的knowledge base/rule直接學習,而非只能從大量data輸入輸出學到,可能會有比較大的突破。\n      另外像是自然語言處理(NLP)還有很多挑戰,它與電腦視覺(CV)的其中一項差異是,因為影像pixel的分布是自然界生成的規則,其實動物也有辨識能力。反之語言則是人類發明特有的,造成了人類與動物的差異,所以教機器學語言可能是更複雜、更艱難的任務。' + }, + { + title:'進入Google Brain等研究機構所需要的能力與特質', + section:'我們重視的能力特質有technical跟non-technical的部分:\n      Technical的部分,研究經驗跟在業界的應用經驗都會加分,除了會做研究(有publication),也需要知道業界的研究方向要如何調整,才會對產品有好的實際幫助與影響力,當然寫程式、軟體工程的基本能力也是非常重要。\n      Non-technical第一個看的是,一個人是否有成長型思維(growth mindset),它的相反固定型思維(fixed mindset)是覺得天賦已既定,是天生被給予的,也因此只作自己擅長的,不作自己不擅長的。growth mindset則認為任何事情都是學得來的,與先天無關,無論是硬實力或軟實力。這方面可以看一個人一路走來的軌跡,有沒有一直在進步,進步的速率如何。有恆毅力,長期認真做事、成長,與未來的表現常常是有正相關,我們也會傾向優先考慮有這樣特質的人。\n      第二是動力,通常我們會問說面試者接下來自己的目標,越是能能說出目標及明確原因的人,通常會比較有動力,有內在動力自己去前進,想要進步成長,最終的表現也常常會更好。\n      另外溝通表達能力還有領導力也很重要,比較會錄取傾向collaboration跟team work,而非單打獨鬥的人。' + }, + { + title:'近年Deep Learning走紅,Data Scientist職位變多,盛況在未來會不會有所飽和?不如碼農般穩定?', + section:'我的看法是相反,research會一直有需求,但是research的內容會一直在演進,所以必須要適應力很強,有調整很快的心理準備。例如在過去幾年,光是會調deep learning model參數,可能就可以滿足工作需求,但是現在有AutoML等技術出現,可以自動優化model並且取代了調參數的人力,所以做研究的人就必須繼續往前做更難的研究。譬如AutoML雖然在supervised task上表現好,但如果是剛剛所提到需要機器去reasoning的task,還有更多問題需要解決,也需要更多人去研究。\n      而碼農缺,可能不像大家想的有固定需求,例如現在有研究領域是作 Program Synthesis,從 Pseudocode 成出 code 等等,用來輔助、加快並取代code中重複的部分,當程式生成越來越成功,其實碼農缺就會減少了,而研究會一直會有需求。碼農這個詞也暗指了不需要過多思考,只要專心老實的寫code,自然取代性比較高。機器可以取代人類反應很快就能處理的任務,因此鼓勵大家立志往比較深入的研究去走,會比較長遠。' + } + ] + }, + { + bigtitle:'四、未來規劃', + sections: + [ + { + title:'規劃職涯的方式', + section:'我在職涯規劃上,基本上大學申請研究所的時候思考一次,研究所快畢業的時候也思考一次。到了工作之後,幾乎每一兩個月都會跟主管以及員工討論在職涯上的規劃。\n      我覺得有幫助的是常常問自己喜歡的是什麼,很常做選擇,很常逼自己把原因寫下來。我個人在做重要決定時,會去想決定的核心價值,通常都有優缺點,每個選擇都有好壞,最後的選擇還是與價值觀有關,訓練自己嚴格的排序並說出為什麼,就能較輕易的決定。根據當下有的資訊做出最好的判斷,自己也不會猶豫、後悔,只會全心投入。\n      此外,我覺得要找一個終極的目標,我的目標就是期許自己能夠在人工智慧這個研究領域成為一個成功的領導人,以人工智慧影響其他不同的產業,並且讓這些產業產生重大、正向的改變。我希望人類在未來能夠與人工智慧有效合作,提高創造力與生產力。同時,我也希望我能成為一個有遠見以及有影響力的人。由於自己對於舞台表演藝術的熱愛,我也喜歡將自己的想法傳達給觀眾,因此我也想要透過寫書或演講的方式,來影響更多人,也希望能夠幫助他們做出正向的改變。\n      我現階段的規劃是希望能從帶領一個實驗室,成長到帶領某個領域之下的多個實驗室,並且影響多這項重大產品事業的走向。如果有好的機會的話,在未來嘗試創業也是有可能的。' + }, + { + title:'職涯發展迅速的關鍵', + section:'我認為溝通表達能力是一件很重要的事情。臺灣的理工學生,技術面都已經很強了,但是在口頭報告上,卻沒有辦法完整地將自己所知道的內容表達出來。練習口頭表達也會讓你變得比較不怕去爭取機會。在職場上,不要放過任何機會,並且每次都以高品質建立個人品牌,這樣別人也比較容易將任務交給你完成;而每當你完成一項任務,就會再有新的工作機會出現,而每次你都全力以赴,做好自己的工作並且能整地表達,我認為這是一件很重要的事情。' + } + ] + + }, + { + bigtitle:'五、給學弟妹的建議', + sections: + [ + { + title:'及早建立個人品牌', + section:'建立起自己的品牌,找到屬於自己獨特的主題(T型人才的長腳,也就是自己的專長),然後持續經營,有助於在申請博士或未來申請工作的時候,在眾多申請者中脱穎而出。像我當初就覺得我的主題與多媒體信號的處理和理解有關,所以大三、大四就開始經營這方面,修了很多語音、影像辨識等等相關的課,也做了專題,所以在這方面累積了很多經驗,對未來申請幫助很大。申請博士班的時候,有各種不同主題的實驗室供我們選擇,雖然不一定找得到跟自己最初主題完全一樣的實驗室,但是提早經營自己的主題還是可以幫助我們找到主題最接近興趣的實驗室。' + }, + { + title:'大學課業很繁重,學長鼓勵學弟妹一定要嘗試的事情', + section:'我很鼓勵大家可以多嘗試,去社團訓練領導力與創意等。另外,我認為我在大學時比較沒有做,但鼓勵大家去嘗試的,是實習。我認為可以多嘗試不同的實習,尤其是要出國的學弟妹們,可以先考慮在臺灣有實習的工作經驗,先行體驗臺灣的工作環境與工作文化,再到國外實習並且學習不同的環境與文),這樣比較能夠知道各個不同工作的優缺點。這有點像是蒐集 Data 來 Train 自己的感覺,否則如果沒有足夠的經驗,很容易產生盲點而影響未來的發展。' + }, + { + title:'畢業後需掌握能力或特質', + section:'數學基礎很重要,如果是做研究,後面很多都是基本數學基礎再去學習。還有研究能力,也就是去找新問題解的能力,因為大部分課程都是在教解問題,這也是工程師必備的,但找值得解的問題就進入到研究,而這可以在專題研究學到。\n      高EQ,樂觀積極的態度,因為一定會有很多挫折,不要讓自己進入負面循環。高EQ對領導力也很重要,首先要先察覺自己的情緒,將自己維持在能保持高生產力的情緒狀態,進一步要能察覺團體的情緒。' + } + ] + } + + ], + + + annotation:['特別感謝:鄭恆之','撰寫:俞建琁、王廷峻、莊永松、吳建翰、鄭謹譯、李筠婕','校稿彙整:李筠婕'], + id:'column_1912' +} \ No newline at end of file diff --git a/backend/routes/srcs/in/column/preload/details/2001.js b/backend/routes/srcs/in/column/preload/details/2001.js new file mode 100644 index 000000000..307473725 --- /dev/null +++ b/backend/routes/srcs/in/column/preload/details/2001.js @@ -0,0 +1,144 @@ +module.exports = { + title: ['1999級 簡韶逸 (CEO/ Founder @ Ganzin Technology, Prof. @ NTUEE)'], + hashtags: ['系友專訪', '簡韶逸', '1999級', 'B95', '台大電機系教授', 'Ganzin', 'NTUEE', 'PhD'], + sections: [ + { + bigtitle: '一、前言', + sections: [ + { + title: '前言', + section: + '此次我們前往Ganzin(見臻科技)位於新店的辦公室採訪其創辦人簡韶逸教授。簡韶逸教授專精於多媒體訊號處理系統、多媒體積體電路設計與晶片系統設計方法研究,於2018時創辦的Ganzin從台灣大學spin off。自此,簡韶逸教授身兼Ganzin的執行長。Ganzin 致力於開發可以與AR/VR模組整合的眼動追蹤模組(eye tracking solution)。Ganzin 名稱取自台語「眼神」一詞的諧音,亦有英文「凝視」(Gaze) 之意。而其主要產品 — 眼動追蹤模組Aurora,在2020 年的CES 科技大展一舉奪下Innovation Award,其微小、高續航力與易整合性受到各界矚目。我們採訪簡韶逸教授從求學、任職台大教授,到創辦Ganzin的過程,一窺捕捉眼神的奧秘。', + }, + ], + }, + { + bigtitle: '二、求學階段', + sections: [ + { + title: '從大學專題中得到的啟發', + section: + '簡韶逸教授對於研究的熱情緣起於大學時期的專題研究。他大學時加入陳良基教授的實驗室進行專題研究。他對影像有熱忱,陳良基教授的專長剛好與影像處理相關,於是開啟兩人往後多年深厚的師生緣分。簡韶逸教授回憶起大學專題研究的時光,他認為大四這段做研究的經驗甚至比後來讀研究所時還要更美好。由於大四時課業壓力減輕,於是他幾乎將全副精力投入在研究上。關於這段專注於研究的經驗,他分享道:「心理學上有一個狀態:人心無旁騖專注在做一件事情時,會獲得特別多的快樂。我覺得那時候比較像這種狀態。」對於大學部學生參與專題研究,簡韶逸教授認為這是有別於課堂被動吸收知識的重要學習過程。他認為大學生在修課時大部分時候都是被動吸收老師教的內容。而專題研究則是第一次向未知的領域探索,因此他非常鼓勵台大電機大學部的學生參與專題研究。簡韶逸教授自認十分幸運,在大學時期便體會到做研究的樂趣,並且在大四時受到學長鼓勵投稿了一篇論文,獲得在會議裡發表研究的機會。也因為這段美好的經驗,使他大學畢業後便決定繼續跟隨陳良基教授讀碩士。', + }, + { + title: '決定研究主題並思索未來職涯方向', + section: + '初讀碩士時,簡韶逸教授對於自己想做的研究方向已有明確框架。但他考量到自己的研究願景似乎太大,於是他便決定跟隨陳良基教授直攻博士完成此研究。他回憶當時做此決定時台灣電子業的情景:「那是台灣電子業蠻好的時候,那時候聯發科才剛要起來。我本來想說念完碩士就要去園區工作。」但沒想到後來體會到研究的樂趣無窮,於是便一頭栽進博士生涯。簡韶逸教授三年便拿到博士學位,以當時的環境畢業即就獲得教職的機會微乎其微,於是他當時心中並沒有立刻回學校教書的想法,而是決定要投入業界。', + }, + ], + }, + { + bigtitle: '三、工作時期', + sections: [ + { + title: '邁入業界—於廣達電腦的一年', + section: + '簡韶逸教授於2003年至2004年任職於廣達電腦。我們好奇為何簡韶逸教授選擇到廣達電腦呢?簡韶逸教授談到當時他有許多選擇,而廣達電腦吸引他的最大原因是— 當時仍是代工廠的廣達要進行一項具野心的計畫 — 開發手機晶片。簡韶逸教授說:「我是一個蠻喜歡挑戰的人,廣達吸引我的原因就是因為他要做一個蠻有挑戰性的東西。雖然那個晶片計畫做的內容跟我的博士研究是完全無關的,但我想說沒關係,我就嘗試。」簡韶逸教授和另一位學弟一起領導這個手機晶片團隊。這一年他們頗有進展,成功設計出了晶片。他回憶當時使他快速成長的除了熟悉晶片開發經驗,更是領導團隊的能力。他回憶道:「很多人去到大公司變成一個螺絲釘,但我去廣達是帶領一個團隊,我的老闆就是協理(英文是AVP),在廣達電腦裡算很大的。跟我們來往的都是個公司的副總、技術長。」', + }, + { + title: '回校擔任教職', + section: + '至於為何後來又重回校園擔任教職呢?簡韶逸教授表示在學校做研究與在業界仍有很大的落差。他坦言:「跟研究相比,業界沈悶許多。」在業界,他們面對的是產品,因此他們必須花大量時間debug,創新的部分因而變得很少。當時台大剛好有一個教職機會,於是他申請了此機會,沒想到錄取了,因此得以重返校園與研究生活。但綜觀在廣達電腦的這一年四個月的時間,簡韶逸教授仍覺得這段經驗對他影響深遠。由於這一年的工作經驗,他得以完整地了解晶片專案從頭至尾的開發流程,也讓他認知到學校的資訊與產業界相比有些落後。當時產業界多已開始做系統晶片(System on Chip,簡稱SoC),但學校當時仍沒有相關課程。於是他利用在廣達電腦的那一年吸收做SoC的相關知識,後來他一回學校教書便開設相關課程給學生修習。', + }, + ], + }, + { + bigtitle: '四、創業', + sections: [ + { + title: '不忘指導教授的教誨,埋下創業的種子', + section: + ' 談到恩師陳良基教授對他的影響,簡韶逸教授笑道:「我們長期被他洗腦台大電機的學生應該創業,create工作機會。」因此,他同門師兄弟許多人後來紛紛創業,創業彷彿便埋在他們心中一件「一定要做的事情」。擔任教職後,簡韶逸亦不忘傳承陳良基教授的諄諄教誨,他笑著說:「我也把他洗腦的方式學起來。我也一直告訴學生要去創業。結果沒有一個學生真的去創業,畢業以後都去大公司了!」 他十分疑惑為何自己的學生們都不選擇創業。於是他開始思考是不是從自己開始,帶領學生一起出來創業。後來,同事葉丙成教授的創業,也給了他很大的震撼。於是這股要帶著學生一起創業的決心漸漸萌芽茁壯。因緣際會之下,簡韶逸教授看到了由經濟部推動產學合作的「價值創造計畫」(簡稱價創計畫)。價創計畫由政府補助教授將優秀的研究成果朝向商品化、事業化。讓申請的教授有一段時間在政府的補助下將技術轉變成產品,再尋找外部投資人。於是他申請了價創計畫,也成功得到計畫的補助,總共補助了兩千萬元。這時,他向學生提出一起創業的想法。他說:「想不到他們(學生)就答應了!」', + }, + { + title: '捕捉眼神 – 見臻科技的創立', + section: + '於是簡韶逸教授和十多位學生一起創辦了見臻科技(Ganzin), 主攻的核心產品— 眼動追蹤(eye tracking solution),就是從簡韶逸教授的實驗室裡開發出來的技術。我們好奇當初為何會挑選眼動追蹤為創業題目呢?簡韶逸教授答道:「當然我們實驗室其實做了很多技術,老實說眼動追蹤不是我們實驗室最酷炫的技術,我們還有更厲害的。但我們在提價創計畫時的proposal的時候要寫一個business plan。當你在想這東西如何商品化時,你要開始去估這個東西的價值主要是什麼,他的business model 是什麼。他要怎麼可以到市場上面去。你開始想這些問題時你會發現很多技術是沒有辦法商品化的,或者是他的門檻非常高。眼動追蹤就是我們覺得好像商品化的門檻是最低的。而且我們後來也發現eye tracking 是很多場域都需要的,所以我們可以估出一個蠻大的市場。」而Ganzin採用的商業模式是台灣最常見的,也最成熟的模式 — 做出關鍵零組件 (key component )或關鍵解決方案(solution),然後提供給系統廠。Ganzin的客戶,便是製造智慧眼鏡、VR眼鏡的系統廠。這些眼鏡,就需要眼動追蹤技術。據簡韶逸教授的觀察,因為台灣的晶片設計與製造的技術優勢和地緣優勢,這些系統大廠要做產品時多會來台灣,所以在台灣做key component或solution是相對簡單,甚至是全世界最簡單的地方。於是他認為:「我們的出海口很明確。」另外,當時眼動追蹤在市面上的敵人不多。簡韶逸談到原因:「因為很多做eye tracking的公司都被買光了,有一陣子只要eye tracking的公司一出來就被買走了。比如Google買了一間,Facebook 買了一間,Apple 也買了一間。」而Ganzin主要的對手是瑞典的公司Tobi。簡韶逸創業前曾評估:「我當時覺得我們應該可以打的過他。」種種因素,促成眼動追蹤成了當時創業題目的首選,是簡韶逸教授認為要創業最簡單的一個題目。但他笑道:「後來發現,最簡單其實還是蠻困難。比如說瑞典公司Tobi,我們後來發現他其實是一個超大的公司,全球員工有好幾千人。我們發現眼鏡其實也蠻難做的,很多東西其實當時也想不到,都是想起來很簡單,做起來很難。', + }, + { + title: '眼動追蹤應用遍地開花', + section: + '簡韶逸教授提到,現階段已經有許多含有眼動追蹤技術的產品,但都是小量的特定的人在使用,還未普及,所以大眾對眼動追蹤的應用仍然十分陌生。關於已經在使用的應用,他提起:「眼動追蹤其中一個應用『眼睛寫書』,這項應用的使用者就有知名霍金博士。Intel有一個團隊專門幫他打造整個系統,但只給他一個人用。」另外,已有少數人使用的例子還有市調用眼鏡,消費者戴上之後,走進一個賣場,眼動追蹤技術可以偵測消費者在賣場裡看了哪些商品。但簡韶逸教授也說:「但是那個眼鏡一支要賣100萬。」於是簡韶逸教授希望眼動追蹤的技術將來可以推廣到大眾身上,以更便宜的價格,讓眼動追蹤可以顛覆使用者與產品的互動體驗。', + }, + { + title: 'AI 眼鏡的互動介面與智慧家庭助理', + section: + '談起眼動追蹤未來如何走入大眾視野。第一最直覺的應用就是AI眼鏡的互動介面。未來的智慧眼鏡,人的視野中會浮現一張選單,可以利用眼睛去點選選單(比如凝視久一點的應用程式會自動打開)。第二個應用可以呈現未來智慧家庭的可能樣貌。簡韶逸教授說:「假設你回到家,你想開一盞燈,現在不是大家家裡都有語音助理嗎。但你家可能有20盞燈,你怎麼讓語音助理知道你要開哪一盞,這樣你要幫每一盞燈都取名字,太麻煩了。要是你有帶eye tracking的話,你就只要看這個燈,說:『幫我開那盞燈。』」簡韶逸教授又接著笑說:「這個我有時候跟一些女生講他們就會開始想……,我舉一例子,比如你走在路上看到有一個人穿的鞋子很漂亮,你眼睛看著那雙鞋子,你可以直接問:『這雙鞋子多少錢?在哪裡能買到?』照相機馬上拍下來,上網search,幫你做虹膜辨識就直接付款了。或是走在路上看到一家餐廳,眼睛看著餐廳問:『這家餐廳好不好吃?』眼鏡直接告訴你幾顆星評價如何。」簡韶逸教授表示現階段,眼動追蹤可能產生的互動方式是我們現在想像不到的,但這些互動模式並非很遙遠。比如他們已經在許多科技展場實際demo 利用眼睛直視打開電燈。', + }, + { + title: '虛擬實境', + section: + '另外,在虛擬實境(Virtual Reality, 簡稱VR)中,為了讓使用者看到的世界越來越逼真,display的解析度會越來越高的,於是使用者要買高檔的顯示卡,才能畫得出逼真美麗的影像。簡韶逸說:「但人的眼睛不是這樣子的,人的眼睛在一個瞬間,只有一個點是清楚的,你是靠著眼睛不斷轉來轉去才把世界建起來的。所以有一個paper 告訴我們說,老實講VR rendering 的動作98%都是浪費的。畫了半天人根本沒有看到,人只看到其中2%。」而現在VR中有一個技術叫Foveated rendering,利用eye tracking告訴系統使用者在看什麼地方,系統只要畫將這個地方畫清楚就好了。簡韶逸說:「我們自己有做實驗啦,至少省十倍運算,十倍是很可怕的數字噢,多玩十倍的時間,或是你不用買那麼貴的顯卡,你買便宜的顯卡,甚至手機就可以玩很棒的VR 遊戲。」', + }, + { + title: '虛擬社交網路', + section: + '最後一個特別的應用是虛擬社交網路(VR social network)。藉由VR social network,人們可以在虛擬世界裡建立聯繫。簡韶逸教授介紹道:「他的概念是說以後大家回家之後就可以帶上一個VR 頭盔,就進到一個虛擬世界,你會有一個avatar(虛擬人),他代表了你。你可以在這個虛擬世界裡跟其他朋友互動,可以一起去看球賽。」他談到特別是現今武漢肺炎疫情爆發的緣故,VR social network 的價值將更受重視。但這跟眼動追蹤的關係是什麼呢?簡韶逸教授答道:「在虛擬世界,這個虛擬人avatar的眼睛要是不會動,他不就是一個殭屍嗎?所以VR 頭盔裡一定要有eye tracking,他才可以拍攝我們的眼睛,把我們的眼動資訊傳遞到avatar上面。」他表示,當人跟數位世界緊密結合時,必須要把眼睛的資訊捕捉下來,傳到數位世界,才能使人跟數位世界融合在一起。他說:「所以眼睛真的是靈魂之窗!Facebook 的 chief researcher 甚至說:『eye tracking 就是VR 的核心!』」', + }, + { + title: '創業的荊棘之路----從學界與產業界', + section: + '談起創業面臨的挑戰。簡韶逸教授回憶道他剛創業時感受到產業界與學術界的巨大鴻溝。談起學術研究,簡韶逸教授的研究成果不只受到學界肯定,屢屢在國際比賽得到 best paper award,產業界亦重視他的研究成果。曾經一位Sony的一個RD總監級的人物對簡韶逸教授說,他常常在看簡韶逸教授的論文,他覺得的簡韶逸教授的實驗室是全亞洲最好的multimedia IC 的實驗室。類似的肯定讓簡韶逸教授對自己實驗室的研究成果有信心。但當他審視自己的實驗室的技術如何商品化時,他卻發現這些技術離產品差很遠。以eye tracking 為例,他說:「我們就是幾個同學試過覺得好像可以用而且performance 還不錯我們就覺得沒問題了。可是真的出來創業的時候我們就發現這個東西要變成產品其實距離蠻遙遠的。比如他們(客戶)就會開始問說那其他人(比如外國人)可不可以用?所以我們有一個lab 在測試大量的受試者,我們開始去搜集很多人的資料,以確保我們產品是每個人都可以用的。光這些事情就要花非常多的錢跟時間。」', + }, + { + title: '面對客戶的需求', + section: + '簡韶逸教授以技術背景的角度出發,與客戶接觸時,也常發現客戶與自己想的不一樣。以Ganzin 的產品Aurora為例 — 這款屢屢在科技大展得獎的眼動追蹤模組,是由三個晶片構成。其中兩個是眼睛感測器(eye sensor)、一個是眼睛處理單元 (eye-processing unit, 簡稱EPU)。對於EPU,簡韶逸教授說:「你可以把它想成computer vision 或AI晶片。他會直接處理左右拍到的兩張影像,算出你的視線方向。」Aurora之所以會造成轟動是因為之前市面上並沒有這麼小,這麼省電,這麼好安裝的眼動儀。一般眼動儀複雜而且昂貴,要價一百萬。但當他們拿著產品 eye tracking solution問客戶要不要買時,客戶說這樣他們無法決定,能不能給他們一些working sample?客戶想要的一個馬上可以動的東西測試。於是Ganzin團隊又做了眼鏡實際demo eye tracking的範例 。接著客戶又問說:「那可不可以讓我的眼鏡也可以很快的可以裝入eye tracking 的功能?」於是Ganzin 將Aurora 改良為一個中央EPU 連接兩條線串起的eye sensor 的輕便模組,方便客戶直接整合進他們的產品。這時客戶終於說:「這就對了!我們就是想要這個!」於是許多客戶開始紛紛跟Ganzin購買眼動追蹤模組測試。簡韶逸教授笑說:「現在客戶都很懶,你要提供全部的東西,包括driver、SDK、一些example的software。整套都要給。但我們現在也慢慢理解到他為什麼要這些東西,因為他要有一個方式去驗證你的東西有多好!」', + }, + { + title: '產品化的挑戰', + section: + '談起創業面臨的挑戰的過程與心得,簡韶逸說:「很多是商業面,還有實務上的問題,這其實都不是學術界會去想的。所以從我們技術完成,到經過價創計畫,到真的開始東西可以給客戶,其實已經三年了吧。過了三年之後東西才真的交到客戶手上。」而拿這段經驗與他在廣達的業界經驗相比較,當時在廣達令他感到沈悶與無聊的產品化過程,這時在Ganzin卻讓他感到非常珍貴。他認為業界累積的實戰經驗主要就是如何將好的技術產品化。他說:「隨時都有好的技術,但是什麼技術可以變成產品? 這個就有很多的關卡要過。」Ganzin的產品目前還未量產,但簡韶逸教授有信心明年可以量產。他說:「如果明年真的成功量產的話,表示我們大概花了四年的時間,才讓他可以從技術變成一個大家都可以用到的產品。希望明年大家就可以買到我們的東西了。將來可能會有一些智慧眼鏡,上面會有顯示器,會有我們的眼球追蹤技術,使用者可以用眼睛去控制東西。」', + }, + { + title: 'Ganzin 的競爭對手— 瑞典公司Tobi', + section: + 'Ganzin 最主要的競爭對手是瑞典公司Tobi。談到競爭對手Tobi,簡韶逸說道:「他是一個很大的公司,我們一開始沒有意識到這件事情,我們後來才發現他比我們想像中大很多。」Tobi的營收來源大部分來自售價100萬元的眼鏡,Tobi亦有涉獵醫療市場。兩者構成其主要收入來源。而Tobi在PC 遊戲,和VR eye tracking,都是處於虧損狀態。簡韶逸教授說:「這個公司本身就有在大賺錢的東西。我們公司核心完全就是放在他不賺錢的這塊。」簡韶逸教授提到Ganzin的優勢在於,他們的方法和Tobi完全不一樣,Tobi在醫療或研究用途的眼鏡相當貴且複雜,但一般客戶需要簡單好用的東西。而Ganzin就是用簡單好用的東西切入這個市場。另外,簡韶逸教授也強調:在台灣做eye tracking 很有優勢。他重申:「台灣做晶片是全世界最簡單的地方,這些AR、VR眼鏡廠要生產眼鏡也都是來台灣,我常常跟我們同仁講:『天時地利人和。』天時,就是現在是AR、VR、5G的時代,這是一個好的時間點 ; 地利,就是因為我們在台灣,我們正好在一個很好的地點 ; 人和,可能就是靠我們的團隊。大部分都是台大電機的同學。」', + }, + { + title: '多元的團隊', + section: + 'Ganzin的團隊現在有25人,其中工程師有20位。而團隊中有一群台大畢業的新鮮人都是簡韶逸教授的學生,也有一群具有經驗的員工。簡韶逸認為這樣的搭配優點在於:台大電機系的學生十分聰明,年輕人也比較有衝勁,而年紀大的員工富有經驗。剛畢業的學生可能不知道業界做事的流程,而業界一般都有一套做計畫的方法、定進度的方法,因此年輕員工可以向有經驗的員工學習。簡韶逸教授談道:「我們公司的RD (research and development) 比較難找。」由於Ganzin的產品本身是跨領域的,必須整合演算法、晶片、電路機構等所有環節才能成為最佳化的產品。因此Ganzin的 RD 背景多元,從ic設計、演算法、system software、application software到機構等等,有各式各樣的人一個團隊當中。簡韶逸教授說道:「這是一個很神奇的現象,20個RD就有這麼多人種在裡面。很多公司不曉得我們才20幾人,他以為我們是幾百人的公司。」', + }, + { + title: 'CEO 的角色', + section: + '簡韶逸教授以水桶比喻創業:「有人跟我講一個比喻我覺得蠻好的,他說你要創業成功啊,就好像你要做一個水桶,假設你用木片做這個水桶,這個水桶可以裝多少水,其實是用最矮的木片來決定。創業也是一樣,創業有很多層面,研發只是其中的一個版子而已,一般會講一個模式就是產(生產)、銷(行銷)、人(人資)、發(研發)、財(財務),這每一塊都很重要,當執行長要隨時看這五個面向裡面哪一個是最弱的,你就趕快跳下去補。」簡韶逸教授坦言,自從當了執行長後,他開始摸索許多原本不熟悉的事物,比如與客戶接洽、面試員工、編財務報表、編預算。但他也說道:「我覺得每一塊對我來講都還蠻困難的,但是你就是必須要去做,這也會讓你成長,做完你會更全面去了解整間公司是怎麼去運作的。」創業前簡韶逸教授是台大創意創業學程的老師,他表示他自己從裡面學到許多珍貴的事情,他也常常與系上其他創業的教授互相交流。他常與黃鐘揚教授、葉丙成教授相約交換創業心得。簡韶逸教授談到一個令他印象特別深刻的建議,黃鐘揚教授曾建議他,一定要讓同仁們投資認股。簡韶逸教授初創業時約有十多位學生加入,大家分別買了一部分股票,形成一開始創立公司的資本額。簡韶逸教授回憶道:「學生們馬上表情就變了,本來他們的態度是:『老師你要我們做什麼』,成為股東後態度立刻變得積極許多,開始詢問:『我們接下來應該做什麼!』」', + }, + ], + }, + { + bigtitle: '五、未來規劃', + sections: [ + { + title: '我們好奇未來簡韶逸教授是否會將重心移回校園呢?', + section: + '簡韶逸教授表示,他現在的重心仍在Ganzin。未來倘若Ganzin能夠成功,他希望重返台大將創業成功的經驗帶回學校。他提到:「我其實蠻崇尚矽谷或美國的學校,他們的模式是讓某個老師出去創業,成功了以後他把一些經驗帶回學校,甚至他又可以再去開新的一間。」他認為這個模式是值得台灣學習的。而他也期許自己將來可以將創業成功的經驗帶回校園。', + }, + ], + }, + { + bigtitle: '六、給學弟妹的話', + sections: [ + { + title: '對台大電機學生創業的期許', + section: + '我們請教簡韶逸教授對於台大電機學生之後創業的看法與建議,簡韶逸教授認為創業最重要的條件就是韌性要夠。至於創業的時間點,他認爲大家可以自己決定,大學畢業或許有點早,也可以研究所畢業、工作一段時間再創業。他提到自己曾在MIT (麻省理工學院) 訪問時的震撼:「他們有一個教授問我一個問題:『你知道在MIT,100個學生會有幾個創業嗎?』」答案是120個,因為有些人不只創辦一間公司。簡韶逸教授說:「他們會以改變世界為使命,我覺得台大電機的學生應該也要有這個使命感。」簡韶逸教授提到創業時需要多面向地思考問題,而這會使人成為一個多功能的人。他覺得台大電機的學生應該都有能力可以扮演各種角色。他說:「你在新創可能要自己轉換角色,可能不會只是一個RD工程師,我們有些同學其實蠻厲害的,後來發現他其實是一個蠻好的PM,可以跟客戶溝通,或是發現他是很好的人資,他很會選人。所以我覺得多面向的培養蠻重要的,除了技術以外的事情。」', + }, + { + title: '生涯規劃的建議與總結', + section: + '對於生涯規劃,簡韶逸教授坦言自己想法常常改變。但他仍然以自身經驗勉勵學生:「我高中的時候就有去上生涯規劃課,我那時候聽到一個結論我覺得蠻好的,我一直記到現在。」他說:「生涯規劃就是一件你隨時都要做的事,但是隨時都有可能被你推翻掉。但不能不做,不做你就會渾渾噩噩得過下去。隨時都要想自己明年後年有什麼目標,但這些東西隨時都會變沒關係。」', + }, + ], + }, + ], + + annotation: [ + '特別感謝:簡韶逸', + '採訪:羅韻瑢、鄭謹譯 、李筠婕 、吳建翰 、余欣澄', + '撰文:羅韻瑢', + '校稿:鄭謹譯、余欣澄', + ], + id: 'column_2001', +} diff --git a/backend/routes/srcs/in/column/preload/outline_preload.js b/backend/routes/srcs/in/column/preload/outline_preload.js new file mode 100644 index 000000000..7ba8dae8a --- /dev/null +++ b/backend/routes/srcs/in/column/preload/outline_preload.js @@ -0,0 +1,240 @@ +const mongoose = require('../../../../Schemas/db') +const Column_out = require('../../../../Schemas/column_outline') +const Column = require('../../../../Schemas/column') +toInsert = [ + { + anno: ['羅韻瑢', '鄭謹譯', '李筠婕', '吳建翰', '余欣澄'], + date: '2020/03/14 星期六', + id: '2001', + title: ['1999級 簡韶逸 (CEO/ Founder @ Ganzin Technology, Prof. @ NTUEE)'], + exp: [ + '現任:CEO of Ganzin Technology', + '現任:Associate professor at GIEE (NTU)', + '曾任:Research staff in Quanta Research Institute (廣達) (2003~2004)', + ], + edu: ['博士:台灣大學電子工程學研究所 (2003)', '學士:台灣大學電機系 (1999)'], + intro: [ + '簡韶逸教授任職於台大電子所長達16年,致力於多媒體訊號處理系統、多媒體積體電路設計、晶片系統設計方法的研究。 多年來,「媒體晶片系統實驗室」不斷研發出優異的技術。2016 年簡韶逸教授帶學生做出眼球追蹤技術的雛形時,他看好一定能應用在 AR/VR 上,於是決心創業。2018年1月時見臻科技(Ganzin Technology) 從台灣大學spin off。見臻科技提供整合性視線追蹤解決方案 (eye tracking solution),包括視線追蹤模組 (eye tracking module) 及對應之軟體開發平台 (SDK),解決目前市面上眼動儀成本高、功耗高、體積大、機構複雜的問題。', + '見臻科技連兩年參加世界性消費電子展 (CES),2018年獲選Taiwan Tech Star,是32家獲選的台灣新創公司之一,2019年更被路透社評選為Best of CES。', + ], + }, + { + anno: ['李筠婕', '鄭謹譯', '莊永松', '吳建翰', '俞建琁', '王廷峻'], + date: '2020/02/20 星期四', + id: '1912', + title: ['2008級 鄭恆之 (Technical Lead Manager @ Google Brain)'], + exp: ['現任:Google Brain Technical Lead Manager'], + edu: ['博士:CMU ECE (2013)', '學士:台灣大學電機系 (2008)'], + intro: [ + '目前任職於 Google Brain 的團隊技術領導者和軟體主管工程師的鄭恆之,從事大規模機器學習的研究與軟體開發。在2013年加入 Google 的實習生行列進行廣告排行的研究,僅僅花費不到三年的時間,就從實習生轉正職於 Google Research 的軟體工程師並且晉升 Google Brain 的技術領導與主管工程師,亮麗的職涯經歷背後是堅實的學術基礎與多篇國際期刊論文的支撐。', + '熱愛音樂的鄭恆之學長在大學時期曾任台大合唱團公演指揮,並在陳宏銘教授的 Multimedia Processing and Communications Lab 時於ACM Multimedia 等論壇發表三篇論文,畢業後於 CMU ECE 攻讀博士且專注於機器學習與多媒體訊號處理。研究成果在進入博班後達到高峰,屢次在移動通訊、普及計算和行動電腦運算領域發表高度影響力的文章,並在該論壇得到最佳論文獎的殊榮。', + ], + }, + { + anno: ['翁瑋襄', '鄭謹譯', '何俊緯', '周子庭', '謝承霖', '吳建翰', '余欣澄'], + date: '2019/12/07 星期六', + id: '1910', + title: ['2016級 林奕辰 (Bravo AI 洽吧智能執行長)', '2014級 沈昇勳 (Bravo AI 洽吧智能技術長)'], + exp: ['林奕辰 現任:Bravo AI 洽吧智能執行長', '沈昇勳 現任:Bravo AI 洽吧智能技術長'], + edu: [ + '林奕辰 學士:台灣大學電機系 (2016)', + '沈昇勳 碩士:台灣大學電信所', + '沈昇勳 學士:台灣大學電機系 (2014)', + ], + intro: [ + '創立於2017年的BravoAI (洽吧智能)是一間AI公司,專注於研發、提供金融機構廣泛的軟體服務。運用其電腦視覺、自然語言處理、深度學習等AI專長,BravoAI協助人壽與保險公司研發核保、理賠等流程自動化的方案,不僅替客戶節省大量行政成本,也為公司打出了知名度。目前為止,BravoAI以達成台灣壽險市佔率第一,更計畫邁向國際化。', + 'BravoAI主要由創辦人趙式隆、執行長林奕辰與技術長沈昇勳帶領,團隊著重於技術基礎研究與應用技術解決實際問題。', + ], + }, + { + anno: ['李筠婕', '鄭謹譯', '莊永松', '吳建翰', '余欣澄'], + date: '2019/11/24 星期日', + id: '1909', + title: ['2012級 李昀樵 (技術副總 @ 17直播)'], + exp: ['現任:Harvard MBA 學生', '曾任:17直播產品技術副總裁'], + edu: ['碩士:台灣大學電機所 (2014)', '學士:台灣大學電機系 (2012)'], + intro: [ + '李昀樵曾是李琳山教授語音實驗室的研究生,研究所時期除了研究外,更有進行小型創業,開發出新聞摘要軟件及搜尋公共腳踏車的APP,前者以兩萬元售出,後者成為該領域當時市占率最高的APP。之後,李昀樵加入17直播,監管每年五百萬的IT預算,帶領團隊從5人成長到107人,更將17 Media的首屏載入速度從4秒縮減到0.3秒,成為同業中最快速的。李昀樵在新創領域有豐富經驗,現更到Harvard MBA進修。', + ], + }, + { + anno: ['鄭謹譯', '施彥宇', '王德宇', '曾晴', '劉桓桓'], + date: '2019/09/17 星期二', + id: '1908', + title: ['2008級 方劭云 (當屆最年輕升遷副教授)'], + exp: ['現任:國立臺灣科技大學電機系 副教授', '聯絡方式:syfang@mail.ntust.edu.tw'], + edu: ['博士:台灣大學電子所 (2013)', '學士:台灣大學電機系 (2008)'], + intro: [ + '2008畢業於台大電機,目前任職於臺灣科技大學的方劭云教授,僅僅不到十年的時間內已經取得了副教授的頭銜,能有這樣的榮譽,是背後無數國際期刊論文與比賽獲獎的支撐。方教授於IEEE/ACM網絡學報屢次發布論文,二度獲得該學報最佳論文獎的榮譽。自2016起更帶領台科大團隊於CAD Contest at ICCAD比賽中四度獲獎,為臺灣在EDA領域中頗具地位的人才。目前授課項目包括VLSI、邏輯設計、演算法、奈米積體電路實體設計,並持續於EDA、奈米積體電路實體設計、製造可行性/可靠性設計、ML設計最佳化等領域中深造。', + ], + }, + { + anno: ['鄭謹譯', '李筠婕', '莊永松', '戴慕潔', '吳建翰', '毛弘仁'], + date: '2019/08/28 星期三', + id: '1907', + title: ['2012級 王易如 (行動貝果共同創辦人)'], + exp: ['現任:MoBagel 共同創辦人,任職營運長', '聯絡方式:iruwang@mobagel.com'], + edu: [ + '碩士:Computer Science/Computer Hardware , Stanford University', + '學士:台灣大學電機系 (2012)', + ], + intro: [ + '王易如於史丹佛大學取得碩士學位後,經由世界最大黑克松Salesforce $1Million Hackathon籌組MoBagel (行動貝果),目前任職該新創的營運長。MoBagel 致力於讓機器學習落地,目標讓人工智慧普及化,主要方向為APU (Advanced Preprocessing Unit),根據AI的應用類別來進行資料前處理,增加預測準確度,目前已經與台灣許多產業龍頭合作。', + ], + }, + { + anno: ['毛弘仁', '吳兩原', '林芃廷', '王廷峻', '李筠婕', '陳培鳴', '莊永松'], + date: '2018/10/13 星期六', + id: '1808', + title: ['1994級 陳維超 (Skywatch Inc. Co-founder)'], + exp: ['現任:Skywatch Inc. Co-founder'], + edu: ['學士:台灣大學電機系 (1994)'], + intro: [ + '陳維超學長在2002年於北卡羅萊納大學教堂山分校取得博士學位,專攻電腦繪圖運算技術。學長的產業界及學界歷練皆相當豐富,在業界,學長曾任美國NVIDIA 3D Graphics 架構師、Nokia Research Palo Alto 資深研究員等高技術職位,更在近年與大學同學 楊吉評學長共同創立Skywatch Inc.,提供高規格的雲端監視系統服務、同時也擔任英業達 (Inventec) 公司的 Head of AI Center、以及台灣人工智慧學校的理事;在學界,學長也曾擔任過台大資工系的教授,開設多年熱門課程:圖形處理器程式設計,同時也擔任過眾多國際學術會議的committee、member或者reviewer,如ACM SIGGRAPH、ISMAR、CVPR、ICCV、ACCV等等,是國內繪圖運算技術領域的翹楚。', + ], + }, + { + anno: ['莊永松', '莊鎧爾', '琉琉', '何驊凌', '蔡易霖'], + date: '2018/9/10 星期一', + id: '1807', + title: ['2006級 楊奕軒 (研究員/教授 @ 中研院資創中心)'], + exp: ['現任:研究員/教授 @ 中研院資創中心'], + edu: ['學士:台灣大學電機系 (2006)'], + intro: [ + '楊奕軒學長是國內少數專攻音樂資訊領域的學者,研究論文產量十分豐厚。電機系畢業後直攻電信所的博士,過去曾在美國Columbia University、西班牙Universitat Pompeu Fabra擔任訪問學者。目前是中研院資創中心的副研究員,也是清大與成大的合聘教授,同時也擔任過許多conference的editer、chair以及眾多journal、conference的reviewer等等。', + ], + }, + { + anno: ['楊程皓', '吳奕萱', '毛弘仁'], + date: '2018/8/18 星期六', + id: '1806', + title: ['2000級 陳俊仰 (Grindr President & CTO)'], + exp: ['現任:Grindr President & CTO'], + edu: [ + '博士:Electrical Engineering (Digital Signal Processing Group), Caltech (2004-2009)', + '碩士:臺灣大學電信工程學研究所 (2000-2002)', + '學士:臺灣大學電機工程學系 (1996-2000)', + ], + intro: [ + '陳俊仰學長在從台大電信所 (通訊組)畢業後,在 Caltech 唸了五年 PhD,研究領域是通訊和雷達的信號處理。從 Caltech 畢業後進入一流的軟體公司 Facebook 工作,目前則在世界最大的同志交友平台 Grindr 擔任總裁兼技術長。', + ], + }, + { + anno: ['鍾興寰', '陳威成', '孫凡耕', '蘇柏元'], + date: '2018/7/15 星期日', + id: '1805', + title: ['2014級 黃柏源 (Princeton EE PhD)'], + exp: ['Security Research Intern, Intel Coporation', 'Design Automation Intern, TSMC'], + edu: ['博士:EE, Princeton University', '學士:台大電機系 (2014)'], + intro: [ + '黃柏源學長是目前受訪者中最年輕的一位,大學時拿過多次書卷獎,電機系畢業後前往Princeton攻讀EE PhD,專攻EDA領域,大學時就曾擔任IEEE 通訊 paper reviewer,也曾在TSMC及Intel實習過,學界業界經歷皆相當豐富。', + ], + }, + { + anno: ['鍾興寰', 'Paulsu Su', '陳威成', '孫凡耕'], + date: '2017/4/13 星期四', + id: '1606', + title: ['2007級 謝沛倫 (Ambidio Inc. 共同創辦人)'], + exp: ['Co-Founder of Ambidio Inc.'], + edu: [ + '碩士:Columbia University, Fu Foundation School of Engineering and Applied Science Electrical Engineering', + ], + intro: [ + '「想像一下,打開電腦或是手機觀看影片或玩電動,就能感受到360度逼真的立體音效,彷彿賽車就從身旁呼嘯而過、恐龍就從眼前磅礡踩過的震撼聲音。」系學會很榮幸邀請到 2007級 謝沛倫系友參加本次skype專訪。創辦於2014年,LA based的 startup ─ Ambidio,希望能創造「大腦與耳朵重新連結的Ambidio時代」,快點進文章,聽一聽謝沛倫學長為我們帶來的分享吧!', + ], + }, + { + anno: ['劉衡謙', '賴明緯', '潘彥銘', '葉加祈', 'Sasa Chen'], + date: '2017/6/9 星期五', + id: '16052', + title: ['2014級 洪銘駿 (RobotArt 國際機器人藝術大賽首獎)'], + exp: ['JTCF Novel Technology Paper Award for Amusement Culture'], + edu: ['碩士:台大電機所控制組'], + intro: [ + '系學會很榮幸邀請到2014級 洪銘駿 系友參加本次影片專訪。學長將本身在藝術方面的興趣和科技做結合,在RobotArt機器人藝術比賽上奪得首獎!youtube上面也有學長設計的機器人畫Albert Einstein的影片呢!這次的籌備團隊非常用心,特別製作了一份圖文並茂、以故事方式呈現訪談過程的文章 (https://goo.gl/0ZH42K)希望能讓大家對於洪銘駿學長有更完整的認識!快點進文章或觀賞我們精心剪輯的影片,聽一聽 洪銘駿 學長為我們帶來的分享吧 :-)', + ], + }, + { + anno: ['蔡忠紘', '林怡廷', '陳鴻智', '蔡承佑'], + date: '2017/4/28 星期五', + id: '1605', + title: ['2006級 高奕豪 (Quantitative Researcher @Two Sigma Investments)'], + exp: ['Quantitative Researcher, Two Sigma Investments'], + edu: ['博士:Stanford University Information Systems Lab, EE PhD (2012)'], + intro: [ + '系學會很榮幸邀請到 2006級 高奕豪 系友參加本次skype專訪,感謝 2006級 I-Hsiang Wang 教授的推薦。學長的經歷非常豐富,在Stanford University取得EE的PhD後,進入Wall Street 的 hedge fund,在數學、軟硬體、計量金融...等領域,都有很多人生故事願意和我們分享。快點進文章,看一看高奕豪學長為我們帶來的分享吧!', + ], + }, + { + anno: ['吳奕萱', '楊程皓', '毛弘仁', '江庭瑋', '蘇峯廣'], + date: '2017/3/30 星期四', + id: '1604', + title: ['2000級 胡一天 (源鉑資本Kyber Capital執行長,專業投資人與金融觀察家)'], + exp: [ + 'Founder, Chairman & CEO @ Kyber Capital 源鉑資本', + 'Co-Founder/Managing Partner at Infinite Studio', + ], + edu: [ + '碩士:Financial Engineering, Columbia University (2003-2006)', + '臺灣大學電機工程學系、法律系財經法學組 (1996-2001)', + ], + intro: [ + '系學會很榮幸邀請到 2000級 胡一天系友參加本次專訪,感謝 2000級 陳和麟教授的大力幫忙。學長對於金融的洞見非常令人欽佩,而且樂於與學弟妹分享自己的故事、願意撥冗直接參與編修這篇文章 (超熱心!)現在就點擊連結、一起認識學長的心路歷程吧!', + ], + }, + { + anno: ['許秉鈞', '袁培傑', '劉禹辰', '曾耕森'], + date: '2017/2/27 星期一', + id: '1603', + title: ['2008級 趙式隆 (台灣矽谷創業家協會理事長)'], + exp: ['台灣矽谷創業家協會理事長', '學悅科技Zuvio創辦人'], + edu: ['博士:台大電機博士 (D97)'], + intro: [ + '系學會很榮幸邀請到2008級 趙式隆 學長擔任首po系友專訪,為我們介紹他的簡歷以及NTUEE在他心中的定位!', + ], + }, + { + anno: ['藍珮瑜', 'Steven Huang', '林昱嘉', '陳柏瑞'], + date: '2017/5/12 星期五', + id: '1602', + title: ['1992級 梁維仁 (元大證券香港區總經理、花旗環球證券臺灣區總經理)'], + exp: ['元大證券香港區總經理、花旗環球證券臺灣區總經理'], + edu: [ + '碩士:Carnegie Mellon University master of science Computational Finance, Industrial administration.', + ], + intro: [ + '系學會很榮幸邀請到 1992級 梁維仁 系友參加本次skype專訪!學長不但在金融工程領域闖出一片天,退休後投入音樂藝術相關工作,還擅長登山滑雪等休閒活動,人生經歷非常豐富,我們快來看看梁維仁學長的經驗分享吧!', + ], + }, + { + anno: ['廖宜倫', '黃暐倫', '楊耀程', '孫裕修', '林恆陞'], + date: '2017/3/13 星期一', + id: '1601', + title: ['1995級 徐瑞廷 (BCG 波士頓顧問公司董事總經理)'], + exp: ['BCG波士頓顧問公司台北辦公室負責人、合夥人暨董事總經理'], + edu: ['史丹福大學電子工程學碩士', '聖塔克拉拉大學MBA'], + intro: [ + '系學會很榮幸邀請到 1995級 徐瑞廷系友參加本次專訪,感謝 1995級林哲立系友 與 1995級丁建均教授的大力幫忙。現在就點擊文章,讓我們一起看看徐瑞廷學長的分享與心路歷程吧!', + ], + }, +] + +const main = async () => { + for (let i = toInsert.length - 1; i >= 0; i -= 1) { + const obj = toInsert[i] + const column_out = new Column_out(obj) + const { columnImg } = await Column.findOne({ filename: obj.id }) + column_out.columnImg = columnImg + try { + await column_out.save() + console.log(obj.id, 'success') + } catch { + console.log(obj.id, 'fail') + } + } +} + +mongoose.connection.on('open', () => { + console.log('DB on') + main() +}) diff --git a/backend/routes/srcs/in/column/saveImg.js b/backend/routes/srcs/in/column/saveImg.js new file mode 100644 index 000000000..34e56e9d0 --- /dev/null +++ b/backend/routes/srcs/in/column/saveImg.js @@ -0,0 +1,8 @@ +// const { dbCatch } = require('../../../error'); +// const addColumn = require('./imgProcess/addImg'); +// const asyncHandler = require('express-async-handler') + +// module.exports = asyncHandler(async (req, res, next)=>{ +// await addColumn(req.body.filename,req.file) +// return res.status(204).end() +// }) diff --git a/backend/routes/srcs/in/column/search.js b/backend/routes/srcs/in/column/search.js new file mode 100644 index 000000000..3652abf8c --- /dev/null +++ b/backend/routes/srcs/in/column/search.js @@ -0,0 +1,92 @@ +const Column_detail = require('../../../Schemas/column_detail') +const Column_Outline = require('../../../Schemas/column_outline') +const asyncHandler = require('express-async-handler') +const { dbCatch } = require('../../../error') + +/** + * @api {get} /column/search search column by keywords or hashtags + * @apiName Search + * @apiGroup In/column + * @apiDescription 用keyword(空格區分)或hashtag搜尋 + * + * @apiparam {String} keyword 用空格區分 + * @apiparam {String} hashtags 用hashtags搜尋 + * @apiparam {String} perpage 一頁數量(optional,default 5) + * @apiparam {String} page 頁數(optional,default 1) + * + * @apiSuccessExample {json} Success-Response: + * {data: + * [{ +* 'anno': ['String'], + 'date': 'String', + 'title': ['String'], + 'exp': ['String'], + 'edu': ['String'], + 'intro': ['String'], + 'id': 'String', + 'imgSrc':'String' + },], + * 'maxPage':'Number' + * } + * + * + * @apiError (500) {String} description 資料庫錯誤 + */ + +const srhCol = async (req, res, next) => { + const { hashtags, keyword, page, perpage } = req.query + let queryId = [] + const task1 = async () => { + if (hashtags) { + const query1 = { 'top.hashtags': hashtags } + const id1 = await Column_detail.find(query1, 'id').lean().catch(dbCatch) + queryId = queryId.concat(id1.map(({ id }) => id)) + } + } + const task2 = async () => { + if (keyword) { + const query2 = Column_detail.smartQuery(keyword) + const id2 = await Column_detail.find(query2, 'id').lean().catch(dbCatch) + queryId = queryId.concat(id2.map(({ id }) => id)) + } + } + const task3 = async () => { + if (keyword) { + const query3 = Column_Outline.smartQuery(keyword) + const id3 = await Column_Outline.find(query3, 'id').lean().catch(dbCatch) + queryId = queryId.concat(id3.map(({ id }) => id)) + } + } + await Promise.all([task1(), task2(), task3()]) + queryId = [...new Set(queryId)].sort() + console.log('id found:', queryId) + const p = parseInt(page ? page : 1) + const pp = parseInt(perpage & (perpage > 0) ? perpage : 5) + const totalData = queryId.length + const maxPage = Math.ceil(totalData / pp) + const toSkip = p >= maxPage ? 0 : totalData - pp * p + const toLim = p >= maxPage ? totalData - pp * (maxPage - 1) : pp + const query = queryId.slice(toSkip, toSkip + toLim) + const columnOulines = await Column_Outline.find({ id: { $in: query } }).catch(dbCatch) + console.log('find done') + return res + .status(201) + .send({ data: columnOulines.map((col) => col.getPublic()).reverse(), maxPage }) +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['keyword', 'hashtags'], + type: 'string', + method: 'get', + }, + { + filename: 'optional', + field: ['perpage', 'page'], + type: 'any', + method: 'get', + }, +] +module.exports = [valid(rules), asyncHandler(srhCol)] diff --git a/backend/routes/srcs/in/column/update.js b/backend/routes/srcs/in/column/update.js new file mode 100644 index 000000000..0ffc68ea8 --- /dev/null +++ b/backend/routes/srcs/in/column/update.js @@ -0,0 +1,96 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const ColOut = require('../../../Schemas/column_outline') +const ColDet = require('../../../Schemas/column_detail') +const { updateQuery, parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +const updateColumn = async (req, res, next) => { + const { id, top, body, annotation, anno, date, title, exp, edu, intro } = req.body + const columnImg = parseImg(req.file) + + const detailUpdate = updateQuery({ top, body, annotation }) + const outlineUpdate = updateQuery({ anno, date, title, exp, edu, intro, columnImg }) + + await ColDet.findOneAndUpdate({ id }, detailUpdate).catch(dbCatch) + await ColOut.findOneAndUpdate({ id }, outlineUpdate).catch(dbCatch) + res.status(203).send({ id }) +} + +/** + * @api {patch} /column/update update column + * @apiName updateColumn + * @apiGroup In/column + * @apiDescription 管理員更新文章 + * + * @apiParam {String[]} title 文章標題 + * (xxxx 級 xxx (公司名稱與職位))(這邊看要不要和name,experience合併?) + * @apiParam {String} id 文章的編號 + * (建議yymm) + * @apiParam {Object} top + * @apiParam {String} top.name 標題(xxxx 級 xxx) + * @apiParam {String} top.experience 副標題(公司名稱與職位) + * @apiParam {String[]} top.hashtags 文章的hashtag + * (文章類別,訪問者姓名、級別、工作、相關組織與企業) + * @apiParam {Object[]} body.body + * @apiParam {String} body.body.bigtitle (一、標題,二、求學階段...) + * @apiParam {Object[]} body.body.bigsections + * @apiParam {String} body.body.bigsections.subtitle 子標題 + * @apiParam {String} body.body.bigsections.subsection (文章內容) + * @apiParam {String[]} annotation.annotation 參與全人員 + * @apiParam {String[]} annotation.annotation.job 工作 + * @apiParam {String[]} annotation.annotation.contributer 人員 + * + * @apiParam {String[]} anno [所有採訪人員姓名] + * @apiParam {String[]} date yyyy/mm/dd 星期x + * @apiparam {String[]} exp 職位 + * @apiParam {String[]} edu 學歷 + * [學士:校系(畢業年分),碩士:校系(畢業年分),博士:校系(畢業年分)] + * @apiParam {String[]} intro 簡介 + * (1個element是一段) + * @apiParam {File} file 頭貼 + * + * @apiParamExample {js} Input-Example: + * let input = new FormData() + * + * input.append("file", 採訪合照) + * input.append("id", "yymm") + * input.append("title", "2008級 方劭云(當屆最年輕升遷副教授)") + * input.append("top[name]", "2008級 方劭云") + * input.append("top[experience]", "當屆最年輕升遷副教授") + * input.append("top[hashtags][0]", 關鍵字1) + * input.append("annotation[annotation][0][job]", "撰寫") + * input.append("annotation[annotation][0][contributer]][]", "王曉明") + * input.append("anno[]", "作者1") + * input.append("date", "yyyy/mm/dd 星期x") + * input.append("exp[0]", "現任:國立臺灣科技大學電機系 副教授") + * input.append("edu[0]", "博士:台灣大學電子所 (2013)") + * input.append("intro[0]", "2008畢業於台大電機,目前任職於臺灣科技大學的方劭云教授...") + * + * input.append("body[body][][bigtitle]", "一、我的大學生涯") + * input.append("body[body][][bigsections][0][subtitle]", "球隊與課業交織的辛苦大學生活") + * input.append("body[body][][bigsections][0][subsection]", "因為我是排球校隊,沒能花很多時間在系上...") + * + * axios.post("/api/updateColumn", input, {headers:{'content-type': 'multipart/form-data'}}) + * + * @apiSuccess (201) {String} id post的id + * + * @apiError (400) {String} description id is required + * @apiError (500) {String} description 資料庫錯誤 + */ + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['top', 'body', 'annotation'], + type: 'object', + }, + { + filename: 'optional', + field: ['date'], + type: 'string', + }, + { filename: 'optional', field: ['anno', 'title', 'exp', 'edu', 'intro'], type: 'array' }, + { filename: 'required', field: 'id' }, +] +module.exports = [valid(rules), asyncHandler(updateColumn)] diff --git a/backend/routes/srcs/in/dashboard/main.js b/backend/routes/srcs/in/dashboard/main.js new file mode 100644 index 000000000..895122ec9 --- /dev/null +++ b/backend/routes/srcs/in/dashboard/main.js @@ -0,0 +1,8 @@ +const express = require('express') +const router = express.Router() + +router.get('/recommendation/recent', require('./recentRecommendation')) +router.get('/recruitment/recent', require('./recentRecruitment')) +router.get('/column/recent', require('./recentColumn')) + +module.exports = router diff --git a/backend/routes/srcs/in/dashboard/recentColumn.js b/backend/routes/srcs/in/dashboard/recentColumn.js new file mode 100644 index 000000000..3cf5ce59c --- /dev/null +++ b/backend/routes/srcs/in/dashboard/recentColumn.js @@ -0,0 +1,40 @@ +const asyncHandler = require('express-async-handler') +const { dbCatch } = require('../../../error') +const Column_Outline = require('../../../Schemas/column_outline') + +/** + * @api {get} /column/recent get recent column + * @apiName RecentColumn + * @apiGroup Out/recent + * @apiDescription 拿Outline資料(含圖片) + * + * @apiParam {Number} number 篇數(default:5) + * + * @apiSuccessExample {json} Success-Response: + * {'data': + * [{ + * 'anno': ['String'], + * 'date': 'String', + * 'title': ['String'], + * 'exp': ['String'], + * 'edu': ['String'], + * 'intro': ['String'], + * 'id': 'String', + * 'columnImg': { + * 'data': 'Buffer', + * 'contentType': 'String', + * } + * }] + * } + * + * @apiError (500) {String} description 資料庫錯誤 + */ +module.exports = asyncHandler(async (req, res, next) => { + const { number } = req.query + const limit = number ? parseInt(number) : 5 + const totalNumber = parseInt(await Column_Outline.count().catch(dbCatch)) + const columnOulines = await Column_Outline.find() + .skip(totalNumber - limit) + .catch(dbCatch) + return res.status(201).send({ data: columnOulines.reverse().map((col) => col.getPublic()) }) +}) diff --git a/backend/routes/srcs/in/dashboard/recentRecommendation.js b/backend/routes/srcs/in/dashboard/recentRecommendation.js new file mode 100644 index 000000000..6b13dbb1a --- /dev/null +++ b/backend/routes/srcs/in/dashboard/recentRecommendation.js @@ -0,0 +1,40 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recommendation = require('../../../Schemas/recommendation') +const asyncHandler = require('express-async-handler') + +/** + * @api {get} /recommendation/recent get recent recommendation + * @apiName RecentRecommendation + * @apiGroup Out/recent + * @apiDescription 搜尋簡歷 + * + * @apiParam {Number} number 篇數(default:5) + * + * @apiSuccess (201) {Object[]} - 簡歷們 + * @apiSuccess (201) {String} -._id mongodb _id(for update,delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.name 名字 + * @apiSuccess (201) {String} -.title.desire_work_type 想要職位 + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.contact 電話 + * @apiSuccess (201) {String[]} -.info.email 信箱 + * @apiSuccess (201) {String} -.info.diploma 學院 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.experience 經驗 + * @apiSuccess (201) {String[]} -.spec.speciality 專長 + * @apiSuccess (201) {String} -.image 頭像(Ex. \) + * + * @apiError (403) {String} - not login + * @apiError (500) {String} description 資料庫錯誤 + */ +module.exports = asyncHandler(async (req, res, next) => { + const { number } = req.query + const limit = number ? parseInt(number) : 5 + const totalNumber = parseInt(await Recommendation.count().catch(dbCatch)) + console.log(totalNumber) + const recs = await Recommendation.find() + .skip(totalNumber - limit) + .catch(dbCatch) + return res.status(201).send({ data: recs.reverse().map((rec) => rec.getPublic()) }) +}) diff --git a/backend/routes/srcs/in/dashboard/recentRecruitment.js b/backend/routes/srcs/in/dashboard/recentRecruitment.js new file mode 100644 index 000000000..99bf6582d --- /dev/null +++ b/backend/routes/srcs/in/dashboard/recentRecruitment.js @@ -0,0 +1,39 @@ +const { dbCatch } = require('../../../error') +const Recruitment = require('../../../Schemas/recruitment') +const asyncHandler = require('express-async-handler') +// const getPublic = require('./DBquery/getPublic') + +/** + * @api {get} /recruitment/recent get recent recruitment + * @apiName RecentRecruitment + * @apiGroup Out/recent + * @apiDescription 顯示所有職缺(等價於不傳任何參數的searchRecruitment) + * + * @apiParam {Number} number 篇數(default:5) + * + * @apiSuccess (201) {Object[]} - 職缺們 + * @apiSuccess (201) {String} -._id mongodb _id(for delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.company_name 公司名稱 + * @apiSuccess (201) {String} -.title.work_type 職位(ex.前端工程師) + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.salary 薪資 + * @apiSuccess (201) {String[]} -.info.experience 經驗要求 + * @apiSuccess (201) {String} -.info.diploma 學院要求 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.requirement 技能要求 + * @apiSuccess (201) {String[]} -.spec.description 工作的其他描述 + * @apiSuccess (201) {String} -.image 公司頭像(Ex. \) + * + * @apiError (500) {String} description 資料庫錯誤 + */ +module.exports = asyncHandler(async (req, res, next) => { + const { number } = req.query + const limit = number ? parseInt(number) : 5 + const totalNumber = parseInt(await Recruitment.count().catch(dbCatch)) + const recs = await Recruitment.find() + .skip(totalNumber - limit) + .catch(dbCatch) + return res.status(201).send({ data: recs.reverse().map((rec) => rec.getPublic()) }) +}) diff --git a/backend/routes/srcs/in/profile_new/getProfile.js b/backend/routes/srcs/in/profile_new/getProfile.js new file mode 100644 index 000000000..930e0fdc3 --- /dev/null +++ b/backend/routes/srcs/in/profile_new/getProfile.js @@ -0,0 +1,53 @@ +//srcs/chLogin.js +const { dbCatch, ErrorHandler } = require('../../../error') +const Visual = require('../../../Schemas/user_visual_new') +const Login = require('../../../Schemas/user_login') +const asyncHandler = require('express-async-handler') + +async function insertVisual(name, account) { + const user = await new Visual({ + username: name, + account: account, + }) + .save() + .catch(dbCatch) + + await Login.updateOne({ account }, { $set: { visual: user._id } }).catch(dbCatch) + return user +} +/** + * @api {get} /profile show my profile + * @apiName getProfile + * @apiGroup In/profile_new + * @apiDescription 顯示個人profile + * + * @apiSuccess (201) {String} userimage 大頭貼(使用\) + * @apiSuccess (201) {String} account 學號 + * @apiSuccess (201) {String} username 名字 + * @apiSuccess (201) {String} nickname 綽號 + * @apiSuccess (201) {String} profile 自介 + * @apiSuccess (201) {String} publicEmail 公開信相 + * @apiSuccess (201) {String} cellphone 手機 + * @apiSuccess (201) {String} CC city and country + * @apiSuccess (201) {String} web 個人部落格 + * @apiSuccess (201) {String} facebook facebook + * @apiSuccess (201) {String} Linkedin Linkedin + * @apiSuccess (201) {String} major 學士 + * @apiSuccess (201) {String} double_major 雙主修 + * @apiSuccess (201) {String} minor 輔系 + * @apiSuccess (201) {String} master 碩士 + * @apiSuccess (201) {String} doctor 博士 + * @apiSuccess (201) {Object[]} Occupation 職業 + * @apiSuccess (201) {String} Occupation.C 公司 + * @apiSuccess (201) {String} Occupation.O 部門 + * @apiSuccess (201) {String} Occupation.P 職稱 + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const getProfile = async (req, res, next) => { + let session_account = req.session.loginAccount + let obj = await Visual.findOne({ account: session_account }).catch(dbCatch) + if (!obj) obj = await insertVisual(req.session.loginName, session_account) + return res.status(201).send(obj.getPublic()) +} +module.exports = asyncHandler(getProfile) diff --git a/backend/routes/srcs/in/profile_new/main.js b/backend/routes/srcs/in/profile_new/main.js new file mode 100644 index 000000000..292a9068d --- /dev/null +++ b/backend/routes/srcs/in/profile_new/main.js @@ -0,0 +1,10 @@ +const express = require('express') +const router = express.Router() +const parseFile = require('../../../middleware/fileProcess') + +router.get('/profile', require('./getProfile')) +router.patch('/profile', parseFile('userimage'), require('./updateProfile')) +router.post('/searchProfile', require('./searchProfile')) +router.post('/smartsearchProfile', require('./smartSearch')) + +module.exports = router diff --git a/backend/routes/srcs/in/profile_new/searchProfile.js b/backend/routes/srcs/in/profile_new/searchProfile.js new file mode 100644 index 000000000..a746fc958 --- /dev/null +++ b/backend/routes/srcs/in/profile_new/searchProfile.js @@ -0,0 +1,147 @@ +const Visual = require('../../../Schemas/user_visual_new') +const { searchQuery } = require('../../../Schemas/query') +const { dbCatch } = require('../../../error') +const asyncHandler = require('express-async-handler') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {post} /searchProfile search profile by fields + * @apiName SearchProfile + * @apiGroup In/profile_new + * @apiDescription 給定欄位搜尋porfile(OR) + * + * @apiparam {String} account 學號(用'x'進行模糊搜尋, ex.'b079010xx') + * @apiparam {String} username 名字 + * @apiparam {String} nickname 綽號 + * @apiparam {String} profile 自介 + * @apiparam {String} publicEmail 公開信相 + * @apiparam {String} cellphone 手機 + * @apiparam {String} CC city and country + * @apiparam {String} web 個人部落格 + * @apiparam {String} facebook facebook + * @apiparam {String} Linkedin Linkedin + * @apiparam {String} github github + * @apiparam {Object} major 主修 + * @apiparam {String} double_major 雙主修 + * @apiparam {String} minor 輔修 + * @apiparam {String} master 碩士 + * @apiparam {String} doctor 博士 + * @apiparam {Object} Occupation 工作(這裡是obj不是array喔,會用and搜尋)(COP3個中也可以只填1個或2個) + * @apiparam {String} Occupation.C 公司 + * @apiparam {String} Occupation.O 部門 + * @apiparam {String} Occupation.P 職位 + * @apiparam {Number} page default 1 + * @apiparam {Number} perpage default 50 + * + * @apiSuccess (201) {String} userimage 大頭貼(使用\) + * @apiSuccess (201) {String} account 學號 + * @apiSuccess (201) {String} username 名字 + * @apiSuccess (201) {String} nickname 綽號 + * @apiSuccess (201) {String} profile 自介 + * @apiSuccess (201) {String} publicEmail 公開信相 + * @apiSuccess (201) {String} cellphone 手機 + * @apiSuccess (201) {String} CC city and country + * @apiSuccess (201) {String} web 個人部落格 + * @apiSuccess (201) {String} facebook facebook + * @apiSuccess (201) {String} Linkedin Linkedin + * @apiSuccess (201) {String} github github + * @apiSuccess (201) {String} major 學士 + * @apiSuccess (201) {String} double_major 雙主修 + * @apiSuccess (201) {String} minor 輔系 + * @apiSuccess (201) {String} master 碩士 + * @apiSuccess (201) {String} doctor 博士 + * @apiSuccess (201) {Object[]} Occupation 職業 + * @apiSuccess (201) {String} Occupation.C 公司 + * @apiSuccess (201) {String} Occupation.O 部門 + * @apiSuccess (201) {String} Occupation.P 職稱 + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const srhProfile = async function (req, res, next) { + const { + account, + username, + nickname, + profile, + publicEmail, + cellphone, + CC, + web, + facebook, + Linkedin, + github, + major, + double_major, + minor, + master, + doctor, + } = req.body + const query = { + account, + username, + nickname, + profile, + publicEmail, + cellphone, + CC, + web, + facebook, + Linkedin, + github, + major, + double_major, + minor, + master, + doctor, + } + const sq = searchQuery(query) + const { Occupation } = req.body + if (Occupation) { + const { O, C, P } = Occupation + const elem = {} + if (O !== undefined) elem.O = new RegExp(O.replace(' ', '|'), 'i') + if (C !== undefined) elem.C = new RegExp(C.replace(' ', '|'), 'i') + if (P !== undefined) elem.P = new RegExp(P.replace(' ', '|'), 'i') + const obj = { Occupation: { $elemMatch: elem } } + if (sq.$or !== undefined) { + sq.$or.push(obj) + } else { + sq['$or'] = [obj] + } + } + const { page, perpage } = req.body + const [pros, maxPage] = await findWithLimit(Visual, sq, page, perpage || 50) //Visual.find(sq).catch(dbCatch) + return res.status(201).send(pros.map((p) => p.getPublic())) +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: [ + 'account', + 'username', + 'nickname', + 'profile', + 'publicEmail', + 'cellphone', + 'CC', + 'web', + 'facebook', + 'Linkedin', + 'github', + 'major', + 'double_major', + 'minor', + 'master', + 'doctor', + ], + type: 'string', + }, + { + filename: 'optional', + field: ['Occupation'], + type: 'object', + }, +] +module.exports = [valid(rules), asyncHandler(srhProfile)] diff --git a/backend/routes/srcs/in/profile_new/smartSearch.js b/backend/routes/srcs/in/profile_new/smartSearch.js new file mode 100644 index 000000000..3275f5a1e --- /dev/null +++ b/backend/routes/srcs/in/profile_new/smartSearch.js @@ -0,0 +1,50 @@ +const Profile = require('../../../Schemas/user_visual_new') +const asyncHandler = require('express-async-handler') +const { dbCatch } = require('../../../error') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {post} /smartsearchProfile search profile by keywords + * @apiName SearchProfile + * @apiGroup In/profile_new + * @apiDescription 給定關鍵字(用空格區分)搜尋 + * + * @apiparam {String} keyword 用空格區分 + * @apiparam {String} page + * @apiparam {String} perpage default 50 + * + * @apiSuccess (201) {String} userimage 大頭貼(使用\) + * @apiSuccess (201) {String} account 學號 + * @apiSuccess (201) {String} username 名字 + * @apiSuccess (201) {String} nickname 綽號 + * @apiSuccess (201) {String} profile 自介 + * @apiSuccess (201) {String} publicEmail 公開信相 + * @apiSuccess (201) {String} cellphone 手機 + * @apiSuccess (201) {String} CC city and country + * @apiSuccess (201) {String} web 個人部落格 + * @apiSuccess (201) {String} facebook facebook + * @apiSuccess (201) {String} Linkedin Linkedin + * @apiSuccess (201) {String} github github + * @apiSuccess (201) {String} major 學士 + * @apiSuccess (201) {String} double_major 雙主修 + * @apiSuccess (201) {String} minor 輔系 + * @apiSuccess (201) {String} master 碩士 + * @apiSuccess (201) {String} doctor 博士 + * @apiSuccess (201) {Object[]} Occupation 職業 + * @apiSuccess (201) {String} Occupation.C 公司 + * @apiSuccess (201) {String} Occupation.O 部門 + * @apiSuccess (201) {String} Occupation.P 職稱 + * + * @apiError (500) {String} description 資料庫錯誤 + */ + +const smartSearch = async (req, res, next) => { + const { keyword, page, perpage } = req.body + const query = Profile.smartQuery(keyword) + const [pros, maxPage] = await findWithLimit(Profile, query, page, perpage || 50) + return res.status(201).send(pros.map((pro) => pro.getPublic())) +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'optional', field: ['keyword'] }] +module.exports = [valid(rules), asyncHandler(smartSearch)] diff --git a/backend/routes/srcs/in/profile_new/updateProfile.js b/backend/routes/srcs/in/profile_new/updateProfile.js new file mode 100644 index 000000000..ed90e588e --- /dev/null +++ b/backend/routes/srcs/in/profile_new/updateProfile.js @@ -0,0 +1,114 @@ +//srcs/chLogin.js +const { dbCatch, ErrorHandler } = require('../../../error') +const Visual = require('../../../Schemas/user_visual_new') +const Login = require('../../../Schemas/user_login') +const { updateQuery, parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +/** + * @api {patch} /profile update profile + * @apiName ChangeProfile + * @apiGroup In/profile_new + * @apiDescription 更新porfile + * + * @apiHeaderExample {json} header-config + { "content-type": "multipart/form-data" } + * + * @apiparam {File} userimage 大頭貼 + * @apiparam {String} username 名字//account禁止修改 + * @apiparam {String} nickname 綽號 + * @apiparam {String} profile 自介 + * @apiparam {String} publicEmail 公開信相 + * @apiparam {String} cellphone 手機 + * @apiparam {String} CC city and country + * @apiparam {String} web 個人部落格 + * @apiparam {String} facebook facebook + * @apiparam {String} Linkedin Linkedin + * @apiparam {String} major 主修 + * @apiparam {String} double_major 雙主修 + * @apiparam {String} minor 輔修 + * @apiparam {String} master 碩士 + * @apiparam {String} doctor 博士 + * @apiparam {Object[]} Occupation 工作(因為array運算複雜,請直接給我完整的覆蓋) + * @apiparam {String} Occupation.C 公司,append('Occupation[${index}][C]',vlaue) + * @apiparam {String} Occupation.O 部門,append('Occupation[${index}][O]',vlaue) + * @apiparam {String} Occupation.P 職位,append('Occupation[${index}][P]',vlaue) + * + * @apiSuccess (204) - + * + * @apiError (404) {String} description 帳號不存在 + * @apiError (500) {String} description 資料庫錯誤 + */ +const updateProfile = async (req, res, next) => { + let session_account = req.session.loginAccount + const obj = await Visual.findOne({ account: session_account }).catch(dbCatch) + if (!obj) throw new ErrorHandler(404, '帳號不存在') + + const query = ({ + username, + nickname, + profile, + publicEmail, + cellphone, + CC, + web, + facebook, + Linkedin, + github, + major, + double_major, + minor, + master, + doctor, + Occupation, + } = req.body) + query['userimage'] = parseImg(req.file) + const toSet = updateQuery(query) + console.log('toSet', toSet) + await Visual.updateOne({ account: session_account }, toSet).catch(dbCatch) + + if (req.body.username !== undefined && req.body.username !== '') { + await Login.updateOne( + { account: session_account }, + { $set: { username: req.body.username } }, + ).catch(dbCatch) + } + return res.status(204).end() +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: [ + 'account', + 'username', + 'nickname', + 'profile', + 'publicEmail', + 'cellphone', + 'CC', + 'web', + 'facebook', + 'Linkedin', + 'github', + 'major', + 'double_major', + 'minor', + 'master', + 'doctor', + ], + type: 'string', + }, + { + filename: 'optional', + field: ['publicEmail'], + type: 'email', + }, + { + filename: 'optional', + field: ['Occupation'], + type: 'array', + }, +] +module.exports = [valid(rules), asyncHandler(updateProfile)] diff --git a/backend/routes/srcs/in/recommendation/addRec.js b/backend/routes/srcs/in/recommendation/addRec.js new file mode 100644 index 000000000..907eb13e8 --- /dev/null +++ b/backend/routes/srcs/in/recommendation/addRec.js @@ -0,0 +1,68 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recommendation = require('../../../Schemas/recommendation') +const { parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /recommendation add recommendation + * @apiName AddRecommendation + * @apiGroup In/recommendation + * @apiDescription 新增簡歷 + * + * @apiHeaderExample {json} header-config + { "content-type": "multipart/form-data" } + * + * @apiparam {String} title 簡歷標題 + * @apiparam {String} name 姓名 + * @apiparam {String} desire_work_type 想要職位 + * @apiparam {String} contact 電話 + * @apiparam {String} email 信箱 + * @apiparam {String} diploma 學位 + * @apiparam {String[]} experience 經驗 + * @apiparam {String[]} speciality 專長 + * @apiparam {File} file 照片 + * + * @apiSuccess (201) title 簡歷標題 + * @apiSuccess (201) _id mongoDB的_id + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const addRec = async (req, res) => { + const account = req.session.loginAccount + + const { title, name, desire_work_type, contact, email, diploma, experience, speciality } = + req.body + + const img = parseImg(req.file) + const recomd = await new Recommendation({ + account, + title: { title, name, desire_work_type }, + info: { contact, email, diploma }, + spec: { experience, speciality }, + img, + }) + .save() + .catch(dbCatch) + + return res.status(200).send({ title: recomd.title.title, _id: recomd._id }) +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['title', 'name', 'desire_work_type', 'contact', 'diploma'], + type: 'string', + }, + { + filename: 'optional', + field: ['email'], + type: 'email', + }, + { + filename: 'optional', + field: ['experience', 'speciality'], + type: 'array', + }, +] +module.exports = [valid(rules), asyncHandler(addRec)] diff --git a/backend/routes/srcs/in/recommendation/delRec.js b/backend/routes/srcs/in/recommendation/delRec.js new file mode 100644 index 000000000..f8cf67fe6 --- /dev/null +++ b/backend/routes/srcs/in/recommendation/delRec.js @@ -0,0 +1,33 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recommendation = require('../../../Schemas/recommendation') +const asyncHandler = require('express-async-handler') + +/** + * @api {delete} /recommendation delete recommendation + * @apiName DeleteRecommendation + * @apiGroup In/recommendation + * @apiDescription 刪除簡歷 + * + * @apiparam {String} _id get或add時回傳的_id + * + * @apiSuccess (200) title title + * + * @apiError (403) {String} description not authorized + * @apiError (404) {String} description _id not found or not authorized + * @apiError (500) {String} description 資料庫錯誤 + */ +const delRec = async (req, res) => { + const account = req.session.loginAccount + const { _id } = req.body + const delRec = await Recommendation.findById(_id, 'account title').catch(dbCatch) + if (!delRec) throw new ErrorHandler(404, '_id not found') + if (delRec.account !== account && !req.session.isAuth) + throw new ErrorHandler(403, 'not authorized') + + await Recommendation.findByIdAndDelete(_id).catch(dbCatch) + return res.status(200).send({ title: delRec.title.title }) +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'required', field: '_id' }] +module.exports = [valid(rules), asyncHandler(delRec)] diff --git a/backend/routes/srcs/in/recommendation/main.js b/backend/routes/srcs/in/recommendation/main.js new file mode 100644 index 000000000..2247e51f7 --- /dev/null +++ b/backend/routes/srcs/in/recommendation/main.js @@ -0,0 +1,13 @@ +const express = require('express') +const router = express.Router() +const parseFile = require('../../../middleware/fileProcess') + +router.post('/recommendation', parseFile('file'), require('./addRec')) +router.patch('/recommendation', parseFile('file'), require('./updateRec')) + +router.get('/recommendation/mine', require('./showMyRec')) +router.get('/recommendation', require('./showRec')) +router.post('/smartsearchrecommendation', require('./smartSearch')) +router.delete('/recommendation', require('./delRec')) + +module.exports = router diff --git a/backend/routes/srcs/in/recommendation/showMyRec.js b/backend/routes/srcs/in/recommendation/showMyRec.js new file mode 100644 index 000000000..d4bb5e922 --- /dev/null +++ b/backend/routes/srcs/in/recommendation/showMyRec.js @@ -0,0 +1,38 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recommendation = require('../../../Schemas/recommendation') +const asyncHandler = require('express-async-handler') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {get} /recommendation/mine show my recommendation + * @apiName ShowMyRecommendation + * @apiGroup In/recommendation + * @apiDescription 顯示我建立的簡歷 + * + * @apiparam {Number} page default 1 + * @apiparam {Number} perpage default 20 + * + * @apiSuccess (201) {Object[]} - 簡歷們 + * @apiSuccess (201) {String} -._id mongodb _id(for update,delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.name 名字 + * @apiSuccess (201) {String} -.title.desire_work_type 想要職位 + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.contact 電話 + * @apiSuccess (201) {String} -.info.email 信箱 + * @apiSuccess (201) {String} -.info.diploma 學院 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.experience 經驗 + * @apiSuccess (201) {String[]} -.spec.speciality 專長 + * @apiSuccess (201) {String} -.image 頭像(Ex. \) + * + * @apiError (403) {String} - not login + * @apiError (500) {String} description 資料庫錯誤 + */ +module.exports = asyncHandler(async (req, res, next) => { + const account = req.session.loginAccount + const { page, perpage } = req.query + const [recs, maxPage] = await findWithLimit(Recommendation, { account }, page, perpage || 20) + res.status(200).send(recs.map((obj) => obj.getPublic()).reverse()) +}) diff --git a/backend/routes/srcs/in/recommendation/showRec.js b/backend/routes/srcs/in/recommendation/showRec.js new file mode 100644 index 000000000..625d61eda --- /dev/null +++ b/backend/routes/srcs/in/recommendation/showRec.js @@ -0,0 +1,93 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recommendation = require('../../../Schemas/recommendation') +const { searchQuery, findWithLimit } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') +/** + * @api {get} /recommendation search recommendation by field + * @apiName ShowRecommendation + * @apiGroup In/recommendation + * @apiDescription 搜尋簡歷 + * + * @apiparam {String} _id _id + * @apiparam {String} account 學號 + * @apiparam {String} title 簡歷標題 + * @apiparam {String} name 姓名 + * @apiparam {String} desire_work_type 想要職位 + * @apiparam {String} contact 電話 + * @apiparam {String} email 信箱 + * @apiparam {String} diploma 學位 + * @apiparam {String} experience 經驗 + * @apiparam {String} speciality 專長 + * @apiparam {Number} page default 1 + * @apiparam {Number} perpage default 50 + * + * @apiSuccess (201) {Object[]} - 簡歷們 + * @apiSuccess (201) {String} -._id mongodb _id(for update,delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.name 名字 + * @apiSuccess (201) {String} -.title.desire_work_type 想要職位 + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.contact 電話 + * @apiSuccess (201) {String[]} -.info.email 信箱 + * @apiSuccess (201) {String} -.info.diploma 學院 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.experience 經驗 + * @apiSuccess (201) {String[]} -.spec.speciality 專長 + * @apiSuccess (201) {String} -.image 頭像(Ex. \) + * + * @apiError (403) {String} - not login + * @apiError (500) {String} description 資料庫錯誤 + */ +const showRec = async (req, res, next) => { + const { + account, + _id, + title, + name, + desire_work_type, + contact, + email, + diploma, + experience, + speciality, + } = req.query + const query = { + _id, + account, + 'title.title': title, + 'title.name': name, + 'title.desire_work_type': desire_work_type, + 'info.contact': contact, + 'info.email': email, + 'info.diploma': diploma, + 'spec.experience': experience, + 'spec.speciality': speciality, + } + const sq = searchQuery(query) + const { page, perpage } = req.query + const [recs, maxPage] = await findWithLimit(Recommendation, sq, page, perpage || 20) //Recommendation.find(sq).catch(dbCatch) + res.status(200).send({ data: recs.map((obj) => obj.getPublic()).reverse(), maxPage }) +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: [ + 'account', + '_id', + 'title', + 'name', + 'desire_work_type', + 'contact', + 'email', + 'diploma', + 'experience', + 'speciality', + ], + method: 'get', + type: 'string', + }, +] +module.exports = [valid(rules), asyncHandler(showRec)] diff --git a/backend/routes/srcs/in/recommendation/smartSearch.js b/backend/routes/srcs/in/recommendation/smartSearch.js new file mode 100644 index 000000000..37571f5aa --- /dev/null +++ b/backend/routes/srcs/in/recommendation/smartSearch.js @@ -0,0 +1,43 @@ +const Recmd = require('../../../Schemas/recommendation') +const asyncHandler = require('express-async-handler') +const { dbCatch } = require('../../../error') +const { findWithLimit } = require('../../../Schemas/query') + +/** + * @api {post} /smartsearchrecommendation search recommendation by keywords + * @apiName ShowRecommendation + * @apiGroup In/recommendation + * @apiDescription 關鍵字搜尋(空格區分) + * + * @apiparam {String} keyword 用空格區分 + * @apiparam {Number} page default 1 + * @apiparam {Number} perpage default 50 + * + * @apiSuccess (201) {Object[]} - 簡歷們 + * @apiSuccess (201) {String} -._id mongodb _id(for update,delete) + * @apiSuccess (201) {Object} -.title 標題相關 + * @apiSuccess (201) {String} -.title.title 標題 + * @apiSuccess (201) {String} -.title.name 名字 + * @apiSuccess (201) {String} -.title.desire_work_type 想要職位 + * @apiSuccess (201) {Object} -.info 工作資訊 + * @apiSuccess (201) {String} -.info.contact 電話 + * @apiSuccess (201) {String[]} -.info.email 信箱 + * @apiSuccess (201) {String} -.info.diploma 學院 + * @apiSuccess (201) {Object} -.spec 詳細描述 + * @apiSuccess (201) {String[]} -.spec.experience 經驗 + * @apiSuccess (201) {String[]} -.spec.speciality 專長 + * @apiSuccess (201) {String} -.image 頭像(Ex. \) + * + * @apiError (403) {String} - not login + * @apiError (500) {String} description 資料庫錯誤 + */ +const smartSearch = async (req, res, next) => { + const { keyword, page, perpage } = req.body + const query = Recmd.smartQuery(keyword) + const [recmds, maxPage] = await findWithLimit(Recmd, query, page, perpage) + return res.status(201).send({ data: recmds.map((recmd) => recmd.getPublic()).reverse() }) +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'optional', field: ['keyword'] }] +module.exports = [valid(rules), asyncHandler(smartSearch)] diff --git a/backend/routes/srcs/in/recommendation/updateRec.js b/backend/routes/srcs/in/recommendation/updateRec.js new file mode 100644 index 000000000..a993c9404 --- /dev/null +++ b/backend/routes/srcs/in/recommendation/updateRec.js @@ -0,0 +1,79 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Recommendation = require('../../../Schemas/recommendation') +const { updateQuery, parseImg } = require('../../../Schemas/query') +const asyncHandler = require('express-async-handler') + +/** + * @api {patch} /recommendation update recommendation + * @apiName UpdateRecommendation + * @apiGroup In/recommendation + * @apiDescription 更新簡歷 + * + * @apiHeaderExample {json} header-config + { "content-type": "multipart/form-data" } + + * @apiparam {String} _id get或add時回傳的_id + * @apiparam {String} title 簡歷標題 + * @apiparam {String} name 姓名 + * @apiparam {String} desire_work_type 想要職位 + * @apiparam {String} contact 電話 + * @apiparam {String} email 信箱 + * @apiparam {String} diploma 學位 + * @apiparam {String[]} experience 經驗 + * @apiparam {String[]} speciality 專長 + * @apiparam {File} file 照片 + * + * @apiSuccess (203) - - + * + * @apiError (403) {String} description not valid _id or account not match + * @apiError (500) {String} description 資料庫錯誤 + */ +const updateRec = async (req, res) => { + const account = req.session.loginAccount + + const { _id, title, name, desire_work_type, contact, email, diploma, experience, speciality } = + req.body + + const data = await Recommendation.findById(_id, 'account').catch(dbCatch) + if (!data || data.account !== account) + throw new ErrorHandler(403, 'not valid _id or account not match') + + const keys = { + 'title.title': title, + 'title.name': name, + 'title.desire_work_type': desire_work_type, + 'info.contact': contact, + 'info.email': email, + 'info.diploma': diploma, + 'spec.experience': experience, + 'spec.speciality': speciality, + img: parseImg(req.file), + } + const toSet = updateQuery(keys) + await Recommendation.findByIdAndUpdate(_id, toSet).catch((e) => { + throw new ErrorHandler(500, '資料格式錯誤') + }) + + return res.status(203).end() +} + +const valid = require('../../../middleware/validation') +const rules = [ + { + filename: 'optional', + field: ['title', 'name', 'desire_work_type', 'contact', 'diploma'], + type: 'string', + }, + { + filename: 'optional', + field: ['email'], + type: 'email', + }, + { + filename: 'optional', + field: ['experience', 'speciality'], + type: 'array', + }, + { filename: 'required', field: '_id' }, +] +module.exports = [valid(rules), asyncHandler(updateRec)] diff --git a/backend/routes/srcs/in/study/addLink.js b/backend/routes/srcs/in/study/addLink.js new file mode 100644 index 000000000..1ed42a0bf --- /dev/null +++ b/backend/routes/srcs/in/study/addLink.js @@ -0,0 +1,24 @@ +const { dbCatch } = require('../../../error') +const Study_Link = require('../../../Schemas/googlesheet_link') +const asyncHandle = require('express-async-handler') + +/** + * @api {post} /study/addLink 新增本年表單連結 + * @apiName addLink + * @apiGroup In/study + * @apiDescription 設定本年表單 + * + * @apiparam {String} senior 學長姊表單連結 + * @apiparam {String} junior 學弟妹表單連結 + * @apiparam {String} note 備註(截止時間之類的) + * + * @apiSuccess (201) {String} x data stored + * + * @apiError (500) {String} description 資料庫錯誤 + */ +module.exports = asyncHandle(async function (req, res) { + const { senior, junior, note } = req.body + const data = { senior, junior, note } //, publishTime } + await Study_Link.findOneAndUpdate({ id: 1 }, data, { upsert: true }) + res.send('data stored') +}) diff --git a/backend/routes/srcs/in/study/getLinks.js b/backend/routes/srcs/in/study/getLinks.js new file mode 100644 index 000000000..0fab7fd70 --- /dev/null +++ b/backend/routes/srcs/in/study/getLinks.js @@ -0,0 +1,28 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Study_Link = require('../../../Schemas/googlesheet_link') +const asyncHandler = require('express-async-handler') + +/** + * @api {get} /study/links 拿取本年表單連結 + * @apiName getLink + * @apiGroup In/study + * @apiDescription 拿取本年表單連結 + * + * + * @apiSuccess (201) {String} senior 學長姊表單連結 + * @apiSuccess (201) {String} junior 學弟妹表單連結 + * @apiSuccess (201) {String} note 備註 + * + * @apiError (500) {String} description 資料庫錯誤 + * @apiError (404) {String} description not found + * @apiError (403) {String} description server not open yet + * + */ +module.exports = asyncHandler(async (req, res) => { + const doc = await Study_Link.findOne({ id: 1 }) + if (!doc) throw new ErrorHandler(404, 'not found') + // const now = new Date() + const { senior, junior, publishTime, note } = doc + // if (now < publishTime) throw new ErrorHandler(403, 'server not open yet') + res.send({ senior, junior, note }) +}) diff --git a/backend/routes/srcs/in/study/main.js b/backend/routes/srcs/in/study/main.js new file mode 100644 index 000000000..2ca18f895 --- /dev/null +++ b/backend/routes/srcs/in/study/main.js @@ -0,0 +1,15 @@ +const express = require('express') +const router = express.Router() +const router_auth = express.Router() + +router_auth.post('/addLink', require('./addLink')) +router.get('/links', require('./getLinks')) +router.post('/fillForm', require('./runMatch/fillForm')) +router.get('/form', require('./runMatch/getForm').GetForm) +router_auth.get('/allForms', require('./runMatch/getForm').GetAllForms) +router.get('/result', require('./runMatch/getForm').MatchResult) +router_auth.get('/matching', require('./runMatch/main')) +router_auth.post('/sendmail', require('./storeMatch/main')) +router_auth.delete('/form', require('./runMatch/clearForm')) + +module.exports = { router, router_auth } diff --git a/backend/routes/srcs/in/study/runMatch/clearForm.js b/backend/routes/srcs/in/study/runMatch/clearForm.js new file mode 100644 index 000000000..4987521da --- /dev/null +++ b/backend/routes/srcs/in/study/runMatch/clearForm.js @@ -0,0 +1,15 @@ +const { seniorForm, juniorForm } = require('../../../../Schemas/matching_form') +const asyncHandler = require('express-async-handler') +const req = require('express/lib/request') + +const clearForm = async (_, res) => { + try { + await seniorForm.deleteMany() + await juniorForm.deleteMany() + res.status(200).send('資料庫已清空') + } catch (err) { + res.status(500).send('資料庫錯誤') + } +} + +module.exports = asyncHandler(clearForm) diff --git a/backend/routes/srcs/in/study/runMatch/fillForm.js b/backend/routes/srcs/in/study/runMatch/fillForm.js new file mode 100644 index 000000000..ea93e03dd --- /dev/null +++ b/backend/routes/srcs/in/study/runMatch/fillForm.js @@ -0,0 +1,107 @@ +const { seniorForm, juniorForm } = require('../../../../Schemas/matching_form') +const asyncHandler = require('express-async-handler') + +const fillForm = async (req, res) => { + const { identity } = req.body + try { + if (identity === 'senior') { + const account = req.session.loginAccount + const { name, degree, major, gpa, email, number, school, admission } = req.body + if (!name || !email || !number || !school) { + res.status(403).send('Required field found empty!') + } + const oldForm = await seniorForm.findOne({ account }) + if (oldForm) { + try { + await seniorForm.updateOne( + { account }, + { + name, + degree, + major, + gpa, + email, + number, + school, + admission, + }, + ) + } catch (error) { + console.log(error) + } + } else { + try { + await new seniorForm({ + account, + name, + degree, + major, + gpa, + email, + number, + school, + admission, + }).save() + } catch (error) { + console.log(error) + } + } + } else if (identity === 'junior') { + const account = req.session.loginAccount + const { name, degree, hasPaper, major, gpa, email, studentID, school1, school2, school3 } = + req.body + if (!name || !major.length || !email || !studentID || !degree) { + res.status(403).send('Required field found empty!') + } + let _degree + if (degree === '0') _degree = ['0'] + if (degree === '1') _degree = ['1'] + if (degree === '2') _degree = ['0', '1'] + const oldForm = await juniorForm.findOne({ account }) + if (oldForm) { + try { + await juniorForm.updateOne( + { account }, + { + name, + degree: _degree, + hasPaper, + major, + gpa, + email, + studentID, + school1, + school2, + school3, + }, + ) + } catch (error) { + console.log(error) + } + } else { + try { + await new juniorForm({ + account, + name, + degree: _degree, + hasPaper, + major, + gpa, + email, + studentID, + school1, + school2, + school3, + }).save() + } catch (error) { + console.log(error) + } + } + } + res.status(200).send('Form saved!') + } catch (err) { + res.status(403).send('Encounter error when filling forms: ' + err) + } +} + +module.exports = asyncHandler(fillForm) diff --git a/backend/routes/srcs/in/study/runMatch/getForm.js b/backend/routes/srcs/in/study/runMatch/getForm.js new file mode 100644 index 000000000..2c8804e05 --- /dev/null +++ b/backend/routes/srcs/in/study/runMatch/getForm.js @@ -0,0 +1,82 @@ +const { seniorForm, juniorForm } = require('../../../../Schemas/matching_form') +const asyncHandler = require('express-async-handler') +const user_login = require('../../../../Schemas/user_visual_new') + +const getForm = async (req, res) => { + if (!req.session) { + res.status(403).send('Not logged in') + } + const account = req.session.loginAccount + let savedForm + try { + savedForm = await seniorForm.findOne({ account }) + if (savedForm) { + res.status(201).send({ identity: 'senior', ...savedForm._doc }) + } else { + savedForm = await juniorForm.findOne({ account }) + if (savedForm) { + let degree + if (savedForm.degree.includes('0') && savedForm.degree.includes('1')) degree = '2' + else degree = savedForm.degree[0] + res.status(201).send({ identity: 'junior', ...savedForm._doc, degree }) + } else { + res.status(200).send({}) + } + } + } catch (err) { + res.status(500).send('資料庫錯誤') + } +} +// For test +const getAllForms = async (_, res) => { + const juniorForms = await juniorForm.find() + const seniorForms = await seniorForm.find() + res.status(201).send({ + junior: juniorForms, + senior: seniorForms, + juniorCount: juniorForms.length, + seniorCount: seniorForms.length, + }) +} // + +const getMatchResult = async (req, res) => { + const account = req.session.loginAccount + let savedForm + savedForm = await juniorForm.findOne({ account }) + if (!savedForm) { + savedForm = await seniorForm.findOne({ account }) + if (!savedForm) res.status(404).send('form data not found') + else { + // senior's match result + let matchResult = [] + for (jID of savedForm.junior) { + const jData = await juniorForm.findById(jID) + const { name, email, major, account: jAccount } = jData + const jImage = await user_login.findOne({ account: jAccount }) + let image = 'default' + if (jImage && jImage.imgSrc) image = jImage.imgSrc + matchResult.push({ name, email, major, identity: 'junior', image }) + } + if (!matchResult.length) res.status(200).send('unmatched') + else res.status(200).send(matchResult) + } + } else { + // junior's match result + const sID = savedForm.senior + if (!sID) { + res.status(200).send('unmatched') + return + } + const matchResult = await seniorForm.findById(sID) + const { name, email, school, major, gpa, account: sAccount } = matchResult + const sImage = await user_login.findOne({ account: sAccount }) + let image = 'default' + if (sImage && sImage.imgSrc) image = sImage.imgSrc + res.status(200).send({ name, email, school, major, gpa, identity: 'senior', image }) + } +} + +const GetForm = asyncHandler(getForm) +const GetAllForms = asyncHandler(getAllForms) +const MatchResult = asyncHandler(getMatchResult) +module.exports = { GetForm, GetAllForms, MatchResult } diff --git a/backend/routes/srcs/in/study/runMatch/main.js b/backend/routes/srcs/in/study/runMatch/main.js new file mode 100644 index 000000000..11883fede --- /dev/null +++ b/backend/routes/srcs/in/study/runMatch/main.js @@ -0,0 +1,24 @@ +const asyncHandle = require('express-async-handler') +const parseExcel = require('./parseExcel') +const matching = require('./matching') + +/** + * @api {post} /study_matching 配對 + * @apiName Study_matching + * @apiGroup In/study + * @apiDescription 給學長姊跟學弟妹留學配對的.xlsx檔,幫他們配對 + * + * @apiHeaderExample {json} config + { "content-type": "multipart/form-data" } + * @apiParam {File} senior 學長姐的senior.xlsx + * @apiParam {File} junior 學弟妹的junior.xlsx + * + * @apiSuccess (200) {File} - output.xlsx + */ +const post = asyncHandle(async (req, res, next) => { + await matching() + res.setHeader('Content-Type', 'multipart/form-data') + res.download(__dirname + '/uploads/result.xlsx') +}) + +module.exports = [parseExcel, post] diff --git a/backend/routes/srcs/in/study/runMatch/matching.js b/backend/routes/srcs/in/study/runMatch/matching.js new file mode 100644 index 000000000..6fcb916f0 --- /dev/null +++ b/backend/routes/srcs/in/study/runMatch/matching.js @@ -0,0 +1,146 @@ +const { seniorForm, juniorForm } = require('../../../../Schemas/matching_form') +const xlsx = require('xlsx') +const munkres = require('munkres-js') + +const cmp2arr = (a1, a2) => a1.filter((e) => a2.indexOf(e) >= 0).length +const matching = async () => { + const seniorData = await seniorForm.find() + const juniorData = await juniorForm.find() + console.log(juniorData.length, seniorData.length) + const numbers = seniorData.map((aSenior) => Number(aSenior.number)) + const score = seniorData.map(({ degree: sd, major: sm, school: ss }) => { + return juniorData.map( + ({ degree: jd, major: jm, school1: js1, school2: js2, school3: js3, hasPaper }) => { + let score = 0 + let [s1, s2, s3] = [0, 0, 0] + if (jd.includes('0') && sd === '0') { + //MS + if (js1.includes(ss)) s1 += 2 //學校1~3 + if (js2.includes(ss)) s2 += 2 + if (js3.includes(ss)) s3 += 2 + // if(cmp2arr(sm,jm)) score += 1 //領域 + // console.log(cmp2arr(sm, jm)) + score += cmp2arr(sm, jm) * 1 + } + if (jd.includes('1') && sd === '1') { + //PhD + score += hasPaper //[0,2,4,6] for ['無論文經驗','已投稿但尚未公佈','已發表 1 篇','已發表 2 篇以上'] + // if(cmp2arr(sm,jm)) score += 4 //領域 + // console.log(cmp2arr(sm, jm)) + score += cmp2arr(sm, jm) * 4 + if (js1.includes(ss)) s1 += 1 //學校1~3 + if (js2.includes(ss)) s2 += 1 + if (js3.includes(ss)) s3 += 1 + } + // acc.push(-score-s1,-score-s2,-score-s3) + return [-score - s1, -score - s2, -score - s3] //因為是找最小cost,所以負號 + }, + ) + }) + const finalResult = [] + for (let i = 0; i < 3; i++) { + //最多配對3次 + const seniorInv = numbers.map((e, i) => (e > 0 ? i : '')).filter(String) + const score1 = score + .map((jus) => { + return jus.map((abc) => Math.min(...abc)) //最小的score + // .filter((e,i)=>juniorInv.includes(i)) + }) + .filter((e, i) => seniorInv.includes(i)) + // console.log(score1.length, score1[0].length) + if (!score1.length) break + const result = munkres(score1) + result.forEach(([se, ju]) => { + if (score1[se][ju] !== 99) { + //indexing + const trueSe = seniorInv[se] + const abc = score[trueSe][ju] + const minScore = Math.min(...abc) + const juSubIndex = abc.indexOf(minScore) + //record matched + numbers[trueSe] = numbers[trueSe] - 1 + finalResult.push([trueSe, ju, juSubIndex, minScore]) + //fix score 避免重複配對 + score[trueSe][ju] = [99, 99, 99] + score.forEach((e, i) => { + score[i][ju][juSubIndex] = 99 + }) + } else console.log('99:', se, ju) + }) + } + console.log( + finalResult.sort(([a1, a2, a3], [b1, b2, b3]) => { + if (a2 > b2) return 1 + if (a2 < b2) return -1 + if (a3 > b3) return 1 + if (a3 < b3) return -1 + return 0 + }), + ) + + let matchedJunior = [] + let sjPair = [] + const wb = xlsx.utils.book_new() + const ws = xlsx.utils.aoa_to_sheet([ + [ + '學弟妹姓名', + '學弟妹學號', + '學弟妹信箱', + '學弟妹領域', + '學長姊姓名', + '學長姊學校', + '學長姊信箱', + '匹配分數', + ], + ]) + finalResult.forEach(([i, j, type, score], ind) => { + const jRow = juniorData[j] + if (!matchedJunior.includes(jRow.name)) { + matchedJunior.push(jRow.name) + sjPair.push([i, j, score]) + } + }) + sjPair.forEach(async ([i, j, score], ind) => { + const sRow = seniorData[i] + const jRow = juniorData[j] + xlsx.utils.sheet_add_aoa( + ws, + [ + [ + jRow.name, + jRow.studentID, + jRow.email, + jRow.major.join(','), + sRow.name, + sRow.school, + sRow.email, + -score, + ], + ], + { origin: -1 }, + ) + }) + xlsx.utils.book_append_sheet(wb, ws, '配對名單') + //sheet + const ws4 = xlsx.utils.aoa_to_sheet([['分數規則']]) + xlsx.utils.sheet_add_aoa(ws4, [['a,b,c代表\n夢幻,有把握,保底']], { origin: -1 }) //,{origin:`A${ind+2}`}) + xlsx.utils.sheet_add_aoa(ws4, [['MS\n同校2分\n同領域1分']], { origin: -1 }) //,{origin:`A${ind+2}`}) + xlsx.utils.sheet_add_aoa(ws4, [['PhD\n同領域4分\n論文0~3分\n同校1分']], { origin: -1 }) //,{origin:`A${ind+2}`}) + xlsx.utils.book_append_sheet(wb, ws4, '分數規則') + + //寫檔 + xlsx.writeFile(wb, __dirname + '/uploads/result.xlsx') + return wb +} + +module.exports = matching + +// if(學長學弟都MS){ +// if(學校一樣) score += 2 +// if(領域一樣) score += 1 +// } +// if(學長學弟都PhD){ +// if(有論文) score += 0~6 //[無論文經驗:0, 已投稿但尚未公佈:2, 已發表1篇:4, 已發表2篇以上:6] +// if(領域一樣) score += 2 +// if(學校一樣) score += 1 +// } diff --git a/backend/routes/srcs/in/study/runMatch/parseExcel.js b/backend/routes/srcs/in/study/runMatch/parseExcel.js new file mode 100644 index 000000000..8bb80ebe4 --- /dev/null +++ b/backend/routes/srcs/in/study/runMatch/parseExcel.js @@ -0,0 +1,35 @@ +const multer = require('multer') +const { ErrorHandler } = require('../../../../error') +const asyncHandle = require('express-async-handler') + +const storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, __dirname + '/uploads') + }, + filename: function (req, file, cb) { + cb(null, file.originalname) + }, +}) + +const uploadExcel = multer({ + fileFilter: function (req, file, cb) { + if (!file.originalname.match(/\.(xlsx)$/)) { + req.fileValidationError = '請傳送.xlsx檔' + return cb(new Error('請傳送.xlsx檔'), false) + } + cb(null, true) + }, + storage: storage, +}).fields([ + { name: 'senior', maxCount: 1 }, + { name: 'junior', maxCount: 1 }, +]) + +module.exports = (req, res, next) => { + uploadExcel(req, res, (err) => { + if (req.fileValidationError) next(new ErrorHandler(400, req.fileValidationError)) + else if (err instanceof multer.MulterError) next(new ErrorHandler(400, err.message)) + else if (err) next(new ErrorHandler(400, err)) + next() + }) +} diff --git a/backend/routes/srcs/in/study/runMatch/uploads/.gitkeep b/backend/routes/srcs/in/study/runMatch/uploads/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/backend/routes/srcs/in/study/runMatch/uploads/writeSample.js b/backend/routes/srcs/in/study/runMatch/uploads/writeSample.js new file mode 100644 index 000000000..09e097c6d --- /dev/null +++ b/backend/routes/srcs/in/study/runMatch/uploads/writeSample.js @@ -0,0 +1,131 @@ +const mongoose = require('../../../../../Schemas/db') +const { seniorForm, juniorForm } = require('../../../../../Schemas/matching_form') +const readXlsxFile = require('read-excel-file/node') + +mongoose.connection.on('open', async () => { + await main() +}) +const parseXls = async (filePath, keys) => { + const rows = await readXlsxFile(filePath) + const indexs = keys.map(({ chin, ...props }) => { + const index = rows[0].indexOf(chin) + return { index, ...props } + }) + return rows.slice(1).map((data) => { + output = {} + indexs.forEach(({ key, index, splitBy: sb, first }) => { + if (index == -1) return (output[key] = null) + let value = data[index] + if (sb !== undefined) { + try { + value = value.split(sb).map((s) => s.trim().toLowerCase()) //, console.log(value) + } catch { + value = [] + } + } else if (first !== undefined) value = value.split(first)[0].trim().toLowerCase() //, console.log(value) + output[key] = value + }) + return output + }) +} +const splitBy = /,|\+|\// +const sKeys = [ + { key: 'name', chin: '姓名' }, + // {key:'degree',chin:'學位',splitBy:' + '}, + { key: 'major', chin: '領域', splitBy: ',' }, + { key: 'email', chin: '電子郵件地址' }, + { key: 'number', chin: '今年願意接受多少位學弟妹諮詢?' }, + { key: 'gpa', chin: 'GPA' }, + { key: 'school', chin: '最終決定去的學校', splitBy: '/' }, + { key: 'admin', chin: '錄取結果(Admissions)' }, +] +const jKeys = [ + { key: 'name', chin: '姓名' }, + { key: 'degree', chin: '欲申請學位', splitBy: ' + ' }, + // { key: 'degree', chin: '學位', splitBy }, + { key: 'hasPaper', chin: '申請時是否已發表論文' }, + { key: 'major', chin: '欲申請的研究領域(可多選)', splitBy: ', ' }, + { key: 'gpa', chin: 'GPA' }, + { key: 'email', chin: '您的Email (必填)' }, + { key: 'account', chin: '學號' }, + { key: 'school', chin: '1. 希望就讀學校之 夢幻學校', splitBy }, + { key: 'school2', chin: '2. 希望就讀學校之 有把握學校', splitBy }, + { key: 'school3', chin: '3. 希望就讀學校之 保底學校', splitBy }, +] + +const transJunior = (data) => { + return data.map(({ school, school2, school3, hasPaper, degree, ...restData }) => { + if (school[0].includes('無')) school = [] + if (school2[0].includes('無')) school2 = [] + if (school3[0].includes('無')) school3 = [] + newgree = [] + if (degree.includes('ms.')) newgree.push(0) + if (degree.includes('ph.d.')) newgree.push(1) + // if(degree.includes('ms')) newgree.push(0) + // if(degree.includes('phd')) newgree.push(1) + hasPaper = ['無論文經驗', '已投稿但尚未公佈', '已發表 1 篇', '已發表 2 篇以上'].indexOf( + hasPaper, + ) + return { school, school2, school3, hasPaper, degree: newgree, ...restData } + }) +} +const transSenior = (data) => { + const extra = [] + return [ + data.map(({ school, admin, gpa, number, name, ...restData }) => { + let degree = -1 + if (school.includes('ms') || school.includes('meng')) degree = 0 + if (school.includes('phd')) degree = 1 + if (degree === -1) { + number = 0 + extra.push(name) + console.log(`請人工配對:${name}${degree}`) + } + return { degree, school: school[0], gpa, number, name, ...restData } + }), + extra, + ] +} +const main = async () => { + const seniorSampleRaw = await parseXls('./senior_sample.xlsx', sKeys) + const juniorSampleRaw = await parseXls('./junior_sample.xlsx', jKeys) + const [seniorSampleData, extra] = transSenior(seniorSampleRaw) + const juniorSampleData = transJunior(juniorSampleRaw) + seniorSampleData.forEach(async ({ degree, school, gpa, number, name, major, email }) => { + const exist = await seniorForm.findOne({ name }) + if (!exist) { + await new seniorForm({ + name, + degree: String(degree), + school, + gpa: String(gpa), + number: String(number), + major, + email, + admission: [], + }).save() + } + }) + juniorSampleData.forEach( + async ({ school, school2, school3, hasPaper, degree, name, major, gpa, email, account }) => { + const exist = await juniorForm.findOne({ studentID: account }) + if (!exist) { + await new juniorForm({ + name, + degree: degree.map((a) => { + return String(a) + }), + hasPaper: String(hasPaper), + major, + gpa: String(gpa), + email, + studentID: account, + school1: school, + school2, + school3, + }).save() + } + }, + ), + console.log('Sample data inserted to db!') +} diff --git a/backend/routes/srcs/in/study/storeMatch/mail.js b/backend/routes/srcs/in/study/storeMatch/mail.js new file mode 100644 index 000000000..02422ef15 --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/mail.js @@ -0,0 +1,71 @@ +const readXlsxFile = require('read-excel-file/node') +const sendmails = require('./sendmails') +const { seniorForm, juniorForm } = require('../../../../Schemas/matching_form') + +parseXls = async (filePath, keys) => { + const rows = await readXlsxFile(filePath, { sheet: 1 }) + const indexs = keys.map(({ chin, ...props }) => { + const index = rows[0].indexOf(chin) + return { index, ...props } + }) + return rows.slice(1).map((data) => { + output = {} + indexs.forEach(({ key, index, splitBy, first }) => { + if (index == -1) return (output[key] = null) + let value = data[index] + if (splitBy !== undefined) value = value.split(splitBy).map((s) => s.trim()) + else if (first !== undefined) value = value.split(first)[0] + output[key] = value + }) + return output + }) +} +const rKeys = [ + { key: 'sname', chin: '學長姊姓名' }, + { key: 'sSchool', chin: '學長姊學校' }, + { key: 'smail', chin: '學長姊信箱' }, + { key: 'jname', chin: '學弟妹姓名' }, + { key: 'account', chin: '學弟妹學號' }, + { key: 'jmail', chin: '學弟妹信箱' }, + { key: 'major', chin: '學弟妹領域' }, +] + +const post = async (filepath) => { + console.log('start') + const resultData = await parseXls(filepath, rKeys) + const juniorData = [] //[{ jname, sen:[{sname,sSchool,smail }...] }...] + const seniorData = [] //[{ sname, jun:[{jname,account,jmail,major}...] }...] + resultData.forEach(async (data) => { + const { sname, sSchool, smail, jname, account, jmail, major } = data + const sInd = seniorData.findIndex(({ sname: sm }) => sm === sname) + if (sInd === -1) seniorData.push({ sname, smail, jun: [{ jname, account, jmail, major }] }) + else seniorData[sInd].jun.push({ jname, account, jmail, major }) + const jInd = juniorData.findIndex(({ jname: jm }) => jm === jname) + if (jInd === -1) juniorData.push({ jname, jmail, sen: [{ sname, sSchool, smail }] }) + else juniorData[jInd].sen.push({ sname, sSchool, smail }) + let s = await seniorForm.findOne({ name: sname, email: smail }) + let j = await juniorForm.findOne({ name: jname, email: jmail }) + await juniorForm.updateOne({ _id: j._id }, { senior: s._id }) + const junior = (await seniorForm.findById(s._id)).junior + if (!junior.includes(j._id)) { + await seniorForm.updateOne({ _id: s._id }, { junior: [...junior, j._id] }) + } + }) + + // const unPairS = seniorForm.find({ junior: [] }) + // const unPairJ = juniorForm.find({ senior: '' }) + + // unPairJ.forEach(async (j) => { + // juniorForm.updateOne({ _id: j._id }, { senior: '抱歉,沒有配對到適合的學長姊' }) + // }) + // unPairS.forEach(async (s) => { + // seniorForm.updateOne({ _id: s._id }, { junior: ['抱歉,沒有配對到適合的學弟妹'] }) + // }) + + const errors = await sendmails(seniorData, juniorData, filepath) + if (!errors) console.log('no errors') + else console.log('errors', errors) + return errors +} + +module.exports = post diff --git a/backend/routes/srcs/in/study/storeMatch/mail/junior.html b/backend/routes/srcs/in/study/storeMatch/mail/junior.html new file mode 100644 index 000000000..c4aab80b1 --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/mail/junior.html @@ -0,0 +1,269 @@ + + + + + + + 留學配對結果 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + +
    + + Logo + +
    + +
    + + + + + +
    +

    XXX 學長您好:

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    + 今年的NTUEE Chain 已經完成配對!與您配對的學長姊聯絡資訊如下,非常謝謝您!祝您申請順利! +

    +
    + + +
    +
    +

    + 若有任何疑問,請儘速與工作團隊聯繫,謝謝您! +

    +
    +

    + 敬祝 順心
    + NTUEE Chain 工作團隊

    +
    + +
    + + + + + + + + + + + + + + + +
    +

    +
    + +

    +
    + +
    + + + + + + diff --git a/backend/routes/srcs/in/study/storeMatch/mail/junior.js b/backend/routes/srcs/in/study/storeMatch/mail/junior.js new file mode 100644 index 000000000..afdedeb8c --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/mail/junior.js @@ -0,0 +1,82 @@ +const {JSDOM} = require('jsdom') +const jquery = require('jquery') +const path = require('path') +const fs = require('fs') +// const { ErrorHandler } = require('../../../../error') + + +// data = { +// senior:{sname:'鄭謹譯'}, +// junior:[ +// { +// jname:'陳君輔', +// jmail:'b07901029@ntu.edu.tw' +// },{ +// jname:'陳希格', +// jmail:'b07901014@ntu.edu.tw' +// } +// ] +// } +const template_junior = async (jname) => { + const DOM = await JSDOM.fromFile( + path.join(__dirname, './junior.html'), + {contentType: 'text/html'}).catch(e=>{ + console.log(e.message) + throw new Error(500,'信件範本讀取失敗') + }) + const {window} = DOM + const $ = jquery(window) + $('#jname').text(jname) + return window +} + +const template_senior = async ({sname,sSchool:school,smail})=>{//{sname,sSchool,smail} + const DOM = await new JSDOM( + `

    + 姓名:OOO
    + 學校:OOO
    + 聯絡方式:OOO
    +

    `) + const {window} = DOM + const $ = jquery(window) + sname && $('#name').text(sname) + school && $('#school').text(school) + smail && $('#email').text(smail) + return ` + ` + +DOM.serialize() + +` + ` +} + +const main = async ({jname,sen})=>{//{jname,jmail,sen:[{sname,sSchool,smail}]} + // const blockText = await template_senior(jname) + // return await Promise.all( + // junior.map(async (student)=>{ + // const win1 = await template_junior(student) + // const $ = jquery(win1) + // $('#seniorBlock').append(blockText) + // return win1.document.documentElement.outerHTML + // }) + // ) + const win1 = await template_junior(jname) + const $ = jquery(win1) + let blockText = '' + for(const seni of sen){//{sname,sSchool,smail} + blockText += await template_senior(seni) + } + $('#seniorBlock').append(blockText) + return win1.document.documentElement.outerHTML +} + +// const test = async ()=>{ +// const htmls = await main(data) +// htmls.forEach((html,index)=>{ +// fs.writeFile(`jun${index}.html`, html, err => { +// console.log('done'); +// }); +// }) +// } +// test() + +module.exports = main \ No newline at end of file diff --git a/backend/routes/srcs/in/study/storeMatch/mail/senior.html b/backend/routes/srcs/in/study/storeMatch/mail/senior.html new file mode 100644 index 000000000..c197bc895 --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/mail/senior.html @@ -0,0 +1,361 @@ + + + + + + 留學配對結果 + + + + + +
    + NTUEE chain result +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + +
    + + + Logo + +
    + +
    + + + + + +
    +

    + XXX 學長/學姐您好: +

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    + 非常謝謝您的幫助,EE Chain 工作人員向您致上最深的感謝!今年的 NTUEE Chain + 已經配對完成,以下是與您配對的學弟妹聯絡資料。 +

    +
    +
    +
    +

    若有任何疑問,請儘速與工作團隊聯繫,謝謝您!

    +
    +

    + 敬祝 順心
    + NTUEE Chain 工作團隊 +

    +
    + +
    + + + + + + + + + + + + + +
    +

    +
    + +

    +
    + +
    + + + diff --git a/backend/routes/srcs/in/study/storeMatch/mail/senior.js b/backend/routes/srcs/in/study/storeMatch/mail/senior.js new file mode 100644 index 000000000..08167ff24 --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/mail/senior.js @@ -0,0 +1,75 @@ +const {JSDOM} = require('jsdom') +const jquery = require('jquery') +const path = require('path') +const fs = require('fs') +// const { ErrorHandler } = require('../../../../error') + + +data = { + senior:{sname:'鄭謹譯'}, + junior:[ + { + jname:'陳君輔', + jmail:'b07901029@ntu.edu.tw' + },{ + jname:'陳希格', + jmail:'b07901014@ntu.edu.tw' + } + ] +} +const template_senior = async (sname) => { + const DOM = await JSDOM.fromFile( + path.join(__dirname, './senior.html'), + {contentType: 'text/html'}).catch(e=>{ + console.log(e.message) + throw new Error(500,'信件範本讀取失敗') + }) + const {window} = DOM + const $ = jquery(window) + // $('#reset_button').attr("href",href) + // $('#reset_blank').attr("href", href) + $('#sname').text(sname) + return window +} + +const template_junior = async ({jname,account,jmail,major})=>{//{jname,account,jmail,major} + const DOM = await new JSDOM( + `

    + 姓名:OOO
    + 學號:OOO
    + 聯絡方式:OOO
    + 領域:OOO
    +

    `) + const {window} = DOM + const $ = jquery(window) + jname && $('#name').text(jname) + account && $('#account').text(account) + jmail && $('#email').text(jmail) + major && $('#major').text(major) + return ` + ` + +DOM.serialize() + +` + ` +} + +const main = async ({sname,smail,jun})=>{//{sname,smail,jun:[{jname,account,jmail,major}]} + const win1 = await template_senior(sname) + const $ = jquery(win1) + let blockText = '' + for(const student of jun){//{jname,account,jmail,major} + blockText += await template_junior(student) + } + $('#juniorBlock').append(blockText) + return win1.document.documentElement.outerHTML +} + +// const test = async ()=>{ +// const html1 = await main(data) +// fs.writeFile('2.html', html1, err => { +// console.log('done'); +// }); +// } +// test() + +module.exports = main \ No newline at end of file diff --git a/backend/routes/srcs/in/study/storeMatch/main.js b/backend/routes/srcs/in/study/storeMatch/main.js new file mode 100644 index 000000000..485516700 --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/main.js @@ -0,0 +1,23 @@ +const asyncHandle = require('express-async-handler') +const parseExcel = require('./parseExcel') +const post = require('./mail') + + +/** + * @api {post} /study/sendmail 寄配對通知 + * @apiName Study_matching + * @apiGroup In/study + * @apiDescription 給/study/matching拿到的output.xlsx檔,並寄信 + * + * @apiHeaderExample {json} config + { "content-type": "multipart/form-data" } + * @apiParam {File} result /study/match產生的.xlsx + * + * @apiSuccess (203) {String[]} errors 發生錯誤的寄件者姓名 + */ +module.exports = [parseExcel('result'), + asyncHandle(async (req,res,next)=>{ + const errors = await post(__dirname+'/uploads/output.xlsx') + res.send({errors}) + } +)] \ No newline at end of file diff --git a/backend/routes/srcs/in/study/storeMatch/parseExcel.js b/backend/routes/srcs/in/study/storeMatch/parseExcel.js new file mode 100644 index 000000000..3f7a871e9 --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/parseExcel.js @@ -0,0 +1,39 @@ +const multer = require('multer') +const { ErrorHandler } = require('../../../../error') + +const storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, __dirname + '/uploads') + }, + filename: function (req, file, cb) { + cb(null, 'output.xlsx') + }, +}) + +const upload = multer({ + fileFilter: function (req, file, cb) { + if (!file.originalname.match(/\.(xlsx)$/)) { + req.fileValidationError = '請傳送.xlsx檔' + return cb(new Error('請傳送.xlsx檔'), false) + } + cb(null, true) + }, + storage: storage, +}) + +/** + * file preprocess by multer + * @param {String} filename filename in multer + * @return {List} callback functions to put in router + */ +module.exports = (filename) => { + const doUpload = upload.single(filename) + return (req, res, next) => { + doUpload(req, res, (err) => { + if (req.fileValidationError) next(new ErrorHandler(400, req.fileValidationError)) + else if (err instanceof multer.MulterError) next(new ErrorHandler(400, err.message)) + else if (err) next(new ErrorHandler(400, err)) + next() + }) + } +} diff --git a/backend/routes/srcs/in/study/storeMatch/sendmails.js b/backend/routes/srcs/in/study/storeMatch/sendmails.js new file mode 100644 index 000000000..be534298c --- /dev/null +++ b/backend/routes/srcs/in/study/storeMatch/sendmails.js @@ -0,0 +1,53 @@ +const template2senior = require('./mail/senior') +const template2junior = require('./mail/junior') +const sendmail = require('../../../../middleware/mail') + +const toSenior = async (data) => { + //{sname,smail,jun:[{jname,account,jmail,major}]} + const mail_to_senior = await template2senior(data) + await sendmail(data.smail, '留學配對結果', mail_to_senior) +} +const toJunior = async (data) => { + //{jname,jmail,sen:[{sname,sSchool,smail}]} + const mail_to_junior = await template2junior(data) + await sendmail(data.jmail, '留學配對結果', mail_to_junior) +} + +const toAdmin = async (filepath) => { + await sendmail('ntueeplus2020@gmail.com', '留學配對結果', '', filepath) +} + +const sendmails = async (seniorData, juniorData, filepath) => { + //Error Handling + errors = [] + const sent = [] //for skip + const promises1 = seniorData.map(async (data) => { + try { + if (sent.includes(data.sname)) return + await toSenior(data) + } catch { + errors.push(data.sname) + } + }) + const promises2 = juniorData.map(async (data) => { + try { + if (sent.includes(data.jname)) return + await toJunior(data) + } catch { + errors.push(data.jname) + } + }) + const promises3 = [1].map(async () => { + try { + await toAdmin(filepath) + } catch { + errors.push('admin') + } + }) + await Promise.all([...promises1, ...promises2]) + await Promise.all([...promises3]) + console.log(errors) + return errors +} + +module.exports = sendmails diff --git a/backend/routes/srcs/in/study/storeMatch/uploads/.gitkeep b/backend/routes/srcs/in/study/storeMatch/uploads/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/backend/routes/srcs/in/time/getTime.js b/backend/routes/srcs/in/time/getTime.js new file mode 100644 index 000000000..8ec162689 --- /dev/null +++ b/backend/routes/srcs/in/time/getTime.js @@ -0,0 +1,21 @@ +const Time = require('../../../Schemas/time') +const asyncHandler = require('express-async-handler') + +const getTime = async (req, res) => { + const { target } = req.query + try { + const date = await Time.findOne({ target }) + if (date) + res + .status(200) + .send( + date.time.toISOString().substring(0, 10) + + '-' + + date.time.toISOString().substring(11, 16), + ) + else res.status(404).send(`Can't find date of ${target} in db`) + } catch (error) { + res.status(403).send('Encounter error when searching date: ' + error) + } +} +module.exports = asyncHandler(getTime) diff --git a/backend/routes/srcs/in/time/main.js b/backend/routes/srcs/in/time/main.js new file mode 100644 index 000000000..7b3f486da --- /dev/null +++ b/backend/routes/srcs/in/time/main.js @@ -0,0 +1,7 @@ +const express = require('express') +const router = express.Router() +const router_auth = express.Router() + +router.get('/getTime', require('./getTime')) +router.post('/setTime', require('./setTime')) +module.exports = { router, router_auth } diff --git a/backend/routes/srcs/in/time/setTime.js b/backend/routes/srcs/in/time/setTime.js new file mode 100644 index 000000000..2a7dd157b --- /dev/null +++ b/backend/routes/srcs/in/time/setTime.js @@ -0,0 +1,16 @@ +const Time = require('../../../Schemas/time') +const asyncHandler = require('express-async-handler') +const { dbCatch } = require('../../../error') + +const setTime = async (req, res) => { + const { target, time } = req.body + try { + const prevTime = await Time.findOne({ target }).catch(dbCatch) + if (prevTime) await Time.updateOne({ target }, { target, time }).catch(dbCatch) + else await new Time({ target, time }).save().catch(dbCatch) + res.status(200).send(`successfully set ${target} at ${time}`) + } catch (error) { + res.status(403).send('Encounter error when settinng time: ' + err) + } +} +module.exports = asyncHandler(setTime) diff --git a/backend/routes/srcs/out/account/activate.js b/backend/routes/srcs/out/account/activate.js new file mode 100644 index 000000000..79f563ae6 --- /dev/null +++ b/backend/routes/srcs/out/account/activate.js @@ -0,0 +1,78 @@ +const asyncHandler = require('express-async-handler') +const Pending = require('../../../Schemas/user_pending') +const Login = require('../../../Schemas/user_login') +const Visual = require('../../../Schemas/user_visual_new') +const { ErrorHandler, dbCatch } = require('../../../error') + +/** + * @api {get} /regact/:account/:active accountActivate + * @apiName Register + * @apiGroup Out/account + * @apiDescription 註冊後用信箱寄這個連結,驗證。完成後自動導向 + * + * @apiHeaderExample {json} config + { "content-type": "multipart/form-data" } + * + * @apiparam {String} account 學號 + * @apiparam {String} active 驗證碼 + * + * @apiSuccess (201) {HTML} - - + * + * @apiError (400) {String} description 請添加照片 + * @apiError (403) {String} description 帳號已存在 + * + * @apiError (500) {String} description 資料庫錯誤 + */ + +template = (msg, link) => ` + +    +      +     

    ${msg}, redirecting to ${link} after 5 seconds

    +    +` + +const main = async (req, res) => { + try { + const { account, active } = req.params + if (!account || !active) throw new ErrorHandler(400, 'some params missing') + //check user validation + const doc = await Pending.findOneAndDelete({ account, active }).catch(dbCatch) + if (!doc) return res.send(template('activation code expire', '/#/register_entry')) + const userexist = await Login.exists({ account }).catch(dbCatch) + if (userexist) return res.send(template('already registered', '/#/login')) + //register + const { username, userpsw, facebookID, email } = doc + const { _id: visual } = await new Visual({ + username, + account, + publicEmail: email, + }) + .save() + .catch(dbCatch) + await new Login({ username, account, facebookID, userpsw, visual }).save().catch(async (e) => { + await Visual.findByIdAndRemove(visual).catch(dbCatch) + console.log(e) + throw new ErrorHandler(500, '資料庫錯誤') + }) + res.send(template('account activate success', '/#/login')) + } catch (e) { + res.send(template(`error: ${e.message || e.description}`, '/#/login')) + } +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'account', checkAt: 'params' }] +module.exports = [valid(rules), asyncHandler(main)] diff --git a/backend/routes/srcs/out/account/isLogin.js b/backend/routes/srcs/out/account/isLogin.js new file mode 100644 index 000000000..bb87a5d14 --- /dev/null +++ b/backend/routes/srcs/out/account/isLogin.js @@ -0,0 +1,33 @@ +const { dbCatch, ErrorHandler } = require('../../../error') +const Visual = require('../../../Schemas/user_visual_new') +const Login = require('../../../Schemas/user_login') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /isLogin isLogin + * @apiName IsLogin + * @apiGroup Out/account + * @apiDescription 檢查是否有登入 + * + * @apiSuccess (201) {String} account 登入者學號 + * + * @apiError (403) {String} description "未登入" + */ +module.exports = asyncHandler(async (req, res, next) => { + const session_account = req.session.loginAccount + if (session_account) { + const vuser = await Visual.findOne({ account: session_account }) + if (!vuser) throw new ErrorHandler(404, 'profile不存在') + const user = await Login.findOne({ account: session_account }, 'isAuth').catch(dbCatch) + if (!user) throw new ErrorHandler(404, 'profile不存在') + console.log(user) + return res.status(200).send({ + account: session_account, + userimage: vuser.imgSrc, + userCellphone: vuser.cellphone, + userName: vuser.username, + userEmail: vuser.publicEmail, + isAuth: user.isAuth, + }) + } else throw new ErrorHandler(403, '未登入') +}) diff --git a/backend/routes/srcs/out/account/login.js b/backend/routes/srcs/out/account/login.js new file mode 100644 index 000000000..adbe13495 --- /dev/null +++ b/backend/routes/srcs/out/account/login.js @@ -0,0 +1,47 @@ +const Login = require('../../../Schemas/user_login') +const crypto = require('crypto') +const { dbCatch, ErrorHandler } = require('../../../error') +const asyncHandler = require('express-async-handler') +/** + * @api {post} /login login + * @apiName Login + * @apiGroup Out/account + * @apiDescription 登入 + * + * @apiparam {String} account 學號 + * @apiparam {String} password 密碼(以後建議在前端加密) + * + * @apiSuccess (201) {String} username 登入者名字 + * @apiSuccess (201) {String} account 登入者學號 + * @apiSuccess (201) {Boolean} isAuth 是否是管理員 + * + * @apiError (404) {String} description 帳號不存在 + * + * @apiError (401) {String} description 密碼錯誤 + * + * @apiError (500) {String} description 資料庫錯誤 + */ + +const login = async (req, res, next) => { + const account = req.body.account.toLowerCase() + const password = req.body.password + //密碼加密 + const newPsw = crypto.createHash('md5').update(password).digest('hex') + + const query = { account } + const obj = await Login.findOne(query, 'userpsw username account isAuth').catch(dbCatch) + if (!obj) throw new ErrorHandler(404, '帳號不存在') + if (obj.userpsw !== newPsw) throw new ErrorHandler(401, '密碼錯誤') + req.session.loginName = obj.username + req.session.loginAccount = obj.account + req.session.isAuth = obj.isAuth + return res.status(201).send({ + username: obj.username, + account: obj.account, + isAuth: obj.isAuth, + }) +} + +const valid = require('../../../middleware/validation') +const rules = ['account', 'password'] +module.exports = [valid(rules), asyncHandler(login)] diff --git a/backend/routes/srcs/out/account/loginFB.js b/backend/routes/srcs/out/account/loginFB.js new file mode 100644 index 000000000..d77fb4746 --- /dev/null +++ b/backend/routes/srcs/out/account/loginFB.js @@ -0,0 +1,38 @@ +//srcs/login.js +const Login = require('../../../Schemas/user_login') +const { ErrorHandler, dbCatch } = require('../../../error') +const crypto = require('crypto') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /loginFB loginFB + * @apiName LoginFB + * @apiGroup Out/account + * @apiDescription 登入by facebook + * + * @apiparam {String} facebookID facebook ID + * + * @apiSuccess (201) {String} username 登入者名字 + * @apiSuccess (201) {String} account 學號 + * @apiSuccess (201) {Boolean} isAuth 是否是管理員 + * + * @apiError (404) {String} description 帳號不存在 + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const loginFB = async (req, res, next) => { + const { facebookID: unEncID } = req.body + const facebookID = crypto.createHash('md5').update(unEncID).digest('hex') + const query = { facebookID } + const obj = await Login.findOne(query, 'username account').catch(dbCatch) + if (!obj) throw new ErrorHandler(404, '帳號不存在') + console.log(obj) + req.session.loginName = obj.username + req.session.loginAccount = obj.account + req.session.isAuth = obj.isAuth + return res.status(201).send({ username: obj.username, isAuth: obj.isAuth, account: obj.account }) +} + +const valid = require('../../../middleware/validation') +const rules = ['facebookID'] +module.exports = [valid(rules), asyncHandler(loginFB)] diff --git a/backend/routes/srcs/out/account/logout.js b/backend/routes/srcs/out/account/logout.js new file mode 100644 index 000000000..a5d7070f2 --- /dev/null +++ b/backend/routes/srcs/out/account/logout.js @@ -0,0 +1,21 @@ +const { ErrorHandler } = require('../../../error') + +/** + * @api {post} /logout logout + * @apiName Logout + * @apiGroup Out/account + * @apiDescription 登出 + * + * @apiSuccess (204) - + * + * @apiError (500) {String} description "session destroy失敗" + */ +module.exports = function (req, res, next) { + req.session.destroy(function (err) { + if (err) { + console.log('session destroy err\n', err) + throw new ErrorHandler(500, 'session destroy fail') + } + return res.status(204).end() + }) +} diff --git a/backend/routes/srcs/out/account/mailTemplate/accountActivate.html b/backend/routes/srcs/out/account/mailTemplate/accountActivate.html new file mode 100644 index 000000000..78ec1e2c0 --- /dev/null +++ b/backend/routes/srcs/out/account/mailTemplate/accountActivate.html @@ -0,0 +1,396 @@ + + + + + + activate account + + + + + +
    + a password reset link is auto sent from NTUEEplus website. +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + +
    + + + Logo + +
    + +
    + + + + + +
    +

    + Activate Your EE+ Account +

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    + Tab the button below to activate your EE+ account. +
    + If you didn't register for EE+ website, please ignore this email. +

    +
    + + + + +
    + + + + +
    + Activate +
    +
    +
    +

    + If that doesn't work, copy and paste the following link in your browser: +

    +

    + + http://localhost:1993/regact/b07901029/sukz7fdmcvp + +

    +
    +

    Sincerely,
    NTUEE Plus Web Team

    +
    + +
    + + + + + + + + + + + + + +
    +

    + You received this email because we received a request for [register] for your + account. If you didn't request [register] you can safely delete this email. +

    +
    + +

    +
    + +
    + + + diff --git a/backend/routes/srcs/out/account/mailTemplate/template_generator.js b/backend/routes/srcs/out/account/mailTemplate/template_generator.js new file mode 100644 index 000000000..6ed5e3874 --- /dev/null +++ b/backend/routes/srcs/out/account/mailTemplate/template_generator.js @@ -0,0 +1,24 @@ +const { JSDOM } = require('jsdom') +const jquery = require('jquery') +const path = require('path') +const { ErrorHandler } = require('../../../../error') +/** + * generate email with beautiful button + * @param {String} href hyper link to the reset password page + * @param {String} href_br hyper link for user to copy (contain \) + * @return {String} html text + */ +module.exports = async (href, href_br) => { + const DOM = await JSDOM.fromFile(path.join(__dirname, './accountActivate.html'), { + contentType: 'text/html', + }).catch((e) => { + console.log(e.message) + throw new ErrorHandler(500, '信件範本讀取失敗') + }) + const { window } = DOM + const $ = jquery(window) + $('#reset_button').attr('href', href) + $('#reset_blank').attr('href', href) + $('#reset_blank').text(href_br) + return window.document.documentElement.outerHTML +} diff --git a/backend/routes/srcs/out/account/main.js b/backend/routes/srcs/out/account/main.js new file mode 100644 index 000000000..b8622602a --- /dev/null +++ b/backend/routes/srcs/out/account/main.js @@ -0,0 +1,27 @@ +const express = require('express') +const router = express.Router() +const parseFile = require('../../../middleware/fileProcess') + +router.post('/login', require('./login')) + +router.post('/loginFB', require('./loginFB')) + +router.post('/register', parseFile('file'), require('./register')) +router.post('/register_step2', parseFile('file'), require('./reg-step2')) + +router.get('/regact/:account/:active', require('./activate')) + +router.post( + '/registerFB', + parseFile([ + { name: 'avatar', maxCount: 1 }, + { name: 'file', maxCount: 1 }, + ]), + require('./registerFB'), +) + +router.post('/logout', require('./logout')) + +router.post('/isLogin', require('./isLogin')) + +module.exports = router diff --git a/backend/routes/srcs/out/account/preload/fbEncrypt.js b/backend/routes/srcs/out/account/preload/fbEncrypt.js new file mode 100644 index 000000000..ec1044cc2 --- /dev/null +++ b/backend/routes/srcs/out/account/preload/fbEncrypt.js @@ -0,0 +1,8 @@ +// const mongoose = require('../../../../Schemas/db') +// const Login = require('../../../../Schemas/user_login') +// mongoose.connection.on('open', async () => { +// const docs = await Login.find() +// docs.forEach(async ({ facebookID }) => { +// console.log(facebookID) +// }) +// }) diff --git a/backend/routes/srcs/out/account/reg-step2.js b/backend/routes/srcs/out/account/reg-step2.js new file mode 100644 index 000000000..793e95138 --- /dev/null +++ b/backend/routes/srcs/out/account/reg-step2.js @@ -0,0 +1,43 @@ +const Pending = require('../../../Schemas/user_pending') +const { parseImg } = require('../../../Schemas/query') +const { ErrorHandler, dbCatch } = require('../../../error') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /register_step2 registerStep2 + * @apiName RegisterStep2 + * @apiGroup Out/account + * @apiDescription 註冊,新增照片(供管理員檢視) + * + * @apiHeaderExample {json} config + { "content-type": "multipart/form-data" } + * + * @apiparam {String} account 學號 + * @apiparam {File} file 身分證明 + * + * @apiSuccess (201) {String} - - + * + * @apiError (400) {String} description img not given + * @apiError (404) {String} description account not found + * + * @apiError (500) {String} description 資料庫錯誤 + */ + +const main = async (req, res) => { + if (!req.file) throw new ErrorHandler(400, 'img not given') + const { account } = req.body + const isPending = await Pending.exist({ account }).catch(dbCatch) + if (!isPending) throw new ErrorHandler(404, 'account not found') + await Pending.findOneAndUpdate( + { account }, + { img: parseImg(req.file) }, + { + useFindAndModify: false, + }, + ).catch(dbCatch) + return res.end() +} + +const valid = require('../../../middleware/validation') +const rules = ['account'] +module.exports = [valid(rules), asyncHandler(main)] diff --git a/backend/routes/srcs/out/account/register.js b/backend/routes/srcs/out/account/register.js new file mode 100644 index 000000000..a5c6aecdb --- /dev/null +++ b/backend/routes/srcs/out/account/register.js @@ -0,0 +1,163 @@ +const Login = require('../../../Schemas/user_login') +const Pending = require('../../../Schemas/user_pending') +const crypto = require('crypto') +const Visual = require('../../../Schemas/user_visual_new') +const { parseImg } = require('../../../Schemas/query') +const { ErrorHandler, dbCatch } = require('../../../error') +const asyncHandler = require('express-async-handler') +const env = require('dotenv') +env.config() + +/*新增一筆使用者資料*/ +async function insert(username, account, psw, img, visual) { + await new Login({ + username: username, + account: account, + userpsw: psw, + img, + visual: visual._id, + }) + .save() + .catch(dbCatch) +} + +async function insertVisual(name, account, email) { + return await new Visual({ + username: name, + account: account, + publicEmail: email, + }) + .save() + .catch(dbCatch) +} + +/** + * @api {post} /register register + * @apiName Register + * @apiGroup Out/account + * @apiDescription 註冊(by 學號 & email),.env設定newReg=true使用新註冊規則 + * + * @apiHeaderExample {json} config + { "content-type": "multipart/form-data" } + * + * @apiparam {String} account 學號 + * @apiparam {String} password 密碼(以後建議在前端加密) + * @apiparam {String} ConfirmPassword 二次密碼 + * @apiparam {String} username 使用者名字 + * @apiparam {String} Email 信箱 + * @apiparam {String} isGraduated false則寄送email給account@ntu.edu.tw(newRule=version3才需要) + * + * @apiSuccess (201) {String} username 姓名(newRule=false) + * @apiSuccess (201) {String} isGraduated isGraduated(newRule=version3) + * @apiSuccess (201) {String} email account@ntu.edu.tw(newRule=version3 && isGraduated=false) + * + * @apiError (400) {String} description 請添加照片 + * @apiError (403) {String} description 帳號已存在 + * + * @apiError (500) {String} description 資料庫錯誤 + */ +const register = async (req, res) => { + const { username, password, Email } = req.body + const account = req.body.account.toLowerCase() + + //密碼加密 + const newPsw = crypto.createHash('md5').update(password).digest('hex') + + const query = { account } + const isRegistered = await Login.exists(query).catch(dbCatch) + if (isRegistered) throw new ErrorHandler(403, '帳號已存在') + const user = await insertVisual(username, account, Email) + await insert(username, account, newPsw, parseImg(req.file), user) + req.session.loginName = username + req.session.loginAccount = account + return res.status(201).send({ username }) +} + +const secure_reg = async (req, res) => { + const { username, password, Email } = req.body + const account = req.body.account.toLowerCase() + const newPsw = crypto.createHash('md5').update(password).digest('hex') + if (req.file === undefined) throw new ErrorHandler(400, '請添加照片') + const query = { account } + const isRegistered = await Login.exists(query).catch(dbCatch) + if (isRegistered) throw new ErrorHandler(403, '帳號已存在') + + const data = { + username, + account, + userpsw: newPsw, + email: Email, + img: { + data: req.file.buffer, + contentType: req.file.mimetype, + }, + } + await Pending.findOneAndUpdate({ account }, data, { upsert: true }).catch(dbCatch) + + return res.status(201).send({ username }) +} + +const sendmail = require('../../../middleware/mail') +const template = require('./mailTemplate/template_generator') +const reg_v3 = async (req, res) => { + const account = req.body.account.toLowerCase() + const isRegistered = await Login.exists({ account }).catch(dbCatch) + if (isRegistered) throw new ErrorHandler(403, '帳號已存在') + + const { username, password, Email } = req.body + const newPsw = crypto.createHash('md5').update(password).digest('hex') + + const active = Math.random().toString(36).substring(2) + const data = { + username, + account, + userpsw: newPsw, + email: Email, + active, + img: parseImg(req.file), + } + + await Pending.findOneAndUpdate({ account }, data, { + upsert: true, + useFindAndModify: false, + }).catch(dbCatch) + + const { isGraduated } = req.body + if (isGraduated) { + //畢業,不寄email + res.send({ isGraduated }) + } else { + const email = `${account}@ntu.edu.tw` + const link = `${req.protocol}://${req.get('host')}/api/regact/${account}/${active}` + const htmlText = await template(link, link) + await sendmail(email, 'eeplus website account activaiton', htmlText).catch((e) => { + console.log(e) + throw new ErrorHandler(400, 'sendemail fail') + }) + res.send({ email, isGraduated }) + } +} + +const valid = require('../../../middleware/validation') +const rules = [ + 'account', + 'password', + { filename: 'required', field: 'username' }, + 'Email', + 'ConfirmPassword', +] + +const exportVersion = (v) => { + switch (v) { + case 'true': + return [valid(rules), asyncHandler(secure_reg)] + case 'version3': + return [ + valid([...rules, { filename: 'required', field: 'isGraduated' }]), + asyncHandler(reg_v3), + ] + default: + return [valid(rules), asyncHandler(register)] + } +} +module.exports = exportVersion(process.env.newReg) diff --git a/backend/routes/srcs/out/account/registerFB.js b/backend/routes/srcs/out/account/registerFB.js new file mode 100644 index 000000000..fb3cd34c4 --- /dev/null +++ b/backend/routes/srcs/out/account/registerFB.js @@ -0,0 +1,162 @@ +//srcs/register.js +const { ErrorHandler, dbCatch } = require('../../../error') +const Pending = require('../../../Schemas/user_pending') +const Login = require('../../../Schemas/user_login') +const Visual = require('../../../Schemas/user_visual_new') +const asyncHandler = require('express-async-handler') +const { parseImg } = require('../../../Schemas/query') +const crypto = require('crypto') + +async function insertFB(name, account, facebookID, file, user) { + await new Login({ + username: name, + account: account, + facebookID: facebookID, + img: { + data: file.buffer, + contentType: file.mimetype, + }, + visual: user._id, + }) + .save() + .catch(async (e) => { + console.log(e) + await Visual.findByIdAndDelete(user._id) + throw new ErrorHandler(500, '註冊失敗') + }) +} + +/** + * @api {post} /registerFB registerFB + * @apiName RegisterFB + * @apiGroup Out/account + * @apiDescription 註冊(by facebook ID),在.env用newReg=version3 + * + * @apiHeaderExample {json} config + { "content-type": "multipart/form-data" } + * + * @apiparam {String} facebookID facebookID + * @apiparam {String} account 學號 + * @apiparam {String} username 使用者名字 + * @apiparam {File} file 身分證明的照片(optional) + * @apiparam {File} avatar 大頭貼(optional) + * @apiparam {String} Email Email(newRule=true,version3才需要) + * @apiparam {String} isGraduated false則寄送email給account@ntu.edu.tw(newRule=version3才需要) + * + * @apiSuccess (201) {String} username 使用者名字(newRule=false) + * @apiSuccess (201) {String} isGraduated isGraduated(newRule=version3) + * @apiSuccess (201) {String} email account@ntu.edu.tw(newRule=version3 && isGraduated=false) + * + * @apiError (400) {String} description 請添加照片 + * @apiError (403) {String} description 帳號已存在 + * @apiError (500) {String} description 資料庫錯誤 + */ +const registerFB = async (req, res) => { + const account = req.body.account.toLowerCase() + const isRegistered = await Login.exists({ account }).catch(dbCatch) + if (isRegistered) throw new ErrorHandler(403, '帳號已存在') + + const { username, facebookID, Email } = req.body + const fbIdEnc = crypto.createHash('md5').update(facebookID).digest('hex') + + const avatar = parseImg(req.files['avatar'] ? req.files['avatar'][0] : undefined) + const idFile = parseImg(req.files['file'] ? req.files['file'][0] : undefined) + + const user = await new Visual({ + username, + account, + userimage: avatar, + publicEmail: Email, + }) + .save() + .catch(dbCatch) + await insertFB(username, account, fbIdEnc, idFile, user) + + req.session.loginName = username + req.session.loginAccount = account + return res.status(201).send({ username }) +} + +const secure_regFB = async (req, res) => { + const { username, facebookID, Email: email } = req.body + const account = req.body.account.toLowerCase() + + if (req.file === undefined) throw new ErrorHandler(400, '請添加照片') + + const query = { account } + const isRegistered = await Login.exists(query).catch(dbCatch) + if (isRegistered) throw new ErrorHandler(403, '帳號已存在') + + const data = { + username, + account, + facebookID, + email, + img: parseImg(req.file), + } + await Pending.findOneAndUpdate({ account }, data, { upsert: true }).catch(dbCatch) + + return res.status(201).send({ username }) +} + +const sendmail = require('../../../middleware/mail') +const template = require('./mailTemplate/template_generator') +const regFB_v3 = async (req, res) => { + const account = req.body.account.toLowerCase() + const isRegistered = await Login.exists({ account }).catch(dbCatch) + if (isRegistered) throw new ErrorHandler(403, '帳號已存在') + + const { username, facebookID, Email } = req.body + const fbIdEnc = crypto.createHash('md5').update(facebookID).digest('hex') + + const active = Math.random().toString(36).substr(2) + const avatar = parseImg(req.files['avatar'] ? req.files['avatar'][0] : undefined) + const idFile = parseImg(req.files['file'] ? req.files['file'][0] : undefined) + + const data = { + username, + account, + facebookID: fbIdEnc, + email: Email, + active, + img: idFile, + avatar, + } + await Pending.findOneAndUpdate({ account }, data, { + upsert: true, + useFindAndModify: false, + }).catch(dbCatch) + + const { isGraduated } = req.body + if (isGraduated) { + //畢業,不寄email + res.send({ isGraduated }) + } else { + const email = `${account}@ntu.edu.tw` + const link = `${req.protocol}://${req.get('host')}/api/regact/${account}/${active}` + const htmlText = await template(link, link) + await sendmail(email, 'eeplus website account activation', htmlText).catch((e) => { + console.log(e) + throw new ErrorHandler(400, 'sendemail fail') + }) + res.send({ email, isGraduated }) + } +} + +const valid = require('../../../middleware/validation') +const rules = [{ filename: 'required', field: 'username' }, 'account', 'facebookID'] + +const exportVersion = (v) => { + switch (v) { + case 'true': + return [valid([...rules, 'Email']), asyncHandler(secure_regFB)] + case 'version3': + return [ + valid([...rules, 'Email', { filename: 'required', field: 'isGraduated' }]), + asyncHandler(regFB_v3), + ] + default: + return [valid(rules), asyncHandler(registerFB)] + } +} +module.exports = exportVersion(process.env.newReg) diff --git a/backend/routes/srcs/out/contact/main.js b/backend/routes/srcs/out/contact/main.js new file mode 100644 index 000000000..105378aaa --- /dev/null +++ b/backend/routes/srcs/out/contact/main.js @@ -0,0 +1,15 @@ +/** + * @api {get} https://docs.google.com/forms/d/e/1FAIpQLSed-GXXBhqIRUBEX7-nMlwuQ3a22-Z51mtxVSlcyhWzG9TH2Q/formResponse get in touch + * @apiName Contact + * @apiGroup Out/contact + * @apiDescription 用google表單蒐集站內信,統整在[google sheet](https://docs.google.com/spreadsheets/d/13ryrmVkQtOvHjr5I-h1atrqpBgkDOeixp1mFKwAK3U0/edit?resourcekey#gid=1383126660) + * + * + * @apiParamExample {js} Input-Example: + * axios.get(url,{ + * usp:'pp_url', + * 'entry.1670134810':'your name', + * 'entry.302205267':'your email', + * 'entry.307115258':'your message' + * }) + */ diff --git a/backend/routes/srcs/out/forget/activation.js b/backend/routes/srcs/out/forget/activation.js new file mode 100644 index 000000000..e8233e57e --- /dev/null +++ b/backend/routes/srcs/out/forget/activation.js @@ -0,0 +1,40 @@ +const Activation = require('../../../Schemas/activation') +const Login = require('../../../Schemas/user_login') +const crypto = require('crypto') +const { ErrorHandler, dbCatch } = require('../../../error') +const asyncHandler = require('express-async-handler') + +/** + * @api {post} /activation activation + * @apiName Activation + * @apiGroup Out/forget + * @apiDescription 檢查激活碼,忘記密碼重設 + * + * @apiparam {String} account 學號 + * @apiparam {String} active 激活碼(附在信箱的連結裡) + * @apiparam {String} password 要重設的密碼 + * + * @apiSuccess (200) - + * + * @apiError (401) {String} description 驗證碼已不存在,請至forget頁面 + * @apiError (500) {String} description 資料庫錯誤 + */ +const activate = async (req, res) => { + const { account, active, password } = req.body + const newPsw = crypto.createHash('md5').update(password).digest('hex') + + const obj = await Activation.exists({ account, active }).catch(dbCatch) + if (!obj) throw new ErrorHandler(401, '驗證碼已不存在,請至forget頁面') + //更新密碼 + await Login.updateOne({ account }, { $set: { userpsw: newPsw } }).catch(dbCatch) + Activation.deleteMany({ account }) + .exec() + .catch((e) => { + console.log(e.message) + }) + return res.status(200).end() +} + +const valid = require('../../../middleware/validation') +const rules = ['account', { filename: 'required', field: 'active' }, 'password'] +module.exports = [valid(rules), asyncHandler(activate)] diff --git a/backend/routes/srcs/out/forget/forget.js b/backend/routes/srcs/out/forget/forget.js new file mode 100644 index 000000000..610070a82 --- /dev/null +++ b/backend/routes/srcs/out/forget/forget.js @@ -0,0 +1,74 @@ +const Visual = require('../../../Schemas/user_visual_new') +const Activation = require('../../../Schemas/activation') +const sendmail = require('../../../middleware/mail') +const template = require('./mail/template_generator') +const { dbCatch, ErrorHandler } = require('../../../error') +const asyncHandler = require('express-async-handler') + +async function insertActive(name, act) { + const obj = await Activation.findOne({ account: name }) + if (!obj) { + //新建activation + await new Activation({ + account: name, + active: act, + }).save() + } else { + await Activation.updateOne( + { account: name }, + { + $set: { + active: act, + createdAt: Date.now(), + }, + }, + ) + } +} + +/** + * @api {post} /forget forget + * @apiName Forget + * @apiGroup Out/forget + * @apiDescription 忘記密碼,寄信 + * + * @apiparam {String} account 學號 + * + * @apiSuccess (200) {String} email + * - 使用者填寫的email + * - "您的私人信箱" + * + * @apiError (404) {String} description + * - 帳號不存在 + * - 未設定信箱,請聯絡管理員 + * @apiError (500) {String} description + * - 資料庫錯誤 + * - 信件範本讀取失敗 + * - 寄信失敗 + */ +const forget = async (req, res, next) => { + const account = req.body.account.toLowerCase() + + const query = { account } + const obj = await Visual.findOne(query, 'publicEmail').catch(dbCatch) + if (!obj) throw new ErrorHandler(404, '帳號不存在') + if (!obj.publicEmail) throw new ErrorHandler(404, '未設定信箱,請聯絡管理員') + const email = obj.publicEmail + const randomNum = Math.random().toString(36).substr(2) //產生亂碼 + await insertActive(account, randomNum).catch(dbCatch) + + //寄信 + const hylink = `${req.protocol}://${req.get('host')}/#/reset_password/${account}/${randomNum}` + const hy_br = `${req.protocol}://${req.get( + 'host', + )}/#/reset_password/${account}/${randomNum}` + const htmlText = await template(hylink, hylink) //hy_br + await sendmail(email, '重設密碼(一小時後到期)', htmlText) + // if (obj.publicEmail.show) + return res.status(200).send({ email }) + // else return res.status(200).send({ email: '您的私人信箱' }) +} + +const valid = require('../../../middleware/validation') +const rules = ['account'] +module.exports = [valid(rules), asyncHandler(forget)] diff --git a/backend/routes/srcs/out/forget/mail/resetPassword.html b/backend/routes/srcs/out/forget/mail/resetPassword.html new file mode 100644 index 000000000..2c8a63ebc --- /dev/null +++ b/backend/routes/srcs/out/forget/mail/resetPassword.html @@ -0,0 +1,378 @@ + + + + + + Password Reset + + + + + +
    + a password reset link is auto sent from NTUEEplus website. +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + +
    + + + Logo + +
    + +
    + + + + + +
    +

    + Reset Your Password +

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    + Tap the button below to reset your customer account password. If you didn't + request a new password, you can safely delete this email. +

    +
    + + + + +
    + + + + +
    + Reset Password +
    +
    +
    +

    + If that doesn't work, copy and paste the following link in your browser: +

    +

    + + http://localhost:1993/reset_password/b07901029/sukz7fdmcvp + +

    +
    +

    Sincerely,
    NTUEE plus web team

    +
    + +
    + + + + + + + + + + + + + +
    +

    + You received this email because we received a request for [forget password] for + your account. If you didn't request [forget password] you can safely delete this + email. +

    +
    + +

    +
    + +
    + + + diff --git a/backend/routes/srcs/out/forget/mail/template_generator.js b/backend/routes/srcs/out/forget/mail/template_generator.js new file mode 100644 index 000000000..a33157c0c --- /dev/null +++ b/backend/routes/srcs/out/forget/mail/template_generator.js @@ -0,0 +1,24 @@ +const { JSDOM } = require('jsdom') +const jquery = require('jquery') +const path = require('path') +const { ErrorHandler } = require('../../../../error') +/** + * generate email with beautiful button + * @param {String} href hyper link to the reset password page + * @param {String} href_br hyper link for user to copy (contain \) + * @return {String} html text + */ +module.exports = async (href, href_br) => { + const DOM = await JSDOM.fromFile(path.join(__dirname, './resetPassword.html'), { + contentType: 'text/html', + }).catch((e) => { + console.log(e.message) + throw new ErrorHandler(500, '信件範本讀取失敗') + }) + const { window } = DOM + const $ = jquery(window) + $('#reset_button').attr('href', href) + $('#reset_blank').attr('href', href) + $('#reset_blank').text(href_br) + return window.document.documentElement.outerHTML +} diff --git a/backend/routes/srcs/out/forget/main.js b/backend/routes/srcs/out/forget/main.js new file mode 100644 index 000000000..8ed0e5684 --- /dev/null +++ b/backend/routes/srcs/out/forget/main.js @@ -0,0 +1,8 @@ +const express = require('express') +const router = express.Router() + +router.post('/forget', require('./forget')) + +router.post('/activation', require('./activation')) + +module.exports = router diff --git a/.env b/client/.env similarity index 100% rename from .env rename to client/.env diff --git a/.eslintrc.js b/client/.eslintrc.js similarity index 100% rename from .eslintrc.js rename to client/.eslintrc.js diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 000000000..4a4a82392 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,25 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules +package-lock.json +# yarn.lock + +# testing +/coverage + +# production +/dist + +# misc +.eslintcache +.DS_Store +.idea +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/client/package.json b/client/package.json new file mode 100644 index 000000000..ca41c9bf0 --- /dev/null +++ b/client/package.json @@ -0,0 +1,102 @@ +{ + "name": "@ntuee-plus/endofweb", + "description": "NTUEEPLUS", + "version": "0.0.1", + "config": { + "coreui_library_short_version": "4.0" + }, + "homepage": ".", + "copyright": "Copyright 2017-2021 creativeLabs Łukasz Holeczek", + "license": "MIT", + "private": true, + "repository": { + "type": "git", + "url": "git@github.com:coreui/coreui-free-react-admin-template.git" + }, + "dependencies": { + "@babel/core": "^7.14.8", + "@babel/polyfill": "^7.12.1", + "@babel/preset-env": "^7.14.9", + "@babel/preset-react": "^7.14.5", + "@coreui/chartjs": "3.0.0", + "@coreui/coreui": "4.0.0", + "@coreui/icons": "^2.0.1", + "@coreui/icons-react": "^2.0.0-rc.0", + "@coreui/react": "4.0.0-beta.2", + "@coreui/react-chartjs": "2.0.0-rc.0", + "@coreui/utils": "^1.3.1", + "@dsalvagni/react-profile-picture": "^1.0.0", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.11.2", + "@material-ui/lab": "^4.0.0-alpha.60", + "@reduxjs/toolkit": "^1.6.1", + "@wojtekmaj/enzyme-adapter-react-17": "^0.6.2", + "aos": "^2.3.4", + "axios": "^0.21.1", + "babel-loader": "^8.2.2", + "chart.js": "^3.4.1", + "classnames": "^2.3.1", + "core-js": "^3.15.2", + "crypto": "^1.0.1", + "crypto-browserify": "^3.12.0", + "css-loader": "^6.2.0", + "emailjs-com": "^3.2.0", + "enzyme": "^3.11.0", + "fb-react-sdk": "^1.0.4", + "file-loader": "^6.2.0", + "html-react-parser": "^1.2.8", + "html-webpack-plugin": "^5.3.2", + "jodit-react": "^1.1.1", + "path": "^0.12.7", + "prop-types": "^15.7.2", + "react": "^17.0.2", + "react-app-polyfill": "^2.0.0", + "react-dom": "^17.0.2", + "react-easy-crop": "^3.5.2", + "react-error-boundary": "^3.1.3", + "react-facebook-login": "^4.1.1", + "react-masonry-css": "^1.0.16", + "react-redux": "^7.2.4", + "react-router-dom": "^5.2.0", + "react-router-hash-link": "^2.4.3", + "react-tooltip": "^4.2.21", + "react-tsparticles": "^1.39.0", + "react-vertical-timeline-component": "^3.3.3", + "redux": "4.1.0", + "sass-loader": "^12.1.0", + "simplebar-react": "^2.3.4", + "stream-browserify": "^3.0.0", + "style-loader": "^3.2.1", + "webpack": "^5.47.1", + "webpack-cli": "^4.8.0" + }, + "devDependencies": { + "auto-changelog": "~2.3.0", + "eslint": "^7.30.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^3.4.0", + "prettier": "2.3.2", + "react-scripts": "^4.0.3", + "sass": "^1.35.1", + "tsparticles": "^2.0.6" + }, + "scripts": { + "local-build": "webpack --watch --mode development", + "build": "webpack" + }, + "bugs": { + "url": "https://github.com/coreui/coreui-free-react-admin-template/issues" + }, + "jest": { + "collectCoverageFrom": [ + "src/**/*.{js,jsx}", + "!**/*index.js", + "!src/serviceWorker.js", + "!src/polyfill.js" + ] + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } +} diff --git a/client/public/favicon.ico b/client/public/favicon.ico new file mode 100644 index 000000000..3b0e7e62a Binary files /dev/null and b/client/public/favicon.ico differ diff --git a/client/public/historyData.json b/client/public/historyData.json new file mode 100644 index 000000000..9a79e3ae9 --- /dev/null +++ b/client/public/historyData.json @@ -0,0 +1,62 @@ +{ + "history": [ + { + "grade": "B03", + "title": "Founder of NTUEE+", + "people": [ + { "img": "https://i.imgur.com/bguic7F.png", "name": "許秉鈞" }, + { "img": "https://i.imgur.com/IwgjwsG.png", "name": "袁培傑" }, + { "img": "https://i.imgur.com/B4EiDBq.png", "name": "楊景鈞" }, + { "img": "https://i.imgur.com/qxBr5XZ.png", "name": "劉禹辰" } + ] + }, + { + "grade": "B04", + "title": "2nd NTUEE+", + "people": [ + { "img": "https://i.imgur.com/9mzDnFH.png", "name": "蔡忠紘" }, + { "img": "https://i.imgur.com/m9wjMkO.png", "name": "陳曦" } + ] + }, + { + "grade": "B05", + "title": "3rd NTUEE+", + "people": [ + { "img": "https://i.imgur.com/fAEKoAl.png", "name": "莊永松" }, + { "img": "https://i.imgur.com/wewZ26k.png", "name": "趙冠豪" } + ] + }, + { + "grade": "B06", + "title": "4th NTUEE+", + "people": [ + { "img": "https://i.imgur.com/bkdii7Y.png", "name": "鄭謹譯" }, + { "img": "https://i.imgur.com/NfzeSWo.png", "name": "李筠婕" } + ] + }, + { + "grade": "B07", + "title": "5th NTUEE+", + "people": [ + { "img": "https://i.imgur.com/umMRS1L.png", "name": "陳君輔" }, + { "img": "https://i.imgur.com/IYmBNEr.png", "name": "余欣澄" } + ] + }, + { + "grade": "B08", + "title": "6th NTUEE+", + "people": [ + { "img": "https://i.imgur.com/x6Q9l52.png", "name": "王友廷" }, + { "img": "https://i.imgur.com/eOWLfEO.png", "name": "卓昱辰" } + ] + }, + { + "grade": "B09", + "title": "7th NTUEE+", + "people": [ + { "img": "https://i.imgur.com/5psCEQS.png", "name": "巫竑儒" }, + { "img": "https://i.imgur.com/GsStb6e.png", "name": "賀崇恩" } + ] + } + ] +} diff --git a/public/index.html b/client/public/index.html similarity index 58% rename from public/index.html rename to client/public/index.html index 7ef4dec76..85d2d84c7 100644 --- a/public/index.html +++ b/client/public/index.html @@ -8,43 +8,45 @@ --> - - - - - - - CoreUI Free React.js Admin Template + + + + + + NTUEE+ - - + + + - +
    + + To begin the development, run `npm start` or `yarn start`. + To create a production bundle, use `npm run build` or `yarn build`. + + --> diff --git a/client/public/interview_1.json b/client/public/interview_1.json new file mode 100644 index 000000000..5d54032cf --- /dev/null +++ b/client/public/interview_1.json @@ -0,0 +1,111 @@ +{ + "top": { + "experience": ["Technical Lead Manager @ Google Brain"], + "hashtags": [ + "系友專訪", + "鄭恆之", + "2007級", + "B93", + "Google", + "MachineLearning", + "SiliconValley", + "CMU", + "PhD" + ], + "name": "2008級 鄭恆之" + }, + "body": { + "body": [ + { + "bigtitle": "一、大學時期", + "bigsections": [ + { + "subtitle": "大學中印象最深刻的經驗及對未來的影響", + "subsection": "先從社團說起,我大學時參加台大合唱團,大二時擔任男高音聲部部長,大三開始擔任指揮。每週都要帶領約一百人的團練習,訓練到領導能力及溝通能力(鼓勵團員一起來練習、向團員傳達自己對於音樂詮釋與團練模式的想法)。比較特殊的經驗是在國家音樂廳當指揮,有了這種大規模presentation的經驗,讓我之後都不容易怯場。\n 另外,我在合唱團交到女朋友,也就是現在的老婆。從大學的時候,我們就常常討論彼此的興趣、課業,後來討論到生涯規劃,就包括去美國念博士班,雖然到美國之後相隔兩地長達五年,但是仍互相鼓勵,最後一起到加州工作、結婚,她在Facebook工作,我在Google工作。所以情場也是很重要的,鼓勵大家嘗試。\n 除了社團以外,我也參加許多系上的活動。參加過三次野台、擔任過一次電機音樂會總召,也參加過電機之夜和電機營。大學參加這些活動帶給我最大的收穫,首先是表達與溝通能力,這是在進入研究所之後,尤其是到美國進入職場之後相當重要的核心能力,但是台灣的教育常常缺乏這方面的訓練,在社團尤其是當幹部的話,較能夠訓練到這部分。有了表達與溝通能力之後,要進一步擁有領導能力,包括如何在團體中具有影響力,還有如何讓大家目標一致,一起完成一件事情。最後是創意,多嘗試不同面向的事情可以增進自己的創意。" + }, + { + "subtitle": "決定研究主題及未來職涯方向", + "subsection": "這跟修課及所做的專題息息相關。我當初修信號與系統時,就覺得很喜歡,這門課最神奇的就是可以把很多資訊量儲存在信號中藉此傳遞、解讀。由於修了李琳山教授的信號與系統,所以就跟著琳山教授做專題,主題是語音辨識。後來在Homer做的主題是音樂信號處理,這可以跟我對音樂的興趣相結合,把研究與自己的興趣相結合,會讓研究比較具有獨特性,因為別人不一定想做這主題,而且做自己喜歡的事總是較有動力。\n 在Homer的時候,我曾跟楊奕軒學長合作過,他幫助我很多,讓我學到,獨立做研究和做已經設計好的實驗非常的不同。那時候常常做一些未知的題目,像是一開始做和弦辨識,這跟我當時喜歡彈吉他有關,和弦影響一首歌給人的感覺,所以我想要從這個小題目做起,也是那時接觸到machine learning ,覺得很喜歡,我對人類如何接收訊息並學習,然後教機器學習非常有興趣,所以就愈來愈往那方向走。" + } + ] + }, + { + "bigtitle": "二、博士時期", + "bigsections": [ + { + "subtitle": "美國與台灣的文化衝擊", + "subsection": "美國相對台灣而言對於師生之間的輩分差異不會太過拘泥,以課堂演講為例:當學生已經將教室的空位坐滿後,即使是 60 歲以上高齡的資深教授晚到也必須和其他遲來的學生一起席地而坐,雖然心中有想要讓座的想法,不過在觀察週遭同學和教授的反應後,發覺在美國先來後到的觀念還是大於師生間的輩分關係。模糊的輩分關係更讓我之後在與教授和公司上級交流時,以平等的地位溝通,更能直截了當的表達自己的想法,不需要特別顧慮輩分而保留心中想說的話。尤其在職場上,清楚、簡短而且明確的表達想法,對於自己在公司決策的影響力非常重要。" + }, + { + "subtitle": "業界和學界的研究內容差異", + "subsection": "兩者在於研究方法是相同的,最主要的差異在於「目標的定義」和「對於成功的衡量標準」,因為這兩項的不同會造成最佳化的方向不同。\n 學界主要重視在領域的創新程度,而業界以 Google 為例,最重要的是研究的結果對於產品是否具有決定性的影響力。我自己的專業領域是機器學習,有些學界研究可能會以增加模型的複雜度,來達到某些程度的創新;然而在業界會同時實用性,若複雜模型需要耗費過多資源和時間來維持運作,那相對於簡單的模型就不會是好選擇。\n 總而言之,在不同地方研究會受到目標定義而依循不同的研究方向和方法。" + } + ] + }, + { + "bigtitle": "三、工作時期", + "bigsections": [ + { + "subtitle": "留在美國工作的決定", + "subsection": "對於是否留在美國其實不斷的改變,大學的時候不清楚美國的狀況,很難去做規劃,因而沒有預設。後來才看出要做Machine Learning、Research等在美國機會很多,相較而言在台灣蠻缺乏的。雖然台灣有很優秀的Startup從事Machine Learning,但真的與矽谷難以比擬。是否留在美國還是要看領域,當時大學沒有事先規劃,但接觸越多,知道產業狀態,也就能給自己目標與做決定。\n 另外,在美國工作,要確保拿到簽證,主要是自身能力的特殊性。一個國家給外國人工作簽證,通常是因為他的能力本國人難以取代,有需求去發簽證雇用他。像在美國STEM,Science, Technology, Engineering and Math,這種理工科人才會在學生簽證後給比較長的實習時間,申請到的工作簽證機率也比較高。另外薪水超過一定門檻的工作拿到的機會也較高,因為薪水高大致上代表在當地就業市場的價值高,意即這樣的人才比較難尋。未來像是入門程式設計這類越來越多人學習的技能,自己可以貢獻的特殊性可能會越來越小,所以最好除了程式外,還要有其他領域的專業會更好。" + }, + { + "subtitle": "進入 Google 的契機和工作內容", + "subsection": "一開始我是藉由實習得到轉正職的機會,並且參與 Google Brain 的研究計畫。工作內容分成主要分三個階段,第一階段在 2014 年,此時在建構 Large-Scale Machine Learning Platform,實際內容涵蓋 Data Pipeline、利用分散式系統建立快速訓練的平台、利用平行化的技巧做快速的預測和推論。\n 第二階段是從 2015 年到 2017 年,此時將 Deep Learning 導入原先沒有使用機器學習的產品上,例如 Google Search、Google Play、Google System 和 Youtube。除此之外還有從自己的專案「Wide & Deep Learning」開始研究,後來有實際應用在產品上,最終對外界開源 Tensorflow 1.0,被其他學術單位和公司所使用。這個時期讓我發現在 Google 內部做的研究,事實上不只能影響公司產品也能遍及全世界。\n 第三階段主要在做「Neural Sequential Interaction Modeling」,也就是透過一連串使用者和系統的互動來學習,例如對話系統、Google Duplex Assistant。另外,雖然很多產品都開始導入 end-to-end 的學習方法,但考量到 Rule-based method 能精準控制的特性,仍會採用能確保特定嚴重錯誤不會在系統中發生,整體而言是雜揉 Deep Learning 和 Rule-based method 的混合系統。" + }, + { + "subtitle": "Google技術突破的浪潮發展方向", + "subsection": "近年發展的 Deep Learning 能從 Data 裡面自動抽取出很好的 high level feature,做 Perception 的任務作得特別好,而接下來的挑戰是從這些feature當中去做reasoning,如:從對話中抽出feature後去回答問題。由於reasoning是需要common sense的,亦即對世界有基本瞭解(如:影像需符合物理規則,人說的話必須make sense),知道什麼是合理/不合理,這如果能藉由人類給的knowledge base/rule直接學習,而非只能從大量data輸入輸出學到,可能會有比較大的突破。\n 另外像是自然語言處理(NLP)還有很多挑戰,它與電腦視覺(CV)的其中一項差異是,因為影像pixel的分布是自然界生成的規則,其實動物也有辨識能力。反之語言則是人類發明特有的,造成了人類與動物的差異,所以教機器學語言可能是更複雜、更艱難的任務。" + }, + { + "subtitle": "進入Google Brain等研究機構所需要的能力與特質", + "subsection": "我們重視的能力特質有technical跟non-technical的部分:\n Technical的部分,研究經驗跟在業界的應用經驗都會加分,除了會做研究(有publication),也需要知道業界的研究方向要如何調整,才會對產品有好的實際幫助與影響力,當然寫程式、軟體工程的基本能力也是非常重要。\n Non-technical第一個看的是,一個人是否有成長型思維(growth mindset),它的相反固定型思維(fixed mindset)是覺得天賦已既定,是天生被給予的,也因此只作自己擅長的,不作自己不擅長的。growth mindset則認為任何事情都是學得來的,與先天無關,無論是硬實力或軟實力。這方面可以看一個人一路走來的軌跡,有沒有一直在進步,進步的速率如何。有恆毅力,長期認真做事、成長,與未來的表現常常是有正相關,我們也會傾向優先考慮有這樣特質的人。\n 第二是動力,通常我們會問說面試者接下來自己的目標,越是能能說出目標及明確原因的人,通常會比較有動力,有內在動力自己去前進,想要進步成長,最終的表現也常常會更好。\n 另外溝通表達能力還有領導力也很重要,比較會錄取傾向collaboration跟team work,而非單打獨鬥的人。" + }, + { + "subtitle": "近年Deep Learning走紅,Data Scientist職位變多,盛況在未來會不會有所飽和?不如碼農般穩定?", + "subsection": "我的看法是相反,research會一直有需求,但是research的內容會一直在演進,所以必須要適應力很強,有調整很快的心理準備。例如在過去幾年,光是會調deep learning model參數,可能就可以滿足工作需求,但是現在有AutoML等技術出現,可以自動優化model並且取代了調參數的人力,所以做研究的人就必須繼續往前做更難的研究。譬如AutoML雖然在supervised task上表現好,但如果是剛剛所提到需要機器去reasoning的task,還有更多問題需要解決,也需要更多人去研究。\n 而碼農缺,可能不像大家想的有固定需求,例如現在有研究領域是作 Program Synthesis,從 Pseudocode 成出 code 等等,用來輔助、加快並取代code中重複的部分,當程式生成越來越成功,其實碼農缺就會減少了,而研究會一直會有需求。碼農這個詞也暗指了不需要過多思考,只要專心老實的寫code,自然取代性比較高。機器可以取代人類反應很快就能處理的任務,因此鼓勵大家立志往比較深入的研究去走,會比較長遠。" + } + ] + }, + { + "bigtitle": "四、未來規劃", + "bigsections": [ + { + "subtitle": "規劃職涯的方式", + "subsection": "我在職涯規劃上,基本上大學申請研究所的時候思考一次,研究所快畢業的時候也思考一次。到了工作之後,幾乎每一兩個月都會跟主管以及員工討論在職涯上的規劃。\n 我覺得有幫助的是常常問自己喜歡的是什麼,很常做選擇,很常逼自己把原因寫下來。我個人在做重要決定時,會去想決定的核心價值,通常都有優缺點,每個選擇都有好壞,最後的選擇還是與價值觀有關,訓練自己嚴格的排序並說出為什麼,就能較輕易的決定。根據當下有的資訊做出最好的判斷,自己也不會猶豫、後悔,只會全心投入。\n 此外,我覺得要找一個終極的目標,我的目標就是期許自己能夠在人工智慧這個研究領域成為一個成功的領導人,以人工智慧影響其他不同的產業,並且讓這些產業產生重大、正向的改變。我希望人類在未來能夠與人工智慧有效合作,提高創造力與生產力。同時,我也希望我能成為一個有遠見以及有影響力的人。由於自己對於舞台表演藝術的熱愛,我也喜歡將自己的想法傳達給觀眾,因此我也想要透過寫書或演講的方式,來影響更多人,也希望能夠幫助他們做出正向的改變。\n 我現階段的規劃是希望能從帶領一個實驗室,成長到帶領某個領域之下的多個實驗室,並且影響多這項重大產品事業的走向。如果有好的機會的話,在未來嘗試創業也是有可能的。" + }, + { + "subtitle": "職涯發展迅速的關鍵", + "subsection": "我認為溝通表達能力是一件很重要的事情。臺灣的理工學生,技術面都已經很強了,但是在口頭報告上,卻沒有辦法完整地將自己所知道的內容表達出來。練習口頭表達也會讓你變得比較不怕去爭取機會。在職場上,不要放過任何機會,並且每次都以高品質建立個人品牌,這樣別人也比較容易將任務交給你完成;而每當你完成一項任務,就會再有新的工作機會出現,而每次你都全力以赴,做好自己的工作並且能整地表達,我認為這是一件很重要的事情。" + } + ] + }, + { + "bigtitle": "五、給學弟妹的建議", + "bigsections": [ + { + "subtitle": "及早建立個人品牌", + "subsection": "建立起自己的品牌,找到屬於自己獨特的主題(T型人才的長腳,也就是自己的專長),然後持續經營,有助於在申請博士或未來申請工作的時候,在眾多申請者中脱穎而出。像我當初就覺得我的主題與多媒體信號的處理和理解有關,所以大三、大四就開始經營這方面,修了很多語音、影像辨識等等相關的課,也做了專題,所以在這方面累積了很多經驗,對未來申請幫助很大。申請博士班的時候,有各種不同主題的實驗室供我們選擇,雖然不一定找得到跟自己最初主題完全一樣的實驗室,但是提早經營自己的主題還是可以幫助我們找到主題最接近興趣的實驗室。" + }, + { + "subtitle": "大學課業很繁重,學長鼓勵學弟妹一定要嘗試的事情", + "subsection": "我很鼓勵大家可以多嘗試,去社團訓練領導力與創意等。另外,我認為我在大學時比較沒有做,但鼓勵大家去嘗試的,是實習。我認為可以多嘗試不同的實習,尤其是要出國的學弟妹們,可以先考慮在臺灣有實習的工作經驗,先行體驗臺灣的工作環境與工作文化,再到國外實習並且學習不同的環境與文),這樣比較能夠知道各個不同工作的優缺點。這有點像是蒐集 Data 來 Train 自己的感覺,否則如果沒有足夠的經驗,很容易產生盲點而影響未來的發展。" + }, + { + "subtitle": "畢業後需掌握能力或特質", + "subsection": "數學基礎很重要,如果是做研究,後面很多都是基本數學基礎再去學習。還有研究能力,也就是去找新問題解的能力,因為大部分課程都是在教解問題,這也是工程師必備的,但找值得解的問題就進入到研究,而這可以在專題研究學到。\n 高EQ,樂觀積極的態度,因為一定會有很多挫折,不要讓自己進入負面循環。高EQ對領導力也很重要,首先要先察覺自己的情緒,將自己維持在能保持高生產力的情緒狀態,進一步要能察覺團體的情緒。" + } + ] + } + ] + }, + "annotation": { + "annotation": [ + { + "job": "撰寫", + "contributer": "俞建琁、王廷峻、莊永松、吳建翰、鄭謹譯、李筠婕" + } + ] + }, + "id": "1912" +} diff --git a/client/public/interview_2.json b/client/public/interview_2.json new file mode 100644 index 000000000..8b433b110 --- /dev/null +++ b/client/public/interview_2.json @@ -0,0 +1,134 @@ +{ + "top": { + "experience": ["CEO/ Founder @ Ganzin Technology, Prof. @ NTUEE"], + "hashtags": ["系友專訪", "簡韶逸", "1999級", "B95", "台大電機系教授", "Ganzin", "NTUEE", "PhD"], + "name": "1999級 簡韶逸" + }, + "body": { + "body": [ + { + "bigtitle": "一、前言", + "bigsections": [ + { + "subtitle": "前言", + "subsection": "此次我們前往Ganzin(見臻科技)位於新店的辦公室採訪其創辦人簡韶逸教授。簡韶逸教授專精於多媒體訊號處理系統、多媒體積體電路設計與晶片系統設計方法研究,於2018時創辦的Ganzin從台灣大學spin off。自此,簡韶逸教授身兼Ganzin的執行長。Ganzin 致力於開發可以與AR/VR模組整合的眼動追蹤模組(eye tracking solution)。Ganzin 名稱取自台語「眼神」一詞的諧音,亦有英文「凝視」(Gaze) 之意。而其主要產品 — 眼動追蹤模組Aurora,在2020 年的CES 科技大展一舉奪下Innovation Award,其微小、高續航力與易整合性受到各界矚目。我們採訪簡韶逸教授從求學、任職台大教授,到創辦Ganzin的過程,一窺捕捉眼神的奧秘。" + } + ] + }, + { + "bigtitle": "二、求學階段", + "bigsections": [ + { + "subtitle": "從大學專題中得到的啟發", + "subsection": "簡韶逸教授對於研究的熱情緣起於大學時期的專題研究。他大學時加入陳良基教授的實驗室進行專題研究。他對影像有熱忱,陳良基教授的專長剛好與影像處理相關,於是開啟兩人往後多年深厚的師生緣分。簡韶逸教授回憶起大學專題研究的時光,他認為大四這段做研究的經驗甚至比後來讀研究所時還要更美好。由於大四時課業壓力減輕,於是他幾乎將全副精力投入在研究上。關於這段專注於研究的經驗,他分享道:「心理學上有一個狀態:人心無旁騖專注在做一件事情時,會獲得特別多的快樂。我覺得那時候比較像這種狀態。」對於大學部學生參與專題研究,簡韶逸教授認為這是有別於課堂被動吸收知識的重要學習過程。他認為大學生在修課時大部分時候都是被動吸收老師教的內容。而專題研究則是第一次向未知的領域探索,因此他非常鼓勵台大電機大學部的學生參與專題研究。簡韶逸教授自認十分幸運,在大學時期便體會到做研究的樂趣,並且在大四時受到學長鼓勵投稿了一篇論文,獲得在會議裡發表研究的機會。也因為這段美好的經驗,使他大學畢業後便決定繼續跟隨陳良基教授讀碩士。" + }, + { + "subtitle": "決定研究主題並思索未來職涯方向", + "subsection": "初讀碩士時,簡韶逸教授對於自己想做的研究方向已有明確框架。但他考量到自己的研究願景似乎太大,於是他便決定跟隨陳良基教授直攻博士完成此研究。他回憶當時做此決定時台灣電子業的情景:「那是台灣電子業蠻好的時候,那時候聯發科才剛要起來。我本來想說念完碩士就要去園區工作。」但沒想到後來體會到研究的樂趣無窮,於是便一頭栽進博士生涯。簡韶逸教授三年便拿到博士學位,以當時的環境畢業即就獲得教職的機會微乎其微,於是他當時心中並沒有立刻回學校教書的想法,而是決定要投入業界。" + } + ] + }, + { + "bigtitle": "三、工作時期", + "bigsections": [ + { + "subtitle": "邁入業界—於廣達電腦的一年", + "subsection": "簡韶逸教授於2003年至2004年任職於廣達電腦。我們好奇為何簡韶逸教授選擇到廣達電腦呢?簡韶逸教授談到當時他有許多選擇,而廣達電腦吸引他的最大原因是— 當時仍是代工廠的廣達要進行一項具野心的計畫 — 開發手機晶片。簡韶逸教授說:「我是一個蠻喜歡挑戰的人,廣達吸引我的原因就是因為他要做一個蠻有挑戰性的東西。雖然那個晶片計畫做的內容跟我的博士研究是完全無關的,但我想說沒關係,我就嘗試。」簡韶逸教授和另一位學弟一起領導這個手機晶片團隊。這一年他們頗有進展,成功設計出了晶片。他回憶當時使他快速成長的除了熟悉晶片開發經驗,更是領導團隊的能力。他回憶道:「很多人去到大公司變成一個螺絲釘,但我去廣達是帶領一個團隊,我的老闆就是協理(英文是AVP),在廣達電腦裡算很大的。跟我們來往的都是個公司的副總、技術長。」" + }, + { + "subtitle": "回校擔任教職", + "subsection": "至於為何後來又重回校園擔任教職呢?簡韶逸教授表示在學校做研究與在業界仍有很大的落差。他坦言:「跟研究相比,業界沈悶許多。」在業界,他們面對的是產品,因此他們必須花大量時間debug,創新的部分因而變得很少。當時台大剛好有一個教職機會,於是他申請了此機會,沒想到錄取了,因此得以重返校園與研究生活。但綜觀在廣達電腦的這一年四個月的時間,簡韶逸教授仍覺得這段經驗對他影響深遠。由於這一年的工作經驗,他得以完整地了解晶片專案從頭至尾的開發流程,也讓他認知到學校的資訊與產業界相比有些落後。當時產業界多已開始做系統晶片(System on Chip,簡稱SoC),但學校當時仍沒有相關課程。於是他利用在廣達電腦的那一年吸收做SoC的相關知識,後來他一回學校教書便開設相關課程給學生修習。" + } + ] + }, + { + "bigtitle": "四、創業", + "bigsections": [ + { + "subtitle": "不忘指導教授的教誨,埋下創業的種子", + "subsection": " 談到恩師陳良基教授對他的影響,簡韶逸教授笑道:「我們長期被他洗腦台大電機的學生應該創業,create工作機會。」因此,他同門師兄弟許多人後來紛紛創業,創業彷彿便埋在他們心中一件「一定要做的事情」。擔任教職後,簡韶逸亦不忘傳承陳良基教授的諄諄教誨,他笑著說:「我也把他洗腦的方式學起來。我也一直告訴學生要去創業。結果沒有一個學生真的去創業,畢業以後都去大公司了!」 他十分疑惑為何自己的學生們都不選擇創業。於是他開始思考是不是從自己開始,帶領學生一起出來創業。後來,同事葉丙成教授的創業,也給了他很大的震撼。於是這股要帶著學生一起創業的決心漸漸萌芽茁壯。因緣際會之下,簡韶逸教授看到了由經濟部推動產學合作的「價值創造計畫」(簡稱價創計畫)。價創計畫由政府補助教授將優秀的研究成果朝向商品化、事業化。讓申請的教授有一段時間在政府的補助下將技術轉變成產品,再尋找外部投資人。於是他申請了價創計畫,也成功得到計畫的補助,總共補助了兩千萬元。這時,他向學生提出一起創業的想法。他說:「想不到他們(學生)就答應了!」" + }, + { + "subtitle": "捕捉眼神 – 見臻科技的創立", + "subsection": "於是簡韶逸教授和十多位學生一起創辦了見臻科技(Ganzin), 主攻的核心產品— 眼動追蹤(eye tracking solution),就是從簡韶逸教授的實驗室裡開發出來的技術。我們好奇當初為何會挑選眼動追蹤為創業題目呢?簡韶逸教授答道:「當然我們實驗室其實做了很多技術,老實說眼動追蹤不是我們實驗室最酷炫的技術,我們還有更厲害的。但我們在提價創計畫時的proposal的時候要寫一個business plan。當你在想這東西如何商品化時,你要開始去估這個東西的價值主要是什麼,他的business model 是什麼。他要怎麼可以到市場上面去。你開始想這些問題時你會發現很多技術是沒有辦法商品化的,或者是他的門檻非常高。眼動追蹤就是我們覺得好像商品化的門檻是最低的。而且我們後來也發現eye tracking 是很多場域都需要的,所以我們可以估出一個蠻大的市場。」而Ganzin採用的商業模式是台灣最常見的,也最成熟的模式 — 做出關鍵零組件 (key component )或關鍵解決方案(solution),然後提供給系統廠。Ganzin的客戶,便是製造智慧眼鏡、VR眼鏡的系統廠。這些眼鏡,就需要眼動追蹤技術。據簡韶逸教授的觀察,因為台灣的晶片設計與製造的技術優勢和地緣優勢,這些系統大廠要做產品時多會來台灣,所以在台灣做key component或solution是相對簡單,甚至是全世界最簡單的地方。於是他認為:「我們的出海口很明確。」另外,當時眼動追蹤在市面上的敵人不多。簡韶逸談到原因:「因為很多做eye tracking的公司都被買光了,有一陣子只要eye tracking的公司一出來就被買走了。比如Google買了一間,Facebook 買了一間,Apple 也買了一間。」而Ganzin主要的對手是瑞典的公司Tobi。簡韶逸創業前曾評估:「我當時覺得我們應該可以打的過他。」種種因素,促成眼動追蹤成了當時創業題目的首選,是簡韶逸教授認為要創業最簡單的一個題目。但他笑道:「後來發現,最簡單其實還是蠻困難。比如說瑞典公司Tobi,我們後來發現他其實是一個超大的公司,全球員工有好幾千人。我們發現眼鏡其實也蠻難做的,很多東西其實當時也想不到,都是想起來很簡單,做起來很難。" + }, + { + "subtitle": "眼動追蹤應用遍地開花", + "subsection": "簡韶逸教授提到,現階段已經有許多含有眼動追蹤技術的產品,但都是小量的特定的人在使用,還未普及,所以大眾對眼動追蹤的應用仍然十分陌生。關於已經在使用的應用,他提起:「眼動追蹤其中一個應用『眼睛寫書』,這項應用的使用者就有知名霍金博士。Intel有一個團隊專門幫他打造整個系統,但只給他一個人用。」另外,已有少數人使用的例子還有市調用眼鏡,消費者戴上之後,走進一個賣場,眼動追蹤技術可以偵測消費者在賣場裡看了哪些商品。但簡韶逸教授也說:「但是那個眼鏡一支要賣100萬。」於是簡韶逸教授希望眼動追蹤的技術將來可以推廣到大眾身上,以更便宜的價格,讓眼動追蹤可以顛覆使用者與產品的互動體驗。" + }, + { + "subtitle": "AI 眼鏡的互動介面與智慧家庭助理", + "subsection": "談起眼動追蹤未來如何走入大眾視野。第一最直覺的應用就是AI眼鏡的互動介面。未來的智慧眼鏡,人的視野中會浮現一張選單,可以利用眼睛去點選選單(比如凝視久一點的應用程式會自動打開)。第二個應用可以呈現未來智慧家庭的可能樣貌。簡韶逸教授說:「假設你回到家,你想開一盞燈,現在不是大家家裡都有語音助理嗎。但你家可能有20盞燈,你怎麼讓語音助理知道你要開哪一盞,這樣你要幫每一盞燈都取名字,太麻煩了。要是你有帶eye tracking的話,你就只要看這個燈,說:『幫我開那盞燈。』」簡韶逸教授又接著笑說:「這個我有時候跟一些女生講他們就會開始想……,我舉一例子,比如你走在路上看到有一個人穿的鞋子很漂亮,你眼睛看著那雙鞋子,你可以直接問:『這雙鞋子多少錢?在哪裡能買到?』照相機馬上拍下來,上網search,幫你做虹膜辨識就直接付款了。或是走在路上看到一家餐廳,眼睛看著餐廳問:『這家餐廳好不好吃?』眼鏡直接告訴你幾顆星評價如何。」簡韶逸教授表示現階段,眼動追蹤可能產生的互動方式是我們現在想像不到的,但這些互動模式並非很遙遠。比如他們已經在許多科技展場實際demo 利用眼睛直視打開電燈。" + }, + { + "subtitle": "虛擬實境", + "subsection": "另外,在虛擬實境(Virtual Reality, 簡稱VR)中,為了讓使用者看到的世界越來越逼真,display的解析度會越來越高的,於是使用者要買高檔的顯示卡,才能畫得出逼真美麗的影像。簡韶逸說:「但人的眼睛不是這樣子的,人的眼睛在一個瞬間,只有一個點是清楚的,你是靠著眼睛不斷轉來轉去才把世界建起來的。所以有一個paper 告訴我們說,老實講VR rendering 的動作98%都是浪費的。畫了半天人根本沒有看到,人只看到其中2%。」而現在VR中有一個技術叫Foveated rendering,利用eye tracking告訴系統使用者在看什麼地方,系統只要畫將這個地方畫清楚就好了。簡韶逸說:「我們自己有做實驗啦,至少省十倍運算,十倍是很可怕的數字噢,多玩十倍的時間,或是你不用買那麼貴的顯卡,你買便宜的顯卡,甚至手機就可以玩很棒的VR 遊戲。」" + }, + { + "subtitle": "虛擬社交網路", + "subsection": "最後一個特別的應用是虛擬社交網路(VR social network)。藉由VR social network,人們可以在虛擬世界裡建立聯繫。簡韶逸教授介紹道:「他的概念是說以後大家回家之後就可以帶上一個VR 頭盔,就進到一個虛擬世界,你會有一個avatar(虛擬人),他代表了你。你可以在這個虛擬世界裡跟其他朋友互動,可以一起去看球賽。」他談到特別是現今武漢肺炎疫情爆發的緣故,VR social network 的價值將更受重視。但這跟眼動追蹤的關係是什麼呢?簡韶逸教授答道:「在虛擬世界,這個虛擬人avatar的眼睛要是不會動,他不就是一個殭屍嗎?所以VR 頭盔裡一定要有eye tracking,他才可以拍攝我們的眼睛,把我們的眼動資訊傳遞到avatar上面。」他表示,當人跟數位世界緊密結合時,必須要把眼睛的資訊捕捉下來,傳到數位世界,才能使人跟數位世界融合在一起。他說:「所以眼睛真的是靈魂之窗!Facebook 的 chief researcher 甚至說:『eye tracking 就是VR 的核心!』」" + }, + { + "subtitle": "創業的荊棘之路----從學界與產業界", + "subsection": "談起創業面臨的挑戰。簡韶逸教授回憶道他剛創業時感受到產業界與學術界的巨大鴻溝。談起學術研究,簡韶逸教授的研究成果不只受到學界肯定,屢屢在國際比賽得到 best paper award,產業界亦重視他的研究成果。曾經一位Sony的一個RD總監級的人物對簡韶逸教授說,他常常在看簡韶逸教授的論文,他覺得的簡韶逸教授的實驗室是全亞洲最好的multimedia IC 的實驗室。類似的肯定讓簡韶逸教授對自己實驗室的研究成果有信心。但當他審視自己的實驗室的技術如何商品化時,他卻發現這些技術離產品差很遠。以eye tracking 為例,他說:「我們就是幾個同學試過覺得好像可以用而且performance 還不錯我們就覺得沒問題了。可是真的出來創業的時候我們就發現這個東西要變成產品其實距離蠻遙遠的。比如他們(客戶)就會開始問說那其他人(比如外國人)可不可以用?所以我們有一個lab 在測試大量的受試者,我們開始去搜集很多人的資料,以確保我們產品是每個人都可以用的。光這些事情就要花非常多的錢跟時間。」" + }, + { + "subtitle": "面對客戶的需求", + "subsection": "簡韶逸教授以技術背景的角度出發,與客戶接觸時,也常發現客戶與自己想的不一樣。以Ganzin 的產品Aurora為例 — 這款屢屢在科技大展得獎的眼動追蹤模組,是由三個晶片構成。其中兩個是眼睛感測器(eye sensor)、一個是眼睛處理單元 (eye-processing unit, 簡稱EPU)。對於EPU,簡韶逸教授說:「你可以把它想成computer vision 或AI晶片。他會直接處理左右拍到的兩張影像,算出你的視線方向。」Aurora之所以會造成轟動是因為之前市面上並沒有這麼小,這麼省電,這麼好安裝的眼動儀。一般眼動儀複雜而且昂貴,要價一百萬。但當他們拿著產品 eye tracking solution問客戶要不要買時,客戶說這樣他們無法決定,能不能給他們一些working sample?客戶想要的一個馬上可以動的東西測試。於是Ganzin團隊又做了眼鏡實際demo eye tracking的範例 。接著客戶又問說:「那可不可以讓我的眼鏡也可以很快的可以裝入eye tracking 的功能?」於是Ganzin 將Aurora 改良為一個中央EPU 連接兩條線串起的eye sensor 的輕便模組,方便客戶直接整合進他們的產品。這時客戶終於說:「這就對了!我們就是想要這個!」於是許多客戶開始紛紛跟Ganzin購買眼動追蹤模組測試。簡韶逸教授笑說:「現在客戶都很懶,你要提供全部的東西,包括driver、SDK、一些example的software。整套都要給。但我們現在也慢慢理解到他為什麼要這些東西,因為他要有一個方式去驗證你的東西有多好!」" + }, + { + "subtitle": "產品化的挑戰", + "subsection": "談起創業面臨的挑戰的過程與心得,簡韶逸說:「很多是商業面,還有實務上的問題,這其實都不是學術界會去想的。所以從我們技術完成,到經過價創計畫,到真的開始東西可以給客戶,其實已經三年了吧。過了三年之後東西才真的交到客戶手上。」而拿這段經驗與他在廣達的業界經驗相比較,當時在廣達令他感到沈悶與無聊的產品化過程,這時在Ganzin卻讓他感到非常珍貴。他認為業界累積的實戰經驗主要就是如何將好的技術產品化。他說:「隨時都有好的技術,但是什麼技術可以變成產品? 這個就有很多的關卡要過。」Ganzin的產品目前還未量產,但簡韶逸教授有信心明年可以量產。他說:「如果明年真的成功量產的話,表示我們大概花了四年的時間,才讓他可以從技術變成一個大家都可以用到的產品。希望明年大家就可以買到我們的東西了。將來可能會有一些智慧眼鏡,上面會有顯示器,會有我們的眼球追蹤技術,使用者可以用眼睛去控制東西。」" + }, + { + "subtitle": "Ganzin 的競爭對手— 瑞典公司Tobi", + "subsection": "Ganzin 最主要的競爭對手是瑞典公司Tobi。談到競爭對手Tobi,簡韶逸說道:「他是一個很大的公司,我們一開始沒有意識到這件事情,我們後來才發現他比我們想像中大很多。」Tobi的營收來源大部分來自售價100萬元的眼鏡,Tobi亦有涉獵醫療市場。兩者構成其主要收入來源。而Tobi在PC 遊戲,和VR eye tracking,都是處於虧損狀態。簡韶逸教授說:「這個公司本身就有在大賺錢的東西。我們公司核心完全就是放在他不賺錢的這塊。」簡韶逸教授提到Ganzin的優勢在於,他們的方法和Tobi完全不一樣,Tobi在醫療或研究用途的眼鏡相當貴且複雜,但一般客戶需要簡單好用的東西。而Ganzin就是用簡單好用的東西切入這個市場。另外,簡韶逸教授也強調:在台灣做eye tracking 很有優勢。他重申:「台灣做晶片是全世界最簡單的地方,這些AR、VR眼鏡廠要生產眼鏡也都是來台灣,我常常跟我們同仁講:『天時地利人和。』天時,就是現在是AR、VR、5G的時代,這是一個好的時間點 ; 地利,就是因為我們在台灣,我們正好在一個很好的地點 ; 人和,可能就是靠我們的團隊。大部分都是台大電機的同學。」" + }, + { + "subtitle": "多元的團隊", + "subsection": "Ganzin的團隊現在有25人,其中工程師有20位。而團隊中有一群台大畢業的新鮮人都是簡韶逸教授的學生,也有一群具有經驗的員工。簡韶逸認為這樣的搭配優點在於:台大電機系的學生十分聰明,年輕人也比較有衝勁,而年紀大的員工富有經驗。剛畢業的學生可能不知道業界做事的流程,而業界一般都有一套做計畫的方法、定進度的方法,因此年輕員工可以向有經驗的員工學習。簡韶逸教授談道:「我們公司的RD (research and development) 比較難找。」由於Ganzin的產品本身是跨領域的,必須整合演算法、晶片、電路機構等所有環節才能成為最佳化的產品。因此Ganzin的 RD 背景多元,從ic設計、演算法、system software、application software到機構等等,有各式各樣的人一個團隊當中。簡韶逸教授說道:「這是一個很神奇的現象,20個RD就有這麼多人種在裡面。很多公司不曉得我們才20幾人,他以為我們是幾百人的公司。」" + }, + { + "subtitle": "CEO 的角色", + "subsection": "簡韶逸教授以水桶比喻創業:「有人跟我講一個比喻我覺得蠻好的,他說你要創業成功啊,就好像你要做一個水桶,假設你用木片做這個水桶,這個水桶可以裝多少水,其實是用最矮的木片來決定。創業也是一樣,創業有很多層面,研發只是其中的一個版子而已,一般會講一個模式就是產(生產)、銷(行銷)、人(人資)、發(研發)、財(財務),這每一塊都很重要,當執行長要隨時看這五個面向裡面哪一個是最弱的,你就趕快跳下去補。」簡韶逸教授坦言,自從當了執行長後,他開始摸索許多原本不熟悉的事物,比如與客戶接洽、面試員工、編財務報表、編預算。但他也說道:「我覺得每一塊對我來講都還蠻困難的,但是你就是必須要去做,這也會讓你成長,做完你會更全面去了解整間公司是怎麼去運作的。」創業前簡韶逸教授是台大創意創業學程的老師,他表示他自己從裡面學到許多珍貴的事情,他也常常與系上其他創業的教授互相交流。他常與黃鐘揚教授、葉丙成教授相約交換創業心得。簡韶逸教授談到一個令他印象特別深刻的建議,黃鐘揚教授曾建議他,一定要讓同仁們投資認股。簡韶逸教授初創業時約有十多位學生加入,大家分別買了一部分股票,形成一開始創立公司的資本額。簡韶逸教授回憶道:「學生們馬上表情就變了,本來他們的態度是:『老師你要我們做什麼』,成為股東後態度立刻變得積極許多,開始詢問:『我們接下來應該做什麼!』」" + } + ] + }, + { + "bigtitle": "五、未來規劃", + "bigsections": [ + { + "subtitle": "我們好奇未來簡韶逸教授是否會將重心移回校園呢?", + "subsection": "簡韶逸教授表示,他現在的重心仍在Ganzin。未來倘若Ganzin能夠成功,他希望重返台大將創業成功的經驗帶回學校。他提到:「我其實蠻崇尚矽谷或美國的學校,他們的模式是讓某個老師出去創業,成功了以後他把一些經驗帶回學校,甚至他又可以再去開新的一間。」他認為這個模式是值得台灣學習的。而他也期許自己將來可以將創業成功的經驗帶回校園。" + } + ] + }, + { + "bigtitle": "六、給學弟妹的話", + "bigsections": [ + { + "subtitle": "對台大電機學生創業的期許", + "subsection": "我們請教簡韶逸教授對於台大電機學生之後創業的看法與建議,簡韶逸教授認為創業最重要的條件就是韌性要夠。至於創業的時間點,他認爲大家可以自己決定,大學畢業或許有點早,也可以研究所畢業、工作一段時間再創業。他提到自己曾在MIT (麻省理工學院) 訪問時的震撼:「他們有一個教授問我一個問題:『你知道在MIT,100個學生會有幾個創業嗎?』」答案是120個,因為有些人不只創辦一間公司。簡韶逸教授說:「他們會以改變世界為使命,我覺得台大電機的學生應該也要有這個使命感。」簡韶逸教授提到創業時需要多面向地思考問題,而這會使人成為一個多功能的人。他覺得台大電機的學生應該都有能力可以扮演各種角色。他說:「你在新創可能要自己轉換角色,可能不會只是一個RD工程師,我們有些同學其實蠻厲害的,後來發現他其實是一個蠻好的PM,可以跟客戶溝通,或是發現他是很好的人資,他很會選人。所以我覺得多面向的培養蠻重要的,除了技術以外的事情。」" + }, + { + "subtitle": "生涯規劃的建議與總結", + "subsection": "對於生涯規劃,簡韶逸教授坦言自己想法常常改變。但他仍然以自身經驗勉勵學生:「我高中的時候就有去上生涯規劃課,我那時候聽到一個結論我覺得蠻好的,我一直記到現在。」他說:「生涯規劃就是一件你隨時都要做的事,但是隨時都有可能被你推翻掉。但不能不做,不做你就會渾渾噩噩得過下去。隨時都要想自己明年後年有什麼目標,但這些東西隨時都會變沒關係。」" + } + ] + } + ] + }, + "annotation": { + "annotation": [ + { + "job": "採訪", + "contributer": "羅韻瑢、鄭謹譯 、李筠婕 、吳建翰 、余欣澄" + }, + { + "job": "校稿", + "contributer": "鄭謹譯、余欣澄" + } + ] + }, + "id": "2001" +} diff --git a/client/public/interview_3.json b/client/public/interview_3.json new file mode 100644 index 000000000..cb41d0031 --- /dev/null +++ b/client/public/interview_3.json @@ -0,0 +1,128 @@ +{ + "top": { + "experience": ["17直播 技術副總"], + "hashtags": [ + "系友專訪", + "李昀樵", + "2012級", + "B97", + "Harvard", + "MBA", + "新創", + "創業", + "語音處理", + "17直播" + ], + "name": "2012級 李昀樵" + }, + "body": { + "body": [ + { + "bigtitle": "一、大學時期", + "bigsections": [ + { + "subtitle": "學長在大學時期就對語音產生興趣了嗎?是甚麼原因讓您有興趣呢?", + "subsection": "我大學的時候修了語音實驗室的專題,就覺得對語音相關的資訊很感興趣,事後看來這是一個蠻好的決定,因為碩一時剛好是deep learning在語音開花的時候,也讓我多接觸到 deep learning 這個領域。" + }, + { + "subtitle": "在語音實驗室時做過哪些研究?請問內容與日後工作是否有相關性,以及對您的影響?", + "subsection": "在語音實驗室時主要是做retrieval相關的東西,例如語意檢索(Semantic Retrieval),也就是當你想要尋找的term不存在在文章中但該文章仍跟這個term 有關時,該如何尋找到這篇文章。\n 至於內容是否跟日後工作有關,其實碩士的研究通常跟未來的工作沒有直接的關係,除非做PhD,研究的東西才會跟工作比較有關聯性。碩士主要是培養做研究的能力,也就是教你如何藉由大量閱讀 Paper 和重置他人實驗來學習某個領域最尖端的知識,這其實是很重要的能力。" + }, + { + "subtitle": "為何會對新創事業感到有興趣?", + "subsection": "大三的時候加入了創創學程,當時在創創學程開了非常多的眼界,然後認識了台大各系關於這方面的人才,日後發現這些人在今日的新創產業中仍非常的活躍。大四時跟外系的幾個朋友參加了一個創意競賽,當時其他同學有很好的發想,但不知道如何用程式寫出來,所以就找了身為電機系的我,最後大家一起完成了一個 prototype。之後同一群人也一起做了幾個 app 和產品,因為有各系各個領域的人,所以大家分工,我負責開發,有人負責宣傳。最後甚至跟這群人一起開了公司,甚至在學期中請假到矽谷募資,是個很有趣的經驗。" + }, + { + "subtitle": "系上的課業繁重,有什麼是鼓勵學弟妹在大學中一定要嘗試的事情?", + "subsection": "我會鼓勵大家在各方面都要勇敢嘗試,不能只顧課業。事後看來,大學期間對我幫助最大的事情,在當時看來都是不知道對未來有什麼幫助的,只是覺得好玩就決定試試看。比如參加陽光椰子社、台大藝術季、創創學程、參加創意競賽、做 Side Project、開公司等等。另一方面對我幫助最大的是大學期間認識的朋友,每個人都有獨特的背景、故事和能力,從他們身上我得到了很多啟發。" + } + ] + }, + { + "bigtitle": "二、生涯規劃", + "bigsections": [ + { + "subtitle": "對於出國讀研、在臺讀研、直接工作的選擇?", + "subsection": "其實在大學時,我對於這些路都有事先做一部分的準備,例如有當兵、準備研究所推徵,出國的部分也有去考 TOFEL、準備 GRE 等等。我最後選擇留在臺灣,很大的原因是我希望能夠陪著家人。另外,因為讀研究所可以培養與實際上的知識接軌的能力,而我當時知道自己仍需要培養這種能力,因此選擇讀碩士。\n 然而,我覺得在大學時自己沒有很積極地找已經出社會的學長姐詢問相關的建議。我也建議學弟妹可以多找大約 30 歲左右的學長姐諮詢,他們可以給予較為成熟的建議。我在申請哈佛 MBA 的時候發現管理學院的學生,會時常向年紀較近,或是已畢業一段時間的學長姐諮詢自己的職涯規劃,我覺得這是一個蠻好的決策輔助。" + }, + { + "subtitle": "當時是否曾考慮繼續就讀博士?", + "subsection": "我當時沒有考慮讀博士,因為自己並非很想走純研究路線,我的興趣主要是在實務中應用這些前端知識。另一個考量是時間,博士通常會讀 4、5 年,這個機會成本也是要一起考量的。" + }, + { + "subtitle": "畢業後自行創業的考量?", + "subsection": "其實在讀碩士的時候我就已經有創業的念頭,那時候已經和許多朋友進行很多 side projects,甚至還大膽地在學期中請假飛到美國矽谷募資、面試美國加速器等等,現在回想起來當時真的是一股衝勁。我覺得人生應該多多嘗試,才會知道自己到底想要什麼,尤其現在資源很多,想試就去試。若你覺得非做不可,那就放手去做吧!\n 但相反地,不要一味地跟風,要思考好再做決定。我覺得對學生來說,直接去風險太高。比較好的方法是做 side projects,如果這個 project 能夠做起來的話,再嘗試全職來做,我自己現在也一直都還有在做 side project。另一方面,我覺得學生通常在經驗和能力方面通常都較為不足,所以如果對新創感興趣的話,先加入一個有潛力的新創學習一下,也是一個好方法。" + }, + { + "subtitle": "今年選擇去哈佛念MBA的考量?", + "subsection": "我覺得這是個增廣眼界的過程,回想起過去,常常人生中重要的轉捩點是在不同環境下遇到不同的人而產生的,而 MBA 就提供你一個不同的環境並能夠跟一群特別的人相處。\n 再來就是在亞洲創業的話,常常會覺得台灣新創產業有個天花板,因此覺得需要跟國際接軌,要學習如何去獲得國外的資源,例如眼界、經驗與資本,來突破這個天花板。" + }, + { + "subtitle": "可以說說現在對未來的規劃嗎?", + "subsection": "下一個目標就是希望創業成功,並且有機會的話能夠回饋社會,找到自己能夠貢獻並幫助社會的地方。" + } + ] + }, + { + "bigtitle": "三、在17直播的經歷", + "bigsections": [ + { + "subtitle": "加入 17 直播的考量?", + "subsection": "我會想加入 17 直播的原因有三個。第一是技術層面,我想加入的是有足夠技術深度與挑戰的公司,而 17 直播是臺灣使用人數最多的直播平台,再加上直播本身也是個有足夠技術挑戰的領域,所以 17 直播是個能提供足夠技術挑戰的公司。第二是創辦人,加入之前我與 17 的創辦人之一黃立成聊過幾次,我覺得他的想法、經驗與視野都有很值得學習的地方。第三是公司文化,我很喜歡新創公司那種自由、自治的風氣,每個人都把公司當成是自己的,大家都盡全力讓公司發展得更好,這是我很喜歡 17 這間公司的地方。" + }, + { + "subtitle": "在 17 直播中扮演的角色?", + "subsection": "我在公司的職位是產品和技術副總,負責規劃產品方向、規劃技術架構和確保產品品質等等,也要負責管理團隊、建立團隊文化等等。" + }, + { + "subtitle": "聘用員工時如何判斷一個人的能力?", + "subsection": "要判斷這名員工是否是公司想要的人才,我大概會從三個方向著手。\n 第一,是否具備硬實力,我通常會列出這個職位必須的技能,再逐項驗證。例如他如果是程式工程師,最重要的技能就是寫程式,我的做法通常是給他幾個程式題讓他當場寫。\n 第二,團隊合作,我必須知道這個人如何與團隊合作,以及判斷這個人是否好相處。我會觀察他在面試中的應對進退和過去經驗,比如是否有團隊精神,是否會太自我或是無法接受他人意見等等。\n 第三,如果是需要帶人的職位,我會確認領導能力,主要會問他一些過去建立與帶領團隊的經驗,以及問對方一些情境題,例如與老闆或員工意見不合的狀況,他如何解決。" + }, + { + "subtitle": "大學、碩士與工作的差距?", + "subsection": "加入 17 直播之後所做的東西大部分是大學或碩士時沒有教過的,也就是說要從零開始學,自己找國外的資源閱讀或參加 conference,跟產業中厲害的人學習,比如說跟這領域的意見領袖聊天理解這個領域的大方向。\n 17直播剛開始做的時候主要是兩個領域,media streaming和distributed system(分散式系統),這兩個當初在台大時沒有太多接觸,只能自己從網路上學,或者找會這些東西的人學,當時幾乎是邊學邊做,系統每幾天就會超載一次,壓力還蠻大的,但我覺得在大學基礎有打穩的話,這些技能學起來還蠻快的。建議學弟在大學時要把核心能力打穩,比如說如果未來要走 CS 的話,那現在應該把作業系統、資料結構、演算法、基本的幾種程式語言等等都學好,至於其他比較細節的東西,之後學起來會很快。至於哪些是核心能力,可以向業界的人聊天,請教學長姐或教授。" + } + ] + }, + { + "bigtitle": "四、給學弟妹的建議", + "bigsections": [ + { + "subtitle": "給未來想要進入新創產業的學弟妹的建議?", + "subsection": "我覺得有三個面向可以講:興趣、技能、選擇。\n 首先就是你要有興趣,並且必須知道新創產業是有風險的,不要一窩蜂覺得新創很好就去加入。\n 再來是技能層面,加入新創產業前,自己要先想好要具備並培養怎麼樣的技能,如果你想進大公司 Google、Facebook,那你應該把大部分的時間投資在 coding 上。但是在新創產業裡,可能要稍微分散一點,對各方面都要略有了解,像是 coding 能力、溝通合作能力、對產品的 sense、對公司策略也要有想法,要能在公司裡找到自己的定位做出最大的貢獻,幫助公司成功。對個人而言,你要為自己未來的發展做規劃,如果創業失敗了,要確保自己這樣的技能在市場上是有退路的。\n 再來是選擇,選擇自己想要加入怎麼樣的新創公司,你可以想像你自己是創投公司。概略來講,他們首先可能會評估市場,評估市場對這個產品的需求、有無競爭力等等,再來要評估創辦人是否足夠優秀,跟團隊有無足夠的能力持續地走下去。而你自己在做選擇時,就是要像創投公司一樣思考值不值得把自己未來幾年的時間投資在這間公司上面。" + }, + { + "subtitle": "環境衝擊與建立新的交友圈", + "subsection": "我覺得在大學時,建立多元交友圈是很有幫助的,每個學院的學生都有自己的文化、思考和做事的方式,在大學的時候多跟不同學院的同學接觸,對於開拓眼界是很有幫助的。台大在這方面提供蠻多方式的,比如修學程、跨系修課,參加各種社團等等。\n 比如我之前暑假到 Boston Consulting Group (BCG) 做 Part Time Assistant,就受到很不一樣的文化衝擊,在電機系我們通常把重點放在 code 的品質,但在 BCG 要學習如何思考商業問題、如何為公司創造價值、和如何跟主管與同事溝通等等,這些不一樣的思考方式後來在職場上對我幫助很大。" + }, + { + "subtitle": "您覺得您成功的關鍵是?", + "subsection": "首先我是覺得我離成功還有蠻大一段距離。如果說回頭看過去幾年的話,我覺得在做決定時,比起選擇可預期的道路,我更喜歡多方嘗試各種方向,其中不乏風險高的方向,比如說加入早期的新創公司。我的感覺是按部就班地規劃未來的人生,反而無法跳脫現有的眼界,多方嘗試才更容易找到新機會和新方向。" + } + ] + }, + { + "bigtitle": "五、與台大電機系的連結", + "bigsections": [ + { + "subtitle": "電機系給您的歸屬感?", + "subsection": "很多好朋友甚至現在一起合作的夥伴都是電機系同屆或上下屆的同學,電機系的老師也一直給我很多幫助,我現在也還是有跟比較熟的老師保持聯繫。" + }, + { + "subtitle": "覺得電機系的有什麼共同特質?", + "subsection": "我覺得電機系的同學非常優秀,在各方面學習理解和創新能力都很強,在我們公司的電機系同學表現也很傑出。我覺得學弟妹們如果能更勇於多方嘗試、不要只走成功率高、低風險的路,電機系的同學能夠在更多元的領域做出好成績。" + } + ] + } + ] + }, + "annotation": { + "annotation": [ + { + "job": "撰寫", + "contributer": "吳建翰、余欣澄、莊永松、鄭謹譯" + } + ] + }, + "id": "1909" +} diff --git a/public/manifest.json b/client/public/manifest.json similarity index 100% rename from public/manifest.json rename to client/public/manifest.json diff --git a/client/public/teamData.json b/client/public/teamData.json new file mode 100644 index 000000000..64db8b6d2 --- /dev/null +++ b/client/public/teamData.json @@ -0,0 +1,80 @@ +{ + "minister": [ + { + "name": "鄭謹譯", + "img": "https://i.imgur.com/tBIMSbr.png", + "part": "網頁" + }, + { + "name": "李筠婕", + "img": "https://i.imgur.com/edivzlk.png", + "part": "留學" + } + ], + "abroad": [ + { + "name": "翁瑋襄", + "img": "https://i.imgur.com/7wuyePm.png" + }, + { + "name": "余欣澄", + "img": "https://i.imgur.com/IYmBNEr.png" + }, + { + "name": "謝承霖", + "img": "https://i.imgur.com/7X2YA9P.png" + }, + { + "name": "周軒羽", + "img": "https://i.imgur.com/uco5sLV.png" + }, + { + "name": "施彥宇", + "img": "https://i.imgur.com/5xXLHbb.png" + } + ], + "back": [ + { + "name": "陳君輔", + "img": "https://i.imgur.com/umMRS1L.png" + }, + { + "name": "李宗倫", + "img": "https://i.imgur.com/sXdgp9o.png" + }, + { + "name": "賴侃軒", + "img": "https://i.imgur.com/AYw8d4f.png" + }, + { + "name": "吳建翰", + "img": "https://i.imgur.com/CCdQe52.png" + }, + { + "name": "王維恩", + "img": "https://i.imgur.com/Sjv06Cg.png" + } + ], + "front": [ + { + "name": "呂承燁", + "img": "https://i.imgur.com/v1lP2kc.png" + }, + { + "name": "王友廷", + "img": "https://i.imgur.com/x6Q9l52.png" + }, + { + "name": "何明翰", + "img": "https://i.imgur.com/0FuDgw1.png" + }, + { + "name": "陳育楷", + "img": "https://i.imgur.com/GsEuD2J.png" + }, + { + "name": "俞建旋", + "img": "https://i.imgur.com/Vh9zeFk.png" + } + ] +} diff --git a/client/src/App.js b/client/src/App.js new file mode 100644 index 000000000..2beef87d8 --- /dev/null +++ b/client/src/App.js @@ -0,0 +1,29 @@ +import React from 'react' +import { BrowserRouter, Route, Switch } from 'react-router-dom' +import AOS from 'aos' +import './scss/style.scss' + +// Containers +const DefaultLayout = React.lazy(() => import('./layout/DefaultLayout')) + +const App = () => { + return ( + + +
    + +
    + + } + > + + } /> + +
    +
    + ) +} + +export default App diff --git a/src/App.test.js b/client/src/App.test.js similarity index 100% rename from src/App.test.js rename to client/src/App.test.js diff --git a/client/src/_navAuth.js b/client/src/_navAuth.js new file mode 100644 index 000000000..8a9b270c3 --- /dev/null +++ b/client/src/_navAuth.js @@ -0,0 +1,27 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import CIcon from '@coreui/icons-react' +import { NavLink } from 'react-router-dom' + +const _navAuth = [ + { + _component: 'CNavTitle', + anchor: 'Administrator', + }, + { + _component: 'CNavItem', + as: NavLink, + anchor: 'MATCHING', + to: '/auth/matching', + icon: , + }, + { + _component: 'CNavItem', + as: NavLink, + anchor: 'REGISTER', + to: '/auth/register', + icon: , + }, +] + +export default _navAuth diff --git a/client/src/_navIn.js b/client/src/_navIn.js new file mode 100644 index 000000000..f2e10b653 --- /dev/null +++ b/client/src/_navIn.js @@ -0,0 +1,47 @@ +import React from 'react' +import CIcon from '@coreui/icons-react' +import { NavLink } from 'react-router-dom' + +const _navIn = [ + { + _component: 'CNavTitle', + anchor: 'Services', + }, + { + _component: 'CNavItem', + as: NavLink, + anchor: 'DASHBOARD', + to: '/dashboard', + icon: , + }, + { + _component: 'CNavItem', + as: NavLink, + anchor: 'CAREER', + to: '/career', + icon: , + }, + { + _component: 'CNavItem', + as: NavLink, + anchor: 'COLUMNS', + to: '/column_summary', + icon: , + }, + { + _component: 'CNavItem', + as: NavLink, + anchor: 'MATCHING', + to: '/matching', + icon: , + }, + /* { + _component: 'CNavItem', + as: NavLink, + anchor: 'STUDY', + to: '/study', + icon: , + }, */ +] + +export default _navIn diff --git a/client/src/_navOut.js b/client/src/_navOut.js new file mode 100644 index 000000000..d257988fe --- /dev/null +++ b/client/src/_navOut.js @@ -0,0 +1,62 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import CIcon from '@coreui/icons-react' +import { NavHashLink } from 'react-router-hash-link' + +const _navOut = [ + { + _component: 'CNavTitle', + anchor: 'Information', + }, + { + _component: 'CNavItem', + as: NavHashLink, + anchor: 'HEADER', + to: '/home/#header', + icon: , + }, + { + _component: 'CNavItem', + as: NavHashLink, + anchor: 'SERVICES', + to: '/home/#services', + icon: , + }, + { + _component: 'CNavItem', + as: NavHashLink, + anchor: 'ABOUT', + to: '/home/#about', + icon: , + }, + { + _component: 'CNavItem', + as: NavHashLink, + anchor: 'INTERVIEWS', + to: '/home/#interviews', + icon: , + }, + { + _component: 'CNavItem', + as: NavHashLink, + anchor: 'HISTORY', + to: '/home/#history', + icon: , + }, + { + _component: 'CNavItem', + as: NavHashLink, + anchor: 'TEAM', + to: '/home/#team', + icon: , + }, + { + _component: 'CNavItem', + as: NavHashLink, + anchor: 'CONTACT', + to: '/home/#contact', + icon: , + }, +] + +export default _navOut diff --git a/client/src/assets/icons/history_icon.png b/client/src/assets/icons/history_icon.png new file mode 100644 index 000000000..885b7a794 Binary files /dev/null and b/client/src/assets/icons/history_icon.png differ diff --git a/src/assets/icons/index.js b/client/src/assets/icons/index.js similarity index 90% rename from src/assets/icons/index.js rename to client/src/assets/icons/index.js index 1c4d66465..955387a1a 100644 --- a/src/assets/icons/index.js +++ b/client/src/assets/icons/index.js @@ -1,6 +1,6 @@ import { sygnet } from './sygnet' -import { logo } from './logo' -import { logoNegative } from './logo-negative' +import { website } from './website' +import { sidebar_icon } from './sidebar_icon' import { cibSkype, @@ -28,9 +28,11 @@ import { cibCcApplePay, cibCcAmex, cibGoogle, + cibGmail, } from '@coreui/icons' import { cifUs, cifBr, cifIn, cifFr, cifEs, cifPl } from '@coreui/icons' import { + cilAddressBook, cilAlignCenter, cilAlignLeft, cilAlignRight, @@ -38,12 +40,15 @@ import { cilArrowBottom, cilArrowRight, cilArrowTop, + cilArrowLeft, cilAsterisk, cilBan, cilBasket, cilBell, cilBold, cilBookmark, + cilBraille, + cilBuilding, cilCalculator, cilCalendar, cilCloudDownload, @@ -66,8 +71,10 @@ import { cilEnvelopeLetter, cilEnvelopeOpen, cilEuro, + cilEducation, cilGlobeAlt, cilGrid, + cilFilter, cilFile, cilFullscreen, cilFullscreenExit, @@ -78,6 +85,7 @@ import { cilIndentIncrease, cilInputPower, cilItalic, + cilImage, cilJustifyCenter, cilJustifyLeft, cilLaptop, @@ -105,6 +113,7 @@ import { cilSave, cilScrubber, cilSettings, + cilSearch, cilShare, cilShareAll, cilShareBoxed, @@ -116,6 +125,7 @@ import { cilSun, cilTags, cilTask, + cilThumbUp, cilTrash, cilUnderline, cilUser, @@ -131,10 +141,11 @@ export const icons = Object.assign( {}, { sygnet, - logo, - logoNegative, + website, + sidebar_icon, }, { + cilAddressBook, cilAlignCenter, cilAlignLeft, cilAlignRight, @@ -142,12 +153,15 @@ export const icons = Object.assign( cilArrowBottom, cilArrowRight, cilArrowTop, + cilArrowLeft, cilAsterisk, cilBan, cilBasket, cilBell, cilBold, cilBookmark, + cilBraille, + cilBuilding, cilCalculator, cilCalendar, cilCloudDownload, @@ -170,8 +184,10 @@ export const icons = Object.assign( cilEnvelopeLetter, cilEnvelopeOpen, cilEuro, + cilEducation, cilGlobeAlt, cilGrid, + cilFilter, cilFile, cilFullscreen, cilFullscreenExit, @@ -182,6 +198,7 @@ export const icons = Object.assign( cilIndentIncrease, cilInputPower, cilItalic, + cilImage, cilJustifyCenter, cilJustifyLeft, cilLaptop, @@ -209,6 +226,7 @@ export const icons = Object.assign( cilSave, cilScrubber, cilSettings, + cilSearch, cilShare, cilShareAll, cilShareBoxed, @@ -220,6 +238,7 @@ export const icons = Object.assign( cilSun, cilTags, cilTask, + cilThumbUp, cilTrash, cilUnderline, cilUser, @@ -264,5 +283,6 @@ export const icons = Object.assign( cibCcApplePay, cibCcAmex, cibGoogle, + cibGmail, }, ) diff --git a/src/assets/icons/logo-negative.js b/client/src/assets/icons/logo-negative.js similarity index 100% rename from src/assets/icons/logo-negative.js rename to client/src/assets/icons/logo-negative.js diff --git a/src/assets/icons/logo.js b/client/src/assets/icons/logo.js similarity index 100% rename from src/assets/icons/logo.js rename to client/src/assets/icons/logo.js diff --git a/client/src/assets/icons/sidebar_icon.js b/client/src/assets/icons/sidebar_icon.js new file mode 100644 index 000000000..027efbf2a --- /dev/null +++ b/client/src/assets/icons/sidebar_icon.js @@ -0,0 +1,14 @@ +export const sidebar_icon = [ + '30 30', + ` + + + + + + + + + + `, +] diff --git a/src/assets/icons/sygnet.js b/client/src/assets/icons/sygnet.js similarity index 100% rename from src/assets/icons/sygnet.js rename to client/src/assets/icons/sygnet.js diff --git a/client/src/assets/icons/website.js b/client/src/assets/icons/website.js new file mode 100644 index 000000000..baadca49f --- /dev/null +++ b/client/src/assets/icons/website.js @@ -0,0 +1,21 @@ +export const website = [ + '24 24', + ` + + + + + + `, +] diff --git a/client/src/assets/images/1999_column_outline.jpg b/client/src/assets/images/1999_column_outline.jpg new file mode 100644 index 000000000..85e2b2747 Binary files /dev/null and b/client/src/assets/images/1999_column_outline.jpg differ diff --git a/client/src/assets/images/2008_column_outline.jpg b/client/src/assets/images/2008_column_outline.jpg new file mode 100644 index 000000000..1265a53c4 Binary files /dev/null and b/client/src/assets/images/2008_column_outline.jpg differ diff --git a/client/src/assets/images/2012_column_outline.jpg b/client/src/assets/images/2012_column_outline.jpg new file mode 100644 index 000000000..64c9185bc Binary files /dev/null and b/client/src/assets/images/2012_column_outline.jpg differ diff --git a/client/src/assets/images/FBQRcode.png b/client/src/assets/images/FBQRcode.png new file mode 100644 index 000000000..d216f972d Binary files /dev/null and b/client/src/assets/images/FBQRcode.png differ diff --git a/client/src/assets/images/GithubQRcode.png b/client/src/assets/images/GithubQRcode.png new file mode 100644 index 000000000..7fe0808c8 Binary files /dev/null and b/client/src/assets/images/GithubQRcode.png differ diff --git a/client/src/assets/images/IGQRcode.png b/client/src/assets/images/IGQRcode.png new file mode 100644 index 000000000..226f1b48e Binary files /dev/null and b/client/src/assets/images/IGQRcode.png differ diff --git a/client/src/assets/images/Recommendation.png b/client/src/assets/images/Recommendation.png new file mode 100644 index 000000000..2698e55e0 Binary files /dev/null and b/client/src/assets/images/Recommendation.png differ diff --git a/client/src/assets/images/Recruitment.png b/client/src/assets/images/Recruitment.png new file mode 100644 index 000000000..4f9bd081a Binary files /dev/null and b/client/src/assets/images/Recruitment.png differ diff --git a/client/src/assets/images/Welcome.png b/client/src/assets/images/Welcome.png new file mode 100644 index 000000000..36438389f Binary files /dev/null and b/client/src/assets/images/Welcome.png differ diff --git a/client/src/assets/images/career.png b/client/src/assets/images/career.png new file mode 100644 index 000000000..90cc66760 Binary files /dev/null and b/client/src/assets/images/career.png differ diff --git a/client/src/assets/images/column.png b/client/src/assets/images/column.png new file mode 100644 index 000000000..9ef96528c Binary files /dev/null and b/client/src/assets/images/column.png differ diff --git a/client/src/assets/images/communicate.png b/client/src/assets/images/communicate.png new file mode 100644 index 000000000..fc7d36ceb Binary files /dev/null and b/client/src/assets/images/communicate.png differ diff --git a/client/src/assets/images/default_male.png b/client/src/assets/images/default_male.png new file mode 100644 index 000000000..cd5a3df56 Binary files /dev/null and b/client/src/assets/images/default_male.png differ diff --git a/client/src/assets/images/eesa-icon.png b/client/src/assets/images/eesa-icon.png new file mode 100644 index 000000000..a85fcf759 Binary files /dev/null and b/client/src/assets/images/eesa-icon.png differ diff --git a/client/src/assets/images/error404.png b/client/src/assets/images/error404.png new file mode 100644 index 000000000..024f19c2a Binary files /dev/null and b/client/src/assets/images/error404.png differ diff --git a/client/src/assets/images/header-background.jpg b/client/src/assets/images/header-background.jpg new file mode 100644 index 000000000..bdd51a747 Binary files /dev/null and b/client/src/assets/images/header-background.jpg differ diff --git a/client/src/assets/images/left_minister.png b/client/src/assets/images/left_minister.png new file mode 100644 index 000000000..79a930d5e Binary files /dev/null and b/client/src/assets/images/left_minister.png differ diff --git a/client/src/assets/images/loader.gif b/client/src/assets/images/loader.gif new file mode 100644 index 000000000..91ebd7c1f Binary files /dev/null and b/client/src/assets/images/loader.gif differ diff --git a/client/src/assets/images/logo_row.png b/client/src/assets/images/logo_row.png new file mode 100644 index 000000000..9797ce509 Binary files /dev/null and b/client/src/assets/images/logo_row.png differ diff --git a/client/src/assets/images/mail-sent.png b/client/src/assets/images/mail-sent.png new file mode 100644 index 000000000..b2d3e248f Binary files /dev/null and b/client/src/assets/images/mail-sent.png differ diff --git a/client/src/assets/images/no_result.png b/client/src/assets/images/no_result.png new file mode 100644 index 000000000..a89ae0d76 Binary files /dev/null and b/client/src/assets/images/no_result.png differ diff --git a/client/src/assets/images/overlay-bg.png b/client/src/assets/images/overlay-bg.png new file mode 100644 index 000000000..045b713ef Binary files /dev/null and b/client/src/assets/images/overlay-bg.png differ diff --git a/client/src/assets/images/right_minister.png b/client/src/assets/images/right_minister.png new file mode 100644 index 000000000..1945d716d Binary files /dev/null and b/client/src/assets/images/right_minister.png differ diff --git a/client/src/assets/images/studyabroad.png b/client/src/assets/images/studyabroad.png new file mode 100644 index 000000000..c963ff65c Binary files /dev/null and b/client/src/assets/images/studyabroad.png differ diff --git a/client/src/assets/images/success_checked.png b/client/src/assets/images/success_checked.png new file mode 100644 index 000000000..acc7dac16 Binary files /dev/null and b/client/src/assets/images/success_checked.png differ diff --git a/client/src/assets/images/testimonials-bg.jpg b/client/src/assets/images/testimonials-bg.jpg new file mode 100644 index 000000000..a9218abb0 Binary files /dev/null and b/client/src/assets/images/testimonials-bg.jpg differ diff --git a/client/src/assets/images/upload_file.png b/client/src/assets/images/upload_file.png new file mode 100644 index 000000000..9c8fb9805 Binary files /dev/null and b/client/src/assets/images/upload_file.png differ diff --git a/client/src/components/AppBackground.js b/client/src/components/AppBackground.js new file mode 100644 index 000000000..6590326dd --- /dev/null +++ b/client/src/components/AppBackground.js @@ -0,0 +1,98 @@ +import React from 'react' +import Particles from 'react-tsparticles' +import { loadFull } from 'tsparticles' + +const AppBackground = () => { + const particleSize = Math.floor(Math.random() * 45) + 30 + return ( + <> + + + ) +} + +export default React.memo(AppBackground) diff --git a/client/src/components/AppContent.js b/client/src/components/AppContent.js new file mode 100644 index 000000000..e5ec090fc --- /dev/null +++ b/client/src/components/AppContent.js @@ -0,0 +1,130 @@ +import React, { Suspense, useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { Redirect, Route, Switch, useLocation } from 'react-router-dom' +import { ErrorBoundary } from 'react-error-boundary' +import { + login, + logout, + setImgSrc, + selectLogin, + clearImgSrc, + setStudentInfo, + clearStudentInfo, +} from '../slices/loginSlice' +import axios from 'axios' +import default_male from '../assets/images/default_male.png' +import { AppBackground, AppFallbackRender } from '.' + +// routes config +import { routes_out, routes_in, routes_auth } from '../routes' + +const AppContent = () => { + const ContentStyle = { + maxWidth: `100%`, + maxHeight: `100%`, + } + + const dispatch = useDispatch() + const { isLogin, isAuth } = useSelector(selectLogin) + const { pathname } = useLocation() + + useEffect(() => { + window.scrollTo(0, 0) + }, [pathname]) + + useEffect(() => { + // check login status + axios + .post('/api/isLogin', {}) + .then((res) => { + dispatch(login(res.data.isAuth)) + dispatch(setImgSrc(res.data.userimage === '' ? default_male : res.data.userimage)) + dispatch(setStudentInfo(res.data)) + }) + .catch((err) => { + dispatch(logout()) + dispatch(clearImgSrc()) + dispatch(clearStudentInfo()) + }) + }, [isLogin]) + return ( +
    + + +
    + +
    +
    + } + > + + {routes_out.map((route, idx) => { + return ( + route.component && ( + ( + + + + )} + /> + ) + ) + })} + {!isLogin ? : null} + {isLogin + ? routes_in.map((route, idx) => { + return ( + route.component && ( + ( + + + + )} + /> + ) + ) + }) + : null} + {isAuth + ? routes_auth.map((route, idx) => { + return ( + route.component && ( + ( + <> + + + )} + /> + ) + ) + }) + : null} + {isAuth ? ( + + ) : ( + + )} + + + + ) +} + +export default React.memo(AppContent) diff --git a/client/src/components/AppFallbackRender.js b/client/src/components/AppFallbackRender.js new file mode 100644 index 000000000..b3d689507 --- /dev/null +++ b/client/src/components/AppFallbackRender.js @@ -0,0 +1,26 @@ +import React from 'react' +import Error404 from '../assets/images/error404.png' +import PropTypes from 'prop-types' + +const AppFallbackRender = ({ error, resetErrorBoundary }) => ( +
    + Error + +
    +) +AppFallbackRender.propTypes = { + error: PropTypes.object, + resetErrorBoundary: PropTypes.func, +} +export { AppFallbackRender } diff --git a/client/src/components/AppFooter.js b/client/src/components/AppFooter.js new file mode 100644 index 000000000..1d05cf9c8 --- /dev/null +++ b/client/src/components/AppFooter.js @@ -0,0 +1,41 @@ +import React from 'react' +import { CCol, CFooter } from '@coreui/react' +import eesa from '../assets/images/eesa-icon.png' + +const AppFooter = () => { + return ( + <> + + + 國立臺灣大學電機工程學系 系學會
    +
    + National Taiwan University Electrical Engineering Department Student Association
    {' '} + Email: ntueesa@gmail.com +
    +
    + + eesa + +
    + + Copyright © 2021 NTUEESA + + + ) +} + +export default React.memo(AppFooter) diff --git a/client/src/components/AppHeader.js b/client/src/components/AppHeader.js new file mode 100644 index 000000000..0ad1abbdc --- /dev/null +++ b/client/src/components/AppHeader.js @@ -0,0 +1,118 @@ +import React from 'react' +import { NavLink } from 'react-router-dom' +import { useHistory } from 'react-router' +import { useSelector, useDispatch } from 'react-redux' +import { + selectGlobal, + sidebarOpen, + sidebarHide, + squeezeSidebar, + stretchSidebar, +} from '../slices/globalSlice' +import { selectLogin } from '../slices/loginSlice' +import { selectSearch, setKeywords, setResultProfiles } from '../slices/searchSlice' +import { + CContainer, + CHeader, + CHeaderBrand, + CHeaderNav, + CHeaderToggler, + CNavLink, + CNavItem, + CImage, + CButton, + CFormControl, + CInputGroup, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' + +import { AppHeaderDropdown } from './header' + +import logo_row from '../assets/images/logo_row.png' +import axios from 'axios' + +const AppHeader = () => { + const dispatch = useDispatch() + const history = useHistory() + const { sidebarShow, unfoldable } = useSelector(selectGlobal) + const { isLogin } = useSelector(selectLogin) + const { keywords } = useSelector(selectSearch) + + const handleSearch = (e) => { + e.preventDefault() + axios + .post('api/smartsearchProfile', { keyword: keywords }) + .then((res) => { + dispatch(setResultProfiles(res.data)) + history.push('/search_profile') + }) + .catch((err) => console.log(err)) + } + + const handleEnter = (e) => { + if (e.key === 'Enter') { + e.preventDefault() + handleSearch(e) + } + } + + return ( + + + (sidebarShow ? dispatch(sidebarHide()) : dispatch(sidebarOpen()))} + > + + + { + sidebarShow ? dispatch(sidebarHide()) : dispatch(sidebarOpen()) + unfoldable ? dispatch(squeezeSidebar()) : dispatch(stretchSidebar()) + }} + > + + + + + + + + {isLogin ? ( + + + { + dispatch(setKeywords(e.target.value)) + }} + onKeyPress={handleEnter} + > + + + + + + ) : ( + <> + )} + + {isLogin ? ( + + + + ) : ( + + + Login + + + )} + + + ) +} + +export default AppHeader diff --git a/client/src/components/AppSidebar.js b/client/src/components/AppSidebar.js new file mode 100644 index 000000000..b7ab709bf --- /dev/null +++ b/client/src/components/AppSidebar.js @@ -0,0 +1,69 @@ +import React from 'react' +import { NavLink } from 'react-router-dom' +import { useSelector, useDispatch } from 'react-redux' +import { selectLogin } from '../slices/loginSlice' +import { selectGlobal, sidebarOpen, sidebarHide } from '../slices/globalSlice' +import { + CSidebar, + CSidebarBrand, + CSidebarNav, + CImage, + CCreateNavItem, + CHeaderNav, + CNavLink, + CButton, +} from '@coreui/react' + +import SimpleBar from 'simplebar-react' +import 'simplebar/dist/simplebar.min.css' +import { AppHeaderDropdown } from './header' + +// sidebar nav config +import navOut from '../_navOut' +import navIn from '../_navIn' +import navAuth from '../_navAuth' + +//sidebar top icon +import logo_row from '../assets/images/logo_row.png' + +const AppSidebar = () => { + const dispatch = useDispatch() + const { isLogin, isAuth } = useSelector(selectLogin) + const { sidebarShow, unfoldable } = useSelector(selectGlobal) + const chNav = () => { + return isLogin ? (isAuth ? [...navAuth, ...navIn, ...navOut] : [...navIn, ...navOut]) : navOut + } + return ( + dispatch(sidebarOpen())} + onHide={() => dispatch(sidebarHide())} + > + + + + + + {isLogin ? ( + + + + ) : ( + + + Login + + + )} + + + + + ) +} + +export default React.memo(AppSidebar) diff --git a/client/src/components/header/AppHeaderDropdown.js b/client/src/components/header/AppHeaderDropdown.js new file mode 100644 index 000000000..f98afb2e8 --- /dev/null +++ b/client/src/components/header/AppHeaderDropdown.js @@ -0,0 +1,81 @@ +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { Link } from 'react-router-dom' +import { logout, clearImgSrc, clearStudentInfo, selectLogin } from '../../slices/loginSlice' +import { + CAvatar, + CBadge, + CDropdown, + CDropdownDivider, + CDropdownHeader, + CDropdownItem, + CDropdownMenu, + CDropdownToggle, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' +import axios from 'axios' + +const AppHeaderDropdown = () => { + const dispatch = useDispatch() + const { imgSrc, studentID } = useSelector(selectLogin) + const handleLogOut = (e) => { + e.preventDefault() + axios + .post('/api/logout', {}) + .then((res) => { + alert('登出成功!') + dispatch(logout()) + dispatch(clearImgSrc()) + dispatch(clearStudentInfo()) + }) + .catch((err) => { + console.log(err) + }) + } + + return ( + + + {/*
    src/components/header/AppHeaderDropDown/CAvator
    */} + +
    + + Your Space + + + Profile + + + + + Your Recruitment + + + + + Your Recommendation + + + + + Change Password + + + {/* + + Payments + + 42 + + */} + + + + Logout + + +
    + ) +} + +export default AppHeaderDropdown diff --git a/src/components/header/index.js b/client/src/components/header/index.js similarity index 100% rename from src/components/header/index.js rename to client/src/components/header/index.js diff --git a/client/src/components/index.js b/client/src/components/index.js new file mode 100644 index 000000000..a93268e01 --- /dev/null +++ b/client/src/components/index.js @@ -0,0 +1,17 @@ +import AppContent from './AppContent' +import AppFooter from './AppFooter' +import AppHeader from './AppHeader' +import AppHeaderDropdown from './header/AppHeaderDropdown' +import AppSidebar from './AppSidebar' +import AppBackground from './AppBackground' +import { AppFallbackRender } from './AppFallbackRender' + +export { + AppContent, + AppFooter, + AppHeader, + AppHeaderDropdown, + AppSidebar, + AppBackground, + AppFallbackRender, +} diff --git a/src/index.js b/client/src/index.js similarity index 100% rename from src/index.js rename to client/src/index.js diff --git a/src/layout/DefaultLayout.js b/client/src/layout/DefaultLayout.js similarity index 63% rename from src/layout/DefaultLayout.js rename to client/src/layout/DefaultLayout.js index 43bd64432..49c5fe85e 100644 --- a/src/layout/DefaultLayout.js +++ b/client/src/layout/DefaultLayout.js @@ -5,11 +5,13 @@ const DefaultLayout = () => { return (
    -
    +
    -
    +
    +
    +
    diff --git a/client/src/routes.js b/client/src/routes.js new file mode 100644 index 000000000..f2381a164 --- /dev/null +++ b/client/src/routes.js @@ -0,0 +1,146 @@ +// out pages +import { + Home, + Login, + RegisterEntry, + Register, + RegisterFB, + Forget, + ResetPassword, + Policy, +} from './views/out' +// in pages +import { + ColumnSummary, + Column, + Career, + Recruitment, + AddRecruitment, + EditRecruitment, + OwnRecruitment, + Recommendation, + AddRecommendation, + EditRecommendation, + OwnRecommendation, + Profile, + EditProfile, + SearchProfile, + Study, + Matching, + MatchForm, + ChangePsw, +} from './views/in' +// auth pages +import { AuthMatching, AuthRegister } from './views/auth' + +import Dashboard from './views/dashboard/Dashboard' + +// out routes +const routes_out = [ + { path: '/home', exact: false, name: 'Home', component: Home }, + { path: '/login', exact: true, name: 'Login', component: Login }, + { path: '/register_entry', exact: true, name: 'RegisterEntry', component: RegisterEntry }, + { path: '/register/:identity', exact: true, name: 'Register', component: Register }, + { path: '/register_fb', exact: true, name: 'RegisterFB', component: RegisterFB }, + { path: '/forget', exact: true, name: 'Forget', component: Forget }, + { + path: '/reset_password/:account/:active', + exact: true, + name: 'ResetPassword', + component: ResetPassword, + }, + { path: '/policy', exact: true, name: 'Policy', component: Policy }, + { path: '/interview/:id', exact: false, name: 'Interviews', component: Column }, +] + +const routes_in = [ + { path: '/dashboard', exact: true, name: 'Dashboard', component: Dashboard }, + { path: '/column_summary/:id', exact: false, name: 'column', component: Column }, + { path: '/column_summary', exact: false, name: 'ColumnSummary', component: ColumnSummary }, + { path: '/career', exact: true, name: 'Career', component: Career }, + { path: '/recruitment', exact: true, name: 'Recruitment', component: Recruitment }, + { path: '/add_recruitment', exact: true, name: 'AddRecruitment', component: AddRecruitment }, + { + path: '/edit_recruitment/:id', + exact: false, + name: 'EditRecruitment', + component: EditRecruitment, + }, + { path: '/own_recruitment', exact: true, name: 'OwnRecruitment', component: OwnRecruitment }, + { path: '/recommendation', exact: true, name: 'Recommendation', component: Recommendation }, + { + path: '/add_recommendation', + exact: true, + name: 'AddRecommendation', + component: AddRecommendation, + }, + { + path: '/edit_recommendation/:id', + exact: false, + name: 'EditRecommendation', + component: EditRecommendation, + }, + { + path: '/own_recommendation', + exact: true, + name: 'OwnRecommendation', + component: OwnRecommendation, + }, + { + path: '/profile/:id', + exact: false, + name: 'Profile', + component: Profile, + }, + { + path: '/edit_profile', + exact: true, + name: 'EditProfile', + component: EditProfile, + }, + { + path: '/search_profile', + exact: true, + name: 'SearchProfile', + component: SearchProfile, + }, + { + path: '/study', + exact: true, + name: 'Study', + component: Study, + }, + { + path: '/matching', + exact: true, + name: 'Matching', + component: Matching, + }, + { + path: '/match_form/:identity', + exact: false, + name: 'MatchForm', + component: MatchForm, + }, + { + path: '/change_password', + exact: true, + name: 'ChangePsw', + component: ChangePsw, + }, +] +const routes_auth = [ + { + path: '/auth/matching', + exact: true, + name: 'AuthMatching', + component: AuthMatching, + }, + { + path: '/auth/register', + exact: true, + name: 'Register', + component: AuthRegister, + }, +] +export { routes_out, routes_in, routes_auth } diff --git a/client/src/scss/_careerBlock.scss b/client/src/scss/_careerBlock.scss new file mode 100644 index 000000000..fbed9799f --- /dev/null +++ b/client/src/scss/_careerBlock.scss @@ -0,0 +1,117 @@ +.CareerBlock { + background-color: azure; + // border-radius: 3%; + font-size: 4rem; + box-shadow: aqua 0px 5px 15px; + width: 95%; + margin: 0% 1% 7% 2% !important; +} +.eesa { + background-color: azure; + width: 90%; + padding: 0; +} +.widgetbrand { + background-color: azure; + border: 0px; + border-radius: 5%; + border: 0.5rem; +} +.card-header { + padding: 0%; + border: none; + background-color: azure; +} +.careercontent { + padding: 0% 3% 5% 3%; +} +.fw-semibold { + font-size: 1.9rem !important; + margin-top: 5%; + color: black; +} +.careercontent > h3, +.careercontent > h2 { + font-weight: 600; +} +.CareerBlock > hr { + margin-top: 0; +} +.careercontent > button { + font-size: 1vw; + color: blue; + border: none; + background: none; +} +.add { + padding: 2%; + margin: 1% 1%; + border-radius: 2rem; + border: dashed; + border-width: 2%; + border-color: azure; + font-size: 7rem; + color: azure; + font-weight: 600; +} +.add:hover { + background-color: azure; + color: var(--primary_bgcolor); +} +.hover-pointer { + margin-left: 1%; +} +.hover-pointer:hover { + cursor: pointer; +} +.jodit-toolbar-button_table, +.jodit-toolbar-button_image, +.jodit-toolbar-button_source, +.jodit-toolbar-button_copyformat, +.jodit-toolbar-button_dots, +.jodit-toolbar-button_fullsize { + display: none !important; +} +.career { + height: 100%; + vertical-align: middle; +} +.career_img { + transition-property: transform; + transform: scale(1); + transition-duration: 0.5s; +} +.career_img:hover { + transform: scale(1.05); + animation-name: wobble; + animation-duration: 1s; + animation-iteration-count: 1; +} +.form-add { + border: dashed; + border-width: 2%; + border-color: blue; + width: 94%; + background-color: white; + color: blue; + padding: 0; + font-size: 1.4rem; +} +.form-add:hover { + background-color: blue; + color: white; +} +@keyframes wobble { + 0% { + transform: rotate(0deg); + } + 25% { + transform: rotate(-10deg); + } + 75% { + transform: rotate(10deg); + } + 100% { + transform: rotate(0deg); + } +} diff --git a/client/src/scss/_column.scss b/client/src/scss/_column.scss new file mode 100644 index 000000000..6a98ca7f4 --- /dev/null +++ b/client/src/scss/_column.scss @@ -0,0 +1,316 @@ +.hashtag, +.hashtag:visited { + text-decoration: none; + outline: 0; + color: #11abb0; + -webkit-transition: color 0.3s ease-in-out; + -moz-transition: color 0.3s ease-in-out; + -o-transition: color 0.3s ease-in-out; + transition: color 0.3s ease-in-out; +} +.hashtag:hover, +.hashtag:focus { + color: #313131; +} + +.column { + color: rgba(44, 56, 74, 0.95); +} + +/* column widths */ +.row .one { + width: 8.33333%; +} +.row .two { + width: 16.66667%; +} +.row .three { + width: 25%; +} +.row .four { + width: 33.33333%; +} +.row .five { + width: 41.66667%; +} +.row .six { + width: 50%; +} +.row .seven { + width: 58.33333%; +} +.row .eight { + width: 66.66667%; +} +.row .nine { + width: 75%; +} +.row .ten { + width: 83.33333%; +} +.row .eleven { + width: 91.66667%; +} +.row .twelve { + width: 100%; +} + +/* smaller screens + --------------------------------------------------------------- */ +@media only screen and (max-width: 900px) { + /* block grids on small screens */ + .s-bgrid-sixths [class*='column'] { + width: 16.66667%; + } + .s-bgrid-quarters [class*='column'] { + width: 25%; + } + .s-bgrid-thirds [class*='column'] { + width: 33.33333%; + } + .s-bgrid-halves [class*='column'] { + width: 50%; + } + + /* block grids left clearing */ + .first { + clear: none; + } + .s-first { + clear: left; + } +} + +/* mobile wide/smaller tablets + --------------------------------------------------------------- */ +@media only screen and (max-width: 767px) { + .column .row { + width: 460px; + margin: 0 auto; + padding: 0; + } + .column, + .columns { + width: auto !important; + float: none; + margin-left: 0; + margin-right: 0; + padding: 0 30px; + } + .row .row { + width: auto; + max-width: none; + margin: 0 -30px; + } + + [class*='column'] + [class*='column']:last-child { + float: none; + } + [class*='bgrid'] [class*='column'] + [class*='column']:last-child { + float: none; + } + + /* Offsets */ + .row .offset-1 { + margin-left: 0%; + } + .row .offset-2 { + margin-left: 0%; + } + .row .offset-3 { + margin-left: 0%; + } + .row .offset-4 { + margin-left: 0%; + } + .row .offset-5 { + margin-left: 0%; + } + .row .offset-6 { + margin-left: 0%; + } + .row .offset-7 { + margin-left: 0%; + } + .row .offset-8 { + margin-left: 0%; + } + .row .offset-9 { + margin-left: 0%; + } + .row .offset-10 { + margin-left: 0%; + } + .row .offset-11 { + margin-left: 0%; + } +} + +/* mobile narrow + --------------------------------------------------------------- */ +@media only screen and (max-width: 460px) { + .row { + width: auto; + } +} + +/* larger screens + --------------------------------------------------------------- */ +@media screen and (min-width: 1200px) { + .wide .row { + max-width: 1180px; + } +} +.column header { + position: relative; + height: 800px; + min-height: 500px; + width: 100%; + background: #161415 url(../assets/images/header-background.jpg) no-repeat top center; + background-size: cover !important; + -webkit-background-size: cover !important; + text-align: center; + overflow: hidden; +} + +/* vertically center banner section */ +.column header:before { + content: ''; + vertical-align: middle; + display: inline-block; + height: 100%; +} +.column header .banner { + display: inline-block; + vertical-align: middle; + margin: 0 auto; + width: 85%; + padding-bottom: 30px; + text-align: center; +} +.column header .banner-text h1 { + font: 3.1rem/1.1em 'opensans-bold', sans-serif; + color: #fff; + letter-spacing: -2px; + margin: 0 auto 18px auto; + text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.8); +} + +#resume { + background: #fff; + padding-top: 90px; + padding-bottom: 72px; + overflow: hidden; +} + +#resume h1 { + font: 1.3rem/1.25rem 'opensans-bold', sans-serif; + text-transform: uppercase; + letter-spacing: 0.063rem; +} +#resume h1 span { + border-bottom: 0.188rem solid #11abb0; + padding-bottom: 0.375rem; + margin-left: 0.5rem; +} +#resume h3 { + font: 1.688rem/1.875rem 'opensans-bold', sans-serif; + font-weight: 550; + margin-bottom: 1.2rem; + margin-top: 1rem; +} +#resume p { + font: 1.438rem/1.875rem 'opensans-bold', sans-serif; + margin-bottom: 1.7rem; + line-height: 2.8rem; +} + +#resume .header-col { + padding-top: 9px; +} +#resume .main-col { + padding-right: 5%; +} + +.education, +.work { + margin-bottom: 20px; + padding-bottom: 0px; + border-bottom: 1.5px solid #e8e8e8; +} +#resume .info { + font: 16px/24px 'librebaskerville-italic', serif; + color: #6e7881; + margin-bottom: 18px; + margin-top: 9px; +} +#resume .info span { + margin-right: 5px; + margin-left: 5px; +} +#resume .date { + font: 15px/24px 'opensans-regular', sans-serif; + margin-top: 6px; +} + +/*----------------------------------------------*/ + +#testimonials { + background: #1f1f1f url(../assets/images/testimonials-bg.jpg) no-repeat center center; + background-size: cover !important; + -webkit-background-size: cover !important; + background-attachment: fixed; + + position: relative; + min-height: 200px; + width: 100%; + overflow: hidden; +} +#testimonials .text-container { + padding-top: 96px; + padding-bottom: 66px; +} +#testimonials h1 { + font: 18px/24px 'opensans-bold', sans-serif; + text-transform: uppercase; + letter-spacing: 3px; + color: #fff; +} +#testimonials .header-col { + padding-top: 9px; +} +#testimonials .header-col h1:before { + font-family: 'FontAwesome'; + content: '\f10d'; + padding-right: 10px; + font-size: 72px; + line-height: 72px; + text-align: left; + float: left; + color: #fff; +} + +/* Blockquotes */ +#testimonials blockquote { + margin: 0 0px 30px 0px; + padding-left: 0; + position: relative; + text-shadow: 0px 1px 3px rgba(0, 0, 0, 1); +} +#testimonials blockquote:before { + content: none; +} +#testimonials blockquote p { + font-family: 'librebaskerville-italic', serif; + padding: 0; + font-size: 30px; + line-height: 48px; + color: #fff; +} +#testimonials blockquote cite { + display: block; + font-size: 20px; + font-style: normal; + line-height: 18px; + color: #fff; +} diff --git a/client/src/scss/_custom.scss b/client/src/scss/_custom.scss new file mode 100644 index 000000000..e68a67720 --- /dev/null +++ b/client/src/scss/_custom.scss @@ -0,0 +1,49 @@ +// Here you can add other styles + +:root { + --primary_color1: #63e7e7; + --primary_color2: rgb(106, 255, 198); + --primary_bgcolor: #003f67; +} + +body { + background-color: var(--primary_bgcolor); +} + +.btnFacebook { + width: 3rem; + height: 3rem; + + border: 0px transparent; + border-radius: 50%; + padding-top: 1rem; + + background: #3b5998; + + font-size: 1rem; + color: white; + text-align: center; +} +.nav-title { + color: black !important; + font-size: large !important; +} +.nav-link { + color: black !important; +} +.nav-icon { + color: var(--primary_bgcolor) !important; +} + +// for particles background +#tsparticles { + position: fixed !important; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: -100; +} +.MuiButtonBase-root { + color: white !important; +} diff --git a/client/src/scss/_layout.scss b/client/src/scss/_layout.scss new file mode 100644 index 000000000..4ab1ebafb --- /dev/null +++ b/client/src/scss/_layout.scss @@ -0,0 +1,6 @@ +.wrapper { + width: 100%; + @include ltr-rtl('padding-left', var(--cui-sidebar-occupy-start, 0)); + will-change: auto; + @include transition(padding 0.15s); +} diff --git a/client/src/scss/_matching.scss b/client/src/scss/_matching.scss new file mode 100644 index 000000000..a8a8c7527 --- /dev/null +++ b/client/src/scss/_matching.scss @@ -0,0 +1,69 @@ +.matching { + background-color: white; + border-radius: 0.5rem; + box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), + 0px 1px 10px 0px rgb(0 0 0 / 12%); + box-sizing: border-box; + background-size: cover; + position: absolute; + top: 50%; + transform: translateX(-40%) translateY(-50%); + left: 50%; + text-align: center; +} +.form-check > label { + font-size: 1.5rem; +} +.form-check-input { + margin: 0.5em; +} +.left-part { + border-left: solid; +} +.matching a { + font-size: 1.6rem; +} +.matching .active.nav-link { + font-weight: bold; + border-top: solid 0.5rem; + border-top-color: rgb(124, 155, 240) !important; +} +.matching .nav-link:hover { + border-color: white; +} +.matching .result-block { + margin: auto; + background-color: aliceblue; + border-radius: 0.5rem; + box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), + 0px 1px 10px 0px rgb(0 0 0 / 12%); + box-sizing: border-box; + background-size: cover; + font-size: 1.1rem; +} +.aboard-experience button { + font-size: 1.3rem; + margin: 2rem 0; +} +.open-matching { + padding: 3rem; + border-radius: 0.5rem; + background-color: white; + color: black; + margin: auto; +} +.matching-form { + width: fit-content; + margin: 5rem auto; +} +.matching-form .form-check-label { + font-size: 1.3rem; +} + +.match-result-detail { + text-align: left; +} + +.match-result-image { + width: 24rem; +} diff --git a/client/src/scss/_outLanding.scss b/client/src/scss/_outLanding.scss new file mode 100644 index 000000000..2115d7180 --- /dev/null +++ b/client/src/scss/_outLanding.scss @@ -0,0 +1,519 @@ +.landing h2, +.landing h3, +.landing h4 { + font-family: 'Raleway', sans-serif; +} +.landing h2 { + text-transform: uppercase; + margin: 0 0 30px; + font-weight: 800; + font-size: 36px; + // color: #333; +} +.landing h3 { + font-size: 24px; + font-weight: 600; + // color: #333; +} +.landing h4 { + font-size: 20px; + // color: #333; + font-weight: 600; +} +.landing h5 { + text-transform: uppercase; + font-weight: 700; + line-height: 20px; +} +.landing p { + font-size: 18px; +} +.landing p.intro { + margin: 12px 0 0; + line-height: 24px; +} +.landing a { + color: white; + font-weight: 400; +} +.landing a:hover, +.landing a:focus { + text-decoration: none; + color: #608dfd; +} +.landing ul, +.landing ol { + list-style: none; +} +.landing ul, +.landing ol { + padding: 0; + webkit-padding: 0; + moz-padding: 0; +} +.landing hr { + height: 2px; + width: 70px; + text-align: center; + position: relative; + background: #1e7a46; + margin-bottom: 20px; + border: 0; +} +.section-title { + margin-bottom: 40px; + color: #fff; +} +.section-title h2, +.section-title h1 { + position: relative; + margin-top: 10px; + margin-bottom: 15px; + padding-bottom: 15px; +} +.section-title h2::after, +.section-title h1::after { + position: absolute; + content: ''; + background: linear-gradient(to right, #5ca9fb 0%, #6372ff 100%); + height: 4px; + width: 60px; + bottom: 0; + margin-left: -30px; + left: 50%; +} +.section-title p { + font-size: 18px; +} +.btn-custom { + font-family: 'Raleway', sans-serif; + text-transform: uppercase; + color: #fff; + background-color: #5ca9fb; + background-image: linear-gradient(to right, #5ca9fb 0%, #6372ff 100%); + padding: 14px 34px; + letter-spacing: 1px; + margin: 0; + font-size: 15px; + font-weight: 500; + border-radius: 25px; + transition: all 0.5s linear; + border: 0; +} +.btn-custom:hover, +.btn-custom:focus, +.btn-custom.focus, +.btn-custom:active, +.btn-custom.active { + color: #fff; + background-image: none; + background-color: #6372ff; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.landing a:focus, +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: none; + outline-offset: none; +} +/* Header Section */ +.intro { + display: table; + width: 100%; + padding: 0; + background-color: transparent; + -webkit-background-size: cover; + -moz-background-size: cover; + background-size: cover; + -o-background-size: cover; +} + +.intro h1 { + font-family: 'Raleway', sans-serif; + color: #fff; + font-size: 150px; + font-weight: 700; + text-transform: uppercase; + margin-top: 0; + margin-bottom: 10px; +} +.intro h1 span { + font-weight: 800; + font-size: 8rem; + background-clip: text; + -webkit-background-clip: text; + color: transparent; + background-image: linear-gradient(90deg, #32b1fd 0%, #00ffcf 100%); + background-size: 150% 150%; + -webkit-animation: header-animation 5s ease infinite; + -moz-animation: header-animation 5s ease infinite; + animation: header-animation 5s ease infinite; +} + +@-webkit-keyframes header-animation { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} +@-moz-keyframes header-animation { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} +@keyframes header-animation { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} +.intro p { + color: #fff; + font-size: 22px; + font-weight: 300; + line-height: 30px; + margin: 0 auto; + margin-bottom: 60px; +} +header .intro-text { + padding-top: 200px; + padding-bottom: 200px; + text-align: center; +} + +.section { + padding: 100px 0; +} +/* Features Section */ +#features i.fa { + font-size: 38px; + margin-bottom: 20px; + transition: all 0.5s; + color: #fff; + width: 100px; + height: 100px; + padding: 30px 0; + border-radius: 50%; + background: linear-gradient(to right, #6372ff 0%, #5ca9fb 100%); + box-shadow: 10px 10px 10px rgba(0, 0, 0, 0.05); +} +/* About Section */ + +#about h3 { + font-size: 22px; + margin: 0 0 20px; +} +#about h2 { + position: relative; + margin-bottom: 15px; + padding-bottom: 15px; +} +#about h2::after { + position: absolute; + content: ''; + background: linear-gradient(to right, #5ca9fb 0%, #6372ff 100%); + height: 4px; + width: 60px; + bottom: 0; + left: 0; +} +#about .about-text li { + margin-bottom: 6px; + margin-left: 6px; + list-style: none; + padding: 0; +} +#about .about-text li:before { + content: '\f00c'; + font-family: 'FontAwesome'; + color: #5ca9fb; + font-size: 11px; + font-weight: 300; + padding-right: 8px; +} +#about img { + width: 520px; + margin-top: 10px; + background: #fff; + border-right: 0; + box-shadow: 0 0 50px rgba(0, 0, 0, 0.06); +} +#about p { + line-height: 24px; + font-size: 1.2rem; + margin: 30px 0; + color: rgba(255, 255, 255, 0.75); +} +/* Services Section */ +#services { + color: #fff; +} +#services .service-desc { + margin: 10px 10px 20px; +} +#services h2 { + color: #fff; +} +#services .section-title h2::after { + position: absolute; + content: ''; + background: linear-gradient(to right, #5ca9fb 0%, #6372ff 100%); + height: 4px; + width: 60px; + bottom: 0; + margin-left: -30px; + left: 50%; +} +#services i.fa { + font-size: 42px; + width: 120px; + height: 120px; + padding: 40px 0; + background: linear-gradient(to right, #6372ff 0%, #5ca9fb 100%); + border-radius: 50%; + color: #fff; + box-shadow: 10px 10px 10px rgba(0, 0, 0, 0.05); +} +#services h3 { + font-weight: 500; + padding: 5px 0; + color: #fff; +} +#services p { + color: rgba(255, 255, 255, 0.75); +} +#services .service-desc { + margin-bottom: 40px; +} + +.square-img-container { + position: relative; + overflow: hidden; + padding-bottom: 100%; +} + +#services .img { + position: absolute; + left: 0; +} +/* Portfolio Section */ + +.portfolio-item { + margin: 1px -15px 0 -14px; + padding: 0 1rem; +} +.portfolio-item .hover-bg { + overflow: hidden; + position: relative; + margin: 0; +} +.hover-bg .hover-text { + position: absolute; + text-align: center; + margin: 0 auto; + color: #fff; + background: linear-gradient(to right, rgba(17, 29, 134, 0.568) 0%, rgba(106, 114, 226, 0.8) 100%); + padding: 8% 8% 0; + height: 100%; + width: 100%; + opacity: 0; + transition: all 0.5s; +} +.hover-bg .hover-text > h4 h3 { + opacity: 0; + color: #fff; + -webkit-transform: translateY(100%); + transform: translateY(100%); + transition: all 0.3s; + font-size: 18px; + letter-spacing: 1px; + font-weight: 500; + text-transform: uppercase; +} +.hover-bg:hover .hover-text > h4 { + opacity: 1; + -webkit-backface-visibility: hidden; + -webkit-transform: translateY(0); + transform: translateY(0); +} +.hover-bg:hover .hover-text { + opacity: 1; +} +/*history section*/ +#history { + padding: 60px 0px; +} + +/* Team Section */ + +#team h4 { + margin: 5px 0; +} +#team .img { + position: absolute; +} +#team .thumbnail { + background: transparent; + border: 0; +} +#team .thumbnail .caption { + padding: 10px 0 0; + color: white; +} +/* Contact Section */ +#contact { + color: rgba(255, 255, 255, 0.75); +} +#contact .section-title { + margin-bottom: 40px; +} +#contact .section-title p { + font-size: 16px; +} +#contact h2 { + color: #fff; + margin-top: 10px; + margin-bottom: 15px; + padding-bottom: 15px; + font-weight: 700; +} +#contact .section-title h2::after { + position: absolute; + content: ''; + background: linear-gradient(to right, #5ca9fb 0%, #6372ff 100%); + height: 4px; + width: 60px; + bottom: 0; + left: 30px; +} +#contact h3 { + color: #fff; + margin-top: 80px; + margin-bottom: 25px; + padding-bottom: 20px; + font-weight: 400; +} +#contact form { + padding-top: 20px; +} +#contact .text-danger { + color: #cc0033; + text-align: left; +} +#contact .btn-custom { + margin: 30px 0; + background: transparent; + border: 2px solid #fff; +} +#contact .btn-custom:hover { + color: #1f386e; + background: #fff; +} +label { + font-size: 12px; + font-weight: 400; + font-family: 'Open Sans', sans-serif; + float: left; +} +#contact .form-control { + display: block; + width: 100%; + padding: 6px 12px; + font-size: 16px; + line-height: 1.42857143; + color: #444; + background-color: #fff; + background-image: none; + border: 1px solid #ddd; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-transition: none; + -o-transition: none; + transition: none; +} +#contact .form-control:focus { + border-color: #999; + outline: 0; + -webkit-box-shadow: transparent; + box-shadow: transparent; +} +.form-control::-webkit-input-placeholder { + color: #777; +} +.form-control:-moz-placeholder { + color: #777; +} +.form-control::-moz-placeholder { + color: #777; +} +.form-control:-ms-input-placeholder { + color: #777; +} +#contact .contact-item { + margin: 20px 0; +} +#contact .contact-item span { + color: rgba(255, 255, 255, 1); + margin-bottom: 10px; + display: block; +} +#contact .contact-item i.fa { + margin-right: 10px; +} +#contact .social { + border-top: 1px solid rgba(255, 255, 255, 0.15); + padding-top: 50px; + margin-top: 50px; + text-align: center; +} +#contact .social ul li { + display: inline-block; + margin: 0 20px; +} + +.social-icon { + padding: 3px 0; + fill: white; + width: 48px; + height: 48px; + border-radius: 50%; + transition: all 0.3s; +} + +.social-icon:hover { + fill: #608dfd; + background: #fff; +} + +@media (max-width: 768px) { + #about img { + margin: 50px 0; + } +} diff --git a/client/src/scss/_recent.scss b/client/src/scss/_recent.scss new file mode 100644 index 000000000..6455fd07c --- /dev/null +++ b/client/src/scss/_recent.scss @@ -0,0 +1,25 @@ +.carousel-caption { + background-image: linear-gradient( + 0deg, + rgba(0, 0, 0, 1) 0%, + rgba(119, 119, 119, 0.75) 75%, + rgba(255, 255, 255, 0) 100% + ); + left: 0; +} + +.carousel-indicators { + background-color: black; + margin: 0 !important; + /* margin-top: 0 !important; + margin-bottom: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; */ + // margin-left: calc(var(--cui-gutter-x) * 0.5) !important; + // margin-right: calc(var(--cui-gutter-x) * 0.5) !important; +} + +.recent-ul { + display: table; + margin: 0 auto; +} diff --git a/client/src/scss/_register.scss b/client/src/scss/_register.scss new file mode 100644 index 000000000..552a18cd5 --- /dev/null +++ b/client/src/scss/_register.scss @@ -0,0 +1,23 @@ +.auth-register{ + background-color: white; + border-radius: 0.5rem; + box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), + 0px 1px 10px 0px rgb(0 0 0 / 12%); + box-sizing: border-box; + background-size: cover; + position: absolute; + top: 50%; + transform: translateX(-40%) translateY(-50%); + left: 50%; + text-align: center; +} +.auth-register .applicant { + margin: auto; + background-color: aliceblue; + border-radius: 0.5rem; + box-shadow: 0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), + 0px 1px 10px 0px rgb(0 0 0 / 12%); + box-sizing: border-box; + background-size: cover; + font-size: 1.1rem; +} \ No newline at end of file diff --git a/src/scss/_variables.scss b/client/src/scss/_variables.scss similarity index 98% rename from src/scss/_variables.scss rename to client/src/scss/_variables.scss index 373dbeec2..3190fa83c 100644 --- a/src/scss/_variables.scss +++ b/client/src/scss/_variables.scss @@ -5,32 +5,32 @@ // Color system // scss-docs-start gray-color-variables -// $white: #fff !default; +// $white: #fff !default; // $gray-base: #3c4b64 !default; -// $gray-100: #ebedef !default; -// $gray-200: #d8dbe0 !default; -// $gray-300: #c4c9d0 !default; -// $gray-400: #b1b7c1 !default; -// $gray-500: #9da5b1 !default; -// $gray-600: #8a93a2 !default; -// $gray-700: #768192 !default; -// $gray-800: #636f83 !default; -// $gray-900: #4f5d73 !default; -// $black: #000015 !default; +// $gray-100: #ebedef !default; +// $gray-200: #d8dbe0 !default; +// $gray-300: #c4c9d0 !default; +// $gray-400: #b1b7c1 !default; +// $gray-500: #9da5b1 !default; +// $gray-600: #8a93a2 !default; +// $gray-700: #768192 !default; +// $gray-800: #636f83 !default; +// $gray-900: #4f5d73 !default; +// $black: #000015 !default; // scss-docs-end gray-color-variables // fusv-disable // scss-docs-start gray-colors-map // $grays: ( -// "100": $gray-100, -// "200": $gray-200, -// "300": $gray-300, -// "400": $gray-400, -// "500": $gray-500, -// "600": $gray-600, -// "700": $gray-700, -// "800": $gray-800, -// "900": $gray-900 +// '100': $gray-100, +// '200': $gray-200, +// '300': $gray-300, +// '400': $gray-400, +// '500': $gray-500, +// '600': $gray-600, +// '700': $gray-700, +// '800': $gray-800, +// '900': $gray-900, // ) !default; // scss-docs-end gray-colors-map // fusv-enable @@ -74,69 +74,75 @@ // ) !default; // scss-docs-end colors-map +// our main color +// $primary_color1: #63e7e7 !default; +// $primary_color2: rgb(106, 255, 198) !default; +// $primary_bgcolor: #003f67 !default; + // fusv-disable // $primary-dark: #1f1498 !default; -// $primary-base: #321fdb !default; +// $primary-base: $primary_color1 !default; // $primary-50: #988fed !default; // $primary-25: #ccc7f6 !default; // $secondary-dark: #212233 !default; -// $secondary-base: #9da5b1 !default; +// $secondary-base: black !default; // $secondary-50: #9da5b1 !default; // $secondary-25: #ced2d8 !default; // $success-dark: #1b9e3e !default; -// $success-base: #2eb85c !default; +// $success-base: #2eb85c !default; // $success-50: #96dbad !default; // $success-25: #cbedd6 !default; // $info-dark: #2982cc !default; -// $info-base: #39f !default; +// $info-base: #39f !default; // $info-50: #80c6ff !default; // $info-25: #c0e6ff !default; // $warning-dark: #f6960b !default; -// $warning-base: #f9b115 !default; +// $warning-base: #f9b115 !default; // $warning-50: #fcd88a !default; // $warning-25: #feecc5 !default; // $danger-dark: #d93737 !default; -// $danger-base: #e55353 !default; +// $danger-base: #e55353 !default; // $danger-50: #f2a9a9 !default; // $danger-25: #f9d4d4 !default; // $light-dark: $gray-100 !default; -// $light-base: $gray-100 !default; +// $light-base: $gray-100 !default; // $light-50: shift-color($light-base, -70) !default; // $light-25: shift-color($light-base, -80) !default; // $dark-dark: $gray-900 !default; -// $dark-base: $gray-900 !default; +// $dark-base: $gray-900 !default; // $dark-50: shift-color($dark-base, -70) !default; // $dark-25: shift-color($dark-base, -80) !default; // fusv-enable // scss-docs-start theme-color-variables -// $primary: $primary-base !default; -// $secondary: $secondary-base !default; -// $success: $success-base !default; -// $info: $info-base !default; -// $warning: $warning-base !default; -// $danger: $danger-base !default; -// $light: $light-base !default; -// $dark: $dark-base !default; +// $primary: $primary-base !default; +// $secondary: $secondary-base !default; +// $success: $success-base !default; +// $info: $info-base !default; +// $warning: $warning-base !default; +// $danger: $danger-base !default; +// $light: $light-base !default; +// $dark: $dark-base !default; + // scss-docs-end theme-color-variables // scss-docs-start theme-colors-map // $theme-colors: ( -// "primary": $primary, -// "secondary": $secondary, -// "success": $success, -// "info": $info, -// "warning": $warning, -// "danger": $danger, -// "light": $light, -// "dark": $dark +// 'primary': $primary, +// 'secondary': $secondary, +// 'success': $success, +// 'info': $info, +// 'warning': $warning, +// 'danger': $danger, +// 'light': $light, +// 'dark': $dark, // ) !default; // scss-docs-end theme-colors-map @@ -334,17 +340,16 @@ // // Settings for the `` element. -// $body-bg: $white !default; +// $body-bg: $primary_bgcolor !default; // $body-color: $high-emphasis !default; // $body-text-align: null !default; - // Links // // Style anchor elements. -// $link-color: $primary !default; -// $link-decoration: underline !default; +// $link-color: $primary_bgcolor !default; +$link-decoration: none !default; // $link-shade-percentage: 20% !default; // $link-hover-color: shift-color($link-color, $link-shade-percentage) !default; // $link-hover-decoration: null !default; @@ -358,7 +363,6 @@ // $paragraph-margin-bottom: 1rem !default; - // Grid breakpoints // // Define the minimum dimensions at which your layout will change, @@ -378,7 +382,6 @@ // @include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); // @include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints"); - // Grid containers // // Define the maximum width of `.container` for different screen sizes. @@ -395,7 +398,6 @@ // @include _assert-ascending($container-max-widths, "$container-max-widths"); - // Grid columns // // Set the number of columns and specify the width of the gutters. @@ -410,7 +412,6 @@ // $container-padding-x: $grid-gutter-width / 2 !default; - // Components // // Define common padding and border radius sizes and more. @@ -597,7 +598,6 @@ // $icon-size-lg: $icon-size-base * 1.25 !default; // $icon-size-xl: $icon-size-base * 1.5 !default; - // Tables // // Customizes the `.table` component with basic values, each used across all table variations. @@ -653,7 +653,6 @@ // ) !default; // scss-docs-end table-loop - // Buttons + Forms // // Shared variables that are reassigned to `$input-` and `$btn-` specific variables. @@ -682,7 +681,6 @@ // $input-btn-border-width: $border-width !default; // scss-docs-end input-btn-variables - // Buttons // // For each of Bootstrap's buttons, define text, background, and border color. @@ -755,7 +753,6 @@ // ) !default; // scss-docs-end btn-variables - // Forms // scss-docs-start form-variables // scss-docs-start form-text-variables @@ -1022,7 +1019,6 @@ // $zindex-toaster: 1090 !default; // scss-docs-end zindex-stack - // Navs // scss-docs-start nav-variables // $nav-link-padding-y: .5rem !default; @@ -1047,7 +1043,6 @@ // $nav-pills-link-active-bg: $component-active-bg !default; // scss-docs-end nav-variables - // Navbar // scss-docs-start navbar-variables @@ -1092,7 +1087,6 @@ // $navbar-dark-brand-hover-color: $navbar-dark-active-color !default; // scss-docs-end navbar-theme-variables - // Dropdowns // // Dropdown menu container and contents. @@ -1143,7 +1137,6 @@ // $dropdown-dark-header-color: $gray-500 !default; // scss-docs-end dropdown-dark-variables - // Pagination // scss-docs-start pagination-variables @@ -1185,7 +1178,6 @@ // $pagination-border-radius-lg: $border-radius-lg !default; // scss-docs-end pagination-variables - // Cards // scss-docs-start card-variables // $card-spacer-y: $spacer !default; @@ -1269,7 +1261,6 @@ // $form-feedback-tooltip-border-radius: $tooltip-border-radius !default; // scss-docs-end tooltip-feedback-variables - // Popovers // scss-docs-start popover-variables // $popover-font-size: $font-size-sm !default; @@ -1297,7 +1288,6 @@ // $popover-arrow-outer-color: fade-in($popover-border-color, .05) !default; // scss-docs-end popover-variables - // Toasts // scss-docs-start toast-variables // $toast-max-width: 350px !default; @@ -1317,7 +1307,6 @@ // $toast-header-border-color: rgba(0, 0, 0, .05) !default; // scss-docs-end toast-variables - // Badges // scss-docs-start badge-variables // $badge-font-size: .75em !default; @@ -1333,7 +1322,6 @@ // $badge-padding-x-sm: .5em !default; // scss-docs-end badge-variables - // Modals // scss-docs-start modal-variables @@ -1376,7 +1364,6 @@ // $modal-scale-transform: scale(1.02) !default; // scss-docs-end modal-variables - // Avatars // scss-docs-start avatar-variables // $avatar-width: 2rem !default; @@ -1443,7 +1430,6 @@ // ) !default; // scss-docs-end callout-variables - // Progress bars // scss-docs-start progress-variables @@ -1498,7 +1484,6 @@ // ) !default; // scss-docs-end list-group-variables - // Header // scss-docs-start header-variables // $header-min-height: 4rem !default; @@ -1559,7 +1544,6 @@ // $subheader-disabled-color: $disabled !default; // scss-docs-end subheader-default-themes - // Image thumbnails // scss-docs-start thumbnail-variables @@ -1571,7 +1555,6 @@ // $thumbnail-box-shadow: $box-shadow-sm !default; // scss-docs-end thumbnail-variables - // Figures // scss-docs-start figure-variables @@ -1579,7 +1562,6 @@ // $figure-caption-color: $gray-600 !default; // scss-docs-end figure-variables - // Breadcrumbs // scss-docs-start breadcrumb-variables // $breadcrumb-font-size: null !default; @@ -1743,7 +1725,6 @@ // $spinner-border-width-sm: .2em !default; // scss-docs-end spinner-variables - // Close // scss-docs-start close-variables // $btn-close-width: 1em !default; @@ -1760,7 +1741,6 @@ // $btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default; // scss-docs-end close-variables - // Offcanvas // scss-docs-start offcanvas-variables diff --git a/client/src/scss/style.scss b/client/src/scss/style.scss new file mode 100644 index 000000000..66cb1b233 --- /dev/null +++ b/client/src/scss/style.scss @@ -0,0 +1,24 @@ +// If you want to override variables do it here +@import 'variables'; + +$enable-ltr: true; +$enable-rtl: true; + +// Import CoreUI for React components library +@import '@coreui/coreui/scss/coreui'; + +// Import Chart.js custom tooltips styles +@import '@coreui/chartjs/scss/coreui-chartjs'; +@import 'layout'; + +// Import AOS style +@import 'node_modules/aos/src/sass/aos.scss'; + +// If you want to add custom CSS you can put it here. +@import 'custom'; +@import 'column.scss'; +@import 'careerBlock.scss'; +@import 'recent.scss'; +@import 'matching.scss'; +@import 'outLanding.scss'; +@import 'register.scss' diff --git a/src/serviceWorker.js b/client/src/serviceWorker.js similarity index 100% rename from src/serviceWorker.js rename to client/src/serviceWorker.js diff --git a/src/setupTests.js b/client/src/setupTests.js similarity index 100% rename from src/setupTests.js rename to client/src/setupTests.js diff --git a/client/src/slices/careerSlice.js b/client/src/slices/careerSlice.js new file mode 100644 index 000000000..bfa974b19 --- /dev/null +++ b/client/src/slices/careerSlice.js @@ -0,0 +1,42 @@ +/* eslint-disable prettier/prettier */ +import { createSlice } from '@reduxjs/toolkit' + +export const careerSlice = createSlice({ + name: 'career', + initialState: { + keywords: '', + croppedFile: null, + croppedDataUrl: '', + }, + reducers: { + setKeywords: (state, action) => { + state.keywords = action.payload + }, + clearKeywords: (state) => { + state.keywords = '' + }, + setCroppedFile: (state, action) => { + state.croppedFile = action.payload + }, + clearCroppedFile: (state) => { + state.croppedFile = null + }, + setCroppedDataUrl: (state, action) => { + state.croppedDataUrl = action.payload + }, + clearCroppedDataUrl: (state) => { + state.croppedDataUrl = '' + }, + }, +}) + +export const { + setKeywords, + clearKeywords, + setCroppedFile, + clearCroppedFile, + setCroppedDataUrl, + clearCroppedDataUrl, +} = careerSlice.actions +export const selectCareer = (state) => state.career +export default careerSlice.reducer diff --git a/client/src/slices/columnSummarySlice.js b/client/src/slices/columnSummarySlice.js new file mode 100644 index 000000000..f26a9293d --- /dev/null +++ b/client/src/slices/columnSummarySlice.js @@ -0,0 +1,25 @@ +import { createSlice } from '@reduxjs/toolkit' + +export const columnSummarySlice = createSlice({ + name: 'columnSummary', + initialState: { + page: 1, + keywords: '', + isSearch: false, + }, + reducers: { + setPage: (state, action) => { + state.page = action.payload + }, + setKeywords: (state, action) => { + state.keywords = action.payload + }, + setIsSearch: (state, action) => { + state.isSearch = action.payload + }, + }, +}) + +export const { setPage, setKeywords, setIsSearch } = columnSummarySlice.actions +export const selectColumnSummary = (state) => state.columnSummary +export default columnSummarySlice.reducer diff --git a/client/src/slices/globalSlice.js b/client/src/slices/globalSlice.js new file mode 100644 index 000000000..e6c607a28 --- /dev/null +++ b/client/src/slices/globalSlice.js @@ -0,0 +1,27 @@ +import { createSlice } from '@reduxjs/toolkit' + +export const globalSlice = createSlice({ + name: 'global', + initialState: { + sidebarShow: false, + unfoldable: false, + }, + reducers: { + sidebarHide: (state) => { + state.sidebarShow = false + }, + sidebarOpen: (state) => { + state.sidebarShow = true + }, + stretchSidebar: (state) => { + state.unfoldable = true + }, + squeezeSidebar: (state) => { + state.unfoldable = false + }, + }, +}) + +export const { sidebarHide, sidebarOpen, squeezeSidebar, stretchSidebar } = globalSlice.actions +export const selectGlobal = (state) => state.global +export default globalSlice.reducer diff --git a/client/src/slices/index.js b/client/src/slices/index.js new file mode 100644 index 000000000..ad27a3698 --- /dev/null +++ b/client/src/slices/index.js @@ -0,0 +1,15 @@ +import globalReducer from './globalSlice' +import loginReducer from './loginSlice' +import columnSummaryReducer from './columnSummarySlice' +import profileReducer from './profileSlice' +import searchReducer from './searchSlice' +import careerReducer from './careerSlice' + +export const reducers = { + global: globalReducer, + login: loginReducer, + columnSummary: columnSummaryReducer, + profile: profileReducer, + search: searchReducer, + career: careerReducer, +} diff --git a/client/src/slices/loginSlice.js b/client/src/slices/loginSlice.js new file mode 100644 index 000000000..045ad148c --- /dev/null +++ b/client/src/slices/loginSlice.js @@ -0,0 +1,47 @@ +import { createSlice } from '@reduxjs/toolkit' + +export const loginSlice = createSlice({ + name: 'login', + initialState: { + isLogin: false, + isAuth: false, + imgSrc: null, + studentID: '', + email: '', + cellphone: '', + name: '', + }, + reducers: { + login: (state, action) => { + state.isLogin = true + state.isAuth = action.payload + }, + logout: (state) => { + state.isLogin = false + state.isAuth = false + }, + setImgSrc: (state, action) => { + state.imgSrc = action.payload + }, + clearImgSrc: (state) => { + state.imgSrc = null + }, + setStudentInfo: (state, action) => { + state.studentID = action.payload.account + state.cellphone = action.payload.userCellphone + state.name = action.payload.userName + state.email = action.payload.userEmail + }, + clearStudentInfo: (state, action) => { + state.studentID = '' + state.cellphone = '' + state.name = '' + state.email = '' + }, + }, +}) + +export const { login, logout, setImgSrc, clearImgSrc, setStudentInfo, clearStudentInfo } = + loginSlice.actions +export const selectLogin = (state) => state.login +export default loginSlice.reducer diff --git a/client/src/slices/profileSlice.js b/client/src/slices/profileSlice.js new file mode 100644 index 000000000..a9d6edc9e --- /dev/null +++ b/client/src/slices/profileSlice.js @@ -0,0 +1,20 @@ +import { createSlice } from '@reduxjs/toolkit' + +export const profileSlice = createSlice({ + name: 'profile', + initialState: { + editImage: false, + }, + reducers: { + openEditImageModal: (state) => { + state.editImage = true + }, + closeEditImageModal: (state) => { + state.editImage = false + }, + }, +}) + +export const { openEditImageModal, closeEditImageModal } = profileSlice.actions +export const selectProfile = (state) => state.profile +export default profileSlice.reducer diff --git a/client/src/slices/searchSlice.js b/client/src/slices/searchSlice.js new file mode 100644 index 000000000..10a3383ee --- /dev/null +++ b/client/src/slices/searchSlice.js @@ -0,0 +1,28 @@ +import { createSlice } from '@reduxjs/toolkit' + +export const searchSlice = createSlice({ + name: 'search', + initialState: { + keywords: '', + resultProfiles: [], + }, + reducers: { + setResultProfiles: (state, action) => { + state.resultProfiles = action.payload + }, + clearResultProfiles: (state, action) => { + state.resultProfiles = [] + }, + setKeywords: (state, action) => { + state.keywords = action.payload + }, + clearKeywords: (state) => { + state.keywords = '' + }, + }, +}) + +export const { setResultProfiles, setKeywords, clearResultProfiles, clearKeywords } = + searchSlice.actions +export const selectSearch = (state) => state.search +export default searchSlice.reducer diff --git a/src/store.js b/client/src/store/index.js similarity index 52% rename from src/store.js rename to client/src/store/index.js index 7b0380722..1a0af4f7b 100644 --- a/src/store.js +++ b/client/src/store/index.js @@ -1,7 +1,15 @@ -import { createStore } from 'redux' +import { configureStore } from '@reduxjs/toolkit' +import { reducers } from '../slices' + +export default configureStore({ + reducer: reducers, +}) +/* import { createStore } from 'redux' const initialState = { + isLogin: false, sidebarShow: false, + page: 1, } const changeState = (state = initialState, { type, ...rest }) => { @@ -14,4 +22,4 @@ const changeState = (state = initialState, { type, ...rest }) => { } const store = createStore(changeState) -export default store +export default store */ diff --git a/client/src/views/auth/index.js b/client/src/views/auth/index.js new file mode 100644 index 000000000..35a03a0e3 --- /dev/null +++ b/client/src/views/auth/index.js @@ -0,0 +1,5 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +const AuthMatching = React.lazy(() => import('./matching')) +const AuthRegister=React.lazy(() => import('./register')) +export { AuthMatching, AuthRegister } diff --git a/client/src/views/auth/matching/Mail.js b/client/src/views/auth/matching/Mail.js new file mode 100644 index 000000000..e82d7dc65 --- /dev/null +++ b/client/src/views/auth/matching/Mail.js @@ -0,0 +1,78 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useRef } from 'react' +import PropTypes from 'prop-types' +import CIcon from '@coreui/icons-react' +import { success_icon, mail_sent, Spinner } from './index' +import axios from 'axios' +const Mail = ({ hasSent, setHasSent, setHasMatched }) => { + const [uploadedFile, setUploadedFile] = useState(null) + const inputRef = useRef(null) + const [loading, setLoading] = useState(false) + + const handleUpload = () => { + inputRef.current?.click() + } + const handleInputFile = () => { + inputRef.current?.files && setUploadedFile(inputRef.current.files[0]) + console.log(inputRef.current.files[0]) + } + const sendMail = () => { + setLoading(true) + const config = { 'content-type': 'multipart/form-data' } + let data = new FormData() + data.append('result', uploadedFile) + axios.post('/api/study/sendMail', data, config).then(() => { + console.log('mails sent!!') + setHasSent(true) + setLoading(false) + }) + } + return loading ? ( + + ) : ( +
    + {hasSent ? ( + <> + + success +

    信件已全數寄出,謝謝您的幫忙!

    + + ) : ( + <> + +

    配對已完成!趕快發信告訴大家吧!

    + success +
    + + + +
    + + + )} +
    + ) +} +Mail.propTypes = { + hasSent: PropTypes.bool, + setHasSent: PropTypes.func, + setHasMatched: PropTypes.func, +} +export default Mail diff --git a/client/src/views/auth/matching/Matching.js b/client/src/views/auth/matching/Matching.js new file mode 100644 index 000000000..b1cc2ddb9 --- /dev/null +++ b/client/src/views/auth/matching/Matching.js @@ -0,0 +1,25 @@ +/* eslint-disable prettier/prettier */ +import React, { useState } from 'react' +import RunMatch from './RunMatch' +import Mail from './Mail' +const Matching = () => { + const [hasMatched, setHasMatched] = useState(false) + const [hasSent, setHasSent] = useState(false) + + return ( +
    + {hasMatched ? ( + + ) : ( + + )} +
    + ) +} + +export default Matching diff --git a/client/src/views/auth/matching/RunMatch.js b/client/src/views/auth/matching/RunMatch.js new file mode 100644 index 000000000..5783fc1fa --- /dev/null +++ b/client/src/views/auth/matching/RunMatch.js @@ -0,0 +1,226 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useEffect } from 'react' +import PropTypes from 'prop-types' +import { + CModal, + CModalHeader, + CModalTitle, + CModalBody, + CModalFooter, + CButton, + CInputGroup, + CInputGroupText, + CFormControl, +} from '@coreui/react' +import axios from 'axios' +import Spinner from '../../components/Spinner' +const RunMatch = ({ hasSent, setHasSent, hasMatched, setHasMatched }) => { + const [isModal, setIsModal] = useState(false) + const [isUpdateTime, setIsUpdateTime] = useState(false) + const [startTime, setStartTime] = useState('') + const [endTime, setEndTime] = useState('') + const [newStartTime, setNewStartTime] = useState('') + const [newEndTime, setNewEndTime] = useState('') + const [snumber, setSnumber] = useState(0) + const [jnumber, setJnumber] = useState(0) + const [loading, setLoading] = useState(true) + const [pass, setPass] = useState(false) + const nowDate = new Date() + const match = () => { + axios + .get('/api/study/matching', { responseType: 'blob' }) + .then((res) => { + console.log('res', res) + const url = window.URL.createObjectURL(new Blob([res.data])) + console.log('url', url) + const link = document.createElement('a') + link.href = url + link.setAttribute('download', 'output.xlsx') + document.body.appendChild(link) + link.click() + }) + .then(() => { + console.log('finish match!') + setHasMatched(true) + setHasSent(false) + }) + .catch((err) => console.log(err)) + } + const getMatchInfo = () => { + axios + .get('/api/time/getTime', { params: { target: 'matching_start' } }) + .then((res) => { + setStartTime(res.data) + }) + .catch((err) => console.log(err)) + axios + .get('/api/time/getTime', { params: { target: 'matching_end' } }) + .then((res) => { + setEndTime(res.data) + const realDate = res.data.substring(0, 10) + 'T' + res.data.substring(11, 16) + if (new Date(realDate).getTime() < nowDate) setPass(true) + }) + .catch((err) => console.log(err)) + axios + .get('/api/study/allForms') + .then((res) => { + setSnumber(res.data.seniorCount) + setJnumber(res.data.juniorCount) + }) + .then(() => setLoading(false)) + .catch((err) => console.log(err)) + } + const setMatchTime = () => { + axios + .post('/api/time/setTime', { target: 'matching_start', time: newStartTime }) + .then(() => console.log('set start time')) + .catch((err) => console.log(err)) + axios + .post('/api/time/setTime', { target: 'matching_end', time: newEndTime }) + .then(() => { + console.log('set end time') + hasSent ? setHasSent(false) : getMatchInfo() + }) + .catch((err) => console.log(err)) + } + const clearDB = () => { + axios + .delete('/api/study/form') + .then(() => console.log('clear all form!')) + .catch((e) => console.log(e)) + } + useEffect(() => { + getMatchInfo() + }, [hasSent]) + const updateMatch = () => { + if (newStartTime === '') alert('請輸入起始日期') + if (newEndTime === '') alert('請輸入截止日期') + setMatchTime() + if (!isUpdateTime) { + clearDB() + } + setIsModal(false) + setIsUpdateTime(false) + } + return loading ? ( + + ) : ( + <> + { + setIsModal(false) + setIsUpdateTime(false) + }} + alignment="center" + > + { + setIsModal(false) + setIsUpdateTime(false) + }} + > + {isUpdateTime ? ( + 修改配對時間 + ) : ( + 開啟新一期配對 + )} + + +

    請選擇起始與結束時間:

    + + 起始日期 + { + if (new Date(e.target.value) > new Date(newEndTime)) { + e.target.value = '' + setNewStartTime('') + alert('無效的起始日期') + return + } + setNewStartTime(e.target.value) + }} + /> + + + 截止日期 + { + if (new Date(e.target.value) < new Date(newStartTime)) { + e.target.value = '' + setNewEndTime('') + alert('無效的截止日期') + return + } + setNewEndTime(e.target.value) + }} + /> + +
    + + { + setIsModal(false) + setIsUpdateTime(false) + }} + > + Cancel + + + Yes + + +
    +
    +

    + 本期的配對時間為 +
    + + {startTime} ~ {endTime} + +
    + 請在截止日後配對學長姐與學弟妹 +
    + {pass ? `目前共有${jnumber}名學弟妹以及${snumber}名學長姐在等待您的配對結果` : ''} +

    + +
    +
    +

    + {hasSent + ? '若要開啟一期新配對,請點下方按鈕' + : '要先將本期的配對結果寄給大家後才能再開一期喔~'} +

    + + +
    + + ) +} +RunMatch.propTypes = { + hasSent: PropTypes.bool, + hasMatched: PropTypes.bool, + setHasSent: PropTypes.func, + setHasMatched: PropTypes.func, +} +export default RunMatch diff --git a/client/src/views/auth/matching/index.js b/client/src/views/auth/matching/index.js new file mode 100644 index 000000000..0684adc87 --- /dev/null +++ b/client/src/views/auth/matching/index.js @@ -0,0 +1,7 @@ +/* eslint-disable prettier/prettier */ +import Matching from './Matching' +import success_icon from '../../../assets/images/success_checked.png' +import mail_sent from '../../../assets/images/mail-sent.png' +import Spinner from '../../components/Spinner' +export { success_icon, mail_sent, Spinner } +export default Matching diff --git a/client/src/views/auth/register/Applicant.js b/client/src/views/auth/register/Applicant.js new file mode 100644 index 000000000..19cf806bb --- /dev/null +++ b/client/src/views/auth/register/Applicant.js @@ -0,0 +1,35 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import PropTypes from 'prop-types' +import CIcon from '@coreui/icons-react' +import { CButton } from '@coreui/react' +const Applicant = ({ person,setIsModal,setModalPerson }) => { + const show=()=>{ + setModalPerson(person) + setIsModal(true) + } + return ( +
    +
    姓名:{person.username}
    +
    + +
    +
    + 學號:{person.account} +
    + 信箱:{person.email} +
    +
    + show()} > + + +
    +
    + ) +} +Applicant.propTypes = { + person: PropTypes.object, + setIsModal: PropTypes.func, + setModalPerson:PropTypes.func, +} +export default Applicant diff --git a/client/src/views/auth/register/Register.js b/client/src/views/auth/register/Register.js new file mode 100644 index 000000000..981190316 --- /dev/null +++ b/client/src/views/auth/register/Register.js @@ -0,0 +1,97 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useEffect } from 'react' +import Applicant from './Applicant' +import axios from 'axios' +import { CModal, CModalBody, CModalHeader, CModalFooter, CModalTitle, CButton } from '@coreui/react' +import { Spinner } from './index' +const Register = () => { + const [isModal, setIsModal] = useState(false) + const [modalPerson, setModalPerson] = useState({ + username: '', + account: '', + email: '', + imgSrc: '', + }) + const [isPending, setIsPending] = useState(true) + const [applicants, setApplicants] = useState([]) + const getPendings = () => { + setIsPending(true) + axios + .post('/api/showPending') + .then((res) => { + console.log('pendings:', res.data.pendings.length) + setApplicants(res.data.pendings) + setIsPending(false) + }) + .catch((err) => console.log(err)) + } + const verify = () => { + axios + .post('/api/handlePending', { account: modalPerson.account, acceptUser: true }) + .then((res) => { + alert('已發送驗證信至對方信箱!') + setIsModal(false) + getPendings() + }).catch(err=>console.log(err)) + } + const reject = () => { + axios + .post('/api/handlePending', { account: modalPerson.account, acceptUser: false }) + .then((res) => { + alert('已發送拒絕申請信至對方信箱!') + setIsModal(false) + getPendings() + }).catch(err=>console.log(err)) + } + useEffect(() => { + getPendings() + }, []) + return isPending ? ( + + ) : ( + <> + + setIsModal(false)} alignment="center"> + setIsModal(false)}> + 驗證身分 + + + 姓名:{modalPerson.username} +
    + 學號:{modalPerson.account} +
    + 信箱:{modalPerson.email} +
    + +
    + + reject()}> + 拒絕申請 + + verify()}> + 同意申請 + + +
    +
    + {applicants.length ? ( + <> +

    底下為待您確認的註冊申請:

    + {applicants.map((a, i) => ( + + ))} + + ) : ( +

    目前無新的確認申請!

    + )} +
    + + ) +} + +export default Register diff --git a/client/src/views/auth/register/index.js b/client/src/views/auth/register/index.js new file mode 100644 index 000000000..0d1d080f7 --- /dev/null +++ b/client/src/views/auth/register/index.js @@ -0,0 +1,5 @@ +/* eslint-disable prettier/prettier */ +import Register from './Register' +import Spinner from '../../components/Spinner' +export default Register +export { Spinner } \ No newline at end of file diff --git a/client/src/views/components/Spinner.js b/client/src/views/components/Spinner.js new file mode 100644 index 000000000..5f273debb --- /dev/null +++ b/client/src/views/components/Spinner.js @@ -0,0 +1,14 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' + +const Spinner = () => { + return ( +
    +
    + +
    +
    + ) +} + +export default Spinner diff --git a/client/src/views/dashboard/Dashboard.js b/client/src/views/dashboard/Dashboard.js new file mode 100644 index 000000000..1784443f1 --- /dev/null +++ b/client/src/views/dashboard/Dashboard.js @@ -0,0 +1,171 @@ +import React, { lazy, useEffect, useState } from 'react' +import { useSelector } from 'react-redux' +import { Link, Redirect } from 'react-router-dom' +import { + CContainer, + CRow, + CCol, + CImage, + CCarousel, + CCarouselItem, + CCarouselCaption, + CCard, + CCardHeader, + CCardBody, + CCardImage, + CCardTitle, +} from '@coreui/react' +import logo from '../../assets/images/logo_row.png' +import axios from 'axios' + +const perRecruitment = 4 +const perRecommendation = 4 + +const Dashboard = () => { + const [recentColumns, setRecentColumns] = useState([]) + const [recentRecruitments, setRecentRecruitments] = useState([]) + const [recentRecommendations, setRecentRecommendations] = useState([]) + + const getRecentColumns = () => { + axios + .get('api/column/recent') + .then((res) => { + setRecentColumns(res.data.data) + }) + .catch((err) => console.log(err)) + } + + const getRecentRecruitments = () => { + axios + .get('api/recruitment/recent', { params: { number: perRecruitment } }) + .then((res) => { + setRecentRecruitments(res.data.data) + }) + .catch((err) => console.log(err)) + } + + const getRecentRecommendations = () => { + axios + .get('api/recommendation/recent', { params: { number: perRecommendation } }) + .then((res) => { + setRecentRecommendations(res.data.data) + }) + .catch((err) => console.log(err)) + } + + useEffect(() => { + getRecentColumns() + getRecentRecruitments() + getRecentRecommendations() + }, []) + + return ( + + + + +

    Recent Interviews

    +
    + + + {recentColumns.map((column, index) => { + return ( + + + + + {column.title.map((title, i) => { + return

    {title}

    + })} + + +
      + {column.edu.map((edu, i) => { + return ( +
    • + {edu} +
    • + ) + })} +
    +
    + +
      + {column.exp.map((exp, i) => { + return ( +
    • + {exp} +
    • + ) + })} +
    +
    +
    +
    + +
    + ) + })} +
    +
    +
    +
    + + + +

    Recent Recruitment

    +
    + + + {recentRecruitments.map((recruitment, index) => { + return ( + + + + + + {recruitment.title.title} + + + + + ) + })} + + +
    +
    + + + +

    Recent Job Hunting

    +
    + + + {recentRecommendations.map((recommendation, index) => { + return ( + + + + + + + {recommendation.title.title} + + + + + + ) + })} + + +
    +
    +
    + ) +} + +export default Dashboard diff --git a/client/src/views/dashboard/index.js b/client/src/views/dashboard/index.js new file mode 100644 index 000000000..d08092b26 --- /dev/null +++ b/client/src/views/dashboard/index.js @@ -0,0 +1,2 @@ +import Dashboard from './Dashboard' +export default Dashboard diff --git a/client/src/views/in/agent.js b/client/src/views/in/agent.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/views/in/career/Career.js b/client/src/views/in/career/Career.js new file mode 100644 index 000000000..08ee0df0f --- /dev/null +++ b/client/src/views/in/career/Career.js @@ -0,0 +1,38 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import { Link } from 'react-router-dom' +import { CCol, CRow, CContainer, CImage } from '@coreui/react' +import { Recruitment_image, Recommendation_image } from './index' + +const Career = () => { + return ( +
    + + + + + + +

    來看看各家公司的徵才貼文!

    +
    + + + + +

    電機系人才都在這!

    +
    +
    +
    +
    + ) +} + +export default Career diff --git a/client/src/views/in/career/CareerBlock.js b/client/src/views/in/career/CareerBlock.js new file mode 100644 index 000000000..d1c87b4cb --- /dev/null +++ b/client/src/views/in/career/CareerBlock.js @@ -0,0 +1,159 @@ +/* eslint-disable prettier/prettier */ +import React, { useState } from 'react' +import { Link, useLocation } from 'react-router-dom' +import PropTypes from 'prop-types' +import { CWidgetBrand, CAvatar } from '@coreui/react' +import { eesa } from './index' +import CIcon from '@coreui/icons-react' +import axios from 'axios' +import parser from 'html-react-parser' + +const CareerBlock = ({ post, setData, index }) => { + const location = useLocation() + const recru = location.pathname.search('cruitment') > 0 ? true : false + const recom = location.pathname.search('commendation') > 0 ? true : false + const own = location.pathname.search('own') > 0 ? true : false + const [isExpand, setIsExpand] = useState(false) + const deleteCareer = (id) => { + if (recru) { + axios + .delete('/api/deleteRecruitment', { data: { _id: id } }) + .then((res) => { + setData((data) => { + let newData = [...data] + newData.splice(index, 1) + return newData + }) + alert('delete ' + res.data.data) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } else if (recom) { + axios + .delete('/api/recommendation', { data: { _id: id } }) + .then((res) => { + setData((data) => { + let newData = [...data] + newData.splice(index, 1) + return newData + }) + alert('delete ' + res.data.title) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + } + const spec = (li) => { + return ( +
    + {li} +
    + ) + } + return recru ? ( +
    + + + } + values={[[`${post.title.company_name} 徵 ${post.title.work_type}`]]} + /> + +
    +
    +

    + {post.title.title} + {own ? ( + <> + + + + + deleteCareer(post._id)} + > + + + ) : ( + <> + )} +

    +

    {post.info.salary}

    +

    要求學歷:

    +
    {post.info.diploma}
    + {!isExpand && } + {isExpand && ( + <> +

    工作經驗限制:

    +

    {post.info.experience.map((exp) => spec(exp))}

    +

    要求條件:

    +

    {post.spec.requirement.map((req) => spec(req))}

    +

    說明:

    +

    {parser(post.spec.description)}

    + + + )} +
    +
    + ) : ( +
    + + + } + values={[[post.title.title]]} + /> + +
    +
    +

    + {post.title.name} asking for {post.title.desire_work_type} + {own ? ( + <> + + + + + deleteCareer(post._id)} + > + + + ) : ( + <> + )} +

    +
    + {post.info.diploma} |{' '} + {post.info.contact} | {post.info.email} +
    + {!isExpand && } + {isExpand && ( + <> +

    個人簡歷:

    +

    {post.spec.experience.map((exp) => spec(exp))}

    +

    專業技能:

    +

    {post.spec.speciality.map((speci) => spec(speci))}

    + + + )} +
    +
    + ) +} +CareerBlock.propTypes = { + post: PropTypes.object, + setData: PropTypes.func, + index: PropTypes.number, +} +export default CareerBlock diff --git a/client/src/views/in/career/CareerImageEditor.js b/client/src/views/in/career/CareerImageEditor.js new file mode 100644 index 000000000..076a76c27 --- /dev/null +++ b/client/src/views/in/career/CareerImageEditor.js @@ -0,0 +1,84 @@ +import React, { useState, useCallback } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { selectCareer, setCroppedFile, setCroppedDataUrl } from '../../../slices/careerSlice' +import { + CButton, + CCol, + CContainer, + CImage, + CRow, + CFormLabel, + CFormRange, + CInputGroup, +} from '@coreui/react' +import Cropper from 'react-easy-crop' +import { getCroppedImg } from './' + +const CareerImageEditor = ({ imgSrc }) => { + const dispatch = useDispatch() + const { croppedDataUrl } = useSelector(selectCareer) + const [crop, setCrop] = useState({ x: 0, y: 0 }) + const [zoom, setZoom] = useState(1) + const [croppedAreaPixels, setCroppedAreaPixels] = useState(null) + + const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => { + setCroppedAreaPixels(croppedAreaPixels) + }, []) + + const showCroppedImage = useCallback(async () => { + try { + const { file, dataUrl } = await getCroppedImg(imgSrc, croppedAreaPixels) + dispatch(setCroppedFile(file)) + dispatch(setCroppedDataUrl(dataUrl)) + } catch (e) { + console.error(e) + } + }, [imgSrc, croppedAreaPixels]) + + return ( + <> + {croppedDataUrl === '' ? ( + <> + + + + + + + Zoom + { + setZoom(e.target.value) + }} + /> + + + Show Result + + + + + ) : ( + <> + + + )} + + ) +} + +export default CareerImageEditor diff --git a/client/src/views/in/career/CareerPreview.js b/client/src/views/in/career/CareerPreview.js new file mode 100644 index 000000000..39a300cb0 --- /dev/null +++ b/client/src/views/in/career/CareerPreview.js @@ -0,0 +1,96 @@ +/* eslint-disable prettier/prettier */ +import React, { useState } from 'react' +import PropTypes from 'prop-types' +import { CWidgetBrand } from '@coreui/react' +import { eesa } from './index' +import parser from 'html-react-parser' + +const CareerPreview = ({ post, experience, requirement }) => { + const [isExpand, setIsExpand] = useState(false) + const [previewURL, setPreviewURL] = useState(post.file) + if (post.file && typeof post.file === 'object') { + let reader = new FileReader() + reader.onloadend = () => { + setPreviewURL(reader.result) + } + reader.readAsDataURL(post.file) + } + const spec = (li) => { + return ( +
    + {li} +
    + ) + } + if (typeof post.description === 'string') { + return ( +
    + + } + values={[[`${post.companyName} 徵 ${post.workType}`]]} + /> +
    +
    +

    {post.title}

    +

    {post.salary}

    +

    要求學歷:

    +

    {post.diploma}

    + {!isExpand && } + {isExpand && ( + <> +

    工作經驗限制:

    +

    {experience.map((exp) => spec(exp))}

    +

    要求條件:

    +

    {requirement.map((req) => spec(req))}

    +

    說明:

    +

    {parser(post.description)}

    + + + )} +
    +
    + ) + } else { + return ( +
    + + } + values={[[post.title]]} + /> +
    +
    +

    + {post.name} asking for {post.desireWorkType} +

    +
    + {post.diploma} |{' '} + {post.contact} | {post.email} +
    + {!isExpand && } + {isExpand && ( + <> +

    個人簡歷:

    +

    {experience.map((exp) => spec(exp))}

    +

    專業技能:

    +

    {requirement.map((speci) => spec(speci))}

    + + + )} +
    +
    + ) + } +} +CareerPreview.propTypes = { + post: PropTypes.object, + experience: PropTypes.array, + requirement: PropTypes.array, +} + +export default CareerPreview diff --git a/client/src/views/in/career/index.js b/client/src/views/in/career/index.js new file mode 100644 index 000000000..1ebdfbe88 --- /dev/null +++ b/client/src/views/in/career/index.js @@ -0,0 +1,73 @@ +import Career from './Career' +import { selectCareer, setKeywords, clearKeywords } from '../../../slices/careerSlice' +import eesa from '../../../assets/images/eesa-icon.png' +import Recruitment_image from '../../../assets/images/Recruitment.png' +import Recommendation_image from '../../../assets/images/Recommendation.png' +export { selectCareer, setKeywords, clearKeywords, eesa, Recommendation_image, Recruitment_image } +export default Career + +const createImage = (url) => + new Promise((resolve, reject) => { + const image = new Image() + image.addEventListener('load', () => resolve(image)) + image.addEventListener('error', (error) => reject(error)) + image.setAttribute('crossOrigin', 'anonymous') // needed to avoid cross-origin issues on CodeSandbox + image.src = url + }) + +/* function getRadianAngle(degreeValue) { + return (degreeValue * Math.PI) / 180 +} */ + +/** + * This function was adapted from the one in the ReadMe of https://github.com/DominicTobias/react-image-crop + * @param {File} image - Image File url + * @param {Object} pixelCrop - pixelCrop Object provided by react-easy-crop + * @param {number} rotation - optional rotation parameter (not-used) + */ +export async function getCroppedImg(imageSrc, pixelCrop, rotation = 0) { + const image = await createImage(imageSrc) + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + + const maxSize = Math.max(image.width, image.height) + const safeArea = 2 * ((maxSize / 2) * Math.sqrt(2)) + + // set each dimensions to double largest dimension to allow for a safe area for the + // image to rotate in without being clipped by canvas context + canvas.width = safeArea + canvas.height = safeArea + + // translate canvas context to a central location on image to allow rotating around the center. + ctx.translate(safeArea / 2, safeArea / 2) + // ctx.rotate(getRadianAngle(rotation)) + ctx.translate(-safeArea / 2, -safeArea / 2) + + // draw rotated image and store data. + ctx.drawImage(image, safeArea / 2 - image.width * 0.5, safeArea / 2 - image.height * 0.5) + const data = ctx.getImageData(0, 0, safeArea, safeArea) + + // set canvas width to final desired crop size - this will clear existing context + canvas.width = pixelCrop.width + canvas.height = pixelCrop.height + + // paste generated rotate image with correct offsets for x,y crop values. + ctx.putImageData( + data, + Math.round(0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x), + Math.round(0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y), + ) + + // As Base64 string + // return canvas.toDataURL('image/jpeg'); + + // As a blob + return new Promise((resolve) => { + canvas.toBlob((file) => { + resolve({ + file: file, + dataUrl: canvas.toDataURL('image/jpeg'), + }) + }, 'image/png') + }) +} diff --git a/client/src/views/in/changePsw/ChangePsw.js b/client/src/views/in/changePsw/ChangePsw.js new file mode 100644 index 000000000..a78a550dc --- /dev/null +++ b/client/src/views/in/changePsw/ChangePsw.js @@ -0,0 +1,100 @@ +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' +import { logout } from '../../../slices/loginSlice' +import axios from 'axios' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CRow, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' + +const ChangeFormTemplate = { + oldPsw: '', + newPsw: '', +} + +const ChangePsw = () => { + const dispatch = useDispatch() + const [changeForm, setChangeForm] = useState(ChangeFormTemplate) + + const handleInputChange = (e) => { + setChangeForm({ ...changeForm, [e.target.name]: e.target.value }) + } + + const handleSubmit = (e) => { + e.preventDefault() + // connect with backend + axios + .post('/api/chPassword', changeForm) + .then((res) => { + alert(`密碼已更改 \n請使用新密碼登入`) + axios + .post('/api/logout') + .then((res) => { + dispatch(logout()) + }) + .catch((err) => { + alert(err.response.data.description) + }) + }) + .catch((err) => { + alert(err.response.data.description) + }) + } + + return ( +
    + + + + + + +

    Wanna Change Your Password ?

    +

    + + + + + + + + + + + + + +
    + + Change + +
    +
    +
    +
    +
    +
    +
    +
    +
    + ) +} + +export default ChangePsw diff --git a/client/src/views/in/changePsw/index.js b/client/src/views/in/changePsw/index.js new file mode 100644 index 000000000..c4bff5acb --- /dev/null +++ b/client/src/views/in/changePsw/index.js @@ -0,0 +1,2 @@ +import ChangePsw from './ChangePsw' +export default ChangePsw diff --git a/client/src/views/in/column/Column.js b/client/src/views/in/column/Column.js new file mode 100644 index 000000000..6fe2955b6 --- /dev/null +++ b/client/src/views/in/column/Column.js @@ -0,0 +1,45 @@ +import React, { useEffect, useState } from 'react' +import PropTypes from 'prop-types' +import Title from './Title' +import Resume from './Resume' +import Testimonials from './Testimonials' +import { useParams } from 'react-router-dom' +import axios from 'axios' + +const Column = () => { + const id = useParams().id + const [data, setData] = useState([]) + const getData = () => { + if (id.includes('interview')) { + axios + .get(`./${id}.json`) + .then((res) => { + setData(res.data) + }) + .catch((err) => {}) + } else { + axios + .get('/api/column/detail', { params: { id: id } }) + .then((res) => { + setData(res.data) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + } + useEffect(() => { + getData() + }, []) + return ( +
    + {data.top && } + {data.body && <Resume data={data.body} />} + {data.annotation && <Testimonials data={data.annotation} />} + </div> + ) +} +Column.propTypes = { + id: PropTypes.string, +} +export default Column diff --git a/client/src/views/in/column/Resume.js b/client/src/views/in/column/Resume.js new file mode 100644 index 000000000..01c029061 --- /dev/null +++ b/client/src/views/in/column/Resume.js @@ -0,0 +1,34 @@ +/* eslint-disable react/prop-types */ +import React from 'react' +const Resume = ({ data }) => { + const getSubSections = (bigsection) => { + const subSections = bigsection.bigsections.map((bigsection) => { + return ( + <div key={bigsection.subtitle}> + <h3>{bigsection.subtitle}</h3> + <p>    {bigsection.subsection}</p> + </div> + ) + }) + return subSections + } + const bigSections = data.body.map((bigsection) => { + return ( + <div className="row education" key={bigsection.bigtitle}> + <div className="three columns header-col"> + <h1 align="center" style={{ lineHeight: '2.6rem' }}> + <span>{bigsection.bigtitle}</span> + </h1> + </div> + <div className="nine columns main-col"> + <div className="row item"> + <div className="twelve columns">{getSubSections(bigsection)}</div> + </div> + </div> + </div> + ) + }) + return <section id="resume">{bigSections}</section> +} + +export default Resume diff --git a/client/src/views/in/column/Testimonials.js b/client/src/views/in/column/Testimonials.js new file mode 100644 index 000000000..1cbda6777 --- /dev/null +++ b/client/src/views/in/column/Testimonials.js @@ -0,0 +1,28 @@ +/* eslint-disable react/prop-types */ +import React from 'react' + +const Testimonials = ({ data }) => { + const annotations = data.annotation.map((annotation) => { + return ( + <li key={annotation.contributer}> + <blockquote> + <p>{annotation.job}</p> + <cite>{annotation.contributer}</cite> + </blockquote> + </li> + ) + }) + return ( + <section id="testimonials"> + <div className="text-container"> + <div className="row"> + <div className="ten columns flex-container"> + <ul className="slides">{annotations}</ul> + </div> + </div> + </div> + </section> + ) +} + +export default Testimonials diff --git a/client/src/views/in/column/Title.js b/client/src/views/in/column/Title.js new file mode 100644 index 000000000..e3f964d3c --- /dev/null +++ b/client/src/views/in/column/Title.js @@ -0,0 +1,34 @@ +/* eslint-disable react/prop-types */ +import React from 'react' + +const Title = ({ data }) => { + const name = data.name + const exp = data.experience + const hashtags = data.hashtags.map((hashtag) => { + return ( + <nobr key={hashtag}> + <a className="hashtag" href=""> + #{hashtag}  + </a> + </nobr> + ) + }) + return ( + <header> + <div className="row banner"> + <div className="banner-text"> + <h1 className="responsive-headline">{name}</h1> + {exp.map((e) => ( + <h2 key={e} style={{ fontSize: '2rem', fontWeight: '500', color: 'white' }}> + <nobr>{e}</nobr> + </h2> + ))} + {hashtags} + <hr /> + </div> + </div> + </header> + ) +} + +export default Title diff --git a/client/src/views/in/column/index.js b/client/src/views/in/column/index.js new file mode 100644 index 000000000..228c5fc36 --- /dev/null +++ b/client/src/views/in/column/index.js @@ -0,0 +1,2 @@ +import Column from './Column' +export default Column diff --git a/client/src/views/in/columnSummary/ColumnSummary.js b/client/src/views/in/columnSummary/ColumnSummary.js new file mode 100644 index 000000000..6c3fa1115 --- /dev/null +++ b/client/src/views/in/columnSummary/ColumnSummary.js @@ -0,0 +1,252 @@ +/* eslint-disable react/prop-types */ +import React, { useState, useEffect } from 'react' +import { Link } from 'react-router-dom' +import { CButton, CFormControl, CInputGroup } from '@coreui/react' +import CIcon from '@coreui/icons-react' +import { makeStyles } from '@material-ui/core/styles' +import Typography from '@material-ui/core/Typography' +import Box from '@material-ui/core/Box' +import Grid from '@material-ui/core/Grid' +import Card from '@material-ui/core/Card' +import CardActions from '@material-ui/core/CardActions' +import CardMedia from '@material-ui/core/CardMedia' +import CardContent from '@material-ui/core/CardContent' +import Avatar from '@material-ui/core/Avatar' +import BookmarkBorderIcon from '@material-ui/icons/BookmarkBorder' +import Pagination from '@material-ui/lab/Pagination' +import { Spinner, default_male } from './index' +import 'bootstrap-icons/font/bootstrap-icons.css' +import axios from 'axios' +import { useDispatch, useSelector } from 'react-redux' +import { + selectColumnSummary, + setPage, + setKeywords, + setIsSearch, +} from '../../../slices/columnSummarySlice' + +const useStyles = makeStyles((theme) => ({ + hero: { + backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://images.unsplash.com/photo-1558981852-426c6c22a060?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80')`, + height: '500px', + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + backgroundSize: 'cover', + position: 'relative', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + color: '#fff', + flexDirection: 'column', + }, + blogsContainer: { + paddingTop: theme.spacing(3), + }, + card: { + maxWidth: '100%', + }, + media: { + height: '680px', + }, + cardActions: { + display: 'flex', + margin: '0 10px', + justifyContent: 'space-between', + }, + author: { + display: 'flex', + }, + paginationContainer: { + display: 'flex', + justifyContent: 'center', + }, + exp: { + fontSize: '1.875rem', + fontWeight: '10', + }, + intro: { + fontFamily: ['Roboto', 'Helvetica', 'Arial', 'sans-serif'], + fontSize: '1.15rem', + margin: '0.2rem', + }, +})) + +const ColumnSummary = () => { + const postsPerPage = 5 + const classes = useStyles() + const dispatch = useDispatch() + const [data, setData] = useState({ maxPage: undefined, data: [] }) + const { page, keywords, isSearch } = useSelector(selectColumnSummary) + const [isPending, setIsPending] = useState(true) + const getData = () => { + setIsPending(true) + axios + .get('/api/column/outline', { + params: { perpage: postsPerPage.toString(), page: page.toString() }, + }) + .then((res) => { + setData(res.data) + setIsPending(false) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + const searchData = (e = null) => { + if (e) { + e.preventDefault() + } + dispatch(setIsSearch(true)) + setIsPending(true) + axios + .get('/api/column/search', { params: { keyword: keywords, page: page } }) + .then((res) => { + setData(res.data) + setIsPending(false) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + useEffect(() => { + if (!isSearch) { + getData() + } else { + searchData() + } + }, [page]) + const articles = (data) => { + return data.data.map((art, index) => { + return ( + <Grid className="my-4" item xs={12} md={12} key={index}> + <Card className={classes.card}> + <Link to={'/column_summary/' + art.id}> + <CardMedia + className={classes.media} + image={art.imgSrc} + title="Contemplative Reptile" + /> + <CardContent> + <Typography gutterBottom variant="h3" component="h3"> + {art.title} + </Typography> + {art.exp.map((e, i) => ( + <Typography gutterBottom className={classes.exp} key={i}> + {e} + </Typography> + ))} + <Typography + variant="body2" + color="textSecondary" + component="p" + className={classes.intro} + > + {art.intro} + </Typography> + </CardContent> + </Link> + <CardActions className={classes.cardActions}> + {art.anno.map((person, i) => { + return ( + <Box className={classes.author} key={i}> + <Avatar src={default_male} /> + <Box ml={2} display="flex" alignItems="center"> + <Typography variant="subtitle2" component="p"> + {person} + </Typography> + </Box> + </Box> + ) + })} + <Box> + <Typography variant="subtitle2" color="textSecondary" component="p"> + {art.date}   + <BookmarkBorderIcon /> + </Typography> + </Box> + </CardActions> + </Card> + </Grid> + ) + }) + } + return ( + <div className="d-flex flex-column justify-content-center m-auto w-75"> + <Box className={classes.hero}> + <Box className="display-1">Our Articles</Box> + <form className="text-light p-2 m-2 w-75" onSubmit={searchData} method="GET"> + <CInputGroup> + <CButton + onClick={() => { + dispatch(setIsSearch(false)) + dispatch(setKeywords('')) + if (page === 1) { + getData() + } else { + dispatch(setPage(1)) + } + }} + color="light" + > + <CIcon icon="cil-home" name="cil-home" /> + </CButton> + <CFormControl + type="search" + placeholder={keywords === '' ? 'search for...' : keywords} + onChange={(e) => dispatch(setKeywords(e.target.value))} + value={keywords ? keywords : ''} + ></CFormControl> + <CButton color="light" type="submit"> + <CIcon icon="cil-search" name="cil-search" /> + </CButton> + </CInputGroup> + </form> + </Box> + {data.maxPage === 0 && !isPending ? ( + <div className="display-2 d-flex justify-content-center mt-3">No corresponding columns</div> + ) : data.maxPage ? ( + <Box my={4} className={classes.paginationContainer}> + <Pagination + count={data.maxPage} + defaultPage={page} + page={page} + color="secondary" + onChange={(e, val) => { + window.scrollTo(0, 0) + dispatch(setPage(val)) + }} + /> + </Box> + ) : ( + [] + )} + {isPending === true ? ( + <Spinner /> + ) : ( + <> + {data && ( + <div className={classes.blogsContainer}> + <Grid container spacing={1}> + {articles(data)} + </Grid> + </div> + )} + <Box my={4} className={classes.paginationContainer}> + <Pagination + count={data.maxPage} + defaultPage={page} + page={page} + color="secondary" + onChange={(e, val) => { + window.scrollTo(0, 0) + dispatch(setPage(val)) + }} + /> + </Box> + </> + )} + </div> + ) +} + +export default ColumnSummary diff --git a/client/src/views/in/columnSummary/index.js b/client/src/views/in/columnSummary/index.js new file mode 100644 index 000000000..4076949b5 --- /dev/null +++ b/client/src/views/in/columnSummary/index.js @@ -0,0 +1,5 @@ +import ColumnSummary from './ColumnSummary' +import Spinner from '../../components/Spinner' +import default_male from '../../../assets/images/default_male.png' +export { Spinner, default_male } +export default ColumnSummary diff --git a/client/src/views/in/form.js b/client/src/views/in/form.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/views/in/index.js b/client/src/views/in/index.js new file mode 100644 index 000000000..9c3c8e488 --- /dev/null +++ b/client/src/views/in/index.js @@ -0,0 +1,46 @@ +import React from 'react' + +const ColumnSummary = React.lazy(() => import('./columnSummary')) +const Column = React.lazy(() => import('./column')) +const Career = React.lazy(() => import('./career')) + +const Recruitment = React.lazy(() => import('./recruitment')) +const AddRecruitment = React.lazy(() => import('./recruitment/add')) +const EditRecruitment = React.lazy(() => import('./recruitment/edit')) +const OwnRecruitment = React.lazy(() => import('./recruitment/own')) + +const Recommendation = React.lazy(() => import('./recommendation')) +const AddRecommendation = React.lazy(() => import('./recommendation/add')) +const EditRecommendation = React.lazy(() => import('./recommendation/edit')) +const OwnRecommendation = React.lazy(() => import('./recommendation/own')) + +const Profile = React.lazy(() => import('./profile')) +const EditProfile = React.lazy(() => import('./profile/edit')) +const SearchProfile = React.lazy(() => import('./profile/search')) + +const Study = React.lazy(() => import('./study')) + +const Matching = React.lazy(() => import('./matching')) +const MatchForm = React.lazy(() => import('./matching/matchForm')) + +const ChangePsw = React.lazy(() => import('./changePsw')) +export { + ColumnSummary, + Column, + Career, + Recruitment, + AddRecruitment, + EditRecruitment, + OwnRecruitment, + Recommendation, + AddRecommendation, + EditRecommendation, + OwnRecommendation, + Profile, + EditProfile, + SearchProfile, + Study, + Matching, + MatchForm, + ChangePsw, +} diff --git a/client/src/views/in/matching/Experience.js b/client/src/views/in/matching/Experience.js new file mode 100644 index 000000000..835812be2 --- /dev/null +++ b/client/src/views/in/matching/Experience.js @@ -0,0 +1,23 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' + +const Experience = () => { + return ( + <div className="aboard-experience d-flex flex-column align-items-center p-3"> + <h2>此處為精華區,收錄歷年申請結果及心得,請點下方連結</h2> + <img + src="https://ssl.gstatic.com/images/branding/product/2x/hh_drive_96dp.png" + alt="Google Drive" + className="img-fluid my-3" + /> + <a + href="https://drive.google.com/drive/folders/1_yuxj4UDDwlwC26yxTMBu8yE7BaRyZS6?usp=sharing" + className="btn btn-primary h3" + > + 點此進入GDrive精華區 + </a> + </div> + ) +} + +export default Experience diff --git a/client/src/views/in/matching/MatchResult.js b/client/src/views/in/matching/MatchResult.js new file mode 100644 index 000000000..9d0615eb2 --- /dev/null +++ b/client/src/views/in/matching/MatchResult.js @@ -0,0 +1,68 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import { useHistory } from 'react-router' +import PropTypes from 'prop-types' +import { CButton } from '@coreui/react' +import ResultBlock from './ResultBlock' +import { useSelector } from 'react-redux' +import { selectLogin } from '../../../slices/loginSlice' + +const MatchResult = ({ sdata, jdata, identity, ended }) => { + const history = useHistory() + const reFill = (e) => { + e.preventDefault() + history.push(`/match_form/${identity}`) + } + const { imgSrc } = useSelector(selectLogin) + return ( + <div className="p-4"> + {(identity === 'senior' && jdata.length && jdata !== 'unmatched') || + (identity === 'junior' && sdata !== 'unmatched') ? ( + identity === 'junior' ? ( + <> + <ResultBlock data={sdata} /> + <ResultBlock data={{ ...jdata, image: imgSrc }} /> + </> + ) : ( + <> + <ResultBlock data={{ ...sdata, image: imgSrc }} /> + <> + {jdata.map((j) => { + return <ResultBlock data={j} key={j.name} /> + })} + </> + </> + ) + ) : ( + <> + {ended ? ( + identity === 'junior' && sdata === 'unmatched' ? ( + <h3>很抱歉我們無法為您配對到適合的學長姐</h3> + ) : identity === 'senior' && jdata === 'unmatched' ? ( + <h3>感謝您願意提供經驗分享,但目前沒有需要的學弟妹</h3> + ) : ( + <> + <h3>配對結果尚未公佈!</h3> + <h3>請靜待結果</h3> + </> + ) + ) : ( + <> + <h3>配對結果尚未公佈!</h3> + <h3>DEADLINE一到,我們便會公佈這一期所有的配對結果~</h3> + <CButton onClick={reFill}>修改表單</CButton> + </> + )} + </> + )} + </div> + ) +} +MatchResult.propTypes = { + jdata: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), + sdata: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), + + identity: PropTypes.string, + ended: PropTypes.bool, +} +export default MatchResult diff --git a/client/src/views/in/matching/Matching.js b/client/src/views/in/matching/Matching.js new file mode 100644 index 000000000..17a6bba56 --- /dev/null +++ b/client/src/views/in/matching/Matching.js @@ -0,0 +1,143 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useEffect } from 'react' +import { CNav, CNavItem, CNavLink, CTabContent, CTabPane } from '@coreui/react' +import axios from 'axios' +import OpenMatching from './OpenMatching' +import MatchResult from './MatchResult' +import Experience from './Experience' +import Spinner from '../../components/Spinner' + +const earlier = (t1, t2) => { + const time1 = Date.parse(t1) + const time2 = Date.parse(new Date(t2[0], Number(t2[1]) - 1, t2[2], t2[3], t2[4], 0, 0)) + return time1 < time2 +} + +const Matching = () => { + const [startTime, setStartTime] = useState() + const [endTime, setEndTime] = useState() + const [jdata, setJdata] = useState({}) + const [sdata, setSdata] = useState({}) + const [identity, setIdentity] = useState('') + const [loading, setLoading] = useState(true) + const [page, setPage] = useState(1) + const getTime = async () => { + axios.get('/api/time/getTime', { params: { target: 'matching_start' } }).then((res) => { + const [year, month, day, h_m] = res.data.split('-') + const [hour, min] = h_m.split(':') + setStartTime(() => [year, month, day, hour, min]) + }) + axios.get('/api/time/getTime', { params: { target: 'matching_end' } }).then((res) => { + const [year, month, day, h_m] = res.data.split('-') + const [hour, min] = h_m.split(':') + setEndTime(() => [year, month, day, hour, min]) + }) + } + const checkOpen = async () => { + axios + .get('/api/study/form') + .then((res) => { + setIdentity(res.data.identity) + if (res.data.identity === 'senior') setSdata(res.data) + else if (res.data.identity === 'junior') setJdata(res.data) + else setLoading(false) + }) + .catch( + (err) => err.response.data.description && alert('錯誤\n' + err.response.data.description), + ) + } + const checkResult = async () => { + axios + .get('/api/study/result') + .then((res) => { + if (identity === 'junior') { + setSdata(res.data) + } + if (identity === 'senior') { + setJdata(res.data) + } + setLoading(false) + }) + .catch((err) => { + console.log('Error while fetching match results') + }) + } + useEffect(() => { + getTime() + }, []) + + useEffect(() => { + if (!startTime || !endTime) return + checkOpen() + }, [startTime, endTime]) + + useEffect(() => { + if (!identity) return + checkResult() + }, [identity]) + return loading ? ( + <Spinner /> + ) : ( + <div className="matching mx-auto mt-5 w-75 pb-3"> + <CNav className="mb-4" variant="tabs" role="tablist" layout="justified"> + <CNavItem> + <CNavLink href="javascript:void(0);" active={page === 1} onClick={() => setPage(1)}> + 配對結果 + </CNavLink> + </CNavItem> + <CNavItem> + <CNavLink href="javascript:void(0);" active={page === 2} onClick={() => setPage(2)}> + 申請心得精華區 + </CNavLink> + </CNavItem> + </CNav> + <CTabContent> + <CTabPane role="tabpanel" aria-labelledby="home-tab" visible={page === 1}> + {earlier(Date(), startTime) ? ( + <div> + <h2>抱歉,配對表單填寫尚未開始</h2> + <h3> + 本期表單開放時間: {startTime[0]}/{startTime[1]}/{startTime[2]} {startTime[3]}: + {startTime[4]} ~ {endTime[0]}/{endTime[1]}/{endTime[2]} {endTime[3]}:{endTime[4]} + </h3> + </div> + ) : ( + <div className="d-flex align-items-center justify-content-around p-2"> + {identity ? ( + <MatchResult + jdata={jdata} + sdata={sdata} + identity={identity} + ended={!earlier(Date(), endTime)} + /> + ) : earlier(Date(), endTime) ? ( + <div className="d-flex flex-column"> + <h2> + 本期表單開放時間: {startTime[0]}/{startTime[1]}/{startTime[2]} {startTime[3]}: + {startTime[4]} ~ {endTime[0]}/{endTime[1]}/{endTime[2]} {endTime[3]}: + {endTime[4]} + </h2> + <OpenMatching /> + </div> + ) : ( + <div> + <h2>抱歉,配對表單填寫已經結束</h2> + <h3> + 本期表單開放時間: {startTime[0]}/{startTime[1]}/{startTime[2]} {startTime[3]}: + {startTime[4]} ~ {endTime[0]}/{endTime[1]}/{endTime[2]} {endTime[3]}: + {endTime[4]} + </h3> + </div> + )} + </div> + )} + </CTabPane> + <CTabPane role="tabpanel" aria-labelledby="contact-tab" visible={page === 2}> + <Experience /> + </CTabPane> + </CTabContent> + </div> + ) +} + +export default Matching diff --git a/client/src/views/in/matching/OpenMatching.js b/client/src/views/in/matching/OpenMatching.js new file mode 100644 index 000000000..11a825905 --- /dev/null +++ b/client/src/views/in/matching/OpenMatching.js @@ -0,0 +1,56 @@ +/* eslint-disable prettier/prettier */ +import React, { useState } from 'react' +import { useHistory } from 'react-router' +import { CFormCheck, CButton } from '@coreui/react' + +const OpenMatching = () => { + const history = useHistory() + const [identity, setIdentity] = useState('') + const handleSubmit = (e) => { + e.preventDefault() + if (!identity) { + alert('請選擇身分') + return + } + history.push(`/match_form/${identity}`) + } + return ( + <form onSubmit={handleSubmit} className="mt-3 p-3"> + <h2> + 歡迎使用留學交流(原EE Chain)功能! + <br /> + 您是第一次開通,所以請選擇您的身份 + </h2> + <br /> + <div className="d-flex"> + <div className="col-6 h4 px-3"> + <CFormCheck + type="radio" + name="identity" + value="senior" + id="senior" + label="我是學長姊" + className="my-1 d-flex justify-content-center align-items-center" + onChange={(e) => setIdentity(e.target.value)} + /> + 已有申請學校或在國外工作等等的經驗,願意回來幫助學弟妹出國、與其他已畢業學長姐交流。 + </div> + <div className="col-6 h4 px-3 left-part"> + <CFormCheck + type="radio" + name="identity" + value="junior" + id="junior" + label="我是學弟妹" + className="my-1 d-flex justify-content-center align-items-center" + onChange={(e) => setIdentity(e.target.value)} + /> + 想申請出國希望能多得到學長姊的經驗,未來出國後將再把經驗流傳給下一屆! + </div> + </div> + <br /> + <CButton onClick={handleSubmit}>下一步</CButton> + </form> + ) +} +export default OpenMatching diff --git a/client/src/views/in/matching/ResultBlock.js b/client/src/views/in/matching/ResultBlock.js new file mode 100644 index 000000000..73ae9f705 --- /dev/null +++ b/client/src/views/in/matching/ResultBlock.js @@ -0,0 +1,69 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import { CButton } from '@coreui/react' +import PropTypes from 'prop-types' +import { default_male } from './index' + +const ResultBlock = ({ data }) => { + return data.identity === 'senior' ? ( + <div className="d-flex align-items-center m-4 justify-content-around p-2 result-block"> + <div className="col-3 d-flex flex-column justify-content-center align-items-center"> + <CButton + color="info" + shape="rounded-pill" + style={{ width: 'fit-content', color: 'white' }} + disabled + > + 學長姐 + </CButton> + <br /> + <h1>{data.name}</h1> + </div> + <div className="col-3"> + {data.image === 'default' ? ( + <img src={default_male} alt="senior" className="img-fluid match-result-image" /> + ) : ( + <img src={data.image} alt="senior" className="img-fluid match-result-image" /> + )} + </div> + <div className="col-5 match-result-detail"> + 學校:{data.school} + <br /> + 研究領域:{data.major.join('、')} + <br /> + GPA @ NTUEE BS: {data.gpa} + <br /> + Mail: {data.email} + </div> + </div> + ) : ( + <div className="d-flex align-items-center m-4 justify-content-around p-2 result-block"> + <div className="col-3 d-flex flex-column justify-content-center align-items-center"> + <CButton color="primary" shape="rounded-pill" style={{ width: 'fit-content' }} disabled> + 學弟妹 + </CButton> + <br /> + <h1>{data.name}</h1> + </div> + <div className="col-3"> + {data.image === 'default' ? ( + <img src={default_male} alt="senior" className="img-fluid match-result-image" /> + ) : ( + <img src={data.image} alt="senior" className="img-fluid match-result-image" /> + )} + </div> + <div className="col-5 match-result-detail"> + 預計申請領域:{data.major.join('、')} + <br /> + GPA @ NTUEE BS: {data.gpa} + <br /> + Mail: {data.email} + </div> + </div> + ) +} + +ResultBlock.propTypes = { + data: PropTypes.object, +} +export default ResultBlock diff --git a/client/src/views/in/matching/index.js b/client/src/views/in/matching/index.js new file mode 100644 index 000000000..ddd755b95 --- /dev/null +++ b/client/src/views/in/matching/index.js @@ -0,0 +1,5 @@ +/* eslint-disable prettier/prettier */ +import Matching from './Matching' +import default_male from '../../../assets/images/default_male.png' +export default Matching +export { default_male } diff --git a/client/src/views/in/matching/matchForm/MatchForm.js b/client/src/views/in/matching/matchForm/MatchForm.js new file mode 100644 index 000000000..8ba6e6008 --- /dev/null +++ b/client/src/views/in/matching/matchForm/MatchForm.js @@ -0,0 +1,543 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useEffect } from 'react' +import { useSelector } from 'react-redux' +import { selectLogin } from '../../../../slices/loginSlice' +import { useParams, useHistory } from 'react-router-dom' +import axios from 'axios' +import { + CRow, + CCol, + CCard, + CCardBody, + CForm, + CInputGroup, + CInputGroupText, + CFormControl, + CButton, + CFormCheck, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' +import ReactTooltip from 'react-tooltip' +import PropTypes from 'prop-types' + +const strToArray = (str) => { + let ad = [str] + + if (str.includes(',')) { + ad = str.split(',') + } + if (str.includes(',')) { + ad = str.split(',') + } + ad = ad.map((a) => (a = a.trim())) + return ad +} +const MatchForm = () => { + const { identity } = useParams() + const history = useHistory() + const senior = identity === 'senior' ? true : false + const { email: userEmail, name: userName, studentID } = useSelector(selectLogin) + const [degrees, setDegrees] = useState([false, false, false]) + const [hasPapers, setHasPapers] = useState([false, false, false, false]) + + const formTemplate = senior + ? { + identity: 'senior', + name: userName, + email: userEmail, + major: [], + degree: '', + school: '', + gpa: '', + number: '', + admission: [], + } + : { + identity: 'junior', + name: userName, + email: userEmail, + studentID: studentID, + major: [], + degree: '', + gpa: '', + hasPaper: '', + school1: [], + school2: [], + school3: [], + } + const [requiredStyle, setRequiredStyle] = useState( + senior + ? { + name: '', + major: '', + email: '', + school: '', + number: '', + } + : { + name: '', + email: '', + major: '', + school1: '', + school2: '', + }, + ) + const [dataForm, setDataForm] = useState(formTemplate) + const handleInputChange = (e) => { + setDataForm({ ...dataForm, [e.target.name]: e.target.value }) + handleInputRadio(e) + if (requiredStyle.hasOwnProperty(e.target.name)) { + if (e.target.value === '') + setRequiredStyle({ ...requiredStyle, [e.target.name]: 'border-3 border-danger' }) + else setRequiredStyle({ ...requiredStyle, [e.target.name]: '' }) + } + } + const handleInputArray = (e) => { + const a = strToArray(e.target.value) + setDataForm({ ...dataForm, [e.target.name]: a }) + if (requiredStyle.hasOwnProperty(e.target.name)) { + if (e.target.value === '') + setRequiredStyle({ ...requiredStyle, [e.target.name]: 'border-3 border-danger' }) + else setRequiredStyle({ ...requiredStyle, [e.target.name]: '' }) + } + } + const handleInputRadio = (e) => { + if (e.target.name === 'hasPaper') { + switch (e.target.value) { + case '0': + setHasPapers([true, false, false, false]) + break + case '1': + setHasPapers([false, true, false, false]) + break + case '2': + setHasPapers([false, false, true, false]) + break + case '3': + setHasPapers([false, false, false, true]) + break + default: + break + } + } else if (e.target.name === 'degree') { + switch (e.target.value) { + case '0': + setDegrees([true, false, false]) + break + case '1': + setDegrees([false, true, false]) + break + case '2': + setDegrees([false, false, true]) + break + default: + break + } + } + } + const handleSubmit = (e) => { + e.preventDefault() + if (dataForm.gpa > 4.3 || isNaN(dataForm.gpa)) { + alert('please fill in correct gpa') + return + } + axios + .post('/api/study/fillForm', dataForm) + .then(() => { + alert('已送出') + history.push('/matching') + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + const handleBack = (e) => { + e.preventDefault() + history.push('/matching') + } + const getPrevForm = () => { + axios + .get('/api/study/form') + .then((res) => { + console.log('degree', res.data) + if (res.data.degree) { + setDataForm({ ...res.data, ['degree']: res.data.degree[0] }) + switch (res.data.degree[0]) { + case '0': + setDegrees([true, false, false]) + console.log('degree0') + break + case '1': + setDegrees([false, true, false]) + console.log('degree1') + break + case '2': + setDegrees([false, false, true]) + console.log('degree2') + break + + default: + break + } + } + if (res.data.hasPaper) { + switch (res.data.hasPaper) { + case '0': + setHasPapers([true, false, false, false]) + break + case '1': + setHasPapers([false, true, false, false]) + break + case '2': + setHasPapers([false, false, true, false]) + break + case '3': + setHasPapers([false, false, false, true]) + break + + default: + break + } + } + }) + .then(() => console.log('hp', dataForm.hasPaper)) + .catch( + (err) => err.response.data.description && alert('錯誤\n' + err.response.data.description), + ) + } + useEffect(() => { + getPrevForm() + }, [studentID]) + return ( + <div className="matching-form"> + <div className="d-flex flex-row align-items-center text-color-black"> + <CRow className="justify-content-center"> + <CCol md="11" lg="10" xl="9"> + <CCard className="mx-2"> + <CCardBody className="px-5"> + <button + className="align-self-baseline btn btn-ghost-info my-3" + onClick={handleBack} + > + <CIcon name="cil-arrow-left" size="lg" /> + </button> + <CForm> + <h2> + {senior ? '學長姐' : '學弟妹'}您好,請於2/1前填妥以下表單,我們才會幫您配對 + {senior ? '您的學弟妹' : '輔導您的學長姐'}喔! + </h2> + <p className="text-medium-emphasis">開通EEChain</p> + + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-layers" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.name} + data-for="name" + data-tip="Please fill in your name" + placeholder="Name*" + value={dataForm.name} + name="name" + onChange={handleInputChange} + /> + <ReactTooltip id="name" place="top" type="dark" effect="solid" /> + </CInputGroup> + {!senior && ( + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-dollar" /> + </CInputGroupText> + <CFormControl + data-for="studentID" + data-tip="Please fill in your student number" + placeholder="Student number" + name="studentID" + value={dataForm.studentID} + onChange={handleInputChange} + /> + <ReactTooltip id="studentID" place="top" type="dark" effect="solid" /> + </CInputGroup> + )} + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-building" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.email} + data-for="email" + data-tip="Please fill in your email" + placeholder="Email*" + value={dataForm.email} + name="email" + onChange={handleInputChange} + /> + <ReactTooltip id="email" place="top" type="dark" effect="solid" /> + </CInputGroup> + <h5 className="text-medium-emphasis"> + 填入您{senior ? '現在研究的領域' : '想申請的領域(若有多個請用 , 分開)'} + </h5> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-braille" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.major} + data-for="major" + data-tip="What subjects or fields are you majar?" + placeholder="Major" + value={dataForm.major} + name="major" + onChange={senior ? handleInputChange : handleInputArray} + /> + </CInputGroup> + <h5 className="text-medium-emphasis"> + {senior ? '入學時持有的最高學位' : '想申請的學位'} + </h5> + <CInputGroup className="mb-4"> + <div className="d-flex justify-content-around"> + <div style={{ 'margin-right': '1.2rem' }}> + <CFormCheck + type="radio" + name="degree" + value="0" + label="MS" + id="degree0" + onChange={handleInputChange} + checked={degrees[0]} + /> + </div> + <div style={{ 'margin-right': '1.2rem' }}> + <CFormCheck + type="radio" + name="degree" + value="1" + label="PhD" + id="degree1" + onChange={handleInputChange} + checked={degrees[1]} + /> + </div> + {senior ? null : ( + <div> + <CFormCheck + type="radio" + name="degree" + value="2" + label="Both" + id="degree2" + onChange={handleInputChange} + checked={degrees[2]} + /> + </div> + )} + </div> + </CInputGroup> + <h5 className="text-medium-emphasis">請填入之前的在校GPA</h5> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-education" /> + </CInputGroupText> + <CFormControl + data-for="gpa" + data-tip="GPA(in 4.3 scale)" + placeholder="gpa" + value={dataForm.gpa} + name="gpa" + onChange={handleInputChange} + /> + <ReactTooltip id="gpa" place="top" type="dark" effect="solid" /> + </CInputGroup> + {senior ? ( + <> + <h5 className="text-medium-emphasis">請填入您現在就讀的學校</h5> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-dollar" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.school} + data-for="school" + data-tip="your current school" + placeholder="School" + name="school" + value={dataForm.school} + onChange={handleInputChange} + /> + <ReactTooltip id="school" place="top" type="dark" effect="solid" /> + </CInputGroup> + <h5 className="text-medium-emphasis">請問您想接收幾位學弟妹呢?</h5> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-education" /> + </CInputGroupText> + <CFormControl + data-for="number" + data-tip="Number of juniors you'd like to receive" + placeholder="number" + value={dataForm.number} + name="number" + onChange={handleInputChange} + /> + <ReactTooltip id="number" place="top" type="dark" effect="solid" /> + </CInputGroup> + <h5 className="text-medium-emphasis"> + 填入您之前有錄取的學校(若有多個請用 , 分開) + </h5> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-braille" /> + </CInputGroupText> + <CFormControl + data-for="admission" + data-tip="What schools do you get admitted?" + placeholder="Admissions" + value={dataForm.admission} + name="admission" + onChange={handleInputArray} + /> + <ReactTooltip id="admission" place="top" type="dark" effect="solid" /> + </CInputGroup> + </> + ) : ( + <> + <h5 className="text-medium-emphasis">發論文的經驗</h5> + <div className="d-flex justify-content-around"> + <CInputGroup className="mb-4"> + <div className="col-3"> + <CFormCheck + type="radio" + name="hasPaper" + value="0" + label="無論文經驗" + id="hp0" + onChange={handleInputChange} + checked={hasPapers[0]} + /> + </div> + <div className="col-3"> + <CFormCheck + type="radio" + name="hasPaper" + value="1" + label="已投稿但尚未公佈" + id="hp1" + onChange={handleInputChange} + checked={hasPapers[1]} + /> + </div> + <div className="col-3"> + <CFormCheck + type="radio" + name="hasPaper" + value="2" + label="已發表 1 篇" + id="hp2" + onChange={handleInputChange} + checked={hasPapers[2]} + /> + </div> + <div className="col-3"> + <CFormCheck + type="radio" + name="hasPaper" + value="3" + id="hp3" + label="已發表 2 篇以上" + onChange={handleInputChange} + checked={hasPapers[3]} + /> + </div> + </CInputGroup> + </div> + <h5 className="text-medium-emphasis">你的夢想學校(若有多個請用 , 分開)</h5> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-dollar" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.school1} + data-for="school1" + data-tip="Please fill in your dream schools" + placeholder="Dream college" + name="school1" + value={dataForm.school1} + onChange={handleInputArray} + /> + <ReactTooltip id="school1" place="top" type="dark" effect="solid" /> + </CInputGroup> + <h5 className="text-medium-emphasis">稍有把握的學校(若有多個請用 , 分開)</h5> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-dollar" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.school2} + data-for="school2" + data-tip="Please fill in your confident colleges" + placeholder="Confident colleges" + name="school2" + value={dataForm.school2} + onChange={handleInputArray} + /> + <ReactTooltip id="school2" place="top" type="dark" effect="solid" /> + </CInputGroup> + <h5 className="text-medium-emphasis">你的保底學校(若有多個請用 , 分開)</h5> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-dollar" /> + </CInputGroupText> + <CFormControl + data-for="school3" + data-tip="Please fill your guaranteed colleges" + placeholder="Guaranteed colleges" + name="school3" + value={dataForm.school3} + onChange={handleInputArray} + /> + <ReactTooltip id="school3" place="top" type="dark" effect="solid" /> + </CInputGroup> + </> + )} + <CRow className="justify-content-center mt-3"> + <div className="d-flex d-flex justify-content-center"> + <CButton + color="dark" + onClick={(e) => { + let miss = [] + for (let info in requiredStyle) { + if (!dataForm[info]) { + miss.push(info) + } + } + if (miss.length !== 0) { + let missStyle = requiredStyle + for (let m of miss) { + missStyle[m] = 'border-3 border-danger' + } + alert(`You have to fill out ${miss}`) + setRequiredStyle({ ...requiredStyle, ...missStyle }) + return + } + handleSubmit(e) + }} + > + 確認送出 + </CButton> + </div> + </CRow> + </CForm> + </CCardBody> + </CCard> + </CCol> + </CRow> + </div> + </div> + ) +} +MatchForm.propTypes = { + identity: PropTypes.string, + setIdentity: PropTypes.func, + setOpened: PropTypes.func, +} +export default MatchForm diff --git a/client/src/views/in/matching/matchForm/index.js b/client/src/views/in/matching/matchForm/index.js new file mode 100644 index 000000000..929b46cb5 --- /dev/null +++ b/client/src/views/in/matching/matchForm/index.js @@ -0,0 +1,3 @@ +/* eslint-disable prettier/prettier */ +import MatchForm from './MatchForm' +export default MatchForm diff --git a/client/src/views/in/profile/Profile.js b/client/src/views/in/profile/Profile.js new file mode 100644 index 000000000..8f7261240 --- /dev/null +++ b/client/src/views/in/profile/Profile.js @@ -0,0 +1,243 @@ +import React, { useState, useEffect } from 'react' +import { useSelector } from 'react-redux' +import { useParams } from 'react-router' +import { selectLogin } from '../../../slices/loginSlice' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CRow, + CListGroup, + CListGroupItem, + CAvatar, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' +import { Link } from 'react-router-dom' +import axios from 'axios' +import default_male from '../../../assets/images/default_male.png' + +const Profile = () => { + const id = useParams().id + const { studentID } = useSelector(selectLogin) + const getProfile = () => { + axios + .post('api/searchProfile', { account: id }) + .then((res) => { + setData(res.data[0]) + }) + .catch((err) => { + switch (err.response.status) { + case 404: + alert(err.response.data.description) + break + default: + alert(err.response.data.description) + break + } + }) + } + const [data, setData] = useState(null) + + useEffect(() => { + getProfile() + }, []) + + return data ? ( + <CContainer> + <CRow> + <CCol md="4" className="mb-3"> + <CCard> + <CCardBody> + <div className="d-flex flex-column align-items-center text-center"> + <img + src={data.userimage === '' ? default_male : data.userimage} + alt="Admin" + className="rounded-circle" + width="150" + /> + <div className="mt-3"> + <h4>{data.username}</h4> + <p className="text-secondary mb-1">{data.profile}</p> + <p className="text-muted font-size-sm">{data.CC}</p> + </div> + </div> + </CCardBody> + </CCard> + <CCard className="mt-3"> + <CListGroup> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="website" name="website"></CIcon> + </CAvatar> + Website + </h6> + <span className="text-secondary">{data.web}</span> + </CListGroupItem> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="cib-github" name="cib-github"></CIcon> + </CAvatar> + Github + </h6> + <span className="text-secondary">{data.github}</span> + </CListGroupItem> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="cib-linkedin" name="cib-linkedin"></CIcon> + </CAvatar> + Linkedin + </h6> + <span className="text-secondary">{data.Linkedin}</span> + </CListGroupItem> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="cib-facebook" name="cib-facebook"></CIcon> + </CAvatar> + Facebook + </h6> + <span className="text-secondary">{data.facebook}</span> + </CListGroupItem> + </CListGroup> + </CCard> + </CCol> + <CCol md="8"> + <CCard className="mb-3"> + <CCardBody> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Nick Name</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.nickname} + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Student ID</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.account} + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Email</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.publicEmail} + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Mobile</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.cellphone} + </CCol> + </CRow> + <hr /> + </CCardBody> + </CCard> + <CRow> + <CCol sm="6" className="mb-3"> + <CCard className="h-100"> + <CCardBody> + <h6 className="d-flex align-items-center mb-3"> + <i className="material-icons text-info mr-2">Education</i> + </h6> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Bachelor</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.major} + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Master</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.master} + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Doctor</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.doctor} + </CCol> + </CRow> + <hr /> + </CCardBody> + </CCard> + </CCol> + <CCol sm="6" className="mb-3"> + <CCard className="h-100"> + <CCardBody> + <h6 className="d-flex align-items-center mb-3"> + <i className="material-icons text-info mr-2">Current Occupation</i> + </h6> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Company</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.Occupation.length != 0 ? data.Occupation[0].C : ''} + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Division</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.Occupation.length != 0 ? data.Occupation[0].O : ''} + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Position</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + {data.Occupation.length != 0 ? data.Occupation[0].P : ''} + </CCol> + </CRow> + <hr /> + </CCardBody> + </CCard> + </CCol> + </CRow> + {studentID === id ? ( + <CRow> + <CCol col-sm-3> + <Link to="/edit_profile"> + <CButton size="lg" color="info"> + Edit + </CButton> + </Link> + </CCol> + </CRow> + ) : null} + </CCol> + </CRow> + </CContainer> + ) : null +} + +export default Profile diff --git a/client/src/views/in/profile/edit/EditProfile.js b/client/src/views/in/profile/edit/EditProfile.js new file mode 100644 index 000000000..5bc1356f2 --- /dev/null +++ b/client/src/views/in/profile/edit/EditProfile.js @@ -0,0 +1,348 @@ +import React, { useState, useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { openEditImageModal } from '../../../../slices/profileSlice' +import { selectLogin } from '../../../../slices/loginSlice' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CFormControl, + CInputGroup, + CRow, + CListGroup, + CListGroupItem, + CAvatar, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' +import { Link, useHistory } from 'react-router-dom' +import ProfileImageEditor from './ProfileImageEditor' +import axios from 'axios' + +const ProfileEdit = () => { + const dispatch = useDispatch() + const history = useHistory() + const { imgSrc, studentID } = useSelector(selectLogin) + const [data, setData] = useState(null) + const getProfile = () => { + axios + .get('api/profile') + .then((res) => { + setData({ ...data, ...res.data }) + }) + .catch((err) => { + console.log(err) + switch (err.response.status) { + case 404: + alert(err.response.data.description) + break + default: + alert(err.response.data.description) + break + } + }) + } + const handleSave = () => { + axios + .patch('api/profile', data) + .then((res) => { + alert(`completed`) + history.push(`/profile/${studentID}`) + }) + .catch((err) => { + console.log(err) + switch (err.response.status) { + case 404: + alert(err.response.data.description) + break + default: + alert(err.response.data.description) + break + } + }) + } + const handleOccupationChange = (index, key, value) => { + let list = [...data.Occupation] + let item = { ...list[index] } + item[key] = value + list[index] = item + setData({ ...data, Occupation: list }) + } + const inputStyle = { + border: '0px', + // outline: 'none', + } + useEffect(() => { + getProfile() + }, []) + return data ? ( + <> + <ProfileImageEditor /> + <CContainer> + <CRow> + <CCol md="4" className="mb-3"> + <CCard> + <CCardBody> + <CInputGroup className="d-flex flex-column align-items-center text-center"> + <img src={imgSrc} alt="Admin" className="rounded-circle" width="150" /> + <CButton + onClick={(e) => { + e.preventDefault() + dispatch(openEditImageModal()) + }} + > + Edit + </CButton> + + <div className="mt-3"> + <CFormControl + style={inputStyle} + value={data.username} + onChange={(e) => setData({ ...data, username: e.target.value })} + /> + <CFormControl + className="text-secondary mb-1" + style={inputStyle} + value={data.profile} + onChange={(e) => setData({ ...data, profile: e.target.value })} + /> + <CFormControl + className="text-muted font-size-sm" + style={inputStyle} + value={data.CC} + onChange={(e) => setData({ ...data, CC: e.target.value })} + /> + </div> + </CInputGroup> + </CCardBody> + </CCard> + <CCard className="mt-3"> + <CListGroup> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="website" name="website"></CIcon> + </CAvatar> + Website + </h6> + <CFormControl + className="text-secondary" + style={inputStyle} + value={data.web} + onChange={(e) => setData({ ...data, web: e.target.value })} + /> + </CListGroupItem> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="cib-github" name="cib-github"></CIcon> + </CAvatar> + Github + </h6> + <CFormControl + className="text-secondary" + style={inputStyle} + value={data.github} + onChange={(e) => setData({ ...data, github: e.target.value })} + /> + </CListGroupItem> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="cib-linkedin" name="cib-linkedin"></CIcon> + </CAvatar> + Linkedin + </h6> + <CFormControl + className="text-secondary" + style={inputStyle} + value={data.Linkedin} + onChange={(e) => setData({ ...data, Linkedin: e.target.value })} + /> + </CListGroupItem> + <CListGroupItem> + <h6 className="mb-0"> + <CAvatar> + <CIcon icon="cib-facebook" name="cib-facebook"></CIcon> + </CAvatar> + Facebook + </h6> + <CFormControl + className="text-secondary" + style={inputStyle} + value={data.facebook} + onChange={(e) => setData({ ...data, facebook: e.target.value })} + /> + </CListGroupItem> + </CListGroup> + </CCard> + </CCol> + <CCol md="8"> + <CCard className="mb-3"> + <CCardBody> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Nick Name</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.nickname} + onChange={(e) => setData({ ...data, nickname: e.target.value })} + /> + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Student ID</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl style={inputStyle} value={studentID} /> + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Email</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.publicEmail} + onChange={(e) => setData({ ...data, publicEmail: e.target.value })} + /> + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Mobile</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.cellphone} + onChange={(e) => setData({ ...data, cellphone: e.target.value })} + /> + </CCol> + </CRow> + <hr /> + </CCardBody> + </CCard> + <CRow> + <CCol sm="6" className="mb-3"> + <CCard className="h-100"> + <CCardBody> + <h6 className="d-flex align-items-center mb-3"> + <i className="material-icons text-info mr-2">Education</i> + </h6> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Bachelor</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.major} + onChange={(e) => setData({ ...data, major: e.target.value })} + /> + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Master</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.master} + onChange={(e) => setData({ ...data, master: e.target.value })} + /> + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Doctor</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.doctor} + onChange={(e) => setData({ ...data, doctor: e.target.value })} + /> + </CCol> + </CRow> + <hr /> + </CCardBody> + </CCard> + </CCol> + <CCol sm="6" className="mb-3"> + <CCard className="h-100"> + <CCardBody> + <h6 className="d-flex align-items-center mb-3"> + <i className="material-icons text-info mr-2">Current Occupation</i> + </h6> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Company</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.Occupation.length != 0 ? data.Occupation[0].C : ''} + onChange={(e) => handleOccupationChange(0, 'C', e.target.value)} + /> + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Division</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.Occupation.length != 0 ? data.Occupation[0].O : ''} + onChange={(e) => handleOccupationChange(0, 'O', e.target.value)} + /> + </CCol> + </CRow> + <hr /> + <CRow> + <CCol sm="3"> + <h6 className="mb-0">Position</h6> + </CCol> + <CCol sm="9" className="text-secondary"> + <CFormControl + style={inputStyle} + value={data.Occupation.length != 0 ? data.Occupation[0].P : ''} + onChange={(e) => handleOccupationChange(0, 'P', e.target.value)} + /> + </CCol> + </CRow> + <hr /> + </CCardBody> + </CCard> + </CCol> + </CRow> + <CRow> + <CCol col-sm-3> + <CButton size="lg" color="info" onClick={handleSave}> + Save + </CButton> + </CCol> + </CRow> + </CCol> + </CRow> + </CContainer> + </> + ) : null +} + +export default ProfileEdit diff --git a/client/src/views/in/profile/edit/ProfileImageEditor.js b/client/src/views/in/profile/edit/ProfileImageEditor.js new file mode 100644 index 000000000..4c5877a93 --- /dev/null +++ b/client/src/views/in/profile/edit/ProfileImageEditor.js @@ -0,0 +1,71 @@ +import React, { useState, useEffect, useRef } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { selectProfile, closeEditImageModal } from '../../../../slices/profileSlice' +import { selectLogin, setImgSrc } from '../../../../slices/loginSlice' +import ProfilePicture from '@dsalvagni/react-profile-picture' +import '@dsalvagni/react-profile-picture/dist/ProfilePicture.css' + +import { CModal, CModalHeader, CModalBody, CModalTitle, CModalFooter, CButton } from '@coreui/react' +import axios from 'axios' + +const ProfileImageEditor = () => { + const dispatch = useDispatch() + const { imgSrc } = useSelector(selectLogin) + const { editImage } = useSelector(selectProfile) + const profilePictureRef = useRef(null) + + const closeModal = () => { + dispatch(closeEditImageModal()) + } + + const handleSaveImage = (e) => { + e.preventDefault() + + let canvas = profilePictureRef.current.canvasRef.current + canvas.toBlob( + (blob) => { + let data = new FormData() + data.append('userimage', blob, '.png') + const config = { + headers: { + 'content-type': 'multipart/form-data', + }, + } + // send to backend + axios + .patch('api/profile', data, config) + .then((res) => { + dispatch(setImgSrc(profilePictureRef.current.getImageAsDataUrl())) + closeModal() + }) + .catch((err) => { + switch (err.response.status) { + default: + console.log(err.response) + break + } + }) + }, + 'image/png', + 1, + ) + } + + return ( + <CModal visible={editImage} onDismiss={closeModal}> + <CModalHeader onDismiss={closeModal}> + <CModalTitle>Edit Your Photo</CModalTitle> + </CModalHeader> + <CModalBody> + <ProfilePicture ref={profilePictureRef} frameFormat={'circle'} cropSize="150" /> + </CModalBody> + <CModalFooter> + <CButton color="dark" onClick={handleSaveImage}> + OK + </CButton> + </CModalFooter> + </CModal> + ) +} + +export default ProfileImageEditor diff --git a/client/src/views/in/profile/edit/index.js b/client/src/views/in/profile/edit/index.js new file mode 100644 index 000000000..4fd902350 --- /dev/null +++ b/client/src/views/in/profile/edit/index.js @@ -0,0 +1,2 @@ +import EditProfile from './EditProfile' +export default EditProfile diff --git a/client/src/views/in/profile/index.js b/client/src/views/in/profile/index.js new file mode 100644 index 000000000..de26e459a --- /dev/null +++ b/client/src/views/in/profile/index.js @@ -0,0 +1,2 @@ +import Profile from './Profile' +export default Profile diff --git a/client/src/views/in/profile/search/SearchProfile.js b/client/src/views/in/profile/search/SearchProfile.js new file mode 100644 index 000000000..5a13c690f --- /dev/null +++ b/client/src/views/in/profile/search/SearchProfile.js @@ -0,0 +1,78 @@ +import React from 'react' +import { useSelector } from 'react-redux' +import { selectSearch } from '../../../../slices/searchSlice' +import default_male from '../../../../assets/images/default_male.png' +import { CAvatar, CCallout, CContainer, CImage } from '@coreui/react' +import { makeStyles } from '@material-ui/core/styles' +import List from '@material-ui/core/List' +import ListItem from '@material-ui/core/ListItem' +import Divider from '@material-ui/core/Divider' +import ListItemText from '@material-ui/core/ListItemText' +import ListItemAvatar from '@material-ui/core/ListItemAvatar' +import Avatar from '@material-ui/core/Avatar' +import { Link } from 'react-router-dom' +import { no_result } from '.' + +const useStyles = makeStyles((theme) => ({ + root: { + backgroundColor: theme.palette.background.paper, + borderRadius: '10px', + }, + fontColor: { + color: 'black', + }, + large: { + width: theme.spacing(10), + height: theme.spacing(10), + }, + primary: { + paddingBottom: '1rem', + fontSize: '1.3rem', + }, + secondary: { + fontSize: '1rem', + }, +})) + +const SearchProfile = () => { + const classes = useStyles() + const { resultProfiles } = useSelector(selectSearch) + return resultProfiles.length === 0 ? ( + <> + <CContainer className="align-items-center"> + <CImage src={no_result} fluid /> + </CContainer> + </> + ) : ( + <CContainer className="align-items-center col-md-10 col-xl-8"> + <List className={classes.root}> + {resultProfiles.map((profile) => { + return ( + <Link to={`/profile/${profile.account}`}> + <ListItem alignItems="flex-start" className={classes.fontColor}> + <ListItemAvatar className="px-3"> + <Avatar + src={profile.userimage === '' ? default_male : profile.userimage} + className={classes.large} + /> + </ListItemAvatar> + <ListItemText + primary={profile.username} + secondary={profile.profile} + classes={{ + primary: classes.primary, + secondary: classes.secondary, + }} + /> + </ListItem> + + <Divider variant="inset" component="li" /> + </Link> + ) + })} + </List> + </CContainer> + ) +} + +export default SearchProfile diff --git a/client/src/views/in/profile/search/index.js b/client/src/views/in/profile/search/index.js new file mode 100644 index 000000000..654d705ff --- /dev/null +++ b/client/src/views/in/profile/search/index.js @@ -0,0 +1,4 @@ +import SearchProfile from './SearchProfile' +import no_result from '../../../../assets/images/no_result.png' +export default SearchProfile +export { no_result } diff --git a/client/src/views/in/recommendation/Recommendation.js b/client/src/views/in/recommendation/Recommendation.js new file mode 100644 index 000000000..b1ab886a6 --- /dev/null +++ b/client/src/views/in/recommendation/Recommendation.js @@ -0,0 +1,145 @@ +import React, { useState, useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import axios from 'axios' +import { selectCareer, setKeywords, clearKeywords } from '../career/index' +import CareerBlock from '../career/CareerBlock' +import Masonry from 'react-masonry-css' +import { Spinner } from './index' +import { CButton, CFormControl, CInputGroup } from '@coreui/react' +import Pagination from '@material-ui/lab/Pagination' +import CIcon from '@coreui/icons-react' + +const Recommendation = () => { + const [data, setData] = useState({ data: [], maxPage: 1 }) + const dispatch = useDispatch() + const { keywords } = useSelector(selectCareer) + const [isPending, setIsPending] = useState() + const [isSearch, setIsSearch] = useState(false) + const [page, setPage] = useState(1) + const postsPerPage = 9 + const breakpointColumnsObj = { + default: 3, + 1100: 2, + 500: 1, + } + const searchData = (e) => { + e.preventDefault() + setIsPending(true) + setIsSearch(true) + axios + .post('/api/smartsearchrecommendation', { keyword: keywords, perpage: 99 }) + .then((res) => { + setData(res.data) + setIsPending(false) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + const getData = () => { + setIsPending(true) + setIsSearch(false) + dispatch(clearKeywords()) + axios + .get('/api/recommendation', { params: { page, perpage: postsPerPage } }) + .then((res) => { + setData(res.data) + setIsPending(false) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + useEffect(() => { + getData() + }, [page]) + + return ( + <> + <div + className="d-flex flex-column justify-content-center align-items-center mb-4" + style={{ + backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://images.unsplash.com/photo-1558981852-426c6c22a060?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80')`, + backgroundSize: 'cover', + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + height: '500px', + color: 'white', + }} + > + <div className="display-1">Recommendations</div> + <form className="text-light py-2 my-2 w-75" onSubmit={(e) => searchData(e)}> + <CInputGroup> + <CButton + onClick={() => { + clearKeywords() + getData() + }} + color="light" + > + <CIcon icon="cil-home" name="cil-home" /> + </CButton> + <CFormControl + type="search" + placeholder={keywords === '' ? 'search for...' : keywords} + value={keywords} + onChange={(e) => { + dispatch(setKeywords(e.target.value)) + }} + ></CFormControl> + <CButton color="light" onClick={(e) => searchData(e)}> + <CIcon icon="cil-search" name="cil-search" /> + </CButton> + </CInputGroup> + </form> + </div> + <Pagination + className="my-4 d-flex justify-content-center" + count={data.maxPage} + defaultPage={page} + page={page} + color="secondary" + onChange={(e, val) => { + window.scrollTo(0, 0) + setPage(val) + }} + /> + {isPending ? ( + <Spinner /> + ) : isSearch && data.data.length === 0 ? ( + <div className="display-2 d-flex justify-content-center mt-3">Result not found</div> + ) : ( + <> + <Masonry + breakpointCols={breakpointColumnsObj} + className="my-masonry-grid" + columnClassName="my-masonry-grid_column" + columnAttrs={{ + className: 'should be overridden', + 'data-test': '', + style: { '--test': 'test', color: 'black' }, + }} + style={{ display: 'flex' }} + > + {data.data.map((post) => ( + <CareerBlock post={post} key={post._id} /> + ))} + </Masonry> + <Pagination + className="my-4 d-flex justify-content-center" + count={data.maxPage} + defaultPage={page} + page={page} + color="secondary" + onChange={(e, val) => { + window.scrollTo(0, 0) + setPage(val) + }} + /> + </> + )} + </> + ) +} + +export default Recommendation diff --git a/client/src/views/in/recommendation/RecommendationForm.js b/client/src/views/in/recommendation/RecommendationForm.js new file mode 100644 index 000000000..61ed82152 --- /dev/null +++ b/client/src/views/in/recommendation/RecommendationForm.js @@ -0,0 +1,448 @@ +/* eslint-disable prettier/prettier */ +import React, { useState } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { selectLogin } from '../../../slices/loginSlice' +import { selectCareer, clearCroppedDataUrl, clearCroppedFile } from '../../../slices/careerSlice' +import { useHistory } from 'react-router' +import CareerImageEditor from '../career/CareerImageEditor' +import ReactTooltip from 'react-tooltip' +import PropTypes from 'prop-types' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CRow, + CModal, + CModalHeader, + CModalBody, + CModalTitle, + CModalFooter, +} from '@coreui/react' +import axios from 'axios' +import CIcon from '@coreui/icons-react' +import CareerPreview from '../career/CareerPreview' +const RecommendationForm = ({ data }) => { + const add = data ? false : true + const { cellphone: userPhone, email: userEmail, name: userName } = useSelector(selectLogin) + const formTemplate = add + ? { + title: '', + name: userName, + desireWorkType: '', + contact: userPhone, + email: userEmail, + diploma: '', + file: '', + } + : { + title: data.title.title, + name: data.title.name, + desireWorkType: data.title.desire_work_type, + contact: data.info.contact, + email: data.info.email, + diploma: data.info.diploma, + file: data.image, + _id: data._id, + } + const dispatch = useDispatch() + const history = useHistory() + const { croppedFile } = useSelector(selectCareer) + const [isModal, setIsModal] = useState(false) + const [blockModal, setBlockModal] = useState(false) + const [originalImage, setOriginalImage] = useState(null) + const [experience, setExperience] = useState(add ? [''] : data.spec.experience) + const [speciality, setSpeciality] = useState(add ? [''] : data.spec.speciality) + const [fileButton, setFileButton] = useState(null) + const [dataForm, setDataForm] = useState(formTemplate) + const [requiredStyle, setRequiredStyle] = useState({ + title: '', + name: '', + desireWorkType: '', + }) + const handleInputChange = (e) => { + setDataForm({ ...dataForm, [e.target.name]: e.target.value }) + if (requiredStyle.hasOwnProperty(e.target.name)) { + if (e.target.value === '') + setRequiredStyle({ ...requiredStyle, [e.target.name]: 'border-3 border-danger' }) + else setRequiredStyle({ ...requiredStyle, [e.target.name]: '' }) + } + } + const addArray = (e) => { + if (e.target.name === 'experience') { + const newArray = experience.concat(['']) + setExperience(newArray) + } else if (e.target.name === 'speciality') { + const newArray = speciality.concat(['']) + setSpeciality(newArray) + } + } + const handleInputArray = (e, index) => { + if (e.target.name === 'experience') { + const newArray = experience.map((exp, idx) => { + if (idx !== index) return exp + else return e.target.value + }) + setExperience(newArray) + } else if (e.target.name === 'speciality') { + const newArray = speciality.map((req, idx) => { + if (idx !== index) return req + else return e.target.value + }) + setSpeciality(newArray) + } + } + const handleDeleteArray = (e, index) => { + if (e.target.name === 'experience') { + const newArray = experience.filter((exp, idx) => idx !== index) + setExperience(newArray) + } else if (e.target.name === 'speciality') { + const newArray = speciality.filter((spec, idx) => idx !== index) + setSpeciality(newArray) + } + } + const handleChangeImage = (e) => { + let reader = new FileReader() + let file = e.target.files[0] + setFileButton(e.target) + // clear old edit image + dispatch(clearCroppedDataUrl()) + dispatch(clearCroppedFile()) + reader.onloadend = () => { + setOriginalImage(reader.result) + } + reader.readAsDataURL(file) + // call the modal + setIsModal(true) + } + + const clearImage = (e) => { + // close modal + setIsModal(false) + // clear all the image + setOriginalImage(null) + dispatch(clearCroppedDataUrl()) + dispatch(clearCroppedFile()) + setDataForm({ ...dataForm, file: null }) + fileButton.value = '' + } + + const saveEditImage = (e) => { + // close the modal + setIsModal(false) + // fill the form + setDataForm({ ...dataForm, file: croppedFile }) + } + + const handleSubmit = () => { + const data = new FormData() + data.append('title', dataForm.title) + data.append('name', dataForm.name) + data.append('desire_work_type', dataForm.desireWorkType) + data.append('contact', dataForm.contact) + data.append('email', dataForm.email) + data.append('diploma', dataForm.diploma) + for (let exp of experience) { + data.append('experience[]', exp) + } + for (let spec of speciality) { + data.append('speciality[]', spec) + } + if (croppedFile) { + data.append('file', dataForm.file, '.png') + } + const config = { 'content-type': 'multipart/form-data' } + if (add) { + axios + .post('/api/recommendation', data, config) + .then(() => { + alert('已新增') + history.push('/own_recommendation') + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } else { + data.append('_id', dataForm._id) + axios + .patch('/api/recommendation', data, config) + .then(() => { + alert('已更新') + history.push('/own_recommendation') + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + } + return ( + <> + <CModal size="xl" visible={isModal} onDismiss={() => setIsModal(false)} alignment="center"> + <CModalHeader onDismiss={() => setIsModal(false)}> + <CModalTitle>Edit Your Photo</CModalTitle> + </CModalHeader> + <CModalBody> + <CareerImageEditor imgSrc={originalImage} /> + </CModalBody> + <CModalFooter> + <CButton color="warning" onClick={clearImage}> + Clear + </CButton> + <CButton color="dark" onClick={saveEditImage} disabled={!croppedFile}> + OK + </CButton> + </CModalFooter> + </CModal> + <CModal visible={blockModal} onDismiss={() => setBlockModal(false)} alignment="center"> + <CModalHeader onDismiss={() => setBlockModal(false)}> + <CModalTitle>Preview New Post</CModalTitle> + </CModalHeader> + <CModalBody> + <CareerPreview + post={dataForm} + experience={experience} + requirement={speciality} + description={[]} + /> + </CModalBody> + <CModalFooter> + <CButton color="warning" onClick={() => setBlockModal(false)}> + Back + </CButton> + <CButton color="dark" onClick={handleSubmit}> + Post + </CButton> + </CModalFooter> + </CModal> + <div className="d-flex flex-row align-items-center text-color-black"> + <CContainer> + <CRow className="justify-content-center"> + <CCol md="11" lg="9" xl="8"> + <CCard className="mx-4"> + <CCardBody className="p-4"> + <CForm> + <h1>{add ? 'Ready to post' : 'Want to edit'} a recommendation?</h1> + <p className="text-medium-emphasis"> + {add ? 'Create' : 'Edit'} your recommendation + </p> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-image" name="cil-image" /> + </CInputGroupText> + <CFormControl + data-for="image" + data-tip="Put a picture that can represent you!" + id="formFile" + type="file" + onChange={handleChangeImage} + onClick={(e) => (e.target.value = null)} + ></CFormControl> + <ReactTooltip id="image" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-user" name="cil-user" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.title} + data-for="title" + data-tip="Use impressing title to get people's attention!" + placeholder="Title*" + value={dataForm.title} + name="title" + onChange={handleInputChange} + /> + <ReactTooltip id="title" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-user" name="cil-user" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.name} + data-for="name" + data-tip="Enter your name" + placeholder="Name*" + value={dataForm.name} + name="name" + onChange={handleInputChange} + /> + <ReactTooltip id="name" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-braille" name="cil-braille" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.desireWorkType} + data-for="workType" + data-tip="What's your desired work?" + placeholder="Desired Work Type*" + value={dataForm.desireWorkType} + name="desireWorkType" + onChange={handleInputChange} + /> + <ReactTooltip id="workType" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-phone" name="cil-phone" /> + </CInputGroupText> + <CFormControl + data-for="phone" + data-tip="Let others can call you!" + placeholder="Phone" + value={dataForm.contact} + name="contact" + onChange={handleInputChange} + /> + <ReactTooltip id="phone" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-4"> + <CInputGroupText>@</CInputGroupText> + <CFormControl + data-for="mail" + data-tip="Let others can email you!" + placeholder="Email" + value={dataForm.email} + name="email" + onChange={handleInputChange} + /> + <ReactTooltip id="mail" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-education" name="cil-education" /> + </CInputGroupText> + <CFormControl + data-for="diploma" + data-tip="Enter your highest education level" + placeholder="Diploma" + value={dataForm.diploma} + name="diploma" + onChange={handleInputChange} + /> + <ReactTooltip id="diploma" place="top" type="dark" effect="solid" /> + </CInputGroup> + {experience.map((exp, index) => { + return ( + <CInputGroup className="mb-3" key={index}> + <CInputGroupText> + <CIcon icon="cil-address-book" name="cil-address-book" /> + </CInputGroupText> + <CFormControl + data-for="experience" + data-tip="Enter your experience" + placeholder="Experience" + name="experience" + value={exp} + onChange={(e) => handleInputArray(e, index)} + /> + <ReactTooltip id="experience" place="top" type="dark" effect="solid" /> + <CButton + type="button" + name="experience" + onClick={(e) => handleDeleteArray(e, index)} + > + x + </CButton> + </CInputGroup> + ) + })} + <CInputGroup className="mb-4 d-flex flex-row"> + <CInputGroupText> + <CIcon icon="cil-address-book" name="cil-address-book" /> + </CInputGroupText> + <CButton + type="button" + name="experience" + className="form-add" + onClick={addArray} + > + + + </CButton> + </CInputGroup> + {speciality.map((req, index) => { + return ( + <CInputGroup className="mb-3" key={index}> + <CInputGroupText> + <CIcon icon="cil-thumb-up" name="cil-thumb-up" /> + </CInputGroupText> + <CFormControl + data-for="specialty" + data-tip="Enter your strength or other specialty" + placeholder="Speciality" + name="speciality" + value={req} + onChange={(e) => handleInputArray(e, index)} + /> + <ReactTooltip id="specialty" place="top" type="dark" effect="solid" /> + <CButton + type="button" + name="speciality" + onClick={(e) => handleDeleteArray(e, index)} + > + x + </CButton> + </CInputGroup> + ) + })} + <CInputGroup className="mb-4 d-flex flex-row"> + <CInputGroupText> + <CIcon icon="cil-thumb-up" name="cil-thumb-up" /> + </CInputGroupText> + <CButton + type="button" + name="speciality" + className="form-add" + onClick={addArray} + > + + + </CButton> + </CInputGroup> + <CRow className="justify-content-center mt-3"> + <div className="d-flex d-flex justify-content-center"> + <CButton + color="dark" + onClick={() => { + let miss = [] + for (let info in requiredStyle) { + if (!dataForm[info]) { + miss.push(info) + } + } + if (miss.length !== 0) { + let missStyle = requiredStyle + for (let m of miss) { + missStyle[m] = 'border-3 border-danger' + } + alert(`You have to fill out ${miss}`) + setRequiredStyle({ ...requiredStyle, ...missStyle }) + return + } + setBlockModal(true) + }} + > + Preview + </CButton> + </div> + </CRow> + </CForm> + </CCardBody> + </CCard> + </CCol> + </CRow> + </CContainer> + </div> + </> + ) +} +RecommendationForm.propTypes = { + data: PropTypes.object, +} +export default RecommendationForm diff --git a/client/src/views/in/recommendation/add/index.js b/client/src/views/in/recommendation/add/index.js new file mode 100644 index 000000000..bf93c2fe1 --- /dev/null +++ b/client/src/views/in/recommendation/add/index.js @@ -0,0 +1,2 @@ +import RecommendationForm from '../RecommendationForm' +export default RecommendationForm diff --git a/client/src/views/in/recommendation/edit/EditRecommendation.js b/client/src/views/in/recommendation/edit/EditRecommendation.js new file mode 100644 index 000000000..62d1b9ff8 --- /dev/null +++ b/client/src/views/in/recommendation/edit/EditRecommendation.js @@ -0,0 +1,26 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useEffect } from 'react' +import { useParams } from 'react-router-dom' +import RecommendationForm from '../RecommendationForm' +import axios from 'axios' +const EditRecommendation = () => { + const id = useParams().id + const [data, setData] = useState([]) + const getData = () => { + axios + .get('/api/recommendation', { params: { _id: id } }) + .then((res) => { + setData(res.data.data[0]) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + useEffect(() => { + getData() + }, []) + // return <>{data._id && <EditBlock data={data} />}</> + return <>{data._id && <RecommendationForm data={data} />}</> +} + +export default EditRecommendation diff --git a/client/src/views/in/recommendation/edit/index.js b/client/src/views/in/recommendation/edit/index.js new file mode 100644 index 000000000..32a6c7c57 --- /dev/null +++ b/client/src/views/in/recommendation/edit/index.js @@ -0,0 +1,2 @@ +import EditRecommendation from './EditRecommendation' +export default EditRecommendation diff --git a/client/src/views/in/recommendation/index.js b/client/src/views/in/recommendation/index.js new file mode 100644 index 000000000..a886ad842 --- /dev/null +++ b/client/src/views/in/recommendation/index.js @@ -0,0 +1,4 @@ +import Recommendation from './Recommendation' +import Spinner from '../../components/Spinner' +export { Spinner } +export default Recommendation diff --git a/client/src/views/in/recommendation/own/OwnRecommendation.js b/client/src/views/in/recommendation/own/OwnRecommendation.js new file mode 100644 index 000000000..4066c5aed --- /dev/null +++ b/client/src/views/in/recommendation/own/OwnRecommendation.js @@ -0,0 +1,59 @@ +import React, { useState, useEffect } from 'react' +import CareerBlock from '../../career/CareerBlock' +import { Link } from 'react-router-dom' +import Masonry from 'react-masonry-css' +import axios from 'axios' +import { Spinner } from './index' +const OwnRecommendation = () => { + const [isPending, setIsPending] = useState(true) + const [data, setData] = useState([]) + const breakpointColumnsObj = { + default: 3, + 1100: 2, + 500: 1, + } + const getData = () => { + axios + .get('/api/recommendation/mine') + .then((res) => { + setData(res.data) + setIsPending(false) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + useEffect(() => { + getData() + }, []) + return ( + <div className="text-color-black"> + <Link to="/add_recommendation"> + <div className="d-flex justify-content-center add" width="100%"> + + + </div> + </Link> + {isPending ? ( + <Spinner /> + ) : ( + <Masonry + breakpointCols={breakpointColumnsObj} + className="my-masonry-grid" + columnClassName="my-masonry-grid_column" + columnAttrs={{ + className: 'should be overridden', + 'data-test': '', + style: { '--test': 'test' }, + }} + style={{ display: 'flex' }} + > + {data.map((post, i) => ( + <CareerBlock post={post} setData={setData} index={i} key={i} /> + ))} + </Masonry> + )} + </div> + ) +} + +export default OwnRecommendation diff --git a/client/src/views/in/recommendation/own/index.js b/client/src/views/in/recommendation/own/index.js new file mode 100644 index 000000000..11e05f346 --- /dev/null +++ b/client/src/views/in/recommendation/own/index.js @@ -0,0 +1,4 @@ +import OwnRecommendation from './OwnRecommendation' +import Spinner from '../../../components/Spinner' +export { Spinner } +export default OwnRecommendation diff --git a/client/src/views/in/recruitment/Recruitment.js b/client/src/views/in/recruitment/Recruitment.js new file mode 100644 index 000000000..41cca5aa5 --- /dev/null +++ b/client/src/views/in/recruitment/Recruitment.js @@ -0,0 +1,133 @@ +import React, { useState, useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import axios from 'axios' +import { selectCareer, setKeywords, clearKeywords } from '../career/index' +import CareerBlock from '../career/CareerBlock' +import Masonry from 'react-masonry-css' +import { Spinner } from './index' +import { CButton, CFormControl, CInputGroup } from '@coreui/react' +import CIcon from '@coreui/icons-react' +import Pagination from '@material-ui/lab/Pagination' +const Recruitment = () => { + const [data, setData] = useState({ data: [], maxPage: 0 }) + const dispatch = useDispatch() + const { keywords } = useSelector(selectCareer) + const [isPending, setIsPending] = useState() + const [isSearch, setIsSearch] = useState(false) + const [page, setPage] = useState(1) + const postsPerPage = 9 + const breakpointColumnsObj = { + default: 3, + 1100: 2, + 500: 1, + } + const searchData = (e) => { + e.preventDefault() + setIsPending(true) + setIsSearch(true) + axios + .post('/api/smartsearchRecruitment', { keyword: keywords, perpage: 99 }) + .then((res) => { + setData(res.data) + setIsPending(false) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + const getData = () => { + setIsPending(true) + setIsSearch(false) + dispatch(clearKeywords()) + axios + .post('/api/showRecruitment', { page, perpage: postsPerPage }) + .then((res) => { + if (res.data.length !== 0) { + setData(res.data) + setIsPending(false) + } + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + useEffect(() => { + getData() + }, [page]) + + return ( + <> + <div + className="d-flex flex-column justify-content-center align-items-center mb-4" + style={{ + backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://images.unsplash.com/photo-1558981852-426c6c22a060?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80')`, + backgroundSize: 'cover', + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + height: '500px', + color: 'white', + }} + > + <div className="display-1">Recruitments</div> + <form className="text-light py-2 my-2 w-75" onSubmit={(e) => searchData(e)}> + <CInputGroup> + <CButton + onClick={() => { + clearKeywords() + getData() + }} + color="light" + > + <CIcon name="cil-home" /> + </CButton> + <CFormControl + type="search" + placeholder={keywords === '' ? 'search for...' : keywords} + value={keywords} + onChange={(e) => { + dispatch(setKeywords(e.target.value)) + }} + ></CFormControl> + <CButton onClick={(e) => searchData(e)} color="light"> + <CIcon name="cil-search" /> + </CButton> + </CInputGroup> + </form> + </div> + <Pagination + className="my-4 d-flex justify-content-center" + count={data.maxPage} + defaultPage={page} + page={page} + color="secondary" + onChange={(e, val) => { + window.scrollTo(0, 0) + setPage(val) + }} + /> + {isPending ? ( + <Spinner /> + ) : isSearch && data.data.length === 0 ? ( + <div className="display-2 d-flex justify-content-center mt-3">Result not found</div> + ) : ( + <Masonry + breakpointCols={breakpointColumnsObj} + className="my-masonry-grid" + columnClassName="my-masonry-grid_column" + columnAttrs={{ + className: 'should be overridden', + 'data-test': '', + style: { '--test': 'test', color: 'black' }, + }} + style={{ display: 'flex' }} + > + {data.data.map((post) => ( + <CareerBlock post={post} key={post._id} /> + ))} + </Masonry> + )} + </> + ) +} + +export default Recruitment diff --git a/client/src/views/in/recruitment/RecruitmentForm.js b/client/src/views/in/recruitment/RecruitmentForm.js new file mode 100644 index 000000000..c4f860ee9 --- /dev/null +++ b/client/src/views/in/recruitment/RecruitmentForm.js @@ -0,0 +1,448 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useRef } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { selectCareer, clearCroppedDataUrl, clearCroppedFile } from '../../../slices/careerSlice' +import { useHistory } from 'react-router' +import CareerImageEditor from '../career/CareerImageEditor' +import JoditEditor from 'jodit-react' +import ReactTooltip from 'react-tooltip' +import PropTypes from 'prop-types' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CRow, + CModal, + CModalHeader, + CModalBody, + CModalTitle, + CModalFooter, +} from '@coreui/react' +import axios from 'axios' +import CIcon from '@coreui/icons-react' +import CareerPreview from '../career/CareerPreview' +const CareerForm = ({ data }) => { + const add = data ? false : true + const formTemplate = add + ? { + title: '', + companyName: '', + workType: '', + salary: '', + diploma: '', + description: '', + file: '', + } + : { + title: data.title.title, + companyName: data.title.company_name, + workType: data.title.work_type, + salary: data.info.salary, + diploma: data.info.diploma, + description: data.spec.description, + file: data.image, + _id: data._id, + } + const dispatch = useDispatch() + const history = useHistory() + const editor = useRef(null) + const { croppedFile } = useSelector(selectCareer) + const [isModal, setIsModal] = useState(false) + const [blockModal, setBlockModal] = useState(false) + const [originalImage, setOriginalImage] = useState(null) + const [experience, setExperience] = useState(add ? [''] : data.info.experience) + const [requirement, setRequirement] = useState(add ? [''] : data.spec.requirement) + const [fileButton, setFileButton] = useState(null) + const [dataForm, setDataForm] = useState(formTemplate) + const [requiredStyle, setRequiredStyle] = useState({ + title: '', + }) + const config = { + readonly: false, // all options from https://xdsoft.net/jodit/doc/ + } + const handleInputChange = (e) => { + setDataForm({ ...dataForm, [e.target.name]: e.target.value }) + if (requiredStyle[e.target.name] !== undefined) { + if (e.target.value === '') + setRequiredStyle({ ...requiredStyle, [e.target.name]: 'border-3 border-danger' }) + else setRequiredStyle({ ...requiredStyle, [e.target.name]: '' }) + } + } + const addArray = (e) => { + if (e.target.name === 'experience') { + const newArray = experience.concat(['']) + setExperience(newArray) + } else if (e.target.name === 'requirement') { + const newArray = requirement.concat(['']) + setRequirement(newArray) + } + } + const handleInputArray = (e, index) => { + if (e.target.name === 'experience') { + const newArray = experience.map((exp, idx) => { + if (idx !== index) return exp + else return e.target.value + }) + setExperience(newArray) + } else if (e.target.name === 'requirement') { + const newArray = requirement.map((req, idx) => { + if (idx !== index) return req + else return e.target.value + }) + setRequirement(newArray) + } + } + const handleDeleteArray = (e, index) => { + if (e.target.name === 'experience') { + const newArray = experience.filter((exp, idx) => idx !== index) + setExperience(newArray) + } else if (e.target.name === 'requirement') { + const newArray = requirement.filter((req, idx) => idx !== index) + setRequirement(newArray) + } + } + const handleChangeImage = (e) => { + let reader = new FileReader() + let file = e.target.files[0] + setFileButton(e.target) + // clear old edit image + dispatch(clearCroppedDataUrl()) + dispatch(clearCroppedFile()) + reader.onloadend = () => { + setOriginalImage(reader.result) + } + reader.readAsDataURL(file) + // call the modal + setIsModal(true) + } + + const clearImage = (e) => { + // close modal + setIsModal(false) + // clear all the image + setOriginalImage(null) + dispatch(clearCroppedDataUrl()) + dispatch(clearCroppedFile()) + setDataForm({ ...dataForm, file: null }) + fileButton.value = '' + } + + const saveEditImage = (e) => { + // close the modal + setIsModal(false) + // fill the form + setDataForm({ ...dataForm, file: croppedFile }) + } + const handleSubmit = () => { + const data = new FormData() + data.append('title', dataForm.title) + data.append('company_name', dataForm.companyName) + data.append('work_type', dataForm.workType) + data.append('salary', dataForm.salary) + data.append('diploma', dataForm.diploma) + data.append('description', dataForm.description) + for (let exp of experience) { + data.append('experience[]', exp) + } + for (let req of requirement) { + data.append('requirement[]', req) + } + if (croppedFile) { + data.append('file', dataForm.file, '.png') + } + const config = { + headers: { 'content-type': 'multipart/form-data' }, + } + if (add) { + axios + .post('/api/addRecruitment', data, config) + .then(() => { + alert('已新增') + history.push('/own_recruitment') + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } else if (!add) { + data.append('_id', dataForm._id) + axios + .patch('/api/recruitment', data, config) + .then(() => { + alert('已更新') + history.push('/own_recruitment') + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + } + return ( + <> + <CModal size="xl" visible={isModal} onDismiss={() => setIsModal(false)} alignment="center"> + <CModalHeader onDismiss={() => setIsModal(false)}> + <CModalTitle>Edit Your Photo</CModalTitle> + </CModalHeader> + <CModalBody> + <CareerImageEditor imgSrc={originalImage} /> + </CModalBody> + <CModalFooter> + <CButton color="warning" onClick={clearImage}> + Clear + </CButton> + <CButton color="dark" onClick={saveEditImage} disabled={!croppedFile}> + OK + </CButton> + </CModalFooter> + </CModal> + <CModal visible={blockModal} onDismiss={() => setBlockModal(false)} alignment="center"> + <CModalHeader onDismiss={() => setBlockModal(false)}> + <CModalTitle>Preview New Post</CModalTitle> + </CModalHeader> + <CModalBody> + <CareerPreview post={dataForm} experience={experience} requirement={requirement} /> + </CModalBody> + <CModalFooter> + <CButton color="warning" onClick={() => setBlockModal(false)}> + Back + </CButton> + <CButton color="dark" onClick={handleSubmit}> + Post + </CButton> + </CModalFooter> + </CModal> + <div className="d-flex flex-row align-items-center text-color-black"> + <CContainer> + <CRow className="justify-content-center"> + <CCol md="11" lg="9" xl="8"> + <CCard className="mx-4"> + <CCardBody className="p-4"> + <CForm> + <h1>{add ? 'Ready to post' : 'Want to edit'} a recruitment?</h1> + <p className="text-medium-emphasis"> + {add ? 'Create' : 'Edit'} your recruitment + </p> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-image" name="cil-image" /> + </CInputGroupText> + <CFormControl + data-for="image" + data-tip="Put your company's brand!" + id="formFile" + type="file" + onChange={handleChangeImage} + onClick={(e) => (e.target.value = null)} + ></CFormControl> + <ReactTooltip id="image" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-layers" name="cil-layers" /> + </CInputGroupText> + <CFormControl + className={requiredStyle.title} + data-for="title" + data-tip="Use impressing title to get people's attention!" + placeholder="Title*" + value={dataForm.title} + name="title" + onChange={handleInputChange} + /> + <ReactTooltip id="title" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-building" name="cil-building" /> + </CInputGroupText> + <CFormControl + data-for="companyName" + data-tip="Enter your company's name" + placeholder="Company name" + value={dataForm.companyName} + name="companyName" + onChange={handleInputChange} + /> + <ReactTooltip id="companyName" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-braille" name="cil-braille" /> + </CInputGroupText> + <CFormControl + data-for="workType" + data-tip="The position you are recruiting" + placeholder="Work Type" + value={dataForm.workType} + name="workType" + onChange={handleInputChange} + /> + <ReactTooltip id="workType" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-dollar" name="cil-dollar" /> + </CInputGroupText> + <CFormControl + data-for="salary" + data-tip="Salary paid (/month or /year)" + placeholder="Salary" + name="salary" + value={dataForm.salary} + onChange={handleInputChange} + /> + <ReactTooltip id="salary" place="top" type="dark" effect="solid" /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-education" name="cil-education" /> + </CInputGroupText> + <CFormControl + data-for="diploma" + data-tip="Prefered education degree or major" + placeholder="Diploma" + value={dataForm.diploma} + name="diploma" + onChange={handleInputChange} + /> + <ReactTooltip id="diploma" place="top" type="dark" effect="solid" /> + </CInputGroup> + {experience.map((exp, index) => { + return ( + <CInputGroup className="mb-3" key={index}> + <CInputGroupText> + <CIcon icon="cil-address-book" name="cil-address-book" /> + </CInputGroupText> + <CFormControl + data-for="experience" + data-tip="Prefered experience" + placeholder="Required Experience" + name="experience" + value={exp} + onChange={(e) => handleInputArray(e, index)} + /> + <ReactTooltip id="experience" place="top" type="dark" effect="solid" /> + <CButton + type="button" + name="experience" + onClick={(e) => handleDeleteArray(e, index)} + > + x + </CButton> + </CInputGroup> + ) + })} + <CInputGroup className="mb-4 d-flex flex-row"> + <CInputGroupText> + <CIcon icon="cil-address-book" name="cil-address-book" /> + </CInputGroupText> + <CButton + type="button" + name="experience" + className="form-add" + onClick={addArray} + > + + + </CButton> + </CInputGroup> + {requirement.map((req, index) => { + return ( + <CInputGroup className="mb-3" key={index}> + <CInputGroupText> + <CIcon icon="cil-thumb-up" name="cil-thumb-up" /> + </CInputGroupText> + <CFormControl + data-for="requirement" + data-tip="Any requirement for this job" + placeholder="Required skills" + name="requirement" + value={req} + onChange={(e) => handleInputArray(e, index)} + /> + <ReactTooltip id="requirement" place="top" type="dark" effect="solid" /> + <CButton + type="button" + name="requirement" + onClick={(e) => handleDeleteArray(e, index)} + > + x + </CButton> + </CInputGroup> + ) + })} + <CInputGroup className="mb-4 d-flex flex-row"> + <CInputGroupText> + <CIcon icon="cil-thumb-up" name="cil-thumb-up" /> + </CInputGroupText> + <CButton + type="button" + name="requirement" + className="form-add" + onClick={addArray} + > + + + </CButton> + </CInputGroup> + <div + className="mb-3 mw-100" + data-for="description" + data-tip="Some description for this job" + > + <JoditEditor + name="description" + ref={editor} + value={dataForm.description} + config={config} + tabIndex={1} // tabIndex of textarea + onBlur={(newContent) => + setDataForm({ ...dataForm, description: newContent }) + } // preferred to use only this option to update the content for performance reasons + /> + <ReactTooltip id="description" place="top" type="dark" effect="solid" /> + </div> + <CRow className="justify-content-center mt-3"> + <div className="d-flex d-flex justify-content-center"> + <CButton + color="dark" + onClick={() => { + let miss = [] + for (let info in requiredStyle) { + if (!dataForm[info]) { + miss.push(info) + } + } + if (miss.length !== 0) { + let missStyle = requiredStyle + for (let m of miss) { + missStyle[m] = 'border-3 border-danger' + } + alert(`You have to fill out ${miss}`) + setRequiredStyle({ ...requiredStyle, ...missStyle }) + return + } + setBlockModal(true) + }} + > + Preview + </CButton> + </div> + </CRow> + </CForm> + </CCardBody> + </CCard> + </CCol> + </CRow> + </CContainer> + </div> + </> + ) +} +CareerForm.propTypes = { + data: PropTypes.object, +} +export default CareerForm diff --git a/client/src/views/in/recruitment/add/index.js b/client/src/views/in/recruitment/add/index.js new file mode 100644 index 000000000..2a54818f3 --- /dev/null +++ b/client/src/views/in/recruitment/add/index.js @@ -0,0 +1,2 @@ +import RecruitmentForm from '../RecruitmentForm' +export default RecruitmentForm diff --git a/client/src/views/in/recruitment/edit/EditRecruitment.js b/client/src/views/in/recruitment/edit/EditRecruitment.js new file mode 100644 index 000000000..54301e1e8 --- /dev/null +++ b/client/src/views/in/recruitment/edit/EditRecruitment.js @@ -0,0 +1,25 @@ +/* eslint-disable prettier/prettier */ +import React, { useState, useEffect } from 'react' +import { useParams } from 'react-router-dom' +import RecruitmentForm from '../RecruitmentForm' +import axios from 'axios' +const EditRecruitment = () => { + const id = useParams().id + const [data, setData] = useState([]) + const getData = () => { + axios + .post('/api/searchRecruitment', { _id: id }) + .then((res) => { + setData(res.data[0]) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + useEffect(() => { + getData() + }, []) + return <>{data._id && <RecruitmentForm data={data} />}</> +} + +export default EditRecruitment diff --git a/client/src/views/in/recruitment/edit/index.js b/client/src/views/in/recruitment/edit/index.js new file mode 100644 index 000000000..76f64c2d5 --- /dev/null +++ b/client/src/views/in/recruitment/edit/index.js @@ -0,0 +1,2 @@ +import EditRecruitment from './EditRecruitment' +export default EditRecruitment diff --git a/client/src/views/in/recruitment/index.js b/client/src/views/in/recruitment/index.js new file mode 100644 index 000000000..bac93dd49 --- /dev/null +++ b/client/src/views/in/recruitment/index.js @@ -0,0 +1,4 @@ +import Recruitment from './Recruitment' +import Spinner from '../../components/Spinner' +export { Spinner } +export default Recruitment diff --git a/client/src/views/in/recruitment/own/OwnRecruitment.js b/client/src/views/in/recruitment/own/OwnRecruitment.js new file mode 100644 index 000000000..ee1c8825c --- /dev/null +++ b/client/src/views/in/recruitment/own/OwnRecruitment.js @@ -0,0 +1,60 @@ +import React, { useState, useEffect } from 'react' +import { Link } from 'react-router-dom' +import Masonry from 'react-masonry-css' +import CareerBlock from '../../career/CareerBlock' +import axios from 'axios' +import { Spinner } from './index' + +const OwnRecruitment = () => { + const [isPending, setIsPending] = useState(true) + const [data, setData] = useState([]) + const breakpointColumnsObj = { + default: 3, + 1100: 2, + 500: 1, + } + const getData = () => { + axios + .get('/api/recruitment') + .then((res) => { + setData(res.data) + setIsPending(false) + }) + .catch((err) => { + err.response.data.description && alert('錯誤\n' + err.response.data.description) + }) + } + useEffect(() => { + getData() + }, []) + return ( + <div className="text-color-black"> + <Link to="/add_recruitment"> + <div className="d-flex justify-content-center add" width="100%"> + + + </div> + </Link> + {isPending ? ( + <Spinner /> + ) : ( + <Masonry + breakpointCols={breakpointColumnsObj} + className="my-masonry-grid" + columnClassName="my-masonry-grid_column" + columnAttrs={{ + className: 'should be overridden', + 'data-test': '', + style: { '--test': 'test' }, + }} + style={{ display: 'flex' }} + > + {data.map((post, i) => ( + <CareerBlock post={post} setData={setData} index={i} key={i} /> + ))} + </Masonry> + )} + </div> + ) +} + +export default OwnRecruitment diff --git a/client/src/views/in/recruitment/own/index.js b/client/src/views/in/recruitment/own/index.js new file mode 100644 index 000000000..634a00594 --- /dev/null +++ b/client/src/views/in/recruitment/own/index.js @@ -0,0 +1,4 @@ +import OwnRecruitment from './OwnRecruitment' +import Spinner from '../../../components/Spinner' +export { Spinner } +export default OwnRecruitment diff --git a/client/src/views/in/study/index.js b/client/src/views/in/study/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/views/out/agent.js b/client/src/views/out/agent.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/views/out/forget/Forget.js b/client/src/views/out/forget/Forget.js new file mode 100644 index 000000000..b4d5112a9 --- /dev/null +++ b/client/src/views/out/forget/Forget.js @@ -0,0 +1,79 @@ +import React, { useState } from 'react' +import axios from 'axios' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CRow, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' + +const ForgetFormTemplate = { + account: '', +} + +const Forget = () => { + const [forgetForm, setForgetForm] = useState(ForgetFormTemplate) + + const handleInputChange = (e) => { + setForgetForm({ ...forgetForm, [e.target.name]: e.target.value }) + } + + const handleSubmit = (e) => { + e.preventDefault() + // connect with backend + // and send email to user + axios + .post('/api/forget', forgetForm) + .then((res) => { + alert(`重設密碼信已寄出,請至${res.data.email}收信`) + }) + .catch((err) => { + alert(err.response.data.description) + }) + } + + return ( + <div className="min-vh-100 d-flex flex-row align-items-center"> + <CContainer> + <CRow className="justify-content-center"> + <CCol md="10" lg="8" xl="7"> + <CCard className="mx-4"> + <CCardBody className="p-4"> + <CForm> + <h1>Forget Password? Don{"'"}t Worry!</h1> + <p>We{"'"}ll help you!</p> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-education" name="cil-education" /> + </CInputGroupText> + <CFormControl + placeholder="Student ID" + name="account" + onChange={handleInputChange} + /> + </CInputGroup> + <CRow className="justify-content-center mt-3"> + <div className="d-flex justify-content-center"> + <CButton color="dark" block onClick={handleSubmit}> + Send Link to Your Mail + </CButton> + </div> + </CRow> + </CForm> + </CCardBody> + </CCard> + </CCol> + </CRow> + </CContainer> + </div> + ) +} + +export default Forget diff --git a/client/src/views/out/forget/index.js b/client/src/views/out/forget/index.js new file mode 100644 index 000000000..5c03b6280 --- /dev/null +++ b/client/src/views/out/forget/index.js @@ -0,0 +1,2 @@ +import Forget from './Forget' +export default Forget diff --git a/client/src/views/out/form.js b/client/src/views/out/form.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/views/out/home/Home.js b/client/src/views/out/home/Home.js new file mode 100644 index 000000000..e5b7dc91a --- /dev/null +++ b/client/src/views/out/home/Home.js @@ -0,0 +1,39 @@ +/* eslint-disable prettier/prettier */ +import { useEffect } from 'react' +import AOS from 'aos' +import React from 'react' +import About from './about' +import Contact from './contact' +import History from './history' +import Team from './team' +import Services from './services' +import Interviews from './interviews' +import Header from './header' + +const Home = () => { + useEffect(() => { + AOS.init({ + once: false, + disable: 'phone', + duration: 1000, + easing: 'ease-out-cubic', + }) + window.addEventListener('load', AOS.refresh) + }, []) + return ( + <div + className="landing" + style={{ color: 'white', justifyContent: 'center', alignItems: 'center' }} + > + <Header /> + <Services /> + <About /> + <Interviews /> + <History /> + <Team /> + <Contact /> + </div> + ) +} + +export default Home diff --git a/client/src/views/out/home/about/About.js b/client/src/views/out/home/about/About.js new file mode 100644 index 000000000..555d01a6a --- /dev/null +++ b/client/src/views/out/home/about/About.js @@ -0,0 +1,54 @@ +import React from 'react' +import { CContainer, CRow } from '@coreui/react' +import Welcome from '../../../../assets/images/Welcome.png' +const About = () => { + return ( + <div id="about" className="section"> + <CContainer data-aos="fade-up" data-aos-anchor-placement="bottom-bottom"> + <CRow> + <div className="col-xs-12 col-md-6"> + {' '} + <img src={Welcome} className="img-fluid" alt="" />{' '} + </div> + <div className="col-xs-12 col-md-6"> + <div className="about-text"> + <h2>About Us</h2> + <p> +    + 試著回想18歲的自己,未來的人脈往往成為促使我們選擇台大電機的原因,然而曾經我們引以為傲的人脈資產,現在卻如此凋零。 + 一瞥世界上知名大學,他們都擁有一個共通點:<b>人脈網絡</b>。 + <br /> +    + 哈佛大學的老爺爺願意為了甫錄取的學弟妹提點長談,史丹佛大學的前輩也不遺餘力提拔後進。相比之下,我們認為系上一直缺乏專屬平台 + <b>供系友建立緊密的網路</b> + ,遂使人脈日益薄弱。 + <br /> +    + 我們希望這個聯絡網能成為一個整合式的社群網路,讓NTUEErs聚在一起;秉持著恢復人脈網的精神,讓NTUEE能在世界上有更大的影響力;建立一個連結電機系的共同回憶,讓系友們有專屬的家! + </p> + {/* <h3>Why Choose Us?</h3> + <div className="list-style"> + <div className="col-lg-6 col-sm-6 col-xs-12"> + <ul> + {props.data + ? props.data.Why.map((d, i) => <li key={`${d}-${i}`}>{d}</li>) + : 'loading'} + </ul> + </div> + <div className="col-lg-6 col-sm-6 col-xs-12"> + <ul> + {props.data + ? props.data.Why2.map((d, i) => <li key={`${d}-${i}`}> {d}</li>) + : 'loading'} + </ul> + </div> + </div> */} + </div> + </div> + </CRow> + </CContainer> + </div> + ) +} + +export default About diff --git a/client/src/views/out/home/about/index.js b/client/src/views/out/home/about/index.js new file mode 100644 index 000000000..966a1eb11 --- /dev/null +++ b/client/src/views/out/home/about/index.js @@ -0,0 +1,2 @@ +import About from './About' +export default About diff --git a/client/src/views/out/home/contact/Contact.js b/client/src/views/out/home/contact/Contact.js new file mode 100644 index 000000000..f3904f3ec --- /dev/null +++ b/client/src/views/out/home/contact/Contact.js @@ -0,0 +1,186 @@ +/* eslint-disable prettier/prettier */ +import { CCol, CContainer, CRow } from '@coreui/react' +import React, { useState } from 'react' +import CIcon from '@coreui/icons-react' + +import axios from 'axios' + +const initialState = { + name: '', + email: '', + message: '', +} +const Contact = () => { + const [{ name, email, message }, setState] = useState(initialState) + + const handleChange = (e) => { + const { name, value } = e.target + setState((prevState) => ({ ...prevState, [name]: value })) + } + const clearState = () => setState({ ...initialState }) + + const handleSubmit = (e) => { + e.preventDefault() + + const url = + 'https://docs.google.com/forms/d/e/1FAIpQLSed-GXXBhqIRUBEX7-nMlwuQ3a22-Z51mtxVSlcyhWzG9TH2Q/formResponse' + + axios + .get(url, { + params: { + usp: 'pp_url', + 'entry.1670134810': name, + 'entry.302205267': email, + 'entry.307115258': message, + }, + }) + .then((res) => { + alert('感謝您的意見!') + clearState() + }) + } + return ( + <div> + <div id="contact" className="section"> + <CContainer data-aos="fade-up" data-aos-anchor-placement="top-bottom"> + <CRow> + <div className="col-md-8"> + <CRow> + <div className="section-title"> + <h2>Get In Touch</h2> + <p> + Please fill out the form below to send us an email and we will get back to you + as soon as possible. + </p> + </div> + <form name="sentMessage" validate onSubmit={handleSubmit}> + <CRow> + <div className="col-md-6"> + <div className="form-group"> + <input + type="text" + id="name" + name="name" + className="form-control" + placeholder="Name" + required + onChange={handleChange} + value={name} + /> + <p className="help-block text-danger"></p> + </div> + </div> + <div className="col-md-6"> + <div className="form-group"> + <input + type="email" + id="email" + name="email" + className="form-control" + placeholder="Email" + required + onChange={handleChange} + value={email} + /> + <p className="help-block text-danger"></p> + </div> + </div> + </CRow> + <div className="form-group"> + <textarea + name="message" + id="message" + className="form-control" + rows="4" + placeholder="Message" + required + onChange={handleChange} + value={message} + ></textarea> + <p className="help-block text-danger"></p> + </div> + <div id="success"></div> + <button type="submit" className="btn btn-custom btn-lg"> + Send Message + </button> + </form> + </CRow> + </div> + <div className="col-md-4 col-md-offset-1 contact-info"> + <div className="contact-item"> + <h3>Contact Info</h3> + <p> + <span> + <CIcon icon="cil-home" name="cil-home" /> Address + </span> + 台灣大學博理館 B1 系學會辦 + </p> + </div> + <div className="contact-item"> + <p> + <span> + <CIcon icon="cib-gmail" name="cib-gmail" /> Email + </span>{' '} + eeplus2020@gmail.com + </p> + </div> + <div className="contact-item"> + <p> + <span> + <CIcon icon="cil-dollar" name="cil-dollar" /> Support + </span>{' '} + 700-0001236-0553850(Taiwan) + </p> + </div> + </div> + <div className="col-md-12"> + <CRow style={{ color: 'white' }}> + <div className="social"> + <ul> + <li> + <a + href="https://www.facebook.com/groups/ntueeplus" + target="_blank" + rel="noreferrer" + > + <CIcon + icon="cib-facebook" + name="cib-facebook" + customClassName="social-icon" + /> + </a> + </li> + <li> + <a + href="https://www.instagram.com/ntueeplus/" + target="_blank" + rel="noreferrer" + > + <CIcon + icon="cib-instagram" + name="cib-instagram" + customClassName="social-icon" + /> + </a> + </li> + <li> + <a + href="https://github.com/NTUEE-PLUS/EndOfWeb/" + target="_blank" + rel="noreferrer" + > + <CIcon icon="cib-github" name="cib-github" customClassName="social-icon" /> + </a> + </li> + </ul> + </div> + </CRow> + </div> + </CRow> + </CContainer> + </div> + </div> + ) +} + +export default Contact diff --git a/client/src/views/out/home/contact/index.js b/client/src/views/out/home/contact/index.js new file mode 100644 index 000000000..2a3273981 --- /dev/null +++ b/client/src/views/out/home/contact/index.js @@ -0,0 +1,2 @@ +import Contact from './Contact' +export default Contact diff --git a/client/src/views/out/home/header/Header.js b/client/src/views/out/home/header/Header.js new file mode 100644 index 000000000..6d503a6b6 --- /dev/null +++ b/client/src/views/out/home/header/Header.js @@ -0,0 +1,34 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import { CButton, CContainer, CRow } from '@coreui/react' +import { NavHashLink } from 'react-router-hash-link' + +const Header = () => { + return ( + <header id="header" data-aos-anchor-placement="bottom-bottom"> + <div className="intro" data-aos="fade-up"> + <div className="overlay"> + <CContainer> + <CRow className="justify-content-center"> + <div className="col-md-8 col-md-offset-2 intro-text"> + <h1> + <span className="header-title">NTUEE+</span> + <span></span> + </h1> + <h2>系友們專屬的家!</h2> + {/* <a href="/home/#services" className="btn btn-custom btn-lg page-scroll"> + Learn More + </a>{' '} */} + <NavHashLink to="#services"> + <CButton className="btn-custom">Learn More</CButton> + </NavHashLink> + </div> + </CRow> + </CContainer> + </div> + </div> + </header> + ) +} + +export default Header diff --git a/client/src/views/out/home/header/index.js b/client/src/views/out/home/header/index.js new file mode 100644 index 000000000..75599b217 --- /dev/null +++ b/client/src/views/out/home/header/index.js @@ -0,0 +1,2 @@ +import Header from './Header' +export default Header diff --git a/client/src/views/out/home/history/History.js b/client/src/views/out/home/history/History.js new file mode 100644 index 000000000..602047097 --- /dev/null +++ b/client/src/views/out/home/history/History.js @@ -0,0 +1,21 @@ +import React, { useState, useEffect } from 'react' +import Timeline from './Timeline' +const History = () => { + const [data, setData] = useState([]) + const getData = () => { + fetch('historyData.json', { + headers: { + ContentType: 'application/json', + Accept: 'application/json', + }, + }) + .then((res) => res.json()) + .then((data) => setData(data)) + } + useEffect(() => { + getData() + }, []) + return <>{data.history && <Timeline data={data} />}</> +} + +export default History diff --git a/client/src/views/out/home/history/Timeline.js b/client/src/views/out/home/history/Timeline.js new file mode 100644 index 000000000..2a4d8138c --- /dev/null +++ b/client/src/views/out/home/history/Timeline.js @@ -0,0 +1,62 @@ +import React from 'react' +import { VerticalTimeline, VerticalTimelineElement } from 'react-vertical-timeline-component' +import history_icon from '../../../../assets/icons/history_icon.png' +import 'react-vertical-timeline-component/style.min.css' +import PropTypes from 'prop-types' +import { CRow } from '@coreui/react' + +const Timeline = ({ data }) => { + return ( + <div + className="d-flex flex-column jusitfy-contnet-center align-items-center section" + id="history" + > + <CRow className="section-title"> + <h2>History</h2> + </CRow> + <VerticalTimeline> + {data.history.map((year, i) => ( + <VerticalTimelineElement + key={i} + className="vertical-timeline-element--work" + contentStyle={{ background: 'rgb(33, 150, 243)', color: '#fff' }} + contentArrowStyle={{ borderRight: '7px solid rgb(33, 150, 243)' }} + date={year.grade} + iconStyle={{ background: 'rgb(33, 150, 243)', color: '#fff' }} + icon={<img src={history_icon} alt="O" className="img-fluid" />} + > + <h3 className="vertical-timeline-element-title">{year.title}</h3> + <div className="row"> + {year.people.map((person) => { + return ( + <div + key={person.name} + className="col d-flex flex-column align-items-center justify-content-between mt-2" + > + <img + src={person.img} + alt="" + className="img-fluid" + style={{ + boxShadow: '3px 3px 12px gray', + padding: '2px', + borderRadius: '50%', + maxHeight: '10rem', + }} + /> + <h4 className="mt-2">{person.name}</h4> + </div> + ) + })} + </div> + </VerticalTimelineElement> + ))} + </VerticalTimeline> + </div> + ) +} + +export default Timeline +Timeline.propTypes = { + data: PropTypes.object, +} diff --git a/client/src/views/out/home/history/index.js b/client/src/views/out/home/history/index.js new file mode 100644 index 000000000..a35a23b05 --- /dev/null +++ b/client/src/views/out/home/history/index.js @@ -0,0 +1,4 @@ +import History from './History' +import history_icon from '../../../../assets/icons/history_icon.png' +export default History +export { history_icon } diff --git a/client/src/views/out/home/index.js b/client/src/views/out/home/index.js new file mode 100644 index 000000000..2d8cdd203 --- /dev/null +++ b/client/src/views/out/home/index.js @@ -0,0 +1,2 @@ +import Home from './Home' +export default Home diff --git a/client/src/views/out/home/interviews/Interviews.js b/client/src/views/out/home/interviews/Interviews.js new file mode 100644 index 000000000..1ba86bb85 --- /dev/null +++ b/client/src/views/out/home/interviews/Interviews.js @@ -0,0 +1,89 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import { CContainer } from '@coreui/react' +import { Link } from 'react-router-dom' +import columnOutline1999 from '../../../../assets/images/1999_column_outline.jpg' +import columnOutline2008 from '../../../../assets/images/2008_column_outline.jpg' +import columnOutline2012 from '../../../../assets/images/2012_column_outline.jpg' + +const Interviews = () => { + return ( + <div id="interviews" className="text-center section"> + <CContainer data-aos="fade-up" data-aos-anchor-placement="bottom-bottom"> + <div className="section-title"> + <h2>Interviews</h2> + <p>底下是我們節錄的一些採訪介紹,一起看看吧!</p> + </div> + <div className="d-flex flex-row"> + <div className="col-xs-12 col-sm-4"> + <div className="portfolio-item"> + <div className="hover-bg"> + {' '} + <Link + to="/interview/interview_1" + title="Project Title" + data-lightbox-gallery="gallery1" + > + <div className="hover-text"> + <h3>2012級 李昀樵 </h3> + <h4>17直播 技術副總</h4> + <p className="mx-3"> + 李昀樵曾是李琳山教授語音實驗室的研究生,研究所時期除了研究外,更有進行小型創業,開發出新聞摘要軟件及搜尋公共腳踏車的APP,前者以兩萬元售出,後者成為該領域當時市占率最高的APP。 + </p> + </div> + <img src={columnOutline2012} className="img-fluid rounded" alt="Project Title" />{' '} + </Link>{' '} + </div> + </div> + </div> + <div className="col-xs-12 col-sm-4"> + <div className="portfolio-item"> + <div className="hover-bg"> + {' '} + <Link + to="/interview/interview_2" + title="Project Title" + data-lightbox-gallery="gallery1" + > + <div className="hover-text"> + <h3>1999級 簡韶逸</h3> + <h4> (CEO/ Founder @ Ganzin Technology, Prof. @ NTUEE)</h4> + <p className="mx-3"> + 簡韶逸教授任職於台大電子所長達16年,致力於多媒體訊號處理系統、多媒體積體電路設計、晶片系統設計方法的研究。 + 多年來,「媒體晶片系統實驗室」不斷研發出優異的技術。 + </p> + </div> + <img src={columnOutline1999} className="img-fluid rounded" alt="Project Title" />{' '} + </Link>{' '} + </div> + </div> + </div> + <div className="col-xs-12 col-sm-4"> + <div className="portfolio-item"> + <div className="hover-bg"> + {' '} + <Link + to="/interview/interview_3" + title="Project Title" + data-lightbox-gallery="gallery1" + > + <div className="hover-text"> + <h3>2008級 鄭恆之</h3> + <h4> (Technical Lead Manager @ Google Brain)</h4> + <p className="mx-3"> + 目前任職於 Google Brain + 的團隊技術領導者和軟體主管工程師的鄭恆之,從事大規模機器學習的研究與軟體開發。 + </p> + </div> + <img src={columnOutline2008} className="img-fluid rounded" alt="Project Title" />{' '} + </Link>{' '} + </div> + </div> + </div> + </div> + </CContainer> + </div> + ) +} + +export default Interviews diff --git a/client/src/views/out/home/interviews/index.js b/client/src/views/out/home/interviews/index.js new file mode 100644 index 000000000..fac84490f --- /dev/null +++ b/client/src/views/out/home/interviews/index.js @@ -0,0 +1,2 @@ +import Interviews from './Interviews' +export default Interviews diff --git a/client/src/views/out/home/services/Services.js b/client/src/views/out/home/services/Services.js new file mode 100644 index 000000000..5a66290c9 --- /dev/null +++ b/client/src/views/out/home/services/Services.js @@ -0,0 +1,75 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import { CContainer, CRow, CImage } from '@coreui/react' +import studyabroad from '../../../../assets/images/studyabroad.png' +import column from '../../../../assets/images/column.png' +import communicate from '../../../../assets/images/communicate.png' +import career from '../../../../assets/images/career.png' + +const Services = () => { + return ( + <div id="services" className="text-center section"> + <CContainer + className="justify-content-center" + data-aos="fade-up" + data-aos-anchor-placement="bottom-bottom" + > + <CRow className="section-title"> + <h2>Our Services</h2> + </CRow> + <CRow> + <div className="col-xs-6 col-sm-3"> + {' '} + <div className="square-img-container"> + <CImage src={communicate} alt="..." className="img img-fluid"></CImage> + </div> + <h3>通訊錄</h3> + <p> + 建立完整系友聯繫網路 + <br /> + 加深NTUEErs之間的連結 + </p> + </div> + <div className="col-xs-6 col-sm-3"> + {' '} + <div className="square-img-container"> + <CImage src={career} alt="..." className="img img-fluid"></CImage> + </div> + <h3>徵才求職</h3> + <p> + 透過已建立之聯繫網絡分享工作資訊 + <br /> + 不怕錯失內推機會 + </p> + </div> + <div className="col-xs-6 col-sm-3"> + {' '} + <div className="square-img-container"> + <CImage src={studyabroad} alt="..." className="img img-fluid"></CImage> + </div> + <h3>留學資訊</h3> + <p> + 整理國外各校和本系歷年的留學資料 + <br /> + 提供在校學生參考 + </p> + </div> + <div className="col-xs-6 col-sm-3"> + {' '} + <div className="square-img-container"> + <CImage src={column} alt="..." className="img img-fluid"></CImage> + </div> + <h3>採訪文章</h3> + <p> + 收錄過去採訪眾多系友的紀錄 + <br /> + 分享獨一無二的經歷和體驗 + </p> + </div> + </CRow> + </CContainer> + </div> + ) +} + +export default Services diff --git a/client/src/views/out/home/services/index.js b/client/src/views/out/home/services/index.js new file mode 100644 index 000000000..7e18a84cc --- /dev/null +++ b/client/src/views/out/home/services/index.js @@ -0,0 +1,2 @@ +import Services from './Services' +export default Services diff --git a/client/src/views/out/home/team/Team.js b/client/src/views/out/home/team/Team.js new file mode 100644 index 000000000..57e3c4be9 --- /dev/null +++ b/client/src/views/out/home/team/Team.js @@ -0,0 +1,87 @@ +/* eslint-disable prettier/prettier */ +import React from 'react' +import { CContainer, CRow } from '@coreui/react' + +const Team = () => { + return ( + <div id="team" className="text-center section"> + <CContainer data-aos="fade-up" data-aos-anchor-placement="bottom-bottom"> + <CRow className="section-title"> + <h2>Meet the Team</h2> + </CRow> + <CRow> + <div className="col-sm-3 col-xs-6 team"> + <div className="thumbnail"> + {' '} + <img + src="https://i.imgur.com/eOWLfEO.png" + alt="..." + className="team-img img-fluid" + style={{ + borderRadius: '50%', + }} + /> + <div className="caption"> + <h4>卓昱辰</h4> + <p>留學採訪負責人</p> + </div> + </div> + </div> + <div className="col-sm-3 col-xs-6 team"> + <div className="thumbnail"> + {' '} + <img + src="https://i.imgur.com/y6P3fTw.png" + alt="..." + className="team-img img-fluid" + style={{ + borderRadius: '50%', + }} + /> + <div className="caption"> + <h4>王友廷</h4> + <p>網頁維護負責人</p> + </div> + </div> + </div> + <div className="col-sm-3 col-xs-6 team"> + <div className="thumbnail"> + {' '} + <img + src="https://i.imgur.com/VorzAuV.png" + alt="..." + className="team-img img-fluid" + style={{ + borderRadius: '50%', + }} + /> + <div className="caption"> + <h4>陳亮君</h4> + <p>網頁維護負責人</p> + </div> + </div> + </div> + <div className="col-sm-3 col-xs-6 team"> + <div className="thumbnail"> + {' '} + <img + src="https://i.imgur.com/TT53RQB.png" + alt="..." + className="team-img img-fluid" + style={{ + borderRadius: '50%', + }} + /> + <div className="caption"> + <h4>何明翰</h4> + <p>美術設計負責人</p> + </div> + </div> + </div> + </CRow> + </CContainer> + </div> + ) +} + +export default Team diff --git a/client/src/views/out/home/team/TeamBlocks.js b/client/src/views/out/home/team/TeamBlocks.js new file mode 100644 index 000000000..17be60f7d --- /dev/null +++ b/client/src/views/out/home/team/TeamBlocks.js @@ -0,0 +1,69 @@ +/* eslint-disable prettier/prettier */ +import { CCol, CImage, CRow } from '@coreui/react' +import React from 'react' +import PropTypes from 'prop-types' + +const TeamBlocks = ({ data }) => { + const minister = data.minister.map((person) => { + return ( + <CCol xs={2} key={person.name} align="center" className="justify-content-center"> + <div className="square-img-container"> + <CImage src={person.img} height="200rem" className="img img-fluid" /> + </div> + <h3 className="mb-4"> + {person.name} {person.part} + </h3> + </CCol> + ) + }) + const front = data.front.map((person) => { + return ( + <CCol xs={2} key={person.name} align="center" className="justify-content-between"> + <CImage src={person.img} height="200rem" className="img-fluid" /> + <h3 className="mb-4">{person.name}</h3> + </CCol> + ) + }) + const back = data.back.map((person) => { + return ( + <CCol xs={2} key={person.name} align="center" className="justify-content-center"> + <CImage src={person.img} height="200rem" className="img-fluid" /> + <h3 className="mb-4">{person.name}</h3> + </CCol> + ) + }) + const abroad = data.abroad.map((person) => { + return ( + <CCol xs={2} key={person.name} align="center" className="justify-content-center"> + <CImage src={person.img} height="200rem" className="img-fluid" /> + <h3 className="mb-4">{person.name}</h3> + </CCol> + ) + }) + return ( + <> + <CCol> + <CRow className="justify-content-around mt-4 mb-4" style={{ marginBottom: '20rem' }} xs={5}> + <h3 className="mb-4">負責人:</h3> + {minister} + </CRow> + <CRow className="justify-content-around mt-4 mb-4 " xs={5}> + <h3 className="mb-4">網頁前端團隊:</h3> + {front} + </CRow> + <CRow className="justify-content-around mt-4 mb-4 " xs={5}> + <h3 className="mb-4">網頁後端團隊:</h3> + {back} + </CRow> + <CRow className="justify-content-around mt-4 mb-4 " xs={5}> + <h3 className="mb-4">留學資料蒐集團隊:</h3> + {abroad} + </CRow> + </CCol> + </> + ) +} +TeamBlocks.propTypes = { + data: PropTypes.array, +} +export default TeamBlocks diff --git a/client/src/views/out/home/team/index.js b/client/src/views/out/home/team/index.js new file mode 100644 index 000000000..47ae24b4a --- /dev/null +++ b/client/src/views/out/home/team/index.js @@ -0,0 +1,2 @@ +import Team from './Team' +export default Team diff --git a/client/src/views/out/index.js b/client/src/views/out/index.js new file mode 100644 index 000000000..ad80971d0 --- /dev/null +++ b/client/src/views/out/index.js @@ -0,0 +1,11 @@ +import React from 'react' + +const Home = React.lazy(() => import('./home')) +const Login = React.lazy(() => import('./login')) +const RegisterEntry = React.lazy(() => import('./registerEntry')) +const Register = React.lazy(() => import('./register')) +const RegisterFB = React.lazy(() => import('./registerFB')) +const Forget = React.lazy(() => import('./forget')) +const ResetPassword = React.lazy(() => import('./resetPassword')) +const Policy = React.lazy(() => import('./policy')) +export { Home, Login, RegisterEntry, Register, RegisterFB, Forget, ResetPassword, Policy } diff --git a/client/src/views/out/login/Login.js b/client/src/views/out/login/Login.js new file mode 100644 index 000000000..d5ab4a886 --- /dev/null +++ b/client/src/views/out/login/Login.js @@ -0,0 +1,185 @@ +import React, { useState, useEffect } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { selectLogin, login } from '../../../slices/loginSlice' +import { Redirect, Link } from 'react-router-dom' +import FacebookLogin from 'react-facebook-login' +import axios from 'axios' +import { + CButton, + CCard, + CCardBody, + CCardGroup, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CRow, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' + +const LoginFormTemplate = { + account: '', + password: '', +} + +const Login = () => { + const dispatch = useDispatch() + const { isLogin } = useSelector(selectLogin) + const [loginForm, setLoginForm] = useState(LoginFormTemplate) + const [needRegister, setNeedRegister] = useState(false) + + const handleInputChange = (e) => { + setLoginForm({ ...loginForm, [e.target.name]: e.target.value }) + } + + const handleSubmit = (e) => { + e.preventDefault() + // connect with backend + // check if login + // if success then redirect inside + axios + .post('api/login', loginForm) + .then((res) => { + const { username, isAuth } = res.data + alert(`歡迎回來! ${username}`) + dispatch(login(isAuth)) + }) + .catch((err) => { + switch (err.response.status) { + case 404: + alert(err.response.data.description) + setNeedRegister(true) + break + default: + alert(err.response.data.description) + break + } + }) + } + + const handleFBSubmit = (res) => { + if (res.status == 'unknown') { + return + } + // call backend + // send res.userID + axios + .post('/api/loginFB', { facebookID: res.userID }) + .then((res) => { + const { username, isAuth } = res.data + alert(`歡迎回來! ${username}`) + dispatch(login(isAuth)) + }) + .catch((err) => { + switch (err.response.status) { + case 404: + alert(err.response.data.description) + setNeedRegister(true) + break + default: + alert(err.response.data.description) + break + } + }) + } + if (isLogin) { + return <Redirect to="/home"></Redirect> + } else if (needRegister) { + return <Redirect to="/register_entry"></Redirect> + } else { + return ( + <div className="min-vh-100 d-flex flex-row align-items-center"> + <CContainer className="align-items-center"> + <CRow className="justify-content-center"> + <CCol md="8"> + <CCardGroup> + <CCard className="p-4"> + <CCardBody> + <CForm + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault() + handleSubmit(e) + } + }} + > + <h1>Login</h1> + <p className="text-medium-emphasis">Sign In to your account</p> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-education" name="cil-education" /> + </CInputGroupText> + <CFormControl + placeholder="Student ID" + name="account" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup className="mb-2"> + <CInputGroupText> + <CIcon icon="cil-lock-locked" name="cil-lock-locked" /> + </CInputGroupText> + <CFormControl + type="password" + placeholder="Password" + name="password" + onChange={handleInputChange} + /> + </CInputGroup> + <CRow> + <CCol xs="6"> + <Link to="/register_entry" color="link" className="px-0"> + Create a new account? + </Link> + </CCol> + <CCol xs="6" className="d-flex justify-content-end"> + <Link to="/forget" color="link" className="px-0"> + Forgot password? + </Link> + </CCol> + </CRow> + <CRow className="mt-3"> + <CCol className="d-flex justify-content-center"> + <CButton color="dark" className="px-4" onClick={handleSubmit}> + Login + </CButton> + </CCol> + </CRow> + {/* <CRow className="justify-content-center"> + <div + className="w-75 text-center mt-3 pt-3" + style={{ borderTop: '1px solid gray' }} + > + or login with... + </div> + </CRow> + <CRow className="justify-content-center"> + <div style={{ width: '5rem' }}> + <FacebookLogin + appId={process.env.REACT_APP_fbAPIid || '571174603253755'} + autoLoad={false} + isMobile={false} + fields="name,email,picture" + callback={handleFBSubmit} + cssClass="btnFacebook d-flex justify-content-center mt-2" + icon="fa-facebook" + name="fa-facebook" + textButton="" + /> + </div> + </CRow> */} + </CForm> + </CCardBody> + </CCard> + </CCardGroup> + </CCol> + </CRow> + </CContainer> + </div> + ) + } +} + +export default Login diff --git a/client/src/views/out/login/index.js b/client/src/views/out/login/index.js new file mode 100644 index 000000000..8b449752e --- /dev/null +++ b/client/src/views/out/login/index.js @@ -0,0 +1,2 @@ +import Login from './Login' +export default Login diff --git a/client/src/views/out/policy/Policy.js b/client/src/views/out/policy/Policy.js new file mode 100644 index 000000000..067442999 --- /dev/null +++ b/client/src/views/out/policy/Policy.js @@ -0,0 +1,401 @@ +import React, { Component } from 'react' +import { CCard, CCardBody } from '@coreui/react' +class Policy extends Component { + render() { + return ( + <CCard> + <CCardBody> + <h1>Privacy Policy</h1> + + <p>Last updated: August 31, 2020</p> + + <p> + This Privacy Policy describes Our policies and procedures on the collection, use and + disclosure of Your information when You use the Service and tells You about Your privacy + rights and how the law protects You. + </p> + + <p> + We use Your Personal data to provide and improve the Service. By using the Service, You + agree to the collection and use of information in accordance with this Privacy Policy. + This Privacy Policy is maintained by the{' '} + <a href="https://www.privacypolicies.com/privacy-policy-generator/"> + Privacy Policy Generator + </a> + . + </p> + + <h1>Interpretation and Definitions</h1> + <h2>Interpretation</h2> + <p> + The words of which the initial letter is capitalized have meanings defined under the + following conditions. + </p> + <p> + The following definitions shall have the same meaning regardless of whether they appear + in singular or in plural. + </p> + + <h2>Definitions</h2> + <p>For the purposes of this Privacy Policy:</p> + <ul> + <li> + <p> + <strong>You</strong> means the individual accessing or using the Service, or the + company, or other legal entity on behalf of which such individual is accessing or + using the Service, as applicable. + </p> + </li> + <li> + <p> + <strong>Company</strong> (referred to as either "the Company", "We", "Us" or "Our" + in this Agreement) refers to NTUEE+. + </p> + </li> + <li> + <strong>Affiliate</strong> means an entity that controls, is controlled by or is under + common control with a party, where "control" means ownership of 50% or more of the + shares, equity interest or other securities entitled to vote for election of directors + or other managing authority. + </li> + <li> + <strong>Account</strong> means a unique account created for You to access our Service + or parts of our Service. + </li> + <li> + <strong>Website</strong> refers to NTUEE+, accessible from https://plus.ntuee.org/ + </li>{' '} + <li> + <strong>Service</strong> refers to the Website. + </li> + <li> + <strong>Country</strong> refers to: Taiwan + </li> + <li> + <p> + <strong>Service Provider</strong> means any natural or legal person who processes + the data on behalf of the Company. It refers to third-party companies or individuals + employed by the Company to facilitate the Service, to provide the Service on behalf + of the Company, to perform services related to the Service or to assist the Company + in analyzing how the Service is used. + </p> + </li> + <li> + <strong>Third-party Social Media Service</strong> refers to any website or any social + network website through which a User can log in or create an account to use the + Service. + </li> + <li> + <p> + <strong>Personal Data</strong> is any information that relates to an identified or + identifiable individual. + </p> + </li> + <li> + <strong>Cookies</strong> are small files that are placed on Your computer, mobile + device or any other device by a website, containing the details of Your browsing + history on that website among its many uses. + </li>{' '} + <li> + <strong>Device</strong> means any device that can access the Service such as a + computer, a cellphone or a digital tablet. + </li> + <li> + <strong>Usage Data</strong> refers to data collected automatically, either generated + by the use of the Service or from the Service infrastructure itself (for example, the + duration of a page visit). + </li> + </ul> + + <h1>Collecting and Using Your Personal Data</h1> + <h2>Types of Data Collected</h2> + + <h3>Personal Data</h3> + <p> + While using Our Service, We may ask You to provide Us with certain personally + identifiable information that can be used to contact or identify You. Personally + identifiable information may include, but is not limited to: + </p> + <ul> + <li>Email address</li> <li>First name and last name</li> <li>Usage Data</li> + </ul> + + <h3>Usage Data</h3> + <p>Usage Data is collected automatically when using the Service.</p> + <p> + Usage Data may include information such as Your Device's Internet Protocol address (e.g. + IP address), browser type, browser version, the pages of our Service that You visit, the + time and date of Your visit, the time spent on those pages, unique device identifiers + and other diagnostic data. + </p> + <p> + When You access the Service by or through a mobile device, We may collect certain + information automatically, including, but not limited to, the type of mobile device You + use, Your mobile device unique ID, the IP address of Your mobile device, Your mobile + operating system, the type of mobile Internet browser You use, unique device identifiers + and other diagnostic data. + </p> + <p> + We may also collect information that Your browser sends whenever You visit our Service + or when You access the Service by or through a mobile device. + </p> + + <h3>Tracking Technologies and Cookies</h3> + <p> + We use Cookies and similar tracking technologies to track the activity on Our Service + and store certain information. Tracking technologies used are beacons, tags, and scripts + to collect and track information and to improve and analyze Our Service. + </p> + <p> + You can instruct Your browser to refuse all Cookies or to indicate when a Cookie is + being sent. However, if You do not accept Cookies, You may not be able to use some parts + of our Service. + </p> + <p> + Cookies can be "Persistent" or "Session" Cookies. Persistent Cookies remain on your + personal computer or mobile device when You go offline, while Session Cookies are + deleted as soon as You close your web browser. Learn more about cookies in the{' '} + <a href="https://www.privacypolicies.com/blog/cookies/">"What Are Cookies"</a> article. + </p> + <p>We use both session and persistent Cookies for the purposes set out below:</p> + <ul> + <li> + <p> + <strong>Necessary / Essential Cookies</strong> + </p> + <p>Type: Session Cookies</p> + <p>Administered by: Us</p> + <p> + Purpose: These Cookies are essential to provide You with services available through + the Website and to enable You to use some of its features. They help to authenticate + users and prevent fraudulent use of user accounts. Without these Cookies, the + services that You have asked for cannot be provided, and We only use these Cookies + to provide You with those services. + </p> + </li> + <li> + <p> + <strong>Cookies Policy / Notice Acceptance Cookies</strong> + </p> + <p>Type: Persistent Cookies</p> + <p>Administered by: Us</p> + <p> + Purpose: These Cookies identify if users have accepted the use of cookies on the + Website. + </p> + </li> + <li> + <p> + <strong>Functionality Cookies</strong> + </p> + <p>Type: Persistent Cookies</p> + <p>Administered by: Us</p> + <p> + Purpose: These Cookies allow us to remember choices You make when You use the + Website, such as remembering your login details or language preference. The purpose + of these Cookies is to provide You with a more personal experience and to avoid You + having to re-enter your preferences every time You use the Website. + </p> + </li> + </ul> + <p> + For more information about the cookies we use and your choices regarding cookies, please + visit our Cookies Policy. + </p> + + <h2>Use of Your Personal Data</h2> + <p>The Company may use Personal Data for the following purposes:</p> + <ul> + <li> + <strong>To provide and maintain our Service</strong>, including to monitor the usage + of our Service. + </li> + <li> + <strong>To manage Your Account:</strong> to manage Your registration as a user of the + Service. The Personal Data You provide can give You access to different + functionalities of the Service that are available to You as a registered user. + </li> + <li> + <strong>For the performance of a contract:</strong> the development, compliance and + undertaking of the purchase contract for the products, items or services You have + purchased or of any other contract with Us through the Service. + </li> + <li> + <strong>To contact You:</strong> To contact You by email, telephone calls, SMS, or + other equivalent forms of electronic communication, such as a mobile application's + push notifications regarding updates or informative communications related to the + functionalities, products or contracted services, including the security updates, when + necessary or reasonable for their implementation. + </li> + <li> + <strong>To provide You</strong> with news, special offers and general information + about other goods, services and events which we offer that are similar to those that + you have already purchased or enquired about unless You have opted not to receive such + information. + </li> + <li> + <strong>To manage Your requests:</strong> To attend and manage Your requests to Us. + </li> + </ul> + + <p>We may share your personal information in the following situations:</p> + + <ul> + <li> + <strong>With Service Providers:</strong> We may share Your personal information with + Service Providers to monitor and analyze the use of our Service, to contact You. + </li> + <li> + <strong>For Business transfers:</strong> We may share or transfer Your personal + information in connection with, or during negotiations of, any merger, sale of Company + assets, financing, or acquisition of all or a portion of our business to another + company. + </li> + <li> + <strong>With Affiliates:</strong> We may share Your information with Our affiliates, + in which case we will require those affiliates to honor this Privacy Policy. + Affiliates include Our parent company and any other subsidiaries, joint venture + partners or other companies that We control or that are under common control with Us. + </li> + <li> + <strong>With Business partners:</strong> We may share Your information with Our + business partners to offer You certain products, services or promotions. + </li> + <li> + <strong>With other users:</strong> when You share personal information or otherwise + interact in the public areas with other users, such information may be viewed by all + users and may be publicly distributed outside. If You interact with other users or + register through a Third-Party Social Media Service, Your contacts on the Third-Party + Social Media Service may see You name, profile, pictures and description of Your + activity. Similarly, other users will be able to view descriptions of Your activity, + communicate with You and view Your profile. + </li> + </ul> + + <h2>Retention of Your Personal Data</h2> + <p> + The Company will retain Your Personal Data only for as long as is necessary for the + purposes set out in this Privacy Policy. We will retain and use Your Personal Data to + the extent necessary to comply with our legal obligations (for example, if we are + required to retain your data to comply with applicable laws), resolve disputes, and + enforce our legal agreements and policies. + </p> + <p> + The Company will also retain Usage Data for internal analysis purposes. Usage Data is + generally retained for a shorter period of time, except when this data is used to + strengthen the security or to improve the functionality of Our Service, or We are + legally obligated to retain this data for longer time periods. + </p> + + <h2>Transfer of Your Personal Data</h2> + <p> + Your information, including Personal Data, is processed at the Company's operating + offices and in any other places where the parties involved in the processing are + located. It means that this information may be transferred to — and maintained on — + computers located outside of Your state, province, country or other governmental + jurisdiction where the data protection laws may differ than those from Your + jurisdiction. + </p> + <p> + Your consent to this Privacy Policy followed by Your submission of such information + represents Your agreement to that transfer. + </p> + <p> + The Company will take all steps reasonably necessary to ensure that Your data is treated + securely and in accordance with this Privacy Policy and no transfer of Your Personal + Data will take place to an organization or a country unless there are adequate controls + in place including the security of Your data and other personal information. + </p> + + <h2>Disclosure of Your Personal Data</h2> + <h3>Business Transactions</h3> + <p> + If the Company is involved in a merger, acquisition or asset sale, Your Personal Data + may be transferred. We will provide notice before Your Personal Data is transferred and + becomes subject to a different Privacy Policy. + </p> + <h3>Law enforcement</h3> + <p> + Under certain circumstances, the Company may be required to disclose Your Personal Data + if required to do so by law or in response to valid requests by public authorities (e.g. + a court or a government agency). + </p> + <h3>Other legal requirements</h3> + <p> + The Company may disclose Your Personal Data in the good faith belief that such action is + necessary to: + </p> + <ul> + <li>Comply with a legal obligation</li> + <li>Protect and defend the rights or property of the Company</li> + <li>Prevent or investigate possible wrongdoing in connection with the Service</li> + <li>Protect the personal safety of Users of the Service or the public</li> + <li>Protect against legal liability</li> + </ul> + + <h2>Security of Your Personal Data</h2> + <p> + The security of Your Personal Data is important to Us, but remember that no method of + transmission over the Internet, or method of electronic storage is 100% secure. While We + strive to use commercially acceptable means to protect Your Personal Data, We cannot + guarantee its absolute security. + </p> + + <h1>Children's Privacy</h1> + <p> + Our Service does not address anyone under the age of 13. We do not knowingly collect + personally identifiable information from anyone under the age of 13. If You are a parent + or guardian and You are aware that Your child has provided Us with Personal Data, please + contact Us. If We become aware that We have collected Personal Data from anyone under + the age of 13 without verification of parental consent, We take steps to remove that + information from Our servers. + </p> + <p> + If We need to rely on consent as a legal basis for processing Your information and Your + country requires consent from a parent, We may require Your parent's consent before We + collect and use that information. + </p> + + <h1>Links to Other Websites</h1> + <p> + Our Service may contain links to other websites that are not operated by Us. If You + click on a third party link, You will be directed to that third party's site. We + strongly advise You to review the Privacy Policy of every site You visit. + </p> + <p> + We have no control over and assume no responsibility for the content, privacy policies + or practices of any third party sites or services. + </p> + + <h1>Changes to this Privacy Policy</h1> + <p> + We may update our Privacy Policy from time to time. We will notify You of any changes by + posting the new Privacy Policy on this page. + </p> + <p> + We will let You know via email and/or a prominent notice on Our Service, prior to the + change becoming effective and update the "Last updated" date at the top of this Privacy + Policy. + </p> + <p> + You are advised to review this Privacy Policy periodically for any changes. Changes to + this Privacy Policy are effective when they are posted on this page. + </p> + + <h1>Contact Us</h1> + <p>If you have any questions about this Privacy Policy, You can contact us:</p> + <ul> + <li>By email: ntueeplus2020@gmail.com</li> + <li>By visiting this page on our website: https://plus.ntuee.org/Contact</li> + </ul> + </CCardBody> + </CCard> + ) + } +} + +const Policy_page = () => { + return <Policy /> +} + +export default Policy_page diff --git a/client/src/views/out/policy/index.js b/client/src/views/out/policy/index.js new file mode 100644 index 000000000..a0a158764 --- /dev/null +++ b/client/src/views/out/policy/index.js @@ -0,0 +1,2 @@ +import Policy from './Policy' +export default Policy diff --git a/client/src/views/out/register/Register.js b/client/src/views/out/register/Register.js new file mode 100644 index 000000000..d7a42814f --- /dev/null +++ b/client/src/views/out/register/Register.js @@ -0,0 +1,281 @@ +import React, { useState } from 'react' +import axios from 'axios' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CListGroup, + CListGroupItem, + CRow, + CCollapse, + CModal, + CModalHeader, + CModalBody, + CModalTitle, + CModalFooter, + CLink, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' +import { Redirect, useParams } from 'react-router-dom' + +const Register = () => { + const { identity } = useParams() + const RegisterFormTemplate = { + account: '', + password: '', + ConfirmPassword: '', + username: '', + Email: '', + file: null, + isGraduated: identity === 'alumni', + } + // for web control + const [isExpand, setIsExpand] = useState(false) + const [isModal, setIsModal] = useState(false) + const [previewURL, setPreviewURL] = useState(null) + const [fileButton, setFileButton] = useState(null) + const [toLogin, setToLogin] = useState(false) + + // data to backend + const [registerForm, setRegisterForm] = useState(RegisterFormTemplate) + + const expand = (e) => { + e.preventDefault() + setIsExpand(true) + } + const constract = (e) => { + e.preventDefault() + setIsExpand(false) + } + + const openModal = (e) => { + setIsModal(true) + } + + const closeModal = (e) => { + setIsModal(false) + } + + const handleChangeImage = (e) => { + let reader = new FileReader() + let file = e.target.files[0] + setFileButton(e.target) + setRegisterForm({ ...registerForm, file: file }) + reader.onloadend = () => { + setPreviewURL(reader.result) + } + reader.readAsDataURL(file) + // call the modal + setIsModal(true) + } + + const clearImage = (e) => { + setIsModal(false) + setPreviewURL(null) + setRegisterForm({ ...registerForm, file: null }) + fileButton.value = '' + } + + const handleInputChange = (e) => { + setRegisterForm({ ...registerForm, [e.target.name]: e.target.value }) + } + + const handleSubmit = (e) => { + e.preventDefault() + if (registerForm.password !== registerForm.ConfirmPassword) { + return alert('密碼不一致') + } else { + let data = new FormData() + if (identity === 'student') + for (let key in registerForm) { + if (key === 'Email') data.append(key, `${registerForm.account}@ntu.edu.tw`) + data.append(key, registerForm[key]) + } + else if (identity === 'alumni') + for (let key in registerForm) { + data.append(key, registerForm[key]) + } + + const config = { + headers: { + 'content-type': 'multipart/form-data', + }, + } + // send to backend + // then redirect to login + axios + .post('api/register', data, config) + .then((res) => { + alert( + identity === 'student' + ? '請至學校信箱接收開通信,您的帳號就會被激活!' + : '等待管理員確認您的身分後就會寄開通信至您的信箱,請耐心等候!', + ) + setToLogin(true) + }) + .catch((err) => { + switch (err.response.status) { + default: + alert(err.response.data.description) + break + } + }) + } + } + + return toLogin ? ( + <Redirect to="/login" /> + ) : ( + <> + <CModal visible={isModal} onDismiss={closeModal} alignment="center"> + <CModalHeader onDismiss={closeModal}> + <CModalTitle>Preview Your Photo</CModalTitle> + </CModalHeader> + <CModalBody> + <img src={previewURL} className="img-fluid container justify-content-center d-flex" /> + </CModalBody> + <CModalFooter> + <CButton color="warning" onClick={clearImage}> + Clear + </CButton> + <CButton color="dark" onClick={closeModal}> + OK + </CButton> + </CModalFooter> + </CModal> + <div className="min-vh-100 d-flex flex-row align-items-center"> + <CContainer> + <CRow className="justify-content-center"> + <CCol md="11" lg="9" xl="8"> + <CCard className="mx-4"> + <CCardBody className="p-4"> + <CForm> + <h1>Just A Few Steps to Join EE+!</h1> + <p className="text-medium-emphasis">Create your account</p> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-user" name="cil-user" /> + </CInputGroupText> + <CFormControl + placeholder="Your Chinese Name" + name="username" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-education" name="cil-education" /> + </CInputGroupText> + <CFormControl + placeholder="Student ID" + name="account" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-lock-locked" name="cil-lock-locked" /> + </CInputGroupText> + <CFormControl + type="password" + placeholder="Password" + name="password" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-lock-locked" name="cil-lock-locked" /> + </CInputGroupText> + <CFormControl + type="password" + placeholder="Repeat password" + name="ConfirmPassword" + onChange={handleInputChange} + /> + </CInputGroup> + {identity === 'alumni' && ( + <> + <CInputGroup className="mb-3"> + <CInputGroupText>@</CInputGroupText> + <CFormControl + placeholder="Email" + name="Email" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup + className="mb-3" + onMouseEnter={expand} + onFocus={expand} + onBlur={constract} + > + <CInputGroupText> + <CIcon icon="cil-image" name="cil-image" /> + </CInputGroupText> + <CFormControl + id="formFile" + type="file" + onChange={handleChangeImage} + ></CFormControl> + </CInputGroup> + <CCollapse visible={isExpand} onMouseLeave={constract}> + <CListGroup> + <CListGroupItem color="info"> + Please go to{' '} + <a + href="https://my.ntu.edu.tw/alumnusJobManage/cst01-1.aspx" + target="_blank" + rel="noreferrer" + className="text-warning fw-bold" + > + this website + </a>{' '} + and <b>take a screenshot</b> to prove your identity of alumni of NTUEE + <br /> It should contain your <b>full name</b> and <b>studentID</b>. + </CListGroupItem> + <CListGroupItem color="success"> + Screenshot is used to confirm your identity, and will be auto deleted + after account is activated + </CListGroupItem> + <CListGroupItem color="warning"> + The size of screenshot is at most <b>1MB</b>. + </CListGroupItem> + </CListGroup> + <div className="d-flex justify-content-end"> + {previewURL ? ( + <CLink color="link" onClick={openModal} style={{ cursor: 'pointer' }}> + Preview Again? + </CLink> + ) : ( + <></> + )} + </div> + </CCollapse> + </> + )} + <CRow className="justify-content-center mt-3"> + <div className="d-flex justify-content-center"> + <CButton color="dark" onClick={handleSubmit}> + Create Account + </CButton> + </div> + </CRow> + </CForm> + </CCardBody> + </CCard> + </CCol> + </CRow> + </CContainer> + </div> + </> + ) +} + +export default Register diff --git a/client/src/views/out/register/index.js b/client/src/views/out/register/index.js new file mode 100644 index 000000000..2bedd15ae --- /dev/null +++ b/client/src/views/out/register/index.js @@ -0,0 +1,2 @@ +import Register from './Register' +export default Register diff --git a/client/src/views/out/registerEntry/RegisterEntry.js b/client/src/views/out/registerEntry/RegisterEntry.js new file mode 100644 index 000000000..c34971c96 --- /dev/null +++ b/client/src/views/out/registerEntry/RegisterEntry.js @@ -0,0 +1,104 @@ +import React, { useState } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { selectLogin, login } from '../../../slices/loginSlice' +import axios from 'axios' +import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props' +import { Link, Redirect } from 'react-router-dom' +import { CCol, CContainer, CRow, CImage } from '@coreui/react' +import FB from 'fb-react-sdk' + +import Alumnus from './images/Alumnus.png' +import Student from './images/Student.png' + +const RegisterEntry = () => { + const dispatch = useDispatch() + const { isLogin } = useSelector(selectLogin) + + const [needRegister, setNeedRegister] = useState(false) + const [userId, setUserId] = useState(null) + + const handleFBSubmit = async (res) => { + if (res.status == 'unknown') { + return + } + const { email, userID, accessToken } = res + const imgUrl = await new Promise((resolve, reject) => { + FB.setAccessToken(accessToken) + const url = `${userID}/picture` + FB.get( + url, + { redirect: false, height: 720 }, //type:"large"}, + function (err, res) { + console.log(res) + if (err) { + console.log('error occurred', err) + return reject(false) + } + console.log(res.data.url) + resolve({ url: res.data.url }) + }, + ) + }) + console.log('img', imgUrl.url) + // try to loginFB + // check if login success + // if no redirect to registerFB, then redirect to login + // if success redirect to inside + + setUserId(res.userID) + axios + .post('/api/loginFB', { facebookID: res.userID }) + .then((res) => { + const { username, isAuth } = res.data + alert(`歡迎回來! ${username}`) + dispatch(login(isAuth)) + }) + .catch((err) => { + switch (err.response.status) { + case 404: + setNeedRegister(true) + break + default: + alert(err.response.data.description) + break + } + }) + } + if (isLogin) { + return <Redirect to="/home" /> + } else { + return ( + <div className="min-vh-100 d-flex flex-row align-items-center"> + <CContainer className="align-items-center"> + <CRow className="justify-content-center mt-5 mb-3"> + <p className="text-wrap text-center badge" style={{ fontSize: '1.5rem' }}> + Please choose your identity to sign up by different method + </p> + </CRow> + {/* for desktop and ipad */} + <CRow className="justify-content-around d-flex"> + <CCol md="4" sm="8"> + <Link + to="register/student" + className="d-flex justify-content-center display-3 text-white" + > + <CImage src={Student} alt="Register as Student" className="img-fluid" /> + </Link> + <h3 className="text-light text-center mt-4">我是未畢業學生!</h3> + </CCol> + <CCol md="4" sm="8"> + <Link + to="register/alumni" + className="d-flex justify-content-center display-3 text-white" + > + <CImage src={Alumnus} alt="Register as Alumnus" className="img-fluid" /> + </Link> + <h3 className="text-light text-center mt-4">我是已畢業系友!</h3> + </CCol> + </CRow> + </CContainer> + </div> + ) + } +} +export default RegisterEntry diff --git a/client/src/views/out/registerEntry/images/Alumnus.png b/client/src/views/out/registerEntry/images/Alumnus.png new file mode 100644 index 000000000..8a96bd117 Binary files /dev/null and b/client/src/views/out/registerEntry/images/Alumnus.png differ diff --git a/client/src/views/out/registerEntry/images/Student.png b/client/src/views/out/registerEntry/images/Student.png new file mode 100644 index 000000000..30dbe45ad Binary files /dev/null and b/client/src/views/out/registerEntry/images/Student.png differ diff --git a/client/src/views/out/registerEntry/index.js b/client/src/views/out/registerEntry/index.js new file mode 100644 index 000000000..ca0e04b7a --- /dev/null +++ b/client/src/views/out/registerEntry/index.js @@ -0,0 +1,2 @@ +import RegisterEntry from './RegisterEntry' +export default RegisterEntry diff --git a/client/src/views/out/registerFB/RegisterFB.js b/client/src/views/out/registerFB/RegisterFB.js new file mode 100644 index 000000000..e6f5f4475 --- /dev/null +++ b/client/src/views/out/registerFB/RegisterFB.js @@ -0,0 +1,230 @@ +/* eslint-disable prettier/prettier */ +import React, { useEffect, useState } from 'react' +import { Redirect, useLocation } from 'react-router' +import axios from 'axios' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CListGroup, + CListGroupItem, + CRow, + CCollapse, + CModal, + CModalHeader, + CModalBody, + CModalTitle, + CModalFooter, + CLink, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' + +const RegisterFBFormTemplate = { + account: '', + username: '', + facebookID: '', + file: null, +} + +const RegisterFB = () => { + // for web control + const location = useLocation() + const [isExpand, setIsExpand] = useState(false) + const [isModal, setIsModal] = useState(false) + const [previewURL, setPreviewURL] = useState(null) + const [fileButton, setFileButton] = useState(null) + const [toLogin, setToLogin] = useState(false) + + // data to backend + const [registerFBForm, setRegisterFBForm] = useState(RegisterFBFormTemplate) + + useEffect(() => { + if (location.state) { + setRegisterFBForm({ ...registerFBForm, facebookID: location.state.facebookID }) + } + }, []) + + const expand = (e) => { + e.preventDefault() + setIsExpand(true) + } + const constract = (e) => { + e.preventDefault() + setIsExpand(false) + } + + const openModal = (e) => { + setIsModal(true) + } + + const closeModal = (e) => { + setIsModal(false) + } + + const handleChangeImage = (e) => { + let reader = new FileReader() + let file = e.target.files[0] + setFileButton(e.target) + setRegisterFBForm({ ...registerFBForm, file: file }) + reader.onloadend = () => { + setPreviewURL(reader.result) + } + reader.readAsDataURL(file) + // call the modal + setIsModal(true) + } + + const clearImage = (e) => { + setIsModal(false) + setPreviewURL(null) + setRegisterFBForm({ ...registerFBForm, file: null }) + fileButton.value = '' + } + + const handleInputChange = (e) => { + setRegisterFBForm({ ...registerFBForm, [e.target.name]: e.target.value }) + } + + const handleFBSubmit = (e) => { + e.preventDefault() + + let data = new FormData() + for (let key in registerFBForm) { + data.append(key, registerFBForm[key]) + } + const config = { + headers: { + 'content-type': 'multipart/form-data', + }, + } + axios + .post('api/registerFB', data, config) + .then((res) => { + alert('註冊成功,跳轉至登入頁面') + setToLogin(true) + }) + .catch((err) => { + switch (err.response.status) { + default: + alert(err.response.data.description) + break + } + }) + } + if (!location.state) { + return <Redirect to="/register_entry" /> + } else if (toLogin) { + return <Redirect to="/login" /> + } else { + return ( + <> + <CModal visible={isModal} onDismiss={closeModal} alignment="center"> + <CModalHeader onDismiss={closeModal}> + <CModalTitle>Preview Your Photo</CModalTitle> + </CModalHeader> + <CModalBody> + <img src={previewURL} className="img-fluid container justify-content-center d-flex" /> + </CModalBody> + <CModalFooter> + <CButton color="warning" onClick={clearImage}> + Clear + </CButton> + <CButton color="dark" onClick={closeModal}> + OK + </CButton> + </CModalFooter> + </CModal> + <div className="min-vh-100 d-flex flex-row align-items-center"> + <CContainer> + <CRow className="justify-content-center"> + <CCol md="11" lg="9" xl="8"> + <CCard className="mx-4"> + <CCardBody className="p-4"> + <CForm> + <h1>Just A Few Steps to Join EE+!</h1> + <p className="text-medium-emphasis">Create your account</p> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-user" name="cil-user" /> + </CInputGroupText> + <CFormControl + placeholder="Your Chinese Name" + name="username" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup className="mb-3"> + <CInputGroupText> + <CIcon icon="cil-education" name="cil-education" /> + </CInputGroupText> + <CFormControl + placeholder="Student ID" + name="account" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup + className="mb-3" + onMouseEnter={expand} + onFocus={expand} + onBlur={constract} + > + <CInputGroupText> + <CIcon icon="cil-image" name="cil-image" /> + </CInputGroupText> + <CFormControl + id="formFile1" + type="file" + onChange={handleChangeImage} + ></CFormControl> + </CInputGroup> + <CCollapse visible={isExpand} onMouseLeave={constract}> + <CListGroup> + <CListGroupItem color="info"> + ID photo should contain your <b>full name</b> and{' '} + <b>intact, clear face</b>. + </CListGroupItem> + <CListGroupItem color="success"> + ID photo is used to confirm your identity, and will be auto deleted + after account is activated + </CListGroupItem> + <CListGroupItem color="warning"> + The size of photo is at most <b>1MB</b>. + </CListGroupItem> + </CListGroup> + <div className="d-flex justify-content-end"> + {previewURL ? ( + <CLink color="link" onClick={openModal} style={{ cursor: 'pointer' }}> + Preview Again? + </CLink> + ) : ( + <></> + )} + </div> + </CCollapse> + <CRow className="justify-content-center mt-3"> + <div className="d-flex justify-content-center"> + <CButton color="dark" block onClick={handleFBSubmit}> + Create Account + </CButton> + </div> + </CRow> + </CForm> + </CCardBody> + </CCard> + </CCol> + </CRow> + </CContainer> + </div> + </> + ) + } +} + +export default RegisterFB diff --git a/client/src/views/out/registerFB/index.js b/client/src/views/out/registerFB/index.js new file mode 100644 index 000000000..419bde85c --- /dev/null +++ b/client/src/views/out/registerFB/index.js @@ -0,0 +1,2 @@ +import RegisterFB from './RegisterFB' +export default RegisterFB diff --git a/client/src/views/out/resetPassword/ResetPassword.js b/client/src/views/out/resetPassword/ResetPassword.js new file mode 100644 index 000000000..f219d6ee9 --- /dev/null +++ b/client/src/views/out/resetPassword/ResetPassword.js @@ -0,0 +1,117 @@ +import React, { useEffect, useState } from 'react' +import { Redirect, useParams } from 'react-router-dom' +import axios from 'axios' +import { + CButton, + CCard, + CCardBody, + CCol, + CContainer, + CForm, + CFormControl, + CInputGroup, + CInputGroupText, + CRow, +} from '@coreui/react' +import CIcon from '@coreui/icons-react' + +const ResetPasswordFormTemplate = { + account: '', + active: '', + password: '', + ConfirmPassword: '', +} + +const ResetPassword = () => { + const [toLogin, setToLogin] = useState(false) + + const { account, active } = useParams() + const [resetPasswordForm, setResetPasswordForm] = useState(ResetPasswordFormTemplate) + + useEffect(() => { + setResetPasswordForm({ ...resetPasswordForm, account: account, active: active }) + }, []) + + const handleInputChange = (e) => { + setResetPasswordForm({ ...resetPasswordForm, [e.target.name]: e.target.value }) + } + + const handleSubmit = (e) => { + e.preventDefault() + // connect with backend + // and send email to user + if (resetPasswordForm.password != resetPasswordForm.ConfirmPassword) { + return alert('密碼不一致') + } else { + let r = window.confirm('確認修改密碼?') + if (r) { + // send to backend + // then redirect to login + axios + .post('api/activation', resetPasswordForm) + .then((res) => { + alert('修改密碼成功,跳轉至登入頁面') + setToLogin(true) + }) + .catch((err) => { + switch (err.response.status) { + default: + alert(err.response.data.description) + break + } + }) + } + } + } + + return toLogin ? ( + <Redirect to="/login" /> + ) : ( + <div className="min-vh-100 d-flex flex-row align-items-center"> + <CContainer> + <CRow className="justify-content-center"> + <CCol md="10" lg="8" xl="7"> + <CCard className="mx-4"> + <CCardBody className="p-4"> + <CForm> + <h1>Let's Reset Your Password!</h1> + <CInputGroup className="my-3"> + <CInputGroupText> + <CIcon icon="cil-lock-locked" name="cil-lock-locked" /> + </CInputGroupText> + <CFormControl + type="password" + placeholder="New Password" + name="password" + onChange={handleInputChange} + /> + </CInputGroup> + <CInputGroup className="mb-4"> + <CInputGroupText> + <CIcon icon="cil-lock-locked" name="cil-lock-locked" /> + </CInputGroupText> + <CFormControl + type="password" + placeholder="Repeat password" + name="ConfirmPassword" + onChange={handleInputChange} + /> + </CInputGroup> + <CRow className="justify-content-center mt-3"> + <div className="d-flex justify-content-center"> + <CButton color="dark" block onClick={handleSubmit}> + Reset Password + </CButton> + </div> + </CRow> + </CForm> + </CCardBody> + </CCard> + </CCol> + </CRow> + </CContainer> + </div> + ) +} + +export default ResetPassword diff --git a/client/src/views/out/resetPassword/index.js b/client/src/views/out/resetPassword/index.js new file mode 100644 index 000000000..6b2df2392 --- /dev/null +++ b/client/src/views/out/resetPassword/index.js @@ -0,0 +1,2 @@ +import ResetPassword from './ResetPassword' +export default ResetPassword diff --git a/client/webpack.config.js b/client/webpack.config.js new file mode 100644 index 000000000..cd749cbda --- /dev/null +++ b/client/webpack.config.js @@ -0,0 +1,95 @@ +const path = require('path') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const webpack = require('webpack') +const env = require('dotenv') +env.config({ path: '../.env' }) + +module.exports = { + entry: ['@babel/polyfill', path.resolve(__dirname, 'src', 'index.js')], + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'bundle.js', + }, + resolve: { + fallback: { + crypto: require.resolve('crypto-browserify'), + stream: require.resolve('stream-browserify'), + }, + }, + //=============== webpack server (currently not use)================= + // devServer:{ + // contentBase: path.resolve(__dirname,'dist'), + // open:true, + // clientLogLevel: 'silent', + // port: 9000, + // }, + //=================================================================== + //=============== html template plugin (currently not use) ========== + plugins: [ + new HtmlWebpackPlugin({ + template: path.resolve(__dirname, 'public/index.html'), + }), + new webpack.ProvidePlugin({ + process: 'process/browser', + }), + new webpack.DefinePlugin({ + 'process.env.REACT_APP_fbAPIid': process.env.REACT_APP_fbAPIid, + }), + ], + //=================================================================== + module: { + rules: [ + { + test: /\.(jsx|js)$/, + include: path.resolve(__dirname, 'src'), + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-env', + { + targets: 'defaults', + }, + ], + '@babel/preset-react', + ], + // plugins: ["react-hot-loader/babel"] + }, + }, + ], + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.(scss)$/, + use: ['style-loader', 'css-loader', 'sass-loader'], + }, + { + test: /\.(png|svg|jpg|gif)$/, + use: { + loader: 'file-loader', + options: { + name: '[path][name].[ext]', + }, + }, + }, + { + test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, + use: [ + { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'fonts/', + }, + }, + ], + }, + ], + }, +} diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 000000000..523a5878d --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,35 @@ +steps: + # Build the container image + - name: 'gcr.io/cloud-builders/docker' + entrypoint: 'bash' + args: ['-c', 'docker pull gcr.io/$PROJECT_ID/eeplus || true'] + # Build the container image + - name: 'gcr.io/cloud-builders/docker' + args: + - 'build' + - '--cache-from' + - 'gcr.io/$PROJECT_ID/eeplus' + - '-t' + - 'gcr.io/$PROJECT_ID/eeplus' + - '--build-arg' + - 'MONGO_URI=${_MONGO_URI}' + - '--build-arg' + - 'REACT_APP_fbAPIid=${_FB_APPID}' + - '.' + # Push the container image to Container Registry + - name: 'gcr.io/cloud-builders/docker' + args: ['push', 'gcr.io/$PROJECT_ID/eeplus'] + # Deploy container image to Cloud Run + - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' + entrypoint: gcloud + args: + - 'run' + - 'deploy' + - 'eeplus' + - '--image' + - 'gcr.io/$PROJECT_ID/eeplus' + - '--region' + - 'asia-east1' +images: + - 'gcr.io/$PROJECT_ID/eeplus' +timeout: 1200s diff --git a/doc/contribution.md b/doc/contribution.md new file mode 100644 index 000000000..f24d69828 --- /dev/null +++ b/doc/contribution.md @@ -0,0 +1,87 @@ +# Follow Fork & Pull model + +## Step1 Fork(first time) + +Fork repo : [EndOfWeb](https://github.com/NTUEE-PLUS/EndOfWeb.git) + +## Step 2 Clone to local + +``` +#USERNAME~your GitHub account + +git clone https://github.com/${USERNAME}/EndOfWeb.git + +cd EndOfWeb +#setting +git remote add upstream https://github.com/NTUEE-PLUS/EndOfWeb.git +git remote set-url --push upstream no_push +git remote -v //check +``` + +![image](https://github.com/featherchen/EndOfWeb/blob/NTUEEPLUS-5/screenshot/remote-v.png) + +## Step 3 Go to [Jira](https://ntueeplus.atlassian.net/jira/software/c/projects/NTUEEPLUS/issues/) to find/create an issue + +## Step 4 Create a local branch for contribution + +for each issue you got, create a branch with its jira number. + +``` +cd EndOfWeb + +# Make local main up-to-date +git checkout main +git fetch upstream +git rebase upstream/main + +#Create a new branch +git checkout -b NTUEEPLUS-${jira_number} + +#Ex:git checkout -b NTUEEPLUS-5 +``` + +## Step 5 Develop and commits + +Coding on branch NTUEEPLUS-${jira_number}. + +``` +git add ${edited files} +git commit -m {commit message} +#please be clear and use git rebase -i to squash commit if too many commits.(like more than 10). +``` + +## Step 6 Push + +After you finished tour work, push this branch to origin. +Please do not use git pull, since it may create redundant commits. + +``` +#On branch NTUEEPLUS-${jira_number} + +#Syncing +git fetch upstream +git rebase upstream/main + +#Push +git push origin NTUEEPLUS-${jira_number} +``` + +## Step 7 Create a pull request (PR) GitHub UI. + +### PR + +![image](https://github.com/featherchen/EndOfWeb/blob/NTUEEPLUS-5/screenshot/PR.png) + +### Fill the PR template + +![image](https://github.com/featherchen/EndOfWeb/blob/NTUEEPLUS-5/screenshot/PR%20template.png) + +## Step 8 The review process + +Ask reviewer to review the PR.(Ping them) + +## Step 9 Address review comment + +Once you push your new commits to your local branch, PR will update automatically. + +## Step 10 Merge to main diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..bbafff54c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3.8' +services: + mongodb: + image: mongo + expose: + - '27017' + web: + image: node:14-alpine + working_dir: /app + restart: on-failure:5 + build: + context: ./ + dockerfile: ee.Dockerfile + volumes: + - ./:/app + ports: + - '3000:1993' + depends_on: + - mongodb + environment: + MONGO_URI: mongodb://mongodb/eeplus + tty: true + stdin_open: true diff --git a/ee.Dockerfile b/ee.Dockerfile new file mode 100644 index 000000000..c4d4a2b0a --- /dev/null +++ b/ee.Dockerfile @@ -0,0 +1,14 @@ +FROM node:14-alpine +WORKDIR /app +# Install app dependencies +COPY package*.json ./ +COPY client/package*.json ./client/ +COPY backend/package*.json ./backend/ +RUN yarn local-install +# Bundle app source +COPY . . +ENV NODE_ENV=production +RUN yarn run build-client + +EXPOSE 1993 +CMD [ "node", "index.js" ] \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 000000000..c895119bb --- /dev/null +++ b/index.js @@ -0,0 +1,99 @@ +//ee0125/index.js +const express = require('express') +const app = express() +const path = require('path') +const cors = require('cors') +const mongoose = require('./backend/routes/Schemas/db') + +mongoose.connection.on('open', () => { + console.log('DB on') + const bodyParser = require('body-parser') + const session = require('express-session') + const MongoStore = require('connect-mongo')(session) + //post, get時的解碼 + app.use(bodyParser.urlencoded({ extended: true })) + app.use(bodyParser.json()) + // app.use(function(req, res, next) { + // res.header("Access-Control-Allow-Origin", 'http://localhost:3000'); + // res.header("Access-Control-Allow-Credentials", true); + // res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); + // res.header("Access-Control-Allow-Headers", 'Origin, X-Requested-With, Content-Type, Accept'); + // next(); + // }); + // app.use( + // cors({ + // origin: 'http://localhost:3000', + // methods: ['POST', 'PUT', 'GET', 'OPTIONS', 'HEAD', 'PATCH', 'DELETE'], + // credentials: true, + // }), + // ) + //session 設定 + //參考網站https://www.cnblogs.com/chyingp/p/nodejs-learning-express-session.html + app.use( + session({ + name: 'eeplus', + secret: 'fuewhzk', // 用来對session id相關的cookie進行簽名,建議128byte亂碼 + //store: new FileStore({ logFn: function () {} }), // 本地儲存session(文本文件,也可以選擇其他store,比如redis的) + store: new MongoStore({ + mongooseConnection: mongoose.connection, + }), + saveUninitialized: false, // 是否自动保存未初始化的會話,建議false + resave: false, // 是否每次都重新保存會話,建議false + cookie: { + httpOnly: true, //false前端可read和set + maxAge: 60 * 60 * 1000, // 有效期(ms) + }, + }), + ) + + app.use('/api', require('./backend/routes/api')) + // }) + + //frontend + if (!process.env.HERO) { + const connectHistoryApiFallback = require('connect-history-api-fallback') + app.use( + connectHistoryApiFallback({ + verbose: false, + }), + ) + const JSON_DIR = path.join(__dirname, './client/public') // for develop + const DIST_DIR = path.join(__dirname, './client/dist') + const HTML_FILE = path.join(__dirname, './client/dist/index.html') + app.use(express.static(DIST_DIR)) + app.use(express.static(JSON_DIR)) // for develop + app.get('/', (req, res) => { + res.sendFile(HTML_FILE) // EDIT + }) + } + //old frontend + //Serve static files from the React app + //詳細資訊看:https://expressjs.com/zh-tw/starter/static-files.html + // app.use(express.static(path.join(__dirname, 'client/build'))) + /*app.get('*', (req, res) => { + res.sendFile(path.join(__dirname+'/client/build/index.html')); //這個缺點是react build的index不是我們寫的那個 + //res.redirect('/'); //這個按F5會亂跳,先捨棄 +});*/ + + //server on + app.listen(process.env.PORT || 1993, () => { + if (process.env.HERO) { + const { wakeDyno } = require('heroku-keep-awake') + wakeDyno('https://eeplus-back.herokuapp.com/', { + logging: false, + stopTimes: { start: '16:00', end: '00:00' }, //time zone +0,so -8hr + }) + } + console.log(`Server is up on port ${process.env.PORT || 1993}.`) + }) + //https server + // connect to https://localhost:1993 + // const fs = require('fs') + // const options = { + // key: fs.readFileSync('./certificate.key'), + // cert: fs.readFileSync('./certificate.crt') + // }; + // const https = require('https'); + // https.createServer(null, app).listen(process.env.PORT||1993, () => { + // console.log(`Server is up on port ${process.env.PORT || 1993}.`) +}) diff --git a/jsconfig.json b/jsconfig.json deleted file mode 100644 index 63f923e44..000000000 --- a/jsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "." - }, - "include": ["src"] -} \ No newline at end of file diff --git a/migration.md b/migration.md deleted file mode 100644 index 1217ada1d..000000000 --- a/migration.md +++ /dev/null @@ -1,102 +0,0 @@ -# Migration from version 3 - -## CSS - -- `ml-*` to `ms-*` -- `mfs-*` to `ms-*` -- `mr-*` to `me-*` -- `mfe-*` to `me-*` -- `pl-*` to `ps-*` -- `pr-*` to `pe-*` -- `float-left` to `float-start` -- `float-right` to `float-end` - -## Components - -- Deprecated component `CEmbed` -- Deprecated component `CJumbotron` - -### Badges - -- variant="pill" => shape="rounded-pill" -- variant="square" => shape="rounded-0" - -### Forms - -- `CInput` => `CFormControl` -- `CInputCheckbox` => `CFormCheck` -- `CLabel` => `CFormLabel` -- `CSelect` => `CFormSelect` -- `CValidFeedback` => `CFormFeedback valid` -- `CInvalidFeedback` => `CFormFeedback invalid` - -- Deprecated component `CFormGroup` -- Deprecated component `CInputGroupAppend` -- Deprecated component `CInputGroupPrepend` -- Depreacted component `CSwitch`, use `CFormCheck switch` instead of. -- Deprecated `.help-block` - -### Header - -- Deprecated pro `withSubheader` -- Deprecated component `CHeaderNavItem`, use `CNavItem` instead of. -- Deprecated component `CHeaderNavLink`, use `CNavLink` instead of. - -### List Group - -- Depracated prop `action` Use `component="a"` or `component="b"` instead of `action`. - -### Modal - -- Depracated prop `show` Use `visible` instead of. - -### Popover - -- Depracated prop `header` Use `title` instead of. - -### Progress Bar - -- Depracated prop `precision` -- Depracated prop `showLabel` -- Depracated prop `showPercentage` -- Depracated prop `showValue` - -### Tabs - -- Deprecated component `<CTabs>` use `<CNav variant="tabs">` without wrapper component `<CTabs>` - - -# Migration from version 2 - -Migration from version 2 must be performed manually because the components library `@coreui/coreui-react` has been completely rewritten. - -The docs of the new components are available [here](https://coreui.io/react/docs/) - -The good news is that most probably it will be sufficient to migrate layout components (Sidebar, Header, Footer, Aside) and `Switch` component - -The best way to do a migration is: -1. Install `@coreui/coreui-react` v3 -2. Make a copy of the current `containers` folder -2. Paste [containers](https://github.com/coreui/coreui-free-react-admin-template/tree/master/src/containers) folder from v3 template to project -3. Correct routing paths -4. Add previous content to new template layout components -5. Replace `Switch` components with `CSwitch` - -Layout components/ corresponding components in version 3 -- Aside -> CSidebar (with prop aside={true}) -- AsideToggler -> CToggler -- Breadcrumb -> CBreadcrumbRouter -- Footer -> CFooter -- Header -> CHeader -- HeaderDropdown -> CDropdown -- NavbarBrand -> CSidebarBrand -- Sidebar -> CSidebar -- SidebarFooter -> CSidebarFooter -- SidebarForm -> CSidebarForm -- SidebarHeader -> CSidebarHeader -- SidebarMinimizer -> CSidebarMinimizer -- SidebarNav -> CSidebarNav + CSidebarNavDropdown + CSidebarNavItem -- SidebarToggler -> CSidebarToggler -- Switch -> CSwitch - -After the migration is done, you can start using new components of `@coreui/coreui-react` v3 library. diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 000000000..849d1c7a6 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx + +COPY nginx.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 000000000..1b66e5db8 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,18 @@ +worker_processes 4; + +events { worker_connections 1024; } + +http { + server { + listen 80; + + location / { + proxy_pass http://web:8080; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index 810134cee..f76d3e4d2 100644 --- a/package.json +++ b/package.json @@ -1,84 +1,34 @@ { - "name": "@coreui/coreui-free-react-admin-template", - "description": "CoreUI Free React Admin Template", - "version": "4.0.0-beta.0", - "config": { - "coreui_library_short_version": "4.0" - }, - "author": { - "name": "CoreUI", - "url": "https://coreui.io", - "github": "https://github.com/coreui", - "twitter": "https://twitter.com/core_ui" + "scripts": { + "prepare": "husky install", + "local-install": "yarn install && cd client && yarn install && cd ../backend && yarn install && cd ..", + "start-server": "nodemon index.js", + "start-client": "cd client && yarn run local-build && cd ..", + "dev": "npm-run-all --parallel start-client start-server", + "build-client": "cd client && yarn build && cd ..", + "reset-db": "node ./backend/routes/Schemas/preload.js", + "heroku-postbuild": "cd client && yarn install --dev && yarn run webpack && cd ../backend && yarn install && cd ..", + "start": "node index.js" }, - "contributors": [ - { - "name": "CoreUI Team", - "url": "https://github.com/orgs/coreui/people" - } - ], - "homepage": ".", - "copyright": "Copyright 2017-2021 creativeLabs Łukasz Holeczek", - "license": "MIT", - "private": true, - "repository": { - "type": "git", - "url": "git@github.com:coreui/coreui-free-react-admin-template.git" + "engines": { + "node": ">=14.15", + "yarn": ">=1.22" }, "dependencies": { - "@coreui/chartjs": "3.0.0", - "@coreui/coreui": "4.0.0", - "@coreui/icons": "^2.0.1", - "@coreui/icons-react": "^2.0.0-rc.0", - "@coreui/react": "4.0.0-beta.2", - "@coreui/react-chartjs": "2.0.0-rc.0", - "@coreui/utils": "^1.3.1", - "@wojtekmaj/enzyme-adapter-react-17": "^0.6.2", - "chart.js": "^3.4.1", - "classnames": "^2.3.1", - "core-js": "^3.15.2", - "enzyme": "^3.11.0", - "prop-types": "^15.7.2", - "react": "^17.0.2", - "react-app-polyfill": "^2.0.0", - "react-dom": "^17.0.2", - "react-redux": "^7.2.4", - "react-router-dom": "^5.2.0", - "redux": "4.1.0", - "simplebar-react": "^2.3.4" + "@material-ui/icons": "^4.11.2", + "bootstrap-icons": "^1.5.0", + "connect-history-api-fallback": "^1.6.0", + "connect-mongo": "^3.2.0", + "cors": "^2.8.5", + "dotenv": "^10.0.0", + "express": "^4.17.1", + "express-session": "^1.17.2", + "prettier": "^2.3.2" }, "devDependencies": { - "auto-changelog": "~2.3.0", - "eslint": "^7.30.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^3.4.0", - "prettier": "2.3.2", - "react-scripts": "^4.0.3", - "sass": "^1.35.1" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "lint": "eslint \"src/**/*.js\"", - "test": "react-scripts test", - "test:cov": "npm test -- --coverage --watchAll=false", - "test:debug": "react-scripts --inspect-brk test --runInBand", - "eject": "react-scripts eject", - "changelog": "auto-changelog --starting-version 3.0.0 --commit-limit false --hide-credit" - }, - "bugs": { - "url": "https://github.com/coreui/coreui-free-react-admin-template/issues" - }, - "jest": { - "collectCoverageFrom": [ - "src/**/*.{js,jsx}", - "!**/*index.js", - "!src/serviceWorker.js", - "!src/polyfill.js" - ] - }, - "engines": { - "node": ">=10", - "npm": ">=6" + "husky": "^7.0.0", + "nodemon": "^2.0.12", + "npm-run-all": "^4.1.5", + "pretty-quick": "^3.1.1" } } diff --git a/public/avatars/1.jpg b/public/avatars/1.jpg deleted file mode 100644 index 8b5f8091c..000000000 Binary files a/public/avatars/1.jpg and /dev/null differ diff --git a/public/avatars/2.jpg b/public/avatars/2.jpg deleted file mode 100644 index 161eeef53..000000000 Binary files a/public/avatars/2.jpg and /dev/null differ diff --git a/public/avatars/3.jpg b/public/avatars/3.jpg deleted file mode 100644 index 53ecc542f..000000000 Binary files a/public/avatars/3.jpg and /dev/null differ diff --git a/public/avatars/4.jpg b/public/avatars/4.jpg deleted file mode 100644 index a6ee3c72e..000000000 Binary files a/public/avatars/4.jpg and /dev/null differ diff --git a/public/avatars/5.jpg b/public/avatars/5.jpg deleted file mode 100644 index c38baeed7..000000000 Binary files a/public/avatars/5.jpg and /dev/null differ diff --git a/public/avatars/6.jpg b/public/avatars/6.jpg deleted file mode 100644 index 57bbf9685..000000000 Binary files a/public/avatars/6.jpg and /dev/null differ diff --git a/public/avatars/7.jpg b/public/avatars/7.jpg deleted file mode 100644 index dfc20b7a7..000000000 Binary files a/public/avatars/7.jpg and /dev/null differ diff --git a/public/avatars/8.jpg b/public/avatars/8.jpg deleted file mode 100644 index 4e8b48d4f..000000000 Binary files a/public/avatars/8.jpg and /dev/null differ diff --git a/public/avatars/9.jpg b/public/avatars/9.jpg deleted file mode 100644 index f690e78cc..000000000 Binary files a/public/avatars/9.jpg and /dev/null differ diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index bf08304b0..000000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/screenshot/PR template.png b/screenshot/PR template.png new file mode 100644 index 000000000..5306e9117 Binary files /dev/null and b/screenshot/PR template.png differ diff --git a/screenshot/PR.png b/screenshot/PR.png new file mode 100644 index 000000000..67732d5c2 Binary files /dev/null and b/screenshot/PR.png differ diff --git a/screenshot/gcp-arg-step1.png b/screenshot/gcp-arg-step1.png new file mode 100644 index 000000000..8de6cd2a1 Binary files /dev/null and b/screenshot/gcp-arg-step1.png differ diff --git a/screenshot/gcp-arg-step2.png b/screenshot/gcp-arg-step2.png new file mode 100644 index 000000000..a42734c34 Binary files /dev/null and b/screenshot/gcp-arg-step2.png differ diff --git a/screenshot/gcp-arg-step3.png b/screenshot/gcp-arg-step3.png new file mode 100644 index 000000000..789964801 Binary files /dev/null and b/screenshot/gcp-arg-step3.png differ diff --git a/screenshot/gcp-arg-step4.png b/screenshot/gcp-arg-step4.png new file mode 100644 index 000000000..0895fb61f Binary files /dev/null and b/screenshot/gcp-arg-step4.png differ diff --git a/screenshot/heroku-arg.png b/screenshot/heroku-arg.png new file mode 100644 index 000000000..2d75fe03f Binary files /dev/null and b/screenshot/heroku-arg.png differ diff --git a/screenshot/remote-v.png b/screenshot/remote-v.png new file mode 100644 index 000000000..19e824188 Binary files /dev/null and b/screenshot/remote-v.png differ diff --git a/src/App.js b/src/App.js deleted file mode 100644 index 14c614f96..000000000 --- a/src/App.js +++ /dev/null @@ -1,43 +0,0 @@ -import React, { Component } from 'react' -import { HashRouter, Route, Switch } from 'react-router-dom' -import './scss/style.scss' - -const loading = ( - <div className="pt-3 text-center"> - <div className="sk-spinner sk-spinner-pulse"></div> - </div> -) - -// Containers -const DefaultLayout = React.lazy(() => import('./layout/DefaultLayout')) - -// Pages -const Login = React.lazy(() => import('./views/pages/login/Login')) -const Register = React.lazy(() => import('./views/pages/register/Register')) -const Page404 = React.lazy(() => import('./views/pages/page404/Page404')) -const Page500 = React.lazy(() => import('./views/pages/page500/Page500')) - -class App extends Component { - render() { - return ( - <HashRouter> - <React.Suspense fallback={loading}> - <Switch> - <Route exact path="/login" name="Login Page" render={(props) => <Login {...props} />} /> - <Route - exact - path="/register" - name="Register Page" - render={(props) => <Register {...props} />} - /> - <Route exact path="/404" name="Page 404" render={(props) => <Page404 {...props} />} /> - <Route exact path="/500" name="Page 500" render={(props) => <Page500 {...props} />} /> - <Route path="/" name="Home" render={(props) => <DefaultLayout {...props} />} /> - </Switch> - </React.Suspense> - </HashRouter> - ) - } -} - -export default App diff --git a/src/_nav.js b/src/_nav.js deleted file mode 100644 index 45c919892..000000000 --- a/src/_nav.js +++ /dev/null @@ -1,321 +0,0 @@ -import React from 'react' -import CIcon from '@coreui/icons-react' -import { NavLink } from 'react-router-dom' - -const _nav = [ - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Dashboard', - to: '/dashboard', - icon: <CIcon name="cil-speedometer" customClassName="nav-icon" />, - badge: { - color: 'info', - text: 'NEW', - }, - }, - { - _component: 'CNavTitle', - anchor: 'Theme', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Colors', - to: '/theme/colors', - icon: <CIcon name="cil-drop" customClassName="nav-icon" />, - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Typography', - to: '/theme/typography', - icon: <CIcon name="cil-pencil" customClassName="nav-icon" />, - }, - { - _component: 'CNavTitle', - anchor: 'Components', - }, - { - _component: 'CNavGroup', - as: NavLink, - anchor: 'Base', - to: '/to', - icon: <CIcon name="cil-puzzle" customClassName="nav-icon" />, - items: [ - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Accordion', - to: '/base/accordion', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Breadcrumb', - to: '/base/breadcrumbs', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Cards', - to: '/base/cards', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Carousel', - to: '/base/carousels', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Collapse', - to: '/base/collapses', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'List group', - to: '/base/list-groups', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Navs & Tabs', - to: '/base/navs', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Pagination', - to: '/base/paginations', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Popovers', - to: '/base/popovers', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Progress', - to: '/base/progress', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Spinners', - to: '/base/spinners', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Tables', - to: '/base/tables', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Tooltips', - to: '/base/tooltips', - }, - ], - }, - { - _component: 'CNavGroup', - anchor: 'Buttons', - icon: <CIcon name="cil-cursor" customClassName="nav-icon" />, - items: [ - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Buttons', - to: '/buttons/buttons', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Buttons groups', - to: '/buttons/button-groups', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Dropdowns', - to: '/buttons/dropdowns', - }, - ], - }, - { - _component: 'CNavGroup', - anchor: 'Forms', - icon: <CIcon name="cil-notes" customClassName="nav-icon" />, - items: [ - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Form Control', - to: '/forms/form-control', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Select', - to: '/forms/select', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Checks & Radios', - to: '/forms/checks-radios', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Range', - to: '/forms/range', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Input Group', - to: '/forms/input-group', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Floating Labels', - to: '/forms/floating-labels', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Layout', - to: '/forms/layout', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Validation', - to: '/forms/validation', - }, - ], - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Charts', - to: '/charts', - icon: <CIcon name="cil-chart-pie" customClassName="nav-icon" />, - }, - { - _component: 'CNavGroup', - anchor: 'Icons', - icon: <CIcon name="cil-star" customClassName="nav-icon" />, - items: [ - { - _component: 'CNavItem', - as: NavLink, - anchor: 'CoreUI Free', - to: '/icons/coreui-icons', - badge: { - color: 'success', - text: 'NEW', - }, - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'CoreUI Flags', - to: '/icons/flags', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'CoreUI Brands', - to: '/icons/brands', - }, - ], - }, - { - _component: 'CNavGroup', - anchor: 'Notifications', - icon: <CIcon name="cil-bell" customClassName="nav-icon" />, - items: [ - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Alerts', - to: '/notifications/alerts', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Badges', - to: '/notifications/badges', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Modal', - to: '/notifications/modals', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Toasts', - to: '/notifications/toasts', - }, - ], - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Widgets', - to: '/widgets', - icon: <CIcon name="cil-calculator" customClassName="nav-icon" />, - badge: { - color: 'info', - text: 'NEW', - }, - }, - { - _component: 'CNavTitle', - anchor: 'Extras', - }, - { - _component: 'CNavGroup', - anchor: 'Pages', - icon: <CIcon name="cil-star" customClassName="nav-icon" />, - items: [ - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Login', - to: '/login', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Register', - to: '/register', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Error 404', - to: '/404', - }, - { - _component: 'CNavItem', - as: NavLink, - anchor: 'Error 500', - to: '/500', - }, - ], - }, -] - -export default _nav diff --git a/src/components/AppBreadcrumb.js b/src/components/AppBreadcrumb.js deleted file mode 100644 index 006c1a5cd..000000000 --- a/src/components/AppBreadcrumb.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react' -import { useLocation } from 'react-router-dom' - -import routes from '../routes' - -import { CBreadcrumb, CBreadcrumbItem } from '@coreui/react' - -const AppBreadcrumb = () => { - const currentLocation = useLocation().pathname - - const getRouteName = (pathname, routes) => { - const currentRoute = routes.find((route) => route.path === pathname) - return currentRoute.name - } - - const getBreadcrumbs = (location) => { - const breadcrumbs = [] - location.split('/').reduce((prev, curr, index, array) => { - const currentPathname = `${prev}/${curr}` - breadcrumbs.push({ - pathname: currentPathname, - name: getRouteName(currentPathname, routes), - active: index + 1 === array.length ? true : false, - }) - return currentPathname - }) - return breadcrumbs - } - - const breadcrumbs = getBreadcrumbs(currentLocation) - - return ( - <CBreadcrumb className="m-0 ms-2"> - <CBreadcrumbItem href="/">Home</CBreadcrumbItem> - {breadcrumbs.map((breadcrumb, index) => { - return ( - <CBreadcrumbItem - {...(breadcrumb.active ? { active: true } : { href: breadcrumb.pathname })} - key={index} - > - {breadcrumb.name} - </CBreadcrumbItem> - ) - })} - </CBreadcrumb> - ) -} - -export default React.memo(AppBreadcrumb) diff --git a/src/components/AppContent.js b/src/components/AppContent.js deleted file mode 100644 index d048fb8e1..000000000 --- a/src/components/AppContent.js +++ /dev/null @@ -1,37 +0,0 @@ -import React, { Suspense } from 'react' -import { Redirect, Route, Switch } from 'react-router-dom' -import { CContainer, CSpinner } from '@coreui/react' - -// routes config -import routes from '../routes' - -const AppContent = () => { - return ( - <CContainer lg> - <Suspense fallback={<CSpinner color="primary" />}> - <Switch> - {routes.map((route, idx) => { - return ( - route.component && ( - <Route - key={idx} - path={route.path} - exact={route.exact} - name={route.name} - render={(props) => ( - <> - <route.component {...props} /> - </> - )} - /> - ) - ) - })} - <Redirect from="/" to="/dashboard" /> - </Switch> - </Suspense> - </CContainer> - ) -} - -export default React.memo(AppContent) diff --git a/src/components/AppFooter.js b/src/components/AppFooter.js deleted file mode 100644 index b512c1e3a..000000000 --- a/src/components/AppFooter.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import { CFooter } from '@coreui/react' - -const AppFooter = () => { - return ( - <CFooter> - <div> - <a href="https://coreui.io" target="_blank" rel="noopener noreferrer"> - CoreUI - </a> - <span className="ms-1">© 2021 creativeLabs.</span> - </div> - <div className="ms-auto"> - <span className="me-1">Powered by</span> - <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer"> - CoreUI for React - </a> - </div> - </CFooter> - ) -} - -export default React.memo(AppFooter) diff --git a/src/components/AppHeader.js b/src/components/AppHeader.js deleted file mode 100644 index b2a3ba4d4..000000000 --- a/src/components/AppHeader.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react' -import { NavLink } from 'react-router-dom' -import { useSelector, useDispatch } from 'react-redux' -import { - CContainer, - CHeader, - CHeaderBrand, - CHeaderDivider, - CHeaderNav, - CHeaderToggler, - CNavLink, - CNavItem, -} from '@coreui/react' -import CIcon from '@coreui/icons-react' - -import { AppBreadcrumb } from './index' - -import { AppHeaderDropdown } from './header/index' - -const AppHeader = () => { - const dispatch = useDispatch() - const sidebarShow = useSelector((state) => state.sidebarShow) - - return ( - <CHeader position="sticky" className="mb-4"> - <CContainer fluid> - <CHeaderToggler - className="ms-md-3 d-lg-none" - onClick={() => dispatch({ type: 'set', sidebarShow: !sidebarShow })} - > - <CIcon name="cil-menu" size="lg" /> - </CHeaderToggler> - <CHeaderBrand className="mx-auto d-md-none" to="/"> - <CIcon name="logo" height="48" alt="Logo" /> - </CHeaderBrand> - <CHeaderNav className="d-none d-md-flex me-auto"> - <CNavItem> - <CNavLink to="/dashboard" component={NavLink} activeClassName="active"> - Dashboard - </CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href="#">Users</CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href="#">Settings</CNavLink> - </CNavItem> - </CHeaderNav> - <CHeaderNav> - <CNavItem> - <CNavLink href="#"> - <CIcon name="cil-bell" size="lg" /> - </CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href="#"> - <CIcon name="cil-list" size="lg" /> - </CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href="#"> - <CIcon name="cil-envelope-open" size="lg" /> - </CNavLink> - </CNavItem> - </CHeaderNav> - <CHeaderNav className="ms-3"> - <AppHeaderDropdown /> - </CHeaderNav> - </CContainer> - <CHeaderDivider /> - <CContainer fluid> - <AppBreadcrumb /> - </CContainer> - </CHeader> - ) -} - -export default AppHeader diff --git a/src/components/AppSidebar.js b/src/components/AppSidebar.js deleted file mode 100644 index c48a0614e..000000000 --- a/src/components/AppSidebar.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react' -import { useSelector, useDispatch } from 'react-redux' - -import { - CSidebar, - CSidebarBrand, - CSidebarNav, - CSidebarToggler, - CCreateNavItem, -} from '@coreui/react' - -import CIcon from '@coreui/icons-react' - -import SimpleBar from 'simplebar-react' -import 'simplebar/dist/simplebar.min.css' - -// sidebar nav config -import navigation from '../_nav' - -const AppSidebar = () => { - const dispatch = useDispatch() - const unfoldable = useSelector((state) => state.sidebarUnfoldable) - const sidebarShow = useSelector((state) => state.sidebarShow) - - return ( - <CSidebar - position="fixed" - selfHiding="md" - unfoldable={unfoldable} - show={sidebarShow} - onShow={() => console.log('show')} - onHide={() => { - dispatch({ type: 'set', sidebarShow: false }) - }} - > - <CSidebarBrand className="d-none d-md-flex" to="/"> - <CIcon className="sidebar-brand-full" name="logo-negative" height={35} /> - <CIcon className="sidebar-brand-narrow" name="sygnet" height={35} /> - </CSidebarBrand> - <CSidebarNav> - <SimpleBar> - <CCreateNavItem items={navigation} /> - </SimpleBar> - </CSidebarNav> - <CSidebarToggler - className="d-none d-lg-flex" - onClick={() => dispatch({ type: 'set', sidebarUnfoldable: !unfoldable })} - /> - </CSidebar> - ) -} - -export default React.memo(AppSidebar) diff --git a/src/components/header/AppHeaderDropdown.js b/src/components/header/AppHeaderDropdown.js deleted file mode 100644 index 023e41fba..000000000 --- a/src/components/header/AppHeaderDropdown.js +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react' -import { - CAvatar, - CBadge, - CDropdown, - CDropdownDivider, - CDropdownHeader, - CDropdownItem, - CDropdownMenu, - CDropdownToggle, -} from '@coreui/react' -import CIcon from '@coreui/icons-react' - -const AppHeaderDropdown = () => { - return ( - <CDropdown variant="nav-item"> - <CDropdownToggle placement="bottom-end" className="py-0" caret={false}> - <CAvatar src="/avatars/8.jpg" size="md" /> - </CDropdownToggle> - <CDropdownMenu className="pt-0" placement="bottom-end"> - <CDropdownHeader className="bg-light fw-semibold py-2">Account</CDropdownHeader> - <CDropdownItem href="#"> - <CIcon name="cil-bell" className="me-2" /> - Updates - <CBadge color="info" className="ms-2"> - 42 - </CBadge> - </CDropdownItem> - <CDropdownItem href="#"> - <CIcon name="cil-envelope-open" className="me-2" /> - Messages - <CBadge color="success" className="ms-2"> - 42 - </CBadge> - </CDropdownItem> - <CDropdownItem href="#"> - <CIcon name="cil-task" className="me-2" /> - Tasks - <CBadge color="danger" className="ms-2"> - 42 - </CBadge> - </CDropdownItem> - <CDropdownItem href="#"> - <CIcon name="cil-comment-square" className="me-2" /> - Comments - <CBadge color="warning" className="ms-2"> - 42 - </CBadge> - </CDropdownItem> - <CDropdownHeader className="bg-light fw-semibold py-2">Settings</CDropdownHeader> - <CDropdownItem href="#"> - <CIcon name="cil-user" className="me-2" /> - Profile - </CDropdownItem> - <CDropdownItem href="#"> - <CIcon name="cil-settings" className="me-2" /> - Settings - </CDropdownItem> - <CDropdownItem href="#"> - <CIcon name="cil-credit-card" className="me-2" /> - Payments - <CBadge color="secondary" className="ms-2"> - 42 - </CBadge> - </CDropdownItem> - <CDropdownItem href="#"> - <CIcon name="cil-file" className="me-2" /> - Projects - <CBadge color="primary" className="ms-2"> - 42 - </CBadge> - </CDropdownItem> - <CDropdownDivider /> - <CDropdownItem href="#"> - <CIcon name="cil-lock-locked" className="me-2" /> - Lock Account - </CDropdownItem> - </CDropdownMenu> - </CDropdown> - ) -} - -export default AppHeaderDropdown diff --git a/src/components/index.js b/src/components/index.js deleted file mode 100644 index a4a12654e..000000000 --- a/src/components/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import AppBreadcrumb from './AppBreadcrumb' -import AppContent from './AppContent' -import AppFooter from './AppFooter' -import AppHeader from './AppHeader' -import AppHeaderDropdown from './header/AppHeaderDropdown' -import AppSidebar from './AppSidebar' - -export { AppBreadcrumb, AppContent, AppFooter, AppHeader, AppHeaderDropdown, AppSidebar } diff --git a/src/reusable/DocsCallout.js b/src/reusable/DocsCallout.js deleted file mode 100644 index 63146a544..000000000 --- a/src/reusable/DocsCallout.js +++ /dev/null @@ -1,35 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { CCallout, CLink } from '@coreui/react' - -import packageJson from '../../package.json' - -const DocsCallout = (props) => { - const { href, name } = props - - const plural = name.slice(-1) === 's' ? true : false - - const _href = `https://coreui.io/react/docs/${packageJson.config.coreui_library_short_version}/${href}` - - return ( - <CCallout color="info" className="bg-white"> - A React {name} component {plural ? 'have' : 'has'} been created as a native React.js version - of Bootstrap {name}. {name} {plural ? 'are' : 'is'} delivered with some new features, - variants, and unique design that matches CoreUI Design System requirements. - <br /> - <br /> - For more information please visit our official{' '} - <CLink href={_href} target="_blank"> - documentation of CoreUI Components Library for React.js - </CLink> - . - </CCallout> - ) -} - -DocsCallout.propTypes = { - href: PropTypes.string, - name: PropTypes.string, -} - -export default React.memo(DocsCallout) diff --git a/src/reusable/DocsLink.js b/src/reusable/DocsLink.js deleted file mode 100644 index b5cdb97e1..000000000 --- a/src/reusable/DocsLink.js +++ /dev/null @@ -1,31 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { CLink } from '@coreui/react' - -const DocsLink = (props) => { - const { href, name, text, ...rest } = props - - const _href = name ? `https://coreui.io/react/docs/components/${name}` : href - - return ( - <div className="float-end"> - <CLink - {...rest} - href={_href} - rel="noreferrer noopener" - target="_blank" - className="card-header-action" - > - <small className="text-medium-emphasis">{text || 'docs'}</small> - </CLink> - </div> - ) -} - -DocsLink.propTypes = { - href: PropTypes.string, - name: PropTypes.string, - text: PropTypes.string, -} - -export default React.memo(DocsLink) diff --git a/src/reusable/Example.js b/src/reusable/Example.js deleted file mode 100644 index 789d5007e..000000000 --- a/src/reusable/Example.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { CNav, CNavItem, CNavLink, CTabContent, CTabPane } from '@coreui/react' -import CIcon from '@coreui/icons-react' - -import packageJson from '../../package.json' - -const Example = (props) => { - const { children, href } = props - - const _href = `https://coreui.io/react/docs/${packageJson.config.coreui_library_short_version}/${href}` - - return ( - <div className="example"> - <CNav variant="tabs"> - <CNavItem> - <CNavLink href="#" active> - <CIcon name="cil-media-play" className="me-2" /> - Preview - </CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href={_href} target="_blank"> - <CIcon name="cil-code" className="me-2" /> - Code - </CNavLink> - </CNavItem> - </CNav> - <CTabContent className="rounded-bottom"> - <CTabPane className="p-3 preview" visible> - {children} - </CTabPane> - </CTabContent> - </div> - ) -} - -Example.propTypes = { - children: PropTypes.node, - href: PropTypes.string, -} - -export default React.memo(Example) diff --git a/src/reusable/index.js b/src/reusable/index.js deleted file mode 100644 index 098eab51a..000000000 --- a/src/reusable/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import DocsCallout from './DocsCallout' -import DocsLink from './DocsLink' -import Example from './Example' - -export { DocsCallout, DocsLink, Example } diff --git a/src/routes.js b/src/routes.js deleted file mode 100644 index f0b717e51..000000000 --- a/src/routes.js +++ /dev/null @@ -1,108 +0,0 @@ -import React from 'react' - -// examples - -const Colors = React.lazy(() => import('./views/theme/colors/Colors')) -const Typography = React.lazy(() => import('./views/theme/typography/Typography')) - -const Accordion = React.lazy(() => import('./views/components/base/accordion/Accordion')) -const Breadcrumbs = React.lazy(() => import('./views/components/base/breadcrumbs/Breadcrumbs')) -const Cards = React.lazy(() => import('./views/components/base/cards/Cards')) -const Carousels = React.lazy(() => import('./views/components/base/carousels/Carousels')) -const Collapses = React.lazy(() => import('./views/components/base/collapses/Collapses')) -const ListGroups = React.lazy(() => import('./views/components/base/list-groups/ListGroups')) -const Navs = React.lazy(() => import('./views/components/base/navs/Navs')) -const Paginations = React.lazy(() => import('./views/components/base/paginations/Paginations')) -const Popovers = React.lazy(() => import('./views/components/base/popovers/Popovers')) -const Progress = React.lazy(() => import('./views/components/base/progress/Progress')) -const Spinners = React.lazy(() => import('./views/components/base/spinners/Spinners')) -const Tables = React.lazy(() => import('./views/components/base/tables/Tables')) -const Tooltips = React.lazy(() => import('./views/components/base/tooltips/Tooltips')) - -const Buttons = React.lazy(() => import('./views/components/buttons/buttons/Buttons')) -const ButtonGroups = React.lazy(() => - import('./views/components/buttons/button-groups/ButtonGroups'), -) -const Dropdowns = React.lazy(() => import('./views/components/buttons/dropdowns/Dropdowns')) - -const ChecksRadios = React.lazy(() => import('./views/components/forms/checks-radios/ChecksRadios')) -const FloatingLabels = React.lazy(() => - import('./views/components/forms/floating-labels/FloatingLabels'), -) -const FormControl = React.lazy(() => import('./views/components/forms/form-control/FormControl')) -const InputGroup = React.lazy(() => import('./views/components/forms/input-group/InputGroup')) -const Layout = React.lazy(() => import('./views/components/forms/layout/Layout')) -const Range = React.lazy(() => import('./views/components/forms/range/Range')) -const Select = React.lazy(() => import('./views/components/forms/select/Select')) -const Validation = React.lazy(() => import('./views/components/forms/validation/Validation')) - -const CoreUIIcons = React.lazy(() => import('./views/components/icons/coreui-icons/CoreUIIcons')) -const Flags = React.lazy(() => import('./views/components/icons/flags/Flags')) -const Brands = React.lazy(() => import('./views/components/icons/brands/Brands')) - -const Alerts = React.lazy(() => import('./views/components/notifications/alerts/Alerts')) -const Badges = React.lazy(() => import('./views/components/notifications/badges/Badges')) -const Modals = React.lazy(() => import('./views/components/notifications/modals/Modals')) -const Toasts = React.lazy(() => import('./views/components/notifications/toasts/Toasts')) - -// const Login = React.lazy(() => import('./views/examples/pages/login/Login')) -// const Register = React.lazy(() => import('./views/examples/pages/register/Register')) -// const Page404 = React.lazy(() => import('./views/examples/pages/page404/Page404')) -// const Page500 = React.lazy(() => import('./views/examples/pages/page500/Page500')) - -const Widgets = React.lazy(() => import('./views/components/widgets/Widgets')) - -const Charts = React.lazy(() => import('./views/components/charts/Charts')) -const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard')) - -const routes = [ - { path: '/', exact: true, name: 'Home' }, - { path: '/dashboard', name: 'Dashboard', component: Dashboard }, - { path: '/theme', name: 'Theme', component: Colors, exact: true }, - { path: '/theme/colors', name: 'Colors', component: Colors }, - { path: '/theme/typography', name: 'Typography', component: Typography }, - { path: '/base', name: 'Base', component: Cards, exact: true }, - { path: '/base/accordion', name: 'Accordion', component: Accordion }, - { path: '/base/breadcrumbs', name: 'Breadcrumbs', component: Breadcrumbs }, - { path: '/base/cards', name: 'Cards', component: Cards }, - { path: '/base/carousels', name: 'Carousel', component: Carousels }, - { path: '/base/collapses', name: 'Collapse', component: Collapses }, - { path: '/base/list-groups', name: 'List Groups', component: ListGroups }, - { path: '/base/navs', name: 'Navs', component: Navs }, - { path: '/base/paginations', name: 'Paginations', component: Paginations }, - { path: '/base/popovers', name: 'Popovers', component: Popovers }, - { path: '/base/progress', name: 'Progress', component: Progress }, - { path: '/base/spinners', name: 'Spinners', component: Spinners }, - { path: '/base/tables', name: 'Tables', component: Tables }, - { path: '/base/tooltips', name: 'Tooltips', component: Tooltips }, - { path: '/buttons', name: 'Buttons', component: Buttons, exact: true }, - { path: '/buttons/buttons', name: 'Buttons', component: Buttons }, - { path: '/buttons/dropdowns', name: 'Dropdowns', component: Dropdowns }, - { path: '/buttons/button-groups', name: 'Button Groups', component: ButtonGroups }, - { path: '/charts', name: 'Charts', component: Charts }, - { path: '/forms', name: 'Forms', component: FormControl, exact: true }, - { path: '/forms/form-control', name: 'Form Control', component: FormControl }, - { path: '/forms/select', name: 'Select', component: Select }, - { path: '/forms/checks-radios', name: 'Checks & Radios', component: ChecksRadios }, - { path: '/forms/range', name: 'Range', component: Range }, - { path: '/forms/input-group', name: 'Input Group', component: InputGroup }, - { path: '/forms/floating-labels', name: 'Floating Labels', component: FloatingLabels }, - { path: '/forms/layout', name: 'Layout', component: Layout }, - { path: '/forms/validation', name: 'Validation', component: Validation }, - { path: '/icons', exact: true, name: 'Icons', component: CoreUIIcons }, - { path: '/icons/coreui-icons', name: 'CoreUI Icons', component: CoreUIIcons }, - { path: '/icons/flags', name: 'Flags', component: Flags }, - { path: '/icons/brands', name: 'Brands', component: Brands }, - { path: '/notifications', name: 'Notifications', component: Alerts, exact: true }, - { path: '/notifications/alerts', name: 'Alerts', component: Alerts }, - { path: '/notifications/badges', name: 'Badges', component: Badges }, - { path: '/notifications/modals', name: 'Modals', component: Modals }, - { path: '/notifications/toasts', name: 'Toasts', component: Toasts }, - // { path: '/login', name: 'Login', component: Login }, - // { path: '/register', name: 'Register', component: Register }, - // { path: '/404', name: '404', component: Page404 }, - // { path: '/500', name: '500', component: Page500 }, - { path: '/widgets', name: 'Widgets', component: Widgets }, -] - -export default routes diff --git a/src/scss/_custom.scss b/src/scss/_custom.scss deleted file mode 100644 index 15d367af4..000000000 --- a/src/scss/_custom.scss +++ /dev/null @@ -1 +0,0 @@ -// Here you can add other styles diff --git a/src/scss/_example.scss b/src/scss/_example.scss deleted file mode 100644 index f8791fb21..000000000 --- a/src/scss/_example.scss +++ /dev/null @@ -1,109 +0,0 @@ -.example { - &:not(:first-child) { - margin-top: 1.5rem; - } - - .tab-content { - background-color: $light-50 !important; - - @at-root .dark-theme & { - background-color: rgba(255, 255, 255, .1) !important; - } - } - - code[class*="language-"], - pre[class*="language-"] { - font-size: .875rem !important; - } - - :not(pre) > code[class*="language-"], - pre[class*="language-"] { - background: transparent; - } - - & + p { - margin-top: 1.5rem - } - - // Components examples - .preview, - .preview .col { - + p { - margin-top: 2rem; - } - - > .form-control { - + .form-control { - margin-top: .5rem; - } - } - - > .nav + .nav, - > .alert + .alert, - > .navbar + .navbar, - > .progress + .progress { - margin-top: 1rem; - } - - > .dropdown-menu { - position: static; - display: block; - } - - > :last-child { - margin-bottom: 0; - } - - // Images - > svg + svg, - > img + img { - margin-left: .5rem; - } - - // Buttons - > .btn, - > .btn-group { - margin: .25rem .125rem; - } - > .btn-toolbar + .btn-toolbar { - margin-top: .5rem; - } - - // List groups - > .list-group { - max-width: 400px; - } - - > [class*="list-group-horizontal"] { - max-width: 100%; - } - - // Navbars - .fixed-top, - .sticky-top { - position: static; - margin: -1rem -1rem 1rem; - } - - .fixed-bottom { - position: static; - margin: 1rem -1rem -1rem; - } - - @include media-breakpoint-up(sm) { - .fixed-top, - .sticky-top { - margin: -1.5rem -1.5rem 1rem; - } - .fixed-bottom { - margin: 1rem -1.5rem -1.5rem; - } - } - - // Pagination - .pagination { - margin-top: .5rem; - margin-bottom: .5rem; - } - } -} diff --git a/src/scss/_layout.scss b/src/scss/_layout.scss deleted file mode 100644 index 38bfe8c45..000000000 --- a/src/scss/_layout.scss +++ /dev/null @@ -1,6 +0,0 @@ -.wrapper { - width: 100%; - @include ltr-rtl("padding-left", var(--cui-sidebar-occupy-start, 0)); - will-change: auto; - @include transition(padding .15s); -} diff --git a/src/scss/style.scss b/src/scss/style.scss deleted file mode 100644 index 02f199c40..000000000 --- a/src/scss/style.scss +++ /dev/null @@ -1,17 +0,0 @@ -// If you want to override variables do it here -@import "variables"; - -$enable-ltr: true; -$enable-rtl: true; - -// Import CoreUI for React components library -@import "@coreui/coreui/scss/coreui"; - -// Import Chart.js custom tooltips styles -@import "@coreui/chartjs/scss/coreui-chartjs"; - -@import "layout"; -@import "example"; - -// If you want to add custom CSS you can put it here. -@import "custom"; diff --git a/src/views/components/base/accordion/Accordion.js b/src/views/components/base/accordion/Accordion.js deleted file mode 100644 index 53cfa20b3..000000000 --- a/src/views/components/base/accordion/Accordion.js +++ /dev/null @@ -1,188 +0,0 @@ -import React, { useState } from 'react' -import { - CCard, - CCardBody, - CCardHeader, - CCol, - CRow, - CAccordion, - CAccordionBody, - CAccordionButton, - CAccordionCollapse, - CAccordionHeader, - CAccordionItem, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Accordion = () => { - const [activeKey, setActiveKey] = useState(0) - const [activeKey2, setActiveKey2] = useState(0) - - return ( - <CRow> - <CCol xs={12}> - <DocsCallout name="Accordion" href="components/accordion" /> - </CCol> - <CCol xs={12}> - <CCard className="mb-4"> - <CCardHeader> - <strong>React Accordion</strong> - </CCardHeader> - <CCardBody> - <p className="text-medium-emphasis small"> - Click the accordions below to expand/collapse the accordion content. - </p> - <Example href="components/accordion"> - <CAccordion> - <CAccordionItem> - <CAccordionHeader> - <CAccordionButton - collapsed={activeKey !== 1} - onClick={() => (activeKey === 1 ? setActiveKey(0) : setActiveKey(1))} - > - Accordion Item #1 - </CAccordionButton> - </CAccordionHeader> - <CAccordionCollapse visible={activeKey === 1}> - <CAccordionBody> - <strong>This is the first item's accordion body.</strong> It is hidden by - default, until the collapse plugin adds the appropriate classes that we use to - style each element. These classes control the overall appearance, as well as - the showing and hiding via CSS transitions. You can modify any of this with - custom CSS or overriding our default variables. It's also worth noting - that just about any HTML can go within the <code>.accordion-body</code>, - though the transition does limit overflow. - </CAccordionBody> - </CAccordionCollapse> - </CAccordionItem> - <CAccordionItem> - <CAccordionHeader> - <CAccordionButton - collapsed={activeKey !== 2} - onClick={() => (activeKey === 2 ? setActiveKey(0) : setActiveKey(2))} - > - Accordion Item #2 - </CAccordionButton> - </CAccordionHeader> - <CAccordionCollapse visible={activeKey === 2}> - <CAccordionBody> - <strong>This is the second item's accordion body.</strong> It is hidden by - default, until the collapse plugin adds the appropriate classes that we use to - style each element. These classes control the overall appearance, as well as - the showing and hiding via CSS transitions. You can modify any of this with - custom CSS or overriding our default variables. It's also worth noting - that just about any HTML can go within the <code>.accordion-body</code>, - though the transition does limit overflow. - </CAccordionBody> - </CAccordionCollapse> - </CAccordionItem> - <CAccordionItem> - <CAccordionHeader> - <CAccordionButton - collapsed={activeKey !== 3} - onClick={() => (activeKey === 3 ? setActiveKey(0) : setActiveKey(3))} - > - Accordion Item #3 - </CAccordionButton> - </CAccordionHeader> - <CAccordionCollapse visible={activeKey === 3}> - <CAccordionBody> - <strong>This is the third item's accordion body.</strong> It is hidden by - default, until the collapse plugin adds the appropriate classes that we use to - style each element. These classes control the overall appearance, as well as - the showing and hiding via CSS transitions. You can modify any of this with - custom CSS or overriding our default variables. It's also worth noting - that just about any HTML can go within the <code>.accordion-body</code>, - though the transition does limit overflow. - </CAccordionBody> - </CAccordionCollapse> - </CAccordionItem> - </CAccordion> - </Example> - </CCardBody> - </CCard> - <CCard className="mb-4"> - <CCardHeader> - <strong>React Accordion</strong> <small>Flush</small> - </CCardHeader> - <CCardBody> - <p className="text-medium-emphasis small"> - Add <code>flush</code> to remove the default <code>background-color</code>, some - borders, and some rounded corners to render accordions edge-to-edge with their parent - container. - </p> - <Example href="components/accordion#flush"> - <CAccordion flush> - <CAccordionItem> - <CAccordionHeader> - <CAccordionButton - collapsed={activeKey2 !== 1} - onClick={() => (activeKey2 === 1 ? setActiveKey2(0) : setActiveKey2(1))} - > - Accordion Item #1 - </CAccordionButton> - </CAccordionHeader> - <CAccordionCollapse visible={activeKey2 === 1}> - <CAccordionBody> - <strong>This is the first item's accordion body.</strong> It is hidden by - default, until the collapse plugin adds the appropriate classes that we use to - style each element. These classes control the overall appearance, as well as - the showing and hiding via CSS transitions. You can modify any of this with - custom CSS or overriding our default variables. It's also worth noting - that just about any HTML can go within the <code>.accordion-body</code>, - though the transition does limit overflow. - </CAccordionBody> - </CAccordionCollapse> - </CAccordionItem> - <CAccordionItem> - <CAccordionHeader> - <CAccordionButton - collapsed={activeKey2 !== 2} - onClick={() => (activeKey2 === 2 ? setActiveKey2(0) : setActiveKey2(2))} - > - Accordion Item #2 - </CAccordionButton> - </CAccordionHeader> - <CAccordionCollapse visible={activeKey2 === 2}> - <CAccordionBody> - <strong>This is the second item's accordion body.</strong> It is hidden by - default, until the collapse plugin adds the appropriate classes that we use to - style each element. These classes control the overall appearance, as well as - the showing and hiding via CSS transitions. You can modify any of this with - custom CSS or overriding our default variables. It's also worth noting - that just about any HTML can go within the <code>.accordion-body</code>, - though the transition does limit overflow. - </CAccordionBody> - </CAccordionCollapse> - </CAccordionItem> - <CAccordionItem> - <CAccordionHeader> - <CAccordionButton - collapsed={activeKey2 !== 3} - onClick={() => (activeKey2 === 3 ? setActiveKey2(0) : setActiveKey2(3))} - > - Accordion Item #3 - </CAccordionButton> - </CAccordionHeader> - <CAccordionCollapse visible={activeKey2 === 3}> - <CAccordionBody> - <strong>This is the third item's accordion body.</strong> It is hidden by - default, until the collapse plugin adds the appropriate classes that we use to - style each element. These classes control the overall appearance, as well as - the showing and hiding via CSS transitions. You can modify any of this with - custom CSS or overriding our default variables. It's also worth noting - that just about any HTML can go within the <code>.accordion-body</code>, - though the transition does limit overflow. - </CAccordionBody> - </CAccordionCollapse> - </CAccordionItem> - </CAccordion> - </Example> - </CCardBody> - </CCard> - </CCol> - </CRow> - ) -} - -export default Accordion diff --git a/src/views/components/base/breadcrumbs/Breadcrumbs.js b/src/views/components/base/breadcrumbs/Breadcrumbs.js deleted file mode 100644 index fc64dbe56..000000000 --- a/src/views/components/base/breadcrumbs/Breadcrumbs.js +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react' -import { - CBreadcrumb, - CBreadcrumbItem, - CCard, - CCardBody, - CCardHeader, - CCol, - CRow, - CLink, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Breadcrumbs = () => { - return ( - <CRow> - <CCol xs={12}> - <DocsCallout name="Breadcrumb" href="components/breadcrumb" /> - </CCol> - <CCol xs={12}> - <CCard className="mb-4"> - <CCardHeader> - <strong>React Breadcrumb</strong> - </CCardHeader> - <CCardBody> - <p className="text-medium-emphasis small"> - The breadcrumb navigation provides links back to each previous page the user navigated - through and shows the current location in a website or an application. You don’t have - to add separators, because they automatically added in CSS through{' '} - <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::before"> - {' '} - <code>::before</code> - </a>{' '} - and{' '} - <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/content"> - {' '} - <code>content</code> - </a> - . - </p> - <Example href="components/breadcrumb"> - <CBreadcrumb> - <CBreadcrumbItem> - <CLink href="#">Home</CLink> - </CBreadcrumbItem> - <CBreadcrumbItem active>Library</CBreadcrumbItem> - </CBreadcrumb> - <CBreadcrumb> - <CBreadcrumbItem> - <CLink href="#">Home</CLink> - </CBreadcrumbItem> - <CBreadcrumbItem> - <CLink href="#">Library</CLink> - </CBreadcrumbItem> - <CBreadcrumbItem active>Data</CBreadcrumbItem> - </CBreadcrumb> - <CBreadcrumb> - <CBreadcrumbItem> - <CLink href="#">Home</CLink> - </CBreadcrumbItem> - <CBreadcrumbItem> - <CLink href="#">Library</CLink> - </CBreadcrumbItem> - <CBreadcrumbItem> - <CLink href="#">Data</CLink> - </CBreadcrumbItem> - <CBreadcrumbItem active>Bootstrap</CBreadcrumbItem> - </CBreadcrumb> - </Example> - </CCardBody> - </CCard> - </CCol> - </CRow> - ) -} - -export default Breadcrumbs diff --git a/src/views/components/base/cards/Cards.js b/src/views/components/base/cards/Cards.js deleted file mode 100644 index 5ea9161b6..000000000 --- a/src/views/components/base/cards/Cards.js +++ /dev/null @@ -1,1230 +0,0 @@ -import React from 'react' -import { - CButton, - CCard, - CCardBody, - CCardFooter, - CCardGroup, - CCardHeader, - CCardImage, - CCardLink, - CCardSubtitle, - CCardText, - CCardTitle, - CListGroup, - CListGroupItem, - CNav, - CNavItem, - CNavLink, - CCol, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Cards = () => { - return ( - <CRow> - <CCol xs={12}> - <DocsCallout name="Card" href="components/card" /> - </CCol> - <CCol xs={12}> - <CCard className="mb-4"> - <CCardHeader> - <strong>Card</strong> <small>Example</small> - </CCardHeader> - <CCardBody> - <p className="text-medium-emphasis small"> - Cards are built with as little markup and styles as possible but still manage to - deliver a bunch of control and customization. Built with flexbox, they offer easy - alignment and mix well with other CoreUI components. Cards have no top, left, and - right margins by default, so use{' '} - <a href="https://coreui.io/docs/utilities/spacing">spacing utilities</a> as needed. - They have no fixed width to start, so they'll fill the full width of its parent. - </p> - <p className="text-medium-emphasis small"> - Below is an example of a basic card with mixed content and a fixed width. Cards have - no fixed width to start, so they'll naturally fill the full width of its parent - element. - </p> - <Example href="components/card"> - <CCard style={{ width: '18rem' }}> - <CCardImage - component="svg" - orientation="top" - className="docs-placeholder-img" - width="100%" - height="180" - xmlns="http://www.w3.org/2000/svg" - role="img" - aria-label="Placeholder: Image cap" - preserveAspectRatio="xMidYMid slice" - focusable="false" - > - <title>Placeholder - - - Image cap - - - - Card title - - Some quick example text to build on the card title and make up the bulk of the - card's content. - - Go somewhere - - - - - - - - - - Card Body - - -

    - The main block of a card is the <CCardBody>. Use it whenever you - need a padded section within a card. -

    - - - This is some text within a card body. - - -
    -
    -
    - - - - Card Titles, text, and links - - -

    - Card titles are managed by <CCardTitle> component. Identically, - links are attached and collected next to each other by <CCardLink>{' '} - component. -

    -

    - Subtitles are managed by <CCardSubtitle> component. If the{' '} - <CCardTitle> also, the <CCardSubtitle> items are - stored in a <CCardBody> item, the card title, and subtitle are - arranged rightly. -

    - - - - Card title - Card subtitle - - Some quick example text to build on the card title and make up the bulk of the - card's content. - - Card link - Another link - - - -
    -
    -
    - - - - Card Images - - -

    - .card-img-top places a picture to the top of the card. With{' '} - .card-text, text can be added to the card. Text within{' '} - .card-text can additionally be styled with the regular HTML tags. -

    - - - - Placeholder - - - Image cap - - - - - Some quick example text to build on the card title and make up the bulk of the - card's content. - - - - -
    -
    -
    - - - - Card List groups - - -

    - Create lists of content in a card with a flush list group. -

    - - - - - - Cras justo odio - Dapibus ac facilisis in - Vestibulum at eros - - - - - - Header - - Cras justo odio - Dapibus ac facilisis in - Vestibulum at eros - - - - - - - Cras justo odio - Dapibus ac facilisis in - Vestibulum at eros - - Footer - - - - -
    -
    -
    - - - - Card Kitchen sink - - -

    - Combine and match many content types to build the card you need, or throw everything - in there. Shown below are image styles, blocks, text styles, and a list group—all - wrapped in a fixed-width card. -

    - - - - Placeholder - - - Image cap - - - - Card title - - Some quick example text to build on the card title and make up the bulk of the - card's content. - - - - Cras justo odio - Dapibus ac facilisis in - Vestibulum at eros - - - Card link - Another link - - - -
    -
    -
    - - - - Card Header and footer - - -

    - Add an optional header and/or footer within a card. -

    - - - Header - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - -

    - Card headers can be styled by adding ex. component="h5". -

    - - - Header - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - - - Quote - -
    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat - a ante. -

    -
    - Someone famous in Source Title -
    -
    -
    -
    -
    - - - Header - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - 2 days ago - - -
    -
    -
    - - - - Card Body - - -

    - Cards assume no specific width to start, so they'll be 100% wide - unless otherwise stated. You can adjust this as required with custom CSS, grid - classes, grid Sass mixins, or services. -

    -

    Using grid markup

    -

    - Using the grid, wrap cards in columns and rows as needed. -

    - - - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - - -

    Using utilities

    -

    - Use some of{' '} - available sizing utilities to - rapidly set a card's width. -

    - - - - Card title - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - - Card title - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - Using custom CSS -

    - Use custom CSS in your stylesheets or as inline styles to set a width. -

    - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - -
    -
    -
    - - - - Card Text alignment - - -

    - You can instantly change the text arrangement of any card—in its whole or specific - parts—with{' '} - text align classes - . -

    - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - -
    -
    -
    - - - - Card Navigation - - -

    - Add some navigation to a <CCardHeader> with our{' '} - <CNav> component. -

    - - - - - - - Active - - - - Link - - - - Disabled - - - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - - - - - - - - Active - - - - Link - - - - Disabled - - - - - - Special title treatment - - With supporting text below as a natural lead-in to additional content. - - Go somewhere - - - -
    -
    -
    - - - - Card Image caps - - -

    - Similar to headers and footers, cards can include top and bottom "image - caps"—images at the top or bottom of a card. -

    - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - Last updated 3 mins ago - - - - - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - Last updated 3 mins ago - - - - Placeholder - - - Image cap - - - - - - -
    -
    -
    - - - - Card Card styles - - -

    - Cards include various options for customizing their backgrounds, borders, and color. -

    -

    Background and color

    -

    - Use color property to change the appearance of a card. -

    - - - {[ - { color: 'primary', textColor: 'white' }, - { color: 'secondary', textColor: 'white' }, - { color: 'success', textColor: 'white' }, - { color: 'danger', textColor: 'white' }, - { color: 'warning' }, - { color: 'info', textColor: 'white' }, - { color: 'light' }, - { color: 'dark', textColor: 'white' }, - ].map((item, index) => ( - - - Header - - {item.color} card title - - Some quick example text to build on the card title and make up the bulk of - the card's content. - - - - - ))} - - -

    Border

    -

    - Use border utilities to change - just the border-color of a card. Note that you can set{' '} - textColor property on the <CCard> or a subset of the - card's contents as shown below. -

    - - - {[ - { color: 'primary', textColor: 'primary' }, - { color: 'secondary', textColor: 'secondary' }, - { color: 'success', textColor: 'success' }, - { color: 'danger', textColor: 'danger' }, - { color: 'warning', textColor: 'warning' }, - { color: 'info', textColor: 'info' }, - { color: 'light' }, - { color: 'dark' }, - ].map((item, index) => ( - - - Header - - {item.color} card title - - Some quick example text to build on the card title and make up the bulk of - the card's content. - - - - - ))} - - -

    Top border

    -

    - Use border utilities to change - just the border-color of a card. Note that you can set{' '} - textColor property on the <CCard> or a subset of the - card's contents as shown below. -

    - - - {[ - { color: 'primary', textColor: 'primary' }, - { color: 'secondary', textColor: 'secondary' }, - { color: 'success', textColor: 'success' }, - { color: 'danger', textColor: 'danger' }, - { color: 'warning', textColor: 'warning' }, - { color: 'info', textColor: 'info' }, - { color: 'light' }, - { color: 'dark' }, - ].map((item, index) => ( - - - Header - - {item.color} card title - - Some quick example text to build on the card title and make up the bulk of - the card's content. - - - - - ))} - - -
    -
    -
    - - - - Card Card groups - - -

    - Use card groups to render cards as a single, attached element with equal width and - height columns. Card groups start off stacked and use display: flex; to - become attached with uniform dimensions starting at the sm breakpoint. -

    - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - Last updated 3 mins ago - - - - - - Placeholder - - - Image cap - - - - Card title - - This card has supporting text below as a natural lead-in to additional - content. - - - Last updated 3 mins ago - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This card has even longer content than the first to show - that equal height action. - - - Last updated 3 mins ago - - - - - -

    - When using card groups with footers, their content will automatically line up. -

    - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - Placeholder - - - Image cap - - - - Card title - - This card has supporting text below as a natural lead-in to additional - content. - - - - Last updated 3 mins ago - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This card has even longer content than the first to show - that equal height action. - - - - Last updated 3 mins ago - - - - -
    -
    -
    - - - - Card Grid cards - - -

    - Use the CRow component and set{' '} - {xs|sm|md|lg|xl|xxl}={{ cols: * }} property - to control how many grid columns (wrapped around your cards) you show per row. For - example, here's xs={{cols: 1}} laying out the - cards on one column, and md={{cols: 1}} splitting - four cards to equal width across multiple rows, from the medium breakpoint up. -

    - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - -

    - Change it to md={{ cols: 3}} and you'll see the - fourth card wrap. -

    - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - - - Placeholder - - - Image cap - - - - Card title - - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - - - - Last updated 3 mins ago - - - - - -
    -
    -
    - - ) -} - -export default Cards diff --git a/src/views/components/base/carousels/Carousels.js b/src/views/components/base/carousels/Carousels.js deleted file mode 100644 index 62c49fe47..000000000 --- a/src/views/components/base/carousels/Carousels.js +++ /dev/null @@ -1,217 +0,0 @@ -import React from 'react' -import { - CCard, - CCardBody, - CCardHeader, - CCarousel, - CCarouselCaption, - CCarouselItem, - CCol, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const slides = [ - 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_1607923e7e2%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1607923e7e2%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.9296875%22%20y%3D%22217.75625%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', - 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa20%20text%20%7B%20fill%3A%23444%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa20%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23666%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22218.3%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', - 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa21%20text%20%7B%20fill%3A%23333%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa21%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23555%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22218.3%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', -] - -const slidesLight = [ - 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_1607923e7e2%20text%20%7B%20fill%3A%23AAA%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1607923e7e2%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23F5F5F5%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.9296875%22%20y%3D%22217.75625%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', - 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa20%20text%20%7B%20fill%3A%23BBB%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa20%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23EEE%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22218.3%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', - 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_15ba800aa21%20text%20%7B%20fill%3A%23999%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_15ba800aa21%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23E5E5E5%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22218.3%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E', -] - -const Carousels = () => { - return ( - - - - - - - - Carousel Slide only - - -

    Here’s a carousel with slides

    - - - - slide 1 - - - slide 2 - - - slide 3 - - - -
    -
    -
    - - - - Carousel With controls - - -

    - Adding in the previous and next controls by controls property. -

    - - - - slide 1 - - - slide 2 - - - slide 3 - - - -
    -
    -
    - - - - Carousel With indicators - - -

    - You can attach the indicators to the carousel, lengthwise the controls, too. -

    - - - - slide 1 - - - slide 2 - - - slide 3 - - - -
    -
    -
    - - - - Carousel With captions - - -

    - You can add captions to slides with the <CCarouselCaption> element - within any <CCarouselItem>. They can be immediately hidden on - smaller viewports, as shown below, with optional{' '} - display utilities. We hide them - with .d-none and draw them back on medium-sized devices with{' '} - .d-md-block. -

    - - - - slide 1 - -
    First slide label
    -

    Some representative placeholder content for the first slide.

    -
    -
    - - slide 2 - -
    Second slide label
    -

    Some representative placeholder content for the first slide.

    -
    -
    - - slide 3 - -
    Third slide label
    -

    Some representative placeholder content for the first slide.

    -
    -
    -
    -
    -
    -
    -
    - - - - Carousel Crossfade - - -

    - Add transition="crossfade" to your carousel to animate slides - with a fade transition instead of a slide. -

    - - - - slide 1 - - - slide 2 - - - slide 3 - - - -
    -
    -
    - - - - Carousel Dark variant - - -

    - Add dark property to the CCarousel for darker controls, - indicators, and captions. Controls have been inverted from their default white fill - with the filter CSS property. Captions and controls have additional Sass - variables that customize the color and background-color. -

    - - - - slide 1 - -
    First slide label
    -

    Some representative placeholder content for the first slide.

    -
    -
    - - slide 2 - -
    Second slide label
    -

    Some representative placeholder content for the first slide.

    -
    -
    - - slide 3 - -
    Third slide label
    -

    Some representative placeholder content for the first slide.

    -
    -
    -
    -
    -
    -
    -
    -
    - ) -} - -export default Carousels diff --git a/src/views/components/base/collapses/Collapses.js b/src/views/components/base/collapses/Collapses.js deleted file mode 100644 index 4a85d3966..000000000 --- a/src/views/components/base/collapses/Collapses.js +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useState } from 'react' -import { CButton, CCard, CCardBody, CCardHeader, CCol, CCollapse, CRow } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Collapses = () => { - const [visible, setVisible] = useState(false) - const [visibleA, setVisibleA] = useState(false) - const [visibleB, setVisibleB] = useState(false) - - return ( - - - - - - - - React Collapse - - -

    You can use a link or a button component.

    - - { - e.preventDefault() - setVisible(!visible) - }} - > - Link - - setVisible(!visible)}>Button - - - - Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry - richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes - anderson cred nesciunt sapiente ea proident. - - - - -
    -
    -
    - - - - React Collapse multi target - - -

    - A <CButton> can show and hide multiple elements. -

    - - setVisibleA(!visibleA)}>Toggle first element - setVisibleB(!visibleB)}>Toggle second element - { - setVisibleA(!visibleA) - setVisibleB(!visibleB) - }} - > - Toggle both elements - - - - - - - Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry - richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes - anderson cred nesciunt sapiente ea proident. - - - - - - - - - Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry - richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes - anderson cred nesciunt sapiente ea proident. - - - - - - -
    -
    -
    -
    - ) -} - -export default Collapses diff --git a/src/views/components/base/index.js b/src/views/components/base/index.js deleted file mode 100644 index 2b7656c82..000000000 --- a/src/views/components/base/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import Breadcrumbs from './Breadcrumbs' -import Cards from './Cards' -import Carousels from './Carousels' -import Collapses from './Collapses' -import Dropdowns from './Dropdowns' -import Jumbotrons from './Jumbotrons' -import ListGroups from './ListGroups' -import Navbars from './Navbars' -import Navs from './Navs' -import Paginations from './Paginations' -import Popovers from './Popovers' -import ProgressBar from './ProgressBar' -import Tabs from './Tabs' -import Tooltips from './Tooltips' - -export { - Breadcrumbs, - Cards, - Carousels, - Collapses, - Dropdowns, - Jumbotrons, - ListGroups, - Navbars, - Navs, - Popovers, - ProgressBar, - Tabs, - Tooltips, - Paginations, -} diff --git a/src/views/components/base/jumbotrons/Jumbotrons.js b/src/views/components/base/jumbotrons/Jumbotrons.js deleted file mode 100644 index 1bd9340e4..000000000 --- a/src/views/components/base/jumbotrons/Jumbotrons.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' -import { CButton, CCard, CCardBody, CCardHeader, CCol, CContainer, CRow } from '@coreui/react' -import { DocsLink } from 'src/reusable' - -const Jumbotrons = () => { - return ( - <> - - - Jumbotron - - - - -

    Custom jumbotron

    -

    - Using a series of utilities, you can create this jumbotron, just like the one in - previous versions of Bootstrap. Check out the examples below for how you can remix and - restyle it to your liking. -

    - Example button -
    - - -
    -

    Change the background

    -

    - Swap the background-color utility and add a `.text-*` color utility to mix up the - jumbotron look. Then, mix and match with additional component themes and more. -

    - - Example button - -
    -
    - -
    -

    Add borders

    -

    - Or, keep it light and add a border for some added definition to the boundaries of - your content. Be sure to look under the hood at the source HTML here as we've - adjusted the alignment and sizing of both column's content for equal-height. -

    - - Example button - -
    -
    -
    -
    -
    - - ) -} - -export default Jumbotrons diff --git a/src/views/components/base/list-groups/ListGroups.js b/src/views/components/base/list-groups/ListGroups.js deleted file mode 100644 index bc236617f..000000000 --- a/src/views/components/base/list-groups/ListGroups.js +++ /dev/null @@ -1,349 +0,0 @@ -import React from 'react' -import { - CBadge, - CCard, - CCardBody, - CCardHeader, - CCol, - CFormCheck, - CListGroup, - CListGroupItem, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const ListGroups = () => { - return ( - - - - - - - - React List Group Basic example - - -

    - The default list group is an unordered list with items and the proper CSS classes. - Build upon it with the options that follow, or with your CSS as required. -

    - - - Cras justo odio - Dapibus ac facilisis in - Morbi leo risus - Porta ac consectetur ac - Vestibulum at eros - - -
    -
    -
    - - - - React List Group Active items - - -

    - Add active boolean property to a <CListGroupItem> to - show the current active selection. -

    - - - Cras justo odio - Dapibus ac facilisis in - Morbi leo risus - Porta ac consectetur ac - Vestibulum at eros - - -
    -
    -
    - - - - React List Group Disabled items - - -

    - Add disabled boolean property to a <CListGroupItem> to - make it appear disabled. -

    - - - Cras justo odio - Dapibus ac facilisis in - Morbi leo risus - Porta ac consectetur ac - Vestibulum at eros - - -
    -
    -
    - - - - React List Group Links and buttons - - -

    - Use <a>s or <button>s to create{' '} - actionable list group items with hover, disabled, and active states by adding{' '} - component="a|button". We separate these pseudo-classes to ensure - list groups made of non-interactive elements (like <li>s or{' '} - <div> - s) don'tprovide a click or tap affordance. -

    - - - - Cras justo odio - - - Dapibus ac facilisis in - - - Morbi leo risus - - - Porta ac consectetur ac - - - Vestibulum at eros - - - -
    -
    -
    - - - - React List Group Flush - - -

    - Add flush boolean property to remove some borders and rounded corners to - render list group items edge-to-edge in a parent container (e.g., cards). -

    - - - Cras justo odio - Dapibus ac facilisis in - Morbi leo risus - Porta ac consectetur ac - Vestibulum at eros - - -
    -
    -
    - - - - React List Group Horizontal - - -

    - Add layout="horizontal" to change the layout of list group items - from vertical to horizontal across all breakpoints. Alternatively, choose a responsive - variant .layout="horizontal-{sm | md | lg | xl | xxl}"{' '} - to make a list group horizontal starting at that breakpoint's{' '} - min-width. Currently{' '} - horizontal list groups cannot be combined with flush list groups. -

    - - {['', '-sm', '-md', '-lg', '-xl', '-xxl'].map((breakpoint, index) => ( - - Cras justo odio - Dapibus ac facilisis in - Morbi leo risus - - ))} - -
    -
    -
    - - - - React List Group Contextual classes - - -

    - Use contextual classes to style list items with a stateful background and color. -

    - - - Dapibus ac facilisis in - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - A simple {color} list group item - - ))} - - -

    - Contextual classes also work with <a>s or{' '} - <button>s. Note the addition of the hover styles here not present - in the previous example. Also supported is the active state; apply it to - indicate an active selection on a contextual list group item. -

    - - - - Dapibus ac facilisis in - - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - A simple {color} list group item - - ))} - - -
    -
    -
    - - - - React List Group With badges - - -

    - Add badges to any list group item to show unread counts, activity, and more. -

    - - - - Cras justo odio - - 14 - - - - Dapibus ac facilisis in - - 2 - - - - Morbi leo risus - - 1 - - - - -
    -
    -
    - - - - React List Group Custom content - - -

    - Add nearly any HTML within, even for linked list groups like the one below, with the - help of flexbox utilities. -

    - - - -
    -
    List group item heading
    - 3 days ago -
    -

    - Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus - varius blandit. -

    - Donec id elit non mi porta. -
    - -
    -
    List group item heading
    - 3 days ago -
    -

    - Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus - varius blandit. -

    - Donec id elit non mi porta. -
    - -
    -
    List group item heading
    - 3 days ago -
    -

    - Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus - varius blandit. -

    - Donec id elit non mi porta. -
    -
    -
    -
    -
    -
    - - - - React List Group Checkboxes and radios - - -

    - Place CoreUI's checkboxes and radios within list group items and customize as - needed. -

    - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - ) -} - -export default ListGroups diff --git a/src/views/components/base/navbars/Navbars.js b/src/views/components/base/navbars/Navbars.js deleted file mode 100644 index e4c0e3704..000000000 --- a/src/views/components/base/navbars/Navbars.js +++ /dev/null @@ -1,174 +0,0 @@ -import React, { useState } from 'react' -import { - CCard, - CCardBody, - CCardHeader, - CCollapse, - CDropdownItem, - CDropdownMenu, - CDropdownToggle, - CForm, - CFormControl, - CImage, - CNavbar, - CNavbarNav, - CNavbarBrand, - CNavbarText, - CNavbarToggler, - CNavLink, - CDropdown, - CButton, -} from '@coreui/react' -import { DocsLink } from 'src/reusable' - -const CNavbars = () => { - const [visible, setVisible] = useState(false) - const [isOpenDropdown, setIsOpenDropdown] = useState(false) - const [navbarText, setNavbarText] = useState(false) - - return ( - <> - - - CNavbar - - - - - setVisible(!visible)} /> - NavbarBrand - - - Home - Link - - - - - - Search - - - - Lang - - EN - ES - RU - FA - - - - User - - Account - Settings - - - - - - - - - - CNavbar brand - - - - - CoreUI React - - - - - - - CNavbar text - - - { - setNavbarText(!navbarText) - }} - /> - NavbarBrand - - - Navbar text - - - - - - - - CNavbar dropdown - - - { - setIsOpenDropdown(!isOpenDropdown) - }} - /> - - - Home - Link - - Lang - - EN - ES - RU - FA - - - - User - - Account - Settings - - - - - - - - - - CNavbar form - - - - - - Search - - - - - - - - CNavbar input group - - - - - - - - - - ) -} - -export default CNavbars diff --git a/src/views/components/base/navs/Navs.js b/src/views/components/base/navs/Navs.js deleted file mode 100644 index d45c7a235..000000000 --- a/src/views/components/base/navs/Navs.js +++ /dev/null @@ -1,400 +0,0 @@ -import React from 'react' -import { - CRow, - CCol, - CCard, - CCardBody, - CCardHeader, - CDropdown, - CDropdownItem, - CDropdownMenu, - CDropdownToggle, - CNav, - CNavItem, - CNavLink, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Navs = () => { - return ( - - - - - - - - React Navs Base navs - - -

    - The base .nav component is built with flexbox and provide a strong - foundation for building all types of navigation components. It includes some style - overrides (for working with lists), some link padding for larger hit areas, and basic - disabled styling. -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -

    - Classes are used throughout, so your markup can be super flexible. Use{' '} - <ul>s like above, <ol> if the order of your - items is important, or roll your own with a <nav> element. Because - the .nav uses display: flex, the nav links behave the same as nav items would, but - without the extra markup. -

    - - - - Active - - Link - Link - - Disabled - - - -
    -
    -
    - - - - React Navs Horizontal alignment - - -

    - Change the horizontal alignment of your nav with{' '} - - flexbox utilities - - . By default, navs are left-aligned, but you can easily change them to center or right - aligned. -

    -

    - Centered with .justify-content-center: -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -

    - Right-aligned with .justify-content-end: -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -
    -
    -
    - - - - React Navs Vertical - - -

    - Stack your navigation by changing the flex item direction with the{' '} - .flex-column utility. Need to stack them on some viewports but not - others? Use the responsive versions (e.g., .flex-sm-column). -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -
    -
    -
    - - - - React Navs Tabs - - -

    - Takes the basic nav from above and adds the variant="tabs" class - to generate a tabbed interface -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -
    -
    -
    - - - - React Navs Pills - - -

    - Take that same HTML, but use variant="pills" instead: -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -
    -
    -
    - - - - React Navs Fill and justify - - -

    - Force your .nav's contents to extend the full available width one of - two modifier classes. To proportionately fill all available space with your{' '} - .nav-items, use layout="fill". Notice that all - horizontal space is occupied, but not every nav item has the same width. -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -

    - For equal-width elements, use layout="justified". All horizontal - space will be occupied by nav links, but unlike the .nav-fill above, every nav item - will be the same width. -

    - - - - - Active - - - - Link - - - Link - - - - Disabled - - - - -
    -
    -
    - - - - React Navs Working with flex utilities - - -

    - If you need responsive nav variations, consider using a series of{' '} - flexbox utilities. While more - verbose, these utilities offer greater customization across responsive breakpoints. In - the example below, our nav will be stacked on the lowest breakpoint, then adapt to a - horizontal layout that fills the available width starting from the small breakpoint. -

    - - - - Active - - Link - Link - - Disabled - - - -
    -
    -
    - - - - React Navs Tabs with dropdowns - - - - - - - Active - - - - Dropdown button - - Action - Another action - Something else here - - - - Link - - - - Disabled - - - - - - - - - - - React Navs Pills with dropdowns - - - - - - - Active - - - - Dropdown button - - Action - Another action - Something else here - - - - Link - - - - Disabled - - - - - - - -
    - ) -} - -export default Navs diff --git a/src/views/components/base/paginations/Paginations.js b/src/views/components/base/paginations/Paginations.js deleted file mode 100644 index 986382005..000000000 --- a/src/views/components/base/paginations/Paginations.js +++ /dev/null @@ -1,177 +0,0 @@ -import React from 'react' -import { - CCard, - CCardBody, - CCardHeader, - CCol, - CPagination, - CPaginationItem, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Paginations = () => { - return ( - - - - - - - - React Pagination - - -

    - We use a large block of connected links for our pagination, making links hard to miss - and easily scalable—all while providing large hit areas. Pagination is built with list - HTML elements so screen readers can announce the number of available links. Use a - wrapping <nav> element to identify it as a navigation section to - screen readers and other assistive technologies. -

    -

    - In addition, as pages likely have more than one such navigation section, it's - advisable to provide a descriptive aria-label for the{' '} - <nav> to reflect its purpose. For example, if the pagination - component is used to navigate between a set of search results, an appropriate label - could be aria-label="Search results pages". -

    - - - Previous - 1 - 2 - 3 - Next - - -
    -
    -
    - - - - React Pagination Working with icons - - -

    - Looking to use an icon or symbol in place of text for some pagination links? Be sure - to provide proper screen reader support with aria attributes. -

    - - - - - - 1 - 2 - 3 - - - - - -
    -
    -
    - - - - React Pagination Disabled and active states - - -

    - Pagination links are customizable for different circumstances. Use{' '} - disabled for links that appear un-clickable and .active to - indicate the current page. -

    -

    - While the disabled prop uses pointer-events: none to{' '} - try to disable the link functionality of <a>s, that CSS - property is not yet standardized and doesn'taccount for keyboard navigation. As - such, we always add tabindex="-1" on disabled links and use - custom JavaScript to fully disable their functionality. -

    - - - - - - 1 - 2 - 3 - - - - - -
    -
    -
    - - - - React Pagination Sizing - - -

    - Fancy larger or smaller pagination? Add size="lg" or{' '} - size="sm" for additional sizes. -

    - - - Previous - 1 - 2 - 3 - Next - - - - - Previous - 1 - 2 - 3 - Next - - -
    -
    -
    - - - - React Pagination Alignment - - -

    - Change the alignment of pagination components with{' '} - flexbox utilities. -

    - - - Previous - 1 - 2 - 3 - Next - - - - - Previous - 1 - 2 - 3 - Next - - -
    -
    -
    -
    - ) -} - -export default Paginations diff --git a/src/views/components/base/popovers/Popovers.js b/src/views/components/base/popovers/Popovers.js deleted file mode 100644 index 7aba3dcc8..000000000 --- a/src/views/components/base/popovers/Popovers.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react' -import { CButton, CCard, CCardBody, CCardHeader, CPopover, CRow, CCol } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Popovers = () => { - return ( - - - - - - - - React Popover Basic example - - - - - - Click to toggle popover - - - - - - - - - - React Popover Four directions - - -

    - Four options are available: top, right, bottom, and left aligned. Directions are - mirrored when using CoreUI for React in RTL. -

    - - - Popover on top - - - Popover on right - - - Popover on bottom - - - Popover on left - - -
    -
    -
    -
    - ) -} - -export default Popovers diff --git a/src/views/components/base/progress/Progress.js b/src/views/components/base/progress/Progress.js deleted file mode 100644 index c24bbe516..000000000 --- a/src/views/components/base/progress/Progress.js +++ /dev/null @@ -1,189 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCardHeader, CCol, CProgress, CProgressBar, CRow } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Progress = () => { - return ( - - - - - - - - React Progress Basic example - - -

    - Progress components are built with two HTML elements, some CSS to set the width, and a - few attributes. We don'tuse{' '} - - the HTML5 <progress> element - - , ensuring you can stack progress bars, animate them, and place text labels over them. -

    - - - - - - - - - - - - - - - - - -
    -
    -
    - - - - React Progress Labels - - -

    - Add labels to your progress bars by placing text within the{' '} - <CProgressBar>. -

    - - - 25% - - -
    -
    -
    - - - - React Progress Height - - -

    - We only set a height value on the <CProgress>, so if - you change that value the inner <CProgressBar> will automatically - resize accordingly. -

    - - - - - - - - -
    -
    -
    - - - - React Progress Backgrounds - - -

    - Use color prop to change the appearance of individual progress bars. -

    - - - - - - - - - - - - - - -
    -
    -
    - - - - React Progress Multiple bars - - -

    - Include multiple progress bars in a progress component if you need. -

    - - - - - - - -
    -
    -
    - - - - React Progress Striped - - -

    - Add variant="striped" to any <CProgressBar> to - apply a stripe via CSS gradient over the progress bar's background color. -

    - - - - - - - - - - - - - - -
    -
    -
    - - - - React Progress Animated stripes - - -

    - The striped gradient can also be animated. Add animated property to{' '} - <CProgressBar> to animate the stripes right to left via CSS3 - animations. -

    - - - - - - - - - - - - - - -
    -
    -
    -
    - ) -} - -export default Progress diff --git a/src/views/components/base/spinners/Spinners.js b/src/views/components/base/spinners/Spinners.js deleted file mode 100644 index 31eece656..000000000 --- a/src/views/components/base/spinners/Spinners.js +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react' -import { CButton, CCard, CCardBody, CCardHeader, CCol, CSpinner, CRow } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Accordion = () => { - return ( - - - - - - - - React Spinner Border - - -

    - Use the border spinners for a lightweight loading indicator. -

    - - - -

    - The border spinner uses currentColor for its border-color. - You can use any of our text color utilities on the standard spinner. -

    - - - - - - - - - - -
    -
    -
    - - - - React Spinner Growing - - -

    - If you don'tfancy a border spinner, switch to the grow spinner. While it - doesn't technically spin, it does repeatedly grow! -

    - - - -

    - Once again, this spinner is built with currentColor, so you can easily - change its appearance. Here it is in blue, along with the supported variants. -

    - - - - - - - - - - -
    -
    -
    - - - - React Spinner Size - - -

    - Add size="sm" property to make a smaller spinner that can quickly - be used within other components. -

    - - - - -
    -
    -
    - - - - React Spinner Buttons - - -

    - Use spinners within buttons to indicate an action is currently processing or taking - place. You may also swap the text out of the spinner element and utilize button text - as needed. -

    - - - - - - - - - - - - -
    -
    -
    -
    - ) -} - -export default Accordion diff --git a/src/views/components/base/tables/Tables.js b/src/views/components/base/tables/Tables.js deleted file mode 100644 index 8f1d9c4df..000000000 --- a/src/views/components/base/tables/Tables.js +++ /dev/null @@ -1,989 +0,0 @@ -import React from 'react' -import { - CCard, - CCardBody, - CCardHeader, - CCol, - CRow, - CTable, - CTableBody, - CTableCaption, - CTableDataCell, - CTableHead, - CTableHeaderCell, - CTableRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Tables = () => { - return ( - - - - - - - - React Table Basic example - - -

    - Using the most basic table CoreUI, here's how <CTable>-based - tables look in CoreUI. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Variants - - -

    - Use contextual classes to color tables, table rows or individual cells. -

    - - - - - Class - Heading - Heading - - - - - Default - Cell - Cell - - - Primary - Cell - Cell - - - Secondary - Cell - Cell - - - Success - Cell - Cell - - - Danger - Cell - Cell - - - Warning - Cell - Cell - - - Info - Cell - Cell - - - Light - Cell - Cell - - - Dark - Cell - Cell - - - - -
    -
    -
    - - - - React Table Striped rows - - -

    - Use striped property to add zebra-striping to any table row within the{' '} - <CTableBody>. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -

    - These classes can also be added to table variants: -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Hoverable rows - - -

    - Use hover property to enable a hover state on table rows within a{' '} - <CTableBody>. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Active tables - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - - Larry the Bird - - @twitter - - - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - - Larry the Bird - - @twitter - - - - - - - - - - - React Table Bordered tables - - -

    - Add bordered property for borders on all sides of the table and cells. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -

    - - Border color utilities - {' '} - can be added to change colors: -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Tables without borders - - -

    - Add borderless property for a table without borders. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Small tables - - -

    - Add small property to make any <CTable> more compact - by cutting all cell padding in half. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Vertical alignment - - -

    - Table cells of <CTableHead> are always vertical aligned to the - bottom. Table cells in <CTableBody> inherit their alignment from{' '} - <CTable> and are aligned to the the top by default. Use the align - property to re-align where needed. -

    - - - - - - Heading 1 - - - Heading 2 - - - Heading 3 - - - Heading 4 - - - - - - - This cell inherits vertical-align: middle; from the table - - - This cell inherits vertical-align: middle; from the table - - - This cell inherits vertical-align: middle; from the table - - - This here is some placeholder text, intended to take up quite a bit of - vertical space, to demonsCTableRowate how the vertical alignment works in the - preceding cells. - - - - - This cell inherits vertical-align: bottom; from the table row - - - This cell inherits vertical-align: bottom; from the table row - - - This cell inherits vertical-align: bottom; from the table row - - - This here is some placeholder text, intended to take up quite a bit of - vertical space, to demonsCTableRowate how the vertical alignment works in the - preceding cells. - - - - - This cell inherits vertical-align: middle; from the table - - - This cell inherits vertical-align: middle; from the table - - This cell is aligned to the top. - - This here is some placeholder text, intended to take up quite a bit of - vertical space, to demonsCTableRowate how the vertical alignment works in the - preceding cells. - - - - - -
    -
    -
    - - - - React Table Nesting - - -

    - Border styles, active styles, and table variants are not inherited by nested tables. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - - - - - Header - Header - Header - - - - - A - First - Last - - - B - First - Last - - - C - First - Last - - - - - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Table head - - -

    - Similar to tables and dark tables, use the modifier prop{' '} - color="light" or color="dark" to make{' '} - <CTableHead>s appear light or dark gray. -

    - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry - the Bird - @twitter - - - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - -
    -
    -
    - - - - React Table Table foot - - - - - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry the Bird - @twitter - - - - - Footer - Footer - Footer - Footer - - - - - - - - - - - React Table Captions - - -

    - A <CTableCaption> functions like a heading for a table. It helps - users with screen readers to find a table and understand what it's about and - decide if they want to read it. -

    - - - List of users - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry - the Bird - @twitter - - - - -

    - You can also put the <CTableCaption> on the top of the table with{' '} - caption="top". -

    - - - List of users - - - # - Class - Heading - Heading - - - - - 1 - Mark - Otto - @mdo - - - 2 - Jacob - Thornton - @fat - - - 3 - Larry - the Bird - @twitter - - - - -
    -
    -
    -
    - ) -} - -export default Tables diff --git a/src/views/components/base/tooltips/Tooltips.js b/src/views/components/base/tooltips/Tooltips.js deleted file mode 100644 index 9ef67f503..000000000 --- a/src/views/components/base/tooltips/Tooltips.js +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react' -import { CButton, CCard, CCardBody, CCardHeader, CLink, CTooltip, CRow, CCol } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Tooltips = () => { - return ( - - - - - - - - React Tooltip Basic example - - -

    - Hover over the links below to see tooltips: -

    - -

    - Tight pants next level keffiyeh - - you probably - - haven'theard of them. Photo booth beard raw denim letterpress vegan messenger - bag stumptown. Farm-to-table seitan, mcsweeney's fixie sustainable quinoa 8-bit - american apparel - - have a - - terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo - thundercats. Tofu biodiesel williamsburg marfa, four loko mcsweeney''s - cleanse vegan chambray. A really ironic artisan - - whatever keytar - - scenester farm-to-table banksy Austin - - twitter handle - - freegan cred raw denim single-origin coffee viral. -

    -
    -

    - Hover over the buttons below to see the four tooltips directions: top, right, bottom, - and left. Directions are mirrored when using CoreUI in RTL. -

    - - - Tooltip on top - - - Tooltip on right - - - Tooltip on bottom - - - Tooltip on left - - -
    -
    -
    -
    - ) -} - -export default Tooltips diff --git a/src/views/components/buttons/button-groups/ButtonGroups.js b/src/views/components/buttons/button-groups/ButtonGroups.js deleted file mode 100644 index 64ce7d80d..000000000 --- a/src/views/components/buttons/button-groups/ButtonGroups.js +++ /dev/null @@ -1,454 +0,0 @@ -import React from 'react' -import { - CButton, - CDropdown, - CDropdownDivider, - CDropdownItem, - CDropdownMenu, - CDropdownToggle, - CButtonGroup, - CButtonToolbar, - CCard, - CCardBody, - CCardHeader, - CCol, - CFormCheck, - CFormControl, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const ButtonGroups = () => { - return ( - - - - - - - - React Button Group Basic example - - -

    - Wrap a series of <CButton> components in{' '} - <CButtonGroup>.{' '} -

    - - - Left - Middle - Right - - -

    - These classes can also be added to groups of links, as an alternative to the{' '} - <CNav> components. -

    - - - - Active link - - - Link - - - Link - - - -
    -
    -
    - - - - React Button Group Mixed styles - - - - - Left - Middle - Right - - - - - - - - - React Button Group Outlined styles - - - - - - Left - - - Middle - - - Right - - - - - - - - - - React Button Group Checkbox and radio button groups - - -

    - Combine button-like checkbox and radio toggle buttons into a seamless looking button - group. -

    - - - - - - - - - - - - - - -
    -
    -
    - - - - React Button Group Button toolbar - - -

    - Join sets of button groups into button toolbars for more complicated components. Use - utility classes as needed to space out groups, buttons, and more. -

    - - - - 1 - 2 - 3 - 4 - - - 5 - 6 - 7 - - - 8 - - - -

    - Feel free to combine input groups with button groups in your toolbars. Similar to the - example above, you’ll likely need some utilities through to space items correctly. -

    - - - - - 1 - - - 2 - - - 3 - - - 4 - - - - @ - - - - - - - 1 - - - 2 - - - 3 - - - 4 - - - - @ - - - - -
    -
    -
    - - - - React Button Group Sizing - - -

    - Alternatively, of implementing button sizing classes to each button in a group, set{' '} - size property to all <CButtonGroup>'s, including - each one when nesting multiple groups. -

    - - - - Left - - - Middle - - - Right - - -
    - - - Left - - - Middle - - - Right - - -
    - - - Left - - - Middle - - - Right - - -
    -
    -
    -
    - - - - React Button Group Nesting - - -

    - Put a <CButtonGroup> inside another{' '} - <CButtonGroup> when you need dropdown menus combined with a series - of buttons. -

    - - - 1 - 2 - - Dropdown - - Action - Another action - Something else here - - Separated link - - - - -
    -
    -
    - - - - React Button Group Vertical variation - - -

    - Create a set of buttons that appear vertically stacked rather than horizontally.{' '} - Split button dropdowns are not supported here. -

    - - - Button - Button - Button - Button - Button - Button - Button - - - - - Button - Button - - Dropdown - - Action - Another action - Something else here - - Separated link - - - Button - Button - - Dropdown - - Action - Another action - Something else here - - Separated link - - - - Dropdown - - Action - Another action - Something else here - - Separated link - - - - Dropdown - - Action - Another action - Something else here - - Separated link - - - - - - - - - - - -
    -
    -
    -
    - ) -} - -export default ButtonGroups diff --git a/src/views/components/buttons/buttons/Buttons.js b/src/views/components/buttons/buttons/Buttons.js deleted file mode 100644 index 3cdfdf728..000000000 --- a/src/views/components/buttons/buttons/Buttons.js +++ /dev/null @@ -1,403 +0,0 @@ -import React from 'react' -import { CButton, CCard, CCardBody, CCardHeader, CCol, CRow } from '@coreui/react' -import CIcon from '@coreui/icons-react' -import { DocsCallout, Example } from 'src/reusable' - -const Buttons = () => { - return ( - - - - - - - - React Button - - -

    - CoreUI includes a bunch of predefined buttons components, each serving its own - semantic purpose. Buttons show what action will happen when the user clicks or touches - it. CoreUI buttons are used to initialize operations, both in the background or - foreground of an experience. -

    - - {['normal', 'active', 'disabled'].map((state, index) => ( - - - {state.charAt(0).toUpperCase() + state.slice(1)} - - - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - {color.charAt(0).toUpperCase() + color.slice(1)} - - ))} - Link - - - ))} - -
    -
    -
    - - - - React Button with icons - - -

    - You can combine button with our CoreUI Icons. -

    - - {['normal', 'active', 'disabled'].map((state, index) => ( - - - {state.charAt(0).toUpperCase() + state.slice(1)} - - - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - - {color.charAt(0).toUpperCase() + color.slice(1)} - - ))} - - - Link - - - - ))} - -
    -
    -
    - - - - React Button Button components - - -

    - The <CButton> component are designed for{' '} - <button> , <a> or <input>{' '} - elements (though some browsers may apply a slightly different rendering). -

    -

    - If you're using <CButton> component as <a>{' '} - elements that are used to trigger functionality ex. collapsing content, these links - should be given a role="button" to adequately communicate their - meaning to assistive technologies such as screen readers. -

    - - - Link - - - Button - - - - - -
    -
    -
    - - - - React Button outline - - -

    - If you need a button, but without the strong background colors. Set{' '} - variant="outline" prop to remove all background colors. -

    - - {['normal', 'active', 'disabled'].map((state, index) => ( - - - {state.charAt(0).toUpperCase() + state.slice(1)} - - - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - {color.charAt(0).toUpperCase() + color.slice(1)} - - ))} - - - ))} - -
    -
    -
    - - - - React Button ghost - - -

    - If you need a ghost variant of button, set variant="ghost" prop - to remove all background colors. -

    - - {['normal', 'active', 'disabled'].map((state, index) => ( - - - {state.charAt(0).toUpperCase() + state.slice(1)} - - - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - {color.charAt(0).toUpperCase() + color.slice(1)} - - ))} - - - ))} - -
    -
    -
    - - - - React Button Sizes - - -

    - Larger or smaller buttons? Add size="lg" or{' '} - size="sm" for additional sizes. -

    - - - Large button - - - Large button - - - - - Small button - - - Small button - - -
    -
    -
    - - - - React Button Pill - - - - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - {color.charAt(0).toUpperCase() + color.slice(1)} - - ))} - - - - - - - - React Button Square - - - - {[ - 'primary', - 'secondary', - 'success', - 'danger', - 'warning', - 'info', - 'light', - 'dark', - ].map((color, index) => ( - - {color.charAt(0).toUpperCase() + color.slice(1)} - - ))} - - - - - - - - React Button Disabled state - - -

    - Add the disabled boolean prop to any <CButton>{' '} - component to make buttons look inactive. Disabled button has{' '} - pointer-events: none applied to, disabling hover and active states from - triggering. -

    - - - Primary button - - - Button - - -

    - Disabled buttons using the <a> component act a little different: -

    -

    - <a>s don'tsupport the disabled attribute, so - CoreUI has to add .disabled className to make buttons look inactive. - CoreUI also has to add to the disabled button component{' '} - aria-disabled="true" attribute to show the state of the component - to assistive technologies. -

    - - - Primary link - - - Link - - -
    -
    -
    - - - - React Button Block buttons - - -

    - Create buttons that span the full width of a parent—by using utilities. -

    - -
    - Button - Button -
    -
    -

    - Here we create a responsive variation, starting with vertically stacked buttons until - the md breakpoint, where .d-md-block replaces the{' '} - .d-grid class, thus nullifying the gap-2 utility. Resize - your browser to see them change. -

    - -
    - Button - Button -
    -
    -

    - You can adjust the width of your block buttons with grid column width classes. For - example, for a half-width "block button", use .col-6. Center it - horizontally with .mx-auto, too. -

    - -
    - Button - Button -
    -
    -

    - Additional utilities can be used to adjust the alignment of buttons when horizontal. - Here we've taken our previous responsive example and added some flex utilities and - a margin utility on the button to right align the buttons when they're no longer - stacked. -

    - -
    - - Button - - Button -
    -
    -
    -
    -
    -
    - ) -} - -export default Buttons diff --git a/src/views/components/buttons/dropdowns/Dropdowns.js b/src/views/components/buttons/dropdowns/Dropdowns.js deleted file mode 100644 index 8a1c18b0d..000000000 --- a/src/views/components/buttons/dropdowns/Dropdowns.js +++ /dev/null @@ -1,341 +0,0 @@ -import React from 'react' -import { - CButton, - CButtonGroup, - CCard, - CCardBody, - CCardHeader, - CCol, - CDropdown, - CDropdownDivider, - CDropdownItem, - CDropdownMenu, - CDropdownToggle, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Dropdowns = () => { - return ( - - - - - - - - React Dropdown Single button - - -

    - Here's how you can put them to work with either <button>{' '} - elements: -

    - - - Dropdown button - - Action - Another action - Something else here - - - -

    - The best part is you can do this with any button variant, too: -

    - - <> - {['primary', 'secondary', 'success', 'info', 'warning', 'danger'].map( - (color, index) => ( - - {color} - - Action - Another action - Something else here - - Separated link - - - ), - )} - - -
    -
    -
    - - - - React Dropdown Split button - - -

    - Similarly, create split button dropdowns with virtually the same markup as single - button dropdowns, but with the addition of boolean prop split for proper - spacing around the dropdown caret. -

    -

    - We use this extra class to reduce the horizontal padding on either side - of the caret by 25% and remove the margin-left that's attached for - normal button dropdowns. Those additional changes hold the caret centered in the split - button and implement a more properly sized hit area next to the main button. -

    - - <> - {['primary', 'secondary', 'success', 'info', 'warning', 'danger'].map( - (color, index) => ( - - {color} - - - Action - Another action - Something else here - - Separated link - - - ), - )} - - -
    -
    -
    - - - - React Dropdown Sizing - - -

    - Button dropdowns work with buttons of all sizes, including default and split dropdown - buttons. -

    - - - - Large button - - - Action - Another action - Something else here - - Separated link - - - - - Large split button - - - - Action - Another action - Something else here - - Separated link - - - - - - - Small button - - - Action - Another action - Something else here - - Separated link - - - - - Small split button - - - - Action - Another action - Something else here - - Separated link - - - -
    -
    -
    - - - - React Dropdown Single button - - -

    - Opt into darker dropdowns to match a dark navbar or custom style by set{' '} - dark property. No changes are required to the dropdown items. -

    - - - Dropdown button - - Action - Another action - Something else here - - Separated link - - - -

    And putting it to use in a navbar:

    - - - -
    -
    -
    - - - - React Dropdown Dropup - - -

    - Trigger dropdown menus above elements by adding{' '} - direction="dropup" to the <CDropdown>{' '} - component. -

    - - - Dropdown - - Action - Another action - Something else here - - Separated link - - - - Small split button - - - Action - Another action - Something else here - - Separated link - - - -
    -
    -
    - - - - React Dropdown Dropright - - -

    - Trigger dropdown menus at the right of the elements by adding{' '} - direction="dropend" to the <CDropdown>{' '} - component. -

    - - - Dropdown - - Action - Another action - Something else here - - Separated link - - - - Small split button - - - Action - Another action - Something else here - - Separated link - - - -
    -
    -
    - - - - React Dropdown Dropleft - - -

    - Trigger dropdown menus at the left of the elements by adding{' '} - direction="dropstart" to the <CDropdown>{' '} - component. -

    - - - - - - Action - Another action - Something else here - - Separated link - - - Small split button - - -
    -
    -
    -
    - ) -} - -export default Dropdowns diff --git a/src/views/components/buttons/index.js b/src/views/components/buttons/index.js deleted file mode 100644 index 6634d1521..000000000 --- a/src/views/components/buttons/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import ButtonDropdowns from './ButtonDropdowns' -import ButtonGroups from './ButtonGroups' -import Buttons from './Buttons' - -export { ButtonDropdowns, ButtonGroups, Buttons } diff --git a/src/views/components/charts/Charts.js b/src/views/components/charts/Charts.js deleted file mode 100644 index 13bb5cdde..000000000 --- a/src/views/components/charts/Charts.js +++ /dev/null @@ -1,172 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCol, CCardHeader, CRow } from '@coreui/react' -import { - CChartBar, - CChartDoughnut, - CChartLine, - CChartPie, - CChartPolarArea, - CChartRadar, -} from '@coreui/react-chartjs' -import { DocsLink } from 'src/reusable' - -const Charts = () => { - const random = () => Math.round(Math.random() * 100) - - return ( - - - - - Bar Chart - - - - - - - - - - Line Chart - - - - - - - - Doughnut Chart - - - - - - - - Pie Chart - - - - - - - - Polar Area Chart - - - - - - - - Radar Chart - - - - - - - ) -} - -export default Charts diff --git a/src/views/components/forms/checks-radios/ChecksRadios.js b/src/views/components/forms/checks-radios/ChecksRadios.js deleted file mode 100644 index 11117c14d..000000000 --- a/src/views/components/forms/checks-radios/ChecksRadios.js +++ /dev/null @@ -1,409 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCardHeader, CCol, CFormCheck, CRow } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const ChecksRadios = () => { - return ( - - - - - - - - React Checkbox - - - - - - - - - - - - - React Checkbox Disabled - - -

    - Add the disabled attribute and the associated <label>s - are automatically styled to match with a lighter color to help indicate the - input's state. -

    - - - - -
    -
    -
    - - - - React Radio - - -

    - Add the disabled attribute and the associated <label>s - are automatically styled to match with a lighter color to help indicate the - input's state. -

    - - - - -
    -
    -
    - - - - React Radio Disabled - - - - - - - - - - - - - React Switches - - -

    - A switch has the markup of a custom checkbox but uses the switch boolean - properly to render a toggle switch. Switches also support the disabled{' '} - attribute. -

    - - - - - - -
    -
    -
    - - - - React Switches Sizes - - - - - - - - - - - - - - React Checks and Radios Default layout (stacked) - - -

    - By default, any number of checkboxes and radios that are immediate sibling will be - vertically stacked and appropriately spaced. -

    - - - - - - - - - -
    -
    -
    - - - - React Checks and Radios Inline - - -

    - Group checkboxes or radios on the same horizontal row by adding inline{' '} - boolean property to any <CFormCheck>. -

    - - - - - - - - - - -
    -
    -
    - - - - React Checks and Radios Without labels - - -

    - Remember to still provide some form of accessible name for assistive technologies (for - instance, using aria-label). -

    - -
    - -
    -
    - -
    -
    -
    -
    -
    - - - - Toggle buttons - - -

    - Create button-like checkboxes and radio buttons by using button boolean - property on the <CFormCheck> component. These toggle buttons can - further be grouped in a button group if needed. -

    - - - - - - - - - -

    Radio toggle buttons

    - - - - - - -

    Outlined styles

    -

    - Different variants of button, such at the various outlined styles, are supported. -

    - -
    - -
    -
    - -
    -
    - - -
    -
    -
    -
    -
    -
    - ) -} - -export default ChecksRadios diff --git a/src/views/components/forms/floating-labels/FloatingLabels.js b/src/views/components/forms/floating-labels/FloatingLabels.js deleted file mode 100644 index edb7c384f..000000000 --- a/src/views/components/forms/floating-labels/FloatingLabels.js +++ /dev/null @@ -1,176 +0,0 @@ -import React from 'react' -import { - CCard, - CCardBody, - CCardHeader, - CCol, - CFormControl, - CFormLabel, - CFormFloating, - CFormSelect, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const FloatingLabels = () => { - return ( - - - - - - - - React Floating labels - - -

    - Wrap a pair of <CFormControl> and <CFormLabel>{' '} - elements in CFormFloating to enable floating labels with textual form - fields. A placeholder is required on each{' '} - <CFormControl> as our method of CSS-only floating labels uses the{' '} - :placeholder-shown pseudo-element. Also note that the{' '} - <CFormControl> must come first so we can utilize a sibling selector - (e.g., ~). -

    - - - - Email address - - - - Password - - -

    - When there's a value already defined, <CFormLabel> - s will automatically adjust to their floated position. -

    - - - - Input with value - - -
    -
    -
    - - - - React Floating labels Textareas - - -

    - By default, <CFormControl component="textarea">s will be - the same height as <CFormControl>s. -

    - - - - Comments - - -

    - To set a custom height on your{' '} - <CFormControl component="textarea">, do not use the{' '} - rows attribute. Instead, set an explicit height (either - inline or via custom CSS). -

    - - - - Comments - - -
    -
    -
    - - - - React Floating labels Selects - - -

    - Other than <CFormControl>, floating labels are only available on{' '} - <CFormSelect>s. They work in the same way, but unlike{' '} - <CFormControl>s, they'll always show the{' '} - <CFormLabel> in its floated state.{' '} - - Selects with size and multiple are not supported. - -

    - - - - - - - - - Works with selects - - -
    -
    -
    - - - - React Floating labels Layout - - -

    - When working with the CoreUI for Bootstrap grid system, be sure to place form elements - within column classes. -

    - - - - - - Email address - - - - - - - - - - - Works with selects - - - - -
    -
    -
    -
    - ) -} - -export default FloatingLabels diff --git a/src/views/components/forms/form-control/FormControl.js b/src/views/components/forms/form-control/FormControl.js deleted file mode 100644 index 1a84cbc43..000000000 --- a/src/views/components/forms/form-control/FormControl.js +++ /dev/null @@ -1,254 +0,0 @@ -import React from 'react' -import { - CButton, - CCard, - CCardBody, - CCardHeader, - CCol, - CForm, - CFormControl, - CFormLabel, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const FormControl = () => { - return ( - - - - - - - - React Form Control - - - - -
    - Email address - -
    -
    - Example textarea - -
    -
    -
    -
    -
    -
    - - - - React Form Control Sizing - - -

    - Set heights using size property like size="lg" and{' '} - size="sm". -

    - - -
    - -
    - -
    -
    -
    -
    - - - - React Form Control Disabled - - -

    - Add the disabled boolean attribute on an input to give it a grayed out - appearance and remove pointer events. -

    - - -
    - -
    -
    -
    -
    -
    - - - - React Form Control Readonly - - -

    - Add the readOnly boolean attribute on an input to prevent modification of - the input's value. Read-only inputs appear lighter (just like disabled inputs), - but retain the standard cursor. -

    - - - -
    -
    -
    - - - - React Form Control Readonly plain text - - -

    - If you want to have <input readonly> elements in your form styled - as plain text, use the plainText boolean property to remove the default - form field styling and preserve the correct margin and padding. -

    - - - - Email - -
    - -
    -
    - - - Password - -
    - -
    -
    -
    - - -
    - - Email - - -
    -
    - - Password - - -
    -
    - - Confirm identity - -
    -
    -
    -
    -
    -
    - - - - React Form Control File input - - - -
    - Default file input example - -
    -
    - Multiple files input example - -
    -
    - Disabled file input example - -
    -
    - Small file input example - -
    -
    - Large file input example - -
    -
    -
    -
    -
    - - - - React Form Control Color - - - - Color picker - - - - - -
    - ) -} - -export default FormControl diff --git a/src/views/components/forms/input-group/InputGroup.js b/src/views/components/forms/input-group/InputGroup.js deleted file mode 100644 index a7431d9cf..000000000 --- a/src/views/components/forms/input-group/InputGroup.js +++ /dev/null @@ -1,505 +0,0 @@ -import React from 'react' -import { - CButton, - CCard, - CCardBody, - CCardHeader, - CCol, - CDropdown, - CDropdownDivider, - CDropdownItem, - CDropdownMenu, - CDropdownToggle, - CFormCheck, - CFormControl, - CFormLabel, - CFormSelect, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Select = () => { - return ( - - - - - - - - React Input group Basic example - - -

    - Place one add-on or button on either side of an input. You may also place one on both - sides of an input. Remember to place <CFormLabel>s outside the - input group. -

    - - - @ - - - - - @example.com - - Your vanity URL - - https://example.com/users/ - - - - $ - - .00 - - - - @ - - - - With textarea - - - -
    -
    -
    - - - - React Input group Wrapping - - -

    - Input groups wrap by default via flex-wrap: wrap in order to accommodate - custom form field validation within an input group. You may disable this with{' '} - .flex-nowrap. -

    - - - @ - - - -
    -
    -
    - - - - React Input group Sizing - - -

    - Add the relative form sizing classes to the <CInputGroup> itself - and contents within will automatically resize—no need for repeating the form control - size classes on each element. -

    -

    - Sizing on the individual input group elements isn'tsupported. -

    - - - Small - - - - Default - - - - Large - - - -
    -
    -
    - - - - React Input group Checkboxes and radios - - -

    - Place any checkbox or radio option within an input group's addon instead of text. -

    - - - - - - - - - - - - - - -
    -
    -
    - - - - React Input group Multiple inputs - - -

    - While multiple <CFormControl>s are supported visually, validation - styles are only available for input groups with a single{' '} - <CFormControl>. -

    - - - First and last name - - - - -
    -
    -
    - - - - React Input group Multiple addons - - -

    - Multiple add-ons are supported and can be mixed with checkbox and radio input - versions.. -

    - - - $ - 0.00 - - - - - $ - 0.00 - - -
    -
    -
    - - - - React Input group Button addons - - -

    - Multiple add-ons are supported and can be mixed with checkbox and radio input - versions.. -

    - - - - Button - - - - - - - Button - - - - - Button - - - Button - - - - - - - Button - - - Button - - - -
    -
    -
    - - - - React Input group Buttons with dropdowns - - - - - - - Dropdown - - - Action - Another action - Something else here - - Separated link - - - - - - - - - Dropdown - - - Action - Another action - Something else here - - Separated link - - - - - - - Dropdown - - - Action - Another action - Something else here - - Separated link - - - - - - Dropdown - - - Action - Another action - Something else here - - Separated link - - - - - - - - - - - React Input group Segmented buttons - - - - - - - Action - - - - Action - Another action - Something else here - - Separated link - - - - - - - - - Action - - - - Action - Another action - Something else here - - Separated link - - - - - - - - - - - React Input group Custom select - - - - - - Options - - - - - - - - - - - - - - - - - Options - - - - - Button - - - - - - - - - - - - - - - - - Button - - - - - - - - - - React Input group Custom file input - - - - - - Upload - - - - - - - Upload - - - - - Button - - - - - - - Button - - - - - - -
    - ) -} - -export default Select diff --git a/src/views/components/forms/layout/Layout.js b/src/views/components/forms/layout/Layout.js deleted file mode 100644 index d6b1ce6b5..000000000 --- a/src/views/components/forms/layout/Layout.js +++ /dev/null @@ -1,414 +0,0 @@ -import React from 'react' -import { - CButton, - CCard, - CCardBody, - CCardHeader, - CCol, - CForm, - CFormCheck, - CFormControl, - CFormLabel, - CFormSelect, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import { Example } from 'src/reusable' - -const Layout = () => { - return ( - - - - - Layout Form grid - - -

    - More complex forms can be built using our grid classes. Use these for form layouts - that require multiple columns, varied widths, and additional alignment options. -

    - - - - - - - - - - -
    -
    -
    - - - - Layout Gutters - - -

    - By adding gutter modifier classes - , you can have control over the gutter width in as well the inline as block direction. -

    - - - - - - - - - - -

    - More complex layouts can also be created with the grid system. -

    - - - - Email - - - - Password - - - - Address - - - - Address 2 - - - - City - - - - State - - - - - - - Zip - - - - - - - Sign in - - - -
    -
    -
    - - - - Layout Horizontal form - - -

    - Create horizontal forms with the grid by adding the .row class to form - groups and using the .col-*-* classes to specify the width of your labels - and controls. Be sure to add .col-form-label to your{' '} - <CFormLabel>s as well so they're vertically centered with their - associated form controls. -

    -

    - At times, you maybe need to use margin or padding utilities to create that perfect - alignment you need. For example, we've removed the padding-top on our - stacked radio inputs label to better align the text baseline. -

    - - - - - Email - - - - - - - - Password - - - - - -
    - Radios - - - - - -
    - -
    - -
    -
    - Sign in -
    -
    -
    -
    -
    - - - - Layout Horizontal form label sizing - - -

    - Be sure to use .col-form-label-sm or .col-form-label-lg to - your <CFormLabel>s or <legend>s to correctly - follow the size of .form-control-lg and .form-control-sm. -

    - - - - Email - - - - - - - - Email - - - - - - - - Email - - - - - - -
    -
    -
    - - - - Layout Column sizing - - -

    - As shown in the previous examples, our grid system allows you to place any number of{' '} - <CCol>s within a <CRow>. They'll split the - available width equally between them. You may also pick a subset of your columns to - take up more or less space, while the remaining <CCol>s equally - split the rest, with specific column classes like{' '} - <CCol sm="7">. -

    - - - - - - - - - - - - - -
    -
    -
    - - - - Layout Auto-sizing - - -

    - The example below uses a flexbox utility to vertically center the contents and changes{' '} - <CCol> to <CCol xs="auto"> so that your - columns only take up as much space as needed. Put another way, the column sizes itself - based on the contents. -

    - - - - - Name - - - - - - Username - - - @ - - - - - - Preference - - - - - - - - - - - - - Submit - - - -

    - You can then remix that once again with size-specific column classes. -

    - - - - - Name - - - - - - Username - - - @ - - - - - - Preference - - - - - - - - - - - - - Submit - - - -
    -
    -
    - - - - Layout Inline forms - - -

    - Use the <CCol xs="auto"> class to create horizontal - layouts. By adding{' '} - gutter modifier classes, we will - have gutters in horizontal and vertical directions. The{' '} - .align-items-center aligns the form elements to the middle, making the{' '} - <CFormCheck> align properly. -

    - - - - - Username - - - @ - - - - - - Preference - - - - - - - - - - - - - Submit - - - -
    -
    -
    -
    - ) -} - -export default Layout diff --git a/src/views/components/forms/range/Range.js b/src/views/components/forms/range/Range.js deleted file mode 100644 index d07dd8f4b..000000000 --- a/src/views/components/forms/range/Range.js +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCardHeader, CCol, CFormLabel, CFormRange, CRow } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Range = () => { - return ( - - - - - - - - React Range - - -

    - Create custom <input type="range"> controls with{' '} - <CFormControl>. -

    - - Example range - - -
    -
    -
    - - - - React Range Disabled - - -

    - Add the disabled boolean attribute on an input to give it a grayed out - appearance and remove pointer events. -

    - - Disabled range - - -
    -
    -
    - - - - React Range Min and max - - -

    - Range inputs have implicit values for min and max— - 0 and 100, respectively. You may specify new values for - those using the min and max attributes. -

    - - Example range - - -
    -
    -
    - - - - React Range Steps - - -

    - By default, range inputs "snap" to integer values. To change this, you can - specify a step value. In the example below, we double the number of steps - by using step="0.5". -

    - - Example range - - -
    -
    -
    -
    - ) -} - -export default Range diff --git a/src/views/components/forms/select/Select.js b/src/views/components/forms/select/Select.js deleted file mode 100644 index 8b91c5f9b..000000000 --- a/src/views/components/forms/select/Select.js +++ /dev/null @@ -1,114 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCardHeader, CCol, CFormSelect, CRow } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Select = () => { - return ( - - - - - - - - React Select Default - - - - - - - - - - - - - - - - - React Select Sizing - - -

    - You may also choose from small and large custom selects to match our similarly sized - text inputs. -

    - - - - - - - - - - - - - - -

    - The multiple attribute is also supported: -

    - - - - - - - - -

    - As is the htmlSize property: -

    - - - - - - - - -
    -
    -
    - - - - React Select Disabled - - -

    - Add the disabled boolean attribute on a select to give it a grayed out - appearance and remove pointer events. -

    - - - - - - - - -
    -
    -
    - {/* - - - React Select - - - - - - - - */} -
    - ) -} - -export default Select diff --git a/src/views/components/forms/validation/Validation.js b/src/views/components/forms/validation/Validation.js deleted file mode 100644 index 001b4a2a8..000000000 --- a/src/views/components/forms/validation/Validation.js +++ /dev/null @@ -1,506 +0,0 @@ -import React, { useState } from 'react' -import { - CButton, - CCard, - CCardBody, - CCardHeader, - CCol, - CForm, - CFormCheck, - CFormControl, - CFormFeedback, - CFormLabel, - CFormSelect, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const CustomStyles = () => { - const [validated, setValidated] = useState(false) - const handleSubmit = (event) => { - const form = event.currentTarget - if (form.checkValidity() === false) { - event.preventDefault() - event.stopPropagation() - } - setValidated(true) - } - return ( - - - Email - - Looks good! - - - Email - - Looks good! - - - Username - - @ - - Please choose a username. - - - - City - - Please provide a valid city. - - - City - - - - - Please provide a valid city. - - - City - - Please provide a valid zip. - - - - You must agree before submitting. - - - - Submit form - - - - ) -} - -const BrowserDefaults = () => { - const [validated, setValidated] = useState(false) - const handleSubmit = (event) => { - const form = event.currentTarget - if (form.checkValidity() === false) { - event.preventDefault() - event.stopPropagation() - } - setValidated(true) - } - return ( - - - Email - - Looks good! - - - Email - - Looks good! - - - Username - - @ - - Please choose a username. - - - - City - - Please provide a valid city. - - - City - - - - - Please provide a valid city. - - - City - - Please provide a valid zip. - - - - You must agree before submitting. - - - - Submit form - - - - ) -} - -const Tooltips = () => { - const [validated, setValidated] = useState(false) - const handleSubmit = (event) => { - const form = event.currentTarget - if (form.checkValidity() === false) { - event.preventDefault() - event.stopPropagation() - } - setValidated(true) - } - return ( - - - Email - - - Looks good! - - - - Email - - - Looks good! - - - - Username - - @ - - - Please choose a username. - - - - - City - - - Please provide a valid city. - - - - City - - - - - - Please provide a valid city. - - - - City - - - Please provide a valid zip. - - - - - Submit form - - - - ) -} - -const Validation = () => { - return ( - - - - - - - - Validation Custom styles - - -

    - For custom CoreUI form validation messages, you'll need to add the{' '} - noValidate boolean property to your <CForm>. This - disables the browser default feedback tooltips, but still provides access to the form - validation APIs in JavaScript. Try to submit the form below; our JavaScript will - intercept the submit button and relay feedback to you. When attempting to submit, - you'll see the :invalid and :valid styles applied to - your form controls. -

    -

    - Custom feedback styles apply custom colors, borders, focus styles, and background - icons to better communicate feedback.{' '} -

    - {CustomStyles()} -
    -
    -
    - - - - Validation Browser defaults - - -

    - Not interested in custom validation feedback messages or writing JavaScript to change - form behaviors? All good, you can use the browser defaults. Try submitting the form - below. Depending on your browser and OS, you'll see a slightly different style of - feedback. -

    -

    - While these feedback styles cannot be styled with CSS, you can still customize the - feedback text through JavaScript. -

    - {BrowserDefaults()} -
    -
    -
    - - - - Validation Server side - - -

    - We recommend using client-side validation, but in case you require server-side - validation, you can indicate invalid and valid form fields with invalid{' '} - and valid boolean properties. -

    -

    - For invalid fields, ensure that the invalid feedback/error message is associated with - the relevant form field using aria-describedby (noting that this - attribute allows more than one id to be referenced, in case the field - already points to additional form text). -

    - - - - Email - - Looks good! - - - Email - - Looks good! - - - Username - - @ - - Please choose a username. - - - - City - - Please provide a valid city. - - - City - - - - - Please provide a valid city. - - - City - - Please provide a valid zip. - - - - You must agree before submitting. - - - - Submit form - - - - -
    -
    -
    - - - - Validation Supported elements - - -

    - Validation styles are available for the following form controls and components: -

    -
      -
    • - <CFormControl>s -
    • -
    • - <CFormSelect>s -
    • -
    • - <CFormCheck>s -
    • -
    - - -
    - - Textarea - - - Please enter a message in the textarea. -
    - - Example invalid feedback text - - - - - More example invalid feedback text - -
    - - - - - - - Example invalid select feedback -
    - -
    - - Example invalid form file feedback -
    - -
    - - Submit form - -
    -
    -
    -
    -
    -
    - - - - Validation Tooltips - - -

    - If your form layout allows it, you can swap the text for the tooltip to display - validation feedback in a styled tooltip. Be sure to have a parent with{' '} - position: relative on it for tooltip positioning. In the example below, - our column classes have this already, but your project may require an alternative - setup. -

    - {Tooltips()} -
    -
    -
    -
    - ) -} - -export default Validation diff --git a/src/views/components/icons/brands/Brands.js b/src/views/components/icons/brands/Brands.js deleted file mode 100644 index 9532c8f9b..000000000 --- a/src/views/components/icons/brands/Brands.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCardHeader, CCol, CRow } from '@coreui/react' -import CIcon from '@coreui/icons-react' -import { brandSet } from '@coreui/icons' -import { DocsLink } from 'src/reusable' - -const toKebabCase = (str) => { - return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase() -} - -export const getIconsView = (iconset) => { - return Object.entries(iconset).map(([name, value]) => ( - - -
    {toKebabCase(name)}
    -
    - )) -} - -const CoreUIIcons = () => { - return ( - - - Brand Icons - - - - {getIconsView(brandSet)} - - - ) -} - -export default CoreUIIcons diff --git a/src/views/components/icons/coreui-icons/CoreUIIcons.js b/src/views/components/icons/coreui-icons/CoreUIIcons.js deleted file mode 100644 index 2145cec2c..000000000 --- a/src/views/components/icons/coreui-icons/CoreUIIcons.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react' -import { freeSet } from '@coreui/icons' -import { getIconsView } from '../brands/Brands.js' -import { DocsLink } from 'src/reusable' - -const CoreUIIcons = () => { - return ( - - - Free Icons / as CIcon{' '} - - - - {getIconsView(freeSet)} - - - ) -} - -export default CoreUIIcons diff --git a/src/views/components/icons/flags/Flags.js b/src/views/components/icons/flags/Flags.js deleted file mode 100644 index 146930b49..000000000 --- a/src/views/components/icons/flags/Flags.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react' -import { getIconsView } from '../brands/Brands.js' -import { flagSet } from '@coreui/icons' -import { DocsLink } from 'src/reusable' - -const CoreUIIcons = () => { - return ( - - - Flag Icons - - - - {getIconsView(flagSet)} - - - ) -} - -export default CoreUIIcons diff --git a/src/views/components/icons/index.js b/src/views/components/icons/index.js deleted file mode 100644 index 92db64e57..000000000 --- a/src/views/components/icons/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import CoreUIIcons from './coreui-icons' -import Flags from './flags' -import Brands from './brands' - -export { CoreUIIcons, Flags, Brands } diff --git a/src/views/components/notifications/alerts/Alerts.js b/src/views/components/notifications/alerts/Alerts.js deleted file mode 100644 index b4b60fa5d..000000000 --- a/src/views/components/notifications/alerts/Alerts.js +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react' -import { - CAlert, - CAlertHeading, - CAlertLink, - CCard, - CCardBody, - CCardHeader, - CCol, - CRow, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Alerts = () => { - return ( - - - - - - - - React Alert - - -

    - React Alert is prepared for any length of text, as well as an optional close button. - For a styling, use one of the required contextual color{' '} - props (e.g., primary). For inline dismissal, use the{' '} - - dismissing prop - - . -

    - - A simple primary alert—check it out! - A simple secondary alert—check it out! - A simple success alert—check it out! - A simple danger alert—check it out! - A simple warning alert—check it out! - A simple info alert—check it out! - A simple light alert—check it out! - A simple dark alert—check it out! - -
    -
    -
    - - - - React Alert Link color - - -

    - Use the <CAlertLink> component to immediately give matching colored - links inside any alert. -

    - - - A simple primary alert with an example link. Give - it a click if you like. - - - A simple secondary alert with an example link. - Give it a click if you like. - - - A simple success alert with an example link. Give - it a click if you like. - - - A simple danger alert with an example link. Give - it a click if you like. - - - A simple warning alert with an example link. Give - it a click if you like. - - - A simple info alert with an example link. Give it - a click if you like. - - - A simple light alert with an example link. Give it - a click if you like. - - - A simple dark alert with an example link. Give it - a click if you like. - - -
    -
    -
    - - - - React Alert Additional content - - -

    - Alert can also incorporate supplementary components & elements like heading, - paragraph, and divider. -

    - - - Well done! -

    - Aww yeah, you successfully read this important alert message. This example text is - going to run a bit longer so that you can see how spacing within an alert works - with this kind of content. -

    -
    -

    - Whenever you need to, be sure to use margin utilities to keep things nice and - tidy. -

    -
    -
    -
    -
    -
    - - - - React Alert Dismissing - - -

    - Alerts can also be easily dismissed. Just add the dismissible prop. -

    - - { - alert('👋 Well, hi there! Thanks for dismissing me.') - }} - > - Go right ahead and click that dimiss over there on the right. - - -
    -
    -
    -
    - ) -} - -export default Alerts diff --git a/src/views/components/notifications/badges/Badges.js b/src/views/components/notifications/badges/Badges.js deleted file mode 100644 index 1297c910f..000000000 --- a/src/views/components/notifications/badges/Badges.js +++ /dev/null @@ -1,125 +0,0 @@ -import React from 'react' -import { CButton, CCard, CCardBody, CCardHeader, CCol, CBadge, CRow } from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const Badges = () => { - return ( - - - - - - - - React Badges Dismissing - - -

    - Bootstrap badge scale to suit the size of the parent element by using relative font - sizing and em units. -

    - -

    - Example heading New -

    -

    - Example heading New -

    -

    - Example heading New -

    -

    - Example heading New -

    -
    - Example heading New -
    -
    - Example heading New -
    -
    -

    - Badges can be used as part of links or buttons to provide a counter. -

    - - - Notifications 4 - - -

    - Remark that depending on how you use them, badges may be complicated for users of - screen readers and related assistive technologies. -

    -

    - Unless the context is clear, consider including additional context with a visually - hidden piece of additional text. -

    - - - Profile 9 - unread messages - - -
    -
    -
    - - - - React Badges Contextual variations - - -

    - Add any of the below-mentioned color props to modify the presentation of - a badge. -

    - - primary - success - danger - warning - info - light - dark - -
    -
    - - - React Badges Pill badges - - -

    - Apply the shape="rounded-pill" prop to make badges rounded. -

    - - - primary - - - success - - - danger - - - warning - - - info - - - light - - - dark - - -
    -
    -
    -
    - ) -} - -export default Badges diff --git a/src/views/components/notifications/index.js b/src/views/components/notifications/index.js deleted file mode 100644 index 08e31cd72..000000000 --- a/src/views/components/notifications/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import Alerts from './Alerts' -import Badges from './Badges' -import Modals from './Modals' -import Toaster from './toasts' - -export { Alerts, Badges, Modals, Toaster } diff --git a/src/views/components/notifications/modals/Modals.js b/src/views/components/notifications/modals/Modals.js deleted file mode 100644 index 7631f0fa8..000000000 --- a/src/views/components/notifications/modals/Modals.js +++ /dev/null @@ -1,717 +0,0 @@ -import React, { useState } from 'react' -import { - CButton, - CCard, - CCardBody, - CCardHeader, - CCol, - CLink, - CModal, - CModalBody, - CModalFooter, - CModalHeader, - CModalTitle, - CPopover, - CRow, - CTooltip, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const LiveDemo = () => { - const [visible, setVisible] = useState(false) - return ( - <> - setVisible(!visible)}>Launch demo modal - setVisible(false)}> - setVisible(false)}> - Modal title - - Woohoo, you're reading this text in a modal! - - setVisible(false)}> - Close - - Save changes - - - - ) -} - -const StaticBackdrop = () => { - const [visible, setVisible] = useState(false) - return ( - <> - setVisible(!visible)}>Launch static backdrop modal - - setVisible(false)}> - Modal title - - - I will not close if you click outside me. Don'teven try to press escape key. - - - setVisible(false)}> - Close - - Save changes - - - - ) -} - -const ScrollingLongContent = () => { - const [visible, setVisible] = useState(false) - return ( - <> - setVisible(!visible)}>Launch demo modal - setVisible(false)}> - setVisible(false)}> - Modal title - - -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -
    - - setVisible(false)}> - Close - - Save changes - -
    - - ) -} - -const ScrollingLongContent2 = () => { - const [visible, setVisible] = useState(false) - return ( - <> - setVisible(!visible)}>Launch demo modal - setVisible(false)}> - setVisible(false)}> - Modal title - - -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -
    - - setVisible(false)}> - Close - - Save changes - -
    - - ) -} - -const VerticallyCentered = () => { - const [visible, setVisible] = useState(false) - return ( - <> - setVisible(!visible)}>Vertically centered modal - - setVisible(false)}> - Modal title - - - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. - - - setVisible(false)}> - Close - - Save changes - - - - ) -} - -const VerticallyCentered2 = () => { - const [visible, setVisible] = useState(false) - return ( - <> - setVisible(!visible)}>Vertically centered scrollable modal - - setVisible(false)}> - Modal title - - -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -

    - Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel - scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus - auctor fringilla. -

    -

    - Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis - in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. -

    -

    - Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis - lacus vel augue laoreet rutrum faucibus dolor auctor. -

    -
    - - setVisible(false)}> - Close - - Save changes - -
    - - ) -} - -const TooltipsPopovers = () => { - const [visible, setVisible] = useState(false) - return ( - <> - setVisible(!visible)}>Launch demo modal - - setVisible(false)}> - Modal title - - -
    Popover in a modal
    -

    - This - - button - {' '} - triggers a popover on click. -

    -
    -
    Tooltips in a modal
    -

    - - This link - {' '} - and - - that link - {' '} - have tooltips on hover. -

    -
    - - setVisible(false)}> - Close - - Save changes - -
    - - ) -} - -const OptionalSizes = () => { - const [visibleXL, setVisibleXL] = useState(false) - const [visibleLg, setVisibleLg] = useState(false) - const [visibleSm, setVisibleSm] = useState(false) - return ( - <> - setVisibleXL(!visibleXL)}>Extra large modal - setVisibleLg(!visibleLg)}>Large modal - setVisibleSm(!visibleSm)}>Small large modal - - setVisibleXL(false)}> - Extra large modal - - ... - - - setVisibleLg(false)}> - Large modal - - ... - - - setVisibleSm(false)}> - Small modal - - ... - - - ) -} - -const FullscreenModal = () => { - const [visible, setVisible] = useState(false) - const [visibleSm, setVisibleSm] = useState(false) - const [visibleMd, setVisibleMd] = useState(false) - const [visibleLg, setVisibleLg] = useState(false) - const [visibleXL, setVisibleXL] = useState(false) - const [visibleXXL, setVisibleXXL] = useState(false) - - return ( - <> - setVisible(!visible)}>Full screen - setVisibleSm(!visibleSm)}>Full screen below sm - setVisibleMd(!visibleMd)}>Full screen below md - setVisibleLg(!visibleLg)}>Full screen below lg - setVisibleXL(!visibleXL)}>Full screen below xl - setVisibleXXL(!visibleXXL)}>Full screen below xxl - - setVisible(false)}> - Full screen - - ... - - - setVisibleSm(false)}> - Full screen below sm - - ... - - - setVisibleMd(false)}> - Full screen below md - - ... - - - setVisibleLg(false)}> - Full screen below lg - - ... - - - setVisibleXL(false)}> - Full screen below xl - - ... - - - setVisibleXXL(false)}> - Full screen below xxl - - ... - - - ) -} - -const Modals = () => { - return ( - - - - - - - - React Modal - - -

    - Below is a static modal example (meaning its position and{' '} - display have been overridden). Included are the modal header, modal body - (required for padding), and modal footer (optional). We ask that you - include modal headers with dismiss actions whenever possible, or provide another - explicit dismiss action. -

    - - - - Modal title - - Modal body text goes here. - - Close - Save changes - - - -
    -
    -
    - - - - React Modal Live demo - - -

    - Toggle a working modal demo by clicking the button below. It will slide down and fade - in from the top of the page. -

    - {LiveDemo()} -
    -
    -
    - - - - React Modal Static backdrop - - -

    - If you don’t provide an onDimsiss handler to the Modal component, your - modal will behave as though the backdrop is static, meaning it will not close when - clicking outside it. Click the button below to try it. -

    - {StaticBackdrop()} -
    -
    -
    - - - - React Modal Scrolling long content - - -

    - If you don’t provide an onDimsiss handler to the Modal component, your - modal will behave as though the backdrop is static, meaning it will not close when - clicking outside it. Click the button below to try it. -

    - - {ScrollingLongContent()} - -

    - You can also create a scrollable modal that allows scroll the modal body by adding{' '} - scrollable prop. -

    - - {ScrollingLongContent2()} - -
    -
    -
    - - - - React Modal Vertically centered - - -

    - Add alignment="center" to <CModal> to - vertically center the modal. -

    - {VerticallyCentered()} - {VerticallyCentered2()} -
    -
    -
    - - - - React Modal Tooltips and popovers - - -

    - <CTooltips> and <CPopovers> can be placed within - modals as needed. When modals are closed, any tooltips and popovers within are also - automatically dismissed. -

    - {TooltipsPopovers()} -
    -
    -
    - - - - React Modal Optional sizes - - -

    - Modals have three optional sizes, available via modifier classes to be placed on a{' '} - <CModal>. These sizes kick in at certain breakpoints to avoid - horizontal scrollbars on narrower viewports. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SizeProperty sizeModal max-width
    Small - 'sm' - - 300px -
    DefaultNone - 500px -
    Large - 'lg' - - 800px -
    Extra large - 'xl' - - 1140px -
    - {OptionalSizes()} -
    -
    -
    - - - - React Modal Fullscreen Modal - - -

    - Another override is the option to pop up a modal that covers the user viewport, - available via property fullscrean. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Property fullscreanAvailability
    - true - Always
    - 'sm' - - Below 576px -
    - 'md' - - Below 768px -
    - 'lg' - - Below 992px -
    - 'xl' - - Below 1200px -
    - 'xxl' - - Below 1400px -
    - {FullscreenModal()} -
    -
    -
    -
    - ) -} - -export default Modals diff --git a/src/views/components/notifications/toasts/Toasts.js b/src/views/components/notifications/toasts/Toasts.js deleted file mode 100644 index 1cdcf9069..000000000 --- a/src/views/components/notifications/toasts/Toasts.js +++ /dev/null @@ -1,250 +0,0 @@ -import React, { useRef, useState } from 'react' -import { - CCard, - CCardHeader, - CCardBody, - CButton, - CRow, - CCol, - CToast, - CToastBody, - CToastClose, - CToastHeader, - CToaster, -} from '@coreui/react' -import { DocsCallout, Example } from 'src/reusable' - -const ExampleToast = () => { - const [toast, addToast] = useState(0) - const toaster = useRef() - const exampleToast = ( - - - - - - CoreUI for React.js - 7 min ago - - Hello, world! This is a toast message. - - ) - return ( - <> - addToast(exampleToast)}>Send a toast - - - ) -} - -const Toasts = () => { - return ( - - - - - - - - React Toast Basic - - -

    - Toasts are as flexible as you need and have very little required markup. At a minimum, - we require a single element to contain your “toasted” content and strongly encourage a - dismiss button. -

    - - - - - - - CoreUI for React.js - 7 min ago - - Hello, world! This is a toast message. - - - {ExampleToast()} -
    -
    -
    - - - - React Toast Translucent - - -

    - Toasts are slightly translucent to blend in with what's below them. -

    - -
    - - - - - - CoreUI for React.js - 7 min ago - - Hello, world! This is a toast message. - -
    -
    -
    -
    -
    - - - - React Toast Stacking - - -

    - You can stack toasts by wrapping them in a toast container, which will vertically add - some spacing. -

    - - - - - - - - CoreUI for React.js - 7 min ago - - Hello, world! This is a toast message. - - - - - - - CoreUI for React.js - 7 min ago - - Hello, world! This is a toast message. - - - -
    -
    -
    - - - - React Toast Custom content - - -

    - Customize your toasts by removing sub-components, tweaking them with{' '} - utilities, or by adding your - own markup. Here we've created a simpler toast by removing the default{' '} - <CToastHeader>, adding a custom hide icon from{' '} - CoreUI Icons, and using some{' '} - flexbox utilities to adjust - the layout. -

    - - -
    - Hello, world! This is a toast message. - -
    -
    -
    -

    - Alternatively, you can also add additional controls and components to toasts. -

    - - - - Hello, world! This is a toast message. -
    - - Take action - - - Close - -
    -
    -
    -
    -
    -
    -
    - - - - React Toast Custom content - - -

    - Building on the above example, you can create different toast color schemes with our{' '} - color and{' '} - background utilities. - Here we've set color="primary" and added{' '} - .text-white class to the <Ctoast>, and then set{' '} - white property to our close button. For a crisp edge, we remove the - default border with .border-0. -

    - - -
    - Hello, world! This is a toast message. - -
    -
    -
    -
    -
    -
    -
    - ) -} - -export default Toasts diff --git a/src/views/components/widgets/Widgets.js b/src/views/components/widgets/Widgets.js deleted file mode 100644 index 71f677c9e..000000000 --- a/src/views/components/widgets/Widgets.js +++ /dev/null @@ -1,784 +0,0 @@ -import React from 'react' -import { - CCardGroup, - CCol, - CLink, - CRow, - CWidgetIcon, - CWidgetProgress, - CWidgetProgressIcon, - CWidgetSimple, -} from '@coreui/react' -import { getStyle } from '@coreui/utils' -import CIcon from '@coreui/icons-react' -import { CChartBar, CChartLine } from '@coreui/react-chartjs' - -import WidgetsBrand from './WidgetsBrand' -import WidgetsDropdown from './WidgetsDropdown' - -const Widgets = () => { - const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min) - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="primary" - /> - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="info" - /> - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="warning" - /> - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="danger" - /> - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="primary" - footer={ - - View more - - - } - /> - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="info" - footer={ - - View more - - - } - /> - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="warning" - footer={ - - View more - - - } - /> - - - } - iconPadding={3} - title="income" - value="$1.999,50" - color="danger" - footer={ - - View more - - - } - /> - - - } - iconPadding={4} - title="income" - value="$1.999,50" - color="primary" - /> - - - } - iconPadding={4} - title="income" - value="$1.999,50" - color="info" - /> - - - } - iconPadding={4} - title="income" - value="$1.999,50" - color="warning" - /> - - - } - iconPadding={4} - title="income" - value="$1.999,50" - color="danger" - /> - - - - - - } - value="87.500" - title="Visitors" - progressColor="info" - progressValue={75} - /> - } - value="385" - title="New Clients" - progressColor="success" - progressValue={75} - /> - } - value="1238" - title="Products sold" - progressColor="warning" - progressValue={75} - /> - } - value="28%" - title="Returning Visitors" - progressValue={75} - /> - } - value="5:34:11" - title="Avg. Time" - progressColor="danger" - progressValue={75} - /> - - - - } - value="87.500" - title="Visitors" - progressColor="info" - progressValue={75} - className="mb-4" - /> - - - } - value="385" - title="New Clients" - progressColor="success" - progressValue={75} - className="mb-4" - /> - - - } - value="1238" - title="Products sold" - progressColor="warning" - progressValue={75} - className="mb-4" - /> - - - } - value="28%" - title="Returning Visitors" - progressColor="primary" - progressValue={75} - className="mb-4" - /> - - - } - value="5:34:11" - title="Avg. Time" - progressColor="danger" - progressValue={75} - className="mb-4" - /> - - - } - value="972" - title="comments" - progressColor="info" - progressValue={75} - className="mb-4" - /> - - - - - } - value="87.500" - title="Visitors" - progressValue={75} - progressWhite - className="mb-4" - /> - - - } - value="385" - title="New Clients" - progressValue={75} - progressWhite - className="mb-4" - /> - - - } - value="1238" - title="Products sold" - progressValue={75} - progressWhite - className="mb-4" - /> - - - } - value="28%" - title="Returning Visitors" - progressValue={75} - progressWhite - className="mb-4" - /> - - - } - value="5:34:11" - title="Avg. Time" - progressValue={75} - progressWhite - className="mb-4" - /> - - - } - value="972" - title="comments" - progressValue={75} - progressWhite - className="mb-4" - /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default Widgets diff --git a/src/views/components/widgets/WidgetsBrand.js b/src/views/components/widgets/WidgetsBrand.js deleted file mode 100644 index 82e42c0ab..000000000 --- a/src/views/components/widgets/WidgetsBrand.js +++ /dev/null @@ -1,195 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { CWidgetBrand, CRow, CCol } from '@coreui/react' -import CIcon from '@coreui/icons-react' -import { CChart } from '@coreui/react-chartjs' - -const WidgetsBrand = ({ withCharts }) => { - const chartOptions = { - elements: { - line: { - tension: 0.4, - }, - point: { - radius: 0, - hitRadius: 10, - hoverRadius: 4, - hoverBorderWidth: 3, - }, - }, - maintainAspectRatio: false, - plugins: { - legend: { - display: false, - }, - }, - scales: { - x: { - display: false, - }, - y: { - display: false, - }, - }, - } - - return ( - - - - - {withCharts && ( - - )} - - } - values={[ - ['89k', 'friends'], - ['459', 'feeds'], - ]} - style={{ - '--cui-card-cap-bg': '#3b5998', - }} - /> - - - - - - {withCharts && ( - - )} - - } - values={[ - ['973k', 'followers'], - ['1.792', 'tweets'], - ]} - style={{ - '--cui-card-cap-bg': '#00aced', - }} - /> - - - - - - {withCharts && ( - - )} - - } - values={[ - ['500+', 'contacts'], - ['292', 'feeds'], - ]} - style={{ - '--cui-card-cap-bg': '#4875b4', - }} - /> - - - - - - {withCharts && ( - - )} - - } - values={[ - ['12+', 'events'], - ['4', 'meetings'], - ]} - /> - - - ) -} - -WidgetsBrand.propTypes = { - withCharts: PropTypes.bool, -} - -export default WidgetsBrand diff --git a/src/views/components/widgets/WidgetsDropdown.js b/src/views/components/widgets/WidgetsDropdown.js deleted file mode 100644 index c95a8272c..000000000 --- a/src/views/components/widgets/WidgetsDropdown.js +++ /dev/null @@ -1,367 +0,0 @@ -import React from 'react' -import { - CRow, - CCol, - CDropdown, - CDropdownMenu, - CDropdownItem, - CDropdownToggle, - CWidgetDropdown, -} from '@coreui/react' -import { getStyle } from '@coreui/utils' -import { CChartBar, CChartLine } from '@coreui/react-chartjs' -import CIcon from '@coreui/icons-react' - -const WidgetsDropdown = () => { - return ( - - - - (-12.4% ) - - } - title="Users" - action={ - - - - - - Action - Another action - Something else here... - Disabled action - - - } - chart={ - - } - /> - - - - (40.9% ) - - } - title="Income" - action={ - - - - - - Action - Another action - Something else here... - Disabled action - - - } - chart={ - { - console.log('init1') - console.log(dataset) - console.log(event) - }} - getElementAtEvent={(element, event) => { - console.log('init2') - console.log(element) - console.log(event) - }} - getElementsAtEvent={(element, event) => { - console.log('init3') - console.log(element) - console.log(event) - }} - options={{ - plugins: { - legend: { - display: false, - }, - }, - maintainAspectRatio: false, - scales: { - x: { - grid: { - display: false, - drawBorder: false, - }, - ticks: { - display: false, - }, - }, - y: { - min: -9, - max: 39, - display: false, - grid: { - display: false, - }, - ticks: { - display: false, - }, - }, - }, - elements: { - line: { - borderWidth: 1, - }, - point: { - radius: 4, - hitRadius: 10, - hoverRadius: 4, - }, - }, - }} - /> - } - /> - - - - (84.7% ) - - } - title="Conversion Rate" - action={ - - - - - - Action - Another action - Something else here... - Disabled action - - - } - chart={ - - } - /> - - - - (-23.6% ) - - } - title="Sessions" - action={ - - - - - - Action - Another action - Something else here... - Disabled action - - - } - chart={ - - } - /> - - - ) -} - -export default WidgetsDropdown diff --git a/src/views/dashboard/Dashboard.js b/src/views/dashboard/Dashboard.js deleted file mode 100644 index 431ed524c..000000000 --- a/src/views/dashboard/Dashboard.js +++ /dev/null @@ -1,590 +0,0 @@ -import React, { lazy } from 'react' - -import { - CAvatar, - CButton, - CButtonGroup, - CCard, - CCardBody, - CCardFooter, - CCardHeader, - CCol, - CProgress, - CRow, - CTable, - CTableBody, - CTableDataCell, - CTableHead, - CTableHeaderCell, - CTableRow, -} from '@coreui/react' -import { CChartLine } from '@coreui/react-chartjs' -import { getStyle, hexToRgba } from '@coreui/utils' -import CIcon from '@coreui/icons-react' - -const WidgetsDropdown = lazy(() => import('../components/widgets/WidgetsDropdown.js')) -const WidgetsBrand = lazy(() => import('../components/widgets/WidgetsBrand.js')) - -const Dashboard = () => { - const random = (min, max) => { - return Math.floor(Math.random() * (max - min + 1) + min) - } - - return ( - <> - - - - - -

    - Traffic -

    -
    January - July 2021
    -
    - - - - - - {['Day', 'Month', 'Year'].map((value) => ( - - {value} - - ))} - - -
    - -
    - - - -
    Visits
    - 29.703 Users (40%) - -
    - -
    Unique
    - 24.093 Users (20%) - -
    - -
    Pageviews
    - 78.706 Views (60%) - -
    - -
    New Users
    - 22.123 Users (80%) - -
    - -
    Bounce Rate
    - Average Rate (40.15%) - -
    -
    -
    -
    - - - - - - - Traffic {' & '} Sales - - - - - -
    -
    New Clients
    -
    9,123
    -
    -
    - -
    -
    Recurring Clients
    -
    22,643
    -
    -
    -
    - -
    - -
    -
    - Monday -
    -
    - - -
    -
    -
    -
    - Tuesday -
    -
    - - -
    -
    -
    -
    - Wednesday -
    -
    - - -
    -
    -
    -
    - Thursday -
    -
    - - -
    -
    -
    -
    - Friday -
    -
    - - -
    -
    -
    -
    - Saturday -
    -
    - - -
    -
    -
    -
    - Sunday -
    -
    - - -
    -
    -
    - - - - -
    -
    Pageviews
    -
    78,623
    -
    -
    - -
    -
    Organic
    -
    49,123
    -
    -
    -
    - -
    - -
    -
    - - Male - 43% -
    -
    - -
    -
    -
    -
    - - Female - 37% -
    -
    - -
    -
    - -
    -
    - - Organic Search - - 191,235 (56%) - -
    -
    - -
    -
    -
    -
    - - Facebook - - 51,223 (15%) - -
    -
    - -
    -
    -
    -
    - - Twitter - - 37,564 (11%) - -
    -
    - -
    -
    -
    -
    - - LinkedIn - - 27,319 (8%) - -
    -
    - -
    -
    -
    -
    - -
    - - - - - - - - User - Country - Usage - Payment Method - Activity - - - - - - - - -
    Yiorgos Avraamu
    -
    - New | Registered: Jan 1, 2015 -
    -
    - - - - -
    -
    - 50% -
    -
    - - Jun 11, 2015 - Jul 10, 2015 - -
    -
    - -
    - - - - -
    Last login
    - 10 sec ago -
    -
    - - - - - -
    Avram Tarasios
    -
    - Recurring | Registered: Jan 1, 2015 -
    -
    - - - - -
    -
    - 10% -
    -
    - - Jun 11, 2015 - Jul 10, 2015 - -
    -
    - -
    - - - - -
    Last login
    - 5 minutes ago -
    -
    - - - - - -
    Quintin Ed
    -
    - New | Registered: Jan 1, 2015 -
    -
    - - - - -
    -
    - 74% -
    -
    - - Jun 11, 2015 - Jul 10, 2015 - -
    -
    - -
    - - - - -
    Last login
    - 1 hour ago -
    -
    - - - - - -
    Enéas Kwadwo
    -
    - New | Registered: Jan 1, 2015 -
    -
    - - - - -
    -
    - 98% -
    -
    - - Jun 11, 2015 - Jul 10, 2015 - -
    -
    - -
    - - - - -
    Last login
    - Last month -
    -
    - - - - - -
    Agapetus Tadeáš
    -
    - New | Registered: Jan 1, 2015 -
    -
    - - - - -
    -
    - 22% -
    -
    - - Jun 11, 2015 - Jul 10, 2015 - -
    -
    - -
    - - - - -
    Last login
    - Last week -
    -
    - - - - - -
    Friderik Dávid
    -
    - New | Registered: Jan 1, 2015 -
    -
    - - - - -
    -
    - 43% -
    -
    - - Jun 11, 2015 - Jul 10, 2015 - -
    -
    - -
    - - - - -
    Last login
    - Yesterday -
    -
    -
    -
    -
    -
    -
    -
    - - ) -} - -export default Dashboard diff --git a/src/views/pages/login/Login.js b/src/views/pages/login/Login.js deleted file mode 100644 index 769ee5e9b..000000000 --- a/src/views/pages/login/Login.js +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { - CButton, - CCard, - CCardBody, - CCardGroup, - CCol, - CContainer, - CForm, - CFormControl, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import CIcon from '@coreui/icons-react' - -const Login = () => { - return ( -
    - - - - - - - -

    Login

    -

    Sign In to your account

    - - - - - - - - - - - - - - - - Login - - - - - Forgot password? - - - -
    -
    -
    - - -
    -

    Sign up

    -

    - Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod - tempor incididunt ut labore et dolore magna aliqua. -

    - - - Register Now! - - -
    -
    -
    -
    -
    -
    -
    -
    - ) -} - -export default Login diff --git a/src/views/pages/page404/Page404.js b/src/views/pages/page404/Page404.js deleted file mode 100644 index 9d74359fe..000000000 --- a/src/views/pages/page404/Page404.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' -import { - CButton, - CCol, - CContainer, - CFormControl, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import CIcon from '@coreui/icons-react' - -const Page404 = () => { - return ( -
    - - - -
    -

    404

    -

    Oops! You{"'"}re lost.

    -

    - The page you are looking for was not found. -

    -
    - - - - - - Search - -
    -
    -
    -
    - ) -} - -export default Page404 diff --git a/src/views/pages/page500/Page500.js b/src/views/pages/page500/Page500.js deleted file mode 100644 index c7925b6a1..000000000 --- a/src/views/pages/page500/Page500.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' -import { - CButton, - CCol, - CContainer, - CFormControl, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import CIcon from '@coreui/icons-react' - -const Page500 = () => { - return ( -
    - - - - -

    500

    -

    Houston, we have a problem!

    -

    - The page you are looking for is temporarily unavailable. -

    -
    - - - - - - Search - -
    -
    -
    -
    - ) -} - -export default Page500 diff --git a/src/views/pages/register/Register.js b/src/views/pages/register/Register.js deleted file mode 100644 index cc9fc63b8..000000000 --- a/src/views/pages/register/Register.js +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react' -import { - CButton, - CCard, - CCardBody, - CCol, - CContainer, - CForm, - CFormControl, - CInputGroup, - CInputGroupText, - CRow, -} from '@coreui/react' -import CIcon from '@coreui/icons-react' - -const Register = () => { - return ( -
    - - - - - - -

    Register

    -

    Create your account

    - - - - - - - - @ - - - - - - - - - - - - - - - - Create Account - -
    -
    -
    -
    -
    -
    -
    - ) -} - -export default Register diff --git a/src/views/theme/colors/Colors.js b/src/views/theme/colors/Colors.js deleted file mode 100644 index b29d8a3c1..000000000 --- a/src/views/theme/colors/Colors.js +++ /dev/null @@ -1,91 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useEffect, useState, createRef } from 'react' -import classNames from 'classnames' -import { CRow, CCol, CCard, CCardHeader, CCardBody } from '@coreui/react' -import { rgbToHex } from '@coreui/utils' -import { DocsLink } from 'src/reusable' - -const ThemeView = () => { - const [color, setColor] = useState('rgb(255, 255, 255)') - const ref = createRef() - - useEffect(() => { - const el = ref.current.parentNode.firstChild - const varColor = window.getComputedStyle(el).getPropertyValue('background-color') - setColor(varColor) - }, [ref]) - - return ( - - - - - - - - - - - -
    HEX:{rgbToHex(color)}
    RGB:{color}
    - ) -} - -const ThemeColor = ({ className, children }) => { - const classes = classNames(className, 'theme-color w-75 rounded mb-3') - return ( - -
    - {children} - -
    - ) -} - -ThemeColor.propTypes = { - children: PropTypes.node, - className: PropTypes.string, -} - -const Colors = () => { - return ( - <> - - - Theme colors - - - - - -
    Brand Primary Color
    -
    - -
    Brand Secondary Color
    -
    - -
    Brand Success Color
    -
    - -
    Brand Danger Color
    -
    - -
    Brand Warning Color
    -
    - -
    Brand Info Color
    -
    - -
    Brand Light Color
    -
    - -
    Brand Dark Color
    -
    -
    -
    -
    - - ) -} - -export default Colors diff --git a/src/views/theme/typography/Typography.js b/src/views/theme/typography/Typography.js deleted file mode 100644 index 81bbefba8..000000000 --- a/src/views/theme/typography/Typography.js +++ /dev/null @@ -1,229 +0,0 @@ -import React from 'react' -import { CCard, CCardHeader, CCardBody } from '@coreui/react' -import { DocsLink } from 'src/reusable' - -const Typography = () => { - return ( - <> - - - Headings - - - -

    - Documentation and examples for Bootstrap typography, including global settings, - headings, body text, lists, and more. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    HeadingExample
    -

    - <h1></h1> -

    -
    - h1. Bootstrap heading -
    -

    - <h2></h2> -

    -
    - h2. Bootstrap heading -
    -

    - <h3></h3> -

    -
    - h3. Bootstrap heading -
    -

    - <h4></h4> -

    -
    - h4. Bootstrap heading -
    -

    - <h5></h5> -

    -
    - h5. Bootstrap heading -
    -

    - <h6></h6> -

    -
    - h6. Bootstrap heading -
    -
    -
    - - Headings - -

    - .h1 through - .h6 - classes are also available, for when you want to match the font styling of a heading but - cannot use the associated HTML element. -

    -
    -

    h1. Bootstrap heading

    -

    h2. Bootstrap heading

    -

    h3. Bootstrap heading

    -

    h4. Bootstrap heading

    -

    h5. Bootstrap heading

    -

    h6. Bootstrap heading

    -
    -
    -
    - -
    Display headings
    -
    -

    - Traditional heading elements are designed to work best in the meat of your page content. - When you need a heading to stand out, consider using a display heading - —a larger, slightly more opinionated heading style. -

    -
    - - - - - - - - - - - - - - - -
    - Display 1 -
    - Display 2 -
    - Display 3 -
    - Display 4 -
    -
    -
    -
    - - Inline text elements - -

    - Traditional heading elements are designed to work best in the meat of your page content. - When you need a heading to stand out, consider using a display heading - —a larger, slightly more opinionated heading style. -

    -
    -

    - You can use the mark tag to highlight text. -

    -

    - This line of text is meant to be treated as deleted text. -

    -

    - This line of text is meant to be treated as no longer accurate. -

    -

    - This line of text is meant to be treated as an addition to the document. -

    -

    - This line of text will render as underlined -

    -

    - This line of text is meant to be treated as fine print. -

    -

    - This line rendered as bold text. -

    -

    - This line rendered as italicized text. -

    -
    -
    -
    - - Description list alignment - -

    - Align terms and descriptions horizontally by using our grid system’s predefined classes - (or semantic mixins). For longer terms, you can optionally add a{' '} - .text-truncate class to truncate the text - with an ellipsis. -

    -
    -
    -
    Description lists
    -
    A description list is perfect for defining terms.
    - -
    Euismod
    -
    -

    - Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit. -

    -

    Donec id elit non mi porta gravida at eget metus.

    -
    - -
    Malesuada porta
    -
    Etiam porta sem malesuada magna mollis euismod.
    - -
    Truncated term is truncated
    -
    - Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut - fermentum massa justo sit amet risus. -
    - -
    Nesting
    -
    -
    -
    Nested definition list
    -
    - Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc. -
    -
    -
    -
    -
    -
    -
    - - ) -} - -export default Typography diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index a48cb5fbe..000000000 --- a/yarn.lock +++ /dev/null @@ -1,11843 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.5.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" - integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== - -"@babel/core@7.12.3": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" - integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" - "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.1" - "@babel/parser" "^7.12.3" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5", "@babel/core@^7.8.4": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" - integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helpers" "^7.14.6" - "@babel/parser" "^7.14.6" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/generator@^7.12.1", "@babel/generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" - integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== - dependencies: - "@babel/types" "^7.14.5" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" - integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz#b939b43f8c37765443a19ae74ad8b15978e0a191" - integrity sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-compilation-targets@^7.12.1", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" - integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== - dependencies: - "@babel/compat-data" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.12.1", "@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542" - integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-member-expression-to-functions" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - -"@babel/helper-create-regexp-features-plugin@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" - integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - regexpu-core "^4.7.1" - -"@babel/helper-define-polyfill-provider@^0.2.2": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" - integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-explode-assignable-expression@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645" - integrity sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" - integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== - dependencies: - "@babel/helper-get-function-arity" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-get-function-arity@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" - integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-hoist-variables@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" - integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-member-expression-to-functions@^7.14.5": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" - integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" - integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" - integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== - dependencies: - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-optimise-call-expression@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" - integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-remap-async-to-generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.14.5.tgz#51439c913612958f54a987a4ffc9ee587a2045d6" - integrity sha512-rLQKdQU+HYlxBwQIj8dk4/0ENOUEhA/Z0l4hN8BexpvmSMN9oA9EagjnhnDpNsRdWCfjwa4mn/HyBXO9yhQP6A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-wrap-function" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-replace-supers@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" - integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-simple-access@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" - integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-skip-transparent-expression-wrappers@^7.12.1", "@babel/helper-skip-transparent-expression-wrappers@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" - integrity sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-split-export-declaration@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" - integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-validator-identifier@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" - integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== - -"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - -"@babel/helper-wrap-function@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz#5919d115bf0fe328b8a5d63bcb610f51601f2bff" - integrity sha512-YEdjTCq+LNuNS1WfxsDCNpgXkJaIyqco6DAelTUjT4f2KIWC1nBcaCaSdHTBqQVLnTBexBcVcFhLSU1KnYuePQ== - dependencies: - "@babel/helper-function-name" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helpers@^7.12.1", "@babel/helpers@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" - integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== - dependencies: - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.12.3", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7", "@babel/parser@^7.7.0": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" - integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e" - integrity sha512-ZoJS2XCKPBfTmL122iP6NM9dOg+d4lc9fFk3zxc8iDjvt8Pk4+TlsHSKhIPf6X+L5ORCdBzqMZDjL/WHj7WknQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - "@babel/plugin-proposal-optional-chaining" "^7.14.5" - -"@babel/plugin-proposal-async-generator-functions@^7.12.1", "@babel/plugin-proposal-async-generator-functions@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.7.tgz#784a48c3d8ed073f65adcf30b57bcbf6c8119ace" - integrity sha512-RK8Wj7lXLY3bqei69/cc25gwS5puEc3dknoFPFbqfy3XxYQBQFvu4ioWpafMBAB+L9NyptQK4nMOa5Xz16og8Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.14.5" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" - integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.12.1" - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" - integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.5.tgz#158e9e10d449c3849ef3ecde94a03d9f1841b681" - integrity sha512-KBAH5ksEnYHCegqseI5N9skTdxgJdmDoAOc0uXa+4QMYKeZD0w5IARh4FMlTNtaHhbB8v+KzMdTgxMMzsIy6Yg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-decorators@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz#59271439fed4145456c41067450543aee332d15f" - integrity sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.12.1" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-decorators" "^7.12.1" - -"@babel/plugin-proposal-dynamic-import@^7.12.1", "@babel/plugin-proposal-dynamic-import@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz#0c6617df461c0c1f8fff3b47cd59772360101d2c" - integrity sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.12.1", "@babel/plugin-proposal-export-namespace-from@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz#dbad244310ce6ccd083072167d8cea83a52faf76" - integrity sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.12.1", "@babel/plugin-proposal-json-strings@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz#38de60db362e83a3d8c944ac858ddf9f0c2239eb" - integrity sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.12.1", "@babel/plugin-proposal-logical-assignment-operators@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz#6e6229c2a99b02ab2915f82571e0cc646a40c738" - integrity sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz#3ed4fff31c015e7f3f1467f190dbe545cd7b046c" - integrity sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" - integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.1.tgz#0e2c6774c4ce48be412119b4d693ac777f7685a6" - integrity sha512-MR7Ok+Af3OhNTCxYVjJZHS0t97ydnJZt/DbR4WISO39iDnhiD8XHrY12xuSJ90FFEGjir0Fzyyn7g/zY6hxbxA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-numeric-separator@^7.12.1", "@babel/plugin-proposal-numeric-separator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz#83631bf33d9a51df184c2102a069ac0c58c05f18" - integrity sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz#5920a2b3df7f7901df0205974c0641b13fd9d363" - integrity sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g== - dependencies: - "@babel/compat-data" "^7.14.7" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.14.5" - -"@babel/plugin-proposal-optional-catch-binding@^7.12.1", "@babel/plugin-proposal-optional-catch-binding@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c" - integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz#cce122203fc8a32794296fc377c6dedaf4363797" - integrity sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - -"@babel/plugin-proposal-optional-chaining@^7.12.1", "@babel/plugin-proposal-optional-chaining@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" - integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz#37446495996b2945f30f5be5b60d5e2aa4f5792d" - integrity sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.5.tgz#9f65a4d0493a940b4c01f8aa9d3f1894a587f636" - integrity sha512-62EyfyA3WA0mZiF2e2IV9mc9Ghwxcg8YTu8BS4Wss4Y3PY725OmS9M0qLORbJwLqFtGh+jiE4wAmocK2CTUK2Q== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.12.1", "@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz#0f95ee0e757a5d647f378daa0eca7e93faa8bbe8" - integrity sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.1", "@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-decorators@^7.12.1": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.14.5.tgz#eafb9c0cbe09c8afeb964ba3a7bbd63945a72f20" - integrity sha512-c4sZMRWL4GSvP1EXy0woIP7m4jkVcEuG8R1TOZxPBPtp4FSM/kiPZub9UIs/Jrb5ZAOzvTUSGYrWsrSu1JvoPw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-flow@^7.12.1": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz#2ff654999497d7d7d142493260005263731da180" - integrity sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-import-meta@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201" - integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.12.1", "@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" - integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" - integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-async-to-generator@^7.12.1", "@babel/plugin-transform-async-to-generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz#72c789084d8f2094acb945633943ef8443d39e67" - integrity sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA== - dependencies: - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.14.5" - -"@babel/plugin-transform-block-scoped-functions@^7.12.1", "@babel/plugin-transform-block-scoped-functions@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" - integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-block-scoping@^7.12.1", "@babel/plugin-transform-block-scoping@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939" - integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf" - integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.12.1", "@babel/plugin-transform-computed-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" - integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576" - integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-dotall-regex@^7.12.1", "@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz#2f6bf76e46bdf8043b4e7e16cf24532629ba0c7a" - integrity sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-duplicate-keys@^7.12.1", "@babel/plugin-transform-duplicate-keys@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz#365a4844881bdf1501e3a9f0270e7f0f91177954" - integrity sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-exponentiation-operator@^7.12.1", "@babel/plugin-transform-exponentiation-operator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493" - integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-flow-strip-types@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.12.1.tgz#8430decfa7eb2aea5414ed4a3fa6e1652b7d77c4" - integrity sha512-8hAtkmsQb36yMmEtk2JZ9JnVyDSnDOdlB+0nEGzIDLuK4yR3JcEjfuFPYkdEPSh8Id+rAMeBEn+X0iVEyho6Hg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-flow" "^7.12.1" - -"@babel/plugin-transform-for-of@^7.12.1", "@babel/plugin-transform-for-of@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz#dae384613de8f77c196a8869cbf602a44f7fc0eb" - integrity sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-function-name@^7.12.1", "@babel/plugin-transform-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" - integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== - dependencies: - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-literals@^7.12.1", "@babel/plugin-transform-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" - integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-member-expression-literals@^7.12.1", "@babel/plugin-transform-member-expression-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" - integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-modules-amd@^7.12.1", "@babel/plugin-transform-modules-amd@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz#4fd9ce7e3411cb8b83848480b7041d83004858f7" - integrity sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.12.1", "@babel/plugin-transform-modules-commonjs@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" - integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.12.1", "@babel/plugin-transform-modules-systemjs@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.14.5.tgz#c75342ef8b30dcde4295d3401aae24e65638ed29" - integrity sha512-mNMQdvBEE5DcMQaL5LbzXFMANrQjd2W7FPzg34Y4yEz7dBgdaC+9B84dSO+/1Wba98zoDbInctCDo4JGxz1VYA== - dependencies: - "@babel/helper-hoist-variables" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.12.1", "@babel/plugin-transform-modules-umd@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz#fb662dfee697cce274a7cda525190a79096aa6e0" - integrity sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1", "@babel/plugin-transform-named-capturing-groups-regex@^7.14.7": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.7.tgz#60c06892acf9df231e256c24464bfecb0908fd4e" - integrity sha512-DTNOTaS7TkW97xsDMrp7nycUVh6sn/eq22VaxWfEdzuEbRsiaOU0pqU7DlyUGHVsbQbSghvjKRpEl+nUCKGQSg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - -"@babel/plugin-transform-new-target@^7.12.1", "@babel/plugin-transform-new-target@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz#31bdae8b925dc84076ebfcd2a9940143aed7dbf8" - integrity sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-object-super@^7.12.1", "@babel/plugin-transform-object-super@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" - integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - -"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3" - integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-property-literals@^7.12.1", "@babel/plugin-transform-property-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" - integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-react-constant-elements@^7.12.1": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.14.5.tgz#41790d856f7c5cec82d2bcf5d0e5064d682522ed" - integrity sha512-NBqLEx1GxllIOXJInJAQbrnwwYJsV3WaMHIcOwD8rhYS0AabTWn7kHdHgPgu5RmHLU0q4DMxhAMu8ue/KampgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-react-display-name@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz#1cbcd0c3b1d6648c55374a22fc9b6b7e5341c00d" - integrity sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-transform-react-display-name@^7.12.1", "@babel/plugin-transform-react-display-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.5.tgz#baa92d15c4570411301a85a74c13534873885b65" - integrity sha512-07aqY1ChoPgIxsuDviptRpVkWCSbXWmzQqcgy65C6YSFOfPFvb/DX3bBRHh7pCd/PMEEYHYWUTSVkCbkVainYQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-react-jsx-development@^7.12.1", "@babel/plugin-transform-react-jsx-development@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.14.5.tgz#1a6c73e2f7ed2c42eebc3d2ad60b0c7494fcb9af" - integrity sha512-rdwG/9jC6QybWxVe2UVOa7q6cnTpw8JRRHOxntG/h6g/guAOe6AhtQHJuJh5FwmnXIT1bdm5vC2/5huV8ZOorQ== - dependencies: - "@babel/plugin-transform-react-jsx" "^7.14.5" - -"@babel/plugin-transform-react-jsx-self@^7.12.1": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.14.5.tgz#703b5d1edccd342179c2a99ee8c7065c2b4403cc" - integrity sha512-M/fmDX6n0cfHK/NLTcPmrfVAORKDhK8tyjDhyxlUjYyPYYO8FRWwuxBA3WBx8kWN/uBUuwGa3s/0+hQ9JIN3Tg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-react-jsx-source@^7.12.1": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.14.5.tgz#79f728e60e6dbd31a2b860b0bf6c9765918acf1d" - integrity sha512-1TpSDnD9XR/rQ2tzunBVPThF5poaYT9GqP+of8fAtguYuI/dm2RkrMBDemsxtY0XBzvW7nXjYM0hRyKX9QYj7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-react-jsx@^7.12.1", "@babel/plugin-transform-react-jsx@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.5.tgz#39749f0ee1efd8a1bd729152cf5f78f1d247a44a" - integrity sha512-7RylxNeDnxc1OleDm0F5Q/BSL+whYRbOAR+bwgCxIr0L32v7UFh/pz1DLMZideAUxKT6eMoS2zQH6fyODLEi8Q== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-jsx" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/plugin-transform-react-pure-annotations@^7.12.1", "@babel/plugin-transform-react-pure-annotations@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.14.5.tgz#18de612b84021e3a9802cbc212c9d9f46d0d11fc" - integrity sha512-3X4HpBJimNxW4rhUy/SONPyNQHp5YRr0HhJdT2OH1BRp0of7u3Dkirc7x9FRJMKMqTBI079VZ1hzv7Ouuz///g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-regenerator@^7.12.1", "@babel/plugin-transform-regenerator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" - integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== - dependencies: - regenerator-transform "^0.14.2" - -"@babel/plugin-transform-reserved-words@^7.12.1", "@babel/plugin-transform-reserved-words@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz#c44589b661cfdbef8d4300dcc7469dffa92f8304" - integrity sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz#04b792057eb460389ff6a4198e377614ea1e7ba5" - integrity sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg== - dependencies: - "@babel/helper-module-imports" "^7.12.1" - "@babel/helper-plugin-utils" "^7.10.4" - resolve "^1.8.1" - semver "^5.5.1" - -"@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" - integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" - integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - -"@babel/plugin-transform-sticky-regex@^7.12.1", "@babel/plugin-transform-sticky-regex@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9" - integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" - integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-typeof-symbol@^7.12.1", "@babel/plugin-transform-typeof-symbol@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz#39af2739e989a2bd291bf6b53f16981423d457d4" - integrity sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-typescript@^7.12.1": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.6.tgz#6e9c2d98da2507ebe0a883b100cde3c7279df36c" - integrity sha512-XlTdBq7Awr4FYIzqhmYY80WN0V0azF74DMPyFqVHBvf81ZUgc4X7ZOpx6O8eLDK6iM5cCQzeyJw0ynTaefixRA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.6" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript" "^7.14.5" - -"@babel/plugin-transform-unicode-escapes@^7.12.1", "@babel/plugin-transform-unicode-escapes@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b" - integrity sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-unicode-regex@^7.12.1", "@babel/plugin-transform-unicode-regex@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" - integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/preset-env@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" - integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== - dependencies: - "@babel/compat-data" "^7.12.1" - "@babel/helper-compilation-targets" "^7.12.1" - "@babel/helper-module-imports" "^7.12.1" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-validator-option" "^7.12.1" - "@babel/plugin-proposal-async-generator-functions" "^7.12.1" - "@babel/plugin-proposal-class-properties" "^7.12.1" - "@babel/plugin-proposal-dynamic-import" "^7.12.1" - "@babel/plugin-proposal-export-namespace-from" "^7.12.1" - "@babel/plugin-proposal-json-strings" "^7.12.1" - "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" - "@babel/plugin-proposal-numeric-separator" "^7.12.1" - "@babel/plugin-proposal-object-rest-spread" "^7.12.1" - "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" - "@babel/plugin-proposal-optional-chaining" "^7.12.1" - "@babel/plugin-proposal-private-methods" "^7.12.1" - "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" - "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-class-properties" "^7.12.1" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.0" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.12.1" - "@babel/plugin-transform-arrow-functions" "^7.12.1" - "@babel/plugin-transform-async-to-generator" "^7.12.1" - "@babel/plugin-transform-block-scoped-functions" "^7.12.1" - "@babel/plugin-transform-block-scoping" "^7.12.1" - "@babel/plugin-transform-classes" "^7.12.1" - "@babel/plugin-transform-computed-properties" "^7.12.1" - "@babel/plugin-transform-destructuring" "^7.12.1" - "@babel/plugin-transform-dotall-regex" "^7.12.1" - "@babel/plugin-transform-duplicate-keys" "^7.12.1" - "@babel/plugin-transform-exponentiation-operator" "^7.12.1" - "@babel/plugin-transform-for-of" "^7.12.1" - "@babel/plugin-transform-function-name" "^7.12.1" - "@babel/plugin-transform-literals" "^7.12.1" - "@babel/plugin-transform-member-expression-literals" "^7.12.1" - "@babel/plugin-transform-modules-amd" "^7.12.1" - "@babel/plugin-transform-modules-commonjs" "^7.12.1" - "@babel/plugin-transform-modules-systemjs" "^7.12.1" - "@babel/plugin-transform-modules-umd" "^7.12.1" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1" - "@babel/plugin-transform-new-target" "^7.12.1" - "@babel/plugin-transform-object-super" "^7.12.1" - "@babel/plugin-transform-parameters" "^7.12.1" - "@babel/plugin-transform-property-literals" "^7.12.1" - "@babel/plugin-transform-regenerator" "^7.12.1" - "@babel/plugin-transform-reserved-words" "^7.12.1" - "@babel/plugin-transform-shorthand-properties" "^7.12.1" - "@babel/plugin-transform-spread" "^7.12.1" - "@babel/plugin-transform-sticky-regex" "^7.12.1" - "@babel/plugin-transform-template-literals" "^7.12.1" - "@babel/plugin-transform-typeof-symbol" "^7.12.1" - "@babel/plugin-transform-unicode-escapes" "^7.12.1" - "@babel/plugin-transform-unicode-regex" "^7.12.1" - "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.12.1" - core-js-compat "^3.6.2" - semver "^5.5.0" - -"@babel/preset-env@^7.12.1", "@babel/preset-env@^7.8.4": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.7.tgz#5c70b22d4c2d893b03d8c886a5c17422502b932a" - integrity sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA== - dependencies: - "@babel/compat-data" "^7.14.7" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.14.5" - "@babel/plugin-proposal-async-generator-functions" "^7.14.7" - "@babel/plugin-proposal-class-properties" "^7.14.5" - "@babel/plugin-proposal-class-static-block" "^7.14.5" - "@babel/plugin-proposal-dynamic-import" "^7.14.5" - "@babel/plugin-proposal-export-namespace-from" "^7.14.5" - "@babel/plugin-proposal-json-strings" "^7.14.5" - "@babel/plugin-proposal-logical-assignment-operators" "^7.14.5" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5" - "@babel/plugin-proposal-numeric-separator" "^7.14.5" - "@babel/plugin-proposal-object-rest-spread" "^7.14.7" - "@babel/plugin-proposal-optional-catch-binding" "^7.14.5" - "@babel/plugin-proposal-optional-chaining" "^7.14.5" - "@babel/plugin-proposal-private-methods" "^7.14.5" - "@babel/plugin-proposal-private-property-in-object" "^7.14.5" - "@babel/plugin-proposal-unicode-property-regex" "^7.14.5" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.14.5" - "@babel/plugin-transform-async-to-generator" "^7.14.5" - "@babel/plugin-transform-block-scoped-functions" "^7.14.5" - "@babel/plugin-transform-block-scoping" "^7.14.5" - "@babel/plugin-transform-classes" "^7.14.5" - "@babel/plugin-transform-computed-properties" "^7.14.5" - "@babel/plugin-transform-destructuring" "^7.14.7" - "@babel/plugin-transform-dotall-regex" "^7.14.5" - "@babel/plugin-transform-duplicate-keys" "^7.14.5" - "@babel/plugin-transform-exponentiation-operator" "^7.14.5" - "@babel/plugin-transform-for-of" "^7.14.5" - "@babel/plugin-transform-function-name" "^7.14.5" - "@babel/plugin-transform-literals" "^7.14.5" - "@babel/plugin-transform-member-expression-literals" "^7.14.5" - "@babel/plugin-transform-modules-amd" "^7.14.5" - "@babel/plugin-transform-modules-commonjs" "^7.14.5" - "@babel/plugin-transform-modules-systemjs" "^7.14.5" - "@babel/plugin-transform-modules-umd" "^7.14.5" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.7" - "@babel/plugin-transform-new-target" "^7.14.5" - "@babel/plugin-transform-object-super" "^7.14.5" - "@babel/plugin-transform-parameters" "^7.14.5" - "@babel/plugin-transform-property-literals" "^7.14.5" - "@babel/plugin-transform-regenerator" "^7.14.5" - "@babel/plugin-transform-reserved-words" "^7.14.5" - "@babel/plugin-transform-shorthand-properties" "^7.14.5" - "@babel/plugin-transform-spread" "^7.14.6" - "@babel/plugin-transform-sticky-regex" "^7.14.5" - "@babel/plugin-transform-template-literals" "^7.14.5" - "@babel/plugin-transform-typeof-symbol" "^7.14.5" - "@babel/plugin-transform-unicode-escapes" "^7.14.5" - "@babel/plugin-transform-unicode-regex" "^7.14.5" - "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.14.5" - babel-plugin-polyfill-corejs2 "^0.2.2" - babel-plugin-polyfill-corejs3 "^0.2.2" - babel-plugin-polyfill-regenerator "^0.2.2" - core-js-compat "^3.15.0" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.3", "@babel/preset-modules@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" - integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/preset-react@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.12.1.tgz#7f022b13f55b6dd82f00f16d1c599ae62985358c" - integrity sha512-euCExymHCi0qB9u5fKw7rvlw7AZSjw/NaB9h7EkdTt5+yHRrXdiRTh7fkG3uBPpJg82CqLfp1LHLqWGSCrab+g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-react-display-name" "^7.12.1" - "@babel/plugin-transform-react-jsx" "^7.12.1" - "@babel/plugin-transform-react-jsx-development" "^7.12.1" - "@babel/plugin-transform-react-jsx-self" "^7.12.1" - "@babel/plugin-transform-react-jsx-source" "^7.12.1" - "@babel/plugin-transform-react-pure-annotations" "^7.12.1" - -"@babel/preset-react@^7.12.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.14.5.tgz#0fbb769513f899c2c56f3a882fa79673c2d4ab3c" - integrity sha512-XFxBkjyObLvBaAvkx1Ie95Iaq4S/GUEIrejyrntQ/VCMKUYvKLoyKxOBzJ2kjA3b6rC9/KL6KXfDC2GqvLiNqQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-transform-react-display-name" "^7.14.5" - "@babel/plugin-transform-react-jsx" "^7.14.5" - "@babel/plugin-transform-react-jsx-development" "^7.14.5" - "@babel/plugin-transform-react-pure-annotations" "^7.14.5" - -"@babel/preset-typescript@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.12.1.tgz#86480b483bb97f75036e8864fe404cc782cc311b" - integrity sha512-hNK/DhmoJPsksdHuI/RVrcEws7GN5eamhi28JkO52MqIxU8Z0QpmiSOQxZHWOHV7I3P4UjHV97ay4TcamMA6Kw== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-typescript" "^7.12.1" - -"@babel/runtime-corejs3@^7.10.2": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz#0ef292bbce40ca00f874c9724ef175a12476465c" - integrity sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA== - dependencies: - core-js-pure "^3.15.0" - regenerator-runtime "^0.13.4" - -"@babel/runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" - integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" - integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.10.4", "@babel/template@^7.14.5", "@babel/template@^7.3.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" - integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.7.0": - version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" - integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-hoist-variables" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.14.7" - "@babel/types" "^7.14.5" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.6", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" - integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - to-fast-properties "^2.0.0" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@cnakazawa/watch@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" - integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - -"@coreui/chartjs@3.0.0", "@coreui/chartjs@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@coreui/chartjs/-/chartjs-3.0.0.tgz#54b55d85de0f3146237b7d1d36b1bf33bc051735" - integrity sha512-udbvSxanTNltv94lqTMW8bLRXTtzk9G2SrmFdM/7HH+JSaLX2wdQpZ4VIJhyOCRGLCSKHktl29BnW1/uXQecAg== - dependencies: - "@coreui/coreui" "4.0.0" - chart.js "^3.4.0" - -"@coreui/coreui@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@coreui/coreui/-/coreui-4.0.0.tgz#52ebe0197411a829ba48057ade61923e05859eec" - integrity sha512-8vH6fJrmvCR/Oy5v0E+/1AL3Ygb4jhQ7NXK2fMYWJyK13BePDm9muB3y6S0IdqkpBwjY3hHVwHyt2lJqJdesmQ== - -"@coreui/icons-react@^2.0.0-rc.0": - version "2.0.0-rc.0" - resolved "https://registry.yarnpkg.com/@coreui/icons-react/-/icons-react-2.0.0-rc.0.tgz#5eb4d082a9306c6a2126c61766dcfec503a8a4e1" - integrity sha512-QIayiRpe/ZR7G1GdE1KziE4xgIYRmiG9kmHjo5DhKdE5JjTRuO/Cs5Z3F6WVAaqARwUu1aNWXUbLuKb3kKOaxQ== - dependencies: - "@typescript-eslint/eslint-plugin" "^4.28.1" - "@typescript-eslint/parser" "^4.28.1" - eslint-config-prettier "^8.3.0" - eslint-plugin-jsdoc "^35.4.1" - eslint-plugin-prettier "^3.4.0" - prettier "^2.3.2" - -"@coreui/icons@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@coreui/icons/-/icons-2.0.1.tgz#778022fe2b366abf9594d142607026e4edc667f8" - integrity sha512-gBfFRLPUt3Bv9EZbJRbT3sQRHrhH0c4dRbeE9GpWJgJY8kvE9+3Hf5xGK9XyQhFynHx4o2WQeMxsReQLddlK9w== - -"@coreui/react-chartjs@2.0.0-rc.0": - version "2.0.0-rc.0" - resolved "https://registry.yarnpkg.com/@coreui/react-chartjs/-/react-chartjs-2.0.0-rc.0.tgz#068c54c8d50be119d1cb4e909799440b3911ac13" - integrity sha512-x4eaAKgWGPOwl3F15G4LrbxHOR/ywIvM+j6jikWITebfjraz0iu93RYOcy7elMqfarRPshDQ3mLC94XtnSbcag== - dependencies: - "@coreui/chartjs" "^3.0.0" - chart.js "^3.4.1" - -"@coreui/react@4.0.0-beta.2": - version "4.0.0-beta.2" - resolved "https://registry.yarnpkg.com/@coreui/react/-/react-4.0.0-beta.2.tgz#f9f319f170d35e21bf65e4e08bf3d81cbfc51ec0" - integrity sha512-7rJGcL8s06AD/OUx78DnUB6q7oSjOk2JKAqtGLF3Lkd+Zf0YqVkk8dcrK6j8SG+mPURhbZSPcz5zpE1yf645hg== - -"@coreui/utils@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@coreui/utils/-/utils-1.3.1.tgz#ebe6fa00ae7f46d166b26f116e74d55dfc89f5e0" - integrity sha512-WuWHX7bg89cJH34TWVsLe9RsxzBhTApj+X2Ja19xhjcpxt5Gv11Ozm+fwYt6DD7DgncTvpwYrMcnNlpp701UOg== - -"@csstools/convert-colors@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" - integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== - -"@csstools/normalize.css@^10.1.0": - version "10.1.0" - resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" - integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== - -"@es-joy/jsdoccomment@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.8.0.tgz#1add451f50f57597676ab85ee7bd0a273d7b7c43" - integrity sha512-Xd3GzYsL2sz2pcdtYt5Q0Wz1ol/o9Nt2UQL4nFPDcaEomvPmwjJsbjkKx1SKhl2h3TgwazNBLdcNr2m0UiGiFA== - dependencies: - comment-parser "^1.1.5" - esquery "^1.4.0" - jsdoc-type-pratt-parser "1.0.4" - -"@eslint/eslintrc@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.2.tgz#f63d0ef06f5c0c57d76c4ab5f63d3835c51b0179" - integrity sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg== - dependencies: - ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" - import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - strip-json-comments "^3.1.1" - -"@hapi/address@2.x.x": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" - integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== - -"@hapi/bourne@1.x.x": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" - integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== - -"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": - version "8.5.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" - integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== - -"@hapi/joi@^15.1.0": - version "15.1.1" - resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" - integrity sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ== - dependencies: - "@hapi/address" "2.x.x" - "@hapi/bourne" "1.x.x" - "@hapi/hoek" "8.x.x" - "@hapi/topo" "3.x.x" - -"@hapi/topo@3.x.x": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" - integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== - dependencies: - "@hapi/hoek" "^8.3.0" - -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== - dependencies: - "@humanwhocodes/object-schema" "^1.2.0" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" - integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^26.6.2" - jest-util "^26.6.2" - slash "^3.0.0" - -"@jest/core@^26.6.0", "@jest/core@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" - integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== - 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" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.4" - 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.2" - p-each-series "^2.1.0" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^26.6.0", "@jest/environment@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" - integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== - dependencies: - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - -"@jest/fake-timers@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" - integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== - dependencies: - "@jest/types" "^26.6.2" - "@sinonjs/fake-timers" "^6.0.1" - "@types/node" "*" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-util "^26.6.2" - -"@jest/globals@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" - integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== - dependencies: - "@jest/environment" "^26.6.2" - "@jest/types" "^26.6.2" - expect "^26.6.2" - -"@jest/reporters@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" - integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== - 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.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.2" - graceful-fs "^4.2.4" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - 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.0" - string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^7.0.0" - optionalDependencies: - node-notifier "^8.0.0" - -"@jest/source-map@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" - integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.4" - source-map "^0.6.0" - -"@jest/test-result@^26.6.0", "@jest/test-result@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" - integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== - dependencies: - "@jest/console" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" - integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== - dependencies: - "@jest/test-result" "^26.6.2" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - -"@jest/transform@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" - integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^26.6.2" - babel-plugin-istanbul "^6.0.0" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-regex-util "^26.0.0" - jest-util "^26.6.2" - micromatch "^4.0.2" - pirates "^4.0.1" - slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" - -"@jest/types@^26.6.0", "@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - -"@juggle/resize-observer@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0" - integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" - integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - -"@pmmmwh/react-refresh-webpack-plugin@0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz#1eec460596d200c0236bf195b078a5d1df89b766" - integrity sha512-br5Qwvh8D2OQqSXpd1g/xqXKnK0r+Jz6qVKBbWmpUcrbGOxUrf39V5oZ1876084CGn18uMdR5uvPqBv9UqtBjQ== - dependencies: - ansi-html "^0.0.7" - error-stack-parser "^2.0.6" - html-entities "^1.2.1" - native-url "^0.2.6" - schema-utils "^2.6.5" - source-map "^0.7.3" - -"@rollup/plugin-node-resolve@^7.1.1": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz#80de384edfbd7bfc9101164910f86078151a3eca" - integrity sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q== - dependencies: - "@rollup/pluginutils" "^3.0.8" - "@types/resolve" "0.0.8" - builtin-modules "^3.1.0" - is-module "^1.0.0" - resolve "^1.14.2" - -"@rollup/plugin-replace@^2.3.1": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz#a2d539314fbc77c244858faa523012825068510a" - integrity sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg== - dependencies: - "@rollup/pluginutils" "^3.1.0" - magic-string "^0.25.7" - -"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" - integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== - dependencies: - "@types/estree" "0.0.39" - estree-walker "^1.0.1" - picomatch "^2.2.2" - -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== - dependencies: - "@sinonjs/commons" "^1.7.0" - -"@surma/rollup-plugin-off-main-thread@^1.1.1": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58" - integrity sha512-yBMPqmd1yEJo/280PAMkychuaALyQ9Lkb5q1ck3mjJrFuEobIfhnQ4J3mbvBoISmR3SWMWV+cGB/I0lCQee79A== - dependencies: - ejs "^2.6.1" - magic-string "^0.25.0" - -"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" - integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== - -"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" - integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== - -"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" - integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== - -"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" - integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== - -"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" - integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== - -"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" - integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== - -"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" - integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== - -"@svgr/babel-plugin-transform-svg-component@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" - integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== - -"@svgr/babel-preset@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" - integrity sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig== - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" - "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" - "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" - "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" - "@svgr/babel-plugin-transform-svg-component" "^5.5.0" - -"@svgr/core@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" - integrity sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ== - dependencies: - "@svgr/plugin-jsx" "^5.5.0" - camelcase "^6.2.0" - cosmiconfig "^7.0.0" - -"@svgr/hast-util-to-babel-ast@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461" - integrity sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ== - dependencies: - "@babel/types" "^7.12.6" - -"@svgr/plugin-jsx@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" - integrity sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA== - dependencies: - "@babel/core" "^7.12.3" - "@svgr/babel-preset" "^5.5.0" - "@svgr/hast-util-to-babel-ast" "^5.5.0" - svg-parser "^2.0.2" - -"@svgr/plugin-svgo@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" - integrity sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ== - dependencies: - cosmiconfig "^7.0.0" - deepmerge "^4.2.2" - svgo "^1.2.2" - -"@svgr/webpack@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640" - integrity sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g== - dependencies: - "@babel/core" "^7.12.3" - "@babel/plugin-transform-react-constant-elements" "^7.12.1" - "@babel/preset-env" "^7.12.1" - "@babel/preset-react" "^7.12.5" - "@svgr/core" "^5.5.0" - "@svgr/plugin-jsx" "^5.5.0" - "@svgr/plugin-svgo" "^5.5.0" - loader-utils "^2.0.0" - -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": - version "7.1.14" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" - integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" - integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.0" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" - integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.0.tgz#a34277cf8acbd3185ea74129e1f100491eb1da7f" - integrity sha512-IilJZ1hJBUZwMOVDNTdflOOLzJB/ZtljYVa7k3gEZN/jqIJIPkWHC6dvbX+DD2CwZDHB9wAKzZPzzqMIkW37/w== - dependencies: - "@babel/types" "^7.3.0" - -"@types/eslint@^7.2.6": - version "7.2.13" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.13.tgz#e0ca7219ba5ded402062ad6f926d491ebb29dd53" - integrity sha512-LKmQCWAlnVHvvXq4oasNUMTJJb2GwSyTY8+1C7OH5ILR8mPLaljv1jxL1bXW3xB3jFbQxTKxJAvI8PyjB09aBg== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*": - version "0.0.49" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.49.tgz#3facb98ebcd4114a4ecef74e0de2175b56fd4464" - integrity sha512-K1AFuMe8a+pXmfHTtnwBvqoEylNKVeaiKYkjmcEAdytMQVJ/i9Fu7sc13GxgXdO49gkE7Hy8SyJonUZUn+eVaw== - -"@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== - -"@types/glob@^7.1.1": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" - integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== - dependencies: - "@types/node" "*" - -"@types/hoist-non-react-statics@^3.3.0": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" - integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== - dependencies: - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - -"@types/html-minifier-terser@^5.0.0": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" - integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - -"@types/minimatch@*": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" - integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== - -"@types/node@*": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.0.0.tgz#067a6c49dc7a5c2412a505628e26902ae967bf6f" - integrity sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg== - -"@types/normalize-package-data@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" - integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== - -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== - -"@types/prettier@^2.0.0": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.1.tgz#54dd88bdc7f49958329666af3779561e47d5dab3" - integrity sha512-NVkb4p4YjI8E3O6+1m8I+8JlMpFZwfSbPGdaw0wXuyPRTEz0SLKwBUWNSO7Maoi8tQMPC8JLZNWkrcKPI7/sLA== - -"@types/prop-types@*": - version "15.7.3" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" - integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== - -"@types/q@^1.5.1": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" - integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== - -"@types/react-redux@^7.1.16": - version "7.1.16" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.16.tgz#0fbd04c2500c12105494c83d4a3e45c084e3cb21" - integrity sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw== - dependencies: - "@types/hoist-non-react-statics" "^3.3.0" - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - redux "^4.0.0" - -"@types/react@*": - version "17.0.13" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.13.tgz#6b7c9a8f2868586ad87d941c02337c6888fb874f" - integrity sha512-D/G3PiuqTfE3IMNjLn/DCp6umjVCSvtZTPdtAFy5+Ved6CsdRvivfKeCzw79W4AatShtU4nGqgvOv5Gro534vQ== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/resolve@0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" - integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== - dependencies: - "@types/node" "*" - -"@types/scheduler@*": - version "0.16.1" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" - integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== - -"@types/source-list-map@*": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" - integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/tapable@^1", "@types/tapable@^1.0.5": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" - integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== - -"@types/uglify-js@*": - version "3.13.1" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.1.tgz#5e889e9e81e94245c75b6450600e1c5ea2878aea" - integrity sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ== - dependencies: - source-map "^0.6.1" - -"@types/webpack-sources@*": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-2.1.1.tgz#6af17e3a3ded71eec2b98008d7c12f498a0a4506" - integrity sha512-MjM1R6iuw8XaVbtkCBz0N349cyqBjJHCbQiOeppe3VBeFvxqs74RKHAVt9LkxTnUWc7YLZOEsUfPUnmK6SBPKQ== - dependencies: - "@types/node" "*" - "@types/source-list-map" "*" - source-map "^0.7.3" - -"@types/webpack@^4.41.8": - version "4.41.30" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.30.tgz#fd3db6d0d41e145a8eeeafcd3c4a7ccde9068ddc" - integrity sha512-GUHyY+pfuQ6haAfzu4S14F+R5iGRwN6b2FRNJY7U0NilmFAqbsOfK6j1HwuLBAqwRIT+pVdNDJGJ6e8rpp0KHA== - dependencies: - "@types/node" "*" - "@types/tapable" "^1" - "@types/uglify-js" "*" - "@types/webpack-sources" "*" - anymatch "^3.0.0" - source-map "^0.6.0" - -"@types/yargs-parser@*": - version "20.2.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" - integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== - -"@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== - dependencies: - "@types/yargs-parser" "*" - -"@typescript-eslint/eslint-plugin@^4.28.1", "@typescript-eslint/eslint-plugin@^4.5.0": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz#c045e440196ae45464e08e20c38aff5c3a825947" - integrity sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ== - dependencies: - "@typescript-eslint/experimental-utils" "4.28.1" - "@typescript-eslint/scope-manager" "4.28.1" - debug "^4.3.1" - functional-red-black-tree "^1.0.1" - regexpp "^3.1.0" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/experimental-utils@4.28.1", "@typescript-eslint/experimental-utils@^4.0.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz#3869489dcca3c18523c46018b8996e15948dbadc" - integrity sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q== - dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.28.1" - "@typescript-eslint/types" "4.28.1" - "@typescript-eslint/typescript-estree" "4.28.1" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/experimental-utils@^3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686" - integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/typescript-estree" "3.10.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^4.28.1", "@typescript-eslint/parser@^4.5.0": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.1.tgz#5181b81658414f47291452c15bf6cd44a32f85bd" - integrity sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg== - dependencies: - "@typescript-eslint/scope-manager" "4.28.1" - "@typescript-eslint/types" "4.28.1" - "@typescript-eslint/typescript-estree" "4.28.1" - debug "^4.3.1" - -"@typescript-eslint/scope-manager@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz#fd3c20627cdc12933f6d98b386940d8d0ce8a991" - integrity sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA== - dependencies: - "@typescript-eslint/types" "4.28.1" - "@typescript-eslint/visitor-keys" "4.28.1" - -"@typescript-eslint/types@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" - integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== - -"@typescript-eslint/types@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.1.tgz#d0f2ecbef3684634db357b9bbfc97b94b828f83f" - integrity sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg== - -"@typescript-eslint/typescript-estree@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" - integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w== - dependencies: - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/visitor-keys" "3.10.1" - debug "^4.1.1" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/typescript-estree@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz#af882ae41740d1f268e38b4d0fad21e7e8d86a81" - integrity sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ== - dependencies: - "@typescript-eslint/types" "4.28.1" - "@typescript-eslint/visitor-keys" "4.28.1" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/visitor-keys@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" - integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ== - dependencies: - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/visitor-keys@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz#162a515ee255f18a6068edc26df793cdc1ec9157" - integrity sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg== - dependencies: - "@typescript-eslint/types" "4.28.1" - eslint-visitor-keys "^2.0.0" - -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" - -"@wojtekmaj/enzyme-adapter-react-17@^0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@wojtekmaj/enzyme-adapter-react-17/-/enzyme-adapter-react-17-0.6.2.tgz#7f05a91e6b374ba94ab10467802346198403d3e1" - integrity sha512-9STl8ZKp8VPJgtr6jEAv9IThX0PAJ1JXKv6IlXzq22Ejyk1bhSxJ6RfJCJT9A9+8JlDIf3BX5CC4s0Bs4/1wFQ== - dependencies: - "@wojtekmaj/enzyme-adapter-utils" "^0.1.1" - enzyme-shallow-equal "^1.0.0" - has "^1.0.0" - object.assign "^4.1.0" - object.values "^1.1.0" - prop-types "^15.7.0" - react-is "^17.0.2" - react-test-renderer "^17.0.0" - -"@wojtekmaj/enzyme-adapter-utils@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@wojtekmaj/enzyme-adapter-utils/-/enzyme-adapter-utils-0.1.1.tgz#17773cf264570fbcfc0d33bb74e4002c17f2f1ec" - integrity sha512-bNPWtN/d8huKOkC6j1E3EkSamnRrHHT7YuR6f9JppAQqtoAm3v4/vERe4J14jQKmHLCyEBHXrlgb7H6l817hVg== - dependencies: - function.prototype.name "^1.1.0" - has "^1.0.0" - object.assign "^4.1.0" - object.fromentries "^2.0.0" - prop-types "^15.7.0" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -abab@^2.0.3, abab@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-jsx@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" - integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== - -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -acorn@^7.1.0, acorn@^7.1.1, acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.2.4: - version "8.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" - integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== - -address@1.1.2, address@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" - integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== - -adjust-sourcemap-loader@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" - integrity sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw== - dependencies: - loader-utils "^2.0.0" - regex-parser "^2.2.11" - -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^8.0.1: - version "8.6.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.1.tgz#ae65764bf1edde8cd861281cda5057852364a295" - integrity sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - -ansi-colors@^3.0.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== - -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-html@0.0.7, ansi-html@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - -arity-n@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" - integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - -array-includes@^3.1.1, array-includes@^3.1.2, array-includes@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" - integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - get-intrinsic "^1.1.1" - is-string "^1.0.5" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= - dependencies: - array-uniq "^1.0.1" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -array.prototype.filter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.0.tgz#24d63e38983cdc6bf023a3c574b2f2a3f384c301" - integrity sha512-TfO1gz+tLm+Bswq0FBOXPqAchtCr2Rn48T8dLJoRFl8NoEosjZmzptmuo1X8aZBzZcqsR1W8U761tjACJtngTQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.5" - -array.prototype.flat@^1.2.3, array.prototype.flat@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" - integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" - -array.prototype.flatmap@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" - integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" - function-bind "^1.1.1" - -arrify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== - -asap@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= - -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -ast-types-flow@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -auto-changelog@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/auto-changelog/-/auto-changelog-2.3.0.tgz#08ab8b1840a5d804410f1d1a5d2c4b2df0d835e1" - integrity sha512-S2B+RtTgytsa7l5iFGBoWT9W9ylITT5JJ8OaMJ7nrwvnlRm1dSS2tghaYueDeInZZafOE+1llH3tUQjMDRVS1g== - dependencies: - commander "^5.0.0" - handlebars "^4.7.3" - node-fetch "^2.6.0" - parse-github-url "^1.0.2" - semver "^6.3.0" - -autoprefixer@^9.6.1: - version "9.8.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" - integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== - dependencies: - browserslist "^4.12.0" - caniuse-lite "^1.0.30001109" - colorette "^1.2.1" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.32" - postcss-value-parser "^4.1.0" - -axe-core@^4.0.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.2.3.tgz#2a3afc332f0031b42f602f4a3de03c211ca98f72" - integrity sha512-pXnVMfJKSIWU2Ml4JHP7pZEPIrgBO1Fd3WGx+fPBsS+KRGhE4vxooD8XBGWbQOIVSZsVK7pUDBBkCicNu80yzQ== - -axobject-query@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" - integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== - -babel-eslint@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" - integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.0" - "@babel/traverse" "^7.7.0" - "@babel/types" "^7.7.0" - eslint-visitor-keys "^1.0.0" - resolve "^1.12.0" - -babel-extract-comments@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz#0a2aedf81417ed391b85e18b4614e693a0351a21" - integrity sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ== - dependencies: - babylon "^6.18.0" - -babel-jest@^26.6.0, babel-jest@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" - integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== - dependencies: - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/babel__core" "^7.1.7" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.6.2" - chalk "^4.0.0" - graceful-fs "^4.2.4" - slash "^3.0.0" - -babel-loader@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" - integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== - dependencies: - find-cache-dir "^2.1.0" - loader-utils "^1.4.0" - mkdirp "^0.5.3" - pify "^4.0.1" - schema-utils "^2.6.5" - -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -babel-plugin-istanbul@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" - integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^4.0.0" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" - integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" - "@types/babel__traverse" "^7.0.6" - -babel-plugin-macros@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" - integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== - dependencies: - "@babel/runtime" "^7.7.2" - cosmiconfig "^6.0.0" - resolve "^1.12.0" - -babel-plugin-named-asset-import@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.7.tgz#156cd55d3f1228a5765774340937afc8398067dd" - integrity sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw== - -babel-plugin-polyfill-corejs2@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" - integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== - dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.2.2" - semver "^6.1.1" - -babel-plugin-polyfill-corejs3@^0.2.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz#72add68cf08a8bf139ba6e6dfc0b1d504098e57b" - integrity sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - core-js-compat "^3.14.0" - -babel-plugin-polyfill-regenerator@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" - integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - -babel-plugin-syntax-object-rest-spread@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" - integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= - -babel-plugin-transform-object-rest-spread@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" - integrity sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY= - dependencies: - babel-plugin-syntax-object-rest-spread "^6.8.0" - babel-runtime "^6.26.0" - -babel-plugin-transform-react-remove-prop-types@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" - integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== - -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" - integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== - dependencies: - babel-plugin-jest-hoist "^26.6.2" - babel-preset-current-node-syntax "^1.0.0" - -babel-preset-react-app@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.0.tgz#689b60edc705f8a70ce87f47ab0e560a317d7045" - integrity sha512-itL2z8v16khpuKutx5IH8UdCdSTuzrOhRFTEdIhveZ2i1iBKDrVE0ATa4sFVy+02GLucZNVBWtoarXBy0Msdpg== - dependencies: - "@babel/core" "7.12.3" - "@babel/plugin-proposal-class-properties" "7.12.1" - "@babel/plugin-proposal-decorators" "7.12.1" - "@babel/plugin-proposal-nullish-coalescing-operator" "7.12.1" - "@babel/plugin-proposal-numeric-separator" "7.12.1" - "@babel/plugin-proposal-optional-chaining" "7.12.1" - "@babel/plugin-transform-flow-strip-types" "7.12.1" - "@babel/plugin-transform-react-display-name" "7.12.1" - "@babel/plugin-transform-runtime" "7.12.1" - "@babel/preset-env" "7.12.1" - "@babel/preset-react" "7.12.1" - "@babel/preset-typescript" "7.12.1" - "@babel/runtime" "7.12.1" - babel-plugin-macros "2.8.0" - babel-plugin-transform-react-remove-prop-types "0.4.24" - -babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" - integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.0.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= - -bfj@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2" - integrity sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw== - dependencies: - bluebird "^3.5.5" - check-types "^11.1.1" - hoopy "^0.1.4" - tryer "^1.0.1" - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= - dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - 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.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.1, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@4.14.2: - version "4.14.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.2.tgz#1b3cec458a1ba87588cc5e9be62f19b6d48813ce" - integrity sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw== - dependencies: - caniuse-lite "^1.0.30001125" - electron-to-chromium "^1.3.564" - escalade "^3.0.2" - node-releases "^1.1.61" - -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.6, browserslist@^4.6.2, browserslist@^4.6.4: - version "4.16.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" - integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== - dependencies: - caniuse-lite "^1.0.30001219" - colorette "^1.2.2" - electron-to-chromium "^1.3.723" - escalade "^3.1.1" - node-releases "^1.1.71" - -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -builtin-modules@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" - integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cacache@^12.0.2: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cacache@^15.0.5: - version "15.2.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" - integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== - dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camel-case@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" - integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== - dependencies: - pascal-case "^3.1.2" - tslib "^2.0.3" - -camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.0.0, camelcase@^6.1.0, camelcase@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - -can-use-dom@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a" - integrity sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo= - -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001219: - version "1.0.30001242" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001242.tgz#04201627abcd60dc89211f22cbe2347306cda46b" - integrity sha512-KvNuZ/duufelMB3w2xtf9gEWCSxJwUgoxOx5b6ScLXC4kPc9xsczUVCPrQU26j5kOsHM4pSUL54tAZt5THQKug== - -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" - -case-sensitive-paths-webpack-plugin@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz#23ac613cc9a856e4f88ff8bb73bbb5e989825cf7" - integrity sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ== - -chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - -chart.js@^3.4.0, chart.js@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.4.1.tgz#ff3b2b2a04a37b83618b4a6399a5f87ccc0f1e8a" - integrity sha512-0R4mL7WiBcYoazIhrzSYnWcOw6RmrRn7Q4nKZNsBQZCBrlkZKodQbfeojCCo8eETPRCs1ZNTsAcZhIfyhyP61g== - -check-types@^11.1.1: - version "11.1.2" - resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" - integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== - -cheerio-select@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" - integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg== - dependencies: - css-select "^4.1.3" - css-what "^5.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - domutils "^2.7.0" - -cheerio@^1.0.0-rc.3: - version "1.0.0-rc.10" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" - integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== - dependencies: - cheerio-select "^1.5.0" - dom-serializer "^1.3.2" - domhandler "^4.2.0" - htmlparser2 "^6.1.0" - parse5 "^6.0.1" - parse5-htmlparser2-tree-adapter "^6.0.1" - tslib "^2.2.0" - -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== - -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -cjs-module-lexer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" - integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -classnames@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== - -clean-css@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" - integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== - dependencies: - source-map "~0.6.0" - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0, color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" - integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" - integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.4" - -colorette@^1.2.1, colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^2.19.0, commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - -commander@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - -comment-parser@1.1.5, comment-parser@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.1.5.tgz#453627ef8f67dbcec44e79a9bd5baa37f0bce9b2" - integrity sha512-RePCE4leIhBlmrqiYTvaqEeGYg7qpSl4etaIabKtdOQVi+mSTIBBklGUwIr79GXYnl3LpMwmDw4KeR2stNc6FA== - -common-tags@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" - integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -compose-function@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" - integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8= - dependencies: - arity-n "^1.0.4" - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.5.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -confusing-browser-globals@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" - integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== - -connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== - -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -convert-source-map@1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - -convert-source-map@^0.3.3: - version "0.3.5" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" - integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= - -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -core-js-compat@^3.14.0, core-js-compat@^3.15.0, core-js-compat@^3.6.2: - version "3.15.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.2.tgz#47272fbb479880de14b4e6081f71f3492f5bd3cb" - integrity sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ== - dependencies: - browserslist "^4.16.6" - semver "7.0.0" - -core-js-pure@^3.15.0: - version "3.15.2" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.15.2.tgz#c8e0874822705f3385d3197af9348f7c9ae2e3ce" - integrity sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA== - -core-js@^2.4.0: - version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - -core-js@^3.0.1, core-js@^3.15.2, core-js@^3.6.5: - version "3.15.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" - integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q== - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.7.2" - -cosmiconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= - -css-blank-pseudo@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" - integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== - dependencies: - postcss "^7.0.5" - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-has-pseudo@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee" - integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^5.0.0-rc.4" - -css-loader@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.3.0.tgz#c888af64b2a5b2e85462c72c0f4a85c7e2e0821e" - integrity sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg== - dependencies: - camelcase "^6.0.0" - cssesc "^3.0.0" - icss-utils "^4.1.1" - loader-utils "^2.0.0" - postcss "^7.0.32" - 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.1.0" - schema-utils "^2.7.1" - semver "^7.3.2" - -css-prefers-color-scheme@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4" - integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== - dependencies: - postcss "^7.0.5" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-select@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" - integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== - dependencies: - boolbase "^1.0.0" - css-what "^5.0.0" - domhandler "^4.2.0" - domutils "^2.6.0" - nth-check "^2.0.0" - -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - -css-tree@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - -css-what@^3.2.1: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" - integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== - -css-what@^5.0.0, css-what@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" - integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== - -css@^2.0.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" - integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== - dependencies: - inherits "^2.0.3" - source-map "^0.6.1" - source-map-resolve "^0.5.2" - urix "^0.1.0" - -cssdb@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0" - integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== - -cssesc@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" - integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssnano-preset-default@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff" - integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.3" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@^4.1.10: - version "4.1.11" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.11.tgz#c7b5f5b81da269cb1fd982cb960c1200910c9a99" - integrity sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.8" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@^4.0.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== - dependencies: - css-tree "^1.1.2" - -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - -csstype@^3.0.2: - version "3.0.8" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" - integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== - -cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -damerau-levenshtein@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d" - integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw== - -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decimal.js@^10.2.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= - -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - -deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -default-gateway@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" - integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== - dependencies: - execa "^1.0.0" - ip-regex "^2.1.0" - -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -del@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" - integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== - dependencies: - "@types/glob" "^7.1.1" - globby "^6.1.0" - is-path-cwd "^2.0.0" - is-path-in-cwd "^2.0.0" - p-map "^2.0.0" - pify "^4.0.1" - rimraf "^2.6.3" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - -detect-port-alt@1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" - integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== - dependencies: - address "^1.0.1" - debug "^2.6.0" - -diff-sequences@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" - integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -discontinuous-range@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" - integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= - -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= - -dns-packet@^1.3.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" - integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= - dependencies: - buffer-indexof "^1.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dom-converter@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" - integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== - dependencies: - utila "~0.4" - -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -dom-serializer@^1.0.1, dom-serializer@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" - integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -domelementtype@1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" - integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== - -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - -domhandler@^4.0.0, domhandler@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" - integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== - dependencies: - domelementtype "^2.2.0" - -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" - integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - -dot-prop@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== - dependencies: - is-obj "^2.0.0" - -dotenv-expand@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" - integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== - -dotenv@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== - -duplexer@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -ejs@^2.6.1: - version "2.7.4" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== - -electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.723: - version "1.3.766" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.766.tgz#2fd14a4e54f77665872f4e23fcf4968e83638220" - integrity sha512-u2quJ862q9reRKh/je3GXis3w38+RoXH1J9N3XjtsS6NzmUAosNsyZgUVFZPN/ZlJ3v6T0rTyZR3q/J5c6Sy5w== - -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emittery@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" - integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.0.0: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enhanced-resolve@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" - -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - -enzyme-shallow-equal@^1.0.0, enzyme-shallow-equal@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz#b9256cb25a5f430f9bfe073a84808c1d74fced2e" - integrity sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q== - dependencies: - has "^1.0.3" - object-is "^1.1.2" - -enzyme@^3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28" - integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw== - dependencies: - array.prototype.flat "^1.2.3" - cheerio "^1.0.0-rc.3" - enzyme-shallow-equal "^1.0.1" - function.prototype.name "^1.1.2" - has "^1.0.3" - html-element-map "^1.2.0" - is-boolean-object "^1.0.1" - is-callable "^1.1.5" - is-number-object "^1.0.4" - is-regex "^1.0.5" - is-string "^1.0.5" - is-subset "^0.1.1" - lodash.escape "^4.0.1" - lodash.isequal "^4.5.0" - object-inspect "^1.7.0" - object-is "^1.0.2" - object.assign "^4.1.0" - object.entries "^1.1.1" - object.values "^1.1.1" - raf "^3.4.1" - rst-selector-parser "^2.2.3" - string.prototype.trim "^1.2.1" - -errno@^0.1.3, errno@~0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -error-stack-parser@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" - integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== - dependencies: - stackframe "^1.1.1" - -es-abstract@^1.17.2, es-abstract@^1.18.0, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: - version "1.18.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" - integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.2" - is-callable "^1.2.3" - is-negative-zero "^2.0.1" - is-regex "^1.1.3" - is-string "^1.0.6" - object-inspect "^1.10.3" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" - -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.53" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" - integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.3" - next-tick "~1.0.0" - -es6-iterator@2.0.3, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -escalade@^3.0.2, escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-prettier@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" - integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== - -eslint-config-react-app@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz#ccff9fc8e36b322902844cbd79197982be355a0e" - integrity sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A== - dependencies: - confusing-browser-globals "^1.0.10" - -eslint-import-resolver-node@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" - integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== - dependencies: - debug "^2.6.9" - resolve "^1.13.1" - -eslint-module-utils@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" - integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== - dependencies: - debug "^3.2.7" - pkg-dir "^2.0.0" - -eslint-plugin-flowtype@^5.2.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.8.0.tgz#35b55e4ce559b90efbe913ed33630e391e301481" - integrity sha512-feK1xnUTsMSNTOw9jFw7aVgZl7Ep+ghpta/YEoaV6jbXU6Yso30B7BIj9ObHLzZ5TFJL7D98az080wfykLCrcw== - dependencies: - lodash "^4.17.15" - string-natural-compare "^3.0.1" - -eslint-plugin-import@^2.22.1: - version "2.23.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz#8dceb1ed6b73e46e50ec9a5bb2411b645e7d3d97" - integrity sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ== - dependencies: - array-includes "^3.1.3" - array.prototype.flat "^1.2.4" - debug "^2.6.9" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.4" - eslint-module-utils "^2.6.1" - find-up "^2.0.0" - has "^1.0.3" - is-core-module "^2.4.0" - minimatch "^3.0.4" - object.values "^1.1.3" - pkg-up "^2.0.0" - read-pkg-up "^3.0.0" - resolve "^1.20.0" - tsconfig-paths "^3.9.0" - -eslint-plugin-jest@^24.1.0: - version "24.3.6" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.3.6.tgz#5f0ca019183c3188c5ad3af8e80b41de6c8e9173" - integrity sha512-WOVH4TIaBLIeCX576rLcOgjNXqP+jNlCiEmRgFTfQtJ52DpwnIQKAVGlGPAN7CZ33bW6eNfHD6s8ZbEUTQubJg== - dependencies: - "@typescript-eslint/experimental-utils" "^4.0.1" - -eslint-plugin-jsdoc@^35.4.1: - version "35.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.4.1.tgz#7fb2a8c9bd8e304ab1feee48aa34544df5f79839" - integrity sha512-lnpu2Bj+ta2eAqwCWnb6f3Xjc78TWKo/oMCpDH5NfpPhYnePNtGZJzoAMgU5uo9BQqmXJ8pql8aiodOhg82ofw== - dependencies: - "@es-joy/jsdoccomment" "^0.8.0" - comment-parser "1.1.5" - debug "^4.3.1" - esquery "^1.4.0" - jsdoc-type-pratt-parser "^1.0.4" - lodash "^4.17.21" - regextras "^0.8.0" - semver "^7.3.5" - spdx-expression-parse "^3.0.1" - -eslint-plugin-jsx-a11y@^6.3.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz#a2d84caa49756942f42f1ffab9002436391718fd" - integrity sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg== - dependencies: - "@babel/runtime" "^7.11.2" - aria-query "^4.2.2" - array-includes "^3.1.1" - ast-types-flow "^0.0.7" - axe-core "^4.0.2" - axobject-query "^2.2.0" - damerau-levenshtein "^1.0.6" - emoji-regex "^9.0.0" - has "^1.0.3" - jsx-ast-utils "^3.1.0" - language-tags "^1.0.5" - -eslint-plugin-prettier@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" - integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw== - dependencies: - prettier-linter-helpers "^1.0.0" - -eslint-plugin-react-hooks@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556" - integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ== - -eslint-plugin-react@^7.21.5: - version "7.24.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz#eadedfa351a6f36b490aa17f4fa9b14e842b9eb4" - integrity sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q== - dependencies: - array-includes "^3.1.3" - array.prototype.flatmap "^1.2.4" - doctrine "^2.1.0" - has "^1.0.3" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.0.4" - object.entries "^1.1.4" - object.fromentries "^2.0.4" - object.values "^1.1.4" - prop-types "^15.7.2" - resolve "^2.0.0-next.3" - string.prototype.matchall "^4.0.5" - -eslint-plugin-testing-library@^3.9.2: - version "3.10.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz#609ec2b0369da7cf2e6d9edff5da153cc31d87bd" - integrity sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA== - dependencies: - "@typescript-eslint/experimental-utils" "^3.10.1" - -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^5.0.0, eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-utils@^2.0.0, eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-webpack-plugin@^2.5.2: - version "2.5.4" - resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-2.5.4.tgz#473b84932f1a8e2c2b8e66a402d0497bf440b986" - integrity sha512-7rYh0m76KyKSDE+B+2PUQrlNS4HJ51t3WKpkJg6vo2jFMbEPTG99cBV0Dm7LXSHucN4WGCG65wQcRiTFrj7iWw== - dependencies: - "@types/eslint" "^7.2.6" - arrify "^2.0.1" - jest-worker "^26.6.2" - micromatch "^4.0.2" - normalize-path "^3.0.0" - schema-utils "^3.0.0" - -eslint@^7.11.0, eslint@^7.30.0: - version "7.30.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8" - integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.2" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" - esutils "^2.0.2" - 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.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== - dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.1.0, esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -estree-walker@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" - integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== - -estree-walker@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" - integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -eventsource@^1.0.7: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf" - integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg== - dependencies: - original "^1.0.0" - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expect@^26.6.0, expect@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" - integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== - dependencies: - "@jest/types" "^26.6.2" - ansi-styles "^4.0.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" - -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - 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.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" - integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== - dependencies: - type "^2.0.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== - -fast-glob@^3.1.1: - version "3.2.6" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.6.tgz#434dd9529845176ea049acc9343e8282765c6e1a" - integrity sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -fastq@^1.6.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" - integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== - dependencies: - reusify "^1.0.4" - -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - -fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== - dependencies: - bser "2.1.1" - -figgy-pudding@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -file-loader@6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.1.tgz#a6f29dfb3f5933a1c350b2dbaa20ac5be0539baa" - integrity sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filesize@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" - integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg== - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - 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" - -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-cache-dir@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" - integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - -find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^2.0.0, find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatted@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.0.tgz#da07fb8808050aba6fdeac2294542e5043583f05" - integrity sha512-XprP7lDrVT+kE2c2YlfiV+IfS9zxukiIOvNamPNsImNhXadSsQEbosItdL9bUQlCZXR13SvPk20BjWSWLA7m4A== - -flatten@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" - integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== - -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -follow-redirects@^1.0.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" - integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -fork-ts-checker-webpack-plugin@4.1.6: - version "4.1.6" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" - integrity sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw== - dependencies: - "@babel/code-frame" "^7.5.5" - chalk "^2.4.1" - micromatch "^3.1.10" - minimatch "^3.0.4" - semver "^5.6.0" - tapable "^1.0.0" - worker-rpc "^0.1.0" - -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -fs-extra@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@^2.1.2, fsevents@^2.1.3, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function.prototype.name@^1.1.0, function.prototype.name@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.4.tgz#e4ea839b9d3672ae99d0efd9f38d9191c5eaac83" - integrity sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - functions-have-names "^1.2.2" - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -functions-have-names@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21" - integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA== - -gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-own-enumerable-property-symbols@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" - integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== - -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-modules@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.6.0, globals@^13.9.0: - version "13.9.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" - integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== - dependencies: - type-fest "^0.20.2" - -globby@11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" - integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - -globby@^11.0.3: - version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" - integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= - -gzip-size@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" - integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== - dependencies: - duplexer "^0.1.1" - pify "^4.0.1" - -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - -handlebars@^4.7.3: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - -harmony-reflect@^1.4.6: - version "1.6.2" - resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" - integrity sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g== - -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.0, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -history@^4.9.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== - dependencies: - "@babel/runtime" "^7.1.2" - loose-envify "^1.2.0" - resolve-pathname "^3.0.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - value-equal "^1.0.1" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - -hoopy@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" - integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - -html-element-map@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.3.1.tgz#44b2cbcfa7be7aa4ff59779e47e51012e1c73c08" - integrity sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg== - dependencies: - array.prototype.filter "^1.0.0" - call-bind "^1.0.2" - -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - -html-entities@^1.2.1, html-entities@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" - integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -html-minifier-terser@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054" - integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg== - dependencies: - camel-case "^4.1.1" - clean-css "^4.2.3" - commander "^4.1.1" - he "^1.2.0" - param-case "^3.0.3" - relateurl "^0.2.7" - terser "^4.6.3" - -html-webpack-plugin@4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz#625097650886b97ea5dae331c320e3238f6c121c" - integrity sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw== - dependencies: - "@types/html-minifier-terser" "^5.0.0" - "@types/tapable" "^1.0.5" - "@types/webpack" "^4.41.8" - html-minifier-terser "^5.0.1" - loader-utils "^1.2.3" - lodash "^4.17.15" - pretty-error "^2.1.1" - tapable "^1.1.3" - util.promisify "1.0.0" - -htmlparser2@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-parser-js@>=0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" - integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== - -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - -http-proxy-middleware@0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" - integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== - dependencies: - http-proxy "^1.17.0" - is-glob "^4.0.0" - lodash "^4.17.11" - micromatch "^3.1.10" - -http-proxy@^1.17.0: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" - -identity-obj-proxy@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" - integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ= - dependencies: - harmony-reflect "^1.4.6" - -ieee754@^1.1.4: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - -immer@8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" - integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== - -import-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" - integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= - dependencies: - import-from "^2.1.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" - integrity sha1-M1238qev/VOqpHHUuAId7ja387E= - dependencies: - resolve-from "^3.0.0" - -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - -import-local@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" - integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - -infer-owner@^1.0.3, infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.5: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -internal-ip@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" - integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== - dependencies: - default-gateway "^4.2.0" - ipaddr.js "^1.9.0" - -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - -ip@^1.1.0, ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= - -ipaddr.js@1.9.1, ipaddr.js@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= - -is-absolute-url@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arguments@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" - integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== - dependencies: - call-bind "^1.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-bigint@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" - integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.0.1, is-boolean-object@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" - integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== - dependencies: - call-bind "^1.0.2" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== - -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - -is-core-module@^2.0.0, is-core-module@^2.2.0, is-core-module@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" - integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" - integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= - -is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== - -is-number-object@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" - integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-path-cwd@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-in-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" - integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== - dependencies: - is-path-inside "^2.1.0" - -is-path-inside@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" - integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== - dependencies: - path-is-inside "^1.0.2" - -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - -is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" - integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== - dependencies: - call-bind "^1.0.2" - has-symbols "^1.0.2" - -is-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" - integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-root@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" - integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-string@^1.0.5, is-string@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" - integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== - -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - -is-wsl@^2.1.1, is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -istanbul-lib-coverage@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" - integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== - -istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -jest-changed-files@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" - integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== - dependencies: - "@jest/types" "^26.6.2" - execa "^4.0.0" - throat "^5.0.0" - -jest-circus@26.6.0: - version "26.6.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-26.6.0.tgz#7d9647b2e7f921181869faae1f90a2629fd70705" - integrity sha512-L2/Y9szN6FJPWFK8kzWXwfp+FOR7xq0cUL4lIsdbIdwz3Vh6P1nrpcqOleSzr28zOtSHQNV9Z7Tl+KkuK7t5Ng== - dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.0" - "@jest/test-result" "^26.6.0" - "@jest/types" "^26.6.0" - "@types/babel__traverse" "^7.0.4" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - expect "^26.6.0" - is-generator-fn "^2.0.0" - jest-each "^26.6.0" - jest-matcher-utils "^26.6.0" - jest-message-util "^26.6.0" - jest-runner "^26.6.0" - jest-runtime "^26.6.0" - jest-snapshot "^26.6.0" - jest-util "^26.6.0" - pretty-format "^26.6.0" - stack-utils "^2.0.2" - throat "^5.0.0" - -jest-cli@^26.6.0: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" - integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== - dependencies: - "@jest/core" "^26.6.3" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.4" - import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^26.6.3" - jest-util "^26.6.2" - jest-validate "^26.6.2" - prompts "^2.0.1" - yargs "^15.4.1" - -jest-config@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" - integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== - dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.6.3" - "@jest/types" "^26.6.2" - babel-jest "^26.6.3" - chalk "^4.0.0" - deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.4" - 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.2" - pretty-format "^26.6.2" - -jest-diff@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" - integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-docblock@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" - integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== - dependencies: - detect-newline "^3.0.0" - -jest-each@^26.6.0, jest-each@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" - integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== - dependencies: - "@jest/types" "^26.6.2" - chalk "^4.0.0" - jest-get-type "^26.3.0" - jest-util "^26.6.2" - pretty-format "^26.6.2" - -jest-environment-jsdom@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" - integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== - dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - jsdom "^16.4.0" - -jest-environment-node@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" - integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== - dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - -jest-get-type@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" - integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== - -jest-haste-map@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" - integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== - dependencies: - "@jest/types" "^26.6.2" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^26.0.0" - jest-serializer "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - micromatch "^4.0.2" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.1.2" - -jest-jasmine2@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" - integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== - dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^26.6.2" - is-generator-fn "^2.0.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" - -jest-leak-detector@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" - integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== - dependencies: - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-matcher-utils@^26.6.0, jest-matcher-utils@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" - integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== - dependencies: - chalk "^4.0.0" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-message-util@^26.6.0, jest-message-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" - integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.6.2" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - pretty-format "^26.6.2" - slash "^3.0.0" - stack-utils "^2.0.2" - -jest-mock@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" - integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - -jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== - -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== - -jest-resolve-dependencies@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" - integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== - dependencies: - "@jest/types" "^26.6.2" - jest-regex-util "^26.0.0" - jest-snapshot "^26.6.2" - -jest-resolve@26.6.0: - version "26.6.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.0.tgz#070fe7159af87b03e50f52ea5e17ee95bbee40e1" - integrity sha512-tRAz2bwraHufNp+CCmAD8ciyCpXCs1NQxB5EJAmtCFy6BN81loFEGWKzYu26Y62lAJJe4X4jg36Kf+NsQyiStQ== - dependencies: - "@jest/types" "^26.6.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-pnp-resolver "^1.2.2" - jest-util "^26.6.0" - read-pkg-up "^7.0.1" - resolve "^1.17.0" - slash "^3.0.0" - -jest-resolve@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" - integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== - dependencies: - "@jest/types" "^26.6.2" - chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-pnp-resolver "^1.2.2" - jest-util "^26.6.2" - read-pkg-up "^7.0.1" - resolve "^1.18.1" - slash "^3.0.0" - -jest-runner@^26.6.0, jest-runner@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" - integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== - dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.7.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - 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.6" - throat "^5.0.0" - -jest-runtime@^26.6.0, jest-runtime@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" - integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== - 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.0" - chalk "^4.0.0" - cjs-module-lexer "^0.6.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.4" - 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" - -jest-serializer@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" - integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - -jest-snapshot@^26.6.0, jest-snapshot@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" - integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^26.6.2" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.0.0" - chalk "^4.0.0" - expect "^26.6.2" - graceful-fs "^4.2.4" - 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.2" - -jest-util@^26.6.0, jest-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" - integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" - -jest-validate@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" - integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== - dependencies: - "@jest/types" "^26.6.2" - camelcase "^6.0.0" - chalk "^4.0.0" - jest-get-type "^26.3.0" - leven "^3.1.0" - pretty-format "^26.6.2" - -jest-watch-typeahead@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.6.1.tgz#45221b86bb6710b7e97baaa1640ae24a07785e63" - integrity sha512-ITVnHhj3Jd/QkqQcTqZfRgjfyRhDFM/auzgVo2RKvSwi18YMvh0WvXDJFoFED6c7jd/5jxtu4kSOb9PTu2cPVg== - dependencies: - ansi-escapes "^4.3.1" - chalk "^4.0.0" - jest-regex-util "^26.0.0" - jest-watcher "^26.3.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - -jest-watcher@^26.3.0, jest-watcher@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" - integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== - dependencies: - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - jest-util "^26.6.2" - string-length "^4.0.1" - -jest-worker@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" - integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== - dependencies: - merge-stream "^2.0.0" - supports-color "^6.1.0" - -jest-worker@^26.5.0, jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest@26.6.0: - version "26.6.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.0.tgz#546b25a1d8c888569dbbe93cae131748086a4a25" - integrity sha512-jxTmrvuecVISvKFFhOkjsWRZV7sFqdSUAd1ajOKY+/QE/aLBVstsJ/dX8GczLzwiT6ZEwwmZqtCUHLHHQVzcfA== - dependencies: - "@jest/core" "^26.6.0" - import-local "^3.0.2" - jest-cli "^26.6.0" - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsdoc-type-pratt-parser@1.0.4, jsdoc-type-pratt-parser@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.4.tgz#5750d2d32ffb001866537d3baaedea7cf84c7036" - integrity sha512-jzmW9gokeq9+bHPDR1nCeidMyFUikdZlbOhKzh9+/nJqB75XhpNKec1/UuxW5c4+O+Pi31Gc/dCboyfSm/pSpQ== - -jsdom@^16.4.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" - integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - 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.0.0" - 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.5.0" - ws "^7.4.5" - xml-name-validator "^3.0.0" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json3@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" - integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" - integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q== - dependencies: - array-includes "^3.1.2" - object.assign "^4.1.2" - -killable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" - integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -klona@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" - integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== - -language-subtag-registry@~0.3.2: - version "0.3.21" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" - integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== - -language-tags@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= - dependencies: - language-subtag-registry "~0.3.2" - -last-call-webpack-plugin@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" - integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== - dependencies: - lodash "^4.17.5" - webpack-sources "^1.1.0" - -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= - -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - -loader-utils@2.0.0, loader-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" - integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - -loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= - -lodash.escape@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" - integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= - -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= - -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash.throttle@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" - integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= - -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - -"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.7.0: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -loglevel@^1.6.8: - version "1.7.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" - integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== - -loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -magic-string@^0.25.0, magic-string@^0.25.7: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== - dependencies: - sourcemap-codec "^1.4.4" - -make-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -make-dir@^3.0.0, make-dir@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -makeerror@1.0.x: - version "1.0.11" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" - integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= - dependencies: - tmpl "1.0.x" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -microevent.ts@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" - integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== - -micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== - dependencies: - braces "^3.0.1" - picomatch "^2.2.3" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.48.0, "mime-db@>= 1.43.0 < 2": - version "1.48.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" - integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== - -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24: - version "2.1.31" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" - integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== - dependencies: - mime-db "1.48.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.4.4: - version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mini-create-react-context@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" - integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== - dependencies: - "@babel/runtime" "^7.12.1" - tiny-warning "^1.0.3" - -mini-css-extract-plugin@0.11.3: - version "0.11.3" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz#15b0910a7f32e62ffde4a7430cfefbd700724ea6" - integrity sha512-n9BA8LonkOkW1/zn+IbLPQmovsL0wMb9yx75fMJQZf2X1Zoec9yTZtyMePcyu19wPkmFbzZZA6fLTotpFhQsOA== - dependencies: - loader-utils "^1.1.0" - normalize-url "1.9.1" - schema-utils "^1.0.0" - webpack-sources "^1.1.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-pipeline@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" - integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== - dependencies: - minipass "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== - dependencies: - yallist "^4.0.0" - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -moo@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" - integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== - dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" - -nan@^2.12.1: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - -nanoid@^3.1.23: - version "3.1.23" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" - integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - 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.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -native-url@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.2.6.tgz#ca1258f5ace169c716ff44eccbddb674e10399ae" - integrity sha512-k4bDC87WtgrdD362gZz6zoiXQrl40kYlBmpfmSjwRO1VU0V5ccwJTlxuE72F6m3V0vc1xOf6n3UCP9QyerRqmA== - dependencies: - querystring "^0.2.0" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -nearley@^2.7.10: - version "2.20.1" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" - integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== - dependencies: - commander "^2.19.0" - moo "^0.5.0" - railroad-diagrams "^1.0.0" - randexp "0.4.6" - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -next-tick@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - -node-fetch@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-forge@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" - integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= - -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - -node-notifier@^8.0.0: - version "8.0.2" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" - integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== - dependencies: - growly "^1.3.0" - is-wsl "^2.2.0" - semver "^7.3.2" - shellwords "^0.1.1" - uuid "^8.3.0" - which "^2.0.2" - -node-releases@^1.1.61, node-releases@^1.1.71: - version "1.1.73" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" - integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== - -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -normalize-url@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" - integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -npm-run-path@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -nth-check@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" - integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== - dependencies: - boolbase "^1.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.10.3, object-inspect@^1.7.0, object-inspect@^1.9.0: - version "1.10.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" - integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== - -object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.entries@^1.1.0, object.entries@^1.1.1, object.entries@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd" - integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.2" - -object.fromentries@^2.0.0, object.fromentries@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8" - integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - has "^1.0.3" - -object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" - integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.3, object.values@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" - integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.2" - -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -open@^7.0.2: - version "7.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" - integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== - dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" - -opn@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" - -optimize-css-assets-webpack-plugin@5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz#85883c6528aaa02e30bbad9908c92926bb52dc90" - integrity sha512-wqd6FdI2a5/FdoiCNNkEvLeA//lHHfG24Ln2Xm2qqdIk4aOlsR18jwpyOihqQ8849W3qu2DX8fOYxpvTMj+93A== - dependencies: - cssnano "^4.1.10" - last-call-webpack-plugin "^3.0.0" - -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -original@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" - integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== - dependencies: - url-parse "^1.4.3" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - -p-each-series@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" - integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -p-retry@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" - integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== - dependencies: - retry "^0.12.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pako@~1.0.5: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - -param-case@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" - integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== - dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parse-github-url@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-github-url/-/parse-github-url-1.0.2.tgz#242d3b65cbcdda14bb50439e3242acf6971db395" - integrity sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw== - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse5-htmlparser2-tree-adapter@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" - integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== - dependencies: - parse5 "^6.0.1" - -parse5@6.0.1, parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascal-case@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" - integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pirates@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" - -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-dir@^4.1.0, pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -pkg-up@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== - dependencies: - find-up "^3.0.0" - -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - -pnp-webpack-plugin@1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" - integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== - dependencies: - ts-pnp "^1.1.6" - -portfinder@^1.0.26: - version "1.0.28" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" - integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== - dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.5" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postcss-attribute-case-insensitive@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880" - integrity sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^6.0.2" - -postcss-browser-comments@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz#1248d2d935fb72053c8e1f61a84a57292d9f65e9" - integrity sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig== - dependencies: - postcss "^7" - -postcss-calc@^7.0.1: - version "7.0.5" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e" - integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg== - dependencies: - postcss "^7.0.27" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.2" - -postcss-color-functional-notation@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0" - integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-color-gray@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547" - integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.5" - postcss-values-parser "^2.0.0" - -postcss-color-hex-alpha@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388" - integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== - dependencies: - postcss "^7.0.14" - postcss-values-parser "^2.0.1" - -postcss-color-mod-function@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" - integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-color-rebeccapurple@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77" - integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-custom-media@^7.0.8: - version "7.0.8" - resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c" - integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== - dependencies: - postcss "^7.0.14" - -postcss-custom-properties@^8.0.11: - version "8.0.11" - resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97" - integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== - dependencies: - postcss "^7.0.17" - postcss-values-parser "^2.0.1" - -postcss-custom-selectors@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba" - integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-dir-pseudo-class@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2" - integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-double-position-gradients@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e" - integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== - dependencies: - postcss "^7.0.5" - postcss-values-parser "^2.0.0" - -postcss-env-function@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7" - integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-flexbugs-fixes@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz#9218a65249f30897deab1033aced8578562a6690" - integrity sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ== - dependencies: - postcss "^7.0.26" - -postcss-focus-visible@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" - integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== - dependencies: - postcss "^7.0.2" - -postcss-focus-within@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680" - integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== - dependencies: - postcss "^7.0.2" - -postcss-font-variant@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz#42d4c0ab30894f60f98b17561eb5c0321f502641" - integrity sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA== - dependencies: - postcss "^7.0.2" - -postcss-gap-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715" - integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== - dependencies: - postcss "^7.0.2" - -postcss-image-set-function@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288" - integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-initial@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.4.tgz#9d32069a10531fe2ecafa0b6ac750ee0bc7efc53" - integrity sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg== - dependencies: - postcss "^7.0.2" - -postcss-lab-function@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e" - integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-load-config@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" - integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== - dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" - -postcss-loader@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" - integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== - dependencies: - loader-utils "^1.1.0" - postcss "^7.0.0" - postcss-load-config "^2.0.0" - schema-utils "^1.0.0" - -postcss-logical@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5" - integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== - dependencies: - postcss "^7.0.2" - -postcss-media-minmax@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5" - integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== - dependencies: - postcss "^7.0.2" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" - -postcss-modules-local-by-default@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" - integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== - dependencies: - icss-utils "^4.1.1" - postcss "^7.0.32" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.1.0" - -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - -postcss-modules-values@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== - dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" - -postcss-nesting@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052" - integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== - dependencies: - postcss "^7.0.2" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize@8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize/-/postcss-normalize-8.0.1.tgz#90e80a7763d7fdf2da6f2f0f82be832ce4f66776" - integrity sha512-rt9JMS/m9FHIRroDDBGSMsyW1c0fkvOJPy62ggxSHUldJO7B195TqFMqIf+lY5ezpDcYOV4j86aUp3/XbxzCCQ== - dependencies: - "@csstools/normalize.css" "^10.1.0" - browserslist "^4.6.2" - postcss "^7.0.17" - postcss-browser-comments "^3.0.0" - sanitize.css "^10.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-overflow-shorthand@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30" - integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== - dependencies: - postcss "^7.0.2" - -postcss-page-break@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf" - integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== - dependencies: - postcss "^7.0.2" - -postcss-place@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62" - integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-preset-env@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz#c34ddacf8f902383b35ad1e030f178f4cdf118a5" - integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== - dependencies: - autoprefixer "^9.6.1" - browserslist "^4.6.4" - caniuse-lite "^1.0.30000981" - css-blank-pseudo "^0.1.4" - css-has-pseudo "^0.10.0" - css-prefers-color-scheme "^3.1.1" - cssdb "^4.4.0" - postcss "^7.0.17" - postcss-attribute-case-insensitive "^4.0.1" - postcss-color-functional-notation "^2.0.1" - postcss-color-gray "^5.0.0" - postcss-color-hex-alpha "^5.0.3" - postcss-color-mod-function "^3.0.3" - postcss-color-rebeccapurple "^4.0.1" - postcss-custom-media "^7.0.8" - postcss-custom-properties "^8.0.11" - postcss-custom-selectors "^5.1.2" - postcss-dir-pseudo-class "^5.0.0" - postcss-double-position-gradients "^1.0.0" - postcss-env-function "^2.0.2" - postcss-focus-visible "^4.0.0" - postcss-focus-within "^3.0.0" - postcss-font-variant "^4.0.0" - postcss-gap-properties "^2.0.0" - postcss-image-set-function "^3.0.1" - postcss-initial "^3.0.0" - postcss-lab-function "^2.0.1" - postcss-logical "^3.0.0" - postcss-media-minmax "^4.0.0" - postcss-nesting "^7.0.0" - postcss-overflow-shorthand "^2.0.0" - postcss-page-break "^2.0.0" - postcss-place "^4.0.1" - postcss-pseudo-class-any-link "^6.0.0" - postcss-replace-overflow-wrap "^3.0.0" - postcss-selector-matches "^4.0.0" - postcss-selector-not "^4.0.0" - -postcss-pseudo-class-any-link@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1" - integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-replace-overflow-wrap@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c" - integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== - dependencies: - postcss "^7.0.2" - -postcss-safe-parser@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-5.0.2.tgz#459dd27df6bc2ba64608824ba39e45dacf5e852d" - integrity sha512-jDUfCPJbKOABhwpUKcqCVbbXiloe/QXMcbJ6Iipf3sDIihEzTqRCeMBfRaOHxhBuTYqtASrI1KJWxzztZU4qUQ== - dependencies: - postcss "^8.1.0" - -postcss-selector-matches@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff" - integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== - dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" - -postcss-selector-not@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz#263016eef1cf219e0ade9a913780fc1f48204cbf" - integrity sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ== - dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" - -postcss-selector-parser@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" - integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== - dependencies: - dot-prop "^5.2.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" - integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - dependencies: - cssesc "^2.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: - version "6.0.6" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" - integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -postcss-svgo@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" - integrity sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== - -postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" - integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss@7.0.36, postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.36" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" - integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -postcss@^8.1.0: - version "8.3.5" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" - integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== - dependencies: - colorette "^1.2.2" - nanoid "^3.1.23" - source-map-js "^0.6.2" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prepend-http@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier@2.3.2, prettier@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" - integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== - -pretty-bytes@^5.3.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== - -pretty-error@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" - integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== - dependencies: - lodash "^4.17.20" - renderkid "^2.0.4" - -pretty-format@^26.6.0, pretty-format@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - -promise@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" - integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q== - dependencies: - asap "~2.0.6" - -prompts@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" - integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -prompts@^2.0.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" - integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.8.1" - -proxy-addr@~2.0.5: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= - -psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -query-string@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" - integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= - dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -querystring@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" - integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -raf@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" - integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== - dependencies: - performance-now "^2.1.0" - -railroad-diagrams@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" - integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= - -randexp@0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" - integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== - dependencies: - discontinuous-range "1.0.0" - ret "~0.1.10" - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -react-app-polyfill@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz#a0bea50f078b8a082970a9d853dc34b6dcc6a3cf" - integrity sha512-0sF4ny9v/B7s6aoehwze9vJNWcmCemAUYBVasscVr92+UYiEqDXOxfKjXN685mDaMRNF3WdhHQs76oTODMocFA== - dependencies: - core-js "^3.6.5" - object-assign "^4.1.1" - promise "^8.1.0" - raf "^3.4.1" - regenerator-runtime "^0.13.7" - whatwg-fetch "^3.4.1" - -react-dev-utils@^11.0.3: - version "11.0.4" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" - integrity sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A== - 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" - 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.0.2" - pkg-up "3.1.0" - prompts "2.4.0" - react-error-overlay "^6.0.9" - recursive-readdir "2.2.2" - shell-quote "1.7.2" - strip-ansi "6.0.0" - text-table "0.2.0" - -react-dom@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" - -react-error-overlay@^6.0.9: - version "6.0.9" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" - integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== - -"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-redux@^7.2.4: - version "7.2.4" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225" - integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA== - dependencies: - "@babel/runtime" "^7.12.1" - "@types/react-redux" "^7.1.16" - hoist-non-react-statics "^3.3.2" - loose-envify "^1.4.0" - prop-types "^15.7.2" - react-is "^16.13.1" - -react-refresh@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" - integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== - -react-router-dom@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" - integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.2.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - -react-router@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" - integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - hoist-non-react-statics "^3.1.0" - loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" - path-to-regexp "^1.7.0" - prop-types "^15.6.2" - react-is "^16.6.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - -react-scripts@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-4.0.3.tgz#b1cafed7c3fa603e7628ba0f187787964cb5d345" - integrity sha512-S5eO4vjUzUisvkIPB7jVsKtuH2HhWcASREYWHAQ1FP5HyCv3xgn+wpILAEWkmy+A+tTNbSZClhxjT3qz6g4L1A== - dependencies: - "@babel/core" "7.12.3" - "@pmmmwh/react-refresh-webpack-plugin" "0.4.3" - "@svgr/webpack" "5.5.0" - "@typescript-eslint/eslint-plugin" "^4.5.0" - "@typescript-eslint/parser" "^4.5.0" - babel-eslint "^10.1.0" - babel-jest "^26.6.0" - babel-loader "8.1.0" - babel-plugin-named-asset-import "^0.3.7" - babel-preset-react-app "^10.0.0" - bfj "^7.0.2" - camelcase "^6.1.0" - case-sensitive-paths-webpack-plugin "2.3.0" - css-loader "4.3.0" - dotenv "8.2.0" - dotenv-expand "5.1.0" - eslint "^7.11.0" - eslint-config-react-app "^6.0.0" - eslint-plugin-flowtype "^5.2.0" - eslint-plugin-import "^2.22.1" - eslint-plugin-jest "^24.1.0" - eslint-plugin-jsx-a11y "^6.3.1" - eslint-plugin-react "^7.21.5" - eslint-plugin-react-hooks "^4.2.0" - eslint-plugin-testing-library "^3.9.2" - eslint-webpack-plugin "^2.5.2" - file-loader "6.1.1" - fs-extra "^9.0.1" - html-webpack-plugin "4.5.0" - identity-obj-proxy "3.0.0" - jest "26.6.0" - jest-circus "26.6.0" - jest-resolve "26.6.0" - jest-watch-typeahead "0.6.1" - mini-css-extract-plugin "0.11.3" - optimize-css-assets-webpack-plugin "5.0.4" - pnp-webpack-plugin "1.6.4" - postcss-flexbugs-fixes "4.2.1" - postcss-loader "3.0.0" - postcss-normalize "8.0.1" - postcss-preset-env "6.7.0" - postcss-safe-parser "5.0.2" - prompts "2.4.0" - react-app-polyfill "^2.0.0" - react-dev-utils "^11.0.3" - react-refresh "^0.8.3" - resolve "1.18.1" - resolve-url-loader "^3.1.2" - sass-loader "^10.0.5" - semver "7.3.2" - style-loader "1.3.0" - terser-webpack-plugin "4.2.3" - ts-pnp "1.2.0" - url-loader "4.1.1" - webpack "4.44.2" - webpack-dev-server "3.11.1" - webpack-manifest-plugin "2.2.0" - workbox-webpack-plugin "5.1.4" - optionalDependencies: - fsevents "^2.1.3" - -react-shallow-renderer@^16.13.1: - version "16.14.1" - resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124" - integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg== - dependencies: - object-assign "^4.1.1" - react-is "^16.12.0 || ^17.0.0" - -react-test-renderer@^17.0.0: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" - integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== - dependencies: - object-assign "^4.1.1" - react-is "^17.0.2" - react-shallow-renderer "^16.13.1" - scheduler "^0.20.2" - -react@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.0.6, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -recursive-readdir@2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== - dependencies: - minimatch "3.0.4" - -redux@4.1.0, redux@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4" - integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g== - dependencies: - "@babel/runtime" "^7.9.2" - -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== - dependencies: - "@babel/runtime" "^7.8.4" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regex-parser@^2.2.11: - version "2.2.11" - resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" - integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== - -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" - integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -regexpp@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -regexpu-core@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" - integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - -regextras@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.8.0.tgz#ec0f99853d4912839321172f608b544814b02217" - integrity sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ== - -regjsgen@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== - -regjsparser@^0.6.4: - version "0.6.9" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6" - integrity sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ== - dependencies: - jsesc "~0.5.0" - -relateurl@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -renderkid@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" - integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ== - dependencies: - css-select "^4.1.3" - dom-converter "^0.2.0" - htmlparser2 "^6.1.0" - lodash "^4.17.21" - strip-ansi "^3.0.1" - -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= - dependencies: - resolve-from "^3.0.0" - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== - -resolve-url-loader@^3.1.2: - version "3.1.4" - resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.4.tgz#3c16caebe0b9faea9c7cc252fa49d2353c412320" - integrity sha512-D3sQ04o0eeQEySLrcz4DsX3saHfsr8/N6tfhblxgZKXxMT2Louargg12oGNfoTRLV09GXhVUe5/qgA5vdgNigg== - dependencies: - adjust-sourcemap-loader "3.0.0" - camelcase "5.3.1" - compose-function "3.0.3" - convert-source-map "1.7.0" - es6-iterator "2.0.3" - loader-utils "1.2.3" - postcss "7.0.36" - rework "1.0.1" - rework-visit "1.0.0" - source-map "0.6.1" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" - integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== - dependencies: - is-core-module "^2.0.0" - path-parse "^1.0.6" - -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.8.1: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -resolve@^2.0.0-next.3: - version "2.0.0-next.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" - integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rework-visit@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" - integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= - -rework@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" - integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= - dependencies: - convert-source-map "^0.3.3" - css "^2.0.0" - -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - -rimraf@^2.5.4, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -rollup-plugin-babel@^4.3.3: - version "4.4.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.4.0.tgz#d15bd259466a9d1accbdb2fe2fff17c52d030acb" - integrity sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - rollup-pluginutils "^2.8.1" - -rollup-plugin-terser@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.3.1.tgz#8c650062c22a8426c64268548957463bf981b413" - integrity sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w== - dependencies: - "@babel/code-frame" "^7.5.5" - jest-worker "^24.9.0" - rollup-pluginutils "^2.8.2" - serialize-javascript "^4.0.0" - terser "^4.6.2" - -rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: - version "2.8.2" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" - integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== - dependencies: - estree-walker "^0.6.1" - -rollup@^1.31.1: - version "1.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.32.1.tgz#4480e52d9d9e2ae4b46ba0d9ddeaf3163940f9c4" - integrity sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A== - dependencies: - "@types/estree" "*" - "@types/node" "*" - acorn "^7.1.0" - -rst-selector-parser@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" - integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE= - dependencies: - lodash.flattendeep "^4.4.0" - nearley "^2.7.10" - -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - -sanitize.css@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/sanitize.css/-/sanitize.css-10.0.0.tgz#b5cb2547e96d8629a60947544665243b1dc3657a" - integrity sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg== - -sass-loader@^10.0.5: - version "10.2.0" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.2.0.tgz#3d64c1590f911013b3fa48a0b22a83d5e1494716" - integrity sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw== - dependencies: - klona "^2.0.4" - loader-utils "^2.0.0" - neo-async "^2.6.2" - schema-utils "^3.0.0" - semver "^7.3.2" - -sass@^1.35.1: - version "1.35.1" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.35.1.tgz#90ecf774dfe68f07b6193077e3b42fb154b9e1cd" - integrity sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ== - dependencies: - chokidar ">=3.0.0 <4.0.0" - -sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - -schema-utils@^2.6.5, schema-utils@^2.7.0, schema-utils@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== - dependencies: - "@types/json-schema" "^7.0.5" - ajv "^6.12.4" - ajv-keywords "^3.5.2" - -schema-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" - integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== - dependencies: - "@types/json-schema" "^7.0.6" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= - -selfsigned@^1.10.8: - version "1.10.11" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9" - integrity sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA== - dependencies: - node-forge "^0.10.0" - -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - 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.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== - dependencies: - randombytes "^2.1.0" - -serialize-javascript@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shell-quote@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" - integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== - -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - -simplebar-react@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/simplebar-react/-/simplebar-react-2.3.4.tgz#aed39238b6cd0760e5b6751e29c7d8663a691acb" - integrity sha512-dNRaVa3UDbjyqu2Xpxm44+bfUGS0c1Y6cO0QkzmR90d6BwdlGeelDGYYF8MDloaAu4gttKwhwDqDZe70CJuYXg== - dependencies: - prop-types "^15.6.1" - simplebar "^5.3.4" - -simplebar@^5.3.4: - version "5.3.4" - resolved "https://registry.yarnpkg.com/simplebar/-/simplebar-5.3.4.tgz#7de8d4a07ed3c6612644f4dbc04a8427fdf038ef" - integrity sha512-2mCaVdiroCKmXuD+Qfy+QSE32m5BMuZ4ssHvRD1QEPYH95Re/kox7j/Wy0Hje8Uo7LY7O6JK3XSNJmesGlsP8Q== - dependencies: - "@juggle/resize-observer" "^3.3.1" - can-use-dom "^0.1.0" - core-js "^3.0.1" - lodash.debounce "^4.0.8" - lodash.memoize "^4.1.2" - lodash.throttle "^4.1.1" - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -sockjs-client@^1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.1.tgz#256908f6d5adfb94dabbdbd02c66362cca0f9ea6" - integrity sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ== - dependencies: - debug "^3.2.6" - eventsource "^1.0.7" - faye-websocket "^0.11.3" - inherits "^2.0.4" - json3 "^3.3.3" - url-parse "^1.5.1" - -sockjs@^0.3.21: - version "0.3.21" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417" - integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw== - dependencies: - faye-websocket "^0.11.3" - uuid "^3.4.0" - websocket-driver "^0.7.4" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== - -source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.5.0, source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.7.3, source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - -sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0, spdx-expression-parse@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" - integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -ssri@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" - integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== - dependencies: - figgy-pudding "^3.5.1" - -ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -stack-utils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" - integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== - dependencies: - escape-string-regexp "^2.0.0" - -stackframe@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" - integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= - -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -string-natural-compare@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" - integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string.prototype.matchall@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" - integrity sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.2" - get-intrinsic "^1.1.1" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.3.1" - side-channel "^1.0.4" - -string.prototype.trim@^1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz#6014689baf5efaf106ad031a5fa45157666ed1bd" - integrity sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string_decoder@^1.0.0, string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -stringify-object@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" - integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== - dependencies: - get-own-enumerable-property-symbols "^3.0.0" - is-obj "^1.0.1" - is-regexp "^1.0.0" - -strip-ansi@6.0.0, strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-comments@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-1.0.2.tgz#82b9c45e7f05873bee53f37168af930aa368679d" - integrity sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw== - dependencies: - babel-extract-comments "^1.0.0" - babel-plugin-transform-object-rest-spread "^6.26.0" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -style-loader@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e" - integrity sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q== - dependencies: - loader-utils "^2.0.0" - schema-utils "^2.7.0" - -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.0.0, supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - -svg-parser@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" - integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== - -svgo@^1.0.0, svgo@^1.2.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -table@^6.0.9: - version "6.7.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" - -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - -tar@^6.0.2: - version "6.1.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" - integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -temp-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= - -tempy@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.3.0.tgz#6f6c5b295695a16130996ad5ab01a8bd726e8bf8" - integrity sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ== - dependencies: - temp-dir "^1.0.0" - type-fest "^0.3.1" - unique-string "^1.0.0" - -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - -terser-webpack-plugin@4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" - integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== - dependencies: - cacache "^15.0.5" - find-cache-dir "^3.3.1" - jest-worker "^26.5.0" - p-limit "^3.0.2" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - source-map "^0.6.1" - terser "^5.3.4" - webpack-sources "^1.4.3" - -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@^4.1.2, terser@^4.6.2, terser@^4.6.3: - version "4.8.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -terser@^5.3.4: - version "5.7.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" - integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -text-table@0.2.0, text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== - -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - -timers-browserify@^2.0.4: - version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - -tiny-invariant@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" - integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== - -tiny-warning@^1.0.0, tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== - -tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - -tryer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" - integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== - -ts-pnp@1.2.0, ts-pnp@^1.1.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" - integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== - -tsconfig-paths@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" - integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.0" - strip-bom "^3.0.0" - -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.0.3, tslib@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== - -tsutils@^3.17.1, tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" - integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== - -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.0.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" - integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -uglify-js@^3.1.4: - version "3.13.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d" - integrity sha512-57H3ACYFXeo1IaZ1w02sfA71wI60MGco/IQFjOqK+WtKoprh7Go2/yvd2HPtoJILO2Or84ncLccI4xoHMTSbGg== - -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== - dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" - which-boxed-primitive "^1.0.2" - -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= - dependencies: - crypto-random-string "^1.0.0" - -universalify@^0.1.0, universalify@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.1.1, upath@^1.1.2, upath@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-loader@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" - integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== - dependencies: - loader-utils "^2.0.0" - mime-types "^2.1.27" - schema-utils "^3.0.0" - -url-parse@^1.4.3, url-parse@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b" - integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util.promisify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -util.promisify@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -utila@~0.4: - version "0.4.0" - resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.3.2, uuid@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -v8-to-istanbul@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" - integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - source-map "^0.7.3" - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -vendors@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== - -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" - -walker@^1.0.7, walker@~1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" - integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= - dependencies: - makeerror "1.0.x" - -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -webpack-dev-middleware@^3.7.2: - version "3.7.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" - integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== - dependencies: - memory-fs "^0.4.1" - mime "^2.4.4" - mkdirp "^0.5.1" - range-parser "^1.2.1" - webpack-log "^2.0.0" - -webpack-dev-server@3.11.1: - version "3.11.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz#c74028bf5ba8885aaf230e48a20e8936ab8511f0" - integrity sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ== - dependencies: - ansi-html "0.0.7" - bonjour "^3.5.0" - chokidar "^2.1.8" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - debug "^4.1.1" - del "^4.1.1" - express "^4.17.1" - html-entities "^1.3.1" - http-proxy-middleware "0.19.1" - 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.6.8" - opn "^5.5.0" - p-retry "^3.0.1" - portfinder "^1.0.26" - schema-utils "^1.0.0" - selfsigned "^1.10.8" - semver "^6.3.0" - serve-index "^1.9.1" - sockjs "^0.3.21" - sockjs-client "^1.5.0" - spdy "^4.0.2" - strip-ansi "^3.0.1" - supports-color "^6.1.0" - url "^0.11.0" - webpack-dev-middleware "^3.7.2" - webpack-log "^2.0.0" - ws "^6.2.1" - yargs "^13.3.2" - -webpack-log@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" - integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== - dependencies: - ansi-colors "^3.0.0" - uuid "^3.3.2" - -webpack-manifest-plugin@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz#19ca69b435b0baec7e29fbe90fb4015de2de4f16" - integrity sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ== - dependencies: - fs-extra "^7.0.0" - lodash ">=3.5 <5" - object.entries "^1.1.0" - tapable "^1.0.0" - -webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@4.44.2: - version "4.44.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" - integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.3.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" - -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - -whatwg-fetch@^3.4.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" - integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== - -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.9, which@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - -workbox-background-sync@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-5.1.4.tgz#5ae0bbd455f4e9c319e8d827c055bb86c894fd12" - integrity sha512-AH6x5pYq4vwQvfRDWH+vfOePfPIYQ00nCEB7dJRU1e0n9+9HMRyvI63FlDvtFT2AvXVRsXvUt7DNMEToyJLpSA== - dependencies: - workbox-core "^5.1.4" - -workbox-broadcast-update@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-5.1.4.tgz#0eeb89170ddca7f6914fa3523fb14462891f2cfc" - integrity sha512-HTyTWkqXvHRuqY73XrwvXPud/FN6x3ROzkfFPsRjtw/kGZuZkPzfeH531qdUGfhtwjmtO/ZzXcWErqVzJNdXaA== - dependencies: - workbox-core "^5.1.4" - -workbox-build@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-5.1.4.tgz#23d17ed5c32060c363030c8823b39d0eabf4c8c7" - integrity sha512-xUcZn6SYU8usjOlfLb9Y2/f86Gdo+fy1fXgH8tJHjxgpo53VVsqRX0lUDw8/JuyzNmXuo8vXX14pXX2oIm9Bow== - dependencies: - "@babel/core" "^7.8.4" - "@babel/preset-env" "^7.8.4" - "@babel/runtime" "^7.8.4" - "@hapi/joi" "^15.1.0" - "@rollup/plugin-node-resolve" "^7.1.1" - "@rollup/plugin-replace" "^2.3.1" - "@surma/rollup-plugin-off-main-thread" "^1.1.1" - common-tags "^1.8.0" - fast-json-stable-stringify "^2.1.0" - fs-extra "^8.1.0" - glob "^7.1.6" - lodash.template "^4.5.0" - pretty-bytes "^5.3.0" - rollup "^1.31.1" - rollup-plugin-babel "^4.3.3" - rollup-plugin-terser "^5.3.1" - source-map "^0.7.3" - source-map-url "^0.4.0" - 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" - -workbox-cacheable-response@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-5.1.4.tgz#9ff26e1366214bdd05cf5a43da9305b274078a54" - integrity sha512-0bfvMZs0Of1S5cdswfQK0BXt6ulU5kVD4lwer2CeI+03czHprXR3V4Y8lPTooamn7eHP8Iywi5QjyAMjw0qauA== - dependencies: - workbox-core "^5.1.4" - -workbox-core@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-5.1.4.tgz#8bbfb2362ecdff30e25d123c82c79ac65d9264f4" - integrity sha512-+4iRQan/1D8I81nR2L5vcbaaFskZC2CL17TLbvWVzQ4qiF/ytOGF6XeV54pVxAvKUtkLANhk8TyIUMtiMw2oDg== - -workbox-expiration@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-5.1.4.tgz#92b5df461e8126114943a3b15c55e4ecb920b163" - integrity sha512-oDO/5iC65h2Eq7jctAv858W2+CeRW5e0jZBMNRXpzp0ZPvuT6GblUiHnAsC5W5lANs1QS9atVOm4ifrBiYY7AQ== - dependencies: - workbox-core "^5.1.4" - -workbox-google-analytics@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-5.1.4.tgz#b3376806b1ac7d7df8418304d379707195fa8517" - 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" - -workbox-navigation-preload@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-5.1.4.tgz#30d1b720d26a05efc5fa11503e5cc1ed5a78902a" - integrity sha512-Wf03osvK0wTflAfKXba//QmWC5BIaIZARU03JIhAEO2wSB2BDROWI8Q/zmianf54kdV7e1eLaIEZhth4K4MyfQ== - dependencies: - workbox-core "^5.1.4" - -workbox-precaching@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-5.1.4.tgz#874f7ebdd750dd3e04249efae9a1b3f48285fe6b" - integrity sha512-gCIFrBXmVQLFwvAzuGLCmkUYGVhBb7D1k/IL7pUJUO5xacjLcFUaLnnsoVepBGAiKw34HU1y/YuqvTKim9qAZA== - dependencies: - workbox-core "^5.1.4" - -workbox-range-requests@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-5.1.4.tgz#7066a12c121df65bf76fdf2b0868016aa2bab859" - integrity sha512-1HSujLjgTeoxHrMR2muDW2dKdxqCGMc1KbeyGcmjZZAizJTFwu7CWLDmLv6O1ceWYrhfuLFJO+umYMddk2XMhw== - dependencies: - workbox-core "^5.1.4" - -workbox-routing@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-5.1.4.tgz#3e8cd86bd3b6573488d1a2ce7385e547b547e970" - integrity sha512-8ljknRfqE1vEQtnMtzfksL+UXO822jJlHTIR7+BtJuxQ17+WPZfsHqvk1ynR/v0EHik4x2+826Hkwpgh4GKDCw== - dependencies: - workbox-core "^5.1.4" - -workbox-strategies@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-5.1.4.tgz#96b1418ccdfde5354612914964074d466c52d08c" - integrity sha512-VVS57LpaJTdjW3RgZvPwX0NlhNmscR7OQ9bP+N/34cYMDzXLyA6kqWffP6QKXSkca1OFo/v6v7hW7zrrguo6EA== - dependencies: - workbox-core "^5.1.4" - workbox-routing "^5.1.4" - -workbox-streams@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-5.1.4.tgz#05754e5e3667bdc078df2c9315b3f41210d8cac0" - integrity sha512-xU8yuF1hI/XcVhJUAfbQLa1guQUhdLMPQJkdT0kn6HP5CwiPOGiXnSFq80rAG4b1kJUChQQIGPrq439FQUNVrw== - dependencies: - workbox-core "^5.1.4" - workbox-routing "^5.1.4" - -workbox-sw@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-5.1.4.tgz#2bb34c9f7381f90d84cef644816d45150011d3db" - integrity sha512-9xKnKw95aXwSNc8kk8gki4HU0g0W6KXu+xks7wFuC7h0sembFnTrKtckqZxbSod41TDaGh+gWUA5IRXrL0ECRA== - -workbox-webpack-plugin@5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-5.1.4.tgz#7bfe8c16e40fe9ed8937080ac7ae9c8bde01e79c" - integrity sha512-PZafF4HpugZndqISi3rZ4ZK4A4DxO8rAqt2FwRptgsDx7NF8TVKP86/huHquUsRjMGQllsNdn4FNl8CD/UvKmQ== - dependencies: - "@babel/runtime" "^7.5.5" - fast-json-stable-stringify "^2.0.0" - source-map-url "^0.4.0" - upath "^1.1.2" - webpack-sources "^1.3.0" - workbox-build "^5.1.4" - -workbox-window@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-5.1.4.tgz#2740f7dea7f93b99326179a62f1cc0ca2c93c863" - integrity sha512-vXQtgTeMCUq/4pBWMfQX8Ee7N2wVC4Q7XYFqLnfbXJ2hqew/cU1uMTD2KqGEgEpE4/30luxIxgE+LkIa8glBYw== - dependencies: - workbox-core "^5.1.4" - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - -worker-rpc@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" - integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== - dependencies: - microevent.ts "~0.1.1" - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -ws@^6.2.1: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - -ws@^7.4.5: - version "7.5.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.2.tgz#09cc8fea3bec1bc5ed44ef51b42f945be36900f6" - integrity sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ== - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^1.10.0, yaml@^1.7.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yargs-parser@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@^13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" - integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.2" - -yargs@^15.4.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==