From 0b83077e792531697123a4364a133347d3b7f20c Mon Sep 17 00:00:00 2001 From: Arjuna Date: Tue, 3 Feb 2026 22:13:39 +0700 Subject: [PATCH 1/3] feat: create project logic --- Internal/adapters/repository/postgres.go | 1 + Internal/adapters/repository/projectRepo.go | 20 +++++++++ Internal/api/Routes/routev1.go | 2 + Internal/api/handlers/projectHandler.go | 47 +++++++++++++++++++++ Internal/api/handlers/userHandler.go | 16 +++---- Internal/api/middleware/auth.go | 1 + Internal/core/domain/projectdb.go | 11 +++++ Internal/core/services/projectService.go | 33 +++++++++++++++ cmd/server/main.go | 5 +++ 9 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 Internal/adapters/repository/projectRepo.go create mode 100644 Internal/api/handlers/projectHandler.go create mode 100644 Internal/core/domain/projectdb.go create mode 100644 Internal/core/services/projectService.go diff --git a/Internal/adapters/repository/postgres.go b/Internal/adapters/repository/postgres.go index e1974f5..cbf2b20 100644 --- a/Internal/adapters/repository/postgres.go +++ b/Internal/adapters/repository/postgres.go @@ -41,6 +41,7 @@ func (db *DBContainer) Migrate() error { err := db.Gorm.AutoMigrate( &domain.User{}, &domain.Invitation{}, + &domain.Project{}, ) if err != nil { log.Fatalf("Failed to migrate users: %v", err) diff --git a/Internal/adapters/repository/projectRepo.go b/Internal/adapters/repository/projectRepo.go new file mode 100644 index 0000000..7794ad9 --- /dev/null +++ b/Internal/adapters/repository/projectRepo.go @@ -0,0 +1,20 @@ +package repository + +import ( + "github.com/Arjuna-Ragil/Localbase/Internal/core/domain" +) + +type ProjectRepo struct { + DB *DBContainer +} + +func NewProjectRepo(db *DBContainer) *ProjectRepo { + return &ProjectRepo{DB: db} +} + +func (pr *ProjectRepo) CreateProject(project *domain.Project) (*domain.Project, error) { + if err := pr.DB.Gorm.Create(project).Error; err != nil { + return nil, err + } + return project, nil +} diff --git a/Internal/api/Routes/routev1.go b/Internal/api/Routes/routev1.go index 2a50cb8..3c33d41 100644 --- a/Internal/api/Routes/routev1.go +++ b/Internal/api/Routes/routev1.go @@ -12,6 +12,7 @@ type Deps struct { User *handlers.UserHandler Auth *handlers.AuthHandler System *handlers.SystemHandler + Project *handlers.ProjectHandler UserRepo *repository.UserRepository Config *config.Config } @@ -45,6 +46,7 @@ func SetupRouterV1(r *gin.Engine, deps Deps) { admin.POST("/invite", deps.Auth.CreateInviteHandler) admin.GET("/alluser", deps.User.AllUserHandler) admin.PUT("/updaterole", deps.User.UpdateRoleHandler) + admin.POST("/createproject", deps.Project.CreateProjectHandler) } } } diff --git a/Internal/api/handlers/projectHandler.go b/Internal/api/handlers/projectHandler.go new file mode 100644 index 0000000..d415c60 --- /dev/null +++ b/Internal/api/handlers/projectHandler.go @@ -0,0 +1,47 @@ +package handlers + +import ( + "github.com/Arjuna-Ragil/Localbase/Internal/core/services" + "github.com/gin-gonic/gin" +) + +type ProjectHandler struct { + ProjectService *services.ProjectService +} + +func NewProjectHandler(projectService *services.ProjectService) *ProjectHandler { + return &ProjectHandler{ProjectService: projectService} +} + +func (ph *ProjectHandler) CreateProjectHandler(c *gin.Context) { + var input services.CreateInput + //userRole, _ := c.Get("userRole") + //if userRole != "admin" { + // c.JSON(401, gin.H{ + // "message": "You are not authorized to perform this action", + // "data": nil, + // }) + // return + //} + err := c.ShouldBindJSON(&input) + if err != nil { + c.JSON(400, gin.H{ + "message": "Invalid input", + "data": err.Error(), + }) + return + } + + project, err := ph.ProjectService.CreateProject(&input) + if err != nil { + c.JSON(500, gin.H{ + "message": "failed to create project", + "data": err.Error(), + }) + return + } + c.JSON(200, gin.H{ + "message": "successfully created project", + "data": project, + }) +} diff --git a/Internal/api/handlers/userHandler.go b/Internal/api/handlers/userHandler.go index 26fb067..679ebf8 100644 --- a/Internal/api/handlers/userHandler.go +++ b/Internal/api/handlers/userHandler.go @@ -60,14 +60,14 @@ func (uh *UserHandler) AllUserHandler(c *gin.Context) { func (uh *UserHandler) UpdateRoleHandler(c *gin.Context) { var input services.RoleInput - //userRole, _ := c.Get("userRole") - //if userRole != "admin" { - // c.JSON(400, gin.H{ - // "message": "Not allowed to update role", - // "data": false, - // }) - // return - //} + userRole, _ := c.Get("userRole") + if userRole != "admin" { + c.JSON(400, gin.H{ + "message": "Not allowed to update role", + "data": false, + }) + return + } if err := c.ShouldBindJSON(&input); err != nil { c.JSON(400, gin.H{ "message": "input not valid", diff --git a/Internal/api/middleware/auth.go b/Internal/api/middleware/auth.go index d9a6038..c987b22 100644 --- a/Internal/api/middleware/auth.go +++ b/Internal/api/middleware/auth.go @@ -15,6 +15,7 @@ func AuthMiddleware(userRepo *repository.UserRepository, cfg *config.Config) gin return func(c *gin.Context) { if cfg.AuthMode == "false" { c.Set("userID", 1) + c.Set("userRole", "admin") c.Next() return } diff --git a/Internal/core/domain/projectdb.go b/Internal/core/domain/projectdb.go new file mode 100644 index 0000000..031512a --- /dev/null +++ b/Internal/core/domain/projectdb.go @@ -0,0 +1,11 @@ +package domain + +import "time" + +type Project struct { + ID uint `gorm:"primary_key;auto_increment" json:"id"` + Name string `gorm:"size:255;not null" json:"name"` + Desc string `gorm:"size:255;" json:"desc"` + AdminID uint `json:"admin_id"` + CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"` +} diff --git a/Internal/core/services/projectService.go b/Internal/core/services/projectService.go new file mode 100644 index 0000000..c5b07b7 --- /dev/null +++ b/Internal/core/services/projectService.go @@ -0,0 +1,33 @@ +package services + +import ( + "github.com/Arjuna-Ragil/Localbase/Internal/adapters/repository" + "github.com/Arjuna-Ragil/Localbase/Internal/core/domain" +) + +type ProjectService struct { + ProjectRepo *repository.ProjectRepo +} + +func NewProjectService(projectRepo *repository.ProjectRepo) *ProjectService { + return &ProjectService{ProjectRepo: projectRepo} +} + +type CreateInput struct { + Name string `json:"name"` + Desc string `json:"desc"` + AdminID uint `json:"admin_id"` +} + +func (ps *ProjectService) CreateProject(input *CreateInput) (*domain.Project, error) { + projectInfo := domain.Project{ + Name: input.Name, + Desc: input.Desc, + AdminID: input.AdminID, + } + project, err := ps.ProjectRepo.CreateProject(&projectInfo) + if err != nil { + return nil, err + } + return project, nil +} diff --git a/cmd/server/main.go b/cmd/server/main.go index e5025f8..c8ad29d 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -60,10 +60,15 @@ func SetupApp(db *repository.DBContainer, cfg *config.Config) Routes.Deps { authService := services.NewAuthService(authRepo, systemRepo) authHandler := handlers.NewAuthHandler(authService) + projectRepo := repository.NewProjectRepo(db) + projectService := services.NewProjectService(projectRepo) + projectHandler := handlers.NewProjectHandler(projectService) + return Routes.Deps{ User: userHandler, Auth: authHandler, System: systemHandler, + Project: projectHandler, UserRepo: userRepo, Config: cfg, } From 760ba90e4d92b55fbbecb45841f75c6896518d93 Mon Sep 17 00:00:00 2001 From: Arjuna Date: Wed, 4 Feb 2026 14:28:24 +0700 Subject: [PATCH 2/3] feat: creating project from fe logic implemented --- Internal/api/handlers/projectHandler.go | 16 +-- Lb-web/src/components/ui/textarea.tsx | 24 ++++ .../features/Login/context/AuthContext.tsx | 14 ++- .../project/components/CreateProjectModal.tsx | 109 ++++++++++++++++++ .../project/services/projectService.ts | 23 ++++ Lb-web/src/pages/Home.tsx | 37 +++++- 6 files changed, 208 insertions(+), 15 deletions(-) create mode 100644 Lb-web/src/components/ui/textarea.tsx create mode 100644 Lb-web/src/features/project/components/CreateProjectModal.tsx create mode 100644 Lb-web/src/features/project/services/projectService.ts diff --git a/Internal/api/handlers/projectHandler.go b/Internal/api/handlers/projectHandler.go index d415c60..72a3cbd 100644 --- a/Internal/api/handlers/projectHandler.go +++ b/Internal/api/handlers/projectHandler.go @@ -15,14 +15,14 @@ func NewProjectHandler(projectService *services.ProjectService) *ProjectHandler func (ph *ProjectHandler) CreateProjectHandler(c *gin.Context) { var input services.CreateInput - //userRole, _ := c.Get("userRole") - //if userRole != "admin" { - // c.JSON(401, gin.H{ - // "message": "You are not authorized to perform this action", - // "data": nil, - // }) - // return - //} + userRole, _ := c.Get("userRole") + if userRole != "admin" { + c.JSON(401, gin.H{ + "message": "You are not authorized to perform this action", + "data": nil, + }) + return + } err := c.ShouldBindJSON(&input) if err != nil { c.JSON(400, gin.H{ diff --git a/Lb-web/src/components/ui/textarea.tsx b/Lb-web/src/components/ui/textarea.tsx new file mode 100644 index 0000000..87c2072 --- /dev/null +++ b/Lb-web/src/components/ui/textarea.tsx @@ -0,0 +1,24 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +export interface TextareaProps + extends React.TextareaHTMLAttributes { } + +const Textarea = React.forwardRef( + ({ className, ...props }, ref) => { + return ( +