todoapp
This commit is contained in:
commit
c7d5764c43
13
todoapp/.vscode/settings.json
vendored
Normal file
13
todoapp/.vscode/settings.json
vendored
Normal 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"
|
||||
}
|
||||
|
BIN
todoapp/lib/commons-io-2.16.1.jar
Normal file
BIN
todoapp/lib/commons-io-2.16.1.jar
Normal file
Binary file not shown.
BIN
todoapp/lib/javax.servlet-api-4.0.1.jar
Normal file
BIN
todoapp/lib/javax.servlet-api-4.0.1.jar
Normal file
Binary file not shown.
131
todoapp/pom.xml
Normal file
131
todoapp/pom.xml
Normal 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>
|
44
todoapp/src/main/java/todoapp/JettyServer.java
Normal file
44
todoapp/src/main/java/todoapp/JettyServer.java
Normal 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();
|
||||
}
|
||||
}
|
37
todoapp/src/main/java/todoapp/Task.java
Normal file
37
todoapp/src/main/java/todoapp/Task.java
Normal 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;
|
||||
}
|
||||
}
|
126
todoapp/src/main/java/todoapp/TaskServlet.java
Normal file
126
todoapp/src/main/java/todoapp/TaskServlet.java
Normal 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\"}");
|
||||
}
|
||||
}
|
||||
}
|
136
todoapp/src/main/java/todoapp/TodoAppDAO.java
Normal file
136
todoapp/src/main/java/todoapp/TodoAppDAO.java
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
114
todoapp/src/main/webapp/css/styles.css
Normal file
114
todoapp/src/main/webapp/css/styles.css
Normal 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);
|
||||
}
|
27
todoapp/src/main/webapp/index.html
Normal file
27
todoapp/src/main/webapp/index.html
Normal 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>
|
134
todoapp/src/main/webapp/js/script.js
Normal file
134
todoapp/src/main/webapp/js/script.js
Normal 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();
|
BIN
todoapp/target/classes/todoapp/JettyServer.class
Normal file
BIN
todoapp/target/classes/todoapp/JettyServer.class
Normal file
Binary file not shown.
BIN
todoapp/target/classes/todoapp/Task.class
Normal file
BIN
todoapp/target/classes/todoapp/Task.class
Normal file
Binary file not shown.
BIN
todoapp/target/classes/todoapp/TaskServlet.class
Normal file
BIN
todoapp/target/classes/todoapp/TaskServlet.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
todoapp/target/classes/todoapp/TodoAppDAO.class
Normal file
BIN
todoapp/target/classes/todoapp/TodoAppDAO.class
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/JettyServer.class
Normal file
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/JettyServer.class
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/Task.class
Normal file
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/Task.class
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/TaskServlet.class
Normal file
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/TaskServlet.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/TodoAppDAO.class
Normal file
BIN
todoapp/target/demo-1/WEB-INF/classes/todoapp/TodoAppDAO.class
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jackson-annotations-2.13.0.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jackson-annotations-2.13.0.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jackson-core-2.13.0.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jackson-core-2.13.0.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jackson-databind-2.13.0.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jackson-databind-2.13.0.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-http-10.0.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-http-10.0.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-io-10.0.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-io-10.0.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-security-10.0.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-security-10.0.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-server-10.0.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-server-10.0.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-servlet-10.0.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-servlet-10.0.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-servlet-api-4.0.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-servlet-api-4.0.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-util-10.0.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/jetty-util-10.0.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/logback-classic-1.2.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/logback-classic-1.2.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/logback-core-1.2.6.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/logback-core-1.2.6.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/mysql-connector-j-8.0.33.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/mysql-connector-j-8.0.33.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/protobuf-java-3.21.9.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/protobuf-java-3.21.9.jar
Normal file
Binary file not shown.
BIN
todoapp/target/demo-1/WEB-INF/lib/slf4j-api-1.7.32.jar
Normal file
BIN
todoapp/target/demo-1/WEB-INF/lib/slf4j-api-1.7.32.jar
Normal file
Binary file not shown.
114
todoapp/target/demo-1/css/styles.css
Normal file
114
todoapp/target/demo-1/css/styles.css
Normal 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);
|
||||
}
|
27
todoapp/target/demo-1/index.html
Normal file
27
todoapp/target/demo-1/index.html
Normal 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>
|
134
todoapp/target/demo-1/js/script.js
Normal file
134
todoapp/target/demo-1/js/script.js
Normal 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();
|
3
todoapp/target/maven-archiver/pom.properties
Normal file
3
todoapp/target/maven-archiver/pom.properties
Normal file
@ -0,0 +1,3 @@
|
||||
artifactId=demo
|
||||
groupId=todoapp
|
||||
version=1
|
@ -0,0 +1,5 @@
|
||||
todoapp\JettyServer.class
|
||||
todoapp\Task.class
|
||||
todoapp\TodoAppDAO.class
|
||||
todoapp\TaskServlet.class
|
||||
todoapp\TodoAppDAO$DatabaseOperation.class
|
@ -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
|
BIN
todoapp/target/todoapp_MaciejŻyczyński.war
Normal file
BIN
todoapp/target/todoapp_MaciejŻyczyński.war
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user