Skip to content

18 Movies People Frontend

JP Barbosa edited this page Apr 15, 2023 · 2 revisions

Movies People Frontend

Add People To Movie List

code ./packages/web/src/pages/movies/List/Item.tsx
...
import { ..., RelationshipCollection, relationships } from '@neo4j-crud/shared';
import { People } from './People';

...

export const Item: React.FC<ItemProps> = ({ movie, search }) => {
  return (
    <li key={movie.title}>
      <Link to={`${movie.id}/edit`}>
        ...
        <div className="content">
          <div>
            ...
            <div className="relationships">
              {relationships.map((relationship) => (
                <People
                  key={relationship.key}
                  relationship={relationship}
                  people={
                    movie.people[
                      relationship.collection as RelationshipCollection
                    ]
                  }
                  search={search}
                />
              ))}
            </div>
          </div>
          ...
        </div>
      </Link>
    </li>
  );
};
code ./packages/web/src/pages/movies/List/People.tsx
import {
  MoviePerson,
  Relationship,
  stringToTitleCase,
} from '@neo4j-crud/shared';
import { HighlightedText } from '../../../components';

type PeopleProps = {
  relationship: Relationship;
  people: MoviePerson[];
  search: string;
};

export const People: React.FC<PeopleProps> = ({
  relationship,
  search,
  people,
}) => {
  if (people.length === 0) {
    return null;
  }

  return (
    <div className="mb-8">
      <b>{stringToTitleCase(relationship.key)}:</b>
      <HighlightedText
        text={people.map((person) => person.name).join(', ')}
        search={search}
      />
    </div>
  );
};

Add People To Movie Form

code ./packages/web/src/pages/movies/Form/index.tsx
...
import { ..., relationships, stringToTitleCase } from '@neo4j-crud/shared';
import { ..., InputText } from '../../../components';
import { People } from './People';

...

export const Form: React.FC<FormProps> = ({ movie }) => {
  ...

  return (
    <div>
      ...
      <div className="pd-16">
        <form onSubmit={handleSubmit((data) => upsert.mutate(data))}>
          <fieldset className="basic-info">
            <legend>Basic Info</legend>
            <div>
              <label>Title</label>
              <Controller
                ...
                render={(props) => <InputText {...props} />}
              />
            </div>
            <div>
              <label>Tagline</label>
              <Controller
                ...
                render={(props) => <InputText {...props} />}
              />
            </div>
            <div>
              <label>Released</label>
              <Controller
                ...
                render={(props) => (
                  <InputText {...props} className="number" />
                )}
              />
            </div>
          </fieldset>
          {relationships.map((relationship) => (
            <fieldset key={relationship.key} className="people">
              <legend>{stringToTitleCase(relationship.key)}</legend>
              <People control={control} relationship={relationship} />
            </fieldset>
          ))}
          <div className="bottom-actions-bar">
            ...
          </div>
        </form>
      </div>
    </div>
  );
};
code ./packages/web/src/pages/movies/Form/People.tsx
import { useQuery } from 'react-query';
import { Control, FieldArrayWithId, useFieldArray } from 'react-hook-form';
import {
  Movie,
  Person,
  Relationship,
  stringToTitleCase,
} from '@neo4j-crud/shared';
import * as api from '../../../api';
import { LoadingAlert } from '../../../components';
import { SelectPerson } from './SelectPerson';
import { PersonRoles } from './PersonRoles';

type PeopleProps = {
  control: Control<Movie>;
  relationship: Relationship;
};

export const People: React.FC<PeopleProps> = ({ control, relationship }) => {
  const { fields, append, remove } = useFieldArray({
    control,
    name: `people.${relationship.collection}`,
  });

  const { data, isLoading } = useQuery<Person[]>([], () => api.people.getAll());

  if (isLoading) {
    return <LoadingAlert />;
  }

  return (
    <>
      {fields.map((field, index) => (
        <div key={field.id} className="person">
          {data && (
            <SelectPerson
              data={data}
              index={index}
              control={control}
              relationship={relationship}
            />
          )}
          {relationship.collection === 'actors' && (
            <PersonRoles
              control={control}
              field={field as FieldArrayWithId<Movie, 'people.actors', 'id'>}
              index={index}
            />
          )}
          <button
            type="button"
            className="danger"
            onClick={() => remove(index)}
          >
            X
          </button>
        </div>
      ))}
      <button type="button" onClick={() => append({ name: '' })}>
        Add {stringToTitleCase(relationship.key)}
      </button>
    </>
  );
};
code ./packages/web/src/pages/movies/Form/SelectPerson.tsx
import { useState } from 'react';
import { Control, Controller } from 'react-hook-form';
import { Movie, Person, Relationship } from '@neo4j-crud/shared';
import { FormFieldError } from '../../../components';

type SelectPersonProps = {
  data: Person[];
  control: Control<Movie>;
  index: number;
  relationship: Relationship;
};

type ChangeEvent = React.ChangeEvent<HTMLInputElement | HTMLSelectElement>;

export const SelectPerson: React.FC<SelectPersonProps> = ({
  data,
  control,
  index,
  relationship,
}) => {
  const [addNewPerson, setAddNewPerson] = useState(false);

  const handleChange = (e: ChangeEvent, onChange: (e: ChangeEvent) => void) => {
    if (e.target.value === 'add-new-person') {
      e.target.value = '';
      setAddNewPerson(true);
    }
    onChange(e);
  };

  return (
    <Controller
      name={`people.${relationship.collection}.${index}.name`}
      control={control}
      rules={{ required: true }}
      render={({ field, fieldState }) => (
        <span>
          {addNewPerson ? (
            <input type="text" placeholder="Name" {...field} />
          ) : (
            <select
              {...field}
              onChange={(e) => {
                handleChange(e, field.onChange);
              }}
            >
              <option value="">Select a Person</option>
              <option value="add-new-person">Add new person...</option>
              {data.map((person) => (
                <option key={person.name} value={person.name}>
                  {person.name}
                </option>
              ))}
            </select>
          )}
          <FormFieldError error={fieldState.error} />
        </span>
      )}
    />
  );
};
code ./packages/web/src/pages/movies/Form/PersonRoles.tsx
import { Control, Controller, FieldArrayWithId } from 'react-hook-form';
import { Movie } from '@neo4j-crud/shared';
import { InputText } from '../../../components';

type PersonRolesProps = {
  field: FieldArrayWithId<Movie, 'people.actors', 'id'>;
  control: Control<Movie>;
  index: number;
};

export const PersonRoles: React.FC<PersonRolesProps> = ({
  field,
  control,
  index,
}) => {
  return (
    <div className="roles">
      <div className="pr-8 pb-8">as</div>
      {(field.roles || ['']).map((role, roleIndex) => (
        <Controller
          key={`${field.name}-${roleIndex}`}
          name={`people.actors.${index}.roles.${roleIndex}`}
          control={control}
          rules={{ required: true }}
          render={(props) => <InputText {...props} />}
        />
      ))}
    </div>
  );
};

Test

open http://localhost:4200
Create Edit

Commit

git add .
git commit -m "Movies People Frontend"