-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add crud for events * mass import * wip * fix finish mass import * fix migration filter ban events * fix: review * fix: add type url
- Loading branch information
Showing
18 changed files
with
1,127 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import React from 'react' | ||
|
||
type DateInputProps = { | ||
label: string; | ||
onChange: (value: string) => void; | ||
value?: string[]; | ||
} | ||
|
||
export const DateInput = ({label, onChange, value }: DateInputProps) => { | ||
|
||
return ( | ||
<div> | ||
<label className='fr-label' htmlFor={`select-${label}`}> | ||
{label} | ||
</label> | ||
<input type="date" value={value} onChange={event => onChange(event.target.value)} /> | ||
</div> | ||
) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
import React, { useState } from "react"; | ||
import styled from "styled-components"; | ||
import { Button } from "@codegouvfr/react-dsfr/Button"; | ||
import { Input } from "@codegouvfr/react-dsfr/Input"; | ||
import { Checkbox } from "@codegouvfr/react-dsfr/Checkbox"; | ||
import { EventType, EventTypeTypeEnum } from "types/event"; | ||
import SelectInput from "@/components/select-input"; | ||
import { capitalize } from "@/lib/util/string"; | ||
import { EventTypeTagEnum } from "../../types/event"; | ||
import { MultiSelectInput } from "../multi-select-input"; | ||
|
||
type EventFormProps = { | ||
title: string | React.ReactNode; | ||
data?: EventType; | ||
onSubmit?: (formData: Partial<EventType>) => Promise<void>; | ||
submitLabel?: string; | ||
controls?: React.ReactNode; | ||
isCreation?: boolean; | ||
}; | ||
|
||
const typeOptions = Object.values(EventTypeTypeEnum).map((value) => ({ | ||
value, | ||
label: capitalize(value), | ||
})); | ||
|
||
const tagOptions = Object.values(EventTypeTagEnum).map((value) => ({ | ||
value, | ||
label: capitalize(value), | ||
})); | ||
|
||
const StyledForm = styled.form` | ||
h3, | ||
h4 { | ||
margin-bottom: 1rem; | ||
} | ||
section { | ||
margin: 1rem 0; | ||
.perimeter-checkbox { | ||
margin-top: 1rem; | ||
} | ||
} | ||
.form-controls { | ||
display: flex; | ||
align-items: center; | ||
> :not(:first-child) { | ||
margin-left: 1rem; | ||
} | ||
} | ||
`; | ||
|
||
const newEventForm = { | ||
type: EventTypeTypeEnum.FORMATION, | ||
title: "", | ||
subtitle: "", | ||
description: "", | ||
target: "", | ||
date: "", | ||
startHour: "", | ||
endHour: "", | ||
isOnlineOnly: true, | ||
address: {}, | ||
isSubscriptionClosed: false, | ||
instructions: "", | ||
tags: [], | ||
}; | ||
|
||
export const EventForm = ({ | ||
title, | ||
data, | ||
onSubmit, | ||
submitLabel, | ||
controls, | ||
}: EventFormProps) => { | ||
const [formData, setFormData] = useState<Partial<EventType>>( | ||
data || newEventForm | ||
); | ||
|
||
const handleEdit = | ||
(property: string) => | ||
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | ||
const { value } = e.target; | ||
setFormData((state) => ({ ...state, [property]: value })); | ||
}; | ||
|
||
const handleToggle = (property: string) => () => { | ||
setFormData((state) => ({ ...state, [property]: !state[property] })); | ||
}; | ||
|
||
const handleSubmit = async (e) => { | ||
e.preventDefault(); | ||
|
||
await onSubmit(formData); | ||
}; | ||
|
||
return ( | ||
<StyledForm onSubmit={handleSubmit} className="fr-my-4w"> | ||
{title} | ||
<section> | ||
<h4>Informations générales</h4> | ||
<div className="fr-grid-row fr-grid-row--gutters"> | ||
<div className="fr-col-2"> | ||
<SelectInput | ||
label="Type d'évènement*" | ||
value={formData.type} | ||
options={typeOptions} | ||
handleChange={(type) => { | ||
setFormData((state) => ({ | ||
...state, | ||
type: type as EventTypeTypeEnum, | ||
})); | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-5"> | ||
<Input | ||
label="Titre*" | ||
nativeInputProps={{ | ||
required: true, | ||
value: formData.title, | ||
onChange: handleEdit("title"), | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-5"> | ||
<Input | ||
label="Soutitre" | ||
nativeInputProps={{ | ||
value: formData.subtitle, | ||
onChange: handleEdit("subtitle"), | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-4"> | ||
<Input | ||
label="Description*" | ||
nativeInputProps={{ | ||
required: true, | ||
value: formData.description, | ||
onChange: handleEdit("description"), | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-4"> | ||
<Input | ||
label="Cible*" | ||
nativeInputProps={{ | ||
required: true, | ||
value: formData.target, | ||
onChange: handleEdit("target"), | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-4"> | ||
<MultiSelectInput | ||
label="Tags" | ||
value={formData.tags} | ||
options={tagOptions} | ||
placeholder="Sélectionnez un ou plusieurs tags" | ||
onChange={(tags) => { | ||
setFormData((state) => ({ | ||
...state, | ||
tags, | ||
})); | ||
}} | ||
/> | ||
</div> | ||
</div> | ||
</section> | ||
<section> | ||
<h4>Informations pratiques</h4> | ||
<div className="fr-grid-row fr-grid-row--gutters"> | ||
<div className="fr-col-4"> | ||
<Input | ||
label="Date*" | ||
nativeInputProps={{ | ||
required: true, | ||
type: "date", | ||
value: formData.date, | ||
onChange: handleEdit("date"), | ||
}} | ||
/> | ||
</div> | ||
|
||
<div className="fr-col-4"> | ||
<Input | ||
label="Heure de début*" | ||
nativeInputProps={{ | ||
required: true, | ||
type: "time", | ||
value: formData.startHour, | ||
onChange: handleEdit("startHour"), | ||
}} | ||
/> | ||
</div> | ||
|
||
<div className="fr-col-4"> | ||
<Input | ||
label="Heure de fin*" | ||
nativeInputProps={{ | ||
required: true, | ||
type: "time", | ||
value: formData.endHour, | ||
onChange: handleEdit("endHour"), | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-6"> | ||
<Input | ||
label="Lien d'inscription*" | ||
nativeInputProps={{ | ||
required: true, | ||
value: formData.href, | ||
onChange: handleEdit("href"), | ||
type: "url", | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-6"> | ||
<Input | ||
label="Instructions" | ||
nativeInputProps={{ | ||
value: formData.instructions, | ||
onChange: handleEdit("instructions"), | ||
}} | ||
/> | ||
</div> | ||
<div className="fr-col-3"> | ||
<Checkbox | ||
options={[ | ||
{ | ||
label: "Inscriptions fermées", | ||
nativeInputProps: { | ||
checked: formData.isSubscriptionClosed, | ||
onChange: handleToggle("isSubscriptionClosed"), | ||
}, | ||
}, | ||
]} | ||
/> | ||
</div> | ||
</div> | ||
</section> | ||
<div className="form-controls"> | ||
<Button type="submit" iconId="fr-icon-save-line"> | ||
{submitLabel || "Enregistrer"} | ||
</Button> | ||
{controls} | ||
</div> | ||
</StyledForm> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import React from "react"; | ||
import type { EventType } from "types/event"; | ||
import { EventTypeTypeEnum } from "types/event"; | ||
import { Badge } from "@codegouvfr/react-dsfr/Badge"; | ||
import { Button } from "@codegouvfr/react-dsfr/Button"; | ||
import Link from "next/link"; | ||
|
||
const getEventTypeColor = (type: EventTypeTypeEnum) => { | ||
switch (type) { | ||
case EventTypeTypeEnum.FORMATION: | ||
return "rgb(15, 111, 0)"; | ||
case EventTypeTypeEnum.PARTENAIRE: | ||
return "rgb(0, 83, 179)"; | ||
case EventTypeTypeEnum.ADRESSE_LAB: | ||
return "rgb(209, 51, 91)"; | ||
case EventTypeTypeEnum.ADRESSE_REGION: | ||
return "rgb(130, 0, 191)"; | ||
default: | ||
return 'black'; | ||
} | ||
}; | ||
|
||
const getDate = (date: string) => { | ||
return new Date(date).toLocaleDateString(); | ||
}; | ||
|
||
const getHours = (startHour: string, endHour: string) => { | ||
return `${startHour.replace(':', 'h')} à ${endHour.replace(':', 'h')}`; | ||
}; | ||
|
||
export const EventItem = ({ | ||
_id, | ||
type, | ||
title, | ||
date, | ||
startHour, | ||
endHour, | ||
}: EventType) => ( | ||
<tr key={_id}> | ||
<td className="fr-col fr-my-1v"> | ||
<Badge style={{background: getEventTypeColor(type), color: 'white'}} noIcon> | ||
{type} | ||
</Badge> | ||
</td> | ||
<td className="fr-col fr-my-1v">{title}</td> | ||
<td className="fr-col fr-my-1v">{getDate(date)}</td> | ||
<td className="fr-col fr-my-1v">{getHours(startHour, endHour)}</td> | ||
<td className="fr-col fr-my-1v"> | ||
<Link | ||
passHref | ||
href={{ | ||
pathname: `/events/${_id}`, | ||
}} | ||
> | ||
<Button iconId="fr-icon-arrow-right-line" iconPosition="right"> | ||
Consulter | ||
</Button> | ||
</Link> | ||
</td> | ||
</tr> | ||
); |
Oops, something went wrong.