216 lines
5.6 KiB
TypeScript
216 lines
5.6 KiB
TypeScript
|
import { idb, model, Field } from "async-idb-orm"
|
||
|
import { List, ListItem, Board, Tag, ItemTag } from "./types"
|
||
|
|
||
|
export {
|
||
|
// boards
|
||
|
loadBoards,
|
||
|
updateBoard,
|
||
|
addBoard,
|
||
|
deleteBoard,
|
||
|
archiveBoard,
|
||
|
// lists
|
||
|
loadLists,
|
||
|
updateList,
|
||
|
addList,
|
||
|
deleteList,
|
||
|
archiveList,
|
||
|
// items
|
||
|
loadItems,
|
||
|
updateItem,
|
||
|
addItem,
|
||
|
deleteItem,
|
||
|
archiveItem,
|
||
|
// tags
|
||
|
loadTags,
|
||
|
updateTag,
|
||
|
addTag,
|
||
|
deleteTag,
|
||
|
addItemTag,
|
||
|
deleteItemTag,
|
||
|
// import/export
|
||
|
JsonUtils,
|
||
|
}
|
||
|
|
||
|
const boards = model({
|
||
|
id: Field.number({ primaryKey: true }),
|
||
|
uuid: Field.string({ default: () => crypto.randomUUID() }),
|
||
|
title: Field.string({ default: () => "" }),
|
||
|
created: Field.date({ default: () => new Date() }),
|
||
|
archived: Field.boolean({ default: () => false }),
|
||
|
order: Field.number({ default: () => 0 }),
|
||
|
})
|
||
|
|
||
|
const lists = model({
|
||
|
id: Field.number({ primaryKey: true }),
|
||
|
boardId: Field.number(),
|
||
|
title: Field.string({ default: () => "" }),
|
||
|
created: Field.date({ default: () => new Date() }),
|
||
|
archived: Field.boolean({ default: () => false }),
|
||
|
order: Field.number({ default: () => 0 }),
|
||
|
})
|
||
|
|
||
|
const items = model({
|
||
|
id: Field.number({ primaryKey: true }),
|
||
|
listId: Field.number(),
|
||
|
title: Field.string({ default: () => "" }),
|
||
|
content: Field.string({ default: () => "" }),
|
||
|
created: Field.date({ default: () => new Date() }),
|
||
|
archived: Field.boolean({ default: () => false }),
|
||
|
refereceItems: Field.array(Field.number()),
|
||
|
order: Field.number({ default: () => 0 }),
|
||
|
banner: Field.string({ default: undefined }),
|
||
|
})
|
||
|
|
||
|
const tags = model({
|
||
|
id: Field.number({ primaryKey: true }),
|
||
|
boardId: Field.number(),
|
||
|
title: Field.string({ default: () => "" }),
|
||
|
color: Field.string({ default: () => "#402579" }),
|
||
|
})
|
||
|
|
||
|
const itemTags = model({
|
||
|
id: Field.number({ primaryKey: true }),
|
||
|
itemId: Field.number(),
|
||
|
tagId: Field.number(),
|
||
|
boardId: Field.number(),
|
||
|
})
|
||
|
|
||
|
const db = idb("kanban", { boards, lists, items, tags, itemTags }, 3)
|
||
|
|
||
|
const JsonUtils = {
|
||
|
export: async () => {
|
||
|
const [boards, lists, items, tags, itemTags] = await Promise.all([
|
||
|
db.boards.all(),
|
||
|
db.lists.all(),
|
||
|
db.items.all(),
|
||
|
db.tags.all(),
|
||
|
db.itemTags.all(),
|
||
|
])
|
||
|
return JSON.stringify({
|
||
|
boards,
|
||
|
lists,
|
||
|
items,
|
||
|
tags,
|
||
|
itemTags,
|
||
|
})
|
||
|
},
|
||
|
import: async (data: string) => {
|
||
|
try {
|
||
|
const parsed = JSON.parse(data)
|
||
|
;["boards", "lists", "items", "tags", "itemTags"].forEach((store) => {
|
||
|
if (!(store in parsed))
|
||
|
throw new Error(`store '${store}' not found in import data`)
|
||
|
})
|
||
|
|
||
|
await Promise.all([
|
||
|
db.boards.clear(),
|
||
|
db.lists.clear(),
|
||
|
db.items.clear(),
|
||
|
db.tags.clear(),
|
||
|
db.itemTags.clear(),
|
||
|
])
|
||
|
|
||
|
const { boards, lists, items, tags, itemTags } = parsed as {
|
||
|
boards: Board[]
|
||
|
lists: List[]
|
||
|
items: ListItem[]
|
||
|
tags: Tag[]
|
||
|
itemTags: ItemTag[]
|
||
|
}
|
||
|
await Promise.all([
|
||
|
...boards.map((b) => db.boards.create(b)),
|
||
|
...lists.map((l) => db.lists.create(l)),
|
||
|
...items.map((i) => db.items.create(i)),
|
||
|
...tags.map((t) => db.tags.create(t)),
|
||
|
...itemTags.map((it) => db.itemTags.create(it)),
|
||
|
])
|
||
|
} catch (error) {
|
||
|
alert("an error occured while importing your data: " + error)
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// Boards
|
||
|
|
||
|
const loadBoards = async (): Promise<Board[]> => await db.boards.all()
|
||
|
|
||
|
const updateBoard = (board: Board) => db.boards.update(board) as Promise<Board>
|
||
|
|
||
|
const addBoard = async (): Promise<Board> => {
|
||
|
const board = await db.boards.create({})
|
||
|
if (!board) throw new Error("failed to create board")
|
||
|
await addList(board.id)
|
||
|
return board as Board
|
||
|
}
|
||
|
|
||
|
const deleteBoard = (board: Board) =>
|
||
|
db.boards.delete(board.id) as Promise<void>
|
||
|
|
||
|
const archiveBoard = (board: Board) =>
|
||
|
db.boards.update({ ...board, archived: true }) as Promise<Board>
|
||
|
|
||
|
// Lists
|
||
|
|
||
|
const loadLists = (boardId: number, archived = false) =>
|
||
|
db.lists.findMany((l) => {
|
||
|
return l.boardId === boardId && l.archived === archived
|
||
|
}) as Promise<List[]>
|
||
|
|
||
|
const updateList = (list: List) => db.lists.update(list) as Promise<List>
|
||
|
|
||
|
const addList = (boardId: number, order = 0) =>
|
||
|
db.lists.create({ boardId, order }) as Promise<List>
|
||
|
|
||
|
const deleteList = (list: List) => db.lists.delete(list.id) as Promise<void>
|
||
|
|
||
|
const archiveList = (list: List) =>
|
||
|
db.lists.update({ ...list, archived: true }) as Promise<List>
|
||
|
|
||
|
// Items
|
||
|
|
||
|
const loadItems = (listId: number, archived = false) =>
|
||
|
db.items.findMany((i) => {
|
||
|
return i.listId === listId && i.archived === archived
|
||
|
}) as Promise<ListItem[]>
|
||
|
|
||
|
const updateItem = (item: ListItem) =>
|
||
|
db.items.update(item) as Promise<ListItem>
|
||
|
|
||
|
const addItem = (listId: number, order = 0) =>
|
||
|
db.items.create({ listId, refereceItems: [], order }) as Promise<ListItem>
|
||
|
|
||
|
const deleteItem = (item: ListItem) => db.items.delete(item.id) as Promise<void>
|
||
|
|
||
|
const archiveItem = (item: ListItem) =>
|
||
|
db.items.update({ ...item, archived: true }) as Promise<ListItem>
|
||
|
|
||
|
// Tags
|
||
|
|
||
|
const loadTags = async (
|
||
|
boardId: number
|
||
|
): Promise<{ tags: Tag[]; itemTags: ItemTag[] }> => {
|
||
|
const [tags, itemTags] = await Promise.all([
|
||
|
db.tags.findMany((t) => t.boardId === boardId),
|
||
|
db.itemTags.findMany((t) => t.boardId === boardId),
|
||
|
])
|
||
|
return {
|
||
|
tags,
|
||
|
itemTags,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const updateTag = (tag: Tag) => db.tags.update(tag) as Promise<Tag>
|
||
|
|
||
|
const addTag = (boardId: number) => db.tags.create({ boardId }) as Promise<Tag>
|
||
|
|
||
|
const deleteTag = async (tag: Tag) => db.tags.delete(tag.id)
|
||
|
|
||
|
const addItemTag = (boardId: number, itemId: number, tagId: number) =>
|
||
|
db.itemTags.create({
|
||
|
boardId,
|
||
|
itemId,
|
||
|
tagId,
|
||
|
}) as Promise<ItemTag>
|
||
|
|
||
|
const deleteItemTag = (itemTag: ItemTag) => db.itemTags.delete(itemTag.id)
|