metropolis/frontend/src/main.ts

201 lines
5.4 KiB
TypeScript

import './style.scss';
import { EditItemInput, Item, Mutation, Query } from './generated/graphql';
import root from './components/root.hbs';
import loading from './components/loading.hbs';
import itemList from './components/itemList.hbs';
import itemDetails from './components/itemDetails.hbs';
import itemDetailsActions from './components/itemDetailsActions.hbs';
import itemEdit from './components/itemEdit.hbs';
import itemEditActions from './components/itemEditActions.hbs';
async function request<T = Query>(
query: string,
variables?: { [key: string]: any },
): Promise<T> {
return fetch('/api/graphql', {
method: 'POST',
body: JSON.stringify({ query, variables }),
headers: new Headers({
'Content-Type': 'application/json',
}),
})
.then((res) => res.json())
.then((res) => res.data);
}
const itemListCursors: (string | null)[] = [null];
async function loadItemList(cursor?: string | null) {
document.querySelector('#main')!.innerHTML = loading();
const loaded = await request(
`
query ($cursor: ID) {
itemList(cursor: $cursor) {
nodes {
id
ean13
name
}
cursor
hasNextPage
}
}
`,
{
cursor,
},
);
if (
loaded.itemList.cursor &&
!itemListCursors.includes(loaded.itemList.cursor)
) {
itemListCursors.push(loaded.itemList.cursor);
}
const hasPreviousPage = itemListCursors.length > 2;
document.querySelector('#main')!.innerHTML = itemList({
...loaded.itemList,
previousPage: hasPreviousPage,
nextPage: loaded.itemList.hasNextPage,
});
const itemsNext = document.querySelector('#items-next') as HTMLAnchorElement;
if (loaded.itemList.hasNextPage) {
itemsNext.addEventListener('click', () =>
loadItemList(loaded.itemList.cursor),
);
}
const itemsPrevious = document.querySelector(
'#items-previous',
) as HTMLAnchorElement;
if (hasPreviousPage) {
itemsPrevious.addEventListener('click', () => {
itemListCursors.pop();
loadItemList(itemListCursors[itemListCursors.length - 2]);
});
}
(
Array.from(
document.querySelectorAll('.item-open-details'),
) as HTMLButtonElement[]
).forEach((butt) =>
butt.addEventListener('click', () => showItemDetails(butt.dataset.id!)),
);
}
async function showModal(state: boolean = true) {
const cl = (document.querySelector('#modal') as HTMLDivElement).classList;
if (state) {
cl.add('is-active');
} else {
cl.remove('is-active');
}
}
async function showItemDetails(id: string) {
(document.querySelector('#modal-title') as HTMLParagraphElement).innerHTML =
'Item details';
(document.querySelector('#modal-body') as HTMLDivElement).innerHTML =
loading();
(document.querySelector('#modal-card-foot') as HTMLDivElement).innerHTML =
itemDetailsActions();
showModal(true);
const loaded = await request(
`
query ($id: ID!) {
item(id: $id) {
id
ean13
name
notes
ancestors {
ean13
name
}
descendants {
ean13
name
parent {
ean13
}
}
}
}
`,
{
id,
},
);
(document.querySelector('#modal-body') as HTMLDivElement).innerHTML =
itemDetails(loaded.item!);
(
Array.from(
document.querySelectorAll('#modal .item-open-details'),
) as HTMLButtonElement[]
).forEach((butt) =>
butt.addEventListener('click', () => showItemDetails(butt.dataset.id!)),
);
(
document.querySelector('#item-edit-action') as HTMLButtonElement
).addEventListener('click', () => showItemEdit(loaded.item!));
(
document.querySelector('#item-close-action') as HTMLButtonElement
).addEventListener('click', () => showModal(false));
}
async function showItemEdit(item: Item) {
(document.querySelector('#modal-title') as HTMLParagraphElement).innerHTML =
'Item editing';
(document.querySelector('#modal-card-foot') as HTMLDivElement).innerHTML =
itemEditActions();
if (!item.parent && item.ancestors) {
item.parent = item.ancestors[item.ancestors.length - 1];
}
(document.querySelector('#modal-body') as HTMLDivElement).innerHTML =
itemEdit(item);
(
document.querySelector('#item-save-action') as HTMLButtonElement
).addEventListener('click', () => saveItemEdits());
(
document.querySelector('#item-close-action') as HTMLButtonElement
).addEventListener('click', () => showModal(false));
}
async function saveItemEdits() {
function getInput(name: string) {
return (document.querySelector(`input[name="${name}"]`) as HTMLInputElement)
.value;
}
const input: EditItemInput = {
id: getInput('id'),
name: getInput('name'),
notes:
(document.querySelector('#notes') as HTMLTextAreaElement).value || null,
parent: getInput('parent'),
};
(document.querySelector('#modal-body') as HTMLDivElement).innerHTML =
loading();
await request<Mutation>(
`
mutation ($input: EditItemInput!) {
editItem(input: $input) {
id
}
}
`,
{
input,
},
);
showItemDetails(input.id);
}
window.addEventListener('load', () => {
document.body.innerHTML = root();
['#modal-close', '#modal-background'].forEach((el) =>
(document.querySelector(el) as HTMLButtonElement).addEventListener(
'click',
() => showModal(false),
),
);
loadItemList();
});