Tags
This commit is contained in:
parent
e0056809b3
commit
c4b41d79a2
|
@ -0,0 +1,52 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.bhasher.com/bhasher/focus/backend/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddTagToCard(ct types.CardTag) error {
|
||||||
|
_, err := db.Exec("INSERT INTO cardtags (card_id, tag_id, value) VALUES (?, ?, ?)", ct.CardID, ct.TagID, ct.Value)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllTagsOfCard(cardID int) ([]types.FullCardTag, error) {
|
||||||
|
rows, err := db.Query(`SELECT ct.card_id, ct.tag_id, t.title, ct.value
|
||||||
|
FROM cardtags ct
|
||||||
|
JOIN tags t ON ct.tag_id = t.id
|
||||||
|
WHERE ct.card_id = ?;
|
||||||
|
`, cardID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var cardtags []types.FullCardTag
|
||||||
|
for rows.Next() {
|
||||||
|
var ct types.FullCardTag
|
||||||
|
if err := rows.Scan(&ct.CardID, &ct.TagID, &ct.TagTitle, &ct.Value); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cardtags = append(cardtags, ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardtags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteTagOfCard(card_id int, tag_id int) error {
|
||||||
|
_, err := db.Exec("DELETE FROM cardtags WHERE card_id = ? AND tag_id = ?", card_id, tag_id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteTagsOfCard(card_id int) error {
|
||||||
|
_, err := db.Exec("DELETE FROM cardtags WHERE card_id = ?", card_id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateTagOfCard(ct types.CardTag) error {
|
||||||
|
_, err := db.Exec("UPDATE cardtags SET value = ? WHERE card_id = ? AND tag_id = ?", ct.Value, ct.CardID, ct.TagID)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -31,6 +31,20 @@ func InitDB(driver string, connStr string) error {
|
||||||
title TEXT,
|
title TEXT,
|
||||||
content TEXT,
|
content TEXT,
|
||||||
FOREIGN KEY(project_id) REFERENCES projects(id)
|
FOREIGN KEY(project_id) REFERENCES projects(id)
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS tags (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
project_id INTEGER,
|
||||||
|
title TEXT,
|
||||||
|
type int,
|
||||||
|
FOREIGN KEY(project_id) REFERENCES projects(id)
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS cardtags (
|
||||||
|
card_id INTEGER,
|
||||||
|
tag_id INTEGER,
|
||||||
|
value TEXT,
|
||||||
|
FOREIGN KEY(card_id) REFERENCES cards(id)
|
||||||
|
FOREIGN KEY(tag_id) REFERENCES tags(id)
|
||||||
);
|
);
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import "git.bhasher.com/bhasher/focus/backend/types"
|
||||||
|
|
||||||
|
func CreateTag(t types.Tag) (int, error) {
|
||||||
|
res, err := db.Exec("INSERT INTO tags (project_id, title, type) VALUES (?, ?, ?)", t.ProjectID, t.Title, t.Type)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(id), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllTagsOf(projectID int) ([]types.Tag, error) {
|
||||||
|
rows, err := db.Query("SELECT * FROM tags WHERE project_id = ?", projectID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var tags []types.Tag
|
||||||
|
for rows.Next() {
|
||||||
|
var t types.Tag
|
||||||
|
if err := rows.Scan(&t.ID, &t.ProjectID, &t.Title, &t.Type); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tags = append(tags, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTag(id int) (*types.Tag, error) {
|
||||||
|
var t types.Tag
|
||||||
|
|
||||||
|
err := db.QueryRow("SELECT * FROM tags WHERE id = ?", id).Scan(&t.ID, &t.ProjectID, &t.Title, &t.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteTag(id int) error {
|
||||||
|
_, err := db.Exec("DELETE FROM tags WHERE id = ?", id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateTag(t types.Tag) error {
|
||||||
|
_, err := db.Exec("UPDATE tags SET project_id = ?, title = ?, type = ? WHERE id = ?", t.ProjectID, t.Title, t.Type, t.ID)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateCards(c *fiber.Ctx) error {
|
func CreateCard(c *fiber.Ctx) error {
|
||||||
card := types.Card{}
|
card := types.Card{}
|
||||||
if err := c.BodyParser(&card); err != nil {
|
if err := c.BodyParser(&card); err != nil {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.bhasher.com/bhasher/focus/backend/db"
|
||||||
|
"git.bhasher.com/bhasher/focus/backend/types"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateTagOfCard(c *fiber.Ctx) error {
|
||||||
|
cardtag := types.CardTag{}
|
||||||
|
if err := c.BodyParser(&cardtag); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.AddTagToCard(cardtag)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot add tag to card"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllTagsOfCard(c *fiber.Ctx) error {
|
||||||
|
cardID, err := strconv.Atoi(c.Params("card_id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"status": "error", "error": "Invalid card_id"})
|
||||||
|
}
|
||||||
|
|
||||||
|
cardtags, err := db.GetAllTagsOfCard(cardID)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"status": "error", "error": "Cannot retrieve tags of card", "stack": fmt.Sprint(err)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{"status": "ok", "data": cardtags})
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteTagOfCard(c *fiber.Ctx) error {
|
||||||
|
card_id, err := strconv.Atoi(c.Params("card_id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid card ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_id, err := strconv.Atoi(c.Params("tag_id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.DeleteTagOfCard(card_id, tag_id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot delete tag of card"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteTagsOfCard(c *fiber.Ctx) error {
|
||||||
|
card_id, err := strconv.Atoi(c.Params("card_id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid card ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.DeleteTagsOfCard(card_id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot delete tags of card"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateTagOfCard(c *fiber.Ctx) error {
|
||||||
|
card_id, err := strconv.Atoi(c.Params("card_id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid card ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_id, err := strconv.Atoi(c.Params("tag_id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
cardtag := types.CardTag{CardID: card_id, TagID: tag_id}
|
||||||
|
if err := c.BodyParser(&cardtag); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.UpdateTagOfCard(cardtag)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot update tag of card"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.bhasher.com/bhasher/focus/backend/db"
|
||||||
|
"git.bhasher.com/bhasher/focus/backend/types"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateTag(c *fiber.Ctx) error {
|
||||||
|
tag := types.Tag{}
|
||||||
|
if err := c.BodyParser(&tag); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := db.CreateTag(tag)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot create tag", "trace": fmt.Sprint(err)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"id": id})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllTagsOf(c *fiber.Ctx) error {
|
||||||
|
projectID, err := strconv.Atoi(c.Params("project_id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"status": "error", "error": "Invalid project_id"})
|
||||||
|
}
|
||||||
|
|
||||||
|
projects, err := db.GetAllTagsOf(projectID)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot retrieve tags"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{"status": "ok", "data": projects})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTag(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.Atoi(c.Params("id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := db.GetTag(id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot retrieve tag"})
|
||||||
|
}
|
||||||
|
if tag == nil {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Tag not found"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteTag(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.Atoi(c.Params("id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.DeleteTag(id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot delete tag"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateTag(c *fiber.Ctx) error {
|
||||||
|
id, err := strconv.Atoi(c.Params("id"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := types.Tag{ID: id}
|
||||||
|
if err := c.BodyParser(&tag); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.UpdateTag(tag)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot update tag"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
|
@ -42,11 +42,23 @@ func main() {
|
||||||
app.Delete("/api/list/:id", handlers.DeleteList)
|
app.Delete("/api/list/:id", handlers.DeleteList)
|
||||||
app.Put("/api/list/:id", handlers.UpdateList)
|
app.Put("/api/list/:id", handlers.UpdateList)
|
||||||
|
|
||||||
app.Post("/api/card", handlers.CreateCards)
|
app.Post("/api/card", handlers.CreateCard)
|
||||||
app.Get("/api/cards/:project_id", handlers.GetAllCardsOf)
|
app.Get("/api/cards/:project_id", handlers.GetAllCardsOf)
|
||||||
app.Get("/api/card/:id", handlers.GetCard)
|
app.Get("/api/card/:id", handlers.GetCard)
|
||||||
app.Delete("/api/card/:id", handlers.DeleteCard)
|
app.Delete("/api/card/:id", handlers.DeleteCard)
|
||||||
app.Put("/api/card/:id", handlers.UpdateCard)
|
app.Put("/api/card/:id", handlers.UpdateCard)
|
||||||
|
|
||||||
|
app.Post("/api/tag", handlers.CreateTag)
|
||||||
|
app.Get("/api/tags/:project_id", handlers.GetAllTagsOf)
|
||||||
|
app.Get("/api/tag/:id", handlers.GetTag)
|
||||||
|
app.Delete("/api/tag/:id", handlers.DeleteTag)
|
||||||
|
app.Put("/api/tag/:id", handlers.UpdateTag)
|
||||||
|
|
||||||
|
app.Post("/api/cardtag", handlers.CreateTagOfCard)
|
||||||
|
app.Get("/api/cardtags/:card_id", handlers.GetAllTagsOfCard)
|
||||||
|
app.Delete("/api/cardtag/:card_id/:tag_id", handlers.DeleteTagOfCard)
|
||||||
|
app.Delete("/api/cardtags/:card_id", handlers.DeleteTagsOfCard)
|
||||||
|
app.Put("/api/cardtag/:card_id/:tag_id", handlers.UpdateTag)
|
||||||
|
|
||||||
log.Fatal(app.Listen(fmt.Sprintf(":%v", port)))
|
log.Fatal(app.Listen(fmt.Sprintf(":%v", port)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
type CardTag struct {
|
||||||
|
CardID int `json:"card_id"`
|
||||||
|
TagID int `json:"tag_id"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FullCardTag struct {
|
||||||
|
CardID int `json:"card_id"`
|
||||||
|
TagID int `json:"tag_id"`
|
||||||
|
TagTitle string `json:"tag_title"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
ProjectID int `json:"project_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
}
|
|
@ -1,4 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import axios from 'axios';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
const backend = 'http://127.0.0.1:3000';
|
||||||
|
|
||||||
interface Card {
|
interface Card {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -10,6 +15,24 @@
|
||||||
title: 'No title',
|
title: 'No title',
|
||||||
content: 'Nocontent'
|
content: 'Nocontent'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface FullTag {
|
||||||
|
tag_id: number;
|
||||||
|
tag_title: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tags: FullTag[];
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const response = await axios.get(`${backend}/api/cardtags/${card.id}`);
|
||||||
|
|
||||||
|
if (response.data.status === 'ok') {
|
||||||
|
tags = response.data.data;
|
||||||
|
} else {
|
||||||
|
console.error(response.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -18,8 +41,13 @@
|
||||||
|
|
||||||
<div class="card" draggable={true}>
|
<div class="card" draggable={true}>
|
||||||
<div class="title">{card.title}</div>
|
<div class="title">{card.title}</div>
|
||||||
<div class="tags">
|
{#if tags}
|
||||||
<span class="tag" style="background-color: #874d45;">HIGH</span>
|
<div class="tags">
|
||||||
<span class="tag" style="background-color: #4a8645;">PERSONAL</span>
|
<!-- <span class="tag" style="background-color: #874d45;">HIGH</span>
|
||||||
</div>
|
<span class="tag" style="background-color: #4a8645;">PERSONAL</span> -->
|
||||||
|
{#each tags as tag}
|
||||||
|
<span class="tag" style="border: 1px solid #333">{tag.value}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue