This commit is contained in:
Mateusz Błażek 2024-06-20 10:55:46 +02:00
commit 5c9144b8cf
10 changed files with 1608 additions and 0 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@
Do uruchomienia może być potrzebny pakiet, npm install express body-parser xlsx

7
count_a.awk Normal file
View File

@ -0,0 +1,7 @@
{
count += gsub(/a/, "a");
}
END {
print count;
}

BIN
data/todos.xlsx Normal file

Binary file not shown.

1340
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "todo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.19.2",
"xlsx": "^0.18.5"
}
}

21
public/index.html Normal file
View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo List</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Todo List</h1>
<form id="todo-form">
<input type="text" id="todo-input" placeholder="Add a new task" required>
<button type="submit">Add</button>
</form>
<ul id="todo-list"></ul>
<button id="count-a-button">Count 'a' letters</button>
<div id="count-a-result"></div>
<script src="script.js"></script>
</body>
</html>

78
public/script.js Normal file
View File

@ -0,0 +1,78 @@
document.addEventListener('DOMContentLoaded', () => {
const todoForm = document.getElementById('todo-form');
const todoInput = document.getElementById('todo-input');
const todoList = document.getElementById('todo-list');
const countAButton = document.getElementById('count-a-button');
const countAResult = document.getElementById('count-a-result');
todoForm.addEventListener('submit', (e) => {
e.preventDefault();
const task = todoInput.value;
if (task) {
addTodoToServer(task);
todoInput.value = '';
}
});
countAButton.addEventListener('click', () => {
countA();
});
function addTodoToServer(task) {
fetch('/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ task })
})
.then(response => response.json())
.then(data => {
appendTodoToList(data.data.id, data.data.task);
});
}
function appendTodoToList(id, task) {
const todoItem = document.createElement('li');
todoItem.textContent = task;
todoItem.dataset.id = id;
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.addEventListener('click', () => {
deleteTodoFromServer(id, todoItem);
});
todoItem.appendChild(deleteButton);
todoList.appendChild(todoItem);
}
function deleteTodoFromServer(id, todoItem) {
fetch(`/todos/${id}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(() => {
todoList.removeChild(todoItem);
});
}
function fetchTodos() {
fetch('/todos')
.then(response => response.json())
.then(data => {
data.data.forEach(todo => {
appendTodoToList(todo.id, todo.task);
});
});
}
function countA() {
fetch('/count-a')
.then(response => response.json())
.then(data => {
countAResult.textContent = `Number of 'a' letters: ${data.count}`;
});
}
fetchTodos();
});

76
public/styles.css Normal file
View File

@ -0,0 +1,76 @@
body {
font-family: Arial, sans-serif;
background-color: #f0f4f7;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
margin-top: 20px;
}
form {
margin-bottom: 20px;
}
input[type="text"] {
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
width: 300px;
}
button {
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 4px;
background-color: #28a745;
color: white;
cursor: pointer;
margin-left: 10px;
}
button:hover {
background-color: #218838;
}
#todo-list {
list-style-type: none;
padding: 0;
}
#todo-list li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: white;
margin-bottom: 10px;
width: 400px;
}
#todo-list li button {
background-color: #dc3545;
margin-left: 10px;
}
#todo-list li button:hover {
background-color: #c82333;
}
#count-a-button {
margin-top: 20px;
}
#count-a-result {
margin-top: 10px;
font-weight: bold;
}

63
server.js Normal file
View File

@ -0,0 +1,63 @@
const express = require('express');
const bodyParser = require('body-parser');
const xlsx = require('xlsx');
const fs = require('fs');
const { exec } = require('child_process');
const app = express();
const port = 3000;
const filePath = './data/todos.xlsx';
app.use(bodyParser.json());
app.use(express.static('public'));
function readTodos() {
if (fs.existsSync(filePath)) {
const workbook = xlsx.readFile(filePath);
const sheet = workbook.Sheets[workbook.SheetNames[0]];
return xlsx.utils.sheet_to_json(sheet);
} else {
return [];
}
}
function writeTodos(todos) {
const worksheet = xlsx.utils.json_to_sheet(todos);
const workbook = xlsx.utils.book_new();
xlsx.utils.book_append_sheet(workbook, worksheet, 'Todos');
xlsx.writeFile(workbook, filePath);
}
app.get('/todos', (req, res) => {
const todos = readTodos();
res.json({ data: todos });
});
app.post('/todos', (req, res) => {
const todos = readTodos();
const task = req.body.task;
const newTodo = { id: todos.length ? todos[todos.length - 1].id + 1 : 1, task: task };
todos.push(newTodo);
writeTodos(todos);
res.json({ data: newTodo });
});
app.delete('/todos/:id', (req, res) => {
const todos = readTodos();
const updatedTodos = todos.filter(todo => todo.id != req.params.id);
writeTodos(updatedTodos);
res.json({ message: "success" });
});
app.get('/count-a', (req, res) => {
const todos = readTodos();
const allTasks = todos.map(todo => todo.task).join('\n');
fs.writeFileSync('tasks.txt', allTasks);
exec('awk -f count_a.awk tasks.txt', (error, stdout) => {
res.json({ count: parseInt(stdout.trim(), 10) });
});
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});

5
tasks.txt Normal file
View File

@ -0,0 +1,5 @@
Nowe
1
2
aaa
aa