todoapp
This commit is contained in:
commit
c7d5764c43
|
@ -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.
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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\"}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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>
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||
}
|
|
@ -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>
|
|
@ -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();
|
|
@ -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
|
Binary file not shown.
Loading…
Reference in New Issue