Skip to content

Commit

Permalink
Platform UI - React integration (inventree#5011)
Browse files Browse the repository at this point in the history
* Add SPA views for react inventree#2789

* split up frontend urls

* Add settings for frontend url loading

* add new UI scaffold

* remove tracking insert

* add platform app

* ensure static indexes work too

* add lingui

* add lingui config

* add mgmt tasks

* add base locales

* settings for frontend dev

* fix typo

* update deps

* add pre-commit

* add eslint

* add testing scaffold

* fix paths

* remove error - tests trip correctly

* merge workflow

* cleanup samples

* use name inline with other tests

* Add real worl frontend tests

* setup env

* tun migrations first

* optimize setup time

* setup demo dataset

* optimize run setup

* add test for class ui

* rename

* fix typo

* and another typo

* do install

* run migrations first

* fix name

* cleanup

* use other credentials

* use other credentials

* fix qc

* move envs to qc

* remove create_site

* reduce testing env

* fix test

* fix test call

* allaccess user

* add ui plattform check

* add better check

* remove unneeded env

* enable debug

* reduce wait time

* also build frontend on static

* add sort plugin

* fix order

* run pre-commit fixes

* add node min version

* Docker container (inventree#129)

* Fix allocation check for completing build order (inventree#5199)

- Allocation check only applies to untracked line items

* docker dev

Install required node packages to docker development image

* add import order settings

* cleanout built ui

* remove default arg from build

* remove eslint

* optimize svg

* add build step for plattform UI

* fix install command

* use alpine commands

* do not use cache when creating image

---------

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
  • Loading branch information
matmair and SchrodingersGat authored Jul 18, 2023
1 parent b717011 commit 3e37469
Show file tree
Hide file tree
Showing 44 changed files with 2,817 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
ARG WORKSPACE="/workspaces/InvenTree"

# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="none"
ARG NODE_VERSION="18"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi

# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
Expand Down
1 change: 1 addition & 0 deletions .devcontainer/postCreateCommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ python3 -m venv dev/venv
pip install invoke
invoke update
invoke setup-dev
invoke frontend-install

# remove existing gitconfig created by "Avoiding Dubious Ownership" step
# so that it gets copied from host to the container to have your global
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/qc_checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
outputs:
server: ${{ steps.filter.outputs.server }}
migrations: ${{ steps.filter.outputs.migrations }}
frontend: ${{ steps.filter.outputs.frontend }}

steps:
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0
Expand All @@ -43,6 +44,8 @@ jobs:
migrations:
- '**/migrations/**'
- '.github/workflows**'
frontend:
- 'src/frontend/**'
pep_style:
name: Style [Python]
Expand Down Expand Up @@ -387,3 +390,38 @@ jobs:
cp test-db/stable_0.12.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3
chmod +rw /home/runner/work/InvenTree/db.sqlite3
invoke migrate
plattform_ui:
name: Tests - UI Platform
runs-on: ubuntu-20.04
timeout-minutes: 60
needs: paths-filter
if: needs.paths-filter.outputs.frontend == 'true'
env:
INVENTREE_DB_ENGINE: sqlite3
INVENTREE_DB_NAME: /home/runner/work/InvenTree/db.sqlite3
INVENTREE_DEBUG: True
INVENTREE_PLUGINS_ENABLED: false

steps:
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0
- name: Environment Setup
uses: ./.github/actions/setup
with:
npm: true
install: true
update: true
- name: Set up test data
run: invoke setup-test -i
- name: Install dependencies
run: cd src/frontend && yarn install
- name: Install Playwright Browsers
run: cd src/frontend && npx playwright install --with-deps
- name: Run Playwright tests
run: cd src/frontend && npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: src/frontend/playwright-report/
retention-days: 30
20 changes: 20 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,23 @@ repos:
docs/docs/javascripts/.*|
docs/docs/webfonts/.*
)$
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "v3.0.0-alpha.9-for-vscode"
hooks:
- id: prettier
files: ^src/frontend/.*\.(js|jsx|ts|tsx)$
additional_dependencies:
- "prettier@^2.4.1"
- "@trivago/prettier-plugin-sort-imports"
- repo: https://github.com/pre-commit/mirrors-eslint
rev: "v8.42.0"
hooks:
- id: eslint
additional_dependencies:
- eslint@^8.41.0
- eslint-config-google@^0.14.0
- eslint-plugin-react@6.10.3
- babel-eslint@6.1.2
- "@typescript-eslint/eslint-plugin@latest"
- "@typescript-eslint/parser"
files: ^src/frontend/.*\.(js|jsx|ts|tsx)$
9 changes: 5 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,11 @@ The HEAD of the "stable" branch represents the latest stable release code.
## Environment
### Target version
We are currently targeting:
| Name | Minimum version |
|---|---|
| Python | 3.9 |
| Django | 3.2 |
| Name | Minimum version | Note |
|---|---| --- |
| Python | 3.9 | |
| Django | 3.2 | |
| Node | 18 | Only needed for frontend development |

### Auto creating updates
The following tools can be used to auto-upgrade syntax that was depreciated in new versions:
Expand Down
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ RUN chmod +x init.sh

ENTRYPOINT ["/bin/sh", "./init.sh"]

# Frontend builder image:
FROM inventree_base as frontend

RUN apk add --no-cache --update nodejs npm && npm install -g yarn
COPY InvenTree ${INVENTREE_HOME}/InvenTree
COPY src ${INVENTREE_HOME}/src
COPY tasks.py ${INVENTREE_HOME}/tasks.py
RUN cd ${INVENTREE_HOME}/InvenTree && inv frontend-compile

# InvenTree production image:
# - Copies required files from local directory
# - Starts a gunicorn webserver
Expand All @@ -111,6 +120,7 @@ ENV INVENTREE_COMMIT_DATE="${commit_date}"

# Copy source code
COPY InvenTree ./InvenTree
COPY --from=frontend ${INVENTREE_HOME}/InvenTree/web/static/web ./InvenTree/web/static/web

# Launch the production server
# TODO: Work out why environment variables cannot be interpolated in this command
Expand All @@ -120,6 +130,9 @@ CMD gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8000 --chdir ./Inve

FROM inventree_base as dev

# Install nodejs / npm / yarn
RUN apk add --no-cache --update nodejs npm && npm cache clean -f && npm install -g n && n stable && npm install -g yarn

# The development image requires the source code to be mounted to /home/inventree/
# So from here, we don't actually "do" anything, apart from some file management

Expand Down
6 changes: 5 additions & 1 deletion InvenTree/InvenTree/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
INVENTREE_NEWS_URL = 'https://inventree.org/news/feed.atom'

# Determine if we are running in "test" mode e.g. "manage.py test"
TESTING = 'test' in sys.argv
TESTING = 'test' in sys.argv or 'TESTING' in os.environ

if TESTING:

Expand Down Expand Up @@ -76,6 +76,9 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = get_boolean_setting('INVENTREE_DEBUG', 'debug', True)

ENABLE_CLASSIC_FRONTEND = get_boolean_setting('INVENTREE_CLASSIC_FRONTEND', 'classic_frontend', True)
ENABLE_PLATFORM_FRONTEND = get_boolean_setting('INVENTREE_PLATFORM_FRONTEND', 'platform_frontend', True)

# Configure logging settings
log_level = get_setting('INVENTREE_LOG_LEVEL', 'log_level', 'WARNING')

Expand Down Expand Up @@ -203,6 +206,7 @@
'stock.apps.StockConfig',
'users.apps.UsersConfig',
'plugin.apps.PluginAppConfig',
'web',
'generic',
'InvenTree.apps.InvenTreeConfig', # InvenTree app runs last

Expand Down
18 changes: 17 additions & 1 deletion InvenTree/InvenTree/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from stock.api import stock_api_urls
from stock.urls import stock_urls
from users.api import user_urls
from web.urls import urlpatterns as platform_urls

from .api import APISearchView, InfoView, NotFoundView
from .magic_login import GetSimpleLoginView
Expand Down Expand Up @@ -162,7 +163,7 @@
re_path(r'^api-doc/', SpectacularRedocView.as_view(url_name='schema'), name='api-doc'),
]

frontendpatterns = [
classic_frontendpatterns = [

# Apps
re_path(r'^build/', include(build_urls)),
Expand Down Expand Up @@ -205,6 +206,21 @@
re_path(r'^accounts/', include('allauth.urls')), # included urlpatterns
]


new_frontendpatterns = [
# Platform urls
re_path(r'^platform/', include(platform_urls)),

]

# Load patterns for frontend according to settings
frontendpatterns = []
if settings.ENABLE_CLASSIC_FRONTEND:
frontendpatterns.append(re_path('', include(classic_frontendpatterns)))
if settings.ENABLE_PLATFORM_FRONTEND:
frontendpatterns.append(re_path('', include(new_frontendpatterns)))


# Append custom plugin URLs (if plugin support is enabled)
if settings.PLUGINS_ENABLED:
frontendpatterns.append(get_plugin_urls())
Expand Down
6 changes: 6 additions & 0 deletions InvenTree/config_template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ database:
# Use the environment variable INVENTREE_DEBUG
debug: True

# Set enabled frontends
# Use the environment variable INVENTREE_CLASSIC_FRONTEND
# classic_frontend: True
# Use the environment variable INVENTREE_PLATFORM_FRONTEND
# platform_frontend: True

# Configure the system logging level
# Use environment variable INVENTREE_LOG_LEVEL
# Options: DEBUG / INFO / WARNING / ERROR / CRITICAL
Expand Down
18 changes: 18 additions & 0 deletions InvenTree/web/templates/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% load spa_helper %}
{% load inventree_extras %}

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% inventree_instance_name %}</title>
</head>

<body>
<div id="root"></div>
{% spa_bundle %}
</body>

</html>
36 changes: 36 additions & 0 deletions InvenTree/web/templatetags/spa_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Template tag to render SPA imports."""
import json
from logging import getLogger
from pathlib import Path

from django import template
from django.conf import settings
from django.utils.safestring import mark_safe

logger = getLogger("gwaesser_backend")
register = template.Library()


@register.simple_tag
def spa_bundle():
"""Render SPA bundle."""
manifest = Path(__file__).parent.parent.joinpath("static/web/manifest.json")

if not manifest.exists():
logger.error("Manifest file not found")
return

manifest_data = json.load(manifest.open())
index = manifest_data.get("index.html")

dynmanic_files = index.get("dynamicImports", [])
imports_files = "".join(
[
f'<script type="module" src="{settings.STATIC_URL}web/{manifest_data[file]["file"]}"></script>'
for file in dynmanic_files
]
)

return mark_safe(
f"""<script type="module" src="{settings.STATIC_URL}web/{index['file']}"></script>{imports_files}"""
)
26 changes: 26 additions & 0 deletions InvenTree/web/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""URLs for web app."""
from django.conf import settings
from django.shortcuts import redirect
from django.urls import path, re_path
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import TemplateView


class RedirectAssetView(TemplateView):
"""View to redirect to static asset."""

def get(self, request, *args, **kwargs):
"""Redirect to static asset."""
return redirect(
f"{settings.STATIC_URL}web/assets/{kwargs['path']}", permanent=True
)


spa_view = ensure_csrf_cookie(TemplateView.as_view(template_name="web/index.html"))


urlpatterns = [
path("assets/<path:path>", RedirectAssetView.as_view()),
re_path(r"^(?P<path>.*)/$", spa_view),
path("", spa_view),
]
8 changes: 8 additions & 0 deletions src/frontend/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"presets": [
"@babel/preset-react"
],
"plugins": [
"macros"
]
}
7 changes: 7 additions & 0 deletions src/frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-env node */
module.exports = {
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
};
29 changes: 29 additions & 0 deletions src/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# Testing
/test-results/
/playwright-report/
/playwright/.cache/
19 changes: 19 additions & 0 deletions src/frontend/.linguirc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"locales": ["en", "de", "hu", "pseudo-LOCALE"],
"catalogs": [{
"path": "src/locales/{locale}/messages",
"include": ["src"],
"exclude": ["**/node_modules/**"]
}],
"format": "po",
"orderBy": "origin",
"sourceLocale": "en",
"pseudoLocale": "pseudo-LOCALE",
"fallbackLocales": {
"default": "en",
"pseudo-LOCALE": "en"
},
"extractBabelOptions": {
"presets": ["@babel/preset-typescript"]
}
}
9 changes: 9 additions & 0 deletions src/frontend/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": true,
"trailingComma": "none",
"singleQuote": true,
"printWidth": 80,
"importOrder": ["<THIRD_PARTY_MODULES>", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true
}
Loading

0 comments on commit 3e37469

Please sign in to comment.