mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-27 11:06:44 +00:00
[UI] Web Prefix (#9334)
* [UI] Change default web prefix - Adjust default from "platform" to "web" - Much more standard prefix * Cleanup * Fixes for playwright tests * Fix unit tests * Refactor base_url into getBaseUrl
This commit is contained in:
parent
832d884c85
commit
662a0b275e
6
.github/workflows/qc_checks.yaml
vendored
6
.github/workflows/qc_checks.yaml
vendored
@ -549,7 +549,7 @@ jobs:
|
|||||||
invoke migrate
|
invoke migrate
|
||||||
|
|
||||||
platform_ui:
|
platform_ui:
|
||||||
name: Tests - Platform UI
|
name: Tests - Web UI
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
needs: ["pre-commit", "paths-filter"]
|
needs: ["pre-commit", "paths-filter"]
|
||||||
@ -618,7 +618,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
slug: inventree/InvenTree
|
slug: inventree/InvenTree
|
||||||
flags: pui
|
flags: web
|
||||||
- name: Upload bundler info
|
- name: Upload bundler info
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
@ -628,7 +628,7 @@ jobs:
|
|||||||
yarn run build
|
yarn run build
|
||||||
|
|
||||||
platform_ui_build:
|
platform_ui_build:
|
||||||
name: Build - UI Platform
|
name: Build - Web UI
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ flag_management:
|
|||||||
statuses:
|
statuses:
|
||||||
- type: project
|
- type: project
|
||||||
target: 40%
|
target: 40%
|
||||||
- name: pui
|
- name: web
|
||||||
carryforward: true
|
carryforward: true
|
||||||
statuses:
|
statuses:
|
||||||
- type: project
|
- type: project
|
||||||
|
@ -73,7 +73,7 @@ You can now set breakpoints and vscode will automatically pause execution if tha
|
|||||||
|
|
||||||
!!! info "React Frontend development"
|
!!! info "React Frontend development"
|
||||||
|
|
||||||
The React frontend requires additional steps to run. Refer to [Platform UI / React](./react-frontend.md)
|
The React frontend requires additional steps to run. Refer to [Web UI / React](./react-frontend.md)
|
||||||
|
|
||||||
### Plugin development
|
### Plugin development
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ title: Template editor
|
|||||||
|
|
||||||
## Template editor
|
## Template editor
|
||||||
|
|
||||||
The Template Editor is integrated into the Admin center of Platform UI (PUI). It allows users to create and edit label and report templates directly with a side by side preview for a more productive workflow.
|
The Template Editor is integrated into the Admin center of the Web UI. It allows users to create and edit label and report templates directly with a side by side preview for a more productive workflow.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ A stock location represents a physical real-world location where *Stock Items* a
|
|||||||
|
|
||||||
### Icons
|
### Icons
|
||||||
|
|
||||||
Stock locations can be assigned custom icons (either directly or through [Stock Location Types](#stock-location-type)). When using PUI there is a custom icon picker component available that can help to select the right icon. However in CUI the icon needs to be entered manually.
|
Stock locations can be assigned custom icons (either directly or through [Stock Location Types](#stock-location-type)). In the web interface there is a custom icon picker component available that can help to select the right icon. However in CUI the icon needs to be entered manually.
|
||||||
|
|
||||||
By default, the tabler icons package (with prefix: `ti`) is available. To manually select an item, search on the [tabler icons](https://tabler.io/icons) page for an icon and copy its name e.g. `bookmark`. Some icons have a filled and an outline version (if no variants are specified, it's an outline variant). Now these values can be put into the format: `<package-prefix>:<icon-name>:<variant>`. E.g. `ti:bookmark:outline` or `ti:bookmark:filled`.
|
By default, the tabler icons package (with prefix: `ti`) is available. To manually select an item, search on the [tabler icons](https://tabler.io/icons) page for an icon and copy its name e.g. `bookmark`. Some icons have a filled and an outline version (if no variants are specified, it's an outline variant). Now these values can be put into the format: `<package-prefix>:<icon-name>:<variant>`. E.g. `ti:bookmark:outline` or `ti:bookmark:filled`.
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@ class AllUserRequire2FAMiddleware(MiddlewareMixin):
|
|||||||
'api-token',
|
'api-token',
|
||||||
# web platform urls
|
# web platform urls
|
||||||
'password_reset_confirm',
|
'password_reset_confirm',
|
||||||
'platform',
|
'web',
|
||||||
'platform-wildcard',
|
'web-wildcard',
|
||||||
'web-assets',
|
'web-assets',
|
||||||
]
|
]
|
||||||
app_names = ['headless']
|
app_names = ['headless']
|
||||||
|
@ -7,7 +7,6 @@ import os
|
|||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
import string
|
import string
|
||||||
import warnings
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
logger = logging.getLogger('inventree')
|
logger = logging.getLogger('inventree')
|
||||||
@ -401,51 +400,33 @@ def get_custom_file(
|
|||||||
|
|
||||||
|
|
||||||
def get_frontend_settings(debug=True):
|
def get_frontend_settings(debug=True):
|
||||||
"""Return a dictionary of settings for the frontend interface.
|
"""Return a dictionary of settings for the frontend interface."""
|
||||||
|
|
||||||
Note that the new config settings use the 'FRONTEND' key,
|
|
||||||
whereas the legacy key was 'PUI' (platform UI) which is now deprecated
|
|
||||||
"""
|
|
||||||
# Legacy settings
|
|
||||||
pui_settings = get_setting(
|
|
||||||
'INVENTREE_PUI_SETTINGS', 'pui_settings', {}, typecast=dict
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(pui_settings) > 0:
|
|
||||||
warnings.warn(
|
|
||||||
"The 'INVENTREE_PUI_SETTINGS' key is deprecated. Please use 'INVENTREE_FRONTEND_SETTINGS' instead",
|
|
||||||
DeprecationWarning,
|
|
||||||
stacklevel=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# New settings
|
# New settings
|
||||||
frontend_settings = get_setting(
|
frontend_settings = get_setting(
|
||||||
'INVENTREE_FRONTEND_SETTINGS', 'frontend_settings', {}, typecast=dict
|
'INVENTREE_FRONTEND_SETTINGS', 'frontend_settings', {}, typecast=dict
|
||||||
)
|
)
|
||||||
|
|
||||||
# Merge settings
|
|
||||||
settings = {**pui_settings, **frontend_settings}
|
|
||||||
|
|
||||||
# Set the base URL
|
# Set the base URL
|
||||||
if 'base_url' not in settings:
|
if 'base_url' not in frontend_settings:
|
||||||
settings['base_url'] = get_setting(
|
frontend_settings['base_url'] = (
|
||||||
'INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'platform'
|
get_setting('INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web')
|
||||||
|
or 'web'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the server list
|
# Set the server list
|
||||||
settings['server_list'] = settings.get('server_list', [])
|
frontend_settings['server_list'] = frontend_settings.get('server_list', [])
|
||||||
|
|
||||||
# Set the debug flag
|
# Set the debug flag
|
||||||
settings['debug'] = debug
|
frontend_settings['debug'] = debug
|
||||||
|
|
||||||
if 'environment' not in settings:
|
if 'environment' not in frontend_settings:
|
||||||
settings['environment'] = 'development' if debug else 'production'
|
frontend_settings['environment'] = 'development' if debug else 'production'
|
||||||
|
|
||||||
if (debug and 'show_server_selector' not in settings) or len(
|
if (debug and 'show_server_selector' not in frontend_settings) or len(
|
||||||
settings['server_list']
|
frontend_settings['server_list']
|
||||||
) == 0:
|
) == 0:
|
||||||
# In debug mode, show server selector by default
|
# In debug mode, show server selector by default
|
||||||
# If no servers are specified, show server selector
|
# If no servers are specified, show server selector
|
||||||
settings['show_server_selector'] = True
|
frontend_settings['show_server_selector'] = True
|
||||||
|
|
||||||
return settings
|
return frontend_settings
|
||||||
|
@ -1039,7 +1039,7 @@ def inheritors(
|
|||||||
|
|
||||||
|
|
||||||
def pui_url(subpath: str) -> str:
|
def pui_url(subpath: str) -> str:
|
||||||
"""Return the URL for a PUI subpath."""
|
"""Return the URL for a web subpath."""
|
||||||
if not subpath.startswith('/'):
|
if not subpath.startswith('/'):
|
||||||
subpath = '/' + subpath
|
subpath = '/' + subpath
|
||||||
return f'/{settings.FRONTEND_URL_BASE}{subpath}'
|
return f'/{settings.FRONTEND_URL_BASE}{subpath}'
|
||||||
|
@ -46,4 +46,4 @@ class ViewTests(InvenTreeTestCase):
|
|||||||
f'/accounts/login/?next=/&login={self.username}&password={self.password}'
|
f'/accounts/login/?next=/&login={self.username}&password={self.password}'
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, '/platform')
|
self.assertEqual(response.url, '/web')
|
||||||
|
@ -31,7 +31,7 @@ class BuildTestSimple(InvenTreeTestCase):
|
|||||||
def test_url(self):
|
def test_url(self):
|
||||||
"""Test URL lookup."""
|
"""Test URL lookup."""
|
||||||
b1 = Build.objects.get(pk=1)
|
b1 = Build.objects.get(pk=1)
|
||||||
self.assertEqual(b1.get_absolute_url(), '/platform/manufacturing/build-order/1')
|
self.assertEqual(b1.get_absolute_url(), '/web/manufacturing/build-order/1')
|
||||||
|
|
||||||
def test_is_complete(self):
|
def test_is_complete(self):
|
||||||
"""Test build completion status."""
|
"""Test build completion status."""
|
||||||
|
@ -59,7 +59,7 @@ class CompanySimpleTest(TestCase):
|
|||||||
def test_company_url(self):
|
def test_company_url(self):
|
||||||
"""Test the detail URL for a company."""
|
"""Test the detail URL for a company."""
|
||||||
c = Company.objects.get(pk=1)
|
c = Company.objects.get(pk=1)
|
||||||
self.assertEqual(c.get_absolute_url(), '/platform/purchasing/manufacturer/1')
|
self.assertEqual(c.get_absolute_url(), '/web/purchasing/manufacturer/1')
|
||||||
|
|
||||||
def test_image_renamer(self):
|
def test_image_renamer(self):
|
||||||
"""Test the company image upload functionality."""
|
"""Test the company image upload functionality."""
|
||||||
|
@ -43,7 +43,7 @@ class OrderTest(TestCase, ExchangeRateMixin):
|
|||||||
for pk in range(1, 8):
|
for pk in range(1, 8):
|
||||||
order = PurchaseOrder.objects.get(pk=pk)
|
order = PurchaseOrder.objects.get(pk=pk)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
order.get_absolute_url(), f'/platform/purchasing/purchase-order/{pk}'
|
order.get_absolute_url(), f'/web/purchasing/purchase-order/{pk}'
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(order.reference, f'PO-{pk:04d}')
|
self.assertEqual(order.reference, f'PO-{pk:04d}')
|
||||||
|
@ -127,9 +127,7 @@ class CategoryTest(TestCase):
|
|||||||
|
|
||||||
def test_url(self):
|
def test_url(self):
|
||||||
"""Test that the PartCategory URL works."""
|
"""Test that the PartCategory URL works."""
|
||||||
self.assertEqual(
|
self.assertEqual(self.capacitors.get_absolute_url(), '/web/part/category/3')
|
||||||
self.capacitors.get_absolute_url(), '/platform/part/category/3'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_part_count(self):
|
def test_part_count(self):
|
||||||
"""Test that the Category part count works."""
|
"""Test that the Category part count works."""
|
||||||
|
@ -245,7 +245,7 @@ class PartTest(TestCase):
|
|||||||
def test_attributes(self):
|
def test_attributes(self):
|
||||||
"""Test Part attributes."""
|
"""Test Part attributes."""
|
||||||
self.assertEqual(self.r1.name, 'R_2K2_0805')
|
self.assertEqual(self.r1.name, 'R_2K2_0805')
|
||||||
self.assertEqual(self.r1.get_absolute_url(), '/platform/part/3')
|
self.assertEqual(self.r1.get_absolute_url(), '/web/part/3')
|
||||||
|
|
||||||
def test_category(self):
|
def test_category(self):
|
||||||
"""Test PartCategory path."""
|
"""Test PartCategory path."""
|
||||||
|
@ -285,7 +285,7 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase):
|
|||||||
response.data['stocklocation']['api_url'], '/api/stock/location/5/'
|
response.data['stocklocation']['api_url'], '/api/stock/location/5/'
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.data['stocklocation']['web_url'], '/platform/stock/location/5'
|
response.data['stocklocation']['web_url'], '/web/stock/location/5'
|
||||||
)
|
)
|
||||||
self.assertEqual(response.data['plugin'], 'InvenTreeBarcode')
|
self.assertEqual(response.data['plugin'], 'InvenTreeBarcode')
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase):
|
|||||||
response.data['stocklocation']['api_url'], '/api/stock/location/5/'
|
response.data['stocklocation']['api_url'], '/api/stock/location/5/'
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.data['stocklocation']['web_url'], '/platform/stock/location/5'
|
response.data['stocklocation']['web_url'], '/web/stock/location/5'
|
||||||
)
|
)
|
||||||
self.assertEqual(response.data['plugin'], 'InvenTreeBarcode')
|
self.assertEqual(response.data['plugin'], 'InvenTreeBarcode')
|
||||||
|
|
||||||
|
@ -175,8 +175,8 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
|
|||||||
# Test might return one of two results, depending on test env
|
# Test might return one of two results, depending on test env
|
||||||
# If INVENTREE_SITE_URL is not set in the CI environment, the link will be relative
|
# If INVENTREE_SITE_URL is not set in the CI environment, the link will be relative
|
||||||
options = [
|
options = [
|
||||||
f'<a href="http://localhost:8000/platform/part/{obj.pk}">test</a>',
|
f'<a href="http://localhost:8000/web/part/{obj.pk}">test</a>',
|
||||||
f'<a href="/platform/part/{obj.pk}">test</a>',
|
f'<a href="/web/part/{obj.pk}">test</a>',
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertIn(link, options)
|
self.assertIn(link, options)
|
||||||
|
@ -262,8 +262,8 @@ class StockTest(StockTestBase):
|
|||||||
def test_url(self):
|
def test_url(self):
|
||||||
"""Test get_absolute_url function."""
|
"""Test get_absolute_url function."""
|
||||||
it = StockItem.objects.get(pk=2)
|
it = StockItem.objects.get(pk=2)
|
||||||
self.assertEqual(it.get_absolute_url(), '/platform/stock/item/2')
|
self.assertEqual(it.get_absolute_url(), '/web/stock/item/2')
|
||||||
self.assertEqual(self.home.get_absolute_url(), '/platform/stock/location/1')
|
self.assertEqual(self.home.get_absolute_url(), '/web/stock/location/1')
|
||||||
|
|
||||||
def test_strings(self):
|
def test_strings(self):
|
||||||
"""Test str function."""
|
"""Test str function."""
|
||||||
|
@ -273,7 +273,7 @@ class GetAuthToken(GenericAPIView):
|
|||||||
|
|
||||||
data = {'token': token.key, 'name': token.name, 'expiry': token.expiry}
|
data = {'token': token.key, 'name': token.name, 'expiry': token.expiry}
|
||||||
|
|
||||||
# Ensure that the users session is logged in (PUI -> CUI login)
|
# Ensure that the users session is logged in
|
||||||
if not get_user(request).is_authenticated:
|
if not get_user(request).is_authenticated:
|
||||||
login(
|
login(
|
||||||
request, user, backend='django.contrib.auth.backends.ModelBackend'
|
request, user, backend='django.contrib.auth.backends.ModelBackend'
|
||||||
|
@ -113,7 +113,7 @@ class UserAPITests(InvenTreeAPITestCase):
|
|||||||
def test_login_redirect(self):
|
def test_login_redirect(self):
|
||||||
"""Test login redirect endpoint."""
|
"""Test login redirect endpoint."""
|
||||||
response = self.get(reverse('api-login-redirect'), expected_code=302)
|
response = self.get(reverse('api-login-redirect'), expected_code=302)
|
||||||
self.assertEqual(response.url, '/platform/logged-in/')
|
self.assertEqual(response.url, '/web/logged-in/')
|
||||||
|
|
||||||
|
|
||||||
class UserTokenTests(InvenTreeAPITestCase):
|
class UserTokenTests(InvenTreeAPITestCase):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Tests for PUI backend stuff."""
|
"""Tests for web backend functionality."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@ -68,7 +68,7 @@ class TemplateTagTest(InvenTreeTestCase):
|
|||||||
self.assertSettings(rsp)
|
self.assertSettings(rsp)
|
||||||
|
|
||||||
# No base_url
|
# No base_url
|
||||||
envs = {'INVENTREE_PUI_URL_BASE': ''}
|
envs = {'INVENTREE_FRONTEND_URL_BASE': ''}
|
||||||
with mock.patch.dict(os.environ, envs):
|
with mock.patch.dict(os.environ, envs):
|
||||||
rsp = get_frontend_settings()
|
rsp = get_frontend_settings()
|
||||||
self.assertSettings(rsp)
|
self.assertSettings(rsp)
|
||||||
@ -79,7 +79,9 @@ class TemplateTagTest(InvenTreeTestCase):
|
|||||||
self.assertTrue(rsp['show_server_selector'])
|
self.assertTrue(rsp['show_server_selector'])
|
||||||
|
|
||||||
# No debug, serverlist -> no selector
|
# No debug, serverlist -> no selector
|
||||||
envs = {'INVENTREE_PUI_SETTINGS': json.dumps({'server_list': ['aa', 'bb']})}
|
envs = {
|
||||||
|
'INVENTREE_FRONTEND_SETTINGS': json.dumps({'server_list': ['aa', 'bb']})
|
||||||
|
}
|
||||||
with mock.patch.dict(os.environ, envs):
|
with mock.patch.dict(os.environ, envs):
|
||||||
rsp = get_frontend_settings(False)
|
rsp = get_frontend_settings(False)
|
||||||
self.assertNotIn('show_server_selector', rsp)
|
self.assertNotIn('show_server_selector', rsp)
|
||||||
|
@ -16,8 +16,8 @@ urlpatterns = [
|
|||||||
spa_view,
|
spa_view,
|
||||||
name='password_reset_confirm',
|
name='password_reset_confirm',
|
||||||
),
|
),
|
||||||
re_path('.*', spa_view, name='platform-wildcard'),
|
re_path('.*', spa_view, name='web-wildcard'),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
path(settings.FRONTEND_URL_BASE, spa_view, name='platform'),
|
path(settings.FRONTEND_URL_BASE, spa_view, name='web'),
|
||||||
]
|
]
|
||||||
|
@ -23,7 +23,7 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import type { ModelType } from '../../enums/ModelType';
|
import type { ModelType } from '../../enums/ModelType';
|
||||||
import { navigateToLink } from '../../functions/navigation';
|
import { navigateToLink } from '../../functions/navigation';
|
||||||
import { getDetailUrl } from '../../functions/urls';
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import { base_url } from '../../main';
|
import { getBaseUrl } from '../../main';
|
||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import { Boundary } from '../Boundary';
|
import { Boundary } from '../Boundary';
|
||||||
@ -67,7 +67,7 @@ function NotificationEntry({
|
|||||||
>
|
>
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
<Anchor
|
<Anchor
|
||||||
href={link ? `/${base_url}${link}` : '#'}
|
href={link ? `/${getBaseUrl()}${link}` : '#'}
|
||||||
underline='hover'
|
underline='hover'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
onClick={(event: any) => {
|
onClick={(event: any) => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { base_url } from '../main';
|
import { getBaseUrl } from '../main';
|
||||||
import { cancelEvent } from './events';
|
import { cancelEvent } from './events';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -11,7 +11,7 @@ export const navigateToLink = (link: string, navigate: any, event: any) => {
|
|||||||
|
|
||||||
if (event?.ctrlKey || event?.shiftKey) {
|
if (event?.ctrlKey || event?.shiftKey) {
|
||||||
// Open the link in a new tab
|
// Open the link in a new tab
|
||||||
const url = `/${base_url}${link}`;
|
const url = `/${getBaseUrl()}${link}`;
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
} else {
|
} else {
|
||||||
// Navigate internally
|
// Navigate internally
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ModelInformationDict } from '../components/render/ModelType';
|
import { ModelInformationDict } from '../components/render/ModelType';
|
||||||
import type { ModelType } from '../enums/ModelType';
|
import type { ModelType } from '../enums/ModelType';
|
||||||
import { base_url } from '../main';
|
import { getBaseUrl } from '../main';
|
||||||
import { useLocalState } from '../states/LocalState';
|
import { useLocalState } from '../states/LocalState';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +19,7 @@ export function getDetailUrl(
|
|||||||
|
|
||||||
if (!!pk && modelInfo && modelInfo.url_detail) {
|
if (!!pk && modelInfo && modelInfo.url_detail) {
|
||||||
const url = modelInfo.url_detail.replace(':pk', pk.toString());
|
const url = modelInfo.url_detail.replace(':pk', pk.toString());
|
||||||
const base = base_url;
|
const base = getBaseUrl();
|
||||||
|
|
||||||
if (absolute && base) {
|
if (absolute && base) {
|
||||||
return `/${base}${url}`;
|
return `/${base}${url}`;
|
||||||
|
@ -89,7 +89,8 @@ if (window.INVENTREE_SETTINGS.sentry_dsn) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const base_url = window.INVENTREE_SETTINGS.base_url || 'platform';
|
export const getBaseUrl = (): string =>
|
||||||
|
window.INVENTREE_SETTINGS?.base_url || 'web';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
@ -99,7 +100,7 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
|||||||
|
|
||||||
// Redirect to base url if on /
|
// Redirect to base url if on /
|
||||||
if (window.location.pathname === '/') {
|
if (window.location.pathname === '/') {
|
||||||
window.location.replace(`/${base_url}`);
|
window.location.replace(`/${getBaseUrl()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.React = React;
|
window.React = React;
|
||||||
|
@ -5,7 +5,7 @@ import { api, queryClient } from '../App';
|
|||||||
import { ApiProvider } from '../contexts/ApiContext';
|
import { ApiProvider } from '../contexts/ApiContext';
|
||||||
import { ThemeContext } from '../contexts/ThemeContext';
|
import { ThemeContext } from '../contexts/ThemeContext';
|
||||||
import { defaultHostList } from '../defaults/defaultHostList';
|
import { defaultHostList } from '../defaults/defaultHostList';
|
||||||
import { base_url } from '../main';
|
import { getBaseUrl } from '../main';
|
||||||
import { routes } from '../router';
|
import { routes } from '../router';
|
||||||
import { useLocalState } from '../states/LocalState';
|
import { useLocalState } from '../states/LocalState';
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ export default function DesktopAppView() {
|
|||||||
return (
|
return (
|
||||||
<ApiProvider client={queryClient} api={api}>
|
<ApiProvider client={queryClient} api={api}>
|
||||||
<ThemeContext>
|
<ThemeContext>
|
||||||
<BrowserRouter basename={base_url}>{routes}</BrowserRouter>
|
<BrowserRouter basename={getBaseUrl()}>{routes}</BrowserRouter>
|
||||||
</ThemeContext>
|
</ThemeContext>
|
||||||
</ApiProvider>
|
</ApiProvider>
|
||||||
);
|
);
|
||||||
|
@ -23,7 +23,7 @@ export default function MobileAppView() {
|
|||||||
</Title>
|
</Title>
|
||||||
<Text>
|
<Text>
|
||||||
<Trans>
|
<Trans>
|
||||||
Platform UI is optimized for Tablets and Desktops, you can use
|
InvenTree UI is optimized for Tablets and Desktops, you can use
|
||||||
the official app for a mobile experience.
|
the official app for a mobile experience.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export const classicUrl = 'http://127.0.0.1:8000';
|
export const classicUrl = 'http://127.0.0.1:8000';
|
||||||
|
|
||||||
export const apiUrl = `${classicUrl}/api`;
|
export const apiUrl = `${classicUrl}/api`;
|
||||||
export const baseUrl = './platform';
|
export const baseUrl = './web';
|
||||||
export const loginUrl = `${baseUrl}/login`;
|
export const loginUrl = `${baseUrl}/login`;
|
||||||
export const logoutUrl = `${baseUrl}/logout`;
|
export const logoutUrl = `${baseUrl}/logout`;
|
||||||
export const homeUrl = `${baseUrl}/home`;
|
export const homeUrl = `${baseUrl}/home`;
|
||||||
|
@ -11,11 +11,11 @@ export const doLogin = async (page, username?: string, password?: string) => {
|
|||||||
|
|
||||||
await navigate(page, logoutUrl);
|
await navigate(page, logoutUrl);
|
||||||
await expect(page).toHaveTitle(/^InvenTree.*$/);
|
await expect(page).toHaveTitle(/^InvenTree.*$/);
|
||||||
await page.waitForURL('**/platform/login');
|
await page.waitForURL('**/web/login');
|
||||||
await page.getByLabel('username').fill(username);
|
await page.getByLabel('username').fill(username);
|
||||||
await page.getByLabel('password').fill(password);
|
await page.getByLabel('password').fill(password);
|
||||||
await page.getByRole('button', { name: 'Log in' }).click();
|
await page.getByRole('button', { name: 'Log in' }).click();
|
||||||
await page.waitForURL('**/platform/home');
|
await page.waitForURL('**/web/home');
|
||||||
await page.waitForTimeout(250);
|
await page.waitForTimeout(250);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ export const doQuickLogin = async (
|
|||||||
url = url ?? baseUrl;
|
url = url ?? baseUrl;
|
||||||
|
|
||||||
await navigate(page, `${url}/login?login=${username}&password=${password}`);
|
await navigate(page, `${url}/login?login=${username}&password=${password}`);
|
||||||
await page.waitForURL('**/platform/home');
|
await page.waitForURL('**/web/home');
|
||||||
|
|
||||||
await page.getByLabel('navigation-menu').waitFor({ timeout: 5000 });
|
await page.getByLabel('navigation-menu').waitFor({ timeout: 5000 });
|
||||||
await page.getByText(/InvenTree Demo Server -/).waitFor();
|
await page.getByText(/InvenTree Demo Server -/).waitFor();
|
||||||
@ -45,5 +45,5 @@ export const doQuickLogin = async (
|
|||||||
|
|
||||||
export const doLogout = async (page) => {
|
export const doLogout = async (page) => {
|
||||||
await navigate(page, 'logout');
|
await navigate(page, 'logout');
|
||||||
await page.waitForURL('**/platform/login');
|
await page.waitForURL('**/web/login');
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@ test('Modals - Admin', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
||||||
await page.getByRole('button', { name: 'Close' }).click();
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
await page.waitForURL('**/platform/home');
|
await page.waitForURL('**/web/home');
|
||||||
|
|
||||||
// use license info
|
// use license info
|
||||||
await page.getByLabel('open-spotlight').click();
|
await page.getByLabel('open-spotlight').click();
|
||||||
|
@ -416,7 +416,7 @@ test('Parts - Revision', async ({ page }) => {
|
|||||||
.getByRole('option', { name: 'Thumbnail Green Round Table No stock' })
|
.getByRole('option', { name: 'Thumbnail Green Round Table No stock' })
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page.waitForURL('**/platform/part/101/**');
|
await page.waitForURL('**/web/part/101/**');
|
||||||
await page.getByText('Select Part Revision').waitFor();
|
await page.getByText('Select Part Revision').waitFor();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ test('Sales Orders - Tabs', async ({ page }) => {
|
|||||||
await doQuickLogin(page);
|
await doQuickLogin(page);
|
||||||
|
|
||||||
await navigate(page, 'sales/index/');
|
await navigate(page, 'sales/index/');
|
||||||
await page.waitForURL('**/platform/sales/**');
|
await page.waitForURL('**/web/sales/**');
|
||||||
|
|
||||||
await loadTab(page, 'Sales Orders');
|
await loadTab(page, 'Sales Orders');
|
||||||
await page.waitForURL('**/platform/sales/index/salesorders');
|
await page.waitForURL('**/web/sales/index/salesorders');
|
||||||
await loadTab(page, 'Return Orders');
|
await loadTab(page, 'Return Orders');
|
||||||
|
|
||||||
// Customers
|
// Customers
|
||||||
|
@ -13,16 +13,16 @@ test('Stock - Basic Tests', async ({ page }) => {
|
|||||||
await doQuickLogin(page);
|
await doQuickLogin(page);
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
await navigate(page, 'stock/location/index/');
|
||||||
await page.waitForURL('**/platform/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
await loadTab(page, 'Location Details');
|
await loadTab(page, 'Location Details');
|
||||||
await page.waitForURL('**/platform/stock/location/index/details');
|
await page.waitForURL('**/web/stock/location/index/details');
|
||||||
|
|
||||||
await loadTab(page, 'Stock Items');
|
await loadTab(page, 'Stock Items');
|
||||||
await page.getByText('1551ABK').first().click();
|
await page.getByText('1551ABK').first().click();
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Stock', exact: true }).click();
|
await page.getByRole('tab', { name: 'Stock', exact: true }).click();
|
||||||
await page.waitForURL('**/platform/stock/**');
|
await page.waitForURL('**/web/stock/**');
|
||||||
await loadTab(page, 'Stock Locations');
|
await loadTab(page, 'Stock Locations');
|
||||||
await page.getByRole('cell', { name: 'Electronics Lab' }).first().click();
|
await page.getByRole('cell', { name: 'Electronics Lab' }).first().click();
|
||||||
await loadTab(page, 'Default Parts');
|
await loadTab(page, 'Default Parts');
|
||||||
@ -43,7 +43,7 @@ test('Stock - Location Tree', async ({ page }) => {
|
|||||||
await doQuickLogin(page);
|
await doQuickLogin(page);
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
await navigate(page, 'stock/location/index/');
|
||||||
await page.waitForURL('**/platform/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
await loadTab(page, 'Location Details');
|
await loadTab(page, 'Location Details');
|
||||||
|
|
||||||
await page.getByLabel('nav-breadcrumb-action').click();
|
await page.getByLabel('nav-breadcrumb-action').click();
|
||||||
|
@ -10,7 +10,7 @@ test('Quick Command', async ({ page }) => {
|
|||||||
await page.getByPlaceholder('Search...').fill('Dashboard');
|
await page.getByPlaceholder('Search...').fill('Dashboard');
|
||||||
await page.getByPlaceholder('Search...').press('Tab');
|
await page.getByPlaceholder('Search...').press('Tab');
|
||||||
await page.getByPlaceholder('Search...').press('Enter');
|
await page.getByPlaceholder('Search...').press('Enter');
|
||||||
await page.waitForURL('**/platform/home');
|
await page.waitForURL('**/web/home');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Quick Command - No Keys', async ({ page }) => {
|
test('Quick Command - No Keys', async ({ page }) => {
|
||||||
@ -23,7 +23,7 @@ test('Quick Command - No Keys', async ({ page }) => {
|
|||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page.getByText('InvenTree Demo Server - ').waitFor();
|
await page.getByText('InvenTree Demo Server - ').waitFor();
|
||||||
await page.waitForURL('**/platform/home');
|
await page.waitForURL('**/web/home');
|
||||||
|
|
||||||
// Use navigation menu
|
// Use navigation menu
|
||||||
await page.getByLabel('open-spotlight').click();
|
await page.getByLabel('open-spotlight').click();
|
||||||
@ -55,7 +55,7 @@ test('Quick Command - No Keys', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
||||||
await page.getByRole('button', { name: 'Close' }).click();
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
await page.waitForURL('**/platform/home');
|
await page.waitForURL('**/web/home');
|
||||||
|
|
||||||
// use license info
|
// use license info
|
||||||
await page.getByLabel('open-spotlight').click();
|
await page.getByLabel('open-spotlight').click();
|
||||||
|
@ -6,7 +6,7 @@ import { doQuickLogin } from './login';
|
|||||||
test('Forms - Stock Item Validation', async ({ page }) => {
|
test('Forms - Stock Item Validation', async ({ page }) => {
|
||||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
await doQuickLogin(page, 'steven', 'wizardstaff');
|
||||||
await navigate(page, 'stock/location/index/stock-items');
|
await navigate(page, 'stock/location/index/stock-items');
|
||||||
await page.waitForURL('**/platform/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
// Create new stock item form
|
// Create new stock item form
|
||||||
await page.getByLabel('action-button-add-stock-item').click();
|
await page.getByLabel('action-button-add-stock-item').click();
|
||||||
|
@ -13,7 +13,7 @@ test('Login - Basic Test', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Ally Access' }).click();
|
await page.getByRole('button', { name: 'Ally Access' }).click();
|
||||||
await page.getByRole('menuitem', { name: 'Logout' }).click();
|
await page.getByRole('menuitem', { name: 'Logout' }).click();
|
||||||
|
|
||||||
await page.waitForURL('**/platform/login');
|
await page.waitForURL('**/web/login');
|
||||||
await page.getByLabel('username');
|
await page.getByLabel('username');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -27,13 +27,13 @@ test('Login - Quick Test', async ({ page }) => {
|
|||||||
|
|
||||||
// Go to the dashboard
|
// Go to the dashboard
|
||||||
await navigate(page, '');
|
await navigate(page, '');
|
||||||
await page.waitForURL('**/platform');
|
await page.waitForURL('**/web');
|
||||||
|
|
||||||
await page.getByText('InvenTree Demo Server - ').waitFor();
|
await page.getByText('InvenTree Demo Server - ').waitFor();
|
||||||
|
|
||||||
// Logout (via URL)
|
// Logout (via URL)
|
||||||
await navigate(page, 'logout');
|
await navigate(page, 'logout');
|
||||||
await page.waitForURL('**/platform/login');
|
await page.waitForURL('**/web/login');
|
||||||
await page.getByLabel('username');
|
await page.getByLabel('username');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ test('Login - Failures', async ({ page }) => {
|
|||||||
// Navigate to the 'login' page
|
// Navigate to the 'login' page
|
||||||
await navigate(page, logoutUrl);
|
await navigate(page, logoutUrl);
|
||||||
await expect(page).toHaveTitle(/^InvenTree.*$/);
|
await expect(page).toHaveTitle(/^InvenTree.*$/);
|
||||||
await page.waitForURL('**/platform/login');
|
await page.waitForURL('**/web/login');
|
||||||
|
|
||||||
// Attempt login with invalid credentials
|
// Attempt login with invalid credentials
|
||||||
await page.getByLabel('login-username').fill('invalid user');
|
await page.getByLabel('login-username').fill('invalid user');
|
||||||
|
@ -12,7 +12,7 @@ test('Label Printing', async ({ page }) => {
|
|||||||
await doQuickLogin(page);
|
await doQuickLogin(page);
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
await navigate(page, 'stock/location/index/');
|
||||||
await page.waitForURL('**/platform/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
await loadTab(page, 'Stock Items');
|
await loadTab(page, 'Stock Items');
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ test('Report Printing', async ({ page }) => {
|
|||||||
await doQuickLogin(page);
|
await doQuickLogin(page);
|
||||||
|
|
||||||
await navigate(page, 'stock/location/index/');
|
await navigate(page, 'stock/location/index/');
|
||||||
await page.waitForURL('**/platform/stock/location/**');
|
await page.waitForURL('**/web/stock/location/**');
|
||||||
|
|
||||||
// Navigate to a specific PurchaseOrder
|
// Navigate to a specific PurchaseOrder
|
||||||
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
||||||
|
@ -39,7 +39,7 @@ test('Settings - Language / Color', async ({ page }) => {
|
|||||||
// .click();
|
// .click();
|
||||||
|
|
||||||
await page.getByRole('tab', { name: 'Dashboard' }).click();
|
await page.getByRole('tab', { name: 'Dashboard' }).click();
|
||||||
await page.waitForURL('**/platform/home');
|
await page.waitForURL('**/web/home');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Settings - User theme', async ({ page }) => {
|
test('Settings - User theme', async ({ page }) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user