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.
pnpm dev:example) to see it.Add the components
terminal npx shadcn add badge button card empty field inputInstall the glue
Install the blocks with
npx better-supabase add drive share-links. The glue items are copied into your app, so edit them as you need.None: the page calls the block's client on the server.
Write the screen
React and shadcn
View in the examplefile-request-upload.tsx /** * 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> ); }