This commit is contained in:
Maciej Życzyński 2024-06-21 11:33:20 +02:00
commit c7d5764c43
43 changed files with 1049 additions and 0 deletions

13
todoapp/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-22",
"java.configuration.runtimes": [
{
"name": "JavaSE-22",
"path": "C:\\Program Files\\Java\\jdk-22",
"default": true
}
],
"java.configuration.updateBuildConfiguration": "automatic",
"java.dependency.packagePresentation": "flat"
}

Binary file not shown.

Binary file not shown.

131
todoapp/pom.xml Normal file
View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>todoapp</groupId>
<artifactId>demo</artifactId>
<version>1</version>
<packaging>war</packaging>
<name>demo</name>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>10.0.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>10.0.6</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -0,0 +1,44 @@
package todoapp;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.servlet.DefaultServlet;
public class JettyServer {
public static void main(String[] args) throws Exception {
int port = 8080;
// create Jetty server with 8 threads
QueuedThreadPool threadPool = new QueuedThreadPool(8);
Server server = new Server(threadPool);
// establishing connector
ServerConnector connector = new ServerConnector(server);
connector.setPort(port);
server.addConnector(connector);
// Create a servlet context handler
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/todoapp");
// adding TaskServlet to the servlet context
context.addServlet(new ServletHolder(new TaskServlet()), "/api/tasks/*");
// adding static files from /webapp
String webappDir = "src/main/webapp";
ServletHolder defaultServletHolder = new ServletHolder("default", DefaultServlet.class);
defaultServletHolder.setInitParameter("resourceBase", webappDir);
defaultServletHolder.setInitParameter("dirAllowed", "true");
context.addServlet(defaultServletHolder, "/");
server.setHandler(context);
// starting the server
server.start();
System.out.println("Server started on port " + port);
server.join();
}
}

View File

@ -0,0 +1,37 @@
package todoapp;
public class Task {
private int Number;
private String Text;
private boolean isDone;
public Task(int Number, String Text, boolean isDone) {
this.Number = Number;
this.Text = Text;
this.isDone = isDone;
}
public int getNumber() {
return Number;
}
public void setNumber(int Number) {
this.Number = Number;
}
public String getText() {
return Text;
}
public void setText(String Text) {
this.Text = Text;
}
public boolean isStatus() {
return isDone;
}
public void setStatus(boolean isDone) {
this.isDone = isDone;
}
}

View File

@ -0,0 +1,126 @@
package todoapp;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TaskServlet extends HttpServlet {
//GET, gets info about either all tasks or a specific one depending on path received
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pathInfo = request.getPathInfo();
if (pathInfo == null || pathInfo.equals("/")) {
// return all tasks
List<Task> tasks = TodoAppDAO.getAllTasks();
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json");
mapper.writeValue(response.getWriter(), tasks);
} else {
// return specific task
try {
int taskNumber = Integer.parseInt(pathInfo.substring(1));
Task task = TodoAppDAO.getTaskByNumber(taskNumber);
if (task != null) {
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json");
mapper.writeValue(response.getWriter(), task);
} else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.getWriter().write("{\"error\": \"Task not found\"}");
}
} catch (NumberFormatException e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("{\"error\": \"Invalid task number\"}");
}
}
}
// POST (receives string and adds a new task with set string as text)
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
BufferedReader reader = request.getReader();
String taskText = reader.lines().collect(Collectors.joining(System.lineSeparator()));
boolean success = TodoAppDAO.addTask(taskText);
if (success) {
response.setStatus(HttpServletResponse.SC_CREATED);
response.getWriter().write("{\"success\": true}");
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write("{\"success\": false}");
}
}
// PUT, edit task's status
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pathInfo = request.getPathInfo();
if (pathInfo != null) {
try {
int taskNumber = Integer.parseInt(pathInfo.substring(1));
// get set task's isDone status
Task currentTask = TodoAppDAO.getTaskByNumber(taskNumber);
if (currentTask == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.getWriter().write("{\"error\": \"Task not found\"}");
return;
}
// new status is set to !isDone
boolean newStatus = !currentTask.isStatus();
boolean success = TodoAppDAO.updateTaskStatus(taskNumber, newStatus);
if (success) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\": true}");
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write("{\"success\": false}");
}
} catch (NumberFormatException e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("{\"error\": \"Invalid task number\"}");
}
} else {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("{\"error\": \"Invalid request\"}");
}
}
//DELETE, deletes a task
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String pathInfo = request.getPathInfo();
if (pathInfo != null) {
try {
int taskNumber = Integer.parseInt(pathInfo.substring(1));
boolean success = TodoAppDAO.deleteTask(taskNumber);
if (success) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\": true}");
} else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write("{\"success\": false}");
}
} catch (NumberFormatException e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("{\"error\": \"Invalid task number\"}");
}
} else {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("{\"error\": \"Invalid request\"}");
}
}
}

View File

@ -0,0 +1,136 @@
package todoapp;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class TodoAppDAO {
private static final String JDBC_URL = "jdbc:mysql://mysql.wmi.amu.edu.pl/s492459_db1";
private static final String USERNAME = "s492459";
private static final String PASSWORD = "TYmmLNTTH2";
@FunctionalInterface
private interface DatabaseOperation<T> {
T execute(Connection conn) throws SQLException;
}
// Function returning amount of current tasks
private static int howManyCurrentTasks() {
String getTasksSql = "SELECT Number FROM todoapp_entries ORDER BY Number";
return executeOperation(conn -> {
try (Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(getTasksSql)) {
int taskAmount = 0;
while (resultSet.next()) {
taskAmount += 1;
}
return taskAmount;
}
});
}
// Connect to MySQL database
private static <T> T executeOperation(DatabaseOperation<T> operation) {
try (Connection conn = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD)) {
return operation.execute(conn);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
// Method to get a task by its number
public static Task getTaskByNumber(int number) {
String getTaskSql = "SELECT Number, Text, isDone FROM todoapp_entries WHERE Number = ?";
return executeOperation(conn -> {
try (PreparedStatement preparedStatement = conn.prepareStatement(getTaskSql)) {
preparedStatement.setInt(1, number);
try (ResultSet resultSet = preparedStatement.executeQuery()) {
if (resultSet.next()) {
String text = resultSet.getString("Text");
boolean isDone = resultSet.getBoolean("isDone");
return new Task(number, text, isDone);
}
}
}
return null;
});
}
// Add task
public static boolean addTask(String text) {
String addTaskSql = "INSERT INTO todoapp_entries (Number, Text, isDone) VALUES (?, ?, 0)";
return executeOperation(conn -> {
try (PreparedStatement preparedStatement = conn.prepareStatement(addTaskSql)) {
int newTaskNumber = howManyCurrentTasks() + 1;
preparedStatement.setInt(1, newTaskNumber);
preparedStatement.setString(2, text);
int rowsAffected = preparedStatement.executeUpdate();
return rowsAffected > 0;
}
});
}
// Change status
public static boolean updateTaskStatus(int number, boolean isDone) {
String updateStatusSql = "UPDATE todoapp_entries SET isDone = ? WHERE Number = ?";
return executeOperation(conn -> {
try (PreparedStatement preparedStatement = conn.prepareStatement(updateStatusSql)) {
preparedStatement.setBoolean(1, isDone);
preparedStatement.setInt(2, number);
int rowsAffected = preparedStatement.executeUpdate();
return rowsAffected > 0;
}
});
}
// Delete task and update numeration accordingly
public static boolean deleteTask(int number) {
String deleteTaskSql = "DELETE FROM todoapp_entries WHERE Number = ?";
boolean isSuccessful = executeOperation(conn -> {
try (PreparedStatement preparedStatement = conn.prepareStatement(deleteTaskSql)) {
preparedStatement.setInt(1, number);
int rowsAffected = preparedStatement.executeUpdate();
return rowsAffected > 0;
}
});
if (isSuccessful) {
int taskAmount = howManyCurrentTasks() + 2;
for (int i = number + 1; i < taskAmount; i++) {
int oldNumber = i;
int newNumber = i - 1;
String updateTaskNumberSql = "UPDATE todoapp_entries SET Number = ? WHERE Number = ?";
executeOperation(conn -> {
try (PreparedStatement preparedStatement = conn.prepareStatement(updateTaskNumberSql)) {
preparedStatement.setInt(1, newNumber);
preparedStatement.setInt(2, oldNumber);
preparedStatement.executeUpdate();
}
return null;
});
}
}
return isSuccessful;
}
// Get tasks
public static List<Task> getAllTasks() {
String getAllTasksSql = "SELECT Number, Text, isDone FROM todoapp_entries";
return executeOperation(conn -> {
List<Task> tasks = new ArrayList<>();
try (Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(getAllTasksSql)) {
while (resultSet.next()) {
int number = resultSet.getInt("Number");
String text = resultSet.getString("Text");
boolean isDone = resultSet.getInt("isDone") == 1;
tasks.add(new Task(number, text, isDone));
}
}
return tasks;
});
}
}

View File

@ -0,0 +1,114 @@
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
margin: 20px;
padding: 20px;
max-width: 800px;
margin: auto;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
}
#addTaskForm {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
#taskText {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px 0 0 5px;
font-size: 16px;
}
#addTaskForm button {
padding: 10px 20px;
border: 1px solid #ccc;
border-left: none;
border-radius: 0 5px 5px 0;
background-color: #4CAF50;
color: white;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
#addTaskForm button:hover {
background-color: #45a049;
}
.task-list {
list-style-type: none;
padding: 0;
}
.task-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
padding: 10px;
background-color: white;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s ease, transform 0.2s ease;
}
.task-item.done {
background-color: #e0f7fa;
transform: scale(0.98);
}
.task-number {
font-weight: bold;
color: #666;
margin-right: 10px;
}
.task-text {
flex: 1;
}
.task-actions {
display: flex;
gap: 5px;
}
.task-actions button {
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 3px;
background-color: #f0f0f0;
color: #333;
font-size: 14px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.task-actions button:hover {
background-color: #e0e0e0;
transform: translateY(-2px);
}
.task-actions button.done {
background-color: #4CAF50;
color: white;
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.task-actions button.delete {
background-color: #f44336;
color: white;
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<h1>Todo App</h1>
<!-- button for adding tasks -->
<form id="addTaskForm">
<input type="text" id="taskText" placeholder="Enter task description" required>
<button type="submit">Add Task</button>
</form>
<!-- task list -->
<ul id="taskList" class="task-list"></ul>
<!-- javascript frontend code -->
<script src="js/script.js"></script>
</body>
</html>

View File

@ -0,0 +1,134 @@
// fetch tasks from backends (GET)
function fetchTasks() {
fetch('/todoapp/api/tasks')
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch tasks');
}
return response.json();
})
.then(tasks => {
const taskList = document.getElementById('taskList');
taskList.innerHTML = ''; // Clear existing list
tasks.forEach(task => {
const taskItem = createTaskElement(task);
taskList.appendChild(taskItem);
});
})
.catch(error => console.error('Error fetching tasks:', error));
}
// function adding new task to the list
function createTaskElement(task) {
const taskItem = document.createElement('li');
taskItem.classList.add('task-item');
if (task.status) {
taskItem.classList.add('done');
}
// adding number
const taskNumber = document.createElement('span');
taskNumber.classList.add('task-number');
taskNumber.textContent = `#${task.number}`;
taskItem.appendChild(taskNumber);
// adding text
const taskText = document.createElement('span');
taskText.classList.add('task-text');
taskText.textContent = task.text;
taskItem.appendChild(taskText);
// adding buttons
const taskActions = document.createElement('div');
taskActions.classList.add('task-actions');
// button for changing status
const doneButton = document.createElement('button');
doneButton.textContent = task.isDone ? 'Undo' : 'Done';
doneButton.addEventListener('click', function() {
updateTaskStatus(task.number, !task.isDone);
});
taskActions.appendChild(doneButton);
// button for deleting
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.addEventListener('click', function() {
deleteTask(task.number);
});
taskActions.appendChild(deleteButton);
taskItem.appendChild(taskActions);
return taskItem;
}
// add new task (POST)
document.getElementById('addTaskForm').addEventListener('submit', function(event) {
event.preventDefault();
const taskText = document.getElementById('taskText').value.trim();
if (taskText === '') {
alert('Task description cannot be empty');
return;
}
fetch('/todoapp/api/tasks', {
method: 'POST',
headers: {
'Content-Type': 'text/plain', //request sends a string
},
body: taskText,
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to add task');
}
return response.json();
})
.then(result => {
if (result.success) {
document.getElementById('taskText').value = ''; //clear input area
fetchTasks(); // Refresh task list after adding new task
} else {
console.error('Failed to add task');
}
})
.catch(error => console.error('Error adding task:', error));
});
// update status (PUT)
function updateTaskStatus(taskNumber, isDone) {
fetch(`/todoapp/api/tasks/${taskNumber}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json', //request sends a json table with task's data
},
body: JSON.stringify({ isDone: isDone })
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to update task status');
}
fetchTasks();
})
.catch(error => console.error('Error updating task status:', error));
}
// delete a task (DELETE)
function deleteTask(taskNumber) {
fetch(`/todoapp/api/tasks/${taskNumber}`, {
method: 'DELETE'
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to delete task');
}
fetchTasks();
})
.catch(error => console.error('Error deleting task:', error));
}
// fetch tasks on startup
fetchTasks();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,114 @@
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
margin: 20px;
padding: 20px;
max-width: 800px;
margin: auto;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
}
#addTaskForm {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
#taskText {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px 0 0 5px;
font-size: 16px;
}
#addTaskForm button {
padding: 10px 20px;
border: 1px solid #ccc;
border-left: none;
border-radius: 0 5px 5px 0;
background-color: #4CAF50;
color: white;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
#addTaskForm button:hover {
background-color: #45a049;
}
.task-list {
list-style-type: none;
padding: 0;
}
.task-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
padding: 10px;
background-color: white;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s ease, transform 0.2s ease;
}
.task-item.done {
background-color: #e0f7fa;
transform: scale(0.98);
}
.task-number {
font-weight: bold;
color: #666;
margin-right: 10px;
}
.task-text {
flex: 1;
}
.task-actions {
display: flex;
gap: 5px;
}
.task-actions button {
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 3px;
background-color: #f0f0f0;
color: #333;
font-size: 14px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.task-actions button:hover {
background-color: #e0e0e0;
transform: translateY(-2px);
}
.task-actions button.done {
background-color: #4CAF50;
color: white;
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.task-actions button.delete {
background-color: #f44336;
color: white;
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo App</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<h1>Todo App</h1>
<!-- button for adding tasks -->
<form id="addTaskForm">
<input type="text" id="taskText" placeholder="Enter task description" required>
<button type="submit">Add Task</button>
</form>
<!-- task list -->
<ul id="taskList" class="task-list"></ul>
<!-- javascript frontend code -->
<script src="js/script.js"></script>
</body>
</html>

View File

@ -0,0 +1,134 @@
// fetch tasks from backends (GET)
function fetchTasks() {
fetch('/todoapp/api/tasks')
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch tasks');
}
return response.json();
})
.then(tasks => {
const taskList = document.getElementById('taskList');
taskList.innerHTML = ''; // Clear existing list
tasks.forEach(task => {
const taskItem = createTaskElement(task);
taskList.appendChild(taskItem);
});
})
.catch(error => console.error('Error fetching tasks:', error));
}
// function adding new task to the list
function createTaskElement(task) {
const taskItem = document.createElement('li');
taskItem.classList.add('task-item');
if (task.status) {
taskItem.classList.add('done');
}
// adding number
const taskNumber = document.createElement('span');
taskNumber.classList.add('task-number');
taskNumber.textContent = `#${task.number}`;
taskItem.appendChild(taskNumber);
// adding text
const taskText = document.createElement('span');
taskText.classList.add('task-text');
taskText.textContent = task.text;
taskItem.appendChild(taskText);
// adding buttons
const taskActions = document.createElement('div');
taskActions.classList.add('task-actions');
// button for changing status
const doneButton = document.createElement('button');
doneButton.textContent = task.isDone ? 'Undo' : 'Done';
doneButton.addEventListener('click', function() {
updateTaskStatus(task.number, !task.isDone);
});
taskActions.appendChild(doneButton);
// button for deleting
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.addEventListener('click', function() {
deleteTask(task.number);
});
taskActions.appendChild(deleteButton);
taskItem.appendChild(taskActions);
return taskItem;
}
// add new task (POST)
document.getElementById('addTaskForm').addEventListener('submit', function(event) {
event.preventDefault();
const taskText = document.getElementById('taskText').value.trim();
if (taskText === '') {
alert('Task description cannot be empty');
return;
}
fetch('/todoapp/api/tasks', {
method: 'POST',
headers: {
'Content-Type': 'text/plain', //request sends a string
},
body: taskText,
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to add task');
}
return response.json();
})
.then(result => {
if (result.success) {
document.getElementById('taskText').value = ''; //clear input area
fetchTasks(); // Refresh task list after adding new task
} else {
console.error('Failed to add task');
}
})
.catch(error => console.error('Error adding task:', error));
});
// update status (PUT)
function updateTaskStatus(taskNumber, isDone) {
fetch(`/todoapp/api/tasks/${taskNumber}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json', //request sends a json table with task's data
},
body: JSON.stringify({ isDone: isDone })
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to update task status');
}
fetchTasks();
})
.catch(error => console.error('Error updating task status:', error));
}
// delete a task (DELETE)
function deleteTask(taskNumber) {
fetch(`/todoapp/api/tasks/${taskNumber}`, {
method: 'DELETE'
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to delete task');
}
fetchTasks();
})
.catch(error => console.error('Error deleting task:', error));
}
// fetch tasks on startup
fetchTasks();

View File

@ -0,0 +1,3 @@
artifactId=demo
groupId=todoapp
version=1

View File

@ -0,0 +1,5 @@
todoapp\JettyServer.class
todoapp\Task.class
todoapp\TodoAppDAO.class
todoapp\TaskServlet.class
todoapp\TodoAppDAO$DatabaseOperation.class

View File

@ -0,0 +1,4 @@
P:\Studia\paradygmaty_programowania\PROJEKT\todoapp\src\main\java\todoapp\TaskServlet.java
P:\Studia\paradygmaty_programowania\PROJEKT\todoapp\src\main\java\todoapp\TodoAppDAO.java
P:\Studia\paradygmaty_programowania\PROJEKT\todoapp\src\main\java\todoapp\JettyServer.java
P:\Studia\paradygmaty_programowania\PROJEKT\todoapp\src\main\java\todoapp\Task.java

Binary file not shown.