Commit a1dce685 authored by Stefan Probst's avatar Stefan Probst
Browse files

feat: add actors screen

parent 8243e423
Pipeline #208069 passed with stages
in 10 minutes and 19 seconds
......@@ -178,6 +178,36 @@ interface CreateActorDialogProps {
* Create new actor dialog.
*/
function CreateActorDialog(props: CreateActorDialogProps) {
const createActor = useCreateActor()
const auth = useAuth()
const queryClient = useQueryClient()
const toast = useToast()
function onSubmit(unsanitized: ActorFormValues) {
if (auth.session?.accessToken === undefined) {
toast.error('Authentication required.')
return Promise.reject()
}
const values = sanitizeActorFormValues(unsanitized)
return createActor.mutateAsync(
[values, { token: auth.session.accessToken }],
{
onSuccess() {
queryClient.invalidateQueries(['getActors'])
toast.success('Sucessfully created actor.')
},
onError() {
toast.error('Failed to submit actor.')
},
onSettled() {
props.onDismiss()
},
},
)
}
return (
<Dialog
isOpen={props.isOpen}
......@@ -196,7 +226,12 @@ function CreateActorDialog(props: CreateActorDialogProps) {
<section className="flex flex-col space-y-6">
<h2 className="text-2xl font-medium">Create new actor</h2>
{/* this form is rendered in a portal, so it's valid html, even though it's a <form> "nested" in another <form>. */}
<CreateActorForm onDismiss={props.onDismiss} />
<CreateActorForm
onDismiss={props.onDismiss}
onSubmit={onSubmit}
isLoading={createActor.isLoading}
buttonLabel="Create"
/>
</section>
</Dialog>
)
......@@ -206,58 +241,34 @@ type ActorFormValues = ActorCore
interface CreateActorFormProps {
onDismiss: () => void
initialValues?: ActorFormValues
onSubmit: (actor: ActorFormValues) => void
isLoading: boolean
buttonLabel: string
}
/**
* Create actor.
*/
function CreateActorForm(props: CreateActorFormProps) {
const createActor = useCreateActor()
const auth = useAuth()
const queryClient = useQueryClient()
const toast = useToast()
function onSubmit(unsanitized: ActorFormValues) {
if (auth.session?.accessToken === undefined) {
toast.error('Authentication required.')
return Promise.reject()
}
const values = sanitizeActorFormValues(unsanitized)
return createActor.mutateAsync(
[values, { token: auth.session.accessToken }],
{
onSuccess() {
queryClient.invalidateQueries(['getActors'])
toast.success('Sucessfully created actor.')
},
onError() {
toast.error('Failed to submit actor.')
},
onSettled() {
props.onDismiss()
},
},
)
}
export function CreateActorForm(props: CreateActorFormProps): JSX.Element {
const { initialValues, onSubmit, isLoading, buttonLabel } = props
function onValidate(values: Partial<ActorFormValues>) {
const errors: Partial<Record<keyof typeof values, any>> = {}
if (values.name === undefined) {
if (values.name == null) {
errors.name = 'Name is required.'
}
if (values.email !== undefined && !isEmail(values.email)) {
if (values.email != null && !isEmail(values.email)) {
errors.email = 'Please provide a valid email address.'
}
if (values.website !== undefined && !isUrl(values.website)) {
if (values.website != null && !isUrl(values.website)) {
errors.website = 'Please provide a valid URL.'
}
if (values.externalIds !== undefined) {
if (values.externalIds != null) {
values.externalIds.forEach((id, index) => {
if (
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
......@@ -267,7 +278,7 @@ function CreateActorForm(props: CreateActorFormProps) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
id.identifier == null
) {
if (errors.externalIds === undefined) {
if (errors.externalIds == null) {
errors.externalIds = []
}
errors.externalIds[index] = {
......@@ -277,7 +288,7 @@ function CreateActorForm(props: CreateActorFormProps) {
})
}
if (values.externalIds !== undefined) {
if (values.externalIds != null) {
values.externalIds.forEach((id, index) => {
if (
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
......@@ -287,7 +298,7 @@ function CreateActorForm(props: CreateActorFormProps) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
id.identifierService?.code == null
) {
if (errors.externalIds === undefined) {
if (errors.externalIds == null) {
errors.externalIds = []
}
errors.externalIds[index] = {
......@@ -301,7 +312,11 @@ function CreateActorForm(props: CreateActorFormProps) {
}
return (
<Form onSubmit={onSubmit} validate={onValidate}>
<Form
onSubmit={onSubmit}
validate={onValidate}
initialValues={initialValues}
>
{({ handleSubmit, pristine, invalid, submitting }) => {
return (
<form
......@@ -379,7 +394,7 @@ function CreateActorForm(props: CreateActorFormProps) {
}
>
<ActorComboBox
name={`${name}.code`}
name={`${name}.id`}
label="Affiliation"
index={index}
/>
......@@ -400,11 +415,9 @@ function CreateActorForm(props: CreateActorFormProps) {
<Button
type="submit"
variant="gradient"
isDisabled={
pristine || invalid || submitting || createActor.isLoading
}
isDisabled={pristine || invalid || submitting || isLoading}
>
Create
{buttonLabel}
</Button>
</div>
</form>
......@@ -414,7 +427,9 @@ function CreateActorForm(props: CreateActorFormProps) {
)
}
function sanitizeActorFormValues(values: ActorFormValues) {
export function sanitizeActorFormValues(
values: ActorFormValues,
): ActorFormValues {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
values.affiliations = values.affiliations?.filter((v) => v != null)
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
......
This diff is collapsed.
......@@ -784,7 +784,7 @@ function EditSourceButton(props: EditSourceButtonProps) {
<Icon icon={CloseIcon} className="w-8 h-8" />
</button>
<section className="flex flex-col space-y-6">
<h2 className="text-2xl font-medium">Add source</h2>
<h2 className="text-2xl font-medium">Edit source</h2>
<AddSourceForm
onDismiss={dialog.close}
onSubmit={onSubmit}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment