feat: Add items website code base + drone pipeline (#9)
Hello, I'm adding the whole https://items.sp-tarkov.com code base as well as the drone pipeline to deploy it. Please **double check *thoroughly* every** to ensure it matches the server architecture and configuration.
This commit is contained in:
parent
6e95f4b7a0
commit
1153ac24bf
2
.ansible-items/inventory
Normal file
2
.ansible-items/inventory
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[host]
|
||||||
|
{{ DEPLOYMENT_USER }}@{{ DEPLOY_HOSTNAME }} ansible_connection=ssh ansible_user={{ DEPLOYMENT_USER }}
|
49
.ansible-items/playbook.yml
Normal file
49
.ansible-items/playbook.yml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
- name: install and configure PHP8 and composer
|
||||||
|
hosts: host
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Delete spt-items-api before adding everything again
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
- name: Copy the project
|
||||||
|
copy:
|
||||||
|
src: ../items/api/
|
||||||
|
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
- name: Copy PHP .env file
|
||||||
|
template:
|
||||||
|
src: ./templates/.php-env.j2
|
||||||
|
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/.env"
|
||||||
|
- name: Get JS chunks name
|
||||||
|
shell:
|
||||||
|
cmd: find "{{ lookup('env', 'SPT_ITEMS_PATH') }}" -type f -name "*chunk.js" -printf "%f\n"
|
||||||
|
register: find_output
|
||||||
|
- name: Get file names from find output
|
||||||
|
set_fact:
|
||||||
|
chunk_list: "{{ find_output['stdout'].split('\n') }}"
|
||||||
|
- name: Copy app.blade.php file
|
||||||
|
template:
|
||||||
|
src: ./templates/app.blade.php.j2
|
||||||
|
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/resources/views/app.blade.php"
|
||||||
|
|
||||||
|
- name: Download and installs all composer libs and dependencies
|
||||||
|
community.general.composer:
|
||||||
|
command: install
|
||||||
|
working_dir: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
|
||||||
|
# Ensure the permissions
|
||||||
|
- name: Reset files permissions
|
||||||
|
file:
|
||||||
|
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
owner: "{{ lookup('env', 'DEPLOYMENT_USER') }}"
|
||||||
|
group: "{{ lookup('env', 'DEPLOYMENT_USER_GROUP') }}"
|
||||||
|
mode: 0774
|
||||||
|
recurse: yes
|
||||||
|
|
||||||
|
- name: Initialize database
|
||||||
|
uri:
|
||||||
|
url: "https://{{ lookup('env', 'SPT_ITEMS_HOSTNAME') }}/api/refresh"
|
||||||
|
method: GET
|
||||||
|
status_code: [200, 204]
|
||||||
|
timeout: 60
|
2
.ansible-items/requirements.yml
Normal file
2
.ansible-items/requirements.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
collections:
|
||||||
|
- name: community.general # For composer
|
19
.ansible-items/templates/.php-env.j2
Normal file
19
.ansible-items/templates/.php-env.j2
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
APP_NAME="Item Finder"
|
||||||
|
APP_ENV=production
|
||||||
|
APP_KEY=
|
||||||
|
APP_DEBUG=false
|
||||||
|
APP_URL="https://{{ lookup('env', 'SPT_ITEMS_HOSTNAME') }}"
|
||||||
|
APP_TIMEZONE=UTC
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_SLACK_WEBHOOK_URL=
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=homestead
|
||||||
|
DB_USERNAME=homestead
|
||||||
|
DB_PASSWORD=secret
|
||||||
|
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
QUEUE_CONNECTION=sync
|
23
.ansible-items/templates/app.blade.php.j2
Normal file
23
.ansible-items/templates/app.blade.php.j2
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="/favico.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="items finder" />
|
||||||
|
<link rel="apple-touch-icon" href="/favico.ico" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<title>Item Finder</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body><noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script>!function (e) { function t(t) { for (var n, u, a = t[0], c = t[1], f = t[2], s = 0, p = []; s < a.length; s++)u = a[s], Object.prototype.hasOwnProperty.call(o, u) && o[u] && p.push(o[u][0]), o[u] = 0; for (n in c) Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]); for (l && l(t); p.length;)p.shift()(); return i.push.apply(i, f || []), r() } function r() { for (var e, t = 0; t < i.length; t++) { for (var r = i[t], n = !0, a = 1; a < r.length; a++) { var c = r[a]; 0 !== o[c] && (n = !1) } n && (i.splice(t--, 1), e = u(u.s = r[0])) } return e } var n = {}, o = { 1: 0 }, i = []; function u(t) { if (n[t]) return n[t].exports; var r = n[t] = { i: t, l: !1, exports: {} }; return e[t].call(r.exports, r, r.exports, u), r.l = !0, r.exports } u.e = function (e) { var t = [], r = o[e]; if (0 !== r) if (r) t.push(r[2]); else { var n = new Promise((function (t, n) { r = o[e] = [t, n] })); t.push(r[2] = n); var i, a = document.createElement("script"); a.charset = "utf-8", a.timeout = 120, u.nc && a.setAttribute("nonce", u.nc), a.src = function (e) { return u.p + "static/js/" + ({}[e] || e) + "." + { 3: "ff1076df" }[e] + ".chunk.js" }(e); var c = new Error; i = function (t) { a.onerror = a.onload = null, clearTimeout(f); var r = o[e]; if (0 !== r) { if (r) { var n = t && ("load" === t.type ? "missing" : t.type), i = t && t.target && t.target.src; c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + i + ")", c.name = "ChunkLoadError", c.type = n, c.request = i, r[1](c) } o[e] = void 0 } }; var f = setTimeout((function () { i({ type: "timeout", target: a }) }), 12e4); a.onerror = a.onload = i, document.head.appendChild(a) } return Promise.all(t) }, u.m = e, u.c = n, u.d = function (e, t, r) { u.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, u.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, u.t = function (e, t) { if (1 & t && (e = u(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var r = Object.create(null); if (u.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var n in e) u.d(r, n, function (t) { return e[t] }.bind(null, n)); return r }, u.n = function (e) { var t = e && e.__esModule ? function () { return e.default } : function () { return e }; return u.d(t, "a", t), t }, u.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, u.p = "/", u.oe = function (e) { throw console.error(e), e }; var a = this["webpackJsonpnew-item-finder-website"] = this["webpackJsonpnew-item-finder-website"] || [], c = a.push.bind(a); a.push = t, a = a.slice(); for (var f = 0; f < a.length; f++)t(a[f]); var l = c; r() }([])</script>
|
||||||
|
{% for chunk in chunk_list %}
|
||||||
|
<script src="/static/js/{{ chunk }}"></script>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
73
.drone-docker.yml
Normal file
73
.drone-docker.yml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: deocker
|
||||||
|
name: default
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
limit: 1
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: replace hosts and user variables
|
||||||
|
image: ubuntu:impish
|
||||||
|
environment:
|
||||||
|
DEPLOY_HOSTNAME:
|
||||||
|
from_secret: deploy_hostname
|
||||||
|
SPT_ITEMS_HOSTNAME:
|
||||||
|
from_secret: spt_items_hostname
|
||||||
|
DEPLOYMENT_USER:
|
||||||
|
from_secret: deploy_username
|
||||||
|
commands:
|
||||||
|
- sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example
|
||||||
|
- mv ./items/frontend/.env.example ./items/frontend/.env
|
||||||
|
- sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory
|
||||||
|
- sed -i 's/{{ DEPLOYMENT_USER }}/'"$DEPLOYMENT_USER"'/g' ./.ansible-items/inventory
|
||||||
|
|
||||||
|
- name: build frontend
|
||||||
|
image: node:lts-alpine3.14
|
||||||
|
commands:
|
||||||
|
- node -v
|
||||||
|
- npm -v
|
||||||
|
- yarn --version
|
||||||
|
- yarn --cwd ./items/frontend install
|
||||||
|
- yarn --cwd ./items/frontend build --pure-lockfile
|
||||||
|
- rm -rf ./items/api/public/static/*
|
||||||
|
- mv ./items/frontend/build/* ./items/api/public
|
||||||
|
- rm ./items/api/public/index.html
|
||||||
|
|
||||||
|
- name: check ansible syntax
|
||||||
|
image: plugins/ansible:3
|
||||||
|
settings:
|
||||||
|
playbook: ./.ansible-items/playbook.yml
|
||||||
|
inventory: ./.ansible-items/inventory
|
||||||
|
galaxy: ./.ansible-items/requirements.yml
|
||||||
|
syntax_check: true
|
||||||
|
- name: apply ansible playbook
|
||||||
|
image: plugins/ansible:3
|
||||||
|
settings:
|
||||||
|
playbook: ./.ansible-items/playbook.yml
|
||||||
|
inventory: ./.ansible-items/inventory
|
||||||
|
galaxy: ./.ansible-items/requirements.yml
|
||||||
|
private_key:
|
||||||
|
from_secret: deploy_ssh_key
|
||||||
|
environment:
|
||||||
|
DEPLOY_HOSTNAME:
|
||||||
|
from_secret: deploy_hostname
|
||||||
|
SPT_ITEMS_HOSTNAME:
|
||||||
|
from_secret: spt_items_hostname
|
||||||
|
DEPLOYMENT_USER:
|
||||||
|
from_secret: deploy_username
|
||||||
|
DEPLOYMENT_USER_GROUP:
|
||||||
|
from_secret: deploy_user_group
|
||||||
|
SPT_ITEMS_PATH:
|
||||||
|
from_secret: deploy_path
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
73
.drone-kubernetes.yml
Normal file
73
.drone-kubernetes.yml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: kubernetes
|
||||||
|
name: default
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
limit: 1
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: replace hosts and user variables
|
||||||
|
image: ubuntu:impish
|
||||||
|
environment:
|
||||||
|
DEPLOY_HOSTNAME:
|
||||||
|
from_secret: deploy_hostname
|
||||||
|
SPT_ITEMS_HOSTNAME:
|
||||||
|
from_secret: spt_items_hostname
|
||||||
|
DEPLOYMENT_USER:
|
||||||
|
from_secret: deploy_username
|
||||||
|
commands:
|
||||||
|
- sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example
|
||||||
|
- mv ./items/frontend/.env.example ./items/frontend/.env
|
||||||
|
- sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory
|
||||||
|
- sed -i 's/{{ DEPLOYMENT_USER }}/'"$DEPLOYMENT_USER"'/g' ./.ansible-items/inventory
|
||||||
|
|
||||||
|
- name: build frontend
|
||||||
|
image: node:lts-alpine3.14
|
||||||
|
commands:
|
||||||
|
- node -v
|
||||||
|
- npm -v
|
||||||
|
- yarn --version
|
||||||
|
- yarn --cwd ./items/frontend install
|
||||||
|
- yarn --cwd ./items/frontend build --pure-lockfile
|
||||||
|
- rm -rf ./items/api/public/static/*
|
||||||
|
- mv ./items/frontend/build/* ./items/api/public
|
||||||
|
- rm ./items/api/public/index.html
|
||||||
|
|
||||||
|
- name: check ansible syntax
|
||||||
|
image: plugins/ansible:3
|
||||||
|
settings:
|
||||||
|
playbook: ./.ansible-items/playbook.yml
|
||||||
|
inventory: ./.ansible-items/inventory
|
||||||
|
galaxy: ./.ansible-items/requirements.yml
|
||||||
|
syntax_check: true
|
||||||
|
- name: apply ansible playbook
|
||||||
|
image: plugins/ansible:3
|
||||||
|
settings:
|
||||||
|
playbook: ./.ansible-items/playbook.yml
|
||||||
|
inventory: ./.ansible-items/inventory
|
||||||
|
galaxy: ./.ansible-items/requirements.yml
|
||||||
|
private_key:
|
||||||
|
from_secret: deploy_ssh_key
|
||||||
|
environment:
|
||||||
|
DEPLOY_HOSTNAME:
|
||||||
|
from_secret: deploy_hostname
|
||||||
|
SPT_ITEMS_HOSTNAME:
|
||||||
|
from_secret: spt_items_hostname
|
||||||
|
DEPLOYMENT_USER:
|
||||||
|
from_secret: deploy_username
|
||||||
|
DEPLOYMENT_USER_GROUP:
|
||||||
|
from_secret: deploy_user_group
|
||||||
|
SPT_ITEMS_PATH:
|
||||||
|
from_secret: deploy_path
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
37
.gitignore
vendored
37
.gitignore
vendored
@ -1,2 +1,37 @@
|
|||||||
.dev/
|
.dev/
|
||||||
pastebin/cfg
|
pastebin/cfg
|
||||||
|
|
||||||
|
### items ###
|
||||||
|
# dependencies
|
||||||
|
**/node_modules
|
||||||
|
**/.pnp
|
||||||
|
.pnp.js
|
||||||
|
**/vendor
|
||||||
|
|
||||||
|
# testing
|
||||||
|
**/coverage
|
||||||
|
**/coverage/
|
||||||
|
.phpunit.result.cache
|
||||||
|
**/cypress/videos
|
||||||
|
**/cypress/screnshots
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# production
|
||||||
|
**/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
## IDE cache
|
||||||
|
**/.vscode/
|
||||||
|
**/.idea/
|
||||||
|
**/*.code-workspace
|
@ -7,4 +7,4 @@ items.sp-tarkov.com have been made by Shirito and Revingly
|
|||||||
This repository contain the following websites content:
|
This repository contain the following websites content:
|
||||||
* www.sp-tarkov.com
|
* www.sp-tarkov.com
|
||||||
* codepaste.sp-tarkov.com
|
* codepaste.sp-tarkov.com
|
||||||
* items.sp-tarkov.com
|
* [items.sp-tarkov.com](https://items.sp-tarkov.com) (see [developer documentation](./items/README.md))
|
7
items/.gitignore
vendored
7
items/.gitignore
vendored
@ -1,7 +0,0 @@
|
|||||||
/vendor
|
|
||||||
/.idea
|
|
||||||
Homestead.json
|
|
||||||
Homestead.yaml
|
|
||||||
.env
|
|
||||||
.phpunit.result.cache
|
|
||||||
*.env.local
|
|
@ -1,13 +1,43 @@
|
|||||||
# SP Tarkov items API
|
# SPT Items
|
||||||
|
|
||||||
## How to install
|
* You can use either of the two drone pipeline types:
|
||||||
|
* [docker](../.drone-docker.yml)
|
||||||
|
* [kubernetes](../.drone-kubernetes.yml)
|
||||||
|
* Some enhancement ideas can be found [here](#some-enhancement-ideas)
|
||||||
|
|
||||||
* Clone the repo
|
## Required secrets
|
||||||
* Make sure you have PHP and composer installed on your PC
|
| secret name | description | example |
|
||||||
* To verify that you have PHP and composer working, try these commands `php -v` and `composer -v`
|
| :----------------- | :---------- | :------- |
|
||||||
* Go inside the cloned repo and install the dependencies with this command `composer install`
|
| spt_items_hostname | The remote server where spt-items-finder will be reachable <br> used for the frontend resolution | spt-items.my.server.com |
|
||||||
* If you don't have a local server like Apache or Nginx, you can use PHP to start a server with this command in the root folder of the repo `php -S localhost:8000 -t public`
|
| deploy_path | The path to deploy to in the remote machine | `/var/www/html/aki/Website/items` |
|
||||||
|
| deploy_hostname | The remote server where to deploy <br> used by Ansible SSH | my.server.com |
|
||||||
|
| deploy_username | The default username to use on the remote server <br> used by Ansible SSH | www-data |
|
||||||
|
| deploy_user_group | The default user group to use on the remote server <br> used to set permission on the website folder | www-data |
|
||||||
|
| deploy_ssh_key | The **content** of the ssh private key used to connect to the remote server <br> The key needs to be in RSA in "RSA PRIVATE KEY" format <br> The ssh publick key needs to already be in the user used in the remote server ~/.ssh/authorized_keys | -----BEGIN RSA PRIVATE KEY----- <br> The key <br> -----END RSA PRIVATE KEY----- |
|
||||||
|
|
||||||
## How to use
|
## ⚠ Important notes for the deployment ⚠
|
||||||
|
* Add all required secrets in Drone
|
||||||
|
* Server permissions:
|
||||||
|
1. The server must be able to use `apt` package manager
|
||||||
|
1. The *deploy_usernam* must exists, be part of the group *deploy_user_group* and be able to SSH into the server
|
||||||
|
1. If the parent folder of *deploy_path* already exists, *deploy_username* must have read and write permissions on it
|
||||||
|
* PHP:
|
||||||
|
1. `php8.0-fpm` and all its dependencies must already be installed
|
||||||
|
1. `php8.0-fpm` must be configured to use *deploy_username* (to ensure the cache created by Laravel can be deleted before every new deployment)
|
||||||
|
* Nginx:
|
||||||
|
1. Nginx must be using uses the user group *deploy_user_group*
|
||||||
|
1. Nginx must be configured to use HTTPS
|
||||||
|
1. Nginx must be configured to listen to *spt_items_hostname* and to point to the *deploy_path*
|
||||||
|
|
||||||
* Check the `routes/web.php` for the available routes and use whatever utility you are confortable with to call the API endpoints, ex: Postman
|
## The pipeline summary
|
||||||
|
1. Each push will:
|
||||||
|
1. Builds the frontend
|
||||||
|
1. Move the build frontend in the backend `public` folder
|
||||||
|
1. IF Promoted to production, deploys to the server
|
||||||
|
|
||||||
|
## The pipeline walkthrough
|
||||||
|
see [Walkthrough.md](./docs/Walkthrough.md)
|
||||||
|
|
||||||
|
## Some enhancement ideas
|
||||||
|
- Store the build so that it is not rebuilt on any `promote` event
|
||||||
|
- Use a volume or a cache for Yarn install
|
13
items/api/README.md
Normal file
13
items/api/README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# SP Tarkov items API
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
* Clone the repo
|
||||||
|
* Make sure you have PHP and composer installed on your PC
|
||||||
|
* To verify that you have PHP and composer working, try these commands `php -v` and `composer -v`
|
||||||
|
* Go inside the cloned repo and install the dependencies with this command `composer install`
|
||||||
|
* If you don't have a local server like Apache or Nginx, you can use PHP to start a server with this command in the root folder of the repo `php -S localhost:8000 -t public`
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
* Check the `routes/web.php` for the available routes and use whatever utility you are confortable with to call the API endpoints, ex: Postman
|
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.js": "/static/js/main.d930c480.chunk.js",
|
"main.js": "/static/js/main.4a751c81.chunk.js",
|
||||||
"main.js.map": "/static/js/main.d930c480.chunk.js.map",
|
"main.js.map": "/static/js/main.4a751c81.chunk.js.map",
|
||||||
"runtime-main.js": "/static/js/runtime-main.45092312.js",
|
"runtime-main.js": "/static/js/runtime-main.45092312.js",
|
||||||
"runtime-main.js.map": "/static/js/runtime-main.45092312.js.map",
|
"runtime-main.js.map": "/static/js/runtime-main.45092312.js.map",
|
||||||
"static/js/2.019ef1a9.chunk.js": "/static/js/2.019ef1a9.chunk.js",
|
"static/js/2.0d919e81.chunk.js": "/static/js/2.0d919e81.chunk.js",
|
||||||
"static/js/2.019ef1a9.chunk.js.map": "/static/js/2.019ef1a9.chunk.js.map",
|
"static/js/2.0d919e81.chunk.js.map": "/static/js/2.0d919e81.chunk.js.map",
|
||||||
"static/js/3.ff1076df.chunk.js": "/static/js/3.ff1076df.chunk.js",
|
"static/js/3.ff1076df.chunk.js": "/static/js/3.ff1076df.chunk.js",
|
||||||
"static/js/3.ff1076df.chunk.js.map": "/static/js/3.ff1076df.chunk.js.map",
|
"static/js/3.ff1076df.chunk.js.map": "/static/js/3.ff1076df.chunk.js.map",
|
||||||
"index.html": "/index.html",
|
"index.html": "/index.html",
|
||||||
"static/js/2.019ef1a9.chunk.js.LICENSE.txt": "/static/js/2.019ef1a9.chunk.js.LICENSE.txt"
|
"static/js/2.0d919e81.chunk.js.LICENSE.txt": "/static/js/2.0d919e81.chunk.js.LICENSE.txt"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/js/runtime-main.45092312.js",
|
"static/js/runtime-main.45092312.js",
|
||||||
"static/js/2.019ef1a9.chunk.js",
|
"static/js/2.0d919e81.chunk.js",
|
||||||
"static/js/main.d930c480.chunk.js"
|
"static/js/main.4a751c81.chunk.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
BIN
items/api/public/favicon.ico
Normal file
BIN
items/api/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
BIN
items/api/public/logo192.png
Normal file
BIN
items/api/public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
items/api/public/logo512.png
Normal file
BIN
items/api/public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
File diff suppressed because one or more lines are too long
1
items/api/public/static/js/2.0d919e81.chunk.js.map
Normal file
1
items/api/public/static/js/2.0d919e81.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
3
items/api/public/static/js/2.d37e78ec.chunk.js
Normal file
3
items/api/public/static/js/2.d37e78ec.chunk.js
Normal file
File diff suppressed because one or more lines are too long
73
items/api/public/static/js/2.d37e78ec.chunk.js.LICENSE.txt
Normal file
73
items/api/public/static/js/2.d37e78ec.chunk.js.LICENSE.txt
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
object-assign
|
||||||
|
(c) Sindre Sorhus
|
||||||
|
@license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A better abstraction over CSS.
|
||||||
|
*
|
||||||
|
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
||||||
|
* @website https://github.com/cssinjs/jss
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license MUI v5.0.1
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v0.20.2
|
||||||
|
* scheduler.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v16.13.1
|
||||||
|
* react-is.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v17.0.2
|
||||||
|
* react-dom.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v17.0.2
|
||||||
|
* react-is.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v17.0.2
|
||||||
|
* react-jsx-runtime.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v17.0.2
|
||||||
|
* react.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
1
items/api/public/static/js/2.d37e78ec.chunk.js.map
Normal file
1
items/api/public/static/js/2.d37e78ec.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
items/api/public/static/js/3.f4122446.chunk.js
Normal file
2
items/api/public/static/js/3.f4122446.chunk.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
(this["webpackJsonpnew-item-finder-website"]=this["webpackJsonpnew-item-finder-website"]||[]).push([[3],{208:function(e,t,n){"use strict";n.r(t),n.d(t,"getCLS",(function(){return p})),n.d(t,"getFCP",(function(){return S})),n.d(t,"getFID",(function(){return F})),n.d(t,"getLCP",(function(){return k})),n.d(t,"getTTFB",(function(){return C}));var i,a,r,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v1-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},f=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},s=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},m="function"==typeof WeakSet?new WeakSet:new Set,d=function(e,t,n){var i;return function(){t.value>=0&&(n||m.has(t)||"hidden"===document.visibilityState)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},p=function(e,t){var n,i=u("CLS",0),a=function(e){e.hadRecentInput||(i.value+=e.value,i.entries.push(e),n())},r=c("layout-shift",a);r&&(n=d(e,i,t),f((function(){r.takeRecords().map(a),n()})),s((function(){i=u("CLS",0),n=d(e,i,t)})))},v=-1,l=function(){return"hidden"===document.visibilityState?0:1/0},h=function(){f((function(e){var t=e.timeStamp;v=t}),!0)},g=function(){return v<0&&(v=l(),h(),s((function(){setTimeout((function(){v=l(),h()}),0)}))),{get timeStamp(){return v}}},S=function(e,t){var n,i=g(),a=u("FCP"),r=function(e){"first-contentful-paint"===e.name&&(f&&f.disconnect(),e.startTime<i.timeStamp&&(a.value=e.startTime,a.entries.push(e),m.add(a),n()))},o=performance.getEntriesByName("first-contentful-paint")[0],f=o?null:c("paint",r);(o||f)&&(n=d(e,a,t),o&&r(o),s((function(i){a=u("FCP"),n=d(e,a,t),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),n()}))}))})))},y={passive:!0,capture:!0},w=new Date,E=function(e,t){i||(i=t,a=e,r=new Date,b(removeEventListener),L())},L=function(){if(a>=0&&a<r-w){var e={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+a};o.forEach((function(t){t(e)})),o=[]}},T=function(e){if(e.cancelable){var t=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){E(e,t),a()},i=function(){a()},a=function(){removeEventListener("pointerup",n,y),removeEventListener("pointercancel",i,y)};addEventListener("pointerup",n,y),addEventListener("pointercancel",i,y)}(t,e):E(t,e)}},b=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,T,y)}))},F=function(e,t){var n,r=g(),p=u("FID"),v=function(e){e.startTime<r.timeStamp&&(p.value=e.processingStart-e.startTime,p.entries.push(e),m.add(p),n())},l=c("first-input",v);n=d(e,p,t),l&&f((function(){l.takeRecords().map(v),l.disconnect()}),!0),l&&s((function(){var r;p=u("FID"),n=d(e,p,t),o=[],a=-1,i=null,b(addEventListener),r=v,o.push(r),L()}))},k=function(e,t){var n,i=g(),a=u("LCP"),r=function(e){var t=e.startTime;t<i.timeStamp&&(a.value=t,a.entries.push(e)),n()},o=c("largest-contentful-paint",r);if(o){n=d(e,a,t);var p=function(){m.has(a)||(o.takeRecords().map(r),o.disconnect(),m.add(a),n())};["keydown","click"].forEach((function(e){addEventListener(e,p,{once:!0,capture:!0})})),f(p,!0),s((function(i){a=u("LCP"),n=d(e,a,t),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),n()}))}))}))}},C=function(e){var t,n=u("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();if(n.value=n.delta=t.responseStart,n.value<0)return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("pageshow",t)}}}]);
|
||||||
|
//# sourceMappingURL=3.f4122446.chunk.js.map
|
1
items/api/public/static/js/3.f4122446.chunk.js.map
Normal file
1
items/api/public/static/js/3.f4122446.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
items/api/public/static/js/main.4a751c81.chunk.js
Normal file
2
items/api/public/static/js/main.4a751c81.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
items/api/public/static/js/main.4a751c81.chunk.js.map
Normal file
1
items/api/public/static/js/main.4a751c81.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
items/api/public/static/js/main.4f639ccc.chunk.js
Normal file
2
items/api/public/static/js/main.4f639ccc.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
items/api/public/static/js/main.4f639ccc.chunk.js.map
Normal file
1
items/api/public/static/js/main.4f639ccc.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
items/api/public/static/js/runtime-main.bdac3883.js
Normal file
2
items/api/public/static/js/runtime-main.bdac3883.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
!function(e){function t(t){for(var n,u,a=t[0],c=t[1],f=t[2],s=0,p=[];s<a.length;s++)u=a[s],Object.prototype.hasOwnProperty.call(o,u)&&o[u]&&p.push(o[u][0]),o[u]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(t);p.length;)p.shift()();return i.push.apply(i,f||[]),r()}function r(){for(var e,t=0;t<i.length;t++){for(var r=i[t],n=!0,a=1;a<r.length;a++){var c=r[a];0!==o[c]&&(n=!1)}n&&(i.splice(t--,1),e=u(u.s=r[0]))}return e}var n={},o={1:0},i=[];function u(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,u),r.l=!0,r.exports}u.e=function(e){var t=[],r=o[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=o[e]=[t,n]}));t.push(r[2]=n);var i,a=document.createElement("script");a.charset="utf-8",a.timeout=120,u.nc&&a.setAttribute("nonce",u.nc),a.src=function(e){return u.p+"static/js/"+({}[e]||e)+"."+{3:"f4122446"}[e]+".chunk.js"}(e);var c=new Error;i=function(t){a.onerror=a.onload=null,clearTimeout(f);var r=o[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+i+")",c.name="ChunkLoadError",c.type=n,c.request=i,r[1](c)}o[e]=void 0}};var f=setTimeout((function(){i({type:"timeout",target:a})}),12e4);a.onerror=a.onload=i,document.head.appendChild(a)}return Promise.all(t)},u.m=e,u.c=n,u.d=function(e,t,r){u.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},u.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(e,t){if(1&t&&(e=u(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(u.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)u.d(r,n,function(t){return e[t]}.bind(null,n));return r},u.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(t,"a",t),t},u.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},u.p="/",u.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpnew-item-finder-website"]=this["webpackJsonpnew-item-finder-website"]||[],c=a.push.bind(a);a.push=t,a=a.slice();for(var f=0;f<a.length;f++)t(a[f]);var l=c;r()}([]);
|
||||||
|
//# sourceMappingURL=runtime-main.bdac3883.js.map
|
1
items/api/public/static/js/runtime-main.bdac3883.js.map
Normal file
1
items/api/public/static/js/runtime-main.bdac3883.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -16,8 +16,8 @@
|
|||||||
<body><noscript>You need to enable JavaScript to run this app.</noscript>
|
<body><noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script>!function (e) { function t(t) { for (var n, u, a = t[0], c = t[1], f = t[2], s = 0, p = []; s < a.length; s++)u = a[s], Object.prototype.hasOwnProperty.call(o, u) && o[u] && p.push(o[u][0]), o[u] = 0; for (n in c) Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]); for (l && l(t); p.length;)p.shift()(); return i.push.apply(i, f || []), r() } function r() { for (var e, t = 0; t < i.length; t++) { for (var r = i[t], n = !0, a = 1; a < r.length; a++) { var c = r[a]; 0 !== o[c] && (n = !1) } n && (i.splice(t--, 1), e = u(u.s = r[0])) } return e } var n = {}, o = { 1: 0 }, i = []; function u(t) { if (n[t]) return n[t].exports; var r = n[t] = { i: t, l: !1, exports: {} }; return e[t].call(r.exports, r, r.exports, u), r.l = !0, r.exports } u.e = function (e) { var t = [], r = o[e]; if (0 !== r) if (r) t.push(r[2]); else { var n = new Promise((function (t, n) { r = o[e] = [t, n] })); t.push(r[2] = n); var i, a = document.createElement("script"); a.charset = "utf-8", a.timeout = 120, u.nc && a.setAttribute("nonce", u.nc), a.src = function (e) { return u.p + "static/js/" + ({}[e] || e) + "." + { 3: "ff1076df" }[e] + ".chunk.js" }(e); var c = new Error; i = function (t) { a.onerror = a.onload = null, clearTimeout(f); var r = o[e]; if (0 !== r) { if (r) { var n = t && ("load" === t.type ? "missing" : t.type), i = t && t.target && t.target.src; c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + i + ")", c.name = "ChunkLoadError", c.type = n, c.request = i, r[1](c) } o[e] = void 0 } }; var f = setTimeout((function () { i({ type: "timeout", target: a }) }), 12e4); a.onerror = a.onload = i, document.head.appendChild(a) } return Promise.all(t) }, u.m = e, u.c = n, u.d = function (e, t, r) { u.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, u.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, u.t = function (e, t) { if (1 & t && (e = u(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var r = Object.create(null); if (u.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var n in e) u.d(r, n, function (t) { return e[t] }.bind(null, n)); return r }, u.n = function (e) { var t = e && e.__esModule ? function () { return e.default } : function () { return e }; return u.d(t, "a", t), t }, u.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, u.p = "/", u.oe = function (e) { throw console.error(e), e }; var a = this["webpackJsonpnew-item-finder-website"] = this["webpackJsonpnew-item-finder-website"] || [], c = a.push.bind(a); a.push = t, a = a.slice(); for (var f = 0; f < a.length; f++)t(a[f]); var l = c; r() }([])</script>
|
<script>!function (e) { function t(t) { for (var n, u, a = t[0], c = t[1], f = t[2], s = 0, p = []; s < a.length; s++)u = a[s], Object.prototype.hasOwnProperty.call(o, u) && o[u] && p.push(o[u][0]), o[u] = 0; for (n in c) Object.prototype.hasOwnProperty.call(c, n) && (e[n] = c[n]); for (l && l(t); p.length;)p.shift()(); return i.push.apply(i, f || []), r() } function r() { for (var e, t = 0; t < i.length; t++) { for (var r = i[t], n = !0, a = 1; a < r.length; a++) { var c = r[a]; 0 !== o[c] && (n = !1) } n && (i.splice(t--, 1), e = u(u.s = r[0])) } return e } var n = {}, o = { 1: 0 }, i = []; function u(t) { if (n[t]) return n[t].exports; var r = n[t] = { i: t, l: !1, exports: {} }; return e[t].call(r.exports, r, r.exports, u), r.l = !0, r.exports } u.e = function (e) { var t = [], r = o[e]; if (0 !== r) if (r) t.push(r[2]); else { var n = new Promise((function (t, n) { r = o[e] = [t, n] })); t.push(r[2] = n); var i, a = document.createElement("script"); a.charset = "utf-8", a.timeout = 120, u.nc && a.setAttribute("nonce", u.nc), a.src = function (e) { return u.p + "static/js/" + ({}[e] || e) + "." + { 3: "ff1076df" }[e] + ".chunk.js" }(e); var c = new Error; i = function (t) { a.onerror = a.onload = null, clearTimeout(f); var r = o[e]; if (0 !== r) { if (r) { var n = t && ("load" === t.type ? "missing" : t.type), i = t && t.target && t.target.src; c.message = "Loading chunk " + e + " failed.\n(" + n + ": " + i + ")", c.name = "ChunkLoadError", c.type = n, c.request = i, r[1](c) } o[e] = void 0 } }; var f = setTimeout((function () { i({ type: "timeout", target: a }) }), 12e4); a.onerror = a.onload = i, document.head.appendChild(a) } return Promise.all(t) }, u.m = e, u.c = n, u.d = function (e, t, r) { u.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, u.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, u.t = function (e, t) { if (1 & t && (e = u(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var r = Object.create(null); if (u.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var n in e) u.d(r, n, function (t) { return e[t] }.bind(null, n)); return r }, u.n = function (e) { var t = e && e.__esModule ? function () { return e.default } : function () { return e }; return u.d(t, "a", t), t }, u.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, u.p = "/", u.oe = function (e) { throw console.error(e), e }; var a = this["webpackJsonpnew-item-finder-website"] = this["webpackJsonpnew-item-finder-website"] || [], c = a.push.bind(a); a.push = t, a = a.slice(); for (var f = 0; f < a.length; f++)t(a[f]); var l = c; r() }([])</script>
|
||||||
<script src="/static/js/main.d930c480.chunk.js"></script>
|
<script src="/static/js/2.0d919e81.chunk.js"></script>
|
||||||
<script src="/static/js/2.019ef1a9.chunk.js"></script>
|
<script src="/static/js/main.4a751c81.chunk.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
257
items/docs/Walkthrough.md
Normal file
257
items/docs/Walkthrough.md
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
# Walkthrough
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
* [Overview](#overview)
|
||||||
|
* [Pipeline definition](#pipeline-definition)
|
||||||
|
* [Pipeline concurrency](#pipeline-concurrency)
|
||||||
|
* [Environment variables](#environment-variables)
|
||||||
|
* [Triggers](#triggers)
|
||||||
|
* [Steps](#steps)
|
||||||
|
* [Fetch and update submodules](#Fetch-and-update-submodules)
|
||||||
|
* [Replace hosts and user variables](#replace-hosts-and-user-variables)
|
||||||
|
* [Build frontend](#build-frontend)
|
||||||
|
* [Check ansible syntax](#check-ansible-syntax)
|
||||||
|
* [Apply ansible playbook](#apply-ansible-playbook)
|
||||||
|
* [Playbook definition](#playbook-definition)
|
||||||
|
* [Delete old spt-items-api](#delete-old-spt-items-api)
|
||||||
|
* [Copy the project](#copy-the-project)
|
||||||
|
* [Copy PHP env file](#copy-php-env-file)
|
||||||
|
* [Get JavaScript chunks name](#get-javascript-chunks-name)
|
||||||
|
* [Get file names from find output](#get-file-from-find-output)
|
||||||
|
* [Copy app.blade.php file](#copy-app-blade-php-file)
|
||||||
|
* [Download and install composer dependencies](#download-and-install-composer-dependencies)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
* The project is split between the frontend and the backend
|
||||||
|
* the backend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [api](../api) that points towards [https://dev.sp-tarkov.com/Rev/spt-items-api.git](https://dev.sp-tarkov.com/Rev/spt-items-api.git)
|
||||||
|
* the frontend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [frontend](../frontend) that points towards [https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git](https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git)
|
||||||
|
* There are two Ansible pipelines
|
||||||
|
* A docker pipeline [drone-docker.yml](../drone-docker.yml)
|
||||||
|
* A kubernetes pipeline [drone-kubernetes.yml](../drone-kubernetes.yml)
|
||||||
|
* All ansible playbook files are located in [.ansible](../.ansible)
|
||||||
|
* The documentation is located in [documentation](../documentation)
|
||||||
|
|
||||||
|
## Pipeline definition
|
||||||
|
```yml
|
||||||
|
kind: pipeline
|
||||||
|
type: kubernetes
|
||||||
|
name: default
|
||||||
|
```
|
||||||
|
The pipeline is defined either as Docker or Kubernetes depending on [.drone-docker.yml](../../.drone-docker.yml) or [.drone-kubernetes.yml](../../.drone-kubernetes.yml). The name is set as `default`.
|
||||||
|
|
||||||
|
## Pipeline Concurrency
|
||||||
|
```yml
|
||||||
|
concurrency:
|
||||||
|
limit: 1
|
||||||
|
```
|
||||||
|
The pipeline is set to only one build at a time (every subsequent build with be pending).
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
```yml
|
||||||
|
environment:
|
||||||
|
SPT_ITEMS_PATH: /var/www/html/aki/spt-items-api
|
||||||
|
```
|
||||||
|
Here are the environment variables. They are automatically injected in every step.
|
||||||
|
|
||||||
|
## Triggers
|
||||||
|
```yml
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
```
|
||||||
|
The pipeline is run on every push only on branches `master`, `main` and `development`. We want to check that every development on `development` branch is correct and deploy automatically when merged in `master`/`main`.
|
||||||
|
## Steps
|
||||||
|
### Replace hosts and user variables
|
||||||
|
```yml
|
||||||
|
- name: replace hosts and user variables
|
||||||
|
image: ubuntu:impish
|
||||||
|
environment:
|
||||||
|
DEPLOY_HOSTNAME:
|
||||||
|
from_secret: deploy_hostname
|
||||||
|
SPT_ITEMS_HOSTNAME:
|
||||||
|
from_secret: spt_items_hostname
|
||||||
|
DEPLOYMENT_USER:
|
||||||
|
from_secret: deploy_username
|
||||||
|
commands:
|
||||||
|
- sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example
|
||||||
|
- mv ./items/frontend/.env.example ./items/frontend/.env
|
||||||
|
- sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory
|
||||||
|
- sed -i 's/{{ DEPLOYMENT_USER }}/'"$DEPLOYMENT_USER"'/g' ./.ansible-items/inventory
|
||||||
|
```
|
||||||
|
Executed on every push. \
|
||||||
|
The following environment variables are injected using Drone secrets:
|
||||||
|
* `SPT_ITEMS_HOSTNAME` is used by the frontend to call the backend.
|
||||||
|
* `DEPLOY_HOSTNAME` is used by Ansible to connect to the remote server via SSH.
|
||||||
|
* `DEPLOYMENT_USER` is used by Ansible to connect to the remote server via SSH.
|
||||||
|
* all environment variables at the pipeline level (see [Environment variables](#environment-variables))
|
||||||
|
Using `sed` makes temporary changes in the container/pod instead of commiting secrets in the repo in plain text. \
|
||||||
|
The changes are never pushed and are discarded when the container/pod is terminated.
|
||||||
|
|
||||||
|
### Build frontend
|
||||||
|
```yml
|
||||||
|
- name: build frontend
|
||||||
|
image: node:lts-alpine3.14
|
||||||
|
commands:
|
||||||
|
- node -v
|
||||||
|
- npm -v
|
||||||
|
- yarn --version
|
||||||
|
- yarn --cwd ./items/frontend install
|
||||||
|
- yarn --cwd ./items/frontend build --pure-lockfile
|
||||||
|
- rm -rf ./items/api/public/static/*
|
||||||
|
- mv ./items/frontend/build/* ./items/api/public
|
||||||
|
- rm ./items/api/public/index.html
|
||||||
|
```
|
||||||
|
Executed on every push. \
|
||||||
|
Since the PHP backend serves the ReactJS frontend, the former is built and moved in the latter.
|
||||||
|
Notes:
|
||||||
|
* `yarn --cwd <path> <command>` executes the command in the specified file
|
||||||
|
* `rm -rf ./api/public/static/*` deletes the static files to make sure no old JavaScript files remain
|
||||||
|
* `rm ./api/public/index.html` ReactJS is bundled with a `index.html`. It is discarded to use [](https://dev.sp-tarkov.com/Rev/spt-items-api/raw/branch/master/resources/views/app.blade.php) instead.
|
||||||
|
|
||||||
|
### Check ansible syntax
|
||||||
|
```yml
|
||||||
|
- name: check ansible syntax
|
||||||
|
image: plugins/ansible:3
|
||||||
|
settings:
|
||||||
|
playbook: ./.ansible-items/playbook.yml
|
||||||
|
inventory: ./.ansible-items/inventory
|
||||||
|
galaxy: ./.ansible-items/requirements.yml
|
||||||
|
syntax_check: true
|
||||||
|
```
|
||||||
|
Executed on every push. \
|
||||||
|
Check the Ansible syntax in [playbook.yml](../.ansible/playbook.yml), [inventory](../.ansible/inventory) and [requirements.yml](../.ansible/requirements.yml). The check is executed on every push since we want to detect any error before validating the build using the promotion.
|
||||||
|
|
||||||
|
### Apply ansible playbook
|
||||||
|
```yml
|
||||||
|
- name: apply ansible playbook
|
||||||
|
image: plugins/ansible:3
|
||||||
|
settings:
|
||||||
|
playbook: ./.ansible-items/playbook.yml
|
||||||
|
inventory: ./.ansible-items/inventory
|
||||||
|
galaxy: ./.ansible-items/requirements.yml
|
||||||
|
private_key:
|
||||||
|
from_secret: deploy_ssh_key
|
||||||
|
environment:
|
||||||
|
DEPLOY_HOSTNAME:
|
||||||
|
from_secret: deploy_hostname
|
||||||
|
SPT_ITEMS_HOSTNAME:
|
||||||
|
from_secret: spt_items_hostname
|
||||||
|
DEPLOYMENT_USER:
|
||||||
|
from_secret: deploy_username
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
```
|
||||||
|
Executed only on promotion to production. \
|
||||||
|
This step actually deploys to the server. \
|
||||||
|
This step is [idempotent](https://en.wikipedia.org/wiki/Idempotence). \
|
||||||
|
The following environment variables are injected using Drone secrets:
|
||||||
|
* `SPT_ITEMS_HOSTNAME` is used by the PHP env file.
|
||||||
|
* `DEPLOY_HOSTNAME` is used to connect to the remote server via SSH.
|
||||||
|
* `DEPLOYMENT_USER` is used to connect to the remote server via SSH.
|
||||||
|
* all environment variables at the pipeline level (see [Environment variables](#environment-variables))
|
||||||
|
|
||||||
|
#### Playbook definition
|
||||||
|
```yml
|
||||||
|
hosts: host
|
||||||
|
```
|
||||||
|
Uses the host defined in [inventory](../.ansible/inventory). Remember, the step [Replace hosts and user variables](#replace-hosts-and-user-variables) already replaced the variables at this point. The playbook will be executed as `root` user using `sudo`.
|
||||||
|
|
||||||
|
#### Delete old spt-items-api
|
||||||
|
```yml
|
||||||
|
- name: Delete spt-items-api before adding everything again
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
```
|
||||||
|
Since the copy does not override the folder, this step takes care of it. \
|
||||||
|
`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables))
|
||||||
|
|
||||||
|
#### Copy the project
|
||||||
|
```yml
|
||||||
|
- name: Copy the project
|
||||||
|
copy:
|
||||||
|
src: ../items/api/
|
||||||
|
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
```
|
||||||
|
Copies the whole project (frontend and backend) from the [api](../api) folder into the server.
|
||||||
|
|
||||||
|
#### Copy PHP env file
|
||||||
|
```yml
|
||||||
|
- name: Copy PHP .env file
|
||||||
|
template:
|
||||||
|
src: ./templates/.php-env.j2
|
||||||
|
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/.env"
|
||||||
|
```
|
||||||
|
Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP .env file](../.ansible/templates/.php_env.j2). \
|
||||||
|
`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). \
|
||||||
|
`SPT_ITEMS_HOSTNAME` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook))
|
||||||
|
|
||||||
|
#### Get JavaScript chunks name
|
||||||
|
```yml
|
||||||
|
- name: Get Chunk 2 name
|
||||||
|
shell:
|
||||||
|
cmd: find "{{ lookup('env', 'SPT_ITEMS_PATH') }}" -type f -name "*chunk.js" -printf "%f\n"
|
||||||
|
register: find_output
|
||||||
|
```
|
||||||
|
Prepare a find of all JavaScript chunk files for the [app.blade.php.j2](../.ansible/templates/app.blade.php.j2) template. \
|
||||||
|
`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)).
|
||||||
|
|
||||||
|
#### Get file names from find output
|
||||||
|
```yml
|
||||||
|
- name: Get file names from find output
|
||||||
|
set_fact:
|
||||||
|
chunk_list: "{{ find_output['stdout'].split('\n') }}"
|
||||||
|
```
|
||||||
|
Splits the string containing the list of all JavaScript chunk files for the [app.blade.php.j2](../.ansible/templates/app.blade.php.j2) template.
|
||||||
|
|
||||||
|
#### Copy app.blade.php file
|
||||||
|
```yml
|
||||||
|
- name: Copy app.blade.php file
|
||||||
|
template:
|
||||||
|
src: ./templates/app.blade.php.j2
|
||||||
|
dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/resources/views/app.blade.php"
|
||||||
|
```
|
||||||
|
Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP app.blade.php file](../.ansible/templates/app.blade.php.j2). \
|
||||||
|
`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)).
|
||||||
|
|
||||||
|
#### Download and install composer dependencies
|
||||||
|
```yml
|
||||||
|
- name: Download and installs all composer libs and dependencies
|
||||||
|
community.general.composer:
|
||||||
|
command: install
|
||||||
|
working_dir: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reset files permissions
|
||||||
|
```yml
|
||||||
|
- name: Reset files permissions
|
||||||
|
file:
|
||||||
|
path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}"
|
||||||
|
owner: "{{ lookup('env', 'DEPLOYMENT_USER') }}"
|
||||||
|
group: www-data
|
||||||
|
mode: 0774
|
||||||
|
recurse: yes
|
||||||
|
```
|
||||||
|
Permissions 0644:
|
||||||
|
* user: read/write/execute
|
||||||
|
* group: read
|
||||||
|
* other: read
|
||||||
|
`www-data` is hardcoded here but it should be the standard user for Apache and Nginx. \
|
||||||
|
`SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)).
|
||||||
|
|
||||||
|
#### Initialize database
|
||||||
|
```yml
|
||||||
|
- name: Initialize database
|
||||||
|
uri:
|
||||||
|
url: "https://{{ lookup('env', 'SPT_ITEMS_HOSTNAME') }}/api/refresh"
|
||||||
|
method: GET
|
||||||
|
status_code: [200, 204]
|
||||||
|
timeout: 60
|
||||||
|
```
|
||||||
|
The call to `/api/refresh` fetches the data from AKI Server repository, `development` branch.
|
4
items/frontend/.env.example
Normal file
4
items/frontend/.env.example
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
REACT_APP_BACKEND_URL=https://{{ SPT_ITEMS_HOSTNAME }}
|
||||||
|
REACT_APP_SPTARKOV_HOME=https://www.sp-tarkov.com/
|
||||||
|
REACT_APP_SPTARKOV_WORKSHOP=https://mods.sp-tarkov.com/
|
||||||
|
REACT_APP_SPTARKOV_DOCUMENTATION=https://docs.sp-tarkov.com/
|
9
items/frontend/LICENSE
Normal file
9
items/frontend/LICENSE
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) <year> <copyright holders>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
8
items/frontend/README.md
Normal file
8
items/frontend/README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Item Finder Website
|
||||||
|
|
||||||
|
## How to build
|
||||||
|
1. Make sure `Nodejs` and `Npm` (or `Yarn`) are installed
|
||||||
|
1. Run `npm run build` or `yarn build` to build the production project
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
Created based on CRA template typescript (see [cra.md](./documentation/cra.md))
|
12
items/frontend/cypress.json
Normal file
12
items/frontend/cypress.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"baseUrl": "http://localhost:3000",
|
||||||
|
"integrationFolder": "src/cypress/integration",
|
||||||
|
"fixtureFolder": "src/cypress/fixtures",
|
||||||
|
"supportFile": "src/cypress/support/index.js",
|
||||||
|
"pluginsFile": "src/cypress/plugin/index.js",
|
||||||
|
"env": {
|
||||||
|
"REACT_APP_SPTARKOV_HOME": "https://www.sp-tarkov.com/",
|
||||||
|
"REACT_APP_SPTARKOV_WORKSHOP": "https://mods.sp-tarkov.com/",
|
||||||
|
"REACT_APP_SPTARKOV_DOCUMENTATION": "https://docs.sp-tarkov.com/"
|
||||||
|
}
|
||||||
|
}
|
5
items/frontend/cypress/fixtures/example.json
Normal file
5
items/frontend/cypress/fixtures/example.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Using fixtures to represent data",
|
||||||
|
"email": "hello@cypress.io",
|
||||||
|
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||||
|
}
|
46
items/frontend/documentation/cra.md
Normal file
46
items/frontend/documentation/cra.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `yarn start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.\
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `yarn test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `yarn build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `yarn eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||||
|
|
||||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||||
|
|
||||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
66
items/frontend/package.json
Normal file
66
items/frontend/package.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"name": "new-item-finder-website",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"@cypress/code-coverage": "^3.9.11",
|
||||||
|
"@cypress/instrument-cra": "^1.4.0",
|
||||||
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
|
"@testing-library/react": "^11.1.0",
|
||||||
|
"@testing-library/user-event": "^12.1.10",
|
||||||
|
"@types/jest": "^26.0.15",
|
||||||
|
"@types/node": "^12.0.0",
|
||||||
|
"@types/react": "^17.0.0",
|
||||||
|
"@types/react-dom": "^17.0.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"cypress": "^8.6.0",
|
||||||
|
"start-server-and-test": "^1.14.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.4.1",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"@mui/icons-material": "^5.0.3",
|
||||||
|
"@mui/material": "^5.0.3",
|
||||||
|
"@mui/styles": "^5.0.1",
|
||||||
|
"react": "^17.0.2",
|
||||||
|
"react-dom": "^17.0.2",
|
||||||
|
"react-json-view": "^1.21.3",
|
||||||
|
"react-scripts": "4.0.3",
|
||||||
|
"typescript": "^4.1.2",
|
||||||
|
"web-vitals": "^1.0.1",
|
||||||
|
"zustand": "^3.5.13"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test:jest": "react-scripts test",
|
||||||
|
"cy:start:app": "cross-env BROWSER=none react-scripts -r @cypress/instrument-cra start",
|
||||||
|
"cy:start:wait": "start-server-and-test cy:start:app http://localhost:3000",
|
||||||
|
"cy:open": "yarn run cy:start:wait -- \"cypress open\"",
|
||||||
|
"cy:run": "yarn run cy:start:wait -- \"cypress run\"",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nyc": {
|
||||||
|
"include":["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["src/reportWebVitals.ts"],
|
||||||
|
"excludeAfterRemap": true
|
||||||
|
}
|
||||||
|
}
|
BIN
items/frontend/public/favicon.ico
Normal file
BIN
items/frontend/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
109
items/frontend/public/favicon.svg
Normal file
109
items/frontend/public/favicon.svg
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 135.46666 135.46667"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||||
|
sodipodi:docname="Senko.svg"
|
||||||
|
inkscape:export-filename="G:\Official-SPT-Website\items\frontend\public\Senko.png"
|
||||||
|
inkscape:export-xdpi="6.5542703"
|
||||||
|
inkscape:export-ydpi="6.5542703"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#ffffff"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
width="512px"
|
||||||
|
inkscape:zoom="2"
|
||||||
|
inkscape:cx="237.25"
|
||||||
|
inkscape:cy="308.25"
|
||||||
|
inkscape:window-width="1918"
|
||||||
|
inkscape:window-height="2071"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="layer2" />
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="background"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
sodipodi:insensitive="true"
|
||||||
|
style="display:none;opacity:0.560261">
|
||||||
|
<image
|
||||||
|
width="135.46666"
|
||||||
|
height="135.46666"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
style="image-rendering:optimizeQuality"
|
||||||
|
xlink:href="file:///C:/Users/mangi/Downloads/cbimage.png"
|
||||||
|
id="image851"
|
||||||
|
x="2.7755576e-17"
|
||||||
|
y="0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="Vectors">
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 57.52693,14.843495 C 57.410877,12.661731 51.397926,11.268153 43.831419,8.2809548 45.338713,11.093505 47.068813,13.447161 44.596296,15.191748 38.636156,19.397174 29.955524,24.939364 15.868351,32.519676 26.725142,29.585137 55.838776,18.042907 57.52693,14.843495 Z"
|
||||||
|
id="path973"
|
||||||
|
sodipodi:nodetypes="ccscc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 6.7171919,59.288951 c 0,0 9.7889021,3.914164 12.6354121,3.564442 2.84651,-0.349722 24.019083,-6.879054 38.584354,-13.733298 0,0 -1.537979,-1.906423 -7.295931,-1.874468 -5.757953,0.03195 -20.211551,6.328008 -43.9238351,12.043324 z"
|
||||||
|
id="path973-69"
|
||||||
|
sodipodi:nodetypes="czczc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 48.122176,37.54381 c -1.232593,-1.803961 -6.907354,-3.051501 -14.924557,-1.673159 2.74906,1.620197 6.959357,3.657244 7.330694,6.817329 0.883551,7.519041 -3.70442,22.015324 -35.7508463,67.70939 C 30.583823,83.728004 50.961041,55.466892 48.122176,37.54381 Z"
|
||||||
|
id="path973-6"
|
||||||
|
sodipodi:nodetypes="ccscc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 46.036413,46.48147 c -1.232593,-1.803961 1.979358,4.244957 -6.037845,5.623299 0.223363,6.765135 0.503644,57.527511 -6.420323,69.679121 6.172239,6.35186 7.240267,5.73861 11.114865,8.58937 0.736474,-6.18316 3.15318,-65.968708 1.343303,-83.89179 z"
|
||||||
|
id="path973-6-0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 115.53919,30.415369 C 107.38986,24.808662 103.16969,52.222323 96.943881,65.21414 103.95471,56.4204 124.82782,36.805909 115.53919,30.415369 Z"
|
||||||
|
id="path973-9"
|
||||||
|
sodipodi:nodetypes="scs" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 107.65215,125.52123 c 9.30104,0.3094 49.17635,3.11209 12.29815,-8.69842 -16.53053,-5.29402 -29.508741,-34.427468 -37.036766,-44.827756 0.75933,13.845435 18.534656,45.862106 24.738616,53.526176 z"
|
||||||
|
id="path973-9-6"
|
||||||
|
sodipodi:nodetypes="cscc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.23757px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 68.35759,73.019974 c 4.512042,-0.335257 8.147729,-24.553068 -3.641892,-33.568807 2.793719,8.335678 1.719792,16.407471 0.731829,19.226592 -0.845868,2.413656 -4.878742,14.920943 2.910063,14.342215 z"
|
||||||
|
id="path973-9-3"
|
||||||
|
sodipodi:nodetypes="scss" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.23757px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 59.276543,85.32281 C 56.530494,88.91866 43.057416,72.918966 40.497215,69.39512 c 10.391021,2.965585 12.107326,5.273931 14.449451,6.853352 2.595558,1.750324 7.834568,4.485074 4.329877,9.074338 z"
|
||||||
|
id="path973-9-3-1"
|
||||||
|
sodipodi:nodetypes="scss" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
|
||||||
|
d="m 72.892707,6.7468749 c 7.980077,3.7070851 9.522752,3.9595281 10.517188,8.4997391 1.962552,8.960262 -2.931732,59.541875 -4.564063,65.782031 C 77.622774,85.704215 61.697785,111.71986 51.461458,126.33854 71.486723,113.82936 81.693839,86.666049 82.909268,83.971968 86.747627,75.463979 91.272126,13.970573 90.683182,11.835806 89.929245,9.1029796 81.563522,6.5477024 72.892707,6.7468749 Z"
|
||||||
|
id="path3332"
|
||||||
|
sodipodi:nodetypes="csscssc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
44
items/frontend/public/index.html
Normal file
44
items/frontend/public/index.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<!-- <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> -->
|
||||||
|
<link rel="icon" href="favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="item finder" />
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>Item Finder</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
20
items/frontend/public/manifest.json
Normal file
20
items/frontend/public/manifest.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"short_name": "items.sp-tarkov",
|
||||||
|
"name": "Items Website for sp-tarkov",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "32x32",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "favicon.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "64x64 96x96 128x128 160x160 192x192 512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
3
items/frontend/public/robots.txt
Normal file
3
items/frontend/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
38
items/frontend/src/App.tsx
Normal file
38
items/frontend/src/App.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import {ThemeProvider} from '@mui/material/styles'
|
||||||
|
import {MainPage} from './pages/MainPage'
|
||||||
|
import {ThemeMode} from './state/ThemeMode'
|
||||||
|
import {CssBaseline, useMediaQuery} from '@mui/material'
|
||||||
|
import {useEffect} from "react";
|
||||||
|
import {darkPalette} from "./theme/darkTheme";
|
||||||
|
import {lightPalette} from "./theme/lightTheme";
|
||||||
|
import {getTheme} from "./theme/Theme";
|
||||||
|
import {LocalStorageKeys} from "./dataaccess/SaveKeys";
|
||||||
|
import { useGlobalState } from './state/GlobalState'
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||||||
|
const [preferedColorScheme ,setPreferedColorScheme] = useGlobalState(state => [state.preferedColorScheme, state.setPreferedColorScheme])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const localPreferedTheme = localStorage.getItem(LocalStorageKeys.PREFERED_COLOR_SCHEME);
|
||||||
|
if (localPreferedTheme) {
|
||||||
|
setPreferedColorScheme(localPreferedTheme as ThemeMode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const preferedTheme = prefersDarkMode ? ThemeMode.DARK_MODE : ThemeMode.LIGHT_MODE;
|
||||||
|
setPreferedColorScheme(preferedTheme)
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [prefersDarkMode]) // Need to be only used on prefersDarkMode change
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ThemeProvider theme={getTheme(preferedColorScheme === ThemeMode.DARK_MODE ? darkPalette : lightPalette)}>
|
||||||
|
<CssBaseline/>
|
||||||
|
<MainPage/>
|
||||||
|
</ThemeProvider>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
59
items/frontend/src/components/DarkModeToggle.tsx
Normal file
59
items/frontend/src/components/DarkModeToggle.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Box, IconButton, Theme } from '@mui/material'
|
||||||
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
import Brightness4Icon from '@mui/icons-material/Brightness4'
|
||||||
|
import Brightness7Icon from '@mui/icons-material/Brightness7'
|
||||||
|
import { ThemeMode } from '../state/ThemeMode'
|
||||||
|
import { useGlobalState } from '../state/GlobalState'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import { makeStyles } from '@mui/styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
modeToggleButtonHolder: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: 'primary',
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
iconButton: {
|
||||||
|
ml: 1,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const DarkModeToggle = () => {
|
||||||
|
const theme = useTheme()
|
||||||
|
const classes = useStyles()
|
||||||
|
const [preferedColorScheme, setPreferedColorScheme] = useGlobalState(
|
||||||
|
useCallback(
|
||||||
|
(state) => [state.preferedColorScheme, state.setPreferedColorScheme],
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggleColor = () => {
|
||||||
|
const newTheme =
|
||||||
|
preferedColorScheme === ThemeMode.LIGHT_MODE
|
||||||
|
? ThemeMode.DARK_MODE
|
||||||
|
: ThemeMode.LIGHT_MODE
|
||||||
|
setPreferedColorScheme(newTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box className={classes.modeToggleButtonHolder} id="modeToggleButtonHolder">
|
||||||
|
{theme.palette.mode} mode
|
||||||
|
<IconButton
|
||||||
|
className={classes.iconButton}
|
||||||
|
sx={{ ml: 1 }}
|
||||||
|
onClick={toggleColor}
|
||||||
|
color="inherit"
|
||||||
|
id="modeToggleButton"
|
||||||
|
>
|
||||||
|
{theme.palette.mode === 'dark' ? (
|
||||||
|
<Brightness7Icon />
|
||||||
|
) : (
|
||||||
|
<Brightness4Icon />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
23
items/frontend/src/components/Footer.tsx
Normal file
23
items/frontend/src/components/Footer.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Box, Theme, Typography } from '@mui/material'
|
||||||
|
import { makeStyles } from '@mui/styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
footerHolder: {
|
||||||
|
display: 'flex',
|
||||||
|
flex: '0 1 3vh',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '0 10vw 0 10vw'
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const Footer = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box className={classes.footerHolder}>
|
||||||
|
<Typography>SPT-Aki ©2021 Created by Rev and Shirito</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
86
items/frontend/src/components/Header.tsx
Normal file
86
items/frontend/src/components/Header.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { Box, Link, Theme } from '@mui/material'
|
||||||
|
import { makeStyles } from '@mui/styles'
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useGlobalState } from '../state/GlobalState';
|
||||||
|
import { HeaderForm } from './HeaderForm';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
headerContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
flex: '0 1 3vh',
|
||||||
|
flexDirection: 'row',
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '0 10vw 0 10vw',
|
||||||
|
},
|
||||||
|
linksContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
flexGrow: 2,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
formContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
flexGrow: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
display: 'flex',
|
||||||
|
padding: '0 1vw 0 1vw',
|
||||||
|
height: '100%',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderBottom: `1px solid transparent`,
|
||||||
|
'&:hover': {
|
||||||
|
borderBottom: `1px solid ${theme.palette.action.hover}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const Header = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const websiteLink = useGlobalState(useCallback((state) => state.sptarkovWebsiteUrl,[]))
|
||||||
|
const workshopLink = useGlobalState(useCallback((state) => state.sptarkovWorkshopUrl,[]))
|
||||||
|
const documentationLink = useGlobalState(useCallback((state) => state.sptarkovDocumentationUrl,[]))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box className={classes.headerContainer}>
|
||||||
|
<Box className={classes.linksContainer}>
|
||||||
|
<Link
|
||||||
|
underline="hover"
|
||||||
|
color="inherit"
|
||||||
|
id="website-link"
|
||||||
|
href={websiteLink}
|
||||||
|
className={classes.link}
|
||||||
|
>
|
||||||
|
Website
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
underline="hover"
|
||||||
|
color="inherit"
|
||||||
|
id="workshop-link"
|
||||||
|
href={workshopLink}
|
||||||
|
className={classes.link}
|
||||||
|
>
|
||||||
|
Workshop
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
underline="hover"
|
||||||
|
color="inherit"
|
||||||
|
id="documentation-link"
|
||||||
|
href={documentationLink}
|
||||||
|
className={classes.link}
|
||||||
|
>
|
||||||
|
Documentation
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
<Box className={classes.formContainer}>
|
||||||
|
<HeaderForm/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
29
items/frontend/src/components/HeaderForm.tsx
Normal file
29
items/frontend/src/components/HeaderForm.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { DarkModeToggle } from './DarkModeToggle'
|
||||||
|
import { LocaleSelect } from './LocaleSelect'
|
||||||
|
import { JsonTheme } from './JsonTheme'
|
||||||
|
import { Theme } from '@mui/material'
|
||||||
|
import { makeStyles } from '@mui/styles';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
form: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexGrow: 1,
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
height: '100%'
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const HeaderForm = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form className={classes.form}>
|
||||||
|
<DarkModeToggle />
|
||||||
|
<LocaleSelect />
|
||||||
|
<JsonTheme />
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
69
items/frontend/src/components/JsonTheme.tsx
Normal file
69
items/frontend/src/components/JsonTheme.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
FormControl,
|
||||||
|
MenuItem,
|
||||||
|
Select,
|
||||||
|
Theme,
|
||||||
|
} from '@mui/material'
|
||||||
|
import { makeStyles } from '@mui/styles'
|
||||||
|
import { ReactJsonViewThemes } from '../state/ReactJsonViewThemes'
|
||||||
|
import { LocalStorageKeys } from '../dataaccess/SaveKeys'
|
||||||
|
import { useGlobalState } from '../state/GlobalState'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
jsonHolder: {
|
||||||
|
display: 'flex',
|
||||||
|
flexGrow: 1,
|
||||||
|
padding: '0 0.5vw 0 0.5vw'
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
display: 'flex',
|
||||||
|
flexGrow: 1
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export const JsonTheme = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const [preferedJsonViewerTheme, setPreferedJsonViewerTheme] = useGlobalState(
|
||||||
|
useCallback(
|
||||||
|
(state) => [
|
||||||
|
state.preferedJsonViewerTheme,
|
||||||
|
state.setPreferedJsonViewerTheme,
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box className={classes.jsonHolder}>
|
||||||
|
<FormControl fullWidth variant="standard">
|
||||||
|
<Select
|
||||||
|
displayEmpty
|
||||||
|
className={classes.select}
|
||||||
|
labelId="react-json-view-theme"
|
||||||
|
value={preferedJsonViewerTheme}
|
||||||
|
label="JSON theme"
|
||||||
|
onChange={(evt) => {
|
||||||
|
setPreferedJsonViewerTheme(evt.target.value)
|
||||||
|
localStorage.setItem(
|
||||||
|
LocalStorageKeys.PREFERED_JSON_THEME,
|
||||||
|
evt.target.value,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
id="json-selector"
|
||||||
|
>
|
||||||
|
<MenuItem disabled value="">
|
||||||
|
<em>JSON theme</em>
|
||||||
|
</MenuItem>
|
||||||
|
{ReactJsonViewThemes.map((theme, idx) => (
|
||||||
|
<MenuItem key={idx} value={theme}>
|
||||||
|
{theme}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user