Set up frontend dev tooling (#6)
* Add eslint for linting js (https://github.com/ikreymer/browsertrix-cloud/pull/7) * Add prettier for formatting frontend files (https://github.com/ikreymer/browsertrix-cloud/pull/9) * Add frontend testing framework (https://github.com/ikreymer/browsertrix-cloud/pull/10) closes #4, closes #5
This commit is contained in:
parent
57a4b6b46f
commit
0f97724ad0
27
frontend/.gitignore
vendored
Normal file
27
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
## editors
|
||||
/.idea
|
||||
/.vscode
|
||||
|
||||
## system files
|
||||
.DS_Store
|
||||
|
||||
## npm
|
||||
/node_modules/
|
||||
/npm-debug.log
|
||||
|
||||
## testing
|
||||
/coverage/
|
||||
|
||||
## temp folders
|
||||
/.tmp/
|
||||
/tmp/
|
||||
|
||||
# build
|
||||
/dist/
|
||||
|
||||
# dotenv
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
storybook-static
|
||||
custom-elements.json
|
||||
42
frontend/README.md
Normal file
42
frontend/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Browsertrix Cloud frontend
|
||||
|
||||
## Quickstart
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
|
||||
Start the dev server:
|
||||
|
||||
```sh
|
||||
yarn start-dev
|
||||
```
|
||||
|
||||
This will open `localhost:9870` in a new tab in your default browser.
|
||||
|
||||
## Scripts
|
||||
|
||||
| `yarn <name>` | |
|
||||
| ------------- | ------------------------------------------------------------------- |
|
||||
| `start-dev` | runs app in development server, reloading on file changes |
|
||||
| `test` | runs tests in chromium with playwright |
|
||||
| `build-dev` | bundles app and outputs it in `dist` directory |
|
||||
| `build` | bundles app app, optimized for production, and outputs it to `dist` |
|
||||
| `lint` | find and fix auto-fixable javascript errors |
|
||||
| `format` | formats js, html and css files |
|
||||
|
||||
## Testing
|
||||
|
||||
Tests assertions are written in [Chai](https://www.chaijs.com/api/bdd/).
|
||||
|
||||
To watch for file changes while running tests:
|
||||
```sh
|
||||
yarn test --watch
|
||||
```
|
||||
|
||||
To run tests in multiple browsers:
|
||||
```sh
|
||||
yarn test --browsers chromium firefox webkit
|
||||
```
|
||||
258
frontend/dist/main.js
vendored
258
frontend/dist/main.js
vendored
File diff suppressed because one or more lines are too long
@ -6,6 +6,8 @@
|
||||
<script src="/main.js"></script>
|
||||
</head>
|
||||
<body class="min-w-screen min-h-screen">
|
||||
<browsertrix-app class="flex flex-col min-h-screen bg-blue-400"></browsertrix-app>
|
||||
<browsertrix-app
|
||||
class="flex flex-col min-h-screen bg-blue-400"
|
||||
></browsertrix-app>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"axios": "^0.22.0",
|
||||
"daisyui": "^1.14.2",
|
||||
@ -12,18 +13,55 @@
|
||||
"tailwindcss": "^2.2.16"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "web-test-runner \"src/**/*.test.js\" --node-resolve --playwright --browsers chromium",
|
||||
"build": "webpack --mode production",
|
||||
"build-dev": "webpack --mode development",
|
||||
"start-dev": "webpack serve --mode=development"
|
||||
"start-dev": "webpack serve --mode=development",
|
||||
"lint": "eslint --fix \"src/**/*.js\"",
|
||||
"format": "prettier --write \"**/*.{js,html,css}\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esm-bundle/chai": "^4.3.4-fix.0",
|
||||
"@web/dev-server-import-maps": "^0.0.6",
|
||||
"@web/test-runner": "^0.13.22",
|
||||
"@web/test-runner-playwright": "^0.8.8",
|
||||
"autoprefixer": "^10.3.6",
|
||||
"css-loader": "^6.3.0",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-lit": "^1.6.1",
|
||||
"eslint-plugin-wc": "^1.3.2",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"postcss": "^8.3.8",
|
||||
"postcss-loader": "^6.1.1",
|
||||
"prettier": "^2.4.1",
|
||||
"style-loader": "^3.3.0",
|
||||
"webpack": "^5.56.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"eslintConfig": {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es2017": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"plugins": [
|
||||
"lit"
|
||||
],
|
||||
"rules": {
|
||||
"no-restricted-globals": [
|
||||
2,
|
||||
"event",
|
||||
"error"
|
||||
],
|
||||
"no-unused-vars": "warn"
|
||||
}
|
||||
},
|
||||
"prettier": {}
|
||||
}
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('tailwindcss'),
|
||||
require('autoprefixer'),
|
||||
]
|
||||
plugins: [require("tailwindcss"), require("autoprefixer")],
|
||||
};
|
||||
|
||||
10
frontend/src/__mocks__/css.js
Normal file
10
frontend/src/__mocks__/css.js
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Use to mock css files in tests.
|
||||
*
|
||||
* Usage in web-test-runner.config.mjs:
|
||||
* importMap: {
|
||||
* imports: {
|
||||
* 'styles.css': '/src/__mocks__/css.js'
|
||||
* },
|
||||
* },
|
||||
*/
|
||||
@ -1,9 +1,7 @@
|
||||
import { LiteElement, APIRouter, html } from "./utils";
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
class App extends LiteElement
|
||||
{
|
||||
export class App extends LiteElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.authState = null;
|
||||
@ -14,11 +12,11 @@ class App extends LiteElement
|
||||
}
|
||||
|
||||
this.router = new APIRouter({
|
||||
"home": "/",
|
||||
"login": "/log-in",
|
||||
home: "/",
|
||||
login: "/log-in",
|
||||
"my-account": "/my-account",
|
||||
"archive-info": "/archive/:aid",
|
||||
"archive-info-tab": "/archive/:aid/:tab"
|
||||
"archive-info-tab": "/archive/:aid/:tab",
|
||||
});
|
||||
|
||||
this.viewState = this.router.match(window.location.pathname);
|
||||
@ -27,8 +25,8 @@ class App extends LiteElement
|
||||
static get properties() {
|
||||
return {
|
||||
viewState: { type: Object },
|
||||
authState: { type: Object }
|
||||
}
|
||||
authState: { type: Object },
|
||||
};
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
@ -50,7 +48,7 @@ class App extends LiteElement
|
||||
if (this.viewState._route === "login") {
|
||||
this.clearAuthState();
|
||||
}
|
||||
//console.log(this.view._route, window.location.href);
|
||||
//console.log(this.view._route, window.location.href);
|
||||
window.history.pushState(this.viewState, "", this.viewState._path);
|
||||
}
|
||||
|
||||
@ -61,45 +59,72 @@ class App extends LiteElement
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.renderNavBar()}
|
||||
<div class="w-full h-full px-12 py-12">
|
||||
${this.renderPage()}
|
||||
</div>
|
||||
${this.renderNavBar()}
|
||||
<div class="w-full h-full px-12 py-12">${this.renderPage()}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderNavBar() {
|
||||
return html`
|
||||
<div class="navbar shadow-lg bg-neutral text-neutral-content">
|
||||
<div class="flex-1 px-2 mx-2">
|
||||
<a href="/" class="link link-hover text-lg font-bold" @click="${this.navLink}">Browsertrix Cloud</a>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
${this.authState ? html`
|
||||
<a class="link link-hover font-bold px-4" href="/my-account" @click="${this.navLink}">My Account</a>
|
||||
<button class="btn btn-error" @click="${this.onLogOut}">Log Out</button>`
|
||||
: html`
|
||||
<button class="btn ${this.viewState._route !== "login" ? "btn-primary" : "btn-ghost"}" @click="${this.onNeedLogin}">Log In</button>
|
||||
`}
|
||||
<div class="navbar shadow-lg bg-neutral text-neutral-content">
|
||||
<div class="flex-1 px-2 mx-2">
|
||||
<a
|
||||
href="/"
|
||||
class="link link-hover text-lg font-bold"
|
||||
@click="${this.navLink}"
|
||||
>Browsertrix Cloud</a
|
||||
>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
${this.authState
|
||||
? html` <a
|
||||
class="link link-hover font-bold px-4"
|
||||
href="/my-account"
|
||||
@click="${this.navLink}"
|
||||
>My Account</a
|
||||
>
|
||||
<button class="btn btn-error" @click="${this.onLogOut}">
|
||||
Log Out
|
||||
</button>`
|
||||
: html`
|
||||
<button
|
||||
class="btn ${this.viewState._route !== "login"
|
||||
? "btn-primary"
|
||||
: "btn-ghost"}"
|
||||
@click="${this.onNeedLogin}"
|
||||
>
|
||||
Log In
|
||||
</button>
|
||||
`}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderPage() {
|
||||
switch (this.viewState._route) {
|
||||
case "login":
|
||||
return html`<log-in @logged-in="${this.onLoggedIn}">`;
|
||||
return html`<log-in @logged-in="${this.onLoggedIn}"></log-in>`;
|
||||
|
||||
case "home":
|
||||
return html`<div>Home</div>`;
|
||||
|
||||
case "my-account":
|
||||
return html`<my-account @navigate="${this.onNavigateTo}" @need-login="${this.onNeedLogin}" .authState="${this.authState}"></my-account>`;
|
||||
return html`<my-account
|
||||
@navigate="${this.onNavigateTo}"
|
||||
@need-login="${this.onNeedLogin}"
|
||||
.authState="${this.authState}"
|
||||
></my-account>`;
|
||||
|
||||
case "archive-info":
|
||||
case "archive-info-tab":
|
||||
return html`<btrix-archive @navigate="${this.onNavigateTo}" .authState="${this.authState}" .viewState="${this.viewState}" aid="${this.viewState.aid}" tab="${this.viewState.tab || "running"}"></btrix-archive>`;
|
||||
return html`<btrix-archive
|
||||
@navigate="${this.onNavigateTo}"
|
||||
.authState="${this.authState}"
|
||||
.viewState="${this.viewState}"
|
||||
aid="${this.viewState.aid}"
|
||||
tab="${this.viewState.tab || "running"}"
|
||||
></btrix-archive>`;
|
||||
|
||||
default:
|
||||
return html`<div>Not Found!</div>`;
|
||||
@ -114,7 +139,7 @@ class App extends LiteElement
|
||||
onLoggedIn(event) {
|
||||
this.authState = {
|
||||
username: event.detail.username,
|
||||
headers: {"Authorization": event.detail.auth}
|
||||
headers: { Authorization: event.detail.auth },
|
||||
};
|
||||
window.localStorage.setItem("authState", JSON.stringify(this.authState));
|
||||
this.navigate("/my-account");
|
||||
@ -135,10 +160,8 @@ class App extends LiteElement
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
class LogIn extends LiteElement
|
||||
{
|
||||
class LogIn extends LiteElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.loginError = "";
|
||||
@ -146,40 +169,50 @@ class LogIn extends LiteElement
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
loginError: { type: String }
|
||||
}
|
||||
loginError: { type: String },
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="hero min-h-screen bg-blue-400">
|
||||
<div class="text-center hero-content bg-base-200 shadow-2xl rounded-xl px-16 py-8">
|
||||
|
||||
<div class="max-w-md">
|
||||
<form action="" @submit="${this.onSubmit}">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">User</span>
|
||||
</label>
|
||||
<input id="username" name="username" type="text" placeholder="Username" class="input input-bordered">
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Password</span>
|
||||
</label>
|
||||
<input id="password" name="password" type="password" placeholder="Password" class="input input-bordered">
|
||||
</div>
|
||||
<div class="form-control py-4">
|
||||
<button class="btn btn-primary" type="submit">Log In</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="login-error" class="text-red-600">
|
||||
${this.loginError}
|
||||
<div class="hero min-h-screen bg-blue-400">
|
||||
<div
|
||||
class="text-center hero-content bg-base-200 shadow-2xl rounded-xl px-16 py-8"
|
||||
>
|
||||
<div class="max-w-md">
|
||||
<form action="" @submit="${this.onSubmit}">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">User</span>
|
||||
</label>
|
||||
<input
|
||||
id="username"
|
||||
name="username"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
class="input input-bordered"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Password</span>
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
class="input input-bordered"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control py-4">
|
||||
<button class="btn btn-primary" type="submit">Log In</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="login-error" class="text-red-600">${this.loginError}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -193,9 +226,13 @@ class LogIn extends LiteElement
|
||||
params.set("username", username);
|
||||
params.set("password", this.querySelector("#password").value);
|
||||
|
||||
const headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||
const headers = { "Content-Type": "application/x-www-form-urlencoded" };
|
||||
|
||||
const resp = await fetch("/api/auth/jwt/login", {headers, method: "POST", body: params.toString()});
|
||||
const resp = await fetch("/api/auth/jwt/login", {
|
||||
headers,
|
||||
method: "POST",
|
||||
body: params.toString(),
|
||||
});
|
||||
if (resp.status !== 200) {
|
||||
this.loginError = "Sorry, invalid credentials";
|
||||
return;
|
||||
@ -205,12 +242,11 @@ class LogIn extends LiteElement
|
||||
const data = await resp.json();
|
||||
if (data.token_type === "bearer" && data.access_token) {
|
||||
const auth = "Bearer " + data.access_token;
|
||||
const detail = {auth, username};
|
||||
this.dispatchEvent(new CustomEvent("logged-in", {detail}));
|
||||
const detail = { auth, username };
|
||||
this.dispatchEvent(new CustomEvent("logged-in", { detail }));
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
if (!this.auth) {
|
||||
@ -219,10 +255,8 @@ class LogIn extends LiteElement
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
class MyAccount extends LiteElement
|
||||
{
|
||||
class MyAccount extends LiteElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.archiveList = [];
|
||||
@ -232,8 +266,8 @@ class MyAccount extends LiteElement
|
||||
return {
|
||||
authState: { type: Object },
|
||||
archiveList: { type: Array },
|
||||
id: { type: String }
|
||||
}
|
||||
id: { type: String },
|
||||
};
|
||||
}
|
||||
|
||||
async firstUpdated() {
|
||||
@ -251,22 +285,31 @@ class MyAccount extends LiteElement
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="container bg-base-200 m-auto border rounded-lg px-8 py-8">
|
||||
<h2 class="text-2xl font-bold">Your Archives</h2>
|
||||
${this.archiveList.map(archive => html`
|
||||
<div class="card mt-6 ml-6 border rounded-none border-gray-600 hover:bg-gray-300">
|
||||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
<span class="mr-4">${archive.name}</span>${this.getAccessValue(archive)}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a class="btn btn-primary" href="/archive/${archive.id}" @click="${this.navLink}">View Archive</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`)}
|
||||
</div>
|
||||
<div class="container bg-base-200 m-auto border rounded-lg px-8 py-8">
|
||||
<h2 class="text-2xl font-bold">Your Archives</h2>
|
||||
${this.archiveList.map(
|
||||
(archive) => html`
|
||||
<div
|
||||
class="card mt-6 ml-6 border rounded-none border-gray-600 hover:bg-gray-300"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
<span class="mr-4">${archive.name}</span
|
||||
>${this.getAccessValue(archive)}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a
|
||||
class="btn btn-primary"
|
||||
href="/archive/${archive.id}"
|
||||
@click="${this.navLink}"
|
||||
>View Archive</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -283,56 +326,78 @@ class MyAccount extends LiteElement
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
class Archive extends LiteElement
|
||||
{
|
||||
class Archive extends LiteElement {
|
||||
static get properties() {
|
||||
return {
|
||||
authState: { type: Object },
|
||||
aid: {type: String},
|
||||
tab: {type: String},
|
||||
viewState: { type: Object }
|
||||
}
|
||||
aid: { type: String },
|
||||
tab: { type: String },
|
||||
viewState: { type: Object },
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const aid = this.aid;
|
||||
const tab = this.tab || "running";
|
||||
return html`
|
||||
<div class="container bg-base-200 m-auto border shadow-xl rounded-lg px-8 py-8">
|
||||
<div class="tabs tabs-boxed">
|
||||
<a href="/archive/${aid}/running" class="tab ${tab === "running" ? 'tab-active' : ''}" @click="${this.navLink}">Crawls Running</a>
|
||||
<a href="/archive/${aid}/finished" class="tab ${tab === "finished" ? 'tab-active' : ''}" @click="${this.navLink}">Finished</a>
|
||||
<a href="/archive/${aid}/configs" class="tab ${tab === "configs" ? 'tab-active' : ''}" @click="${this.navLink}">Crawl Configs</a>
|
||||
<div
|
||||
class="container bg-base-200 m-auto border shadow-xl rounded-lg px-8 py-8"
|
||||
>
|
||||
<div class="tabs tabs-boxed">
|
||||
<a
|
||||
href="/archive/${aid}/running"
|
||||
class="tab ${tab === "running" ? "tab-active" : ""}"
|
||||
@click="${this.navLink}"
|
||||
>Crawls Running</a
|
||||
>
|
||||
<a
|
||||
href="/archive/${aid}/finished"
|
||||
class="tab ${tab === "finished" ? "tab-active" : ""}"
|
||||
@click="${this.navLink}"
|
||||
>Finished</a
|
||||
>
|
||||
<a
|
||||
href="/archive/${aid}/configs"
|
||||
class="tab ${tab === "configs" ? "tab-active" : ""}"
|
||||
@click="${this.navLink}"
|
||||
>Crawl Configs</a
|
||||
>
|
||||
</div>
|
||||
${tab === "configs"
|
||||
? html`<btrix-archive-configs
|
||||
.archive=${this}
|
||||
></btrix-archive-configs>`
|
||||
: ""}
|
||||
</div>
|
||||
${tab === "configs" ?
|
||||
html`<btrix-archive-configs .archive=${this}></btrix-archive-configs>` : ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
class ArchiveConfigs extends LiteElement
|
||||
{
|
||||
class ArchiveConfigs extends LiteElement {
|
||||
static get properties() {
|
||||
return {
|
||||
archive: { type: Object },
|
||||
configs: { type: Array }
|
||||
}
|
||||
configs: { type: Array },
|
||||
};
|
||||
}
|
||||
|
||||
async firstUpdated() {
|
||||
const res = await this.apiFetch(`/archives/${this.archive.aid}/crawlconfigs`, this.archive.authState);
|
||||
const res = await this.apiFetch(
|
||||
`/archives/${this.archive.aid}/crawlconfigs`,
|
||||
this.archive.authState
|
||||
);
|
||||
this.configs = res.crawl_configs;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<div>Archive Configs!</div>
|
||||
${this.configs && this.configs.map((config) => html`
|
||||
<div>${config.crawlCount} ${config.config.seeds}</div>
|
||||
`)}
|
||||
`;
|
||||
${this.configs &&
|
||||
this.configs.map(
|
||||
(config) => html`
|
||||
<div>${config.crawlCount} ${config.config.seeds}</div>
|
||||
`
|
||||
)} `;
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,4 +407,3 @@ customElements.define("log-in", LogIn);
|
||||
customElements.define("my-account", MyAccount);
|
||||
customElements.define("btrix-archive", Archive);
|
||||
customElements.define("btrix-archive-configs", ArchiveConfigs);
|
||||
|
||||
|
||||
9
frontend/src/index.test.js
Normal file
9
frontend/src/index.test.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { expect } from "@esm-bundle/chai";
|
||||
|
||||
import { App } from "./index.js";
|
||||
|
||||
describe("App", () => {
|
||||
it("should exist", () => {
|
||||
expect(App).to.exist;
|
||||
});
|
||||
});
|
||||
@ -3,25 +3,29 @@ import "tailwindcss/tailwind.css";
|
||||
import { LitElement, html } from "lit";
|
||||
import { Path } from "path-parser";
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
export class LiteElement extends LitElement
|
||||
{
|
||||
export class LiteElement extends LitElement {
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
navTo(url) {
|
||||
this.dispatchEvent(new CustomEvent("navigate", {detail: url}));
|
||||
this.dispatchEvent(new CustomEvent("navigate", { detail: url }));
|
||||
}
|
||||
|
||||
navLink(event) {
|
||||
event.preventDefault();
|
||||
this.dispatchEvent(new CustomEvent("navigate", {detail: event.currentTarget.href, bubbles: true, composed: true}));
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate", {
|
||||
detail: event.currentTarget.href,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async apiFetch(path, auth) {
|
||||
const resp = await fetch("/api" + path, {headers: auth.headers});
|
||||
const resp = await fetch("/api" + path, { headers: auth.headers });
|
||||
if (resp.status !== 200) {
|
||||
this.navTo("/log-in");
|
||||
throw new Error("logged out");
|
||||
@ -30,7 +34,6 @@ export class LiteElement extends LitElement
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
export class APIRouter {
|
||||
constructor(paths) {
|
||||
@ -54,8 +57,8 @@ export class APIRouter {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return {_route: null, _path: path};
|
||||
|
||||
return { _route: null, _path: path };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
|
||||
mode: "jit",
|
||||
|
||||
purge: {
|
||||
content: ['./*.html', './src/*.js'],
|
||||
content: ["./*.html", "./src/*.js"],
|
||||
options: {
|
||||
safelist: [
|
||||
/data-theme$/,
|
||||
]
|
||||
safelist: [/data-theme$/],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('daisyui')
|
||||
],
|
||||
plugins: [require("daisyui")],
|
||||
extract: {
|
||||
include: ['./src/*.js'],
|
||||
include: ["./src/*.js"],
|
||||
},
|
||||
};
|
||||
|
||||
15
frontend/web-test-runner.config.mjs
Normal file
15
frontend/web-test-runner.config.mjs
Normal file
@ -0,0 +1,15 @@
|
||||
import { importMapsPlugin } from '@web/dev-server-import-maps';
|
||||
|
||||
export default {
|
||||
plugins: [
|
||||
importMapsPlugin({
|
||||
inject: {
|
||||
importMap: {
|
||||
imports: {
|
||||
'tailwindcss/tailwind.css': '/src/__mocks__/css.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
// webpack.config.js
|
||||
const path = require("path")
|
||||
|
||||
const path = require("path");
|
||||
const ESLintPlugin = require("eslint-webpack-plugin");
|
||||
|
||||
const backendUrl = new URL("http://btrix.cloud/");
|
||||
|
||||
@ -9,7 +9,7 @@ module.exports = {
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "main.js",
|
||||
publicPath: "/"
|
||||
publicPath: "/",
|
||||
},
|
||||
|
||||
module: {
|
||||
@ -24,6 +24,7 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
devServer: {
|
||||
watchFiles: ["src/*.js"],
|
||||
open: true,
|
||||
@ -32,18 +33,28 @@ module.exports = {
|
||||
static: {
|
||||
directory: path.join(__dirname),
|
||||
//publicPath: "/",
|
||||
watch: true
|
||||
watch: true,
|
||||
},
|
||||
historyApiFallback: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
"/api": {
|
||||
target: backendUrl.href,
|
||||
headers: {
|
||||
'Host': backendUrl.host
|
||||
},
|
||||
pathRewrite: { '^/api': '' },
|
||||
Host: backendUrl.host,
|
||||
},
|
||||
pathRewrite: { "^/api": "" },
|
||||
},
|
||||
},
|
||||
port: 9870
|
||||
port: 9870,
|
||||
},
|
||||
}
|
||||
|
||||
plugins: [
|
||||
// Lint js files
|
||||
new ESLintPlugin({
|
||||
// lint only changed files:
|
||||
lintDirtyModulesOnly: true,
|
||||
// enable to auto-fix source files:
|
||||
// fix: true
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
1825
frontend/yarn.lock
1825
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user