fix: standardize locked-vs-hidden treatment across all 9 per-tier gated features (v0.79.0)
Rule, applied consistently everywhere via a new isFeatureAvailableAnyTier() helper: if a feature is enabled on at least one tier, it stays visible for locked-out viewers with a small "Pro" badge and opens an upgrade prompt on click; if a feature is disabled on every tier, it hides entirely, since there's no upgrade path to point at. Covers: recipe variations, meal/drink pairings, nutrition estimation, Markdown export (5 call sites), weekly nutrition, import from URL, import from photo, and the Instacart grocery-delivery menu item. Previously inconsistent — some hid outright, one showed a lock icon overlapping its own icon. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ import { getShoppingListAccess, canWriteShoppingList } from "@/lib/shopping-list
|
||||
import { ExportMarkdownButton } from "@/components/shared/export-markdown-button";
|
||||
import { shoppingListToMarkdown } from "@/lib/markdown/shopping-list";
|
||||
import { getMessages, formatMessage } from "@/lib/i18n/server";
|
||||
import { getFeatureFlagMatrix, type Tier } from "@/lib/feature-flags";
|
||||
import { getFeatureFlagMatrix, isFeatureAvailableAnyTier, type Tier } from "@/lib/feature-flags";
|
||||
|
||||
type Params = { params: Promise<{ id: string }> };
|
||||
|
||||
@@ -40,8 +40,11 @@ export default async function ShoppingListPage({ params }: Params) {
|
||||
const canEdit = canWriteShoppingList(access.role);
|
||||
const viewerTier = (session.user as { tier?: string }).tier as Tier | undefined ?? "free";
|
||||
const featureFlags = await getFeatureFlagMatrix();
|
||||
const canExportMarkdown = featureFlags.markdown_export[viewerTier];
|
||||
const instacartEnabled = process.env["NEXT_PUBLIC_GROCERY_PROVIDER"] === "instacart" && featureFlags.grocery_delivery[viewerTier];
|
||||
const markdownExportLocked = !featureFlags.markdown_export[viewerTier];
|
||||
const markdownExportAvailable = isFeatureAvailableAnyTier(featureFlags, "markdown_export");
|
||||
const instacartProviderConfigured = process.env["NEXT_PUBLIC_GROCERY_PROVIDER"] === "instacart";
|
||||
const instacartLocked = !featureFlags.grocery_delivery[viewerTier];
|
||||
const instacartAvailable = instacartProviderConfigured && isFeatureAvailableAnyTier(featureFlags, "grocery_delivery");
|
||||
|
||||
return (
|
||||
<div className="space-y-6 max-w-2xl">
|
||||
@@ -55,7 +58,7 @@ export default async function ShoppingListPage({ params }: Params) {
|
||||
</div>
|
||||
<TooltipProvider>
|
||||
<div className="flex items-center gap-2 overflow-x-auto pb-0.5 [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden">
|
||||
<GroceryExportButton listId={id} instacartEnabled={instacartEnabled} />
|
||||
<GroceryExportButton listId={id} instacartAvailable={instacartAvailable} instacartLocked={instacartLocked} />
|
||||
{access.role === "owner" && (
|
||||
<ShareShoppingListButton listId={id} initialIsPublic={list.isPublic} initialPublicEditable={list.publicEditable} />
|
||||
)}
|
||||
@@ -67,10 +70,11 @@ export default async function ShoppingListPage({ params }: Params) {
|
||||
} />
|
||||
<TooltipContent>{m.common.print}</TooltipContent>
|
||||
</Tooltip>
|
||||
{canExportMarkdown && (
|
||||
{markdownExportAvailable && (
|
||||
<ExportMarkdownButton
|
||||
markdown={shoppingListToMarkdown({ name: list.name, items: list.items })}
|
||||
filename={list.name}
|
||||
locked={markdownExportLocked}
|
||||
/>
|
||||
)}
|
||||
{access.role === "owner" && (
|
||||
|
||||
Reference in New Issue
Block a user