-
Notifications
You must be signed in to change notification settings - Fork 1
18 Movies People Frontend
JP Barbosa edited this page Apr 15, 2023
·
2 revisions
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>
);
};
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>
);
};
open http://localhost:4200
Create | Edit |
![]() |
![]() |
git add .
git commit -m "Movies People Frontend"