Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9739ea55c6 | ||
|
|
99cbbd7a07 | ||
|
|
7c5384621a | ||
|
|
848e12e2d6 | ||
|
|
2f7f5ba5af | ||
|
|
e7d9fe0efb |
No files matched your search
@@ -66,6 +66,15 @@ jobs:
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: npm run tauri build
|
||||
|
||||
- name: Upload Windows packages as GitHub artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: court-pilot-windows
|
||||
if-no-files-found: error
|
||||
path: |
|
||||
src-tauri/target/release/bundle/msi/*.msi
|
||||
src-tauri/target/release/bundle/nsis/*.exe
|
||||
|
||||
- name: Upload release assets to CrabNebula
|
||||
uses: crabnebula-dev/cloud-release@v0
|
||||
with:
|
||||
@@ -116,6 +125,13 @@ jobs:
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: npm run tauri build -- --target ${{ matrix.target }} --bundles app,dmg
|
||||
|
||||
- name: Upload macOS packages as GitHub artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: court-pilot-macos-${{ matrix.target }}
|
||||
if-no-files-found: error
|
||||
path: src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
|
||||
|
||||
- name: Upload release assets to CrabNebula
|
||||
uses: crabnebula-dev/cloud-release@v0
|
||||
with:
|
||||
@@ -228,3 +244,37 @@ jobs:
|
||||
api-key: ${{ secrets.CN_API_KEY }}
|
||||
path: ./src-tauri
|
||||
working-directory: ./src-tauri
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Publish GitHub release after CrabNebula publication succeeds
|
||||
# ---------------------------------------------------------
|
||||
github-release:
|
||||
needs: publish
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Download release packages
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: court-pilot-*
|
||||
path: release-assets
|
||||
|
||||
- name: Create GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
RELEASE_TAG: ${{ github.ref_name }}
|
||||
shell: bash
|
||||
run: |
|
||||
mapfile -d '' assets < <(find release-assets -type f -print0)
|
||||
if [ "${#assets[@]}" -eq 0 ]; then
|
||||
echo "No release packages found" >&2
|
||||
exit 1
|
||||
fi
|
||||
gh release create "$RELEASE_TAG" "${assets[@]}" \
|
||||
--verify-tag \
|
||||
--title "$RELEASE_TAG" \
|
||||
--generate-notes
|
||||
@@ -90,7 +90,7 @@ Do not manually edit generated output or installed dependencies:
|
||||
|
||||
- Node.js must use the `lts/jod` NVM alias, as declared in `.nvmrc`.
|
||||
- Documented commands target Bash on Linux, macOS, or Windows WSL. Use Bash explicitly for agent commands; fish's `nvm.fish` is unavailable in Bash.
|
||||
- Before every npm command, explicitly load Bash NVM and select Node in the same shell session: `export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"; . "$NVM_DIR/nvm.sh" && nvm use lts/jod && npm run build`. Do not rely on `.bashrc` loading in noninteractive shells or fall back to system Node.
|
||||
- Initialize Bash NVM once at the start of each shell session: `export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"; . "$NVM_DIR/nvm.sh" && nvm use` from the project root. `nvm use` reads `lts/jod` from `.nvmrc`. Reuse that initialized shell for subsequent npm commands. If a tool starts a fresh shell for each execution, initialize each new shell or use a persistent shell; environment changes do not carry between independent shells. Stop if initialization fails. Do not rely on `.bashrc` loading in noninteractive shells or fall back to system Node.
|
||||
- If `${NVM_DIR:-$HOME/.nvm}/nvm.sh` is missing, ask the user for permission before installing Bash NVM. Follow `docs/development.md#shell-and-nvm-setup` after approval. Keep existing fish NVM separate.
|
||||
- Install exact dependencies: `npm ci`
|
||||
- Start the Ionic development server: `npm start`
|
||||
@@ -99,7 +99,7 @@ Do not manually edit generated output or installed dependencies:
|
||||
- Run linting: `npm run lint`
|
||||
- Run Tauri CLI commands: `npm run tauri -- <command>`
|
||||
|
||||
Tauri development intentionally uses port `8103`. `src-tauri/tauri.conf.json` expects that port, so its `beforeDevCommand` must start Ionic on `8103`; the current `npm start` script does not explicitly select it. Verify the full `npm run tauri dev` flow before claiming it works.
|
||||
Tauri development intentionally uses port `8103`. `src-tauri/tauri.conf.json` expects that port, so its `beforeDevCommand` starts the project's Angular CLI with `serve --host=0.0.0.0 --port=8103`. Keep the command aligned with `devUrl`. Verify the full `npm run tauri dev` flow before claiming it works.
|
||||
|
||||
## Release versioning
|
||||
|
||||
|
||||
@@ -38,49 +38,48 @@ For desktop development, also install:
|
||||
These commands target **Bash** (Linux, macOS, or Windows WSL). From fish,
|
||||
start `bash` first. Follow the [shell and NVM setup](docs/development.md#shell-and-nvm-setup)
|
||||
before continuing. If Bash NVM is missing, agents must ask permission before
|
||||
installing it. `.nvmrc` declares `lts/jod`:
|
||||
installing it. `.nvmrc` declares `lts/jod`.
|
||||
|
||||
Initialize NVM once at the start of each Bash session, from the project root:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm install lts/jod
|
||||
nvm use lts/jod && npm ci
|
||||
. "$NVM_DIR/nvm.sh" && nvm use
|
||||
```
|
||||
|
||||
Each npm command block loads Bash NVM explicitly and selects the required Node
|
||||
version, including in fresh noninteractive shells.
|
||||
On first setup, run `nvm install` after loading NVM if the required Node version
|
||||
is not installed. Stop if setup fails; do not use the system Node as a fallback.
|
||||
The commands below reuse this session. Repeat the setup when opening a new shell.
|
||||
|
||||
Install the project dependencies:
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
|
||||
If Ionic CLI is not already installed:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm install --global @ionic/cli
|
||||
npm install --global @ionic/cli
|
||||
```
|
||||
|
||||
## Web development
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm start
|
||||
npm start
|
||||
```
|
||||
|
||||
Build the Angular application:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run build
|
||||
npm run build
|
||||
```
|
||||
|
||||
Run linting and tests:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run lint
|
||||
nvm use lts/jod && npm test
|
||||
npm run lint
|
||||
npm test
|
||||
```
|
||||
|
||||
`npm test` starts Karma in its configured mode and may remain open in watch mode.
|
||||
@@ -96,18 +95,14 @@ For VS Code, the recommended extension is `esbenp.prettier-vscode`. Use **Format
|
||||
The Tauri CLI is available through:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run tauri -- <command>
|
||||
npm run tauri -- <command>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run tauri -- dev
|
||||
nvm use lts/jod && npm run tauri -- build
|
||||
npm run tauri -- dev
|
||||
npm run tauri -- build
|
||||
```
|
||||
|
||||
> Tauri development intentionally uses `http://localhost:8103`. The current `npm start` script does not explicitly select that port, so verify that Ionic is listening on `8103` when using `tauri dev`.
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html", "db.ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import Dexie, { type EntityTable } from 'dexie';
|
||||
|
||||
export type WaitingRole = 'umpire' | 'serviceJudge';
|
||||
|
||||
export interface Umpire {
|
||||
id: number;
|
||||
firstName: string;
|
||||
@@ -7,6 +9,7 @@ export interface Umpire {
|
||||
country: string;
|
||||
gender: string;
|
||||
courtNo?: number;
|
||||
returnToQueue?: WaitingRole;
|
||||
}
|
||||
|
||||
export interface CourtUmpire {
|
||||
@@ -38,6 +41,7 @@ export interface Settings {
|
||||
withServiceJudge: boolean;
|
||||
numberOfCourts: number;
|
||||
showAlert: boolean;
|
||||
waitingListQuickButtons?: boolean;
|
||||
}
|
||||
|
||||
const db = new Dexie('CourtPilot') as Dexie & {
|
||||
@@ -58,4 +62,20 @@ db.version(1).stores({
|
||||
waitingServiceJudges: '++id, order, serviceJudgeId'
|
||||
});
|
||||
|
||||
db.version(2)
|
||||
.stores({
|
||||
umpires: '++id, lastName',
|
||||
courtUmpires: '++id, courtNo, umpireId',
|
||||
courtServiceJudges: '++id, courtNo, umpireId',
|
||||
settings: '++id',
|
||||
waitingUmpires: '++id, order, umpireId',
|
||||
waitingServiceJudges: '++id, order, serviceJudgeId'
|
||||
})
|
||||
.upgrade(async (transaction) => {
|
||||
await transaction
|
||||
.table('settings')
|
||||
.toCollection()
|
||||
.modify({ waitingListQuickButtons: false });
|
||||
});
|
||||
|
||||
export { db };
|
||||
+21
-6
@@ -21,11 +21,11 @@ flowchart TD
|
||||
|
||||
The root route lazy-loads the tab routes. The three primary routes are:
|
||||
|
||||
| Route | Source | Responsibility |
|
||||
|---|---|---|
|
||||
| `/tabs/pilot` | `src/app/tab1/` | Waiting queues, court assignments, drag and drop, and rotation actions |
|
||||
| `/tabs/umpires` | `src/app/tab2/` | Court-official roster management |
|
||||
| `/tabs/settings` | `src/app/tab3/` | Court count, theme, service-judge mode, alerts, and language |
|
||||
| Route | Source | Responsibility |
|
||||
| ---------------- | --------------- | ---------------------------------------------------------------------- |
|
||||
| `/tabs/pilot` | `src/app/tab1/` | Waiting queues, court assignments, drag and drop, and rotation actions |
|
||||
| `/tabs/umpires` | `src/app/tab2/` | Court-official roster management |
|
||||
| `/tabs/settings` | `src/app/tab3/` | Court count, theme, service-judge mode, alerts, and language |
|
||||
|
||||
Pages are standalone Angular components and directly import the Ionic standalone components they use. Services are root-provided and injected with Angular's `inject()` function.
|
||||
|
||||
@@ -37,7 +37,7 @@ Persistence services use Dexie's `liveQuery()` and Angular's `toSignal()` to exp
|
||||
|
||||
## Persistence model
|
||||
|
||||
`db.ts` declares a `CourtPilot` Dexie database at schema version 1.
|
||||
`db.ts` declares a `CourtPilot` Dexie database at schema version 2.
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
@@ -85,6 +85,21 @@ component starts and toggles Ionic's `ion-palette-dark` class. Device theme chan
|
||||
apply while the app is open when device-preferred is selected. This does not
|
||||
change the Dexie schema or existing settings records.
|
||||
|
||||
### Waiting-list quick buttons
|
||||
|
||||
The optional waiting-list quick buttons default to disabled and require service
|
||||
judges. Disabling service judges disables this setting. Both waiting lists have
|
||||
an off-duty action; off-duty officials with a saved origin have a return action. Quick-button alerts
|
||||
apply to both actions. Dragging an official from either waiting list to Not on Duty
|
||||
also saves the origin, even when quick buttons are disabled. Returning appends to the saved role queue.
|
||||
|
||||
Schema version 2 adds `waitingListQuickButtons` to settings and optional
|
||||
`returnToQueue` metadata to officials. The upgrade explicitly sets the setting to
|
||||
false for existing settings rows. Existing officials need no transformation;
|
||||
officials without return metadata have no return action. Queue indexes and
|
||||
existing records are preserved. Older application versions do not understand the
|
||||
new metadata; downgrading an IndexedDB database is not supported.
|
||||
|
||||
### Schema-change policy
|
||||
|
||||
Once data may exist in released clients, do not modify schema version 1 in place. Add a new `db.version(n)` declaration and define upgrade behavior when existing records need transformation.
|
||||
|
||||
+70
-25
@@ -27,18 +27,21 @@ NVM. After approval, follow the official
|
||||
Use the default `~/.nvm` location, or set `NVM_DIR` to your existing Bash NVM
|
||||
installation. Keep an existing fish installation separate.
|
||||
|
||||
After NVM is installed, install the required Node release from the project root:
|
||||
Initialize NVM once at the start of each Bash session, from the project root:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm install lts/jod
|
||||
. "$NVM_DIR/nvm.sh" && nvm use
|
||||
```
|
||||
|
||||
Every npm command block below explicitly loads NVM and selects `lts/jod`.
|
||||
This also works in noninteractive Bash sessions that do not load `.bashrc`.
|
||||
Run each block from the project root. Stop if loading NVM or selecting Node fails;
|
||||
do not fall back to the system Node version.
|
||||
`nvm use` reads the required `lts/jod` version from `.nvmrc`. On first setup,
|
||||
run `nvm install` after loading NVM if that version is not installed.
|
||||
Stop if loading NVM or selecting Node fails; do not fall back to system Node.
|
||||
|
||||
Run the commands below in that initialized shell. Repeat setup for each new
|
||||
shell, including noninteractive shells that do not load `.bashrc`. Agents should
|
||||
reuse an initialized shell for subsequent npm commands; separate command
|
||||
executions do not necessarily share environment changes.
|
||||
|
||||
Angular 22 requires TypeScript 6.0 and Node.js 22.22.3 or newer on the
|
||||
Node.js 22 line. If your installed `lts/jod` is older, run `nvm install lts/jod`
|
||||
@@ -47,17 +50,13 @@ after loading Bash NVM.
|
||||
Install exact project dependencies with:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm ci
|
||||
npm ci
|
||||
```
|
||||
|
||||
If needed, install Ionic CLI globally:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm install --global @ionic/cli
|
||||
npm install --global @ionic/cli
|
||||
```
|
||||
|
||||
The committed `.nvmrc` uses the named `lts/jod` alias. Do not replace it with another Node release or add `.tool-versions` unless the team intentionally changes version-management tools.
|
||||
@@ -71,17 +70,13 @@ Install Rust stable and the Tauri 2 prerequisites for your operating system. `sr
|
||||
Start the Ionic development server:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm start
|
||||
npm start
|
||||
```
|
||||
|
||||
Build the Angular application:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run build
|
||||
npm run build
|
||||
```
|
||||
|
||||
The production web output is generated under `www/`. Do not edit that directory manually.
|
||||
@@ -126,21 +121,64 @@ uses Vite 8.3.5 or newer within v8, and Capacitor's Xcode parser uses UUID 11.1.
|
||||
or newer within v11. Xcode uses the compatible CommonJS `uuid.v4()` API.
|
||||
Revisit these overrides when upstream packages include the patched versions.
|
||||
|
||||
The audit overrides also select patched `minimatch` 11 for modern consumers,
|
||||
`glob` 14 for consumers of version 9 or newer, and `follow-redirects` 1.16.2 or
|
||||
newer for Karma's HTTP proxy. These overrides retain Node.js 22 support. Keep
|
||||
Karma's legacy `minimatch` 3 and `glob` 7 dependency paths intact: their callable
|
||||
CommonJS APIs differ from the modern packages. npm still reports moderate
|
||||
findings through those legacy test dependencies; resolving them requires an
|
||||
upstream-compatible test-tooling update. Do not use `npm audit fix --force`,
|
||||
which proposes incompatible downgrades of Angular and the test tooling.
|
||||
|
||||
## Tauri workflow
|
||||
|
||||
Run Tauri commands through the npm script:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run tauri dev
|
||||
nvm use lts/jod && npm run tauri build
|
||||
npm run tauri dev
|
||||
npm run tauri build
|
||||
```
|
||||
|
||||
Tauri development intentionally expects the frontend at `http://localhost:8103`. `devUrl` only tells Tauri where to connect; the Ionic command must also listen on port `8103`. The current `npm start` script does not explicitly select that port, so verify the complete development flow when changing Tauri or serve configuration.
|
||||
Tauri development intentionally expects the frontend at `http://localhost:8103`. `devUrl` tells Tauri where to connect, and `beforeDevCommand` runs `npm run ng -- serve --host=0.0.0.0 --port=8103` to serve the frontend on the matching port without opening a browser. This uses the project's Angular CLI and does not require a global Ionic CLI installation. Keep these settings aligned when changing Tauri or serve configuration.
|
||||
|
||||
The desktop wrapper is under `src-tauri/`. Do not edit generated `src-tauri/target/` or `src-tauri/gen/` content.
|
||||
|
||||
### Local updater signing
|
||||
|
||||
Tauri builds require the private key matching the updater public key in
|
||||
`src-tauri/tauri.conf.json`. Set the key path in the build shell; Tauri does not
|
||||
load these values from `.env` files. Keep private keys and passwords outside the
|
||||
repository.
|
||||
|
||||
```bash
|
||||
export TAURI_SIGNING_PRIVATE_KEY="$HOME/.tauri/myapp.key"
|
||||
# For a key without a password:
|
||||
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD=""
|
||||
npm run tauri build
|
||||
```
|
||||
|
||||
For a password-protected key, replace the empty-password export with a hidden
|
||||
prompt in your terminal:
|
||||
|
||||
```bash
|
||||
read -r -s -p "Signing key password: " TAURI_SIGNING_PRIVATE_KEY_PASSWORD
|
||||
echo
|
||||
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD
|
||||
```
|
||||
|
||||
If you keep the password in `$HOME/.tauri/signing-password`, restrict the file to
|
||||
your account with `chmod 600 "$HOME/.tauri/signing-password"`, then load it for
|
||||
each build without printing it:
|
||||
|
||||
```bash
|
||||
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$(cat "$HOME/.tauri/signing-password")"
|
||||
```
|
||||
|
||||
This stores the password as plaintext outside the repository. The export applies
|
||||
only to the current shell and its child processes.
|
||||
|
||||
See the [Tauri updater signing documentation](https://v2.tauri.app/plugin/updater/#building).
|
||||
|
||||
## Data storage
|
||||
|
||||
Court Pilot has no backend API. It stores domain data in IndexedDB using Dexie. The database and schema are declared in root-level `db.ts`.
|
||||
@@ -214,12 +252,19 @@ GIT_EDITOR=true git tag v1.2.0
|
||||
|
||||
Replace `1.2.0` with the confirmed release version and verify the tag points to the intended commit. Do not overwrite or move an existing release tag.
|
||||
|
||||
Creating the tag is local. Pushing it starts `.github/workflows/release.yml`, which builds Windows and macOS artifacts for CrabNebula and builds/publishes the Linux Snap. Push only after explicit confirmation:
|
||||
Creating the tag is local. Pushing it starts `.github/workflows/release.yml`, which builds Windows and macOS artifacts for CrabNebula and builds/publishes the Linux Snap. After CrabNebula publication succeeds, it also creates a GitHub Release for the pushed tag with generated release notes and attaches the Windows installers, both macOS DMGs, and the Linux Snap. Push only after explicit confirmation:
|
||||
|
||||
```sh
|
||||
git push origin v1.2.0
|
||||
```
|
||||
|
||||
The GitHub Release job uses the built-in `GITHUB_TOKEN` with job-scoped
|
||||
`contents: write` permission; no additional secret is required. Existing
|
||||
CrabNebula, updater-signing, and Snap Store secrets are still required. Manual
|
||||
workflow runs on branches skip the GitHub Release job; runs on `v*` tags include
|
||||
it. Use a new version tag for each release: creation fails if a GitHub Release
|
||||
already exists for that tag.
|
||||
|
||||
## Generated and local directories
|
||||
|
||||
Do not manually edit or commit generated/dependency content unless a workflow explicitly requires an artifact:
|
||||
|
||||
+7
-16
@@ -12,22 +12,19 @@ as the app rather than installing an older nested copy.
|
||||
|
||||
These examples target Bash and run from the project root. Complete the
|
||||
[shell and NVM setup](development.md#shell-and-nvm-setup) first. Agents must ask
|
||||
permission before installing missing Bash NVM. Each npm block loads NVM explicitly.
|
||||
permission before installing missing Bash NVM. Initialize NVM once per Bash
|
||||
session as described there, then run the commands below in that same shell.
|
||||
|
||||
Run the interactive test suite:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm test
|
||||
npm test
|
||||
```
|
||||
|
||||
Run the configured non-watching CI test mode:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm test -- --configuration=ci
|
||||
npm test -- --configuration=ci
|
||||
```
|
||||
|
||||
The test environment requires an available Chrome installation. Test coverage output, when enabled by the test runner, is written under `coverage/app/`.
|
||||
@@ -35,9 +32,7 @@ The test environment requires an available Chrome installation. Test coverage ou
|
||||
Run the suite once in headless Chrome with a coverage report:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm test -- --watch=false --browsers=ChromeHeadless --code-coverage
|
||||
npm test -- --watch=false --browsers=ChromeHeadless --code-coverage
|
||||
```
|
||||
|
||||
If Chrome is not auto-detected, set `CHROME_BIN` to its executable path
|
||||
@@ -53,17 +48,13 @@ spies and signals to isolate assignments, confirmation, and role swaps.
|
||||
Lint TypeScript and Angular templates:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run lint
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Build the production Angular application:
|
||||
|
||||
```bash
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm use lts/jod && npm run build
|
||||
npm run build
|
||||
```
|
||||
|
||||
Check Rust changes from the Tauri directory:
|
||||
|
||||
+2
-2
@@ -4,11 +4,11 @@ const angular = require('angular-eslint');
|
||||
module.exports = defineConfig(
|
||||
{ ignores: ['projects/**', 'www/**', 'coverage/**'] },
|
||||
{
|
||||
files: ['src/**/*.ts'],
|
||||
files: ['src/**/*.ts', 'db.ts'],
|
||||
extends: angular.configs.tsRecommended,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
project: ['./tsconfig.eslint.json'],
|
||||
tsconfigRootDir: __dirname
|
||||
}
|
||||
},
|
||||
|
||||
Generated
+26
-164
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "court-pilot",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "court-pilot",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^22.2.4",
|
||||
"@angular/common": "^22.2.3",
|
||||
@@ -4834,6 +4834,13 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/nonrecursive-glob-match": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/nonrecursive-glob-match/-/nonrecursive-glob-match-1.0.3.tgz",
|
||||
"integrity": "sha512-SI7kyKHq4XXDRozw+r9GlRZkia1ZkFrpLW98r2IC2Bpr8YkFGE3fvjzmxCG0x78A++69HmDNrDvSMYBZmHGpKA==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/@istanbuljs/schema": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
|
||||
@@ -5737,9 +5744,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -5757,9 +5761,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -5777,9 +5778,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -5797,9 +5795,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -5817,9 +5812,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -5837,9 +5829,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -5857,9 +5846,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6095,9 +6081,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6115,9 +6098,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6135,9 +6115,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6155,9 +6132,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6175,9 +6149,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6195,9 +6166,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6215,9 +6183,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6235,9 +6200,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6452,9 +6414,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6476,9 +6435,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6500,9 +6456,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6524,9 +6477,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6548,9 +6498,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6572,9 +6519,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6939,9 +6883,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6959,9 +6900,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6979,9 +6917,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6999,9 +6934,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7019,9 +6951,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7039,9 +6968,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7142,9 +7068,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7158,9 +7081,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7174,9 +7094,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7190,9 +7107,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7379,9 +7293,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7399,9 +7310,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7419,9 +7327,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7439,9 +7344,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7459,9 +7361,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -10835,9 +10734,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz",
|
||||
"integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==",
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.2.tgz",
|
||||
"integrity": "sha512-/kVP2U8z596Ia0gwFrwyIeo74ta5aVmSeU1FfAUMAmcEP5EP7zydmag3SLiN64NaRKef3lxsq7i/g7ltJQDGow==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -13087,9 +12986,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -13111,9 +13007,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -13135,9 +13028,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -13159,9 +13049,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -13680,16 +13567,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
|
||||
"integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.4.tgz",
|
||||
"integrity": "sha512-/UJ1dEoVQTjPNDh6SGlUo+ueLMmoHTas4q+8yaAR/5t6nLAIiBDJtoYUh20IYleix7ehiQkgczb84PE3uzHkxg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.8"
|
||||
"@isaacs/nonrecursive-glob-match": "^1.0.3",
|
||||
"brace-expansion": "^5.0.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
@@ -14514,9 +14402,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-3.0.0.tgz",
|
||||
"integrity": "sha512-1pTbQqH1wIwMi/eleQ5leFTTCkzFsPcgXNjwOA8YGGW/W4/kHT3dssE7Z6NH+zuqNLOzOB0uaD61yl7cAcVhgg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
@@ -15324,15 +15212,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf/node_modules/glob": {
|
||||
"version": "13.0.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||
"version": "14.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-14.0.3.tgz",
|
||||
"integrity": "sha512-A1vjIwC3qpdCso45Iw127gnp7sPhqgY8lUQhDrArIs+GbmfaaVPFuoyy7WyLT9EPZCRZ7j7aLdwUhQmHWesEow==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.2.2",
|
||||
"minimatch": "^11.0.2",
|
||||
"minipass": "^7.1.3",
|
||||
"path-scurry": "^2.0.2"
|
||||
"path-scurry": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
@@ -15660,7 +15548,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "glibc",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15678,7 +15565,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "glibc",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15696,7 +15582,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "musl",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15714,7 +15599,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "musl",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15732,7 +15616,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "musl",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15750,7 +15633,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "musl",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15768,7 +15650,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "glibc",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15786,7 +15667,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": "glibc",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -17457,9 +17337,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -17477,9 +17354,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -17497,9 +17371,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -17517,9 +17388,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -17537,9 +17405,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -17557,9 +17422,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
||||
+6
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "court-pilot",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"author": "Richard Toth",
|
||||
"homepage": "https://github.com/tricsusz/court-pilot",
|
||||
"scripts": {
|
||||
@@ -76,6 +76,11 @@
|
||||
},
|
||||
"xcode": {
|
||||
"uuid": "^11.1.1"
|
||||
},
|
||||
"minimatch@>=9": "^11.0.1",
|
||||
"glob@>=9": "^14.0.2",
|
||||
"http-proxy": {
|
||||
"follow-redirects": "^1.16.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ license: MIT
|
||||
base: core24
|
||||
icon: snap/gui/court-pilot.png
|
||||
|
||||
version: '2.0.0'
|
||||
version: '2.1.0'
|
||||
|
||||
summary: Badminton umpire rotation application
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -519,7 +519,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "court-pilot"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "court-pilot"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
description = "Court Pilot"
|
||||
authors = ["Richard Toth"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||
"productName": "court-pilot",
|
||||
"mainBinaryName": "court-pilot",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"identifier": "com.courtpilot.dev",
|
||||
"build": {
|
||||
"frontendDist": "../www",
|
||||
"devUrl": "http://localhost:8103",
|
||||
"beforeDevCommand": "npm start",
|
||||
"beforeDevCommand": "npm run ng -- serve --host=0.0.0.0 --port=8103",
|
||||
"beforeBuildCommand": "ionic build"
|
||||
},
|
||||
"app": {
|
||||
|
||||
@@ -9,6 +9,14 @@ export const translations = {
|
||||
themeLight: 'Light',
|
||||
themeDark: 'Dark',
|
||||
themeSystem: 'Device-preferred',
|
||||
waitingListQuickButtons: 'Quick buttons for waiting lists',
|
||||
waitingListQuickButtonsHelp:
|
||||
'Move waiting officials off duty and return them to the end of their previous list. Only officials with a saved list have a return button. Requires Service Judge to be enabled.',
|
||||
moveOffDuty: 'Move off duty',
|
||||
returnToQueue: 'Return to waiting list',
|
||||
confirmMoveOffDuty: 'Move this official to Not on Duty?',
|
||||
confirmReturnToQueue:
|
||||
'Return this official to the bottom of the waiting list?',
|
||||
showAlerts: 'Show alerts on quick buttons',
|
||||
serviceJudge: 'Service Judge',
|
||||
language: 'Language',
|
||||
@@ -62,6 +70,14 @@ export const translations = {
|
||||
themeLight: 'Világos',
|
||||
themeDark: 'Sötét',
|
||||
themeSystem: 'Eszközbeállítás szerint',
|
||||
waitingListQuickButtons: 'Gyors gombok a várólistákhoz',
|
||||
waitingListQuickButtonsHelp:
|
||||
'A várakozó tisztségviselőket pihenőre küldheted, majd visszaküldheted őket az előző várólistájuk végére. A visszaküldés gomb csak mentett várólista esetén jelenik meg. Az Adogatásbíró beállítás bekapcsolása szükséges.',
|
||||
moveOffDuty: 'Pihenőre küldés',
|
||||
returnToQueue: 'Vissza a várólistára',
|
||||
confirmMoveOffDuty: 'Pihenőre küldöd ezt a tisztségviselőt?',
|
||||
confirmReturnToQueue:
|
||||
'Visszaküldöd ezt a tisztségviselőt a várólista végére?',
|
||||
showAlerts: 'Figyelmeztetések megjelenítése a gyors gomboknál',
|
||||
serviceJudge: 'Adogatásbíró',
|
||||
language: 'Nyelv',
|
||||
|
||||
@@ -29,7 +29,12 @@ export class SettingsService {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.settings.update(1, changes);
|
||||
await db.settings.update(1, {
|
||||
...changes,
|
||||
...(!(changes.withServiceJudge ?? current.withServiceJudge)
|
||||
? { waitingListQuickButtons: false }
|
||||
: {})
|
||||
});
|
||||
}
|
||||
|
||||
private async ensureSettings() {
|
||||
@@ -40,7 +45,8 @@ export class SettingsService {
|
||||
id: 1,
|
||||
numberOfCourts: 5,
|
||||
withServiceJudge: true,
|
||||
showAlert: true
|
||||
showAlert: true,
|
||||
waitingListQuickButtons: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { db } from 'db';
|
||||
import { WaitingDutyService } from './waiting-duty.service';
|
||||
|
||||
describe('WaitingDutyService', () => {
|
||||
let service: WaitingDutyService;
|
||||
beforeEach(async () => {
|
||||
await Promise.all([
|
||||
db.settings.clear(),
|
||||
db.umpires.clear(),
|
||||
db.waitingUmpires.clear(),
|
||||
db.waitingServiceJudges.clear(),
|
||||
db.courtUmpires.clear(),
|
||||
db.courtServiceJudges.clear()
|
||||
]);
|
||||
await db.settings.put({
|
||||
id: 1,
|
||||
withServiceJudge: true,
|
||||
waitingListQuickButtons: true,
|
||||
showAlert: false,
|
||||
numberOfCourts: 5
|
||||
});
|
||||
await db.umpires.put({
|
||||
id: 1,
|
||||
firstName: 'Alex',
|
||||
lastName: 'Smith',
|
||||
gender: 'M',
|
||||
country: 'GB'
|
||||
});
|
||||
service = TestBed.inject(WaitingDutyService);
|
||||
});
|
||||
afterEach(async () => {
|
||||
TestBed.resetTestingModule();
|
||||
await Promise.all([
|
||||
db.settings.clear(),
|
||||
db.umpires.clear(),
|
||||
db.waitingUmpires.clear(),
|
||||
db.waitingServiceJudges.clear(),
|
||||
db.courtUmpires.clear(),
|
||||
db.courtServiceJudges.clear()
|
||||
]);
|
||||
});
|
||||
for (const role of ['umpire', 'serviceJudge'] as const) {
|
||||
it(`remembers the ${role} queue and returns at its bottom only once`, async () => {
|
||||
if (role === 'umpire')
|
||||
await db.waitingUmpires.add({ umpireId: 1, order: 1 });
|
||||
else await db.waitingServiceJudges.add({ serviceJudgeId: 1, order: 1 });
|
||||
await service.move(1, 'rest');
|
||||
expect((await db.umpires.get(1))?.returnToQueue).toBe(role);
|
||||
expect(
|
||||
(await db.waitingUmpires.count()) +
|
||||
(await db.waitingServiceJudges.count())
|
||||
).toBe(0);
|
||||
if (role === 'umpire')
|
||||
await db.waitingUmpires.add({ umpireId: 2, order: 1 });
|
||||
else await db.waitingServiceJudges.add({ serviceJudgeId: 2, order: 1 });
|
||||
await Promise.all([service.move(1, 'umpire'), service.move(1, 'umpire')]);
|
||||
const items =
|
||||
role === 'umpire'
|
||||
? await db.waitingUmpires.orderBy('order').toArray()
|
||||
: await db.waitingServiceJudges.orderBy('order').toArray();
|
||||
expect(items.map((item) => item.order)).toEqual([1, 2]);
|
||||
expect((await db.umpires.get(1))?.returnToQueue).toBeUndefined();
|
||||
});
|
||||
}
|
||||
for (const role of ['umpire', 'serviceJudge'] as const) {
|
||||
it(`saves the ${role} origin on a drag move even with quick buttons disabled`, async () => {
|
||||
await db.settings.update(1, { waitingListQuickButtons: false });
|
||||
if (role === 'umpire')
|
||||
await db.waitingUmpires.add({ umpireId: 1, order: 1 });
|
||||
else await db.waitingServiceJudges.add({ serviceJudgeId: 1, order: 1 });
|
||||
await service.moveToRest(1);
|
||||
expect((await db.umpires.get(1))?.returnToQueue).toBe(role);
|
||||
expect(await db.waitingUmpires.count()).toBe(0);
|
||||
expect(await db.waitingServiceJudges.count()).toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
it('does not return officials without a saved origin', async () => {
|
||||
await service.move(1, 'umpire');
|
||||
expect(await db.waitingUmpires.count()).toBe(0);
|
||||
expect(await db.waitingServiceJudges.count()).toBe(0);
|
||||
});
|
||||
|
||||
it('ignores stale return actions for assigned officials', async () => {
|
||||
await db.courtUmpires.add({ umpireId: 1, courtNo: 1 });
|
||||
await service.move(1, 'umpire');
|
||||
expect(await db.waitingUmpires.count()).toBe(0);
|
||||
});
|
||||
it('does nothing when service judges are disabled', async () => {
|
||||
await db.settings.update(1, { withServiceJudge: false });
|
||||
await service.move(1, 'umpire');
|
||||
expect(await db.waitingUmpires.count()).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { db, WaitingRole } from 'db';
|
||||
import { WaitingUmpiresService } from './waiting-umpires.service';
|
||||
import { WaitingServiceJudgesService } from './waiting-service-judges.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class WaitingDutyService {
|
||||
private readonly umpires = inject(WaitingUmpiresService);
|
||||
private readonly judges = inject(WaitingServiceJudgesService);
|
||||
|
||||
async moveToRest(officialId: number): Promise<void> {
|
||||
await this.moveOfficial(officialId, 'rest', false);
|
||||
}
|
||||
|
||||
async move(
|
||||
officialId: number,
|
||||
destination: WaitingRole | 'rest'
|
||||
): Promise<void> {
|
||||
await this.moveOfficial(officialId, destination, true);
|
||||
}
|
||||
|
||||
private async moveOfficial(
|
||||
officialId: number,
|
||||
destination: WaitingRole | 'rest',
|
||||
requireQuickButtons: boolean
|
||||
): Promise<void> {
|
||||
await db.transaction(
|
||||
'rw',
|
||||
[
|
||||
db.settings,
|
||||
db.umpires,
|
||||
db.waitingUmpires,
|
||||
db.waitingServiceJudges,
|
||||
db.courtUmpires,
|
||||
db.courtServiceJudges
|
||||
],
|
||||
async () => {
|
||||
const settings = await db.settings.get(1);
|
||||
const official = await db.umpires.get(officialId);
|
||||
if (
|
||||
!official ||
|
||||
(requireQuickButtons &&
|
||||
(!settings?.withServiceJudge || !settings.waitingListQuickButtons))
|
||||
)
|
||||
return;
|
||||
const umpire = await db.waitingUmpires
|
||||
.where('umpireId')
|
||||
.equals(officialId)
|
||||
.first();
|
||||
const judge = await db.waitingServiceJudges
|
||||
.where('serviceJudgeId')
|
||||
.equals(officialId)
|
||||
.first();
|
||||
const onCourt =
|
||||
(await db.courtUmpires
|
||||
.where('umpireId')
|
||||
.equals(officialId)
|
||||
.count()) ||
|
||||
(await db.courtServiceJudges
|
||||
.where('umpireId')
|
||||
.equals(officialId)
|
||||
.count());
|
||||
if (onCourt) return;
|
||||
if (destination === 'rest') {
|
||||
if (!umpire && !judge) return;
|
||||
await db.umpires.update(officialId, {
|
||||
returnToQueue: umpire ? 'umpire' : 'serviceJudge'
|
||||
});
|
||||
await this.umpires.removeByUmpireId(officialId);
|
||||
await this.judges.removeByUmpireId(officialId);
|
||||
} else {
|
||||
if (umpire || judge || !official.returnToQueue) return;
|
||||
const role = official.returnToQueue;
|
||||
if (role === 'serviceJudge') await this.judges.add(officialId);
|
||||
else await this.umpires.add(officialId);
|
||||
await db.umpires.update(officialId, { returnToQueue: undefined });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,18 @@
|
||||
<ion-label>{{ umpire|fullname }}</ion-label>
|
||||
</div>
|
||||
<ion-label>{{ umpire|fullname }}</ion-label>
|
||||
@if (waitingQuickButtonsEnabled()) {
|
||||
<ion-button
|
||||
class="waiting-duty-button"
|
||||
size="small"
|
||||
fill="solid"
|
||||
[attr.aria-label]="('moveOffDuty' | translate) + ': ' + (umpire | fullname)"
|
||||
[attr.title]="'moveOffDuty' | translate"
|
||||
(pointerdown)="$event.stopPropagation()"
|
||||
(click)="$event.stopPropagation(); changeWaitingDuty(umpire, true)">
|
||||
<ion-icon slot="icon-only" name="exit-outline"></ion-icon>
|
||||
</ion-button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</ion-list>
|
||||
@@ -179,6 +191,18 @@
|
||||
<ion-label>{{ umpire|fullname }}</ion-label>
|
||||
</div>
|
||||
<ion-label>{{ umpire|fullname }}</ion-label>
|
||||
@if (waitingQuickButtonsEnabled()) {
|
||||
<ion-button
|
||||
class="waiting-duty-button"
|
||||
size="small"
|
||||
fill="solid"
|
||||
[attr.aria-label]="('moveOffDuty' | translate) + ': ' + (umpire | fullname)"
|
||||
[attr.title]="'moveOffDuty' | translate"
|
||||
(pointerdown)="$event.stopPropagation()"
|
||||
(click)="$event.stopPropagation(); changeWaitingDuty(umpire, true)">
|
||||
<ion-icon slot="icon-only" name="exit-outline"></ion-icon>
|
||||
</ion-button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</ion-list>
|
||||
@@ -198,6 +222,18 @@
|
||||
<ion-label>{{ umpire|fullname }}</ion-label>
|
||||
</div>
|
||||
<ion-label>{{ umpire|fullname }}</ion-label>
|
||||
@if (waitingQuickButtonsEnabled() && umpire.returnToQueue) {
|
||||
<ion-button
|
||||
class="waiting-duty-button"
|
||||
size="small"
|
||||
fill="solid"
|
||||
[attr.aria-label]="('returnToQueue' | translate) + ': ' + (umpire | fullname)"
|
||||
[attr.title]="'returnToQueue' | translate"
|
||||
(pointerdown)="$event.stopPropagation()"
|
||||
(click)="$event.stopPropagation(); changeWaitingDuty(umpire, false)">
|
||||
<ion-icon slot="icon-only" name="enter-outline"></ion-icon>
|
||||
</ion-button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</ion-list>
|
||||
|
||||
@@ -38,3 +38,18 @@
|
||||
background-color: var(--ion-color-step-150, #d9d9d9);
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-lists .custom-ion-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
ion-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.waiting-duty-button {
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CdkDragDrop } from '@angular/cdk/drag-drop';
|
||||
import { signal } from '@angular/core';
|
||||
import { AlertController } from '@ionic/angular';
|
||||
import { CourtServiceJudge, CourtUmpire, Settings, Umpire } from 'db';
|
||||
import { WaitingDutyService } from '../services/waiting-duty.service';
|
||||
import { Tab1Page } from './tab1.page';
|
||||
import { SettingsService } from '../services/settings-service';
|
||||
import { UmpireService } from '../services/umpire.service';
|
||||
@@ -34,6 +36,7 @@ describe('Tab1Page court rotation', () => {
|
||||
let waitingJudge: jasmine.SpyObj<WaitingServiceJudgesService>;
|
||||
let umpireCourt: jasmine.SpyObj<CourtUmpireService>;
|
||||
let judgeCourt: jasmine.SpyObj<CourtServiceJudgeService>;
|
||||
let duty: jasmine.SpyObj<WaitingDutyService>;
|
||||
let alerts: jasmine.SpyObj<AlertController>;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -72,6 +75,9 @@ describe('Tab1Page court rotation', () => {
|
||||
['save', 'removeByUmpireId'],
|
||||
{ umpires: courtJudges }
|
||||
);
|
||||
duty = jasmine.createSpyObj('WaitingDutyService', ['move', 'moveToRest']);
|
||||
duty.move.and.resolveTo();
|
||||
duty.moveToRest.and.resolveTo();
|
||||
alerts = jasmine.createSpyObj('AlertController', ['create']);
|
||||
waitingUmpire.getCurrentUmpire.and.resolveTo(official(1));
|
||||
waitingJudge.getCurrentUmpire.and.resolveTo(official(2));
|
||||
@@ -85,6 +91,7 @@ describe('Tab1Page court rotation', () => {
|
||||
judgeCourt.removeByUmpireId.and.resolveTo();
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: WaitingDutyService, useValue: duty },
|
||||
{ provide: SettingsService, useValue: { settings } },
|
||||
{ provide: UmpireService, useValue: { umpires } },
|
||||
{ provide: WaitingUmpiresService, useValue: waitingUmpire },
|
||||
@@ -98,6 +105,74 @@ describe('Tab1Page court rotation', () => {
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
for (const source of [
|
||||
'list-waiting-umpires',
|
||||
'list-waiting-service-judges'
|
||||
]) {
|
||||
it(`saves the waiting origin when dropped from ${source} to rest`, async () => {
|
||||
await component.dropToRest({
|
||||
previousContainer: { id: source },
|
||||
container: { id: 'list-on-rest' },
|
||||
item: { data: official(1) }
|
||||
} as CdkDragDrop<Umpire[]>);
|
||||
expect(duty.moveToRest).toHaveBeenCalledOnceWith(1);
|
||||
expect(waitingUmpire.removeByUmpireId).not.toHaveBeenCalled();
|
||||
expect(waitingJudge.removeByUmpireId).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
|
||||
it('moves directly when quick-button alerts are disabled', async () => {
|
||||
settings.update((value) => ({ ...value, waitingListQuickButtons: true }));
|
||||
await component.changeWaitingDuty(official(1), true);
|
||||
expect(duty.move).toHaveBeenCalledOnceWith(1, 'rest');
|
||||
expect(alerts.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores return actions without a saved origin', async () => {
|
||||
settings.update((value) => ({
|
||||
...value,
|
||||
waitingListQuickButtons: true,
|
||||
showAlert: true
|
||||
}));
|
||||
await component.changeWaitingDuty(official(5), false);
|
||||
expect(duty.move).not.toHaveBeenCalled();
|
||||
expect(alerts.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('confirms returning to the saved service-judge queue', async () => {
|
||||
settings.update((value) => ({
|
||||
...value,
|
||||
waitingListQuickButtons: true,
|
||||
showAlert: true
|
||||
}));
|
||||
alerts.create.and.resolveTo({
|
||||
present: async () => undefined
|
||||
} as unknown as HTMLIonAlertElement);
|
||||
await component.changeWaitingDuty(
|
||||
{ ...official(5), returnToQueue: 'serviceJudge' },
|
||||
false
|
||||
);
|
||||
expect(duty.move).not.toHaveBeenCalled();
|
||||
const buttons = alerts.create.calls.mostRecent().args[0]?.buttons;
|
||||
const confirm = buttons?.find(
|
||||
(button) => typeof button !== 'string' && button.role === 'confirm'
|
||||
);
|
||||
if (typeof confirm === 'object' && typeof confirm.handler === 'function')
|
||||
confirm.handler(undefined);
|
||||
expect(duty.move).toHaveBeenCalledOnceWith(5, 'serviceJudge');
|
||||
});
|
||||
|
||||
it('hides and ignores quick actions when service judges are disabled', async () => {
|
||||
settings.update((value) => ({
|
||||
...value,
|
||||
waitingListQuickButtons: true,
|
||||
withServiceJudge: false
|
||||
}));
|
||||
expect(component.waitingQuickButtonsEnabled()).toBeFalse();
|
||||
await component.changeWaitingDuty(official(1), true);
|
||||
expect(duty.move).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('waits for confirmation before assigning officials', async () => {
|
||||
settings.update((value) => ({ ...value, showAlert: true }));
|
||||
const present = jasmine.createSpy('present').and.resolveTo();
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
import { SettingsService } from '../services/settings-service';
|
||||
import { UmpireService } from '../services/umpire.service';
|
||||
import { Umpire } from 'db';
|
||||
import { WaitingDutyService } from '../services/waiting-duty.service';
|
||||
import { WaitingUmpiresService } from '../services/waiting-umpires.service';
|
||||
import { WaitingServiceJudgesService } from '../services/waiting-service-judges.service';
|
||||
import { FullnamePipe } from '../fullname-pipe';
|
||||
@@ -71,6 +72,8 @@ export class Tab1Page {
|
||||
addIcons({ exitOutline, enterOutline });
|
||||
}
|
||||
|
||||
readonly waitingDutyService = inject(WaitingDutyService);
|
||||
|
||||
readonly settingsService = inject(SettingsService);
|
||||
readonly umpireService = inject(UmpireService);
|
||||
readonly waitingUmpireService = inject(WaitingUmpiresService);
|
||||
@@ -285,12 +288,53 @@ export class Tab1Page {
|
||||
return this.settings()?.withServiceJudge ?? false;
|
||||
});
|
||||
|
||||
dropToRest(event: CdkDragDrop<Umpire[]>) {
|
||||
readonly waitingQuickButtonsEnabled = computed(
|
||||
() =>
|
||||
this.serviceJudgeEnabled() && !!this.settings()?.waitingListQuickButtons
|
||||
);
|
||||
|
||||
async changeWaitingDuty(umpire: Umpire, toRest: boolean): Promise<void> {
|
||||
if (!this.waitingQuickButtonsEnabled()) return;
|
||||
const destination = toRest ? 'rest' : umpire.returnToQueue;
|
||||
if (!destination) return;
|
||||
const move = () => this.waitingDutyService.move(umpire.id, destination);
|
||||
if (!this.settings()?.showAlert) {
|
||||
await move();
|
||||
return;
|
||||
}
|
||||
const alert = await this.alertController.create({
|
||||
header: this.translationService.translate('confirmation'),
|
||||
subHeader: this.translationService.translate(
|
||||
toRest ? 'confirmMoveOffDuty' : 'confirmReturnToQueue'
|
||||
),
|
||||
message: this.fullnamePipe.transform(umpire),
|
||||
buttons: [
|
||||
{ text: this.translationService.translate('no'), role: 'cancel' },
|
||||
{
|
||||
text: this.translationService.translate('yes'),
|
||||
role: 'confirm',
|
||||
handler: () => {
|
||||
void move();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
|
||||
async dropToRest(event: CdkDragDrop<Umpire[]>): Promise<void> {
|
||||
if (event.previousContainer === event.container) {
|
||||
return;
|
||||
} else {
|
||||
const comingFrom = event.previousContainer.id;
|
||||
const umpireToMove = event.item.data;
|
||||
if (
|
||||
comingFrom === 'list-waiting-umpires' ||
|
||||
comingFrom === 'list-waiting-service-judges'
|
||||
) {
|
||||
await this.waitingDutyService.moveToRest(umpireToMove.id);
|
||||
return;
|
||||
}
|
||||
this.removeFromOriginalPlace(umpireToMove, comingFrom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,21 @@
|
||||
</ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<div class="waiting-list-setting">
|
||||
<ion-toggle
|
||||
[checked]="currentSettings.waitingListQuickButtons ?? false"
|
||||
[disabled]="!currentSettings.withServiceJudge"
|
||||
aria-describedby="waiting-list-help"
|
||||
(ionChange)="updateWaitingListQuickButtons($event)">
|
||||
{{ 'waitingListQuickButtons' | translate }}
|
||||
</ion-toggle>
|
||||
<p id="waiting-list-help" class="setting-help">
|
||||
{{ 'waitingListQuickButtonsHelp' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-toggle
|
||||
[checked]="currentSettings.showAlert"
|
||||
@@ -79,7 +94,7 @@
|
||||
<ion-col sizeLg="6" pushLg="3">
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-center"
|
||||
>{{ 'version' | translate }}: 2.0.0</ion-label
|
||||
>{{ 'version' | translate }}: 2.1.0</ion-label
|
||||
>
|
||||
</ion-item>
|
||||
</ion-col>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
.waiting-list-setting {
|
||||
width: 100%;
|
||||
padding-block: 12px;
|
||||
|
||||
ion-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-help {
|
||||
margin: 8px 0 0;
|
||||
color: var(--ion-color-medium-shade);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
@@ -99,6 +99,14 @@ export class Tab3Page {
|
||||
}
|
||||
}
|
||||
|
||||
async updateWaitingListQuickButtons(
|
||||
event: CustomEvent<{ checked: boolean }>
|
||||
): Promise<void> {
|
||||
await this.settingsService.update({
|
||||
waitingListQuickButtons: event.detail.checked
|
||||
});
|
||||
}
|
||||
|
||||
async updateShowAlert(event: CustomEvent): Promise<void> {
|
||||
await this.settingsService.update({
|
||||
showAlert: event.detail.checked
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"types": ["jasmine"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "db.ts"]
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.spec.json" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"paths": { "db": ["./db.ts"] },
|
||||
"outDir": "./dist/out-tsc",
|
||||
|
||||
Reference in new issue
Block a user