From 1d364daae0de81a10af4c477129094876b3757ed Mon Sep 17 00:00:00 2001 From: WilcoApp Date: Thu, 2 Jan 2025 09:37:27 +0000 Subject: [PATCH] Initial commit --- .devcontainer/setup.sh | 24 + .eslintrc.json | 3 - .github/pull_request_template.md | 3 + .github/workflows/k8s.yml | 157 + .github/workflows/wilco-actions.yml | 24 - .gitignore | 35 +- .vscode/settings.json | 3 + README.md | 17 - frontend/.eslintignore | 2 + frontend/.gitignore | 16 + frontend/Dockerfile.aws | 9 + frontend/jest.config.js | 8 + frontend/package.json | 74 + frontend/public/50precentoff.png | Bin 0 -> 58158 bytes frontend/public/favicon.ico | Bin 0 -> 15086 bytes frontend/public/index.html | 47 + frontend/public/placeholder.png | Bin 0 -> 6146 bytes frontend/public/style.css | 54 + frontend/public/sunray.jpeg | Bin 0 -> 46419 bytes frontend/public/verified_seller.svg | 18 + frontend/readme.md | 26 + frontend/src/agent.js | 98 + frontend/src/components/App.js | 81 + frontend/src/components/Editor.js | 176 + frontend/src/components/Header.js | 73 + frontend/src/components/Home/Banner.js | 19 + frontend/src/components/Home/MainView.js | 100 + frontend/src/components/Home/Tags.js | 40 + frontend/src/components/Home/index.js | 54 + frontend/src/components/Item/Comment.js | 42 + .../src/components/Item/CommentContainer.js | 46 + frontend/src/components/Item/CommentInput.js | 59 + frontend/src/components/Item/CommentList.js | 21 + frontend/src/components/Item/DeleteButton.js | 27 + frontend/src/components/Item/ItemActions.js | 36 + frontend/src/components/Item/ItemMeta.js | 30 + frontend/src/components/Item/index.js | 85 + .../src/components/Item/utils/ItemFetcher.js | 8 + frontend/src/components/ItemList.js | 35 + frontend/src/components/ItemPreview.js | 66 + frontend/src/components/ListErrors.js | 24 + frontend/src/components/ListPagination.js | 52 + frontend/src/components/Login.js | 121 + frontend/src/components/Profile.js | 172 + frontend/src/components/ProfileFavorites.js | 56 + frontend/src/components/Register.js | 148 + frontend/src/components/Settings.js | 142 + frontend/src/components/commons.js | 5 + frontend/src/constants/actionTypes.js | 37 + frontend/src/custom.scss | 61 + frontend/src/imgs/background.png | Bin 0 -> 1250693 bytes frontend/src/imgs/logo.png | Bin 0 -> 149049 bytes frontend/src/imgs/topbar_logo.png | Bin 0 -> 2321 bytes frontend/src/index.js | 18 + frontend/src/middleware.js | 65 + frontend/src/reducer.js | 20 + frontend/src/reducers/auth.js | 36 + frontend/src/reducers/common.js | 79 + frontend/src/reducers/editor.js | 56 + frontend/src/reducers/home.js | 17 + frontend/src/reducers/item.js | 38 + frontend/src/reducers/itemList.js | 88 + frontend/src/reducers/profile.js | 26 + frontend/src/reducers/settings.js | 27 + frontend/src/setupTests.js | 5 + frontend/src/store.js | 25 + frontend/src/tests/components/Header.test.js | 54 + .../__snapshots__/Header.test.js.snap | 119 + frontend/src/tests/item/CommentInput.test.js | 64 + .../__snapshots__/CommentInput.test.js.snap | 35 + frontend/start.sh | 3 + frontend/yarn.lock | 10689 ++++++++++++++++ next.config.mjs | 4 - package-lock.json | 5117 -------- package.json | 29 - postcss.config.js | 6 - public/next.svg | 1 - public/vercel.svg | 1 - readme.md | 17 + src/app/api/translate/route.ts | 36 - src/app/favicon.ico | Bin 25931 -> 0 bytes src/app/globals.css | 20 - src/app/layout.tsx | 22 - src/app/page.tsx | 9 - src/components/Translator.tsx | 179 - src/data/country-codes.json | 245 - src/data/language-codes.json | 190 - tailwind.config.ts | 15 - tests/e2e/.eslintrc.js | 34 + tests/e2e/anytinkClient.js | 219 + tests/e2e/concurrent/health.test.js | 15 + tests/e2e/concurrent/items.test.js | 561 + tests/e2e/concurrent/profiles.test.js | 64 + tests/e2e/concurrent/tags.test.js | 36 + tests/e2e/concurrent/users.test.js | 152 + tests/e2e/jest.concurrent.config.js | 6 + tests/e2e/jest.sequential.config.js | 5 + tests/e2e/package.json | 35 + tests/e2e/sequential/events.test.js | 32 + tests/e2e/setup.js | 7 + tests/e2e/teardown.js | 5 + tests/e2e/utils.js | 58 + tests/e2e/wilcoEngine/mockWilcoEngine.js | 27 + tests/e2e/wilcoEngine/utils.js | 34 + tests/e2e/wilcoEngine/wilcoEngineEvents.js | 13 + tests/e2e/yarn.lock | 3809 ++++++ tests/frontend/global-setup.js | 5 + tests/frontend/package.json | 17 + tests/frontend/playwright.config.js | 67 + tests/frontend/requestValidator.js | 68 + tests/frontend/tests/editor.spec.js | 81 + tests/frontend/tests/signup.spec.js | 99 + tests/frontend/yarn.lock | 695 + tests/readme.md | 8 + tests/utils.js | 7 + tsconfig.json | 26 - 116 files changed, 20013 insertions(+), 5961 deletions(-) create mode 100755 .devcontainer/setup.sh delete mode 100644 .eslintrc.json create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/k8s.yml delete mode 100644 .github/workflows/wilco-actions.yml create mode 100644 .vscode/settings.json delete mode 100644 README.md create mode 100644 frontend/.eslintignore create mode 100644 frontend/.gitignore create mode 100644 frontend/Dockerfile.aws create mode 100644 frontend/jest.config.js create mode 100644 frontend/package.json create mode 100644 frontend/public/50precentoff.png create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/public/index.html create mode 100644 frontend/public/placeholder.png create mode 100644 frontend/public/style.css create mode 100644 frontend/public/sunray.jpeg create mode 100644 frontend/public/verified_seller.svg create mode 100644 frontend/readme.md create mode 100644 frontend/src/agent.js create mode 100644 frontend/src/components/App.js create mode 100644 frontend/src/components/Editor.js create mode 100644 frontend/src/components/Header.js create mode 100644 frontend/src/components/Home/Banner.js create mode 100644 frontend/src/components/Home/MainView.js create mode 100644 frontend/src/components/Home/Tags.js create mode 100644 frontend/src/components/Home/index.js create mode 100644 frontend/src/components/Item/Comment.js create mode 100644 frontend/src/components/Item/CommentContainer.js create mode 100644 frontend/src/components/Item/CommentInput.js create mode 100644 frontend/src/components/Item/CommentList.js create mode 100644 frontend/src/components/Item/DeleteButton.js create mode 100644 frontend/src/components/Item/ItemActions.js create mode 100644 frontend/src/components/Item/ItemMeta.js create mode 100644 frontend/src/components/Item/index.js create mode 100644 frontend/src/components/Item/utils/ItemFetcher.js create mode 100644 frontend/src/components/ItemList.js create mode 100644 frontend/src/components/ItemPreview.js create mode 100644 frontend/src/components/ListErrors.js create mode 100644 frontend/src/components/ListPagination.js create mode 100644 frontend/src/components/Login.js create mode 100644 frontend/src/components/Profile.js create mode 100644 frontend/src/components/ProfileFavorites.js create mode 100644 frontend/src/components/Register.js create mode 100644 frontend/src/components/Settings.js create mode 100644 frontend/src/components/commons.js create mode 100644 frontend/src/constants/actionTypes.js create mode 100644 frontend/src/custom.scss create mode 100644 frontend/src/imgs/background.png create mode 100644 frontend/src/imgs/logo.png create mode 100644 frontend/src/imgs/topbar_logo.png create mode 100644 frontend/src/index.js create mode 100644 frontend/src/middleware.js create mode 100644 frontend/src/reducer.js create mode 100644 frontend/src/reducers/auth.js create mode 100644 frontend/src/reducers/common.js create mode 100644 frontend/src/reducers/editor.js create mode 100644 frontend/src/reducers/home.js create mode 100644 frontend/src/reducers/item.js create mode 100644 frontend/src/reducers/itemList.js create mode 100644 frontend/src/reducers/profile.js create mode 100644 frontend/src/reducers/settings.js create mode 100644 frontend/src/setupTests.js create mode 100644 frontend/src/store.js create mode 100644 frontend/src/tests/components/Header.test.js create mode 100644 frontend/src/tests/components/__snapshots__/Header.test.js.snap create mode 100644 frontend/src/tests/item/CommentInput.test.js create mode 100644 frontend/src/tests/item/__snapshots__/CommentInput.test.js.snap create mode 100755 frontend/start.sh create mode 100644 frontend/yarn.lock delete mode 100644 next.config.mjs delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 postcss.config.js delete mode 100644 public/next.svg delete mode 100644 public/vercel.svg create mode 100644 readme.md delete mode 100644 src/app/api/translate/route.ts delete mode 100644 src/app/favicon.ico delete mode 100644 src/app/globals.css delete mode 100644 src/app/layout.tsx delete mode 100644 src/app/page.tsx delete mode 100644 src/components/Translator.tsx delete mode 100644 src/data/country-codes.json delete mode 100644 src/data/language-codes.json delete mode 100644 tailwind.config.ts create mode 100644 tests/e2e/.eslintrc.js create mode 100644 tests/e2e/anytinkClient.js create mode 100644 tests/e2e/concurrent/health.test.js create mode 100644 tests/e2e/concurrent/items.test.js create mode 100644 tests/e2e/concurrent/profiles.test.js create mode 100644 tests/e2e/concurrent/tags.test.js create mode 100644 tests/e2e/concurrent/users.test.js create mode 100644 tests/e2e/jest.concurrent.config.js create mode 100644 tests/e2e/jest.sequential.config.js create mode 100644 tests/e2e/package.json create mode 100644 tests/e2e/sequential/events.test.js create mode 100644 tests/e2e/setup.js create mode 100644 tests/e2e/teardown.js create mode 100644 tests/e2e/utils.js create mode 100644 tests/e2e/wilcoEngine/mockWilcoEngine.js create mode 100644 tests/e2e/wilcoEngine/utils.js create mode 100644 tests/e2e/wilcoEngine/wilcoEngineEvents.js create mode 100644 tests/e2e/yarn.lock create mode 100644 tests/frontend/global-setup.js create mode 100644 tests/frontend/package.json create mode 100644 tests/frontend/playwright.config.js create mode 100644 tests/frontend/requestValidator.js create mode 100644 tests/frontend/tests/editor.spec.js create mode 100644 tests/frontend/tests/signup.spec.js create mode 100644 tests/frontend/yarn.lock create mode 100644 tests/readme.md create mode 100644 tests/utils.js delete mode 100644 tsconfig.json diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 000000000..f7bc939f8 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,24 @@ +WILCO_ID="`cat .wilco`" +CODESPACE_BACKEND_HOST=$(curl -s "${ENGINE_BASE_URL}/api/v1/codespace/backendHost?codespaceName=${CODESPACE_NAME}&portForwarding=${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}" | jq -r '.codespaceBackendHost') +CODESPACE_BACKEND_URL="https://${CODESPACE_BACKEND_HOST}" +export ENGINE_EVENT_ENDPOINT="${ENGINE_BASE_URL}/users/${WILCO_ID}/event" + +# Update engine that codespace started for user +curl -L -X POST "${ENGINE_EVENT_ENDPOINT}" -H "Content-Type: application/json" --data-raw "{ \"event\": \"github_codespace_started\" }" + +# Export backend envs when in codespaces +echo "export CODESPACE_BACKEND_HOST=\"${CODESPACE_BACKEND_HOST}\"" >> ~/.bashrc +echo "export CODESPACE_BACKEND_URL=\"${CODESPACE_BACKEND_URL}\"" >> ~/.bashrc +echo "export CODESPACE_WDS_SOCKET_PORT=443" >> ~/.bashrc + +# Export welcome prompt in bash: +echo "printf \"\n\n☁️☁️☁️️ Anythink: Develop in the Cloud ☁️☁️☁️\n\"" >> ~/.bashrc +echo "printf \"\n\x1b[31m \x1b[1m👉 Type: \\\`docker compose up\\\` to run the project. 👈\n\n\"" >> ~/.bashrc + +nohup bash -c "cd /wilco-agent && node agent.js &" >> /tmp/agent.log 2>&1 + +# Check if docker is installed +if command -v docker &> /dev/null +then + docker compose pull +fi diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bffb357a7..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..8ebf54968 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,3 @@ +# Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. diff --git a/.github/workflows/k8s.yml b/.github/workflows/k8s.yml new file mode 100644 index 000000000..9a56d6aa2 --- /dev/null +++ b/.github/workflows/k8s.yml @@ -0,0 +1,157 @@ +name: Build and deploy to Kubernetes +on: + push: + branches: + - main + +concurrency: + group: k8s + cancel-in-progress: true + +jobs: + check-kubernetes-enabled: + runs-on: ubuntu-20.04 + outputs: + kubernetes-enabled: ${{ steps.kubernetes-flag-defined.outputs.DEFINED }} + steps: + - id: kubernetes-flag-defined + if: "${{ env.ENABLE_KUBERNETES != '' }}" + run: echo "DEFINED=true" >> $GITHUB_OUTPUT + env: + ENABLE_KUBERNETES: ${{ secrets.ENABLE_KUBERNETES }} + + check-secret: + runs-on: ubuntu-20.04 + needs: [check-kubernetes-enabled] + outputs: + aws-creds-defined: ${{ steps.aws-creds-defined.outputs.DEFINED }} + kubeconfig-defined: ${{ steps.kubeconfig-defined.outputs.DEFINED }} + if: needs.check-kubernetes-enabled.outputs.kubernetes-enabled == 'true' + steps: + - id: aws-creds-defined + if: "${{ env.AWS_ACCESS_KEY_ID != '' && env.AWS_SECRET_ACCESS_KEY != '' }}" + run: echo "DEFINED=true" >> $GITHUB_OUTPUT + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + - id: kubeconfig-defined + if: "${{ env.KUBECONFIG != '' }}" + run: echo "DEFINED=true" >> $GITHUB_OUTPUT + env: + KUBECONFIG: ${{ secrets.KUBECONFIG }} + + build-backend: + name: Build backend image + runs-on: ubuntu-20.04 + needs: [check-secret] + if: needs.check-secret.outputs.aws-creds-defined == 'true' + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-2 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Set the image tag + run: echo IMAGE_TAG=${GITHUB_REPOSITORY/\//-}-latest >> $GITHUB_ENV + + - name: Set repository name + run: | + if [ ${{ secrets.CLUSTER_ENV }} == 'staging' ]; then + echo "REPO_NAME=staging-anythink-backend" >> $GITHUB_ENV + else + echo "REPO_NAME=anythink-backend" >> $GITHUB_ENV + fi + + - name: Build, tag, and push backend image to Amazon ECR + id: build-image-backend + run: | + docker build \ + -t ${{ steps.login-ecr.outputs.registry }}/${{ env.REPO_NAME }}:${{ env.IMAGE_TAG }} \ + -f backend/Dockerfile.aws \ + . + docker push ${{ steps.login-ecr.outputs.registry }}/${{ env.REPO_NAME }}:${{ env.IMAGE_TAG }} + + build-frontend: + name: Build frontend images + runs-on: ubuntu-20.04 + needs: [check-secret] + if: needs.check-secret.outputs.aws-creds-defined == 'true' + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-2 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Set the image tag + run: echo IMAGE_TAG=${GITHUB_REPOSITORY/\//-}-latest >> $GITHUB_ENV + + - name: Set repository name + run: | + if [ ${{ secrets.CLUSTER_ENV }} == 'staging' ]; then + echo "REPO_NAME=staging-anythink-frontend" >> $GITHUB_ENV + else + echo "REPO_NAME=anythink-frontend" >> $GITHUB_ENV + fi + + - name: Build, tag, and push frontend image to Amazon ECR + id: build-image-frontend + run: | + docker build \ + -t ${{ steps.login-ecr.outputs.registry }}/${{ env.REPO_NAME }}:${{ env.IMAGE_TAG }} \ + -f frontend/Dockerfile.aws \ + . + docker push ${{ steps.login-ecr.outputs.registry }}/${{ env.REPO_NAME }}:${{ env.IMAGE_TAG }} + + deploy: + name: Deploy latest tag using helm + runs-on: ubuntu-20.04 + if: needs.check-secret.outputs.kubeconfig-defined == 'true' + needs: + - build-frontend + - build-backend + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Create kube config + run: | + mkdir -p $HOME/.kube/ + echo "${{ secrets.KUBECONFIG }}" > $HOME/.kube/config + chmod 600 $HOME/.kube/config + - name: Install helm + run: | + curl -LO https://get.helm.sh/helm-v3.8.0-linux-amd64.tar.gz + tar -zxvf helm-v3.8.0-linux-amd64.tar.gz + mv linux-amd64/helm /usr/local/bin/helm + helm version + - name: Lint helm charts + run: helm lint ./charts/ + + - name: Set the image tag + run: echo IMAGE_TAG=${GITHUB_REPOSITORY/\//-}-latest >> $GITHUB_ENV + + - name: Deploy + run: | + helm upgrade --install --timeout 10m anythink-market ./charts/ \ + --set clusterEnv=${{ secrets.CLUSTER_ENV }} \ + --set frontend.image.tag=${{ env.IMAGE_TAG }} \ + --set backend.image.tag=${{ env.IMAGE_TAG }} diff --git a/.github/workflows/wilco-actions.yml b/.github/workflows/wilco-actions.yml deleted file mode 100644 index b732c8681..000000000 --- a/.github/workflows/wilco-actions.yml +++ /dev/null @@ -1,24 +0,0 @@ -on: - pull_request: - branches: - - main - -jobs: - wilco: - runs-on: ubuntu-20.04 - timeout-minutes: 10 - name: Pr checks - - steps: - - name: Check out project - uses: actions/checkout@v2 - - - uses: oNaiPs/secrets-to-env-action@v1 - with: - secrets: ${{ toJSON(secrets) }} - - - name: Wilco checks - id: Wilco - uses: trywilco/actions@main - with: - engine: ${{ secrets.WILCO_ENGINE_URL }} diff --git a/.gitignore b/.gitignore index fd3dbb571..10d146d23 100644 --- a/.gitignore +++ b/.gitignore @@ -2,35 +2,36 @@ # dependencies /node_modules +/backend/node_modules +/frontend/node_modules +/.wilco-helpers/node_modules +/tests/e2e/node_modules +/tests/frontend/node_modules/ +/tests/frontend/test-results/ +/tests/frontend/playwright-report/ +/tests/frontend/playwright/.cache/ + /.pnp .pnp.js -.yarn/install-state.gz # testing /coverage -# next.js -/.next/ -/out/ - # production -/build +/backend/build +/frontend/build # misc .DS_Store -*.pem +.env +.env.local +.env.development.local +.env.test.local +.env.production.local -# debug npm-debug.log* yarn-debug.log* yarn-error.log* -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts +#IDEs +/.idea/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0c878f127 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "workbench.startupEditor": "none" +} diff --git a/README.md b/README.md deleted file mode 100644 index 4ae4281ae..000000000 --- a/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# AI Voice Translator in Next.js - -Demo for tutorial [How to Build an AI Voice Translator in Next.js with Web Speech API & OpenAI](https://www.youtube.com/watch?v=JFfCDvKiJqU) - -📝 Article: https://spacejelly.dev/posts/how-to-build-an-ai-voice-translator-in-next-js-with-web-speech-api-openai - -📺 YouTube: https://www.youtube.com/watch?v=JFfCDvKiJqU - -🚀 Demo: https://my-universal-translator.vercel.app/ - -## More tutorials and walkthroughs - -🐦 [Follow me on Twitter](https://twitter.com/colbyfayock) - -📺 [Subscribe on YouTube](https://www.youtube.com/colbyfayock) - -✉️ [Sign Up for My Newsletter](https://colbyfayock.com/newsletter) diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 000000000..dd87e2d73 --- /dev/null +++ b/frontend/.eslintignore @@ -0,0 +1,2 @@ +node_modules +build diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 000000000..edfc11914 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,16 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +node_modules + +# testing +coverage + +# production +build + +# misc +.DS_Store +.env +npm-debug.log +.idea \ No newline at end of file diff --git a/frontend/Dockerfile.aws b/frontend/Dockerfile.aws new file mode 100644 index 000000000..9485ac1bc --- /dev/null +++ b/frontend/Dockerfile.aws @@ -0,0 +1,9 @@ +FROM node:16 +WORKDIR /usr/src + +COPY frontend ./frontend +COPY .wilco ./.wilco + +# Pre-install npm packages +WORKDIR /usr/src/frontend +RUN yarn install diff --git a/frontend/jest.config.js b/frontend/jest.config.js new file mode 100644 index 000000000..582fe825c --- /dev/null +++ b/frontend/jest.config.js @@ -0,0 +1,8 @@ +const config = { + verbose: true, + jest: { + setupFilesAfterEnv: ["src/setupTests.js"], + }, +}; + +module.exports = config; diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 000000000..45a643d60 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,74 @@ +{ + "name": "anythink-market-front", + "version": "0.1.0", + "engines": { + "node": "^16" + }, + "private": true, + "devDependencies": { + "@wojtekmaj/enzyme-adapter-react-17": "^0.6.7", + "core-js": "^3.25.1", + "enzyme": "^3.11.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-react": "^7.26.1", + "prettier": "2.4.1", + "react-test-renderer": "^17.0.2", + "redux-mock-store": "^1.5.4" + }, + "dependencies": { + "@babel/core": "^7.18.13", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-syntax-flow": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.10", + "bootstrap": "^4.6.0", + "bootstrap-icons": "^1.7.1", + "history": "^4.6.3", + "jquery": "^3.6.1", + "marked": "^0.3.6", + "postcss": "^8.4.16", + "prop-types": "^15.5.10", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-redux": "^5.0.7", + "react-router-dom": "^6.9.0", + "react-scripts": "^5.0.1", + "redux": "^3.6.0", + "redux-devtools-extension": "^2.13.2", + "sass": "^1.45.0", + "superagent": "^3.8.2", + "superagent-promise": "^1.1.0", + "typescript": "^4.8.2" + }, + "scripts": { + "start": "REACT_APP_WILCO_ID=${WILCO_ID:-\"$(cat ../.wilco)\"} react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject", + "format": "yarn prettier --write .", + "lint": "yarn eslint . && yarn prettier --check ." + }, + "eslintConfig": { + "extends": [ + "react-app", + "eslint:recommended" + ], + "rules": { + "no-var": "error" + } + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "resolutions": { + "autoprefixer": "10.4.5" + } +} diff --git a/frontend/public/50precentoff.png b/frontend/public/50precentoff.png new file mode 100644 index 0000000000000000000000000000000000000000..4c835a18da3ab0c1cce11600d24a40339221c040 GIT binary patch literal 58158 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