Преглед на файлове

appointment overview added

Liontix преди 1 година
родител
ревизия
257bc7f6ad
променени са 4 файла, в които са добавени 231 реда и са изтрити 5 реда
  1. 25 3
      public/html/appointments.html
  2. 109 0
      public/javascripts/appointments.js
  3. 95 0
      public/stylesheets/appointments.css
  4. 2 2
      routes/appointments.js

+ 25 - 3
public/html/appointments.html

@@ -2,9 +2,31 @@
 <html lang="en">
 <head>
     <meta charset="UTF-8">
-    <title>Title</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Appointment Table</title>
+    <script src="../javascripts/appointments.js" async></script>
+    <link rel="stylesheet" href="../stylesheets/appointments.css">
 </head>
 <body>
-
+<div class="container">
+    <h2>Appointments</h2>
+    <table>
+        <thead>
+        <tr>
+            <th>Title</th>
+            <th>Description</th>
+            <th>Due Date</th>
+            <th>Place</th>
+            <th>Time Span</th>
+            <th>Participants</th>
+            <th>Edit</th>
+            <th>Delete</th>
+        </tr>
+        </thead>
+        <tbody id="appointment-table-body">
+        <!-- Data will be inserted here via JavaScript -->
+        </tbody>
+    </table>
+</div>
 </body>
-</html>
+</html>

+ 109 - 0
public/javascripts/appointments.js

@@ -0,0 +1,109 @@
+"use strict";
+
+let last_clicked = "";
+let click_counter = 0;
+
+// Function to format time as HH:MM
+function formatTime(date) {
+    const hours = date.getUTCHours().toString().padStart(2, '0');
+    const minutes = date.getUTCMinutes().toString().padStart(2, '0');
+    return `${hours}:${minutes}`;
+}
+
+// Function to get the time span (e.g., 09:00 to 10:00)
+function getTimeSpan(start, end) {
+    return `${formatTime(new Date(start))} to ${formatTime(new Date(end))}`;
+}
+
+// Render the appointments into the table
+function renderAppointments(data) {
+    const tableBody = document.getElementById('appointment-table-body');
+    tableBody.innerHTML = '';
+    data.forEach(appointment => {
+        const row = document.createElement('tr');
+        const titleCell = document.createElement('td');
+        const descriptionCell = document.createElement('td');
+        const placeCell = document.createElement('td');
+        const dueDateCell = document.createElement('td');
+        const timeSpanCell = document.createElement('td');
+        const participantCell = document.createElement('td');
+        const editCell = document.createElement('td');
+        const deleteCell = document.createElement('td');
+        const participantATag = document.createElement('a');
+        const editATag = document.createElement('a');
+        const deleteButton = document.createElement('button');
+        participantATag.href = `/html/bookings.html?appointment=${appointment._id}`;
+        participantATag.innerText = 'participants';
+        editATag.href = `/html/appointscreator.html?appointment=${appointment._id}`;
+        editATag.innerText = 'edit';
+        deleteButton.onclick = async () => {
+            if (last_clicked !== appointment._id) {
+                click_counter += 1;
+                last_clicked = appointment._id;
+                return;
+            }
+
+            await deleteAppointment(appointment._id);
+        }
+        deleteButton.innerText = 'delete';
+        participantCell.appendChild(participantATag);
+        editCell.appendChild(editATag);
+        deleteCell.appendChild(deleteButton);
+
+        titleCell.textContent = appointment.title;
+        descriptionCell.textContent = appointment.description;
+        dueDateCell.textContent = new Date(appointment.dueDate).toLocaleDateString();
+        placeCell.textContent = appointment.place;
+
+        timeSpanCell.textContent = getTimeSpan(appointment.startDate, appointment.endDate);
+        timeSpanCell.classList.add('time-span');
+
+        // Append cells to the row
+        row.appendChild(titleCell);
+        row.appendChild(descriptionCell);
+        row.appendChild(dueDateCell);
+        row.appendChild(placeCell);
+        row.appendChild(timeSpanCell);
+        row.appendChild(participantCell);
+        row.appendChild(editCell);
+        row.appendChild(deleteCell);
+
+        // Append the row to the table body
+        tableBody.appendChild(row);
+        tableBody.appendChild(row);
+    });
+}
+
+async function fetchWithToken(url, options) {
+    const authToken = localStorage.getItem('token');
+    return await fetch(url, {...options, headers: {
+            'Authorization': `Bearer ${authToken}`,
+            'Content-Type': 'application/json',
+        }});
+}
+
+async function deleteAppointment(appointmentId) {
+    const response = await fetchWithToken(`/api/users/delete`, {
+        method: 'DELETE',
+        body: JSON.stringify({appointmentId: appointmentId})
+    });
+    if (response.ok) {
+        await fetchAppointments();
+    }
+}
+
+async function fetchAppointments() {
+    const response = await fetchWithToken("/api/users/appointments");
+    const js = await response.json();
+    if (!response.ok) {
+        window.location.replace("/html/404.html");
+    }
+    renderAppointments(js);
+}
+
+// Call the function to render data
+window.onload = async function () {
+    await fetchAppointments();
+}
+
+

+ 95 - 0
public/stylesheets/appointments.css

@@ -0,0 +1,95 @@
+body {
+    font-family: 'Arial', sans-serif;
+    background-color: #f4f4f9;
+    margin: 0;
+    padding: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 100vh;
+    box-sizing: border-box;
+}
+
+.container {
+    width: 90%;
+    max-width: 1200px;
+    background-color: #ffffff;
+    padding: 20px;
+    border-radius: 10px;
+    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+    overflow-x: auto; /* Ensure content is scrollable on smaller screens */
+}
+
+h2 {
+    text-align: center;
+    color: #333333;
+    margin-bottom: 20px;
+}
+
+table {
+    width: 100%;
+    border-collapse: collapse;
+    margin: 20px 0;
+    font-size: 16px;
+    color: #333333;
+}
+
+th, td {
+    text-align: left;
+    padding: 12px;
+    border-bottom: 1px solid #dddddd;
+    word-wrap: break-word; /* Prevent text from overflowing */
+}
+
+th {
+    background-color: #0078d7;
+    color: white;
+}
+
+tr:hover {
+    background-color: #f1f1f1;
+}
+
+.time-span {
+    font-weight: bold;
+    color: #0078d7;
+}
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+    body {
+        height: auto;
+        padding: 20px;
+    }
+
+    h2 {
+        font-size: 1.5rem;
+    }
+
+    table {
+        font-size: 14px;
+    }
+
+    th, td {
+        padding: 10px;
+    }
+}
+
+@media (max-width: 480px) {
+    table {
+        font-size: 12px;
+    }
+
+    th, td {
+        padding: 8px;
+    }
+
+    th {
+        font-size: 0.9rem;
+    }
+}
+
+/* Ensure table stays scrollable on smaller screens */
+.container {
+    overflow-x: auto;
+}

+ 2 - 2
routes/appointments.js

@@ -11,11 +11,11 @@ router.post('/create', async function(req, res, next) {
     const appointmentId = appointment;
 
     if (!appointmentId || !firstname || !lastname || !email) {
-        res.status(400).json({ 'error': 'Parameters are incomplete' });
+        res.status(400).json({ 'message': 'Parameters are incomplete' });
         return;
     }
     if (!isValidEmail(email)) {
-        res.status(400).json({ 'error': 'Email is invalid' });
+        res.status(400).json({ 'message': 'Email is invalid' });
         return;
     }