Skip to content
This repository has been archived by the owner on Apr 15, 2024. It is now read-only.

Commit

Permalink
Merge pull request #53 from HybridPlanner/docs/add-jsdoc
Browse files Browse the repository at this point in the history
docs: add documentation to the code
  • Loading branch information
jvondermarck authored Dec 15, 2023
2 parents eb2a285 + 6315a7c commit 82cbc74
Show file tree
Hide file tree
Showing 18 changed files with 170 additions and 26 deletions.
3 changes: 3 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* API client for making HTTP requests.
*/
import Axios from "axios";

export const apiClient = Axios.create({
Expand Down
32 changes: 32 additions & 0 deletions src/api/meetings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

import { CreateMeetingInput, Meeting } from "@/types/Meeting";
import { apiClient } from ".";

/**
* Retrieves future meetings from the server.
* @returns An array of Meeting objects representing future meetings.
*/
export async function getFutureMeetings(): Promise<Meeting[]> {
const response = await apiClient.get<{ meetings: Meeting[] }>("/meetings");

Expand All @@ -11,6 +16,11 @@ export async function getFutureMeetings(): Promise<Meeting[]> {
}));
}

/**
* Retrieves previous meetings from the server that occurred before the specified date.
* @param before The date before which the meetings should have occurred.
* @returns An array of Meeting objects representing previous meetings.
*/
export async function getPreviousMeetings(before: Date): Promise<Meeting[]> {
const response = await apiClient.get<{ meetings: Meeting[] }>(
`/meetings?before=${before.toISOString()}`
Expand All @@ -22,17 +32,33 @@ export async function getPreviousMeetings(before: Date): Promise<Meeting[]> {
}));
}

/**
* Creates a new meeting on the server.
* @param data The input data for creating the meeting.
* @returns The created Meeting object.
*/
export async function createMeeting(
data: CreateMeetingInput
): Promise<Meeting> {
const response = await apiClient.post<Meeting>("/meetings", data);
return response.data;
}

/**
* Deletes a meeting from the server.
* @param id The ID of the meeting to delete.
* @returns A promise that resolves when the meeting is successfully deleted.
*/
export async function deleteMeeting(id: number): Promise<void> {
await apiClient.delete(`/meetings/${id}`);
}

/**
* Retrieves a specific meeting from the server.
* @param id The ID of the meeting to retrieve.
* @returns The Meeting object representing the retrieved meeting.
* @throws An error if the retrieved data is invalid.
*/
export async function getMeeting(id: number): Promise<Meeting> {
const response = await apiClient.get<Meeting>(`/meetings/${id}`);

Expand All @@ -44,6 +70,12 @@ export async function getMeeting(id: number): Promise<Meeting> {
return response.data;
}

/**
* Updates a meeting on the server.
* @param id The ID of the meeting to update.
* @param data The partial input data for updating the meeting.
* @returns The updated Meeting object.
*/
export async function updateMeeting(
id: number,
data: Partial<CreateMeetingInput>
Expand Down
3 changes: 2 additions & 1 deletion src/components/base/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import classNames from "classnames";
import { FC, ReactElement, forwardRef } from "react";
import { type LucideIcon } from "lucide-react";
import { Link, LinkProps, To } from "react-router-dom";

Expand All @@ -23,6 +22,7 @@ type ButtonPropsAsLink = BaseButtonProps &

type ButtonProps = ButtonPropsAsLink | (ButtonPropsAsButton & { to?: never });


export function Button<T>({
className,
children,
Expand Down Expand Up @@ -55,6 +55,7 @@ export function Button<T>({
className
);

// If the button is a link, use the Link component from react-router-dom
if (props.to) {
const propsAsLink = props as ButtonPropsAsLink;

Expand Down
1 change: 1 addition & 0 deletions src/components/base/title/title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const Title = forwardRef<HTMLHeadingElement, TitleProps>(
"text-blue-600 text-2xl font-semibold inline-grid grid-flow-col gap-2 items-center"
)}
>
{/* If the button has an icon props, render it */}
{Icon && (
<span
className="rounded-full bg-blue-100 px-3 py-3"
Expand Down
4 changes: 4 additions & 0 deletions src/components/form/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
({ id, label, error, className, ...props }, ref) => {
return (
<div className={classNames("relative mb-6 group", className)}>
{/* If the button has a label props, render it */}
{label && (
<label
htmlFor={id}
Expand All @@ -22,6 +23,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
</label>
)}
<div className="relative group">
{/* The input element is wrapped in a div to allow the icon to be placed on the left */}
<input
{...props}
placeholder={props.placeholder || label}
Expand All @@ -48,6 +50,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
)}
/>

{/* If the button has an icon props, render it */}
<div
className={classNames(
"absolute pointer-events-none inset-y-0 left-0 flex items-center pl-3 text-gray-500 peer-disabled:text-gray-500",
Expand All @@ -58,6 +61,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
{props.icon}
</div>
</div>
{/* If the button has an error props, render it */}
{error && (
<span className="text-sm text-red-500 px-2 absolute -bottom-6">
{error}
Expand Down
5 changes: 5 additions & 0 deletions src/components/form/inputTags/inputTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const InputTags = forwardRef<HTMLDivElement, InputTagsProps>(
): JSX.Element => {
return (
<div className={classNames("relative mb-6 group", className)}>
{/* If the input tags has a label props, render it */}
{label && (
<label
htmlFor={id}
Expand All @@ -36,6 +37,7 @@ export const InputTags = forwardRef<HTMLDivElement, InputTagsProps>(
</label>
)}
<div className="relative group">
{/* We're using the react-multi-email library to handle the mails input */}
<ReactMultiEmail
className={classNames(
"relative group inputTags",
Expand All @@ -58,6 +60,7 @@ export const InputTags = forwardRef<HTMLDivElement, InputTagsProps>(
onChange={props.onChange}
emails={props.value}
getLabel={(email, index, removeEmail) => (
// To be able to customize the input, we're using our own TagElement component
<TagElement
key={index}
data-tag
Expand All @@ -66,6 +69,7 @@ export const InputTags = forwardRef<HTMLDivElement, InputTagsProps>(
/>
)}
/>
{/* If the input tags has an icon props, render it */}
<div
className={classNames(
"absolute pointer-events-none inset-y-0 left-0 flex items-center pl-3 text-gray-500 peer-disabled:text-gray-500",
Expand All @@ -76,6 +80,7 @@ export const InputTags = forwardRef<HTMLDivElement, InputTagsProps>(
{props.icon}
</div>
</div>
{/* If the input tags has an error props, render it */}
{error && (
<span className="text-sm text-red-500 px-2 absolute -bottom-6">
{error}
Expand Down
2 changes: 2 additions & 0 deletions src/components/form/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type SelectProps = {
export const Select = forwardRef<HTMLSelectElement, SelectProps>(({ id, label, error, ...props }, ref) => {
return (
<div className="relative mb-8">
{/* If the select has a label props, render it */}
{label && (
<label htmlFor={id} className="font-medium text-gray-700">
{label}
Expand Down Expand Up @@ -45,6 +46,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(({ id, label, e
<ChevronDown />
</div>
</div>
{/* If the select has an error props, render it */}
{error && <span className="text-sm text-red-500 px-2">{error}</span>}
</div>
);
Expand Down
3 changes: 3 additions & 0 deletions src/components/form/textarea/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
({ id, label, error, ...props }, ref) => {
return (
<div className="relative mb-6 group">
{/* If the textarea has a label props, render it */}
{label && (
<label
htmlFor={id}
Expand Down Expand Up @@ -48,6 +49,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
)}
></textarea>

{/* If the textarea has an icon props, render it */}
<div
className={classNames(
"absolute pointer-events-none inset-y-0 left-0 flex items-center pl-3 text-gray-500 peer-disabled:text-gray-500",
Expand All @@ -58,6 +60,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
{props.icon}
</div>
</div>
{/* If the textarea has an error props, render it */}
{error && <span className="text-sm text-red-500 px-2">{error}</span>}
</div>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/meeting/MeetingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function MeetingCard({
<MeetingDateBadge meeting={meeting} />
</div>

{/* Is the meeting isn't a previous meeting, then show the actions buttons */}
{!isPrevious && (
<div className="row-start-1 row-span-2 col-start-3 invisible group-hover:visible group-focus-within:visible text-gray-600 flex flex-col gap-2">
<div className="flex flex-row z-[2]">
Expand Down
2 changes: 0 additions & 2 deletions src/components/meeting/MeetingInfoModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ export const MeetingInfoModal = forwardRef<
</ul>
</div>
</div>

<aside className="w-full md:w-52">{/* Meeting actions */}</aside>
</div>
</div>
</form>
Expand Down
19 changes: 19 additions & 0 deletions src/hooks/useFetchMeetings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { Meeting } from "@/types/Meeting";
import { sortMeetings } from "@/utils/date";
import { useCallback, useEffect, useState } from "react";

/**
* Custom hook for fetching meetings data.
* @returns An object containing functions and state variables related to fetching meetings.
*/
export default function useFetchMeetings() {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<Meeting[] | undefined>(undefined);
Expand All @@ -12,6 +16,10 @@ export default function useFetchMeetings() {
undefined
);

/**
* Fetches future meetings from the server and updates the component state.
* @returns {Promise<void>} A promise that resolves when the fetch is complete.
*/
const fetchFutureMeetings = useCallback(async () => {
console.debug("Fetching future meetings");
setLoading(true);
Expand All @@ -32,6 +40,12 @@ export default function useFetchMeetings() {
}
}, [setData, setError]);

/**
* Fetches previous meetings and updates the state with the fetched data.
* If there are no previous meetings, it fetches meetings until the current date.
*
* @returns {void}
*/
const fetchPreviousMeetings = useCallback(async () => {
console.debug("Fetching previous meetings");
setLoading(true);
Expand Down Expand Up @@ -64,10 +78,15 @@ export default function useFetchMeetings() {
}
}, [setPreviousData, setError]);

// Fetch future meetings on component mount
useEffect(() => {
fetchFutureMeetings();
}, []);


/**
* Refreshes the meeting list by resetting the data and error states, and then fetching future meetings.
*/
const refreshMeetingList = useCallback(async () => {
setData(undefined);
setError(undefined);
Expand Down
10 changes: 10 additions & 0 deletions src/hooks/useMeetingDeleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { MeetingDeleteModal } from "@/components/meeting/MeetingDeleteModal";
import { Meeting } from "@/types/Meeting";
import { useCallback, useRef, useState } from "react";

/**
* Custom hook for managing the meeting delete modal.
* @param deleteCallback - Optional callback function to be executed after a meeting is deleted.
* @returns An object containing the function to show the meeting delete modal and the JSX element for the modal.
*/
export default function useMeetingDeleteModal({
deleteCallback,
}: {
Expand All @@ -14,6 +19,11 @@ export default function useMeetingDeleteModal({

const deleteMeetingModalRef = useRef<HTMLDialogElement>(null);

/**
* Show the deletion modal of the meeting.
*
* @param meeting The meeting to delete.
*/
const showMeetingDeleteModal = useCallback(
async (meeting: Meeting) => {
const meetingData = await getMeeting(meeting.id);
Expand Down
14 changes: 13 additions & 1 deletion src/hooks/useMeetingInfoModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,36 @@ import { MeetingInfoModal } from "@/components/meeting/MeetingInfoModal";
import { Meeting } from "@/types/Meeting";
import { useCallback, useRef, useState } from "react";

/**
* Custom hook for managing the meeting info modal.
* This hook provides functions and state variables to show and manage the meeting info modal.
*
* @returns An object containing the following properties:
* - showMeetingInfoModal: A function to show the meeting info modal for a specific meeting.
* - meetingInfoModal: The JSX element representing the meeting info modal.
*/
export default function useMeetingInfoModal() {
// State variable to store the currently selected meeting
const [modalMeeting, setModalMeeting] = useState<Meeting | undefined>(
undefined
);

// Ref to the dialog element used to show the meeting info modal
const showMeetingModalRef = useRef<HTMLDialogElement>(null);

// Function to show the meeting info modal for a specific meeting
const showMeetingInfoModal = useCallback(
async (meeting: Meeting) => {
const meetingData = await getMeeting(meeting.id);
// Fetch meeting data
const meetingData = await getMeeting(meeting.id);
console.debug("Show meeting %O", meetingData);
setModalMeeting(meetingData);
showMeetingModalRef.current?.showModal();
},
[setModalMeeting, showMeetingModalRef]
);

// JSX element representing the meeting info modal
const meetingInfoModal = (
<MeetingInfoModal ref={showMeetingModalRef} meeting={modalMeeting} />
);
Expand Down
6 changes: 6 additions & 0 deletions src/hooks/useMeetingUpdateModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { MeetingUpdateModal } from "@/components/meeting/MeetingUpdateModal";
import { Meeting } from "@/types/Meeting";
import { useCallback, useRef, useState } from "react";

/**
* Custom hook for managing the meeting update modal.
* @param options - The options for the hook.
* @param options.updateCallback - The callback function to be called after a meeting is updated.
* @returns An object containing the function to show the meeting update modal and the JSX element for the modal.
*/
export default function useMeetingUpdateModal({
updateCallback,
}: {
Expand Down
8 changes: 0 additions & 8 deletions src/pages/Login.tsx

This file was deleted.

13 changes: 13 additions & 0 deletions src/pages/meetings/CreateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export function MeetingCreateForm({
const endDate = watch("end_date", "Invalid Date");
const now = new Date();

/**
* Handles the form submission for creating a meeting.
*
* @param data - The form inputs.
* @returns A Promise that resolves when the meeting is created.
*/
const onSubmit: SubmitHandler<FormInputs> = async (data) => {
setLoading(true);

Expand All @@ -74,6 +80,13 @@ export function MeetingCreateForm({
setLoading(false);
};

/**
* Updates the "end_date" value based on the start date and end date.
* If the start date or end date is invalid, the function returns early.
* If the minimal end date is after the current end date, the end date is updated.
* @param startDate The start date of the meeting.
* @param endDate The end date of the meeting.
*/
useEffect(() => {
if (startDate === "Invalid Date" || endDate === "Invalid Date") return;

Expand Down
Loading

0 comments on commit 82cbc74

Please sign in to comment.