import { Button, Container, Flex, Text } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { Console, Effect, Match, Option, ParseResult, Schema } from "effect" import { Component, Form, Subscribable } from "effect-fc" import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" import { DateTimeUtcFromZonedInput } from "@/lib/schema" import { runtime } from "@/runtime" const email = Schema.pattern( /^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i, { identifier: "email", title: "email", message: () => "Not an email address", }, ) const RegisterFormSchema = Schema.Struct({ email: Schema.String.pipe(email), password: Schema.String.pipe(Schema.minLength(3)), birth: Schema.OptionFromSelf(DateTimeUtcFromZonedInput), }) const RegisterFormSubmitSchema = Schema.Struct({ email: Schema.transformOrFail( Schema.String, Schema.String, { decode: (input, _options, ast) => input !== "admin@admin.com" ? ParseResult.succeed(input) : ParseResult.fail(new ParseResult.Type(ast, input, "This email is already in use.")), encode: ParseResult.succeed, }, ), password: Schema.String, birth: Schema.OptionFromSelf(Schema.DateTimeUtcFromSelf), }) class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.service({ schema: RegisterFormSchema.pipe( Schema.compose( Schema.transformOrFail( Schema.typeSchema(RegisterFormSchema), Schema.typeSchema(RegisterFormSchema), { decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), encode: ParseResult.succeed, }, ), ), ), initialEncodedValue: { email: "", password: "", birth: Option.none() }, onSubmit: Effect.fnUntraced(function*(v) { yield* Effect.sleep("500 millis") return yield* Schema.decode(RegisterFormSubmitSchema)(v) }), debounce: "500 millis", }) }) {} class RegisterFormView extends Component.makeUntraced("RegisterFormView")(function*() { const form = yield* RegisterForm const [canSubmit, submitResult] = yield* Subscribable.useSubscribables([ form.canSubmitSubscribable, form.submitResultRef, ]) const runPromise = yield* Component.useRunPromise() const TextFieldFormInputFC = yield* TextFieldFormInput yield* Component.useOnMount(() => Effect.gen(function*() { yield* Effect.addFinalizer(() => Console.log("RegisterFormView unmounted")) yield* Console.log("RegisterFormView mounted") })) return (
{ e.preventDefault() void runPromise(Form.submit(form)) }}>
{Match.value(submitResult).pipe( Match.tag("Initial", () => <>), Match.tag("Running", () => Submitting...), Match.tag("Success", () => Submitted successfully!), Match.tag("Failure", e => Error: {e.cause.toString()}), Match.exhaustive, )}
) }) {} const RegisterPage = Component.makeUntraced("RegisterPage")(function*() { const RegisterFormViewFC = yield* Effect.provide( RegisterFormView, yield* Component.useContext(RegisterForm.Default), ) return }).pipe( Component.withRuntime(runtime.context) ) export const Route = createFileRoute("/form")({ component: RegisterPage })