0.1.4Updated 6 months ago
// deno-lint-ignore-file no-explicit-any

type valid_value_types = string | boolean | number | Date | valid_value_types[]

type PostifyCallback<T extends Record<string, any> = Record<string, any>> = (error: Error | null, response?: T) => void;
interface PostifyOptions {
  callback?: PostifyCallback | null
  overrides: Record<string, any>
}

export const Postify = ({ callback, overrides = {} }: PostifyOptions) => async (e: SubmitEvent) => {
  e.preventDefault();
  e.stopPropagation();

  const form = e.target as HTMLFormElement;

  const data: Record<string, valid_value_types> = {};
  const input_elements = form.querySelectorAll<HTMLInputElement>('input[name]');

  for(const input of input_elements as any) {
    const name = input.name as string;
    let value: string | number | boolean | Date;

    switch(input.type) {
      case 'number': {
        value = parseFloat(input.value);
        break;
      }
      case 'checkbox': {
        value = input.checked;
        break;
      }
      case 'date': {
        value = new Date(input.value);
        break;
      }
      default: {
        value = input.value;
      }
    }

    if(data[name] === undefined) {
      data[name] = value;
    } else {
      data[name] = [data[name], value].flat();
    }
  }

  for(const [key, value] of Object.entries(overrides)) {
    data[key] = value;
  }

  const result = await fetch(form.action, {
    method: 'POST',
    body: JSON.stringify(data)
  });

  if(!result.ok) return callback?.(new Error(`${result.statusText}\n${await result.text()}`));

  try {
    const response_body = await result.json() as Record<string, any>;
    callback?.(null, response_body);
  } catch(e: any) {
    callback?.(new Error(`Could not parse response json. [${e.message}]`));
  }

  return false;
}