Skip to content

Commit

Permalink
Feat: add crud event (#46)
Browse files Browse the repository at this point in the history
* 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
MaGOs92 authored Jan 23, 2024
1 parent cbc86c6 commit 72dfa80
Show file tree
Hide file tree
Showing 18 changed files with 1,127 additions and 62 deletions.
20 changes: 20 additions & 0 deletions components/date-input.tsx
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>
)
}

254 changes: 254 additions & 0 deletions components/events/event-form.tsx
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>
);
};
61 changes: 61 additions & 0 deletions components/events/event-item.tsx
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>
);
Loading

0 comments on commit 72dfa80

Please sign in to comment.