mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(capture): leave Mood and Medication out of the add menu with their modules off
This commit is contained in:
1 parent
94e07d0d90
commit
a4f8e6db27
3 files changed
+53
-5
No files matched your search
@@ -24,6 +24,11 @@ vi.mock("@/hooks/use-record-capabilities", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
// Every module on: this file is about the discard guard, not the module mask.
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({ user: { modules: {} } }),
|
||||
}));
|
||||
|
||||
vi.mock("react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof ReactModule>();
|
||||
return {
|
||||
|
||||
@@ -17,6 +17,14 @@ vi.mock("@/hooks/use-record-capabilities", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
// The record's resolved module map; each test sets what it needs.
|
||||
const modulesRef: { value: Record<string, boolean> | undefined } = {
|
||||
value: undefined,
|
||||
};
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({ user: { modules: modulesRef.value } }),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ui/responsive-sheet", () => ({
|
||||
ResponsiveSheet: ({
|
||||
open,
|
||||
@@ -51,6 +59,7 @@ function render() {
|
||||
|
||||
describe("<CapturePicker> — offered kinds", () => {
|
||||
it("offers measurement, medication and mood, and no water entry", () => {
|
||||
modulesRef.value = undefined;
|
||||
const html = render();
|
||||
|
||||
expect(html).toContain('data-testid="capture-picker-measurement"');
|
||||
@@ -61,3 +70,20 @@ describe("<CapturePicker> — offered kinds", () => {
|
||||
expect(html).not.toContain('data-testid="capture-picker-water"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("<CapturePicker> — switched-off modules", () => {
|
||||
it("drops Mood with the mood module off and Medication with medications off", () => {
|
||||
modulesRef.value = { mood: false, medications: false };
|
||||
const html = render();
|
||||
expect(html).toContain('data-testid="capture-picker-measurement"');
|
||||
expect(html).not.toContain('data-testid="capture-picker-mood"');
|
||||
expect(html).not.toContain('data-testid="capture-picker-medication"');
|
||||
});
|
||||
|
||||
it("keeps both while their modules are on", () => {
|
||||
modulesRef.value = { mood: true, medications: true };
|
||||
const html = render();
|
||||
expect(html).toContain('data-testid="capture-picker-mood"');
|
||||
expect(html).toContain('data-testid="capture-picker-medication"');
|
||||
});
|
||||
});
|
||||
@@ -19,7 +19,9 @@ import {
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { sheetBodyHasUnsavedInput } from "@/components/dashboard/quick-entry-sheets";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { isSurfaceVisible, type SurfaceModuleMap } from "@/lib/modules/surface";
|
||||
import type { ShareDomain } from "@/lib/sharing/scope";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
useRecordCapabilities,
|
||||
type RecordCapabilities,
|
||||
@@ -106,15 +108,25 @@ export function admittedCaptureKind(
|
||||
}
|
||||
|
||||
/**
|
||||
* The capture kinds to offer, given what the record allows. Exported pure so
|
||||
* the delegation rule can be pinned without opening a sheet: an SSR test
|
||||
* cannot tap the button that opens it.
|
||||
* The capture kinds to offer, given what the record allows and which of its
|
||||
* modules are on. Exported pure so both rules can be pinned without opening a
|
||||
* sheet: an SSR test cannot tap the button that opens it.
|
||||
*
|
||||
* A kind whose module is off (`capture:<kind>` in the surface map) is not
|
||||
* offered: a Mood entry with the mood module off would post to a route that
|
||||
* refuses it. An absent module map (the account still loading) offers every
|
||||
* kind the record allows, the gate's default-on contract.
|
||||
*/
|
||||
export function visibleCaptureKinds(
|
||||
caps: Pick<RecordCapabilities, "canWriteDomain">,
|
||||
kinds: ReadonlyArray<CaptureKind>,
|
||||
modules?: SurfaceModuleMap | null,
|
||||
): CaptureKind[] {
|
||||
return kinds.filter((kind) => caps.canWriteDomain(CAPTURE_KIND_DOMAIN[kind]));
|
||||
return kinds.filter(
|
||||
(kind) =>
|
||||
caps.canWriteDomain(CAPTURE_KIND_DOMAIN[kind]) &&
|
||||
isSurfaceVisible(`capture:${kind}`, modules),
|
||||
);
|
||||
}
|
||||
|
||||
interface CapturePickerProps {
|
||||
@@ -127,7 +139,12 @@ interface CapturePickerProps {
|
||||
export function CapturePicker({ open, onOpenChange }: CapturePickerProps) {
|
||||
const { t } = useTranslations();
|
||||
const capabilities = useRecordCapabilities();
|
||||
const offered = visibleCaptureKinds(capabilities, CAPTURE_KIND_ORDER);
|
||||
const { user } = useAuth();
|
||||
const offered = visibleCaptureKinds(
|
||||
capabilities,
|
||||
CAPTURE_KIND_ORDER,
|
||||
user?.modules,
|
||||
);
|
||||
const [kind, setKind] = useState<CaptureKind | null>(null);
|
||||
// Re-derived on every render, never latched: the offer can shrink under a
|
||||
// sheet that is already open. See `admittedCaptureKind`.
|
||||
|
||||
Reference in new issue
Block a user