Created
February 12, 2025 15:21
-
-
Save syafiqfaiz/0afa1bafacdc1217ac468521510837d8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Todo App with Authentication</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
background-color: #f4f4f4; | |
text-align: center; | |
margin: 0; | |
padding: 20px; | |
} | |
.container { | |
max-width: 400px; | |
margin: 0 auto; | |
background: white; | |
padding: 20px; | |
border-radius: 10px; | |
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | |
} | |
h2 { | |
color: #333; | |
} | |
.todo-item { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
padding: 10px; | |
border-bottom: 1px solid #ddd; | |
} | |
.todo-item:last-child { | |
border-bottom: none; | |
} | |
.completed { | |
text-decoration: line-through; | |
color: gray; | |
} | |
.hidden { | |
display: none; | |
} | |
button { | |
padding: 8px 12px; | |
border: none; | |
border-radius: 5px; | |
cursor: pointer; | |
} | |
.btn-primary { | |
background-color: #28a745; | |
color: white; | |
} | |
.btn-danger { | |
background-color: #dc3545; | |
color: white; | |
} | |
.btn-secondary { | |
background-color: #007bff; | |
color: white; | |
} | |
input[type="text"] { | |
width: calc(100% - 20px); | |
padding: 8px; | |
margin-bottom: 10px; | |
border: 1px solid #ccc; | |
border-radius: 5px; | |
} | |
input[type="password"] { | |
width: calc(100% - 20px); | |
padding: 8px; | |
margin-bottom: 10px; | |
border: 1px solid #ccc; | |
border-radius: 5px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h2>Todo List</h2> | |
<div class="hidden" id="bekasContainer"> | |
<input type="text" id="username" placeholder="Username" /> | |
<input type="password" id="password" placeholder="Password" /> | |
<br /> | |
<button class="btn-primary" onclick="bilaKlikSubmit()">Submit</button> | |
<button class="btn-secondary" onclick="bilaKlikCancel()">Cancel</button> | |
</div> | |
<div id="auth-section"> | |
<button class="btn-secondary" id="loginButton" onclick="bilaKlikLogin()">Login</button> | |
<button class="btn-danger" onclick="logout()">Logout</button> | |
<p id="auth-status">Not logged in</p> | |
</div> | |
<div id="todo-form" class="hidden"> | |
<input type="text" id="todo-input" placeholder="New todo" /> | |
<button class="btn-primary" onclick="addTodo()">Add</button> | |
</div> | |
<ul id="todo-list"></ul> | |
</div> | |
<script> | |
const bilaKlikSubmit = () => { | |
// on klik, kita login | |
// dapatkan value username | |
const username = document.getElementById('username').value | |
// dapatkan value password | |
const password = document.getElementById('password').value | |
console.log('username', username) | |
console.log('password', password) | |
console.log('stringified body', JSON.stringify({ | |
"username": username, | |
"password": password | |
})) | |
// buat API call utk login | |
fetch('https://api.kelasprogramming.com/consumer/login',{ | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
"username": username, | |
"password": password | |
}) | |
}) | |
.then((response) => response.json()) | |
.then((body) => { | |
// kalau success, kita save token | |
if (body.success == false){ | |
// kalau fail, kita alert apa error dia | |
alert('ada error ' + body.message) | |
} else { | |
localStorage.setItem('jwToken', body.token); | |
} | |
}) | |
.catch((err)=> { | |
// kalau fail, kita alert apa error dia | |
alert('ada error ' + err ) | |
}) | |
} | |
const bilaKlikLogin = () => { | |
// dapatkan login container | |
const loginContainer = document.getElementById("bekasContainer") | |
// remove class hidden supaya dia visible | |
loginContainer.classList.remove("hidden"); | |
// dapatkan container yg kita nak hide | |
const authContainer = document.getElementById("auth-section") | |
// tambah kan class hidden supaya dia invisible | |
authContainer.classList.add("hidden") | |
} | |
const bilaKlikCancel = () => { | |
// dapatkan login container | |
const loginContainer = document.getElementById("bekasContainer") | |
// remove class hidden supaya dia visible | |
loginContainer.classList.add("hidden"); | |
// dapatkan container yg kita nak hide | |
const authContainer = document.getElementById("auth-section") | |
// tambah kan class hidden supaya dia invisible | |
authContainer.classList.remove("hidden") | |
} | |
const onLogin = async () => { | |
const username = document.getElementById('username').value | |
const password = document.getElementById('password').value | |
console.log(username, password) | |
} | |
const jwToken = window.localStorage.getItem('jwToken') | |
const headersOption = { | |
Authorization: `Bearer ${jwToken}` | |
} | |
async function getTodos(){ | |
let todos = [] | |
await fetch('https://api.kelasprogramming.com/todo', { | |
headers: headersOption | |
}) | |
.then((res) => res.json()) | |
.then((body) => { | |
todos = body.entry | |
}) | |
return todos | |
} | |
async function getTodo(id){ | |
let todo | |
await fetch(`https://api.kelasprogramming.com/todo/${id}`, { | |
headers: headersOption | |
}) | |
.then((res) => res.json()) | |
.then((body) => { | |
todo = body[0] | |
}) | |
return todo | |
} | |
// dia buat api call utk create todo | |
async function createTodo(details) { | |
await fetch('https://api.kelasprogramming.com/todo',{ | |
method: 'POST', | |
headers: headersOption, | |
body: JSON.stringify({"details": details }) | |
}) | |
} | |
async function updateTodo(id, body) { | |
await fetch(`https://api.kelasprogramming.com/todo/${id}`, { | |
method: 'PUT', | |
headers: headersOption, | |
body: JSON.stringify(body) | |
}) | |
} | |
let isAuthenticated = true; | |
document.addEventListener("DOMContentLoaded", loadTodos); | |
function logout() { | |
// on logout, kita delete token | |
localStorage.removeItem('jwToken') | |
} | |
async function loadTodos() { | |
// get all todos from api | |
const todos = await getTodos() | |
const list = document.getElementById("todo-list"); | |
list.innerHTML = ""; | |
todos.forEach((todo, index) => { | |
const li = document.createElement("li"); | |
li.className = `todo-item ${todo.completed === 1 ? "completed" : ""}`; | |
li.innerHTML = ` | |
<span onclick="toggleComplete(${index})">${todo.details}</span> | |
${ | |
jwToken | |
? `<button class="btn-secondary" onclick="editTodo(${todo.id})">Edit</button> | |
<button class="btn-danger" onclick="deleteTodo(${index})">Delete</button>` | |
: "" | |
} | |
`; | |
list.appendChild(li); | |
}); | |
} | |
async function addTodo() { | |
// check, dah login ke belum? | |
// kalau belum dia keluar alert | |
if (!isAuthenticated) { | |
return alert("You must be logged in to add a todo."); | |
} | |
// dapatkan value dari text input#todo-input | |
const input = document.getElementById("todo-input"); | |
// kalau kosong, jangan buat apa2 | |
if (!input.value.trim()) return; | |
// add new todo item to api | |
await createTodo(input.value) | |
// kita reset text input pada kosong | |
input.value = ""; | |
// kita refetch todo list, so that item yg kita baru tambah akan keluar dalam list | |
loadTodos(); | |
} | |
async function editTodo(id) { | |
if (!isAuthenticated) return; | |
const selectedTodo = await getTodo(id) | |
const newValue = prompt("Edit todo:", selectedTodo.details); | |
await updateTodo(id, { | |
"details": newValue, | |
"completed": selectedTodo.completed | |
}) | |
loadTodos(); | |
} | |
// on page fully loaded, kita check jwt token | |
// kalau token wujud, kita hide login button | |
window.onload = (event)=> { | |
const jwToken = localStorage.getItem('jwToken') | |
if (jwToken) { | |
// if token wujud, saya hide button login, | |
const loginButton = document.getElementById('loginButton') | |
loginButton.setAttribute('style', 'display: none;') | |
// dan display textfield utk create new todo | |
const todoInput = document.getElementById('todo-form') | |
todoInput.classList.remove('hidden') | |
} | |
} | |
function deleteTodo(index) { | |
if (!isAuthenticated) return; | |
const todos = JSON.parse(localStorage.getItem("todos")); | |
todos.splice(index, 1); | |
localStorage.setItem("todos", JSON.stringify(todos)); | |
loadTodos(); | |
} | |
function toggleComplete(index) { | |
const todos = JSON.parse(localStorage.getItem("todos")); | |
todos[index].completed = !todos[index].completed; | |
localStorage.setItem("todos", JSON.stringify(todos)); | |
loadTodos(); | |
} | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pseudocode | |
=========== | |
1) set target | |
2) pecahkan target kepada step paling kecil | |
3) then baru convert kepada code, | |
pseudocode utk togle login form | |
================================= | |
saya nak buat function utk toggle visibility login form | |
apa trigger utk display login form | |
1.1) saya nak bila on click login button, kita display login form | |
1.1.1) kita nak hide pulak button login yg kita baru klik | |
1.2) dan bila cancel kita hide semula login form | |
1.2.1) dan sorok kembali login container | |
onClick login button, display login form | |
onClick cancel button, hide login form | |
pseudocode utk login | |
====================== | |
kita nak user boleh isi username and password | |
dan boleh submit utk login | |
1) user boleh isi username | |
2) user boleh isi password | |
3) user boleh klik button submit utk login | |
on click submit, kita login | |
4) lepas success login, kita simpan jwt token | |
4.1) kalau fail, kita keluarkan error message | |
pseudocode, user yang dah berjaya login | |
===================================== | |
kalau user dah login, dia boleh CRUD todo list dia | |
pseudocode utk logout | |
====================== | |
on logout, kita delete token | |
homework | |
========= | |
1) siapkan registration | |
2) siapakn function remove | |
3) siapkan function utk toggle completion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment