Calendar App

React, Redux, MongoDB, Node, Express, Bootstrap, Vite

Description

Fully functional and fullstack calendar App.

  • Includes Login page for Auth, React-big-calendar- React-date-picker- Middlewares- Form Auth
  • Authentication handled with JWT.
  • Redux for state management.
  • MongoDB

Key takeaways

This is a complex project both in the backend and frontend. Besides all route and controller logic with authentication, CRUD and so on in the backend, my main takeaway for this project is the state management with Redux in the frontend which is depicted in the diagram below.

Store

Combines all reducers and apply async-await fuction in replace of Thunks (middleware that allows you to return functions).

View code
1export const calendarSlice = createSlice({
2  name: "calendar",
3  initialState: {
4    isLoadingEvents: true,
5    events: [
6      //tempEvent
7    ],
8    activeEvent: null,
9  },
10  reducers: {
11    onSetActiveEvent: (state, { payload }) => {
12      state.activeEvent = payload;
13    },
14    onAddNewEvent: (state, { payload }) => {
15      state.events.push(payload);
16      state.activeEvent = null;
17    },
18    onUpdateEvent: (state, { payload }) => {
19      state.events = state.events.map((event) => {
20        if (event.id === payload.id) {
21          return payload;
22        }
23        return event;
24      });
25    },
26    onDeleteEvent: (state) => {
27      if (state.activeEvent) {
28        state.events = state.events.filter(
29          (event) => event.id !== state.activeEvent.id
30        );
31        state.activeEvent = null;
32      }
33    },
34
35    onLoadEvents: (state, { payload = [] }) => {
36      state.isLoadingEvents = false;
37      //state.events = payload;
38      payload.forEach((event) => {
39        const exists = state.events.some((dbEvent) => dbEvent.id === event.id);
40        if (!exists) {
41          state.events.push(event);
42        }
43      });
44    },
45    onLogoutCalendar: (state) => {
46      (state.isLoadingEvents = true), (state.events = []);
47      state.activeEvent = null;
48    },
49  },
50});
51
52export const {
53  onSetActiveEvent,
54  onAddNewEvent,
55  onUpdateEvent,
56  onDeleteEvent,
57  onLoadEvents,
58  onLogoutCalendar,
59} = calendarSlice.actions;
60

Components

This is the look of the composition of the calendar App.

View code
1export const CalendarPage = () => {
2
3  const { user } = useAuthStore();
4  const { openDateModal } = useUiStore ();
5  const { events, setActiveEvent, startLoadingEvents } = useCalendarStore();
6
7  const[lastView, setLastView] = useState(localStorage.getItem('lastView') || 'week');
8
9  const eventStyleGetter = ( event, start, end, isSelected ) => {
10
11    //console.log(event);
12
13    const isMyEvent = ( user.uid === event.user._id ) || ( user.uid === event.user._id);
14
15    const style = {
16      backgroundColor: isMyEvent ? '#347CF7' : '#465660',
17      borderRadius: '0px',
18      opacity: 0.8,
19      color: 'white'
20    }
21
22    return {
23      style
24    }
25  }
26
27  const onDoubleClick = ( event ) => {
28   // console.log( {doubleClick: event });
29   openDateModal();
30  }
31
32  const onSelect = ( event ) => {
33    //console.log( {click: event });
34    setActiveEvent( event );
35  }
36
37  const onViewChanged = ( event ) => {
38    localStorage.setItem('lastView', event );
39    setLastView( event )
40  }
41
42  useEffect(() => {
43    startLoadingEvents()
44  }, [])
45
46  return (
47    <>
48      <Navbar />
49
50      <Calendar
51      culture='es'
52      localizer={localizer}
53      events={events}
54      formats
55      defaultView= { lastView }
56      startAccessor="start"
57      endAccessor="end"
58      style={{ height: 'calc( 100vh - 80px )' }}
59      messages={ getMessagesES() }
60      eventPropGetter= { eventStyleGetter }
61      components= {{
62        event: CalendarEvent
63      }}
64      onDoubleClickEvent = { onDoubleClick }
65      onSelectEvent = { onSelect}
66      onView = {onViewChanged}
67
68    />
69
70    <CalendarModal />
71    <FabAddNew />
72    <FabDelete />
73
74    </>
75  )
76

Server

The heart of the backend application. Connects to the DB via config/db and uses all routes made available by routes
and error handling directly from the middleware.

Config/db

Makes the connection to the database.

Routes

Best regarded as Endpoints. Route methods (get, post, put, delete) are defined for the specified route and used in conjunction with a controller and middleware functions, which hold the logic. (e.g. router.post('/login', authUser))

Controllers

Best regarded as the application logic. The functions defined here will be requested when hitting the defined routes/endpoints. It is the place where the logic for a given route is applied.

Models

Defines the DB schema for a given model. also uses bcrypt to compare and hash passwords.

Screenshots

Login and Register  in the same page.
1. Login and Register in the same page.
Navbar component, Fab icons (add+ , delete-), react-big-calendar.
2. Navbar component, Fab icons (add+ , delete-), react-big-calendar.
into the react-modal component, there are handle date-picker component.
3. into the react-modal component, there are handle date-picker component.
modal popup from fab icon.
4. modal popup from fab icon.