Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -497,12 +497,14 @@ function ProductItemRow({

if (isEditing) {
return (
<div className="flex flex-col gap-1 mb-4">
<div className="flex flex-row">
<div className="flex flex-col gap-2 mb-4">
<div className="flex w-full items-center justify-between gap-2">
<Popover open={itemSelectOpen} onOpenChange={setItemSelectOpen}>
<PopoverTrigger>
<div className="text-sm px-2 py-0.5 rounded bg-muted hover:bg-muted/70 cursor-pointer select-none flex items-center gap-1">
{itemId}
<span className="overflow-x-auto max-w-24">
{itemDisplayName}
</span>
<ChevronsUpDown className="h-4 w-4" />
</div>
</PopoverTrigger>
Expand Down Expand Up @@ -554,16 +556,54 @@ function ProductItemRow({
</div>
</PopoverContent>
</Popover>
<Input
className="ml-auto w-20 text-right tabular-nums mr-2"
inputMode="numeric"
value={quantity}
onChange={(e) => {
const v = e.target.value;
if (v === '' || /^\d*$/.test(v)) setQuantity(v);
if (!readOnly && (v === '' || /^\d*$/.test(v))) updateParent(v);
}}
/>
<div className="flex items-center gap-2">
<Input
className="w-24 text-right tabular-nums"
inputMode="numeric"
value={quantity}
onChange={(e) => {
const v = e.target.value;
if (v === '' || /^\d*$/.test(v)) setQuantity(v);
if (!readOnly && (v === '' || /^\d*$/.test(v))) updateParent(v);
}}
/>
{onRemove && (
<button className="text-muted-foreground hover:text-foreground" onClick={onRemove} aria-label="Remove item">
<X className="h-4 w-4" />
</button>
)}
</div>
</div>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex items-center gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="text-xs px-2 py-0.5 w-fit rounded bg-muted text-muted-foreground cursor-pointer select-none flex items-center gap-1">
{item.expires === 'never' ? 'Never expires' : `${EXPIRES_OPTIONS.find(o => o.value === item.expires)?.label.toLowerCase()}`}
<ChevronsUpDown className="h-4 w-4" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-2">
<div className="flex flex-col gap-2">
{EXPIRES_OPTIONS.map((option) => (
<DropdownMenuItem key={option.value}>
<Button
key={option.value}
variant="ghost"
size="sm"
className="flex flex-col items-start"
onClick={() => {
onSave(itemId, { ...item, expires: option.value });
}}>
{option.label}
<span className="text-xs text-muted-foreground">{option.description}</span>
</Button>
</DropdownMenuItem>
))}
</div>
</DropdownMenuContent>
</DropdownMenu>
</div>
<IntervalPopover
readOnly={readOnly}
intervalText={repeatText}
Expand All @@ -583,41 +623,6 @@ function ProductItemRow({
onSave(itemId, updated);
}}
/>
{onRemove && (
<button className="ml-auto" onClick={onRemove} aria-label="Remove item">
<X className="h-4 w-4" />
</button>
)}
</div>
<div className="flex flex-row items-center gap-2">
<span className="text-xs text-muted-foreground">Expires:</span>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="text-xs px-2 py-0.5 w-fit rounded bg-muted text-muted-foreground cursor-pointer select-none flex items-center gap-1">
{item.expires === 'never' ? 'Never expires' : `${EXPIRES_OPTIONS.find(o => o.value === item.expires)?.label.toLowerCase()}`}
<ChevronsUpDown className="h-4 w-4" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-2">
<div className="flex flex-col gap-2">
{EXPIRES_OPTIONS.map((option) => (
<DropdownMenuItem key={option.value}>
<Button
key={option.value}
variant="ghost"
size="sm"
className="flex flex-col items-start"
onClick={() => {
onSave(itemId, { ...item, expires: option.value });
}}>
{option.label}
<span className="text-xs text-muted-foreground">{option.description}</span>
</Button>
</DropdownMenuItem>
))}
</div>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
);
Expand All @@ -633,7 +638,7 @@ function ProductItemRow({
<ChevronDown className={cn("h-4 w-4 transition-transform", isOpen ? "rotate-0" : "-rotate-90")} />
</button>
</CollapsibleTrigger >
<div className="text-sm">{itemId}</div>
<div className="text-sm">{itemDisplayName}</div>
<div className="ml-auto w-16 text-right text-sm text-muted-foreground tabular-nums">{prettyPrintWithMagnitudes(item.quantity)}</div>
<div className="ml-2">
<div className="text-xs px-2 py-0.5 rounded bg-muted text-muted-foreground">{shortRepeatText}</div>
Expand Down Expand Up @@ -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."
/>
</div>
</div>
Expand Down Expand Up @@ -966,7 +972,7 @@ function ProductCard({ id, activeType, product, allProducts, existingItems, onSa
<div className="space-y-2">
{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 (
<ProductItemRow
key={itemId}
Expand Down Expand Up @@ -1072,6 +1078,7 @@ function ProductCard({ id, activeType, product, allProducts, existingItems, onSa
title="Checkout"
icon="code"
compact
tooltip="Creates a checkout URL for this product and opens it so the customer can finish their purchase."
/>
</div>
)}
Expand Down
19 changes: 14 additions & 5 deletions apps/dashboard/src/components/code-block.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
'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';
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;
Expand All @@ -45,7 +49,12 @@ export function CodeBlock(props: {
{icon}
{props.title}
</h5>
<CopyButton content={props.content} />
<div className="flex items-center gap-2">
{props.tooltip && (
<SimpleTooltip type="info" tooltip={props.tooltip} />
)}
<CopyButton content={props.content} />
</div>
</div>
<div>
{props.customRender ?? <SyntaxHighlighter
Expand Down