version 1.0.0
All checks were successful
Tests et déploiement / validate (push) Successful in 2m43s
Tests et déploiement / deploy (push) Successful in 1m21s

This commit is contained in:
2026-10-11 12:51:47 +02:00
parent 017eb8cc6a
commit 64f61004d1
9 changed files with 40 additions and 6 deletions

View File

@@ -5,6 +5,8 @@ elle peut démarrer automatiquement la journée et proposer sa clôture. Chaque
commence là où la précédente s’est terminée. Les journées clôturées ont un commence là où la précédente s’est terminée. Les journées clôturées ont un
récapitulatif concis, prêt à être recopié dans un registre papier. récapitulatif concis, prêt à être recopié dans un registre papier.
Version actuelle de l’application : **1.0.0**.
Conçue pour l’extérieur : contraste élevé, grandes zones tactiles et utilisable Conçue pour l’extérieur : contraste élevé, grandes zones tactiles et utilisable
avec des gants. Les données sont enregistrées localement dans IndexedDB. Une avec des gants. Les données sont enregistrées localement dans IndexedDB. Une
synchronisation facultative chiffrée de bout en bout les partage entre appareils synchronisation facultative chiffrée de bout en bout les partage entre appareils

View File

@@ -5,6 +5,8 @@ the app is open, it can start a workday automatically and prompt to close it. Ea
task starts when the previous one ends. Closed days have concise summaries ready to task starts when the previous one ends. Closed days have concise summaries ready to
copy into a paper logbook. copy into a paper logbook.
Current application version: **1.0.0**.
Designed for outdoor use: high contrast, large touch targets, and usable with gloves. Designed for outdoor use: high contrast, large touch targets, and usable with gloves.
Data is stored locally in IndexedDB. Optional end-to-end encrypted synchronization Data is stored locally in IndexedDB. Optional end-to-end encrypted synchronization
shares it between authorized devices without requiring an email address or password. shares it between authorized devices without requiring an email address or password.

View File

@@ -44,7 +44,7 @@ http://{$SITE_ADDRESS} {
# If a browser is allowed to serve sw.js from its own cache, a new build is # If a browser is allowed to serve sw.js from its own cache, a new build is
# never discovered and the in-app update prompt can never fire — the app would # never discovered and the in-app update prompt can never fire — the app would
# silently freeze on whatever version it first installed. # silently freeze on whatever version it first installed.
@volatile path / /index.html /sw.js /manifest.webmanifest @volatile path / /index.html /sw.js /manifest.webmanifest /app-version.json
header @volatile Cache-Control "no-cache, must-revalidate" header @volatile Cache-Control "no-cache, must-revalidate"
# Content-hashed filenames; the name changes whenever the bytes do. # Content-hashed filenames; the name changes whenever the bytes do.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "pardis", "name": "pardis",
"version": "0.8.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pardis", "name": "pardis",
"version": "0.8.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@hpke/core": "^1.9.0", "@hpke/core": "^1.9.0",
"@zxing/browser": "^0.2.1", "@zxing/browser": "^0.2.1",

View File

@@ -1,7 +1,7 @@
{ {
"name": "pardis", "name": "pardis",
"private": true, "private": true,
"version": "0.8.0", "version": "1.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -4,7 +4,7 @@
import { loadBackupStatus } from './lib/backup-state.svelte.js'; import { loadBackupStatus } from './lib/backup-state.svelte.js';
import { go, router } from './lib/router.svelte.js'; import { go, router } from './lib/router.svelte.js';
import { loadSettings, settings } from './lib/settings.svelte.js'; import { loadSettings, settings } from './lib/settings.svelte.js';
import { needsRefresh, applyUpdate } from './lib/updates.svelte.js'; import { needsRefresh, availableVersion, applyUpdate } from './lib/updates.svelte.js';
import Resume from './routes/Resume.svelte'; import Resume from './routes/Resume.svelte';
import Settings from './routes/Settings.svelte'; import Settings from './routes/Settings.svelte';
import Today from './routes/Today.svelte'; import Today from './routes/Today.svelte';
@@ -105,7 +105,7 @@
<!-- Only offered while no day is running, so an update can never interrupt <!-- Only offered while no day is running, so an update can never interrupt
a day in progress. --> a day in progress. -->
<div class="update"> <div class="update">
<span>Mise à jour disponible</span> <span>Mise à jour disponible{#if availableVersion.value} (v{availableVersion.value}){/if}</span>
<button type="button" disabled={dayState.importing} onclick={applyUpdate}>Installer</button> <button type="button" disabled={dayState.importing} onclick={applyUpdate}>Installer</button>
</div> </div>
{/if} {/if}

View File

@@ -13,6 +13,19 @@ import { registerSW } from 'virtual:pwa-register';
const CHECK_INTERVAL = 60 * 60 * 1000; const CHECK_INTERVAL = 60 * 60 * 1000;
export const needsRefresh = $state({ value: false }); export const needsRefresh = $state({ value: false });
export const availableVersion = $state({ value: '' });
async function loadAvailableVersion() {
availableVersion.value = '';
try {
const response = await fetch('/app-version.json', { cache: 'no-store' });
if (!response.ok) return;
const result = await response.json();
if (typeof result.version === 'string') availableVersion.value = result.version;
} catch {
// The update prompt remains useful if a proxy briefly makes version data unavailable.
}
}
/** @type {(reloadPage?: boolean) => Promise<void>} */ /** @type {(reloadPage?: boolean) => Promise<void>} */
let update = async () => {}; let update = async () => {};
@@ -22,6 +35,7 @@ if (typeof window !== 'undefined') {
immediate: true, immediate: true,
onNeedRefresh() { onNeedRefresh() {
needsRefresh.value = true; needsRefresh.value = true;
void loadAvailableVersion();
}, },
onRegisteredSW(_url, registration) { onRegisteredSW(_url, registration) {
if (!registration) return; if (!registration) return;

View File

@@ -10,6 +10,7 @@
enregistre les journées, les tâches, les clients, les repas et les pauses, enregistre les journées, les tâches, les clients, les repas et les pauses,
puis prépare un récapitulatif à consulter ou à copier. puis prépare un récapitulatif à consulter ou à copier.
</p> </p>
<p class="dim">Version 1.0.0</p>
<p>L'application, son code source, et le service de synchronisation sont fournis gratuitement et <p>L'application, son code source, et le service de synchronisation sont fournis gratuitement et
sans aucune garantie.</p> sans aucune garantie.</p>

View File

@@ -1,7 +1,21 @@
import { readFileSync } from 'node:fs';
import { svelte } from '@sveltejs/vite-plugin-svelte'; import { svelte } from '@sveltejs/vite-plugin-svelte';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa'; import { VitePWA } from 'vite-plugin-pwa';
const { version: appVersion } = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
const appVersionAsset = {
name: 'pardis-app-version',
generateBundle() {
this.emitFile({
type: 'asset',
fileName: 'app-version.json',
source: JSON.stringify({ version: appVersion }),
});
},
};
export default defineConfig({ export default defineConfig({
server: { server: {
host: '0.0.0.0', host: '0.0.0.0',
@@ -9,6 +23,7 @@ export default defineConfig({
}, },
preview: { proxy: { '/api/sync': 'http://127.0.0.1:3001' } }, preview: { proxy: { '/api/sync': 'http://127.0.0.1:3001' } },
plugins: [ plugins: [
appVersionAsset,
svelte(), svelte(),
VitePWA({ VitePWA({
// Never `autoUpdate`: a worker swap mid-day reloads the page under the // Never `autoUpdate`: a worker swap mid-day reloads the page under the