- Full Typescript support
- Dark & Light mode support
- French & English languages support
- Schedule view with three types : 'event', 'temperature' & 'calendar'
- made with react, react-router-dom, tailwindcss
- The global state of the app is managed by recoil
- All errors are catched and rendered a fallback UI with react-error-boundary
start & end time indication (only in french for the moment, sorry!)
Auto scroll to the first event.
custom modal content (only for the 'event' type of schedule).
The normal modal for all other schedule types is:
To see the live demo: Click here
- as react-router-dom is a dependency of the package, you need to install it first as dependencies in your app.
for info:
yarn add react-router-dom
of course, like all react application wich use react router dom, you need to wrap your App element with a provider into the main.tsx page like that:
// main.tsx
...
<BrowserRouter>
<App />
</BrowserRouter>
...
- recoil & react-error-boundary are dev dependencies of the package, you need to install it first as dev dependencies in your app.
for info:
yarn add -D recoil react-error-boundary
npm install react-simple-schedule-viewer
or
yarn add react-simple-schedule-viewer
Name | Type | Default | Description |
---|---|---|---|
scheduleByEventPlace | Object (required) | { schedules: [] } |
The data object for the schedule. |
weekStartsOn | Number (required) | 0 |
The day of the week start |
isInDarkMode | Boolean (required) | false |
For dark mode support. |
colorCellByEvents | Object (required) | { eventType_1: "", eventType_6: "" } |
The colors of the cells. |
eventsTextColor | Object (required) | { eventType_1: "", eventType_6: "" } |
the color for the text cells calendar. |
locale | String (required) | "fr" |
for fr/en support. |
eventsNameUs | Object (optional) | { eventType_1: "", eventType_6: "" } |
the events name in us version. |
eventsName | Object (optional) | { eventType_1: "", eventType_6: "" } |
the events name in fr version. |
eventTypeData | Object (required) | { eventType_1: "", eventType_6: "" } |
The value of each event (prices, degrees, or other infos). |
modalContent | Array (optional) | [] |
Custom modal content (event type only) |
withDays | boolean (optional) | false |
display the day of each week days. |
withList | Boolean (optional) | false |
display a list of the schedules before the calendar view. |
withListButtonName | String (optional) | "" |
when the list is true you can add a french text for the return button |
withListButtonNameUs | String (optional) | "" |
you can add an English text for the button |
withListReturnButton | Boolean (optional) | false |
when the list is true you can add a return button by setting the value to true |
- weekStartsOn :
you can choice the default day of the beginning of the schedule.
- eventsNameUs & eventsName & modalContent :
These three variables are used only for the 'event' type schedule. for the other types you don't need to implement it.
- withDays :
That variable display the current month on top & indicate the current day with a red point & a more stronger font size. This option is by default for the calendar type (in this case, it's no need to put this variable to the Schedule props).
- withList :
This option display a list of your schedules before the calendar view, with a return button logic.
- withListReturnButton :
This option display a button to return to your home page for example. This option is used when the withList boolean is true.
Example :
withListReturnButton={true} withListButtonName="Retourner Γ la page d'acceuil" withListButtonNameUs="Go to HomePage"
This example is implemented with fake data for easily copy/paste in your project & adding more convenience for understanding the logic.
you can see the example code: here
// App.tsx
import { Suspense, useEffect, useState } from "react";
import "../App.css";
import { Route, Routes } from "react-router-dom";
// If you don't have light & dark theme, you can skip this import & set false to isInDarkMode Schedule props
import { useTheme } from "../useTheme";
// All types for the Schedule
import {
TcolorCellByEvents,
TeventsTextColor,
} from "../dataTypes";
// The content for each event modal
import { contentForModal } from "./dataCards";
// Home page example
import HomePage from "../HomePage";
// The enum for the event types & all data for the schedule cells
import { EeventTypes, eventTypeData, scheduleByEventPlace } from "./eventData";
//Import for the npm registry
import Schedule from "react-simple-schedule-viewer";
function App() {
// Variables for the Schedule component
const weekStartsOn = 0;
const { theme, setTheme } = useTheme();
const [isDarkMode] = useState(theme === "dark" ? true : false);
// the default order of background colors in the array is
const colorCellByEvents: TcolorCellByEvents = {
[EeventTypes.food]: "#FFECC8", // eventType_1 - required
[EeventTypes.concert]:"#FFAACF", // eventType_2 - optional
[EeventTypes.game_force]: "#BFF6C3", // eventType_3 - optional
[EeventTypes.game_dark]: "#CDC1FF", // eventType_4 - optional
[EeventTypes.lecture]: "#7BD3EA", // eventType_5 - optional
[EeventTypes.closed]: isDarkMode ? "#2D3648" : "#f3f3f3", // eventType_6 - required - is always the away, closed or absent event
// eventType_7: "#FFDEFA", // eventType_7 - optional - unused in this example
};
// the default order of text colors in the array is
const eventsTextColor: TeventsTextColor = {
[EeventTypes.food]: "#c8a026", // eventType_1 - required
[EeventTypes.concert]: "#7b2e7b", // eventType_2 - optional
[EeventTypes.game_force]: "#0eb318", // eventType_3 - optional
[EeventTypes.game_dark]: "#756AB6", // eventType_4 - optional
[EeventTypes.lecture]: "#1e646e", // eventType_5 - optional
[EeventTypes.closed]: "#84878a", // eventType_6 - required - is always the away, closed or absent event
// eventType_7: "#B97A95", // eventType_7 - optional -- unused in this example
};
// Just for the demo
useEffect(() => {
if (isDarkMode) {
setTheme("dark");
} else if (!isDarkMode) {
setTheme("light");
}
});
return (
<div className="App">
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/schedule/*" // The path imperatively must be '/schedule/*'
element={
<Suspense fallback={<div>Loading...</div>}>
<Schedule
scheduleByEventPlace={scheduleByEventPlace}
weekStartsOn={weekStartsOn}
isInDarkMode={isDarkMode}
colorCellByEvents={colorCellByEvents}
eventsTextColor={eventsTextColor}
locale={"fr"}
eventTypeData={eventTypeData}
modalContent={contentForModal}
// If you want to display the number of the day on each day names
// withDays={false}
// If you want to display a list with all schedules before the calendar view
// withList={false}
// If you want to display a return button to the list of schedules
// withListReturnButton={true}
// If you display a return button, the french button text
// withListButtonName="Retourner Γ la page d'acceuil"
// If you display a return button, the english button text
// withListButtonNameUs="Go to HomePage"
/>
</Suspense>
}
/>
</Routes>
</div>
);
}
export default App;
- (the same for each type of schedule)
// dataTypes.ts
export interface getSchedulesByEventPlaceIdResponse {
schedules: {
id: string;
title: string;
type: string;
day_slot_set: {
days: number[];
time_slot: {
start: number;
instruction: string;
}[];
}[];
}[];
}
export type TeventTypeData = {
eventPlace_id?: string;
eventType_1: string;
eventType_2?: string;
eventType_3?: string;
eventType_4?: string;
eventType_5?: string;
eventType_6: string;
eventType_7?: string;
};
export type TcolorCellByEvents = Omit<TeventTypeData, "eventPlace_id">;
export type TeventsTextColor = Omit<TeventTypeData, "eventPlace_id">;
export enum LanguageKeys {
en = "en",
fr = "fr",
}
export type TeventsName = {
eventType_1: string;
eventType_2?: string;
eventType_3?: string;
eventType_4?: string;
eventType_5?: string;
eventType_6: string;
eventType_7?: string;
}
// eventData.ts
import { getSchedulesByEventPlaceIdResponse, TeventTypeData } from "../../dataTypes";
// enum for identidying the event_Type easily
export enum EeventTypes {
food = "eventType_1",
concert = "eventType_2",
game_force = "eventType_3",
game_dark = "eventType_4",
lecture = "eventType_5",
closed = "eventType_6", // eventType_6 must be always the away, closed or no activity event
}
// Mocked data for example - set the price of each event for example
export const eventTypeData: TeventTypeData = {
eventPlace_id: "b4514cca-bd0f-4876-a3c3-b77444c047b4", // If you have many places with different prices this id must be unique
[EeventTypes.food]: "12.5 Euros",
[EeventTypes.concert]: "20 Euros",
[EeventTypes.game_force]: "21 Euros",
[EeventTypes.game_dark]: "10 Euros",
[EeventTypes.lecture]: "6.5 Euros",
[EeventTypes.closed]: "pas d'Γ©vΓ©nement",
};
// Mocked data for example - schedules array with each calendar cells event
export const scheduleByEventPlace: getSchedulesByEventPlaceIdResponse = {
schedules: [
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749", // Unique schedule id identifier
title: "Exemple de calendrier de type 'event'", // title of the event
type: "event", // type of the event (event - temperature - calendar)
day_slot_set: [
// each days & time slot instruction, start time only & the type of event (eventType_1 for example)
{
days: [0], // Monday
time_slot: [
{
start: 0, // start at midnight with closed type event
instruction: EeventTypes.closed,
},
{
start: 600, // start at 10:00AM (this is the end of the previous event too)
instruction: EeventTypes.lecture,
},
{
start: 720, // start at 12:00AM with food (eventType_1 in this example)
instruction: EeventTypes.food,
},
{
start: 900,
instruction: EeventTypes.game_dark,
},
{
start: 1230,
instruction: EeventTypes.concert,
},
{
start: 1380,
instruction: EeventTypes.closed,
},
],
},
{
days: [1],
time_slot: [
{
start: 0,
instruction: EeventTypes.closed,
},
{
start: 600,
instruction: EeventTypes.lecture,
},
{
start: 720,
instruction: EeventTypes.food,
},
{
start: 900,
instruction: EeventTypes.game_force,
},
{
start: 1020,
instruction: EeventTypes.concert,
},
{
start: 1380,
instruction: EeventTypes.closed,
},
],
},
{
days: [2],
time_slot: [
{
start: 0,
instruction: EeventTypes.closed,
},
{
start: 495,
instruction: EeventTypes.game_dark,
},
{
start: 720,
instruction: EeventTypes.food,
},
{
start: 900,
instruction: EeventTypes.game_dark,
},
{
start: 1215,
instruction: EeventTypes.closed,
},
],
},
{
days: [3],
time_slot: [
{
start: 0,
instruction: EeventTypes.closed,
},
{
start: 450,
instruction: EeventTypes.concert,
},
{
start: 900,
instruction: EeventTypes.closed,
},
],
},
{
days: [4],
time_slot: [
{
start: 0,
instruction: EeventTypes.closed,
},
{
start: 480,
instruction: EeventTypes.game_force,
},
{
start: 1380,
instruction: EeventTypes.closed,
},
],
},
{
days: [5],
time_slot: [
{
start: 0,
instruction: EeventTypes.closed,
},
{
start: 450,
instruction: EeventTypes.game_dark,
},
{
start: 1020,
instruction: EeventTypes.closed,
},
],
},
{
days: [6],
time_slot: [
{
start: 0,
instruction: EeventTypes.closed,
},
],
},
],
},
{
id: "40f80dba-ab6c-4f71-8a4d-51ce439e9b44",
title: "Exemple de calendrier de type 'event' vide",
type: "event",
day_slot_set: [
{
days: [0, 1, 2, 3, 4, 5, 6],
time_slot: [
{
start: 0,
instruction: EeventTypes.closed,
},
],
},
],
},
],
};
- only for event type
// dataCards.tsx
import { Fragment } from "react/jsx-runtime";
import { EeventTypes } from "./enum";
export const contentForModal = [
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749", // The id of the schedule
day: 0, // The day of the event
eventType: EeventTypes.concert, // The event type - for this example it's the eventType_2 based with the enum name
startTime: 1230, // The start time of the event - the end of this event is defined by the next start time event
eventTitle: "Englewood Concert", // The title
contentModal: ( // This variable get the JSX Element you want, can be card, external component...
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event de lundi Γ 20:30 et rΓ©servation
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 3,
eventType: EeventTypes.concert,
startTime: 450,
eventTitle: "Englewood Concert",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event de jeudi Γ 07:30 et rΓ©servation
</h1>
<p style={{marginBottom:"1vh"}}>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 1,
eventType: EeventTypes.concert,
startTime: 1020,
eventTitle: "Englewood Concert",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du mardi Γ 17h info et rΓ©servation
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 1,
eventType: EeventTypes.game_force,
startTime: 900,
eventTitle: "Force mage game",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du mardi Γ 15h00 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 4,
eventType: EeventTypes.game_force,
startTime: 480,
eventTitle: "Force mage game",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du vendredi Γ 08h00 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 5,
eventType: EeventTypes.game_dark,
startTime: 450,
eventTitle: "Dark rider game",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du samedi Γ 07:30 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 2,
eventType: EeventTypes.game_dark,
startTime: 495,
eventTitle: "Dark rider game",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du mercredi Γ 08:15 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 2,
eventType: EeventTypes.game_dark,
startTime: 900,
eventTitle: "Dark rider game",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du mercredi Γ 15:00 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 0,
eventType: EeventTypes.lecture,
eventTitle: "Marvel Conf",
startTime: 600,
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
session de discussion sur les revues Marvel
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 0,
eventType: EeventTypes.game_dark,
startTime: 900,
eventTitle: "Dark rider game",
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du lundi Γ 15:00 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 1,
eventType: EeventTypes.lecture,
eventTitle: "Marvel Conf",
startTime: 600,
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
session de discution sur les revues Marvel
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 0,
eventType: EeventTypes.food,
eventTitle: "Food Party",
startTime: 720,
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du lundi Γ 12:00 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 1,
eventType: EeventTypes.food,
eventTitle: "Food Party",
startTime: 720,
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du mardi Γ 12:00 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
{
id: "56a2bd7c-a898-4502-8414-fa4ace79e749",
day: 2,
eventType: EeventTypes.food,
eventTitle: "Food Party",
startTime: 720,
contentModal: (
<Fragment>
<h1 style={{ fontWeight: "bold", padding: 10 }}>
Event du lundi Γ 12:00 info
</h1>
<p>This is the place for the custom element (JSXElement)</p>
<h2 style={{fontWeight:"bold"}}>Text example</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta
quibusdam delectus esse! Excepturi, omnis sapiente at quo tempora
eaque repellendus, a aspernatur impedit reiciendis laborum optio sed
quasi cupiditate amet.
</p>
</Fragment>
),
},
];
import { Link } from "react-router-dom";
const HomePage = () => {
return (
<div>
<Link style={{fontSize:18}}
data-testid="demo-button"
to={`/schedule/`}
>
Go to demo...
</Link>
<h1>Go to Schedule view</h1>
</div>
);
};
export default HomePage;
This example is implemented with fake data for easily copy/paste in your project & adding more convenience for understanding the logic.
you can see the example code: here
// App.tsx
import { Suspense, useEffect, useState } from "react";
import "../App.css";
import { Route, Routes } from "react-router-dom";
import Schedule from "react-simple-schedule-viewer";
import { useTheme } from "../theme/useTheme";
import { TcolorCellByEvents, TeventsName, TeventsTextColor } from "./dataTypes";
import { EeventTypes, eventTypeData, scheduleByEventPlace } from "./eventData";
import React from "react";
import HomePage from "../HomePage";
function App() {
// Variables for the Schedule component
const weekStartsOn = 0;
const { theme, setTheme } = useTheme();
const [isDarkMode] = useState(theme === "dark" ? true : false);
// the default order of background colors in the array is
const colorCellByEvents: TcolorCellByEvents = {
[EeventTypes.presence_1]: "#FFECC8", // eventType_1 - required
[EeventTypes.presence_2]: "#FFAACF", // eventType_2 - optional
[EeventTypes.presence_3]: "#BFF6C3", // eventType_3 - optional
[EeventTypes.presence_4]: "#CDC1FF", // eventType_4 - optional
[EeventTypes.eco]: "#7BD3EA", // eventType_5 - optional
[EeventTypes.away]: isDarkMode ? "#2D3648" : "#f3f3f3", // eventType_6 - required - is always the away, closed or absent event
// eventType_7: "#FFDEFA", // eventType_7 - optional
};
// the default order of text colors in the array is
const eventsTextColor: TeventsTextColor = {
[EeventTypes.presence_1]: "#c8a026", // eventType_1 - required
[EeventTypes.presence_2]: "#7b2e7b", // eventType_2 - optional
[EeventTypes.presence_3]: "#0eb318", // eventType_3 - optional
[EeventTypes.presence_4]: "#756AB6", // eventType_4 - optional
[EeventTypes.eco]: "#1e646e", // eventType_5 - optional
[EeventTypes.away]: "#84878a", // eventType_6 - required - is always the away, closed or absent event
// eventType_7: "#B97A95", // eventType_7 - optional
};
// This is for TEMP & CALENDAR type of schedule, the names of all eventTypes.
// (for the EVENT type, the name of the event is on the contentForModal - eventTitle)
const eventsName: TeventsName = {
[EeventTypes.presence_1]: "PrΓ©sence 1",
[EeventTypes.presence_2]: "PrΓ©sence 2",
[EeventTypes.presence_3]: "PrΓ©sence 3",
[EeventTypes.presence_4]: "PrΓ©sence 4",
[EeventTypes.eco]: "Γco",
[EeventTypes.away]: "Absence",
};
// For french/English support both at the same time
const eventsNameUs: TeventsName = {
[EeventTypes.presence_1]: "Presence 1",
[EeventTypes.presence_2]: "Presence 2",
[EeventTypes.presence_3]: "Presence 3",
[EeventTypes.presence_4]: "Presence 4",
[EeventTypes.eco]: "Eco",
[EeventTypes.away]: "Away",
};
useEffect(() => {
if (isDarkMode) {
setTheme("dark");
} else if (!isDarkMode) {
setTheme("light");
}
});
return (
<div className="App">
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/schedule/*"
element={
<Suspense fallback={<div>Loading...</div>}>
<Schedule
scheduleByEventPlace={scheduleByEventPlace}
weekStartsOn={weekStartsOn}
isInDarkMode={isDarkMode}
colorCellByEvents={colorCellByEvents}
eventsTextColor={eventsTextColor}
locale={"fr"}
eventsNameUs={eventsNameUs}
eventsName={eventsName}
eventTypeData={eventTypeData}
// If you want to display the number of the day on each day names
// withDays={false}
// If you want to display a list with all schedules before the calendar view
// withList={false}
// If you want to display a return button to the list of schedules
// withListReturnButton={true}
// If you display a return button, the french button text
// withListButtonName="Retourner Γ la page d'acceuil"
// If you display a return button, the english button text
// withListButtonNameUs="Go to HomePage"
/>
</Suspense>
}
/>
</Routes>
</div>
);
}
export default App;
- (the same for each type of schedule)
// dataTypes.ts
export interface getSchedulesByEventPlaceIdResponse {
schedules: {
id: string;
title: string;
type: string;
day_slot_set: {
days: number[];
time_slot: {
start: number;
instruction: string;
}[];
}[];
}[];
}
export type TeventTypeData = {
eventPlace_id?: string;
eventType_1: string;
eventType_2?: string;
eventType_3?: string;
eventType_4?: string;
eventType_5?: string;
eventType_6: string;
eventType_7?: string;
};
export type TcolorCellByEvents = Omit<TeventTypeData, "eventPlace_id">;
export type TeventsTextColor = Omit<TeventTypeData, "eventPlace_id">;
export enum LanguageKeys {
en = "en",
fr = "fr",
}
export type TeventsName = {
eventType_1: string;
eventType_2?: string;
eventType_3?: string;
eventType_4?: string;
eventType_5?: string;
eventType_6: string;
eventType_7?: string;
}
// eventData.ts
import {
getSchedulesByEventPlaceIdResponse,
TeventTypeData,
} from "./dataTypes";
// enum for identidying the event_Type easily
export enum EeventTypes {
presence_1 = "eventType_1",
presence_2 = "eventType_2",
presence_3 = "eventType_3",
presence_4 = "eventType_4",
eco = "eventType_5",
away = "eventType_6", // eventType_6 must be always the away, away or no activity event
// appointement = "eventType_7", no used in this example
}
export const scheduleByEventPlace: getSchedulesByEventPlaceIdResponse = {
schedules: [
{
id: "f00b3838-4906-4549-a299-0ed334937ab3",
title: "Exemple de calendrier de type 'temp'",
type: "temp",
day_slot_set: [
{
days: [0, 1, 2, 3, 4],
time_slot: [
{
start: 0,
instruction: EeventTypes.away,
},
{
start: 360,
instruction: EeventTypes.eco,
},
{
start: 600,
instruction: EeventTypes.presence_1,
},
{
start: 720,
instruction: EeventTypes.presence_2,
},
{
start: 960,
instruction: EeventTypes.presence_3,
},
],
},
{
days: [5, 6],
time_slot: [
{
start: 0,
instruction: EeventTypes.away,
},
{
start: 360,
instruction: EeventTypes.presence_4,
},
],
},
],
},
{
id: "0dabee1d-0d06-4245-9c2c-235d28817cd0",
title: "Exemple de calendrier de type 'temp' vide",
type: "temp",
day_slot_set: [
{
days: [0, 1, 2, 3, 4, 5, 6],
time_slot: [
{
start: 0,
instruction: EeventTypes.away,
},
],
},
],
},
],
};
export const eventTypeData: TeventTypeData = {
eventPlace_id: "b4514cca-bd0f-4876-a3c3-b77444c047b4",
[EeventTypes.presence_1]: "19",
[EeventTypes.presence_2]: "20",
[EeventTypes.presence_3]: "21",
[EeventTypes.presence_4]: "22",
[EeventTypes.eco]: "6.5",
[EeventTypes.away]: "12",
};
import { Link } from "react-router-dom";
const HomePage = () => {
return (
<div>
<Link style={{fontSize:18}}
data-testid="demo-button"
to={`/schedule/`}
>
Go to demo...
</Link>
<h1>Go to Schedule view</h1>
</div>
);
};
export default HomePage;
This example is implemented with fake data for easily copy/paste in your project & adding more convenience for understanding the logic.
you can see the example code: here
// App.tsx
import { Suspense, useEffect, useState } from "react";
import "../App.css";
import { Route, Routes } from "react-router-dom";
import { TcolorCellByEvents, TeventsName, TeventsTextColor } from "./dataTypes";
import {EeventTypes, eventTypeData, scheduleByEventPlace } from "./eventData";
import React from "react";
import { useTheme } from "../theme/useTheme";
import HomePage from "../HomePage";
import Schedule from "react-simple-schedule-viewer";
function App() {
// Variables for the Schedule component
const weekStartsOn = 0;
const { theme, setTheme } = useTheme();
const [isDarkMode] = useState(theme === "dark" ? true : false);
// the default order of background colors in the array is
const colorCellByEvents: TcolorCellByEvents = {
[EeventTypes.appointement]: "#FFDEFA", // eventType_1 - required
[EeventTypes.away]: isDarkMode ? "#2D3648" : "#f3f3f3", // eventType_6 - required - is always the away, closed or absent event
};
// the default order of text colors in the array is
const eventsTextColor: TeventsTextColor = {
[EeventTypes.appointement]: "#B97A95", // eventType_1 - required
[EeventTypes.away]: "#84878a", // eventType_6 - required - is always the away, closed or absent event
};
// This is for TEMP & CALENDAR type of schedule, the names of all eventTypes.
// (for the EVENT type, the name of the event is on the contentForModal - eventTitle)
const eventsName: TeventsName = {
[EeventTypes.appointement]: "rendez-vous quotidien",
[EeventTypes.away]: "Pas de rendez-vous",
};
// For french/English support both at the same time
const eventsNameUs: TeventsName = {
[EeventTypes.appointement]: "daily appointment",
[EeventTypes.away]: "No appointment",
};
useEffect(() => {
if (isDarkMode) {
setTheme("dark");
} else if (!isDarkMode) {
setTheme("light");
}
});
return (
<div className="App">
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/schedule/*"
element={
<Suspense fallback={<div>Loading...</div>}>
<Schedule
scheduleByEventPlace={scheduleByEventPlace}
weekStartsOn={weekStartsOn}
isInDarkMode={isDarkMode}
colorCellByEvents={colorCellByEvents}
eventsTextColor={eventsTextColor}
locale={"fr"}
eventsNameUs={eventsNameUs}
eventsName={eventsName}
eventTypeData={eventTypeData}
/>
</Suspense>
}
/>
</Routes>
</div>
);
}
export default App;
- (the same for each type of schedule)
// dataTypes.ts
export interface getSchedulesByEventPlaceIdResponse {
schedules: {
id: string;
title: string;
type: string;
day_slot_set: {
days: number[];
time_slot: {
start: number;
instruction: string;
}[];
}[];
}[];
}
export type TeventTypeData = {
eventPlace_id?: string;
eventType_1: string;
eventType_2?: string;
eventType_3?: string;
eventType_4?: string;
eventType_5?: string;
eventType_6: string;
eventType_7?: string;
};
export type TcolorCellByEvents = Omit<TeventTypeData, "eventPlace_id">;
export type TeventsTextColor = Omit<TeventTypeData, "eventPlace_id">;
export enum LanguageKeys {
en = "en",
fr = "fr",
}
export type TeventsName = {
eventType_1: string;
eventType_2?: string;
eventType_3?: string;
eventType_4?: string;
eventType_5?: string;
eventType_6: string;
eventType_7?: string;
}
// eventData.ts
import {
getSchedulesByEventPlaceIdResponse,
TeventTypeData,
} from "./dataTypes";
// enum for identidying the event_Type easily
export enum EeventTypes {
appointement = "eventType_1",
away = "eventType_6", // eventType_6 must be always the away, away or no activity event
// appointement = "eventType_7", no used in this example
}
export const scheduleByEventPlace: getSchedulesByEventPlaceIdResponse = {
schedules: [
{
id: "d86ae448-012e-4f87-a7e5-d8e667346ea6",
title: "Exemple de calendrier de type 'calendar'",
type: "calendar",
day_slot_set: [
{
days: [0, 1, 2, 3, 4, 5, 6],
time_slot: [
{
start: 0,
instruction: EeventTypes.away,
},
{
start: 600,
instruction: EeventTypes.appointement,
},
{
start: 960,
instruction: EeventTypes.away,
},
],
},
],
},
{
id: "61d6024f-8d18-43dc-8cdc-9c38c796b93b",
title: "Exemple de calendrier de type 'calendar' vide",
type: "calendar",
day_slot_set: [
{
days: [0, 1, 2, 3, 4, 5, 6],
time_slot: [
{
start: 0,
instruction: EeventTypes.away,
},
],
},
],
},
],
};
export const eventTypeData: TeventTypeData = {
eventPlace_id: "b4514cca-bd0f-4876-a3c3-b77444c047b4",
[EeventTypes.appointement]: "", // No need to put some text here for this type of schedule but this value is required
[EeventTypes.away]: "Pas de rendez-vous", // This value is required too
};
import { Link } from "react-router-dom";
const HomePage = () => {
return (
<div>
<Link style={{fontSize:18}}
data-testid="demo-button"
to={`/schedule/`}
>
Go to demo...
</Link>
<h1>Go to Schedule view</h1>
</div>
);
};
export default HomePage;
- You can combine all schedule types at the same time,
- if you want to see how, there is a link to the complex demo repository wich combine all schedules type together.
![]()
Contributions to react-simple-schedule-viewer
are welcome! If you have any issues, feature requests, or improvements, please open an issue or submit a pull request on the GitHub repository. Your feedback and support are highly appreciated!
If you encounter any problems while using the library, please open an issue on GitHub. Provide as much detail as possible, including steps to reproduce the issue and any relevant code or screenshots.
Have an idea for a new feature? Please open an issue with a detailed description of the feature you'd like to see, and why it would be useful.
Thank you for your interest in contributing to react-simple-schedule-viewer
!
- Thought, designed and developed with π by Rodolphe Augusto
Enjoy the World π
I put almost everything open-source I can, and try to accommodate anyone who needs help using these projects. Obviously, this takes time. You can use this service for free.
However, if you are using this project and are happy with it or just want to encourage me to keep creating: -
- Put a star and share the project π
Thank you! β€οΈ
The MIT License.
https://github.com/rodolphe37/my-simple-tasks-manager-desktop-version
https://github.com/rodolphe37/halloween2021-bat-tuto-youtube-video
https://github.com/rodolphe37/cra-template-github-my-profile
https://github.com/rodolphe37/react-native_geolocation-tracker
https://github.com/rodolphe37/cra-pwa-react-ultimate-messenger