feat: custom number of slides
This commit is contained in:
parent
afb32c4c1a
commit
044aa243d4
5 changed files with 98 additions and 28 deletions
|
|
@ -23,6 +23,7 @@ import {
|
|||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
// Types
|
||||
interface ConfigurationSelectsProps {
|
||||
|
|
@ -41,28 +42,89 @@ const SLIDE_OPTIONS: SlideOption[] = ["5", "8", "9", "10", "11", "12", "13", "14
|
|||
const SlideCountSelect: React.FC<{
|
||||
value: string | null;
|
||||
onValueChange: (value: string) => void;
|
||||
}> = ({ value, onValueChange }) => (
|
||||
<Select value={value || ""} onValueChange={onValueChange} name="slides">
|
||||
<SelectTrigger
|
||||
className="w-[180px] font-instrument_sans font-medium bg-blue-100 border-blue-200 focus-visible:ring-blue-300"
|
||||
data-testid="slides-select"
|
||||
>
|
||||
<SelectValue placeholder="Select Slides" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="font-instrument_sans">
|
||||
{SLIDE_OPTIONS.map((option) => (
|
||||
<SelectItem
|
||||
key={option}
|
||||
value={option}
|
||||
className="font-instrument_sans text-sm font-medium"
|
||||
role="option"
|
||||
}> = ({ value, onValueChange }) => {
|
||||
const [customInput, setCustomInput] = useState(
|
||||
value && !SLIDE_OPTIONS.includes(value as SlideOption) ? value : ""
|
||||
);
|
||||
|
||||
const sanitizeToPositiveInteger = (raw: string): string => {
|
||||
const digitsOnly = raw.replace(/\D+/g, "");
|
||||
if (!digitsOnly) return "";
|
||||
// Remove leading zeros
|
||||
const noLeadingZeros = digitsOnly.replace(/^0+/, "");
|
||||
return noLeadingZeros;
|
||||
};
|
||||
|
||||
const applyCustomValue = () => {
|
||||
const sanitized = sanitizeToPositiveInteger(customInput);
|
||||
if (sanitized && Number(sanitized) > 0) {
|
||||
onValueChange(sanitized);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Select value={value || ""} onValueChange={onValueChange} name="slides">
|
||||
<SelectTrigger
|
||||
className="w-[180px] font-instrument_sans font-medium bg-blue-100 border-blue-200 focus-visible:ring-blue-300"
|
||||
data-testid="slides-select"
|
||||
>
|
||||
<SelectValue placeholder="Select Slides" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="font-instrument_sans">
|
||||
{/* Sticky custom input at the top */}
|
||||
<div
|
||||
className="sticky top-0 z-10 bg-white dark:bg-slate-900 p-2 border-b"
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{option} slides
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
inputMode="numeric"
|
||||
pattern="[0-9]*"
|
||||
value={customInput}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={(e) => {
|
||||
const next = sanitizeToPositiveInteger(e.target.value);
|
||||
setCustomInput(next);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
applyCustomValue();
|
||||
}
|
||||
}}
|
||||
onBlur={applyCustomValue}
|
||||
placeholder="X"
|
||||
className="h-8 w-16 px-2 text-sm"
|
||||
/>
|
||||
<span className="text-sm font-medium">slides</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hidden item to allow SelectValue to render custom selection */}
|
||||
{value && !SLIDE_OPTIONS.includes(value as SlideOption) && (
|
||||
<SelectItem value={value} className="hidden">
|
||||
{value} slides
|
||||
</SelectItem>
|
||||
)}
|
||||
|
||||
{SLIDE_OPTIONS.map((option) => (
|
||||
<SelectItem
|
||||
key={option}
|
||||
value={option}
|
||||
className="font-instrument_sans text-sm font-medium"
|
||||
role="option"
|
||||
>
|
||||
{option} slides
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a language selection component with search functionality
|
||||
|
|
|
|||
|
|
@ -20,8 +20,10 @@ export async function POST(req: NextRequest) {
|
|||
});
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1280, height: 720 });
|
||||
page.setDefaultNavigationTimeout(300000);
|
||||
page.setDefaultTimeout(300000);
|
||||
|
||||
await page.goto(`http://localhost/pdf-maker?id=${id}`, { waitUntil: 'networkidle0', timeout: 180000 });
|
||||
await page.goto(`http://localhost/pdf-maker?id=${id}`, { waitUntil: 'networkidle0', timeout: 300000 });
|
||||
|
||||
await page.waitForFunction('() => document.readyState === "complete"')
|
||||
|
||||
|
|
@ -47,7 +49,7 @@ export async function POST(req: NextRequest) {
|
|||
return (loadedElements / totalElements) >= 0.99;
|
||||
}
|
||||
`,
|
||||
{ timeout: 10000 }
|
||||
{ timeout: 300000 }
|
||||
);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
|
|
|||
|
|
@ -22,12 +22,14 @@ export async function GET(request: Request) {
|
|||
});
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1280, height: 720 });
|
||||
page.setDefaultNavigationTimeout(300000);
|
||||
page.setDefaultTimeout(300000);
|
||||
await page.goto(schemaPageUrl, {
|
||||
waitUntil: "networkidle0",
|
||||
timeout: 80000,
|
||||
timeout: 300000,
|
||||
});
|
||||
|
||||
await page.waitForSelector("[data-layouts]", { timeout: 30000 });
|
||||
await page.waitForSelector("[data-layouts]", { timeout: 300000 });
|
||||
|
||||
// Extract both data-layouts and data-group-settings attributes
|
||||
const { dataLayouts, dataGroupSettings } = await page.$eval(
|
||||
|
|
|
|||
|
|
@ -75,9 +75,11 @@ async function getBrowserAndPage(id: string): Promise<[Browser, Page]> {
|
|||
const page = await browser.newPage();
|
||||
|
||||
await page.setViewport({ width: 1280, height: 720, deviceScaleFactor: 1 });
|
||||
page.setDefaultNavigationTimeout(300000);
|
||||
page.setDefaultTimeout(300000);
|
||||
await page.goto(`http://localhost/pdf-maker?id=${id}`, {
|
||||
waitUntil: "networkidle0",
|
||||
timeout: 180000,
|
||||
timeout: 300000,
|
||||
});
|
||||
return [browser, page];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ export async function GET(request: Request) {
|
|||
browser = await puppeteer.launch({ headless: true, args: ["--no-sandbox", "--disable-web-security"] });
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1280, height: 720 });
|
||||
await page.goto(schemaPageUrl, { waitUntil: "networkidle0", timeout: 80000 });
|
||||
page.setDefaultNavigationTimeout(300000);
|
||||
page.setDefaultTimeout(300000);
|
||||
await page.goto(schemaPageUrl, { waitUntil: "networkidle0", timeout: 300000 });
|
||||
|
||||
await page.waitForSelector("[data-layouts]", { timeout: 30000 });
|
||||
await page.waitForSelector("[data-layouts]", { timeout: 300000 });
|
||||
|
||||
const { dataLayouts, dataGroupSettings } = await page.$eval(
|
||||
"[data-layouts]",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue