diff --git a/backend/api/CreateCategory.go b/backend/api/CreateCategory.go new file mode 100644 index 0000000..3f3c1df --- /dev/null +++ b/backend/api/CreateCategory.go @@ -0,0 +1,40 @@ +package api + +import ( + "backend/database" + "backend/model" + "fmt" + + "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt/v5" +) + +func CreateCategory(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + userid := claims["id"].(string) + requestdata := new(model.Category) + fmt.Println(string(c.Body())) + if err := c.BodyParser(requestdata); err != nil { + return c.Status( + fiber.StatusBadRequest, + ).JSON( + fiber.Map{ + "error": "Cannot parse request body", + }, + ) + } + fmt.Println(requestdata) + requestdata.UserID = userid + err := database.CreateCategory(requestdata) + if err != nil { + return c.Status( + fiber.StatusInternalServerError, + ).JSON( + fiber.Map{ + "error": err, + }, + ) + } + return c.Status(fiber.StatusOK).JSON(fiber.Map{"status": "ok"}) +} diff --git a/backend/api/GetAllCategories.go b/backend/api/GetAllCategories.go index 879766d..9c34e31 100644 --- a/backend/api/GetAllCategories.go +++ b/backend/api/GetAllCategories.go @@ -4,10 +4,14 @@ import ( "backend/database" "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt/v5" ) func GetAllCategories(c *fiber.Ctx) error { - categories, err := database.GetAllCategories() + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + userid := claims["id"].(string) + categories, err := database.GetAllCategories(userid) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": err.Error(), diff --git a/backend/database/categories.go b/backend/database/categories.go index e20e0fe..a793072 100644 --- a/backend/database/categories.go +++ b/backend/database/categories.go @@ -13,7 +13,9 @@ import ( func CreateCategory(category *model.Category) error { e := Db.Where(model.Category{ - Title: category.Title, + Title: category.Title, + UserID: category.UserID, + Type: category.Type, }).FirstOrCreate(&category).Error if e != nil { return e @@ -75,9 +77,9 @@ func GetCategoriesFromFile(filename string) []model.Category { return res } -func GetAllCategories() ([]model.Category, error) { +func GetAllCategories(userid string) ([]model.Category, error) { var categories []model.Category - e := Db.Find(&categories).Error + e := Db.Where(model.Category{UserID: userid}).Find(&categories).Error if e != nil { return nil, e } diff --git a/backend/database/db.go b/backend/database/db.go index d2c614f..35bf102 100644 --- a/backend/database/db.go +++ b/backend/database/db.go @@ -41,13 +41,16 @@ func InitDatabase() { allusers := GetUsersFromFile(os.Getenv("USERS_FILE")) for i := range allusers { CreateUser(&allusers[i]) - } - } - - if os.Getenv("DEFAULT_CATEGORIES_FILE") != "" { - default_categories := GetCategoriesFromFile(os.Getenv("DEFAULT_CATEGORIES_FILE")) - for i := range default_categories { - CreateCategory(&default_categories[i]) + if os.Getenv("DEFAULT_CATEGORIES_FILE") != "" { + default_categories := GetCategoriesFromFile(os.Getenv("DEFAULT_CATEGORIES_FILE")) + for j := range default_categories { + default_categories[j].UserID = allusers[i].ID + for k := range default_categories[j].Subcategories { + default_categories[j].Subcategories[k].UserID = allusers[i].ID + } + CreateCategory(&default_categories[j]) + } + } } } } diff --git a/backend/internal/internaljwt/JwtFromBody.go b/backend/internal/internaljwt/JwtFromBody.go index 8da28e6..91e1444 100644 --- a/backend/internal/internaljwt/JwtFromBody.go +++ b/backend/internal/internaljwt/JwtFromBody.go @@ -1,36 +1,36 @@ package internaljwt -import ( - "backend/model" +// import ( +// "backend/model" - "github.com/gofiber/fiber/v2" -) +// "github.com/gofiber/fiber/v2" +// ) -func JwtFromBody(c *fiber.Ctx) error { - req := new(model.TokenRequest) - if err := c.BodyParser(req); err != nil { - return c.Status( - fiber.StatusBadRequest, - ).JSON( - fiber.Map{ - "error": "Cannot parse request body", - }, - ) - } +// func JwtFromBody(c *fiber.Ctx) error { +// req := new(model.TokenRequest) +// if err := c.BodyParser(req); err != nil { +// return c.Status( +// fiber.StatusBadRequest, +// ).JSON( +// fiber.Map{ +// "error": "Cannot parse request body", +// }, +// ) +// } - if req.Token == "" { - return c.Status( - fiber.StatusBadRequest, - ).JSON( - fiber.Map{ - "error": "Token not found in request body", - }, - ) - } +// if req.Token == "" { +// return c.Status( +// fiber.StatusBadRequest, +// ).JSON( +// fiber.Map{ +// "error": "Token not found in request body", +// }, +// ) +// } - c.Request().Header.Set( - "Authorization", "Bearer "+req.Token, - ) +// c.Request().Header.Set( +// "Authorization", "Bearer "+req.Token, +// ) - return c.Next() -} +// return c.Next() +// } diff --git a/backend/model/Category.go b/backend/model/Category.go index 975fbd8..162dcd6 100644 --- a/backend/model/Category.go +++ b/backend/model/Category.go @@ -4,7 +4,7 @@ type Category struct { BaseModel Icon string `json:"icon"` Title string `json:"title"` - Subcategories []Subcategory `json:"subcategories" gorm:"foreignKey:CategoryID"` + Subcategories []Subcategory `json:"subcategories,omitempty" gorm:"foreignKey:CategoryID"` Type string `json:"type"` UserID string } diff --git a/backend/server/configure.go b/backend/server/configure.go index 0639528..cfdf1af 100644 --- a/backend/server/configure.go +++ b/backend/server/configure.go @@ -3,7 +3,6 @@ package server import ( "backend/api" "backend/database" - "backend/internal/internaljwt" "backend/utils" "os" @@ -25,33 +24,33 @@ func Configure(app *fiber.App) { apiGroup := app.Group("/api") apiGroup.Post("/token", api.Login) - app.Use(internaljwt.JwtFromBody, jwtware.New(jwtware.Config{ + app.Use(jwtware.New(jwtware.Config{ SigningKey: jwtware.SigningKey{ Key: []byte(os.Getenv("JWT_SECRET_KEY")), }, - ErrorHandler: func(c *fiber.Ctx, err error) error { - if err.Error() == "Missing or malformed JWT" { - return c.Status( - fiber.StatusBadRequest, - ).JSON( - fiber.Map{ - "status": "error", - "message": "Missing or malformed JWT", - "data": nil, - }, - ) - } - return c.Status( - fiber.StatusUnauthorized, - ).JSON( - fiber.Map{ - "status": "error", - "message": "Invalid or expired JWT", - "data": nil, - "error": err.Error(), - }, - ) - }, + // ErrorHandler: func(c *fiber.Ctx, err error) error { + // if err.Error() == "Missing or malformed JWT" { + // return c.Status( + // fiber.StatusBadRequest, + // ).JSON( + // fiber.Map{ + // "status": "error", + // "message": "Missing or malformed JWT", + // "data": nil, + // }, + // ) + // } + // return c.Status( + // fiber.StatusUnauthorized, + // ).JSON( + // fiber.Map{ + // "status": "error", + // "message": "Invalid or expired JWT", + // "data": nil, + // "error": err.Error(), + // }, + // ) + // }, })) usersGroup := apiGroup.Group("/users") @@ -63,4 +62,5 @@ func Configure(app *fiber.App) { categoryGroup := apiGroup.Group("/category") categoryGroup.Get("/", api.GetAllCategories) + categoryGroup.Post("/", api.CreateCategory) } diff --git a/frontend/src/logic/category/CreateCategory.js b/frontend/src/logic/category/CreateCategory.js new file mode 100644 index 0000000..59fe436 --- /dev/null +++ b/frontend/src/logic/category/CreateCategory.js @@ -0,0 +1,20 @@ +import routes from "../../routes"; + +const CreateCategory = async (category) => { + try { + const response = await fetch(routes.categories, { + method: "POST", + headers: { + Authorization: `Bearer ${localStorage.getItem("token")}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(category), + }); + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching categories:", error); + return []; + } +}; +export default CreateCategory; diff --git a/frontend/src/modals/AddCategoryModal/AddCategoryModal.jsx b/frontend/src/modals/AddCategoryModal/AddCategoryModal.jsx index 439cd2b..a38df59 100644 --- a/frontend/src/modals/AddCategoryModal/AddCategoryModal.jsx +++ b/frontend/src/modals/AddCategoryModal/AddCategoryModal.jsx @@ -2,13 +2,34 @@ import { useState } from "react"; import { useTheme } from "../../contexts/ThemeContext"; import { Modal, Button, Card, Form } from "react-bootstrap"; import EmojiPickerButton from "./components/EmojiPickerButton/EmojiPickerButton"; +import CreateCategory from "../../logic/category/CreateCategory"; -const AddCategoryModal = ({ variant, ...props }) => { +const AddCategoryModal = ({ + variant, + type, + showParent, + closeParent, + ...props +}) => { const { theme } = useTheme(); const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); const [title, setTitle] = useState(""); + const [emojivalue, setEmojivate] = useState("😀"); + const createCategory = async () => { + closeParent(); + console.log({ title: title, icon: emojivalue, type: type }); + var result = await CreateCategory({ + title: title, + icon: emojivalue, + type: type, + }); + if (result.status === "ok") { + setShow(false); + showParent(); + } + }; return ( <> - + diff --git a/frontend/src/modals/AddCategoryModal/components/EmojiPickerButton/EmojiPickerButton.jsx b/frontend/src/modals/AddCategoryModal/components/EmojiPickerButton/EmojiPickerButton.jsx index 7e5c241..f0e7c37 100644 --- a/frontend/src/modals/AddCategoryModal/components/EmojiPickerButton/EmojiPickerButton.jsx +++ b/frontend/src/modals/AddCategoryModal/components/EmojiPickerButton/EmojiPickerButton.jsx @@ -3,10 +3,10 @@ import { Button, Overlay, Popover, InputGroup } from "react-bootstrap"; import EmojiPicker from "emoji-picker-react"; import { useTheme } from "../../../../contexts/ThemeContext"; -const EmojiPickerButton = () => { +const EmojiPickerButton = ({ inputValue, setInputValue }) => { const { theme } = useTheme(); const [showPicker, setShowPicker] = useState(false); - const [inputValue, setInputValue] = useState("😀"); + // const [inputValue, setInputValue] = useState("😀"); const target = useRef(null); const handleEmojiSelect = (emoji) => { diff --git a/frontend/src/modals/CategoriesModal/CategoriesModal.jsx b/frontend/src/modals/CategoriesModal/CategoriesModal.jsx index 1e3ef48..69a9a46 100644 --- a/frontend/src/modals/CategoriesModal/CategoriesModal.jsx +++ b/frontend/src/modals/CategoriesModal/CategoriesModal.jsx @@ -21,6 +21,7 @@ const CategoriesModal = ({ variant }) => { const { theme } = useTheme(); const [show, setShow] = useState(false); const [searchTerm, setSearchTerm] = useState(""); + const [categoriestype, setCategoriestype] = useState("expense"); const handleClose = () => setShow(false); const handleShow = () => setShow(true); useEffect(() => { @@ -33,7 +34,7 @@ const CategoriesModal = ({ variant }) => { ); console.log(data); }); - }, []); + }, [show]); const filteredExpenseCategories = useMemo(() => { if (!searchTerm) return expenseCategories; const term = searchTerm.toLowerCase(); @@ -80,6 +81,9 @@ const CategoriesModal = ({ variant }) => { Reorder @@ -101,18 +105,22 @@ const CategoriesModal = ({ variant }) => { { + setCategoriestype(selectedKey); + console.log(selectedKey, categoriestype); + }} > - +
{filteredIncomeCategories.map((category) => ( { ))}
- +
{filteredExpenseCategories.map((category) => ( { >
➞
-
+
{subcategories.map((subcategory) => (
{subcategory.title}