250 lines
6.6 KiB
TypeScript
250 lines
6.6 KiB
TypeScript
import './style.scss';
|
|
import {
|
|
EditItemInput,
|
|
Item,
|
|
Mutation,
|
|
NewItemInput,
|
|
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);
|
|
showModal(true);
|
|
(
|
|
document.querySelector('#item-save-action') as HTMLButtonElement
|
|
).addEventListener('click', () => saveItemEdits());
|
|
(
|
|
document.querySelector('#item-close-action') as HTMLButtonElement
|
|
).addEventListener('click', () => showModal(false));
|
|
}
|
|
|
|
async function showItemCreate() {
|
|
(document.querySelector('#modal-title') as HTMLParagraphElement).innerHTML =
|
|
'Item creating';
|
|
(document.querySelector('#modal-card-foot') as HTMLDivElement).innerHTML =
|
|
itemEditActions();
|
|
(document.querySelector('#modal-body') as HTMLDivElement).innerHTML =
|
|
itemEdit({ creating: true });
|
|
(
|
|
document.querySelector('#item-save-action') as HTMLButtonElement
|
|
).addEventListener('click', () => createItem());
|
|
(
|
|
document.querySelector('#item-close-action') as HTMLButtonElement
|
|
).addEventListener('click', () => showModal(false));
|
|
showModal(true);
|
|
}
|
|
|
|
function getItemData(): EditItemInput | NewItemInput {
|
|
function getInput(name: string) {
|
|
return (document.querySelector(`input[name="${name}"]`) as HTMLInputElement)
|
|
.value;
|
|
}
|
|
return {
|
|
id: getInput('id'),
|
|
name: getInput('name'),
|
|
notes:
|
|
(document.querySelector('#notes') as HTMLTextAreaElement).value || null,
|
|
parent: getInput('parent'),
|
|
};
|
|
}
|
|
|
|
async function saveItemEdits() {
|
|
const input = getItemData() as EditItemInput;
|
|
(document.querySelector('#modal-body') as HTMLDivElement).innerHTML =
|
|
loading();
|
|
await request<Mutation>(
|
|
`
|
|
mutation ($input: EditItemInput!) {
|
|
editItem(input: $input) {
|
|
id
|
|
}
|
|
}
|
|
`,
|
|
{
|
|
input,
|
|
},
|
|
);
|
|
showItemDetails(input.id);
|
|
}
|
|
|
|
async function createItem() {
|
|
const input = getItemData() as NewItemInput;
|
|
(document.querySelector('#modal-body') as HTMLDivElement).innerHTML =
|
|
loading();
|
|
const response = await request<Mutation>(
|
|
`
|
|
mutation ($input: NewItemInput!) {
|
|
createItem(itemData: $input) {
|
|
id
|
|
}
|
|
}
|
|
`,
|
|
{
|
|
input,
|
|
},
|
|
);
|
|
showItemDetails(response.createItem?.id);
|
|
}
|
|
|
|
window.addEventListener('load', () => {
|
|
document.body.innerHTML = root();
|
|
['#modal-close', '#modal-background'].forEach((el) =>
|
|
(document.querySelector(el) as HTMLButtonElement).addEventListener(
|
|
'click',
|
|
() => showModal(false),
|
|
),
|
|
);
|
|
(
|
|
document.querySelector('#show-item-creation') as HTMLButtonElement
|
|
).addEventListener('click', () => showItemCreate());
|
|
loadItemList();
|
|
});
|