refactor(gating): one constant for the split's feature key
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m56s
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m56s
Review of PR #452 (comment 24129): "adjustments" was written three times for the split (the page's gate, the button's edition, the dialog's locked screen). SPLIT_FEATURE in splitGate.ts is now the one source, so moving the split to its own key or to Premium is a one-line change. No behaviour change; setting it to "budget" turns 2 tests red. Refs #369 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d5d0e89d2c
commit
a8c3fd6997
4 changed files with 16 additions and 8 deletions
|
|
@ -2,7 +2,7 @@ import { useTranslation } from "react-i18next";
|
||||||
import { Lock, Split } from "lucide-react";
|
import { Lock, Split } from "lucide-react";
|
||||||
import type { TransactionRow } from "../../shared/types";
|
import type { TransactionRow } from "../../shared/types";
|
||||||
import { requiredTierFor } from "../../shared/entitlements";
|
import { requiredTierFor } from "../../shared/entitlements";
|
||||||
import type { SplitAccess } from "../../shared/splitGate";
|
import { SPLIT_FEATURE, type SplitAccess } from "../../shared/splitGate";
|
||||||
|
|
||||||
interface SplitButtonProps {
|
interface SplitButtonProps {
|
||||||
row: TransactionRow;
|
row: TransactionRow;
|
||||||
|
|
@ -40,7 +40,7 @@ export default function SplitButton({ row, access, onOpenSplit, onOpenUpsell }:
|
||||||
title={
|
title={
|
||||||
locked
|
locked
|
||||||
? t("transactions.splitLocked", {
|
? t("transactions.splitLocked", {
|
||||||
tier: t(`license.editions.${requiredTierFor("adjustments")}`),
|
tier: t(`license.editions.${requiredTierFor(SPLIT_FEATURE)}`),
|
||||||
})
|
})
|
||||||
: t("transactions.splitAdjustment")
|
: t("transactions.splitAdjustment")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import type {
|
||||||
SplitChild,
|
SplitChild,
|
||||||
} from "../../shared/types";
|
} from "../../shared/types";
|
||||||
import type { LinkedTransferTooltipRow } from "../../services/balance.service";
|
import type { LinkedTransferTooltipRow } from "../../services/balance.service";
|
||||||
import type { SplitAccess } from "../../shared/splitGate";
|
import { SPLIT_FEATURE, type SplitAccess } from "../../shared/splitGate";
|
||||||
import { onboardingTargetAttributes } from "../../utils/onboardingPath";
|
import { onboardingTargetAttributes } from "../../utils/onboardingPath";
|
||||||
import CategoryCombobox from "../shared/CategoryCombobox";
|
import CategoryCombobox from "../shared/CategoryCombobox";
|
||||||
import UpsellDialog from "../shared/UpsellDialog";
|
import UpsellDialog from "../shared/UpsellDialog";
|
||||||
|
|
@ -28,7 +28,7 @@ interface TransactionTableProps {
|
||||||
onDeleteSplit: (parentId: number) => Promise<void>;
|
onDeleteSplit: (parentId: number) => Promise<void>;
|
||||||
/**
|
/**
|
||||||
* What the Répartition button does (#369): `splitAccessFor` over
|
* What the Répartition button does (#369): `splitAccessFor` over
|
||||||
* `useEntitlement("adjustments")`. Required on purpose: a default of "open"
|
* `useEntitlement(SPLIT_FEATURE)`. Required on purpose: a default of "open"
|
||||||
* would let a new caller leave the split ungated without a word.
|
* would let a new caller leave the split ungated without a word.
|
||||||
*/
|
*/
|
||||||
splitAccess: SplitAccess;
|
splitAccess: SplitAccess;
|
||||||
|
|
@ -351,7 +351,7 @@ export default function TransactionTable({
|
||||||
)}
|
)}
|
||||||
{/* Symmetric: the upsell goes away once the edition resolves to Base or Premium. */}
|
{/* Symmetric: the upsell goes away once the edition resolves to Base or Premium. */}
|
||||||
{showSplitUpsell && splitAccess !== "open" && (
|
{showSplitUpsell && splitAccess !== "open" && (
|
||||||
<UpsellDialog feature="adjustments" onClose={() => setShowSplitUpsell(false)} />
|
<UpsellDialog feature={SPLIT_FEATURE} onClose={() => setShowSplitUpsell(false)} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import EmptyState from "../components/shared/EmptyState";
|
||||||
import OnboardingStepCard from "../components/onboarding/OnboardingStepCard";
|
import OnboardingStepCard from "../components/onboarding/OnboardingStepCard";
|
||||||
import { useOnboarding } from "../contexts/OnboardingContext";
|
import { useOnboarding } from "../contexts/OnboardingContext";
|
||||||
import { useEntitlement } from "../hooks/useEntitlement";
|
import { useEntitlement } from "../hooks/useEntitlement";
|
||||||
import { splitAccessFor } from "../shared/splitGate";
|
import { SPLIT_FEATURE, splitAccessFor } from "../shared/splitGate";
|
||||||
import {
|
import {
|
||||||
readOnboardingNavState,
|
readOnboardingNavState,
|
||||||
showsStepCard,
|
showsStepCard,
|
||||||
|
|
@ -44,7 +44,7 @@ export default function TransactionsPage() {
|
||||||
const onboarding = useOnboarding();
|
const onboarding = useOnboarding();
|
||||||
// The Répartition button belongs to the Base edition (#369): locked with the
|
// The Répartition button belongs to the Base edition (#369): locked with the
|
||||||
// upsell on Free, inert while the license is not ready. The page itself stays Free.
|
// upsell on Free, inert while the license is not ready. The page itself stays Free.
|
||||||
const splitGate = useEntitlement("adjustments");
|
const splitGate = useEntitlement(SPLIT_FEATURE);
|
||||||
const [resultMessage, setResultMessage] = useState<string | null>(null);
|
const [resultMessage, setResultMessage] = useState<string | null>(null);
|
||||||
const [menu, setMenu] = useState<{ x: number; y: number; row: TransactionRow } | null>(null);
|
const [menu, setMenu] = useState<{ x: number; y: number; row: TransactionRow } | null>(null);
|
||||||
const [pending, setPending] = useState<TransactionRow | null>(null);
|
const [pending, setPending] = useState<TransactionRow | null>(null);
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,19 @@
|
||||||
* license refresh — never locked, never open on a fail-closed `allowed`.
|
* license refresh — never locked, never open on a fail-closed `allowed`.
|
||||||
*/
|
*/
|
||||||
import type { EntitlementGate } from "./profileGate";
|
import type { EntitlementGate } from "./profileGate";
|
||||||
|
import type { FeatureKey } from "./entitlements";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The feature the split belongs to. The page reads its gate, the button names
|
||||||
|
* its edition and the dialog shows its locked screen from this one constant, so
|
||||||
|
* moving the split (to its own key, to Premium) is a one-line change.
|
||||||
|
*/
|
||||||
|
export const SPLIT_FEATURE: FeatureKey = "adjustments";
|
||||||
|
|
||||||
/** What the split button does: open the split window, show the upsell, or nothing yet. */
|
/** What the split button does: open the split window, show the upsell, or nothing yet. */
|
||||||
export type SplitAccess = "open" | "locked" | "pending";
|
export type SplitAccess = "open" | "locked" | "pending";
|
||||||
|
|
||||||
/** The split button's access, from the gate `useEntitlement("adjustments")` returns. */
|
/** The split button's access, from the gate `useEntitlement(SPLIT_FEATURE)` returns. */
|
||||||
export function splitAccessFor(gate: EntitlementGate): SplitAccess {
|
export function splitAccessFor(gate: EntitlementGate): SplitAccess {
|
||||||
if (!gate.ready) return "pending";
|
if (!gate.ready) return "pending";
|
||||||
return gate.allowed ? "open" : "locked";
|
return gate.allowed ? "open" : "locked";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue