UI recipesUI recipes
File request upload
The public page of a file request link, where anyone with the link sends a file into a folder, with the link's password when it has one.
It loads its data on the server through the block's client; the drive, share-links blocks must be installed (npx better-supabase add drive share-links). Its page on bettersupabase.com is /recipes/file-request-upload.
Web, with shadcn components:
npx shadcn add badge button card empty field input/**
* The public page of a file request link: pick a file and send it into the
* folder, with the link's password when it has one. The sender never sees what
* the folder holds.
*/
export function FileRequestUpload({ token }: { token: string }) {
const t = useExtracted("drive");
const fieldId = useId();
const errorMessage = useErrorMessage();
const [password, setPassword] = useState<"none" | "asked" | "wrong">("none");
const [sent, setSent] = useState<string | null>(null);
const form = useActionForm(sendToFileRequest, {
resetOnSuccess: false,
onSuccess: (result, element) => {
if (result.passwordRequired) {
setPassword((current) => (current === "none" ? "asked" : "wrong"));
return;
}
element.reset();
setSent(result.name);
},
});
const error = form.error === undefined ? null : errorMessage(form.error);
const locked = password !== "none";
const wrongPassword = password === "wrong";
return (
<Card data-recipe="file-request-upload">
<CardHeader>
<CardTitle>{t("Send files")}</CardTitle>
<CardDescription>
{t("Files you send go straight to the folder's owner.")}
</CardDescription>
</CardHeader>
<CardContent>
{sent === null ? (
<form {...form.formProps} className="flex flex-col gap-4">
<input type="hidden" name="token" value={token} />
<FieldGroup>
<Field data-invalid={form.fieldErrors["file"] ? true : undefined}>
<FieldLabel htmlFor={`${fieldId}-file`}>{t("File")}</FieldLabel>
<Input
id={`${fieldId}-file`}
name="file"
type="file"
required
/>
<FieldDescription>{t("Up to 1 MB per file.")}</FieldDescription>
</Field>
{locked ? (
<Field data-invalid={wrongPassword ? true : undefined}>
<FieldLabel htmlFor={`${fieldId}-password`}>
{t("Password")}
</FieldLabel>
<Input
id={`${fieldId}-password`}
name="password"
type="password"
required
maxLength={200}
autoComplete="off"
/>
{wrongPassword ? (
<FieldError>
{t(
"This link needs its password. Check it and try again.",
)}
</FieldError>
) : null}
</Field>
) : null}
{error ? <FieldError>{error}</FieldError> : null}
</FieldGroup>
<div className="flex justify-end">
<Button type="submit" disabled={form.pending}>
{t("Send")}
</Button>
</div>
</form>
) : (
<Empty className="border">
<EmptyHeader>
<EmptyMedia variant="icon">
<CircleCheckIcon />
</EmptyMedia>
<EmptyTitle>{t("File sent")}</EmptyTitle>
<EmptyDescription>{sent}</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Badge variant="success-light">{t("Received")}</Badge>
<Button
variant="outline"
onClick={() => {
form.reset();
setSent(null);
}}
>
{t("Send another file")}
</Button>
</EmptyContent>
</Empty>
)}
</CardContent>
</Card>
);
}Last updated on