diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx
index b88dcca186..10e7b74bd4 100644
--- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx
+++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/products/page-client-catalogs-view.tsx
@@ -497,12 +497,14 @@ function ProductItemRow({
if (isEditing) {
return (
-
-
+
+
- {itemId}
+
+ {itemDisplayName}
+
@@ -554,16 +556,54 @@ function ProductItemRow({
-
{
- const v = e.target.value;
- if (v === '' || /^\d*$/.test(v)) setQuantity(v);
- if (!readOnly && (v === '' || /^\d*$/.test(v))) updateParent(v);
- }}
- />
+
+ {
+ const v = e.target.value;
+ if (v === '' || /^\d*$/.test(v)) setQuantity(v);
+ if (!readOnly && (v === '' || /^\d*$/.test(v))) updateParent(v);
+ }}
+ />
+ {onRemove && (
+
+
+
+ )}
+
+
+
+
+
+
+
+ {item.expires === 'never' ? 'Never expires' : `${EXPIRES_OPTIONS.find(o => o.value === item.expires)?.label.toLowerCase()}`}
+
+
+
+
+
+ {EXPIRES_OPTIONS.map((option) => (
+
+ {
+ onSave(itemId, { ...item, expires: option.value });
+ }}>
+ {option.label}
+ {option.description}
+
+
+ ))}
+
+
+
+
- {onRemove && (
-
-
-
- )}
-
-
-
Expires:
-
-
-
- {item.expires === 'never' ? 'Never expires' : `${EXPIRES_OPTIONS.find(o => o.value === item.expires)?.label.toLowerCase()}`}
-
-
-
-
-
- {EXPIRES_OPTIONS.map((option) => (
-
- {
- onSave(itemId, { ...item, expires: option.value });
- }}>
- {option.label}
- {option.description}
-
-
- ))}
-
-
-
);
@@ -633,7 +638,7 @@ function ProductItemRow({
-
{itemId}
+
{itemDisplayName}
{prettyPrintWithMagnitudes(item.quantity)}
{shortRepeatText}
@@ -667,6 +672,7 @@ function ProductItemRow({
title="Example"
icon="code"
compact
+ tooltip="Retrieves this item for the active customer and reads the current quantity they hold."
/>
@@ -966,7 +972,7 @@ function ProductCard({ id, activeType, product, allProducts, existingItems, onSa
{itemsList.map(([itemId, item]) => {
const itemMeta = existingItems.find(i => i.id === itemId);
- const itemLabel = itemMeta ? (itemMeta.displayName || itemMeta.id) : 'Select item';
+ const itemLabel = itemMeta ? itemMeta.id : 'Select item';
return (
)}
diff --git a/apps/dashboard/src/components/code-block.tsx b/apps/dashboard/src/components/code-block.tsx
index b5f13b2bd5..6cd98e7e1a 100644
--- a/apps/dashboard/src/components/code-block.tsx
+++ b/apps/dashboard/src/components/code-block.tsx
@@ -1,7 +1,7 @@
'use client';
import { useThemeWatcher } from '@/lib/theme';
-import { CopyButton } from "@stackframe/stack-ui";
+import { CopyButton, SimpleTooltip } from "@stackframe/stack-ui";
import { Code, Terminal } from "lucide-react";
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import bash from 'react-syntax-highlighter/dist/esm/languages/prism/bash';
@@ -9,21 +9,25 @@ import python from 'react-syntax-highlighter/dist/esm/languages/prism/python';
import tsx from 'react-syntax-highlighter/dist/esm/languages/prism/tsx';
import typescript from 'react-syntax-highlighter/dist/esm/languages/prism/typescript';
import { dark, prism } from 'react-syntax-highlighter/dist/esm/styles/prism';
+import type { ReactNode } from 'react';
import { cn } from '@/lib/utils';
Object.entries({ tsx, bash, typescript, python }).forEach(([key, value]) => {
SyntaxHighlighter.registerLanguage(key, value);
});
-export function CodeBlock(props: {
+type CodeBlockProps = {
language: string,
content: string,
- customRender?: React.ReactNode,
+ customRender?: ReactNode,
title: string,
icon: 'terminal' | 'code',
maxHeight?: number,
compact?: boolean,
-}) {
+ tooltip?: ReactNode,
+};
+
+export function CodeBlock(props: CodeBlockProps) {
const { theme, mounted } = useThemeWatcher();
let icon = null;
@@ -45,7 +49,12 @@ export function CodeBlock(props: {
{icon}
{props.title}
-
+
+ {props.tooltip && (
+
+ )}
+
+
{props.customRender ??