render data with Handlebars template, support tags
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Sergo 2023-12-04 00:07:01 +02:00
parent b772c83c53
commit 1ca64bc925
4 changed files with 69 additions and 101 deletions

View File

@ -5,6 +5,7 @@
"origins": [
"http://localhost:3030"
],
"sessionSecret": "test",
"paginate": {
"default": 10,
"max": 50

View File

@ -2,8 +2,7 @@
<html lang="en">
<head>
<title>walias</title>
<script src="index.js" async></script>
<title>WildDuck Aliases</title>
<meta name="description" content="Aliases for Wild Duck">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
@ -11,6 +10,8 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
<script src="index.js" async></script>
<style>
* {
margin: 0;
@ -54,7 +55,7 @@
<div class="wrapper">
<div class="header">
<img class="logo" src="logo.jpg">
<h1>Wildduck aliases</h1>
<h1>WildDuck Aliases</h1>
</div>
<div id="container"></div>
</div>

View File

@ -1,107 +1,73 @@
function renderAliases(aliases) {
const dataContainer = document.getElementById('container')
// Create table element ant populate with items
const table = document.createElement('table')
table.classList.add('table')
const thead = document.createElement('thead')
const tr = document.createElement('tr')
function renderAliases(data) {
const dataContainer = document.getElementById('container');
const template = Handlebars.compile(okHandlebarsTemplate);
const html = template({
data: data.sort((a, b) => new Date(a.created) - new Date(b.created))
});
for (const value of ['Address', 'Created']) {
const th = document.createElement('th')
th.setAttribute('scope', 'col')
th.innerText = value
tr.appendChild(th)
dataContainer.innerHTML = html;
}
thead.appendChild(tr)
table.appendChild(thead)
function createAlias() {
const tags = document.getElementById('tags').value?.split(',') || [];
const tbody = document.createElement('tbody')
// sort by field created
aliases
.sort((a, b) => new Date(a.created) - new Date(b.created))
.forEach(alias => {
const tr = document.createElement('tr')
const tdAddress = document.createElement('td')
// On click copy to clipboard
tdAddress.addEventListener('click', () => {
navigator.clipboard.writeText(alias.address)
fetch('/aliases', {
method: 'POST',
body: JSON.stringify({ tags }),
headers: {
'Content-Type': 'application/json'
}
})
tdAddress.innerText = alias.address
const tdCreated = document.createElement('td')
tdCreated.innerText = alias.created
const tdActions = document.createElement('td')
const deleteButton = createDeleteButton(alias)
tdActions.appendChild(deleteButton)
tr.appendChild(tdAddress)
tr.appendChild(tdCreated)
tr.appendChild(tdActions)
tbody.appendChild(tr)
.then(res => res.json())
.then(data => {
renderAliases(data)
})
table.appendChild(tbody)
dataContainer.innerHTML = ''
dataContainer.appendChild(table)
renderCreateButton();
}
function createDeleteButton(alias) {
const deleteButton = document.createElement('a')
deleteButton.classList.add('btn', 'btn-danger')
if (!alias.id) {
deleteButton.classList.add('disabled')
} else {
deleteButton.addEventListener('click', async () => {
const res = await fetch(`/aliases/${alias.id}`, {
function deleteAlias(id) {
fetch(`/aliases/${id}`, {
method: 'DELETE'
})
if (res.ok) {
const data = await res.json()
.then(res => res.json())
.then(data => {
renderAliases(data)
return
}
})
}
deleteButton.innerText = 'Delete'
return deleteButton
function copyToClipboard(text) {
navigator.clipboard.writeText(text)
}
function renderCreateButton() {
const dataContainer = document.getElementById('container')
const button = document.createElement('a')
button.classList.add('btn')
button.classList.add('btn-primary')
button.addEventListener('click', async () => {
button.innerText = 'Creating, please wait...'
const res = await fetch('/aliases', {
method: 'POST'
})
if (res.ok) {
const data = await res.json()
renderAliases(data)
return
}
})
button.innerText = 'Create new alias'
dataContainer.appendChild(button)
}
const okHandlebarsTemplate = `
<table class="table">
<thead>
<tr>
<th scope="col">Address</th>
<th scope="col">Tags</th>
<th scope="col">Created</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{{#each data}}
<tr>
<td onclick="copyToClipboard('{{address}}')" style="cursor: pointer;">{{address}}</td>
<td> {{#each tags}}{{this}} {{/each}}</td>
<td>{{created}}</td>
<td>
<div onclick="deleteAlias('{{id}}')" class="btn btn-danger {{#unless id}}disabled{{/unless}}">Delete</div>
</td>
</tr>
{{/each}}
</tbody>
</table>
<input type="text" id="tags" placeholder="Add tags (optional)">
<div class="btn btn-primary" onclick="createAlias()">Create alias</div class="btn btn-primary">
`
fetch('/aliases').then(async res => {
const data = await res.json()
const dataContainer = document.getElementById('container')
const dataContainer = document.getElementById('container');
const data = await res.json();
if (!res.ok) {
dataContainer.innerHTML = `

View File

@ -32,8 +32,7 @@ interface CreateWildDuckAddressResponse {
success: boolean;
id: string;
}
type AliasesData = any;
type AliasesData = { tags?: string[] }
type AliasesPatch = any;
type AliasesQuery = any;
@ -74,6 +73,7 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
const createResult = await wildDuckClient.post<CreateWildDuckAddressResponse>(`/users/${userId}/addresses`, {
address: `${alias}@${emailDomain}`,
tags: data.tags,
});
if (!createResult.data.success) {