186 lines
6.0 KiB
TypeScript
186 lines
6.0 KiB
TypeScript
import { Loader2 } from "lucide-react";
|
|
import {
|
|
MAX_PAGES_LIMIT,
|
|
MIN_PAGES,
|
|
} from "@/client/features/audit/launch/types";
|
|
import type { useLaunchController } from "@/client/features/audit/launch/useLaunchController";
|
|
import { getFieldError, getFormError } from "@/client/lib/forms";
|
|
|
|
type Props = {
|
|
launchForm: ReturnType<typeof useLaunchController>["launchForm"];
|
|
commitMaxPagesInput: () => number;
|
|
};
|
|
|
|
export function LaunchFormCard({ commitMaxPagesInput, launchForm }: Props) {
|
|
return (
|
|
<div className="card bg-base-100 border border-base-300">
|
|
<div className="card-body gap-4">
|
|
<h2 className="card-title text-base">Start New Audit</h2>
|
|
|
|
<form
|
|
className="grid grid-cols-1 gap-3 lg:grid-cols-12 lg:items-center"
|
|
onSubmit={(event) => {
|
|
event.preventDefault();
|
|
void launchForm.handleSubmit();
|
|
}}
|
|
>
|
|
<launchForm.Field name="url">
|
|
{(field) => {
|
|
const urlError = getFieldError(field.state.meta.errors);
|
|
|
|
return (
|
|
<label
|
|
className={`input input-bordered w-full lg:col-span-9 ${urlError ? "input-error" : ""}`}
|
|
>
|
|
<input
|
|
placeholder="https://example.com"
|
|
value={field.state.value}
|
|
onChange={(event) => {
|
|
field.handleChange(event.target.value);
|
|
if (launchForm.state.errorMap.onSubmit) {
|
|
launchForm.setErrorMap({ onSubmit: undefined });
|
|
}
|
|
}}
|
|
/>
|
|
</label>
|
|
);
|
|
}}
|
|
</launchForm.Field>
|
|
|
|
<launchForm.Subscribe selector={(state) => state.isSubmitting}>
|
|
{(isSubmitting) => (
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary btn-sm w-full lg:col-span-3"
|
|
disabled={isSubmitting}
|
|
>
|
|
{isSubmitting ? (
|
|
<>
|
|
<Loader2 className="size-4 animate-spin" /> Starting...
|
|
</>
|
|
) : (
|
|
"Start Audit"
|
|
)}
|
|
</button>
|
|
)}
|
|
</launchForm.Subscribe>
|
|
|
|
<div className="grid w-full grid-cols-1 gap-4 md:grid-cols-2 lg:col-span-12 lg:items-start">
|
|
<LaunchOptions
|
|
launchForm={launchForm}
|
|
commitMaxPagesInput={commitMaxPagesInput}
|
|
/>
|
|
<LighthouseOptions launchForm={launchForm} />
|
|
</div>
|
|
</form>
|
|
|
|
<LaunchErrors launchForm={launchForm} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function LaunchOptions({ launchForm, commitMaxPagesInput }: Props) {
|
|
return (
|
|
<div className="rounded-lg border border-base-300 bg-base-200/20 p-3 space-y-2">
|
|
<label className="text-xs font-medium uppercase tracking-wide text-base-content/60">
|
|
Crawl limit
|
|
</label>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-base-content/70">Max pages</span>
|
|
<launchForm.Field name="maxPagesInput">
|
|
{(field) => (
|
|
<input
|
|
type="number"
|
|
min={MIN_PAGES}
|
|
max={MAX_PAGES_LIMIT}
|
|
className="input input-bordered input-sm w-28"
|
|
value={field.state.value}
|
|
onChange={(event) => {
|
|
const next = event.target.value;
|
|
if (!/^\d*$/.test(next)) return;
|
|
field.handleChange(next);
|
|
if (launchForm.state.errorMap.onSubmit) {
|
|
launchForm.setErrorMap({ onSubmit: undefined });
|
|
}
|
|
}}
|
|
onBlur={commitMaxPagesInput}
|
|
/>
|
|
)}
|
|
</launchForm.Field>
|
|
</div>
|
|
<p className="text-xs text-base-content/50">
|
|
Enter any value from {MIN_PAGES} to {MAX_PAGES_LIMIT}.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function LighthouseOptions({ launchForm }: Pick<Props, "launchForm">) {
|
|
return (
|
|
<div className="rounded-lg border border-base-300 bg-base-200/20 p-3 space-y-2">
|
|
<label className="label cursor-pointer justify-start gap-2 p-0">
|
|
<launchForm.Field name="runLighthouse">
|
|
{(field) => (
|
|
<input
|
|
type="checkbox"
|
|
className="toggle toggle-sm toggle-primary"
|
|
checked={Boolean(field.state.value)}
|
|
onChange={(event) => field.handleChange(event.target.checked)}
|
|
/>
|
|
)}
|
|
</launchForm.Field>
|
|
<span
|
|
className="text-sm font-medium text-base-content/80"
|
|
title="Lighthouse measures the performance of your pages and identifies issues."
|
|
>
|
|
Include Lighthouse
|
|
</span>
|
|
</label>
|
|
|
|
<launchForm.Subscribe
|
|
selector={(snapshot) => snapshot.values.runLighthouse}
|
|
>
|
|
{(runLighthouse) =>
|
|
runLighthouse ? (
|
|
<div className="space-y-1">
|
|
<p className="text-xs text-base-content/60">
|
|
We choose a sample of 20 pages to audit, removing pages from
|
|
duplicate templates.
|
|
</p>
|
|
</div>
|
|
) : null
|
|
}
|
|
</launchForm.Subscribe>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function LaunchErrors({ launchForm }: Pick<Props, "launchForm">) {
|
|
return (
|
|
<div className="space-y-2">
|
|
<launchForm.Field name="url">
|
|
{(field) => {
|
|
const urlError = getFieldError(field.state.meta.errors);
|
|
|
|
return urlError ? (
|
|
<p className="text-sm text-error">{urlError}</p>
|
|
) : null;
|
|
}}
|
|
</launchForm.Field>
|
|
|
|
<launchForm.Subscribe selector={(state) => state.errorMap.onSubmit}>
|
|
{(submitError) => {
|
|
const errorMessage = getFormError(submitError);
|
|
|
|
return errorMessage ? (
|
|
<div className="alert alert-error py-2">
|
|
<span className="text-sm">{errorMessage}</span>
|
|
</div>
|
|
) : null;
|
|
}}
|
|
</launchForm.Subscribe>
|
|
</div>
|
|
);
|
|
}
|