Creating the First File
Before us is the development environment IDE
, where all the files of our account are located. The list of files and folders is on the left side; there we can also add a new file. To do this, we use the bottom-left button called "Add File."
The first thing we will do is create a directory for our plugin. We need a directory because in the following steps we will turn it into a plugin for connecting to external accounts.
- Click "Add File."
- A file type selection will appear; choose Directory.
Enter the name plugin (you can choose any name you like, but further instructions will use this name).
The directory should now appear on the left side. After that, navigate into it and add the first file to this directory.
- Click "Add File."
- A file type selection will appear; choose Server TypeScript.
In the window that appears, enter the file name index and click the Create button:
Also, add the files script.js and style.css, as they will be useful later.
So, the files have been created.
We will be creating our web service in HTML style for now, so let's delete all the content of the file and insert our code.
index.tsx
import { jsx } from "@app/html-jsx";
import { Heap } from "@app/heap";
const Notes = Heap.Table("note", {
title: Heap.String(),
completed: Heap.Boolean(),
});
app.html("/", async (ctx) => {
let notes = await Notes.findAll(ctx, { order: { title: "asc" } });
return (
<html>
<head>
<script src="./script.js"></script>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<h1>Task List</h1>
<form id="addTaskForm" title="Create Task">
<div>
<input
id="taskTitle"
name="title"
placeholder="Describe the task"
required
/>
</div>
<div class="margin">
<button onclick={`createTask('${createRoute.url()}')`}>
Create Task
</button>
</div>
</form>
<div id="addTaskList">
{notes.map((note) => {
return (
<div
id="list"
class="list"
>
<div class='task-container'>
<input
class="task-checkbox"
type="checkbox"
checked={note.completed}
onchange={`checkTask('${toggleRoute({
id: note.id,
}).url()}')`}
/>
<span class="task-text">{note.title}</span>
</div>
<button
onclick={`deleteTask('${deleteRoute({ id: note.id }).url()}')`}
type="button"
class="delete"
>
Delete
</button>
</div>
);
})}
</div>
</body>
</html>
);
});
const createRoute = app.post("/create", async (ctx, req) => {
ctx.account.log("click", { json: req });
await Notes.create(ctx, {
title: req.body.title,
completed: false,
});
return;
});
const toggleRoute = app.post("/toggle/:id", async (ctx, req) => {
ctx.account.log("done");
let note = await Notes.getById(ctx, req.params.id);
await Notes.update(ctx, { id: note.id, completed: !note.completed });
return;
});
const deleteRoute = app.post("/delete/:id/submit", async (ctx, req) => {
await Notes.delete(ctx, req.params.id);
return;
});
script.js
const createTask = async(url)=> {
// event.preventDefault(); // prevent page reload
const title = document.getElementById('taskTitle').value
const response = await fetch(url, {
method: 'POST',
body: new URLSearchParams({
title: title,
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
location.reload();
};
const checkTask = async( url)=> {
const response = await fetch(url, {
method: 'POST',
body: {},
});
location.reload();
};
const deleteTask = async( url)=> {
const response = await fetch(url, {
method: 'POST',
body: {},
});
location.reload();
};
style.css
.task-container {
display: flex;
align-items: center;
margin-bottom: 10px;
}
/* Style for custom checkbox */
.task-checkbox {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #000;
border-radius: 4px;
margin-right: 10px;
position: relative;
cursor: pointer;
}
/* Style for checkbox state with checkmark */
.task-checkbox:checked {
background-color: #4CAF50;
border-color: #4CAF50;
}
.task-checkbox:checked::after {
content: '✔';
position: absolute;
top: 0;
left: 2px;
color: white;
font-size: 16px;
}
/* Style for task text */
.task-text {
font-size: 16px;
}
.list{
margin-top: 2vh;
width: 50vw;
display: flex;
justify-content: space-between;
align-items: center;
}
.margin{
margin: 15px 0px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s ease;
}
button[type="submit"]:hover {
background-color: #45a049;
}
/* Styles for "Delete" buttons */
button.delete {
background-color: #f44336;
color: white;
border: none;
padding: 5px 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s ease;
}
button.delete:hover {
background-color: #da190b;
}
/* Styles for input fields */
input {
width: 300px;
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
outline: none;
transition: border-color 0.3s ease;
}
/* Styles for input field on focus */
input:focus {
border-color: #4CAF50;
}
/* Styles for placeholder */
input::placeholder {
color: #888;
font-style: italic;
}
Save the file using the Save button, and you will see that the preview on the right has updated; it should look like this: