baublet/w8mngr

View on GitHub
client/components/Activity/ActivityForm.tsx

Summary

Maintainability
B
4 hrs
Test Coverage
import React from "react";
import cx from "classnames";

import { Input, MultilineInput, SingleSelect, Slider } from "../Forms";
import { Spacer } from "../Spacer";
import { SecondaryButton } from "../Button/Secondary";
import { ContentContainer } from "../Containers/ContentContainer";
import { ContentLayout } from "../Containers/ContentLayout";

import { activityTypeToHumanReadable, useForm } from "../../helpers";
import { ActivityType, Muscle } from "../../generated";
import { MuscleMap } from "../MuscleMap";
import { IntensityScale } from "./IntensityScale";

type FormData = {
  name: string;
  description: string;
  exrx: string;
  type: ActivityType;
  intensity: number;
  muscleGroups: Muscle[];
};
export type PartialFormData = { [K in keyof FormData]?: FormData[K] | null };

const types: ActivityType[] = ["WEIGHT", "DISTANCE", "REPETITIVE", "TIMED"];

export function ActivityForm({
  initialValues,
  onSave,
  loading,
}: {
  initialValues?: PartialFormData;
  onSave: (formState: PartialFormData) => void;
  loading: boolean;
}) {
  const formData = useForm<FormData>({
    initialValues,
  });

  React.useEffect(() => {
    if (initialValues) {
      formData.setValues(initialValues);
    }
  }, [initialValues]);

  const handleSave = React.useCallback(
    (event?: React.FormEvent<HTMLFormElement>) => {
      if (event) {
        event.preventDefault();
      }
      onSave(formData.getValues());
    },
    []
  );

  return (
    <ContentContainer
      className={cx({ "opacity-50 pointer-events-none blur": loading })}
    >
      <ContentLayout
        sideContent={
          <MuscleMap
            active={true}
            showSummary={true}
            onChange={formData.getHandler("muscleGroups")}
            selected={formData.getValue("muscleGroups")}
          />
        }
        mainContent={
          <form onSubmit={handleSave}>
            <div className="flex w-full flex-col">
              <Input
                placeholder="Activity name"
                type="text"
                label="Name"
                labelPlacement="bottom"
                onChange={formData.getHandler("name")}
                value={formData.getValue("name")}
                size="lg"
              />
              <Spacer size="s" />
              <div className="flex gap-4 flex-col lg:flex-row">
                <div>
                  <SingleSelect
                    id="activity-type"
                    label="Type"
                    defaultSelectedKey={formData.getValue("type")}
                    labelPlacement="bottom"
                    options={types.map((type) => ({
                      key: type,
                      text: activityTypeToHumanReadable(type),
                    }))}
                    onChange={formData.getHandler("type")}
                  />
                </div>
                <div className="w-full">
                  <Slider
                    min={0}
                    max={10}
                    label="intensity"
                    id="intensity-slider"
                    lowerLabel="Less intense"
                    higherLabel="More intense"
                    defaultValue={formData.getValue("intensity")}
                    onChange={formData.getHandler("intensity")}
                    labelPlacement="bottom"
                    scaleEndAdornment={
                      <IntensityScale
                        intensity={formData.getValue("intensity")}
                        size="small"
                      />
                    }
                  />
                </div>
              </div>
              <Spacer size="s" />
              <MultilineInput
                labelPlacement="bottom"
                placeholder="Description"
                type="text"
                label="Description"
                onChange={formData.getHandler("description")}
                value={formData.getValue("description")}
              />
              <Spacer />
              <Spacer />
              <div className="flex w-full gap-4 justify-end">
                <SecondaryButton
                  onClick={handleSave}
                  type="submit"
                  disabled={loading}
                >
                  Save Activity
                </SecondaryButton>
              </div>
            </div>
          </form>
        }
      />
    </ContentContainer>
  );
}