diff --git a/apps/web/cypress/e2e/app-settings-page.cy.ts b/apps/web/cypress/e2e/app-settings-page.cy.ts
index 9309d95..14e6d7a 100644
--- a/apps/web/cypress/e2e/app-settings-page.cy.ts
+++ b/apps/web/cypress/e2e/app-settings-page.cy.ts
@@ -1,16 +1,14 @@
// Mocks the API via cy.intercept — this job doesn't run a live backend (see
-// .github/workflows/ci.yml); apps/api's own Mocha suite covers real API
-// behavior against a real database.
+// .github/workflows/ci.yml).
//
-// Cypress always runs as a plain web browser — `Capacitor.isNativePlatform()`
-// is never true here, so only this page's web-side content (the build
-// version + the "stay on web" preference) is reachable from this spec. The
-// native-only half (App.getInfo()/checkNow's actual version-comparison
-// network call) has no seam to fake native from a web run — same
-// untested-on-purpose posture already accepted for `useAppUpdateCheck`'s
-// background check and `UpdateBanner` (neither has e2e coverage either);
-// the underlying comparison logic itself is unit-tested in
-// version-compare.cy.tsx, unchanged by this ticket.
+// Issue #107 — "Paramètres d'application" is packaged-app only now. Its
+// content (installed version + update check) is inert on the web, and the
+// one web-relevant control it carried (the "stay on web" redirect toggle)
+// moved to `/telecharger` (see download-page.cy.ts). Cypress always runs as
+// a plain web browser (`Capacitor.isNativePlatform()` is never true here),
+// so the page itself has no seam to reach — same untested-on-purpose
+// posture already accepted for `useAppUpdateCheck` / `UpdateBanner`. This
+// spec now only covers the web-side gating.
const authenticatedProfile = {
id: 1,
@@ -22,47 +20,21 @@ const authenticatedProfile = {
dietId: null,
};
-const STAY_ON_WEB_KEY = "batchcooking:stayOnWeb";
-
-describe("App settings (/parametres/application)", () => {
+describe("App settings — hidden on the web (#107)", () => {
beforeEach(() => {
cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile });
+ cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: null });
});
- it("shows the web build version, and the update-check section only on native", () => {
- cy.visit("/parametres/application");
-
- cy.contains("h1", "Paramètres d'application").should("be.visible");
- // The exact build number isn't asserted (would couple this test to
- // package.json's version, bumped independently of this page) — just
- // that a "Version X" line rendered at all, proving `useInstalledVersion`
- // resolved something rather than being stuck empty.
- cy.contains(".app-settings__version", "Version").should("be.visible");
-
- cy.contains("button", "Vérifier les mises à jour").should("not.exist");
- cy.contains(
- "La vérification des mises à jour n'est disponible que dans l'application mobile",
- ).should("be.visible");
+ it("is absent from the Paramètres menu", () => {
+ cy.visit("/");
+ cy.contains("button", "Paramètres").click();
+ cy.contains("a", "Paramètres d'application").should("not.exist");
});
- it("defaults 'stay on web' to unchecked, and persists a toggle across a reload", () => {
+ it("redirects /parametres/application to /parametres/compte instead of an orphaned page", () => {
cy.visit("/parametres/application");
-
- cy.contains("label", "Toujours rester sur le site web")
- .find('input[type="checkbox"]')
- .should("not.be.checked");
-
- cy.contains("label", "Toujours rester sur le site web").click();
- cy.contains("label", "Toujours rester sur le site web")
- .find('input[type="checkbox"]')
- .should("be.checked");
- cy.window().then((win) => {
- expect(win.localStorage.getItem(STAY_ON_WEB_KEY)).to.equal("true");
- });
-
- cy.reload();
- cy.contains("label", "Toujours rester sur le site web")
- .find('input[type="checkbox"]')
- .should("be.checked");
+ cy.url().should("include", "/parametres/compte");
+ cy.contains("h1", "Paramètres d'application").should("not.exist");
});
});
diff --git a/apps/web/cypress/e2e/download-page.cy.ts b/apps/web/cypress/e2e/download-page.cy.ts
index 1e09f17..7fc3aa8 100644
--- a/apps/web/cypress/e2e/download-page.cy.ts
+++ b/apps/web/cypress/e2e/download-page.cy.ts
@@ -23,4 +23,29 @@ describe("DownloadPage — the mobile-app-redirect fallback", () => {
cy.visit("/telecharger");
cy.url().should("include", "/telecharger");
});
+
+ // Issue #107 — the "toujours rester sur le site web" preference moved
+ // here from "Paramètres d'application" (packaged-app only now). Written to
+ // `localStorage` on the public web origin, which is where
+ // `attemptOpenNativeApp` reads it.
+ it("defaults 'stay on web' to unchecked, and persists a toggle across a reload", () => {
+ cy.visit("/telecharger");
+
+ cy.contains("label", "Toujours rester sur le site web")
+ .find('input[type="checkbox"]')
+ .should("not.be.checked");
+
+ cy.contains("label", "Toujours rester sur le site web").click();
+ cy.contains("label", "Toujours rester sur le site web")
+ .find('input[type="checkbox"]')
+ .should("be.checked");
+ cy.window().then((win) => {
+ expect(win.localStorage.getItem("batchcooking:stayOnWeb")).to.equal("true");
+ });
+
+ cy.reload();
+ cy.contains("label", "Toujours rester sur le site web")
+ .find('input[type="checkbox"]')
+ .should("be.checked");
+ });
});
diff --git a/apps/web/cypress/e2e/sidebar.cy.ts b/apps/web/cypress/e2e/sidebar.cy.ts
index d1b94e1..f1543ed 100644
--- a/apps/web/cypress/e2e/sidebar.cy.ts
+++ b/apps/web/cypress/e2e/sidebar.cy.ts
@@ -25,7 +25,7 @@ describe("Sidebar — settings menu and account menu", () => {
cy.get(".app-sidebar__nav a").should("not.contain", "Foyer");
});
- it("reveals the four settings pages behind the Paramètres toggle", () => {
+ it("reveals the settings pages behind the Paramètres toggle", () => {
cy.visit("/");
cy.contains("a", "Compte").should("not.exist");
@@ -43,11 +43,9 @@ describe("Sidebar — settings menu and account menu", () => {
"href",
"/parametres/preferences-utilisateur",
);
- cy.contains("a", "Paramètres d'application").should(
- "have.attr",
- "href",
- "/parametres/application",
- );
+ // "Paramètres d'application" is packaged-app only (issue #107) — Cypress
+ // runs as a plain web browser, so it's filtered out here.
+ cy.contains("a", "Paramètres d'application").should("not.exist");
});
it("opens the account menu from the greeting and links to Mon compte", () => {
diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx
index 9bacd80..f221d9d 100644
--- a/apps/web/src/App.tsx
+++ b/apps/web/src/App.tsx
@@ -1,3 +1,4 @@
+import { Capacitor } from "@capacitor/core";
import { Navigate, Outlet, Route, Routes } from "react-router-dom";
import { AdminAuthProvider } from "./features/admin/AdminAuthContext";
import { RequireAdmin } from "./features/admin/RequireAdmin";
@@ -108,7 +109,21 @@ export function App() {
} />
} />
} />
- } />
+ {/* Native app only (issue #107): on the web its sections are inert
+ (no "installed version", the update check is a no-op) or have
+ moved to `/telecharger` (the stay-on-web toggle), and the nav
+ entry is hidden — so a stray bookmark redirects rather than
+ landing on an orphaned page (same pattern as `/foyer` below). */}
+
+ ) : (
+
+ )
+ }
+ />
} />
} />
diff --git a/apps/web/src/layouts/AppLayout.tsx b/apps/web/src/layouts/AppLayout.tsx
index 7ad72db..69dca5b 100644
--- a/apps/web/src/layouts/AppLayout.tsx
+++ b/apps/web/src/layouts/AppLayout.tsx
@@ -1,3 +1,5 @@
+import { Capacitor } from "@capacitor/core";
+import type { LucideIcon } from "lucide-react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom";
@@ -41,19 +43,44 @@ const NAV_ITEMS = [
},
] as const;
+/** One entry in the "Paramètres" nav — see {@link SETTINGS_ITEMS}. */
+interface SettingsNavItem {
+ to: string;
+ /** Maps to `layout.settings.nav.` in `locales/fr/translation.json`. */
+ key: string;
+ Icon: LucideIcon;
+ /**
+ * Hidden entirely outside the packaged app (issue #107): its page's
+ * options — the version/update check, and the "stay on the website"
+ * redirect toggle — are either inert on the web or have moved to
+ * `/telecharger`. The route itself redirects away on the web too (see
+ * `App.tsx`).
+ */
+ nativeOnly?: boolean;
+}
+
/**
- * One entry in the "Paramètres" nav — revealed by {@link SettingsMenu} on
- * desktop, listed inside {@link MobileMoreSheet} on mobile. `key` maps to
- * `layout.settings.nav.`.
+ * The "Paramètres" nav entries — revealed by {@link SettingsMenu} on
+ * desktop, listed inside {@link MobileMoreSheet} on mobile. Filtered to
+ * {@link settingsNavItems} for actual rendering.
*/
-const SETTINGS_ITEMS = [
+const SETTINGS_ITEMS: SettingsNavItem[] = [
{ to: "/parametres/compte", key: "account", Icon: AccountIcon },
{ to: "/parametres/preferences", key: "preferences", Icon: DietPreferencesIcon },
{ to: "/parametres/foyer", key: "household", Icon: HouseholdIcon },
{ to: "/parametres/preferences-utilisateur", key: "userPreferences", Icon: UserPreferencesIcon },
- { to: "/parametres/application", key: "application", Icon: AppSettingsIcon },
+ { to: "/parametres/application", key: "application", Icon: AppSettingsIcon, nativeOnly: true },
{ to: "/parametres/credits", key: "credits", Icon: CreditsIcon },
-] as const;
+];
+
+/**
+ * What's actually rendered — {@link SETTINGS_ITEMS} minus the `nativeOnly`
+ * ones on the web. `Capacitor.isNativePlatform()` is fixed for the life of
+ * the page, so this is settled once at module load rather than per render.
+ */
+const settingsNavItems: SettingsNavItem[] = SETTINGS_ITEMS.filter(
+ (item) => Capacitor.isNativePlatform() || !item.nativeOnly,
+);
/**
* Shell for every authenticated page: on desktop/tablet, a sidebar (brand,
@@ -220,7 +247,7 @@ function SettingsMenu() {
{isOpen && (