Skip to content

Commit

Permalink
Merge pull request #43 from erland-syafiq/applicants-delete
Browse files Browse the repository at this point in the history
Applicants delete
  • Loading branch information
erland-syafiq authored Jul 3, 2024
2 parents 3952972 + 9b64887 commit 2552542
Show file tree
Hide file tree
Showing 15 changed files with 478 additions and 94 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The VTMUNC website is hosted on an Amazon EC2 instance, which serves as the core
- [⚙️ Environment Variables](docs/env-file.md)
- [📂 Project Overview](docs/project-overview.md)
- [📙 Site Overview](docs/site-overview.md)
- [📘 API Reference ](docs/api-reference.md)
- [🎨 Style Guide](docs/style-guide.md)
- [🌐 Deployment](docs/deployment.md)

Expand Down
6 changes: 6 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 📘 API Reference

Here are all of the APIs the VTMUNC website uses. Linked are more detailed api documentation, including example headers and responses, as well as which endpoints require authorization.

## Table of Contents
- [/applicants](/docs/api/applicants.md)
206 changes: 206 additions & 0 deletions docs/api/applicants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# /applicants

## Base URL
`/api/applicants`

## Overview
The `/api/applicants` endpoint allows for managing applicant data. It supports the following operations:
- `GET`: Retrieve all applicants.
- `POST`: Add a new applicant.
- `DELETE`: Delete an existing applicant by ID.

## Authorization
- The `GET` and `DELETE` methods require an authorization JWT for an admin user.

## Endpoints

### GET `/api/applicants`

#### Description
Fetches a list of all applicants.

#### Fields of each applicant
| Field | Type | Description |
|--------------------------|--------|--------------------------------------|
| `advisorPhone` | string | The phone number of the advisor. |
| `delegationSize` | number | The size of the delegation. |
| `headDelegateName` | string | The name of the head delegate. |
| `schoolName` | string | The name of the school. |
| `advisorOtherInformation`| string | Other information about the advisor. |
| `commentsOrQuestions` | string | Any comments or questions. |
| `advisorEmail` | string | The email of the advisor. |
| `advisorRelation` | string | The relation of the advisor. |
| `schoolMailingAddress` | string | The mailing address of the school. |
| `headDelegateEmail` | string | The email of the head delegate. |
| `headDelegatePhone` | string | The phone number of the head delegate.|
| `advisorName` | string | The name of the advisor. |
| `delegateList` | string | A list of delegates. |
| `id` | number | Unique applicant id. |
| `date` | string | When applicant was created (YYYY-MM-DD)|
| `invoiceStatus` | number | Status of invoice (1: Invoice not sent, 2: Payment not received, 3: Payment received) |


#### Headers
| Key | Value |
|---------------|------------------------|
| Authorization | Bearer `<JWT token>` |

#### Response
| Status Code | Description |
|-------------------------|-----------------------------------------|
| 200 OK | Returns a JSON array of applicants. |
| 500 Internal Server Error | An error occurred while fetching applicants. |

#### Example Request
```http
GET /api/applicants HTTP/1.1
Host: yourdomain.com
Authorization: Bearer <JWT token>
```

#### Example Response
```json
[
{
"advisorPhone": "123-456-7890",
"delegationSize": 10,
"headDelegateName": "John Doe",
"schoolName": "Example School",
"advisorOtherInformation": "Other information",
"commentsOrQuestions": "Comments",
"advisorEmail": "advisor@example.com",
"advisorRelation": "Relation",
"schoolMailingAddress": "123 Example St",
"headDelegateEmail": "delegate@example.com",
"headDelegatePhone": "123-456-7890",
"advisorName": "Jane Smith",
"delegateList": "Jeffery Visonaire, Adam",
"invoiceStatus": 0,
"id": 12345,
"date": "2024-07-02"
}
]
```

### POST `/api/applicants`

#### Description
Creates a new applicant.

#### Request Body
| Field | Type | Description |
|--------------------------|--------|--------------------------------------|
| `advisorPhone` | string | The phone number of the advisor. |
| `delegationSize` | number | The size of the delegation. |
| `headDelegateName` | string | The name of the head delegate. |
| `schoolName` | string | The name of the school. |
| `advisorOtherInformation`| string | Other information about the advisor. |
| `commentsOrQuestions` | string | Any comments or questions. |
| `advisorEmail` | string | The email of the advisor. |
| `advisorRelation` | string | The relation of the advisor. |
| `schoolMailingAddress` | string | The mailing address of the school. |
| `headDelegateEmail` | string | The email of the head delegate. |
| `headDelegatePhone` | string | The phone number of the head delegate.|
| `advisorName` | string | The name of the advisor. |
| `delegateList` | string | A list of delegates. |

#### Response
| Status Code | Description |
|-------------------------|----------------------------------------------|
| 200 OK | Returns the created applicant. |
| 500 Internal Server Error | An error occurred while creating the applicant. |

#### Example Request
```http
POST /api/applicants HTTP/1.1
Host: yourdomain.com
Content-Type: application/json
{
"advisorPhone": "123-456-7890",
"delegationSize": 10,
"headDelegateName": "John Doe",
"schoolName": "Example School",
"advisorOtherInformation": "Other information",
"commentsOrQuestions": "Comments",
"advisorEmail": "advisor@example.com",
"advisorRelation": "Relation",
"schoolMailingAddress": "123 Example St",
"headDelegateEmail": "delegate@example.com",
"headDelegatePhone": "123-456-7890",
"advisorName": "Jane Smith",
"delegateList": "Jeffery Visonaire, Adam"
}
```

#### Example Response
```json
{
"advisorPhone": "123-456-7890",
"delegationSize": 10,
"headDelegateName": "John Doe",
"schoolName": "Example School",
"advisorOtherInformation": "Other information",
"commentsOrQuestions": "Comments",
"advisorEmail": "advisor@example.com",
"advisorRelation": "Relation",
"schoolMailingAddress": "123 Example St",
"headDelegateEmail": "delegate@example.com",
"headDelegatePhone": "123-456-7890",
"advisorName": "Jane Smith",
"delegateList": "Jeffery Visonaire, Adam",
"invoiceStatus": 0,
"id": 12345,
"date": "2024-07-02"
}
```

### DELETE `/api/applicants`

#### Description
Deletes an existing applicant by ID.

#### Headers
| Key | Value |
|---------------|------------------------|
| Authorization | Bearer `<JWT token>` |

#### Request Body
| Field | Type | Description |
|-------|--------|----------------------------|
| `id` | number | The ID of the applicant to delete. |

#### Response
| Status Code | Description |
|-------------------------|-----------------------------------------|
| 200 OK | Returns a message confirming the deletion. |
| 400 Bad Request | The request body did not contain an ID. |
| 500 Internal Server Error | An error occurred while deleting the applicant. |

#### Example Request
```http
DELETE /api/applicants HTTP/1.1
Host: yourdomain.com
Content-Type: application/json
Authorization: Bearer <JWT token>
{
"id": 12345
}
```

#### Example Response
```json
{
"message": "Deleted applicant with id: 12345"
}
```

## Error Handling
In case of errors, the API returns appropriate HTTP status codes along with a message indicating the error.

### Error Responses
| Status Code | Description |
|-------------------------|---------------------------------------------------|
| 500 Internal Server Error | An error occurred while processing the request. |
| 400 Bad Request | The request body did not contain necessary information. |
31 changes: 27 additions & 4 deletions site/app/api/applicants/route.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/**
* Endpoint: /api/applicants
*
* Reference: /docs/api/applicants.md
*/

import { generateRandomId, getCurrentDate } from "@/app/utils/util";
import { getApplicants, putApplicant } from "../db/dynamodb";
import { deleteApplicant, getApplicants, putApplicant } from "../db/dynamodb";

export async function GET() {
try {
Expand All @@ -8,7 +14,7 @@ export async function GET() {
}
catch (e) {
console.log(e);
return new Response("Error with applicants", 500);
return new Response("Error with applicants", {status: 500});
}
}

Expand All @@ -33,12 +39,29 @@ export async function POST(request) {
id: generateRandomId(),
date: getCurrentDate()
}
console.log(applicant);

await putApplicant(applicant);
return Response.json(body);
}
catch (e) {
console.log(e);
return new Response("Error with applicants", 500);
return new Response("Error with applicants", {status: 500});
}
}

export async function DELETE(request) {
try {
const body = await request.json();
const id = body.id;
if (!id) {
return new Response("No id sent", {status: 400})
}

await deleteApplicant(id);
return new Response(`Deleted applicant with id: ${id}`, {status: 200});
}
catch (e) {
console.log(e);
return new Response("Error with deletion", {status: 500});
}
}
13 changes: 11 additions & 2 deletions site/app/api/db/dynamodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ export async function putApplicant(applicant) {
Item: applicant
}

console.log(params);

await dynamoClient.put(params).promise();
}

export async function deleteApplicant(id) {
const params = {
TableName: TABLE_NAME,
Key: {
id: id
}
}

await dynamoClient.delete(params).promise();
}
12 changes: 11 additions & 1 deletion site/app/applicants/DashboardPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@
margin-bottom: 40px;
margin-right: 10px;
margin-left: 10px;
border-radius: 0px;
}

.card > h2 {
color: var(--primary);
}

.dashboard {
background: var(--secondary-background);
background-color: var(--secondary-background);
}

.transparent-overlay-delete-confirmation {
background-color: rgba(0, 0, 0, 0.2);
}

.applicantsLoading {
width: 150px;
height: 150px;
}
71 changes: 71 additions & 0 deletions site/app/applicants/DeleteConfirmationModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useRef, useState } from "react";

const APPLICANTS_URL = "/api/applicants";

export default function DeleteConfirmationModal({applicant, showDeleteConfirmation, setShowDeleteConfirmation, deleteApplicant}) {

const modalRef = useRef(null);
const [error, setError] = useState("");

async function handleDelete() {
try {
const response = await fetch(APPLICANTS_URL, {
method: "DELETE",
mode: "cors",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: applicant.id
})
});

if (response.status != 200) {
throw new Error(`Response error code: ${response.status}, Response message: ${response.body}`)
}

setShowDeleteConfirmation(false);
deleteApplicant(applicant);
}
catch (e) {

setError(`${e.name}: ${e.message}`);
}
}

function handleClickOnModal(event) {
if (modalRef && !modalRef.current.contains(event.target)) {
setShowDeleteConfirmation(false);
}
}


return (
<div className={`modal ${showDeleteConfirmation ? 'd-block' : 'd-none'} transparent-overlay-delete-confirmation`} tabIndex="-1" role="dialog" onClick={handleClickOnModal}>
<div className="modal-dialog modal-xl" role="document" ref={modalRef}>
<div className="modal-content card">
<div className="modal-header">
<h3 className="modal-title">Confirm Deletion</h3>
<button type="button" className="close" onClick={() => setShowDeleteConfirmation(false)} aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
<h4>Are you sure you want to delete this applicant?</h4>
<br />
<p><strong>Advisor:</strong> {applicant.advisorName}</p>
<p><strong>Advisor Relation:</strong> {applicant.advisorRelation}</p>
<p><strong>School Name:</strong> {applicant.schoolName}</p>
<p><strong>School Address:</strong> {applicant.schoolMailingAddress}</p>
<p><strong>Delegation Size:</strong> {applicant.delegationSize}</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={() => setShowDeleteConfirmation(false)}>Cancel</button>
<button type="button" className="btn btn-danger" onClick={handleDelete}>Delete</button>
</div>
<span className="text-danger">{error}</span>
</div>
</div>
</div>
)
}
Loading

0 comments on commit 2552542

Please sign in to comment.