-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation of a new react app for displaying opening hours from CMS with FullCalendar integration - Ddfform 414 #1023
Merged
kasperg
merged 59 commits into
release/form-sprint-10
from
DDFFORM-414-kalendervisning-vise-kalender
Apr 7, 2024
Merged
Changes from 24 commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
f7c5ba6
Initialize a basic App for `Calendar`
kasperbirch1 a7146fb
Implement `FullCalendar` functionality
kasperbirch1 46f03a1
Add `height="100vh"` + `stickyHeaderDates`
kasperbirch1 4777b1e
Add `allDay` property to default event
kasperbirch1 d1d842e
Add logic that prevents events from spanning over multiple days
kasperbirch1 4ce0ef6
WIP: Implementing new Orval-generated hooks to retrieve CMS opening h…
kasperbirch1 8233f95
Add WireMock for `https://dpl-cms.docker` and add opening hours endpo…
kasperbirch1 bd0a53e
Use data from `useDplOpeningHoursListGET` in `Calendar` app
kasperbirch1 7349ded
Add monthly view to FullCalendar
kasperbirch1 e30239c
Remove `height="100vh"`
kasperbirch1 5b5872a
Change app name to `OpeningHoursEditor`
kasperbirch1 f6ed8a8
Add `eslint-disable-next-line no-alert`
kasperbirch1 3a0cf00
Add `height="auto"`to `FullCalendar`
kasperbirch1 79d6de0
Convert helper to TypeScript (.ts) file
kasperbirch1 bd1aaff
Add functionality to edit/remove events in `FullCalendar`
kasperbirch1 4220af3
Refactor handler functions into `helper.ts`
kasperbirch1 f5b57f0
Refactor event start/end comparison into `isSameDay` function
kasperbirch1 4668e00
Refactor `helper.ts` to utilize arrow functions
kasperbirch1 a1e902d
Remove unnecessary 'inline function' from `eventClick`
kasperbirch1 6616578
Refactored `handleEventRemove`
kasperbirch1 1320070
Refactor `OpeningHoursEditorEventContent` into component structure
kasperbirch1 d221388
Add `selectMirror` and Start/End Time to `OpeningHoursEditorEventCont…
kasperbirch1 77cbc69
Add `useOpeningHours` hook
kasperbirch1 25676c2
Refactor `useOpeningHours` hook to fetch opening hours data internally
kasperbirch1 04316ad
Refactor `isSameDay` to `adjustEndDateToStartDay`
kasperbirch1 d8ddd99
Move event dates to week 12 in WireMock
kasperbirch1 70e4c65
POC: Add `Dialog`
kasperbirch1 00e8bc5
Get rid of TypeScript ESLint errors.
kasperbirch1 84ba0fd
Add form elements for event editing in `Dialog`
kasperbirch1 60a7f86
Implemented `DialogFormularAdd` / `DialogFormularEdit`
kasperbirch1 7e09799
Update to latest openAPI spec from DPL-CMS
kasperbirch1 5e56748
Use date objects in helper functions
kasperbirch1 fd6cc81
Fix comment casing in Dialog component
kasperbirch1 921442c
Move `Dialog` to components folder for shared use
kasperbirch1 bbfe79b
Move 'Remove event' button inside `DialogFormularEdit`
kasperbirch1 8a3105f
Set `allDay: false` for Event addition
kasperbirch1 0c988ce
Disable all-day slot in the `FullCalendar`
kasperbirch1 ae3f072
Update to Latest OpenAPI Spec and Refactor `handleEventRemove`
kasperbirch1 963734c
Update wiremock for `useDplOpeningHoursListGET``
kasperbirch1 df3be2f
WIP: Refactor `handleEventAdd`
kasperbirch1 a814e73
Fix date selection Issue + Add `useEscapeKey` hook to `Dialog`
kasperbirch1 2e52b7c
Refactor `useOpeningHours` + Add logic for `handleEventEditing`
kasperbirch1 f19326b
Refactor Add/edit event logic for functionality and code clarity
kasperbirch1 9807ed3
Refactor `EventForm` to use CSS classes and `children` prop
kasperbirch1 bc52558
Rearrange code for improved readability
kasperbirch1 11b039e
Add `withConfig` to handle props from dpl-cms
kasperbirch1 309420e
Add `isSameTime` to disable button in `EventForm`
kasperbirch1 7a3f07c
Add CSS class and simplify`OpeningHoursEditorEventContent`
kasperbirch1 baccd3f
Upgrade `orval` and Execute `yarn codegen:client:dpl-cms`
kasperbirch1 69d878f
Rename `useOpeningHours` to `useOpeningHoursEditor`
kasperbirch1 687b1ef
Merge pull request #1048 from danskernesdigitalebibliotek/DDFFORM-500…
kasperbirch1 625b22b
Merge branch 'release/form-sprint-10' into DDFFORM-414-kalendervisnin…
kasperbirch1 0220c48
Fix Comments in Pull Request
kasperbirch1 5fc436f
Remove 'orval' from dependencies
kasperbirch1 f0e9bd0
Merge branch 'release/form-sprint-10' into DDFFORM-414-kalendervisnin…
kasperg 4d9609e
Delete CMS model before generating a new one with Orval
kasperg 640238e
Update our generated CMS client code
kasperg 9763866
Set initial date through a prop in opening hours editor
kasperg a197f8c
Update opening hours deleted stub to match spec
kasperg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
18 changes: 18 additions & 0 deletions
18
.docker/wiremock/cms/mappings/dummy-479e26d6-5197-4d8f-a2dd-fefe64aabc14.json
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,18 @@ | ||
{ | ||
"id" : "479e26d6-5197-4d8f-a2dd-fefe64aabc14", | ||
"name" : "Opening hours", | ||
"request" : { | ||
"url" : "/dpl_opening_hours", | ||
"method" : "GET" | ||
}, | ||
"response" : { | ||
"status" : 200, | ||
"body" : "[\n {\n \"category\": {\n \"title\": \"Open\"\n },\n \"date\": \"2024-03-14\",\n \"start_time\": \"09:00\",\n \"end_time\": \"17:00\"\n },\n {\n \"category\": {\n \"title\": \"Open\"\n },\n \"date\": \"2024-03-15\",\n \"start_time\": \"09:00\",\n \"end_time\": \"17:00\"\n }\n]", | ||
"headers" : { } | ||
}, | ||
"uuid" : "479e26d6-5197-4d8f-a2dd-fefe64aabc14", | ||
"persistent" : true, | ||
"priority" : 5, | ||
"insertionIndex" : 0, | ||
"postServeActions" : [ ] | ||
} |
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
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
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
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
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
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,16 @@ | ||
import { ComponentMeta, ComponentStory } from "@storybook/react"; | ||
import React from "react"; | ||
import serviceUrlArgs from "../../core/storybook/serviceUrlArgs"; | ||
import OpeningHoursEditor from "./OpeningHoursEditor.entry"; | ||
|
||
export default { | ||
title: "Apps / OpeningHoursEditor", | ||
component: OpeningHoursEditor, | ||
argTypes: { | ||
...serviceUrlArgs | ||
} | ||
} as ComponentMeta<typeof OpeningHoursEditor>; | ||
|
||
export const App: ComponentStory<typeof OpeningHoursEditor> = (args) => ( | ||
<OpeningHoursEditor {...args} /> | ||
); |
13 changes: 13 additions & 0 deletions
13
src/apps/opening-hours-editor/OpeningHoursEditor.entry.tsx
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,13 @@ | ||
import React from "react"; | ||
import GuardedApp from "../../components/guarded-app"; | ||
import { withText } from "../../core/utils/text"; | ||
import { withUrls } from "../../core/utils/url"; | ||
import OpeningHoursEditor from "./OpeningHoursEditor"; | ||
|
||
const CalendarEntry: React.FC = () => ( | ||
<GuardedApp app="opening-hours-editor"> | ||
<OpeningHoursEditor /> | ||
</GuardedApp> | ||
); | ||
|
||
export default withUrls(withText(CalendarEntry)); |
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,4 @@ | ||
import addMount from "../../core/addMount"; | ||
import OpeningHoursEditor from "./OpeningHoursEditor.entry"; | ||
|
||
addMount({ appName: "opening-hours-editor", app: OpeningHoursEditor }); |
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,41 @@ | ||
import React from "react"; | ||
import FullCalendar from "@fullcalendar/react"; | ||
import timeGridPlugin from "@fullcalendar/timegrid"; | ||
import dayGridPlugin from "@fullcalendar/daygrid"; | ||
import interactionPlugin from "@fullcalendar/interaction"; | ||
import da from "@fullcalendar/core/locales/da"; | ||
import OpeningHoursEditorEventContent from "./OpeningHoursEditorEventContent"; | ||
import useOpeningHours from "./useOpeningHours"; | ||
|
||
const OpeningHoursEditor: React.FC = () => { | ||
const { events, handleEventSelect, handleEventClick, handleEventRemove } = | ||
useOpeningHours(); | ||
|
||
return ( | ||
<FullCalendar | ||
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]} | ||
headerToolbar={{ | ||
left: "title", | ||
center: "prev,next today", | ||
right: "dayGridMonth,timeGridWeek" | ||
}} | ||
initialView="timeGridWeek" | ||
locale={da} | ||
selectable | ||
select={handleEventSelect} | ||
eventClick={handleEventClick} | ||
eventContent={(eventInput) => | ||
OpeningHoursEditorEventContent({ | ||
kasperbirch1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
eventInput, | ||
handleEventRemove | ||
}) | ||
} | ||
events={events} | ||
stickyHeaderDates | ||
height="auto" | ||
selectMirror | ||
/> | ||
); | ||
}; | ||
|
||
export default OpeningHoursEditor; |
48 changes: 48 additions & 0 deletions
48
src/apps/opening-hours-editor/OpeningHoursEditorEventContent.tsx
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,48 @@ | ||
import React from "react"; | ||
import { EventInput } from "@fullcalendar/core"; | ||
|
||
type OpeningHoursEditorEventContentProps = { | ||
eventInput: EventInput; | ||
handleEventRemove: (event: EventInput) => void; | ||
}; | ||
|
||
const OpeningHoursEditorEventContent: React.FC< | ||
OpeningHoursEditorEventContentProps | ||
> = ({ eventInput, handleEventRemove }) => { | ||
const { event } = eventInput; | ||
return ( | ||
<div | ||
style={{ | ||
padding: "5px 10px", | ||
cursor: "pointer", | ||
width: "100%" | ||
}} | ||
> | ||
<b>{event.title}</b> | ||
<div | ||
style={{ | ||
display: "flex", | ||
justifyContent: "space-between" | ||
}} | ||
> | ||
<span> | ||
{event.start?.toLocaleTimeString()} -{" "} | ||
{event.end?.toLocaleTimeString()} | ||
</span> | ||
|
||
<span | ||
role="button" | ||
tabIndex={0} | ||
onKeyDown={(e) => { | ||
if (e.key === "Enter") handleEventRemove(event); | ||
}} | ||
onClick={() => handleEventRemove(event)} | ||
> | ||
❌ | ||
</span> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default OpeningHoursEditorEventContent; |
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,29 @@ | ||
import { DplOpeningHoursListGET200Item } from "../../core/dpl-cms/model"; | ||
|
||
const formatDateTimeString = (date: string, time: string): string => { | ||
return `${date}T${time}:00`; | ||
Comment on lines
+6
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using dayjs for this instead of simple string manipulation. If we need the data to be in a specific format e.g. ISO8601 they also have helpers for this. |
||
}; | ||
|
||
export const createCmsEventId = (title: string, startDay: Date) => { | ||
return `${title}-${startDay.toISOString()}`; | ||
}; | ||
|
||
export const formatCmsEventsToFullCalendar = ( | ||
data: DplOpeningHoursListGET200Item[] | ||
) => { | ||
return data.map(({ category, date, start_time, end_time }) => { | ||
const startDateTime = new Date(formatDateTimeString(date, start_time)); | ||
return { | ||
id: createCmsEventId(category.title, startDateTime), | ||
title: category.title, | ||
start: formatDateTimeString(date, start_time), | ||
end: formatDateTimeString(date, end_time), | ||
allDay: false, | ||
color: "blue" | ||
}; | ||
}); | ||
}; | ||
|
||
export const isSameDay = (startDay: Date, endDay: Date) => { | ||
return startDay.toDateString() === endDay.toDateString(); | ||
}; |
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,80 @@ | ||
import { useState, useEffect } from "react"; | ||
import { EventInput, DateSelectArg, EventClickArg } from "@fullcalendar/core"; | ||
import { | ||
createCmsEventId, | ||
formatCmsEventsToFullCalendar, | ||
isSameDay | ||
} from "./helper"; | ||
import { useDplOpeningHoursListGET } from "../../core/dpl-cms/dpl-cms"; | ||
|
||
const useOpeningHours = () => { | ||
const { data: openingHoursData } = useDplOpeningHoursListGET(); | ||
const [events, setEvents] = useState<EventInput[]>([]); | ||
|
||
useEffect(() => { | ||
if (openingHoursData) { | ||
const formattedEvents = formatCmsEventsToFullCalendar(openingHoursData); | ||
setEvents(formattedEvents); | ||
} | ||
}, [openingHoursData]); | ||
|
||
const handleEventSelect = (selectInfo: DateSelectArg) => { | ||
// Todo: Replace prompt with a modal | ||
// eslint-disable-next-line no-alert | ||
const title = prompt("Please enter a new title for your event"); | ||
const calendarApi = selectInfo.view.calendar; | ||
|
||
if (title) { | ||
// Checks if the selected end date is different from the start day; if so, sets the end date to be the same as the start day and the end time to 00:00:00 | ||
const startDay = new Date(selectInfo.startStr); | ||
let endDay = new Date(selectInfo.endStr); | ||
|
||
if (!isSameDay(startDay, endDay)) { | ||
endDay = new Date(startDay); | ||
// Adds one day to the end day and sets the time to 00:00:00ß | ||
endDay.setDate(endDay.getDate() + 1); | ||
endDay.setHours(0, 0, 0); | ||
} | ||
|
||
setEvents([ | ||
...events, | ||
{ | ||
title, | ||
start: startDay.toISOString(), | ||
end: endDay.toISOString(), | ||
allDay: selectInfo.allDay, | ||
color: "green", | ||
id: createCmsEventId(title, startDay) | ||
} | ||
]); | ||
} | ||
|
||
// clear date selection | ||
calendarApi.unselect(); | ||
}; | ||
|
||
const handleEventClick = (clickInfo: EventClickArg) => { | ||
// eslint-disable-next-line no-alert | ||
const newTitle = prompt( | ||
"Enter a new title for this event", | ||
clickInfo.event.title | ||
); | ||
|
||
if (newTitle) { | ||
clickInfo.event.setProp("title", newTitle); | ||
} | ||
}; | ||
|
||
const handleEventRemove = (eventToRemove: EventInput) => { | ||
setEvents(events.filter((event) => event.id !== eventToRemove.id)); | ||
}; | ||
|
||
return { | ||
events, | ||
handleEventSelect, | ||
handleEventClick, | ||
handleEventRemove | ||
}; | ||
}; | ||
|
||
export default useOpeningHours; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This must be changed back when this pr is merged
danskernesdigitalebibliotek/dpl-cms#890