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
14 changes: 13 additions & 1 deletion api/tools/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,7 @@ export type CmsVariant = {
value: Record<string, unknown>;
rule: Record<string, unknown>;
label: string;
name?: string;
};

export type CmsSection = {
Expand Down Expand Up @@ -797,6 +798,7 @@ const cmsSectionItemSchema = z
value: z.record(z.string(), z.unknown()),
rule: z.record(z.string(), z.unknown()),
label: z.string(),
name: z.string().optional(),
}),
)
.optional(),
Expand All @@ -816,6 +818,7 @@ export const getPageSectionsOutputSchema = z.object({
label: z.string(),
rule: z.record(z.string(), z.unknown()),
sections: z.array(cmsSectionItemSchema),
name: z.string().optional(),
}),
)
.optional(),
Expand Down Expand Up @@ -1099,6 +1102,7 @@ export const getPageSectionsTool = createTool({
variants?: Array<{
value?: Record<string, unknown>;
rule?: Record<string, unknown>;
name?: string;
}>;
};
const rawVariants = Array.isArray(mvObj.variants)
Expand Down Expand Up @@ -1143,6 +1147,7 @@ export const getPageSectionsTool = createTool({
value,
rule,
label: formatMatcher(rule),
...(typeof v.name === "string" && v.name ? { name: v.name } : {}),
};
});
const firstValueRt = (
Expand Down Expand Up @@ -1182,6 +1187,7 @@ export const getPageSectionsTool = createTool({
label: string;
rule: Record<string, unknown>;
sections: CmsSection[];
name?: string;
};

let pageVariants: PageVariantEntry[] | undefined;
Expand All @@ -1200,6 +1206,7 @@ export const getPageSectionsTool = createTool({
variants?: Array<{
value?: unknown[];
rule?: Record<string, unknown>;
name?: string;
}>;
};
const mvVariants = Array.isArray(mvField.variants)
Expand All @@ -1210,7 +1217,12 @@ export const getPageSectionsTool = createTool({
const rule = (v.rule ?? {}) as Record<string, unknown>;
const label = formatMatcher(rule);
const { sections } = parseSectionsFromArray(varSections);
return { label, rule, sections };
return {
label,
rule,
sections,
...(typeof v.name === "string" && v.name ? { name: v.name } : {}),
};
});
// Default display: first variant's sections
rawSections = Array.isArray(mvVariants[0]?.value)
Expand Down
126 changes: 107 additions & 19 deletions web/tools/file-explorer/cms-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
Trash2,
Upload,
VideoIcon,
X,
} from "lucide-react";
import {
createContext,
Expand Down Expand Up @@ -306,6 +307,27 @@
return `${y}-${m}-${d}T${hh}:${mm}`;
}

function formatDateDisplay(
dateStr: string | undefined,
mode: "date" | "date-time",
): string {
if (!dateStr) return "";
const date = new Date(dateStr);
if (Number.isNaN(date.getTime())) return "";
return new Intl.DateTimeFormat(
"en-US",
mode === "date"
? { month: "short", day: "numeric", year: "numeric" }
: {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
},
).format(date);
}

function DateField({
label,
description,
Expand All @@ -322,10 +344,9 @@
const inputType = mode === "date" ? "date" : "datetime-local";
const inputRef = useRef<HTMLInputElement>(null);
const [local, setLocal] = useState(() => formatForNativeInput(value, mode));
const [focused, setFocused] = useState(false);

// Sync external value changes, but only if the formatted value actually
// differs from what we already have — avoids re-renders that kill the
// native date picker popup.
// Sync external value changes without clobbering an open picker.
const prevFormattedRef = useRef(local);
useEffect(() => {
const formatted = formatForNativeInput(value, mode);
Expand Down Expand Up @@ -353,31 +374,104 @@
}
};

const handleBlur = () => {
setFocused(false);
const formatted = formatForNativeInput(value, mode);
if (formatted !== local) {
setLocal(formatted);
prevFormattedRef.current = formatted;
}
};

const handleClear = () => {
setLocal("");
prevFormattedRef.current = "";
onChange("");
};

const openPicker = () => {
inputRef.current?.showPicker();
};

const display = formatDateDisplay(value, mode);
const placeholder = mode === "date" ? "Set a date…" : "Set date & time…";
const showOverlay = !focused;

return (
<div className="space-y-1">
<FieldLabel label={label} description={description} />
<div className="relative flex items-center">
<div className="relative">
<input
ref={inputRef}
type={inputType}
value={local}
onChange={handleChange}
className="flex h-7 w-full rounded-md border border-input bg-transparent px-2 pr-7 text-xs shadow-xs outline-none transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [&::-webkit-calendar-picker-indicator]{opacity:0;width:0}"
onFocus={() => setFocused(true)}
onBlur={handleBlur}
className="flex h-9 w-full rounded-md border border-input bg-background px-3 pr-9 text-sm shadow-xs outline-none transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/>
{showOverlay && (
<div className="pointer-events-none absolute inset-0 flex items-center gap-2 rounded-md border border-input bg-background px-3 pr-9 text-sm shadow-xs">
<svg
width="14"
height="14"
viewBox="0 0 16 16"
fill="none"
aria-hidden="true"
className="shrink-0 text-muted-foreground"
>
<rect
x="1"
y="3"
width="14"
height="12"
rx="2"
stroke="currentColor"
strokeWidth="1.5"
/>
<path d="M1 7h14" stroke="currentColor" strokeWidth="1.5" />
<path
d="M5 1v4M11 1v4"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
<span
className={cn(
"flex-1 truncate",
!display && "text-muted-foreground/40",
)}
>
{display || placeholder}
</span>
</div>
)}
{value && !focused && (
<button
type="button"
onMouseDown={(e) => e.preventDefault()}
onClick={handleClear}
className="absolute right-8 top-1/2 z-10 -translate-y-1/2 rounded p-0.5 text-muted-foreground/60 hover:text-foreground"
title="Clear"
>
<X className="h-3.5 w-3.5" />
</button>
)}
<button
type="button"
onClick={() => inputRef.current?.showPicker()}
className="absolute right-1.5 flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground"
onMouseDown={(e) => e.preventDefault()}
onClick={openPicker}
className="absolute right-1 top-1/2 z-10 -translate-y-1/2 rounded p-1 text-muted-foreground/70 hover:bg-accent hover:text-foreground"
title="Open picker"
>
<svg
width="12"
height="12"
width="14"
height="14"
viewBox="0 0 16 16"
fill="none"
aria-hidden="true"
>
<title>Calendar</title>
<rect
x="1"
y="3"
Expand Down Expand Up @@ -1221,9 +1315,7 @@
<div className="px-3 py-2">
<p className="truncate font-mono text-xs font-semibold">{stem}</p>
{ext && (
<p className="text-[10px] uppercase text-muted-foreground">
{ext}
</p>
<p className="text-[10px] text-muted-foreground">{ext}</p>
)}
</div>
)}
Expand Down Expand Up @@ -1356,9 +1448,7 @@
{stem}
</p>
{ext && (
<p className="text-[10px] uppercase text-muted-foreground">
{ext}
</p>
<p className="text-[10px] text-muted-foreground">{ext}</p>
)}
</div>
</>
Expand All @@ -1370,9 +1460,7 @@
{stem}
</p>
{ext && (
<p className="text-[10px] uppercase text-muted-foreground">
{ext}
</p>
<p className="text-[10px] text-muted-foreground">{ext}</p>
)}
</div>
</div>
Expand Down Expand Up @@ -1622,9 +1710,9 @@
description,
value,
onChange,
depth,

Check warning on line 1713 in web/tools/file-explorer/cms-form.tsx

View workflow job for this annotation

GitHub Actions / ci

lint/correctness/noUnusedFunctionParameters

This parameter is unused.

Check warning on line 1713 in web/tools/file-explorer/cms-form.tsx

View workflow job for this annotation

GitHub Actions / ci

lint/correctness/noUnusedFunctionParameters

This parameter is unused.
itemSchema,
schemasMap,

Check warning on line 1715 in web/tools/file-explorer/cms-form.tsx

View workflow job for this annotation

GitHub Actions / ci

lint/correctness/noUnusedFunctionParameters

This parameter is unused.

Check warning on line 1715 in web/tools/file-explorer/cms-form.tsx

View workflow job for this annotation

GitHub Actions / ci

lint/correctness/noUnusedFunctionParameters

This parameter is unused.
}: {
label: string;
description?: string;
Expand Down
Loading
Loading