mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-22 01:52:17 -05:00
client_v2: add ESLint + wire lint/format/typecheck into lefthook
client_v2 has matured, so enforce its checks like the old client: - Add ESLint (typescript-eslint + eslint-plugin-svelte) with a flat config; `lint` now runs prettier --check and eslint. - Add a "Client v2" group to lefthook pre-commit and ci: eslint, prettier, and svelte-check type checking (svelte-kit sync first). - Exclude generated paraglide output from prettier. Fix the linting warnings this surfaced: drop dead vars and an unused svelte-ignore, mark intentional reactive reads with `void`, remove a useless children snippet, and clean up useless string-literal mustaches. Disable svelte/no-navigation-without-resolve (base-path resolution is centralized in params.ts helpers, so the rule only false-positives and wrapping call sites would double-apply the base path). Keep svelte/prefer-svelte-reactivity on with per-line disables at the transient-local Set/Map sites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
52b0580df9
commit
5eb660f420
21 files changed
+1728
-59
No files matched your search
@@ -6,3 +6,6 @@ build
|
|||||||
.svelte-kit
|
.svelte-kit
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
|
# Generated code (paraglide i18n compiler output — also gitignored)
|
||||||
|
src/lib/paraglide
|
||||||
+2
-2
@@ -82,7 +82,7 @@ Everything viewed is subscribed to and live-updates. The seam is `api/live.ts`
|
|||||||
- **`inventory`** is a reactive normalized **cache**. `api/liveSync.ts` runs one
|
- **`inventory`** is a reactive normalized **cache**. `api/liveSync.ts` runs one
|
||||||
central subscription per resource and applies remote events to it, so every
|
central subscription per resource and applies remote events to it, so every
|
||||||
cache-backed view (inspectors, locations) is live automatically.
|
cache-backed view (inspectors, locations) is live automatically.
|
||||||
- The **paginated list** fetches server-paged data *outside* the cache, so it
|
- The **paginated list** fetches server-paged data _outside_ the cache, so it
|
||||||
keeps its **own** subscription and refetches the current page on relevant
|
keeps its **own** subscription and refetches the current page on relevant
|
||||||
events.
|
events.
|
||||||
- Local edits patch the cache **and** emit a `local` event (mirroring the server
|
- Local edits patch the cache **and** emit a `local` event (mirroring the server
|
||||||
@@ -101,7 +101,7 @@ CSS custom properties in `src/app.css`.
|
|||||||
- **Desktop** — two panes: the filament/spool list on the left, a live inspector
|
- **Desktop** — two panes: the filament/spool list on the left, a live inspector
|
||||||
on the right.
|
on the right.
|
||||||
- **Mobile** — the list becomes full-width and front-and-centre; selecting an item
|
- **Mobile** — the list becomes full-width and front-and-centre; selecting an item
|
||||||
slides the *same* inspector up as a bottom-sheet **Drawer**
|
slides the _same_ inspector up as a bottom-sheet **Drawer**
|
||||||
(`src/lib/components/Drawer.svelte`). The top-bar search/nav reflow onto their
|
(`src/lib/components/Drawer.svelte`). The top-bar search/nav reflow onto their
|
||||||
own rows and the "Add spools" button becomes a FAB.
|
own rows and the "Add spools" button becomes a FAB.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import svelte from 'eslint-plugin-svelte';
|
||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import globals from 'globals';
|
||||||
|
import ts from 'typescript-eslint';
|
||||||
|
import svelteConfig from './svelte.config.js';
|
||||||
|
|
||||||
|
export default ts.config(
|
||||||
|
// Generated, tooling-managed, and build-output directories are not linted.
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
'build/',
|
||||||
|
'.svelte-kit/',
|
||||||
|
'dist/',
|
||||||
|
'package/',
|
||||||
|
'src/lib/paraglide/', // auto-generated by inlang paraglide-js
|
||||||
|
'project.inlang/', // inlang-managed
|
||||||
|
'locales/' // Weblate-managed translation files
|
||||||
|
]
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
...ts.configs.recommended,
|
||||||
|
...svelte.configs.recommended,
|
||||||
|
prettier,
|
||||||
|
...svelte.configs.prettier,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.browser, ...globals.node }
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Typed as `no-undef`; TypeScript already reports undefined identifiers.
|
||||||
|
'no-undef': 'off',
|
||||||
|
// Base-path resolution is centralized in src/lib/library/params.ts helpers
|
||||||
|
// (selectHref/libraryHref/etc. already call resolve() from $app/paths), and
|
||||||
|
// generic components take a pre-resolved href prop. This rule can't see through
|
||||||
|
// those helpers, so it only produces false positives here — and blindly wrapping
|
||||||
|
// call sites in resolve() would double-apply the deploy base path.
|
||||||
|
'svelte/no-navigation-without-resolve': 'off'
|
||||||
|
// svelte/prefer-svelte-reactivity is left ON to guard against in-place mutation of
|
||||||
|
// reactive Set/Map. The current hits are all transient computation-local collections
|
||||||
|
// and are silenced with per-line eslint-disable comments at each site.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
extraFileExtensions: ['.svelte'],
|
||||||
|
parser: ts.parser,
|
||||||
|
svelteConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
Generated
+1586
File diff suppressed because it is too large.
Load diff
@@ -13,19 +13,26 @@
|
|||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"lint": "prettier --check ."
|
"lint": "prettier --check . && eslint .",
|
||||||
|
"lint:eslint": "eslint ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.39.5",
|
||||||
"@inlang/paraglide-js": "^2.18.2",
|
"@inlang/paraglide-js": "^2.18.2",
|
||||||
"@sveltejs/adapter-static": "^3.0.6",
|
"@sveltejs/adapter-static": "^3.0.6",
|
||||||
"@sveltejs/kit": "^2.8.1",
|
"@sveltejs/kit": "^2.8.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||||
"@types/qrcode": "^1.5.6",
|
"@types/qrcode": "^1.5.6",
|
||||||
|
"eslint": "^9.39.5",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-svelte": "^3.22.0",
|
||||||
|
"globals": "^16.5.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.7",
|
"prettier-plugin-svelte": "^3.2.7",
|
||||||
"svelte": "^5.1.9",
|
"svelte": "^5.1.9",
|
||||||
"svelte-check": "^4.0.5",
|
"svelte-check": "^4.0.5",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
|
"typescript-eslint": "^8.65.0",
|
||||||
"vite": "^5.4.10"
|
"vite": "^5.4.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -37,4 +37,4 @@
|
|||||||
"lt",
|
"lt",
|
||||||
"tr"
|
"tr"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -162,6 +162,7 @@
|
|||||||
]).then(([vn, localMats, extMats]) => {
|
]).then(([vn, localMats, extMats]) => {
|
||||||
vendorNames = vn;
|
vendorNames = vn;
|
||||||
const specs: typeof materialSpecs = {};
|
const specs: typeof materialSpecs = {};
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local, not reactive state
|
||||||
const names = new Set<string>(localMats);
|
const names = new Set<string>(localMats);
|
||||||
// Seed with the built-in common materials first…
|
// Seed with the built-in common materials first…
|
||||||
for (const m of COMMON_MATERIALS) {
|
for (const m of COMMON_MATERIALS) {
|
||||||
@@ -388,9 +389,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetName = $derived(
|
|
||||||
creating ? nf.name.trim() || m['add.newFilament']() : chosen ? cName(chosen) : ''
|
|
||||||
);
|
|
||||||
let summary = $derived(chosen || creating ? m['add.summary']({ count: countN }) : '');
|
let summary = $derived(chosen || creating ? m['add.summary']({ count: countN }) : '');
|
||||||
|
|
||||||
// --- validation ---------------------------------------------------------
|
// --- validation ---------------------------------------------------------
|
||||||
@@ -520,11 +518,11 @@
|
|||||||
{#each externalResults as ext (ext.id)}
|
{#each externalResults as ext (ext.id)}
|
||||||
<button class="res-item" onclick={() => choose({ source: 'external', ext })}>
|
<button class="res-item" onclick={() => choose({ source: 'external', ext })}>
|
||||||
<Swatch
|
<Swatch
|
||||||
colors={externalColors(ext)}
|
colors={externalColors(ext)}
|
||||||
direction={externalDirection(ext)}
|
direction={externalDirection(ext)}
|
||||||
size={18}
|
size={18}
|
||||||
radius={5}
|
radius={5}
|
||||||
/>
|
/>
|
||||||
<div class="res-name">
|
<div class="res-name">
|
||||||
<span class="rn">{ext.name}</span>
|
<span class="rn">{ext.name}</span>
|
||||||
<span class="rs">{ext.manufacturer} · {ext.material}</span>
|
<span class="rs">{ext.manufacturer} · {ext.material}</span>
|
||||||
|
|||||||
@@ -9,13 +9,7 @@
|
|||||||
let { value, placeholder = '—', mono = false, oninput }: Props = $props();
|
let { value, placeholder = '—', mono = false, oninput }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input
|
<input class="edit" class:mono {value} {placeholder} oninput={(e) => oninput?.(e.currentTarget.value)} />
|
||||||
class="edit"
|
|
||||||
class:mono
|
|
||||||
{value}
|
|
||||||
{placeholder}
|
|
||||||
oninput={(e) => oninput?.(e.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.edit {
|
.edit {
|
||||||
|
|||||||
@@ -58,7 +58,11 @@
|
|||||||
{#if pageCount > 1}
|
{#if pageCount > 1}
|
||||||
<div class="nums">
|
<div class="nums">
|
||||||
{#if hrefFor && page > 1}
|
{#if hrefFor && page > 1}
|
||||||
<a class="pg nav" href={hrefFor(page - 1)} data-sveltekit-keepfocus data-sveltekit-noscroll
|
<a
|
||||||
|
class="pg nav"
|
||||||
|
href={hrefFor(page - 1)}
|
||||||
|
data-sveltekit-keepfocus
|
||||||
|
data-sveltekit-noscroll
|
||||||
aria-label={m['pagination.prev']()}><ChevronLeft size={16} /></a
|
aria-label={m['pagination.prev']()}><ChevronLeft size={16} /></a
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -79,7 +83,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{#if hrefFor && page < pageCount}
|
{#if hrefFor && page < pageCount}
|
||||||
<a class="pg nav" href={hrefFor(page + 1)} data-sveltekit-keepfocus data-sveltekit-noscroll
|
<a
|
||||||
|
class="pg nav"
|
||||||
|
href={hrefFor(page + 1)}
|
||||||
|
data-sveltekit-keepfocus
|
||||||
|
data-sveltekit-noscroll
|
||||||
aria-label={m['pagination.next']()}><ChevronRight size={16} /></a
|
aria-label={m['pagination.next']()}><ChevronRight size={16} /></a
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -137,7 +137,6 @@
|
|||||||
<p class="hint">{m['scanner.description']()}</p>
|
<p class="hint">{m['scanner.description']()}</p>
|
||||||
|
|
||||||
<div class="stage">
|
<div class="stage">
|
||||||
<!-- svelte-ignore a11y_media_has_caption -->
|
|
||||||
<video bind:this={video} playsinline></video>
|
<video bind:this={video} playsinline></video>
|
||||||
{#if error}
|
{#if error}
|
||||||
<div class="msg error">{error}</div>
|
<div class="msg error">{error}</div>
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
});
|
});
|
||||||
async function loadInitial(signal: AbortSignal) {
|
async function loadInitial(signal: AbortSignal) {
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local, not reactive state
|
||||||
const seen = new Set<number>();
|
const seen = new Set<number>();
|
||||||
const ids: number[] = [];
|
const ids: number[] = [];
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
@@ -149,6 +150,7 @@
|
|||||||
async function runSearch(q: string, signal: AbortSignal) {
|
async function runSearch(q: string, signal: AbortSignal) {
|
||||||
try {
|
try {
|
||||||
const { spools, filaments } = await searchAll(q, COLOR_THRESHOLD, signal);
|
const { spools, filaments } = await searchAll(q, COLOR_THRESHOLD, signal);
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local, not reactive state
|
||||||
const ids = new Set<number>(spools.map((m) => m.entity.id));
|
const ids = new Set<number>(spools.map((m) => m.entity.id));
|
||||||
// The /search spool pass only matches spool-native fields, so also pull
|
// The /search spool pass only matches spool-native fields, so also pull
|
||||||
// the spools of any matched filament — that's how a filament- or
|
// the spools of any matched filament — that's how a filament- or
|
||||||
@@ -196,6 +198,7 @@
|
|||||||
const recentCount = $derived(visibleSpools.filter(isRecent).length);
|
const recentCount = $derived(visibleSpools.filter(isRecent).length);
|
||||||
|
|
||||||
function toggle(id: number) {
|
function toggle(id: number) {
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local; `selected` updates via reassignment below
|
||||||
const next = new Set(selected);
|
const next = new Set(selected);
|
||||||
if (next.has(id)) next.delete(id);
|
if (next.has(id)) next.delete(id);
|
||||||
else next.add(id);
|
else next.add(id);
|
||||||
@@ -258,6 +261,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function toggleF(id: string) {
|
function toggleF(id: string) {
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local; `selectedF` updates via reassignment below
|
||||||
const next = new Set(selectedF);
|
const next = new Set(selectedF);
|
||||||
if (next.has(id)) next.delete(id);
|
if (next.has(id)) next.delete(id);
|
||||||
else next.add(id);
|
else next.add(id);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
$effect(() => {
|
$effect(() => {
|
||||||
const id = filamentId;
|
const id = filamentId;
|
||||||
const archived = showArchived;
|
const archived = showArchived;
|
||||||
revision; // refetch on live spool events
|
void revision; // refetch on live spool events
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
spoolSource
|
spoolSource
|
||||||
.listSpools({
|
.listSpools({
|
||||||
@@ -121,9 +121,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SectionLabel>
|
<SectionLabel>
|
||||||
{#snippet children()}<span style="padding-left:20px"
|
<span style="padding-left:20px">{m['inspector.spoolsCount']({ count: spools.length })}</span>
|
||||||
>{m['inspector.spoolsCount']({ count: spools.length })}</span
|
|
||||||
>{/snippet}
|
|
||||||
{#snippet right()}
|
{#snippet right()}
|
||||||
<button
|
<button
|
||||||
class="arch-toggle"
|
class="arch-toggle"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
const ctrl = new AbortController();
|
const ctrl = new AbortController();
|
||||||
const gq = isGrouped ? buildGroupQuery(libraryState, ctrl.signal) : null;
|
const gq = isGrouped ? buildGroupQuery(libraryState, ctrl.signal) : null;
|
||||||
const sq = isGrouped ? null : buildFlatSpoolQuery(libraryState, ctrl.signal);
|
const sq = isGrouped ? null : buildFlatSpoolQuery(libraryState, ctrl.signal);
|
||||||
revision; // refetch on live events
|
void revision; // refetch on live events
|
||||||
const mine = ++reqId;
|
const mine = ++reqId;
|
||||||
loading = true;
|
loading = true;
|
||||||
errored = false;
|
errored = false;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
let reqId = 0;
|
let reqId = 0;
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const ctrl = new AbortController();
|
const ctrl = new AbortController();
|
||||||
revision; // refetch on live events
|
void revision; // refetch on live events
|
||||||
const want = untrack(() => spools.length) || PAGE;
|
const want = untrack(() => spools.length) || PAGE;
|
||||||
const q = buildScopedSpoolQuery(libraryState, group, want, 0, ctrl.signal);
|
const q = buildScopedSpoolQuery(libraryState, group, want, 0, ctrl.signal);
|
||||||
const mine = ++reqId;
|
const mine = ++reqId;
|
||||||
|
|||||||
@@ -160,16 +160,6 @@
|
|||||||
|
|
||||||
// "matched: <field>" label. Extra fields resolve to the field's display name;
|
// "matched: <field>" label. Extra fields resolve to the field's display name;
|
||||||
// native fields get a translated label (falling back to the raw name).
|
// native fields get a translated label (falling back to the raw name).
|
||||||
const NATIVE_MATCH = new Set([
|
|
||||||
'name',
|
|
||||||
'material',
|
|
||||||
'article_number',
|
|
||||||
'comment',
|
|
||||||
'location',
|
|
||||||
'lot_nr',
|
|
||||||
'id',
|
|
||||||
'color'
|
|
||||||
]);
|
|
||||||
const MATCH_LABEL: Record<EntityKind, Record<string, () => string>> = {
|
const MATCH_LABEL: Record<EntityKind, Record<string, () => string>> = {
|
||||||
filament: {
|
filament: {
|
||||||
name: m['filament.fields.name'],
|
name: m['filament.fields.name'],
|
||||||
@@ -257,11 +247,7 @@
|
|||||||
onclick={onResultClick}
|
onclick={onResultClick}
|
||||||
>
|
>
|
||||||
<span class="id mono">#{spool.entity.id}</span>
|
<span class="id mono">#{spool.entity.id}</span>
|
||||||
<Swatch
|
<Swatch colors={filament?.colors ?? []} direction={filament?.multiColorDirection} size={20} />
|
||||||
colors={filament?.colors ?? []}
|
|
||||||
direction={filament?.multiColorDirection}
|
|
||||||
size={20}
|
|
||||||
/>
|
|
||||||
<span class="text">
|
<span class="text">
|
||||||
<span class="title">{filament?.name || m['search.unknownFilament']()}</span>
|
<span class="title">{filament?.name || m['search.unknownFilament']()}</span>
|
||||||
{#if spool.entity.location}<span class="sub">{spool.entity.location}</span>{/if}
|
{#if spool.entity.location}<span class="sub">{spool.entity.location}</span>{/if}
|
||||||
@@ -308,12 +294,7 @@
|
|||||||
<a
|
<a
|
||||||
class="result"
|
class="result"
|
||||||
class:active={activeIndex === indexOf('vendor', vendor.entity.id)}
|
class:active={activeIndex === indexOf('vendor', vendor.entity.id)}
|
||||||
href={searchResultHref(
|
href={searchResultHref(page.url.searchParams, page.url.pathname, 'vendor', vendor.entity.id)}
|
||||||
page.url.searchParams,
|
|
||||||
page.url.pathname,
|
|
||||||
'vendor',
|
|
||||||
vendor.entity.id
|
|
||||||
)}
|
|
||||||
data-sveltekit-noscroll
|
data-sveltekit-noscroll
|
||||||
onclick={onResultClick}
|
onclick={onResultClick}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { getFields, setField, deleteField, type EntityType, type FieldDef, type FieldParams } from '$lib/api/fields';
|
import {
|
||||||
|
getFields,
|
||||||
|
setField,
|
||||||
|
deleteField,
|
||||||
|
type EntityType,
|
||||||
|
type FieldDef,
|
||||||
|
type FieldParams
|
||||||
|
} from '$lib/api/fields';
|
||||||
|
|
||||||
// Reactive cache of extra-field definitions per entity type. Loaded on demand
|
// Reactive cache of extra-field definitions per entity type. Loaded on demand
|
||||||
// (inspectors) and by the settings manager; refetched after mutations.
|
// (inspectors) and by the settings manager; refetched after mutations.
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ export function hexRgb(h: string): [number, number, number] {
|
|||||||
export function swatchStyle(colors: string[] | undefined, direction?: MultiColorDirection): string {
|
export function swatchStyle(colors: string[] | undefined, direction?: MultiColorDirection): string {
|
||||||
if (!colors || !colors.length) return 'background:#555';
|
if (!colors || !colors.length) return 'background:#555';
|
||||||
if (colors.length > 1) {
|
if (colors.length > 1) {
|
||||||
const angle =
|
const angle = direction === 'coaxial' ? '90deg' : direction === 'longitudinal' ? '0deg' : '135deg';
|
||||||
direction === 'coaxial' ? '90deg' : direction === 'longitudinal' ? '0deg' : '135deg';
|
|
||||||
// Each colour gets its own band, but boundaries are softened by a narrow
|
// Each colour gets its own band, but boundaries are softened by a narrow
|
||||||
// feather rather than a hard edge or a full-slot blur: colour i stays solid
|
// feather rather than a hard edge or a full-slot blur: colour i stays solid
|
||||||
// across most of [i*step, (i+1)*step] and only fades over `feather`% on each
|
// across most of [i*step, (i+1)*step] and only fades over `feather`% on each
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { browser } from '$app/environment';
|
|
||||||
|
|
||||||
// Spoolman ships as a client-side SPA served from the backend, so disable SSR
|
// Spoolman ships as a client-side SPA served from the backend, so disable SSR
|
||||||
// and prerender the shell.
|
// and prerender the shell.
|
||||||
export const ssr = false;
|
export const ssr = false;
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
});
|
});
|
||||||
// A NULL location and an empty-string one are distinct rows to the database
|
// A NULL location and an empty-string one are distinct rows to the database
|
||||||
// but the same "No location" card here, so counts are summed by key.
|
// but the same "No location" card here, so counts are summed by key.
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local, not reactive state
|
||||||
const totals = new Map<string, number>();
|
const totals = new Map<string, number>();
|
||||||
for (const g of page.items) totals.set(g.key, (totals.get(g.key) ?? 0) + g.spoolCount);
|
for (const g of page.items) totals.set(g.key, (totals.get(g.key) ?? 0) + g.spoolCount);
|
||||||
|
|
||||||
@@ -360,6 +361,7 @@
|
|||||||
|
|
||||||
// Which locations currently hold spools, by display name.
|
// Which locations currently hold spools, by display name.
|
||||||
function presentNames(): Set<string> {
|
function presentNames(): Set<string> {
|
||||||
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local, not reactive state
|
||||||
const names = new Set<string>();
|
const names = new Set<string>();
|
||||||
for (const [key, b] of Object.entries(buckets)) {
|
for (const [key, b] of Object.entries(buckets)) {
|
||||||
if (b.total > 0) names.add(key === EMPTY ? NO_LOCATION : key);
|
if (b.total > 0) names.add(key === EMPTY ? NO_LOCATION : key);
|
||||||
@@ -666,12 +668,14 @@
|
|||||||
<div class="chip-info">
|
<div class="chip-info">
|
||||||
<div class="chip-title">
|
<div class="chip-title">
|
||||||
<span class="chip-id mono">#{s.id}</span>
|
<span class="chip-id mono">#{s.id}</span>
|
||||||
{#if v.name !== '?'}{v.name}{' - '}{/if}{f.name}
|
{#if v.name !== '?'}{v.name} -
|
||||||
|
{/if}{f.name}
|
||||||
</div>
|
</div>
|
||||||
<div class="chip-subtitle">
|
<div class="chip-subtitle">
|
||||||
{f.material}{' - '}<span class:low={settings.isLow(s.remaining, s.unused)}
|
{f.material} -
|
||||||
>{weightAuto(s.remaining)}</span
|
<span class:low={settings.isLow(s.remaining, s.unused)}>{weightAuto(s.remaining)}</span>
|
||||||
>{' / '}{weightAuto(f.weight)}{#if s.lastUsedLabel}{' - '}{m['locations.lastUsed']({
|
/ {weightAuto(f.weight)}{#if s.lastUsedLabel}
|
||||||
|
- {m['locations.lastUsed']({
|
||||||
time: s.lastUsedLabel
|
time: s.lastUsedLabel
|
||||||
})}{/if}
|
})}{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default defineConfig({
|
|||||||
paraglideVitePlugin({
|
paraglideVitePlugin({
|
||||||
project: './project.inlang',
|
project: './project.inlang',
|
||||||
outdir: './src/lib/paraglide',
|
outdir: './src/lib/paraglide',
|
||||||
strategy: ["localStorage", "preferredLanguage", "baseLocale"]
|
strategy: ['localStorage', 'preferredLanguage', 'baseLocale']
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
server: { port: 5174 }
|
server: { port: 5174 }
|
||||||
|
|||||||
@@ -23,6 +23,21 @@ pre-commit:
|
|||||||
root: "client/"
|
root: "client/"
|
||||||
run: ./node_modules/.bin/prettier --write .
|
run: ./node_modules/.bin/prettier --write .
|
||||||
stage_fixed: true
|
stage_fixed: true
|
||||||
|
- group: # Client v2 checks
|
||||||
|
parallel: false
|
||||||
|
jobs:
|
||||||
|
- name: Run lint check on client_v2 files
|
||||||
|
root: "client_v2/"
|
||||||
|
run: ./node_modules/.bin/eslint --fix . # Hardcoded path because Windows messes up if we use npx
|
||||||
|
stage_fixed: true
|
||||||
|
- name: Run format check on client_v2 files
|
||||||
|
root: "client_v2/"
|
||||||
|
run: ./node_modules/.bin/prettier --write .
|
||||||
|
stage_fixed: true
|
||||||
|
- name: Run type check on client_v2 files
|
||||||
|
root: "client_v2/"
|
||||||
|
# svelte-kit sync generates .svelte-kit/tsconfig.json, which svelte-check needs.
|
||||||
|
run: ./node_modules/.bin/svelte-kit sync && ./node_modules/.bin/svelte-check --tsconfig ./tsconfig.json
|
||||||
|
|
||||||
# Same as pre-commit but no fixes
|
# Same as pre-commit but no fixes
|
||||||
ci:
|
ci:
|
||||||
@@ -46,4 +61,17 @@ ci:
|
|||||||
- name: Run format check on client files
|
- name: Run format check on client files
|
||||||
root: "client/"
|
root: "client/"
|
||||||
run: ./node_modules/.bin/prettier --check .
|
run: ./node_modules/.bin/prettier --check .
|
||||||
|
- group: # Client v2 checks
|
||||||
|
parallel: false
|
||||||
|
jobs:
|
||||||
|
- name: Run lint check on client_v2 files
|
||||||
|
root: "client_v2/"
|
||||||
|
run: ./node_modules/.bin/eslint . # Hardcoded path because Windows messes up if we use npx
|
||||||
|
- name: Run format check on client_v2 files
|
||||||
|
root: "client_v2/"
|
||||||
|
run: ./node_modules/.bin/prettier --check .
|
||||||
|
- name: Run type check on client_v2 files
|
||||||
|
root: "client_v2/"
|
||||||
|
# svelte-kit sync generates .svelte-kit/tsconfig.json, which svelte-check needs.
|
||||||
|
run: ./node_modules/.bin/svelte-kit sync && ./node_modules/.bin/svelte-check --tsconfig ./tsconfig.json
|
||||||
|
|
||||||
Reference in new issue
Block a user