Skip to content

Commit 2acab85

Browse files
authored
Merge pull request #43 from Arjuna-Ragil/feat/39-create-project-logic
Feat/39 create project logic
2 parents 4082ff1 + 2f59365 commit 2acab85

14 files changed

Lines changed: 444 additions & 24 deletions

File tree

Internal/adapters/repository/postgres.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func (db *DBContainer) Migrate() error {
4141
err := db.Gorm.AutoMigrate(
4242
&domain.User{},
4343
&domain.Invitation{},
44+
&domain.Project{},
4445
)
4546
if err != nil {
4647
log.Fatalf("Failed to migrate users: %v", err)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package repository
2+
3+
import (
4+
"github.com/Arjuna-Ragil/Localbase/Internal/core/domain"
5+
)
6+
7+
type ProjectRepo struct {
8+
DB *DBContainer
9+
}
10+
11+
func NewProjectRepo(db *DBContainer) *ProjectRepo {
12+
return &ProjectRepo{DB: db}
13+
}
14+
15+
func (pr *ProjectRepo) CreateProject(project *domain.Project) (*domain.Project, error) {
16+
if err := pr.DB.Gorm.Create(project).Error; err != nil {
17+
return nil, err
18+
}
19+
return project, nil
20+
}
21+
22+
func (pr *ProjectRepo) FetchProjects(projects []domain.Project) ([]domain.Project, error) {
23+
if err := pr.DB.Gorm.Find(&projects).Error; err != nil {
24+
return nil, err
25+
}
26+
return projects, nil
27+
}

Internal/api/Routes/routev1.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type Deps struct {
1212
User *handlers.UserHandler
1313
Auth *handlers.AuthHandler
1414
System *handlers.SystemHandler
15+
Project *handlers.ProjectHandler
1516
UserRepo *repository.UserRepository
1617
Config *config.Config
1718
}
@@ -45,6 +46,11 @@ func SetupRouterV1(r *gin.Engine, deps Deps) {
4546
admin.POST("/invite", deps.Auth.CreateInviteHandler)
4647
admin.GET("/alluser", deps.User.AllUserHandler)
4748
admin.PUT("/updaterole", deps.User.UpdateRoleHandler)
49+
admin.POST("/createproject", deps.Project.CreateProjectHandler)
50+
}
51+
project := protected.Group("/project")
52+
{
53+
project.GET("/projects", deps.Project.GetAllProjectHandler)
4854
}
4955
}
5056
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package handlers
2+
3+
import (
4+
"github.com/Arjuna-Ragil/Localbase/Internal/core/services"
5+
"github.com/gin-gonic/gin"
6+
)
7+
8+
type ProjectHandler struct {
9+
ProjectService *services.ProjectService
10+
}
11+
12+
func NewProjectHandler(projectService *services.ProjectService) *ProjectHandler {
13+
return &ProjectHandler{ProjectService: projectService}
14+
}
15+
16+
func (ph *ProjectHandler) CreateProjectHandler(c *gin.Context) {
17+
var input services.CreateInput
18+
userRole, _ := c.Get("userRole")
19+
if userRole != "admin" {
20+
c.JSON(401, gin.H{
21+
"message": "You are not authorized to perform this action",
22+
"data": nil,
23+
})
24+
return
25+
}
26+
err := c.ShouldBindJSON(&input)
27+
if err != nil {
28+
c.JSON(400, gin.H{
29+
"message": "Invalid input",
30+
"data": err.Error(),
31+
})
32+
return
33+
}
34+
35+
project, err := ph.ProjectService.CreateProject(&input)
36+
if err != nil {
37+
c.JSON(500, gin.H{
38+
"message": "failed to create project",
39+
"data": err.Error(),
40+
})
41+
return
42+
}
43+
c.JSON(200, gin.H{
44+
"message": "successfully created project",
45+
"data": project,
46+
})
47+
}
48+
49+
func (ph *ProjectHandler) GetAllProjectHandler(c *gin.Context) {
50+
userRole := c.GetString("userRole")
51+
projects, err := ph.ProjectService.GetProjects(userRole)
52+
if err != nil {
53+
c.JSON(500, gin.H{
54+
"message": "failed to get projects",
55+
"data": err.Error(),
56+
})
57+
return
58+
}
59+
c.JSON(200, gin.H{
60+
"message": "got all projects",
61+
"data": projects,
62+
})
63+
}

Internal/api/handlers/userHandler.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ func (uh *UserHandler) AllUserHandler(c *gin.Context) {
6060

6161
func (uh *UserHandler) UpdateRoleHandler(c *gin.Context) {
6262
var input services.RoleInput
63-
//userRole, _ := c.Get("userRole")
64-
//if userRole != "admin" {
65-
// c.JSON(400, gin.H{
66-
// "message": "Not allowed to update role",
67-
// "data": false,
68-
// })
69-
// return
70-
//}
63+
userRole, _ := c.Get("userRole")
64+
if userRole != "admin" {
65+
c.JSON(400, gin.H{
66+
"message": "Not allowed to update role",
67+
"data": false,
68+
})
69+
return
70+
}
7171
if err := c.ShouldBindJSON(&input); err != nil {
7272
c.JSON(400, gin.H{
7373
"message": "input not valid",

Internal/api/middleware/auth.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import (
1414
func AuthMiddleware(userRepo *repository.UserRepository, cfg *config.Config) gin.HandlerFunc {
1515
return func(c *gin.Context) {
1616
if cfg.AuthMode == "false" {
17-
c.Set("userID", 1)
17+
c.Set("userID", uint(1))
18+
c.Set("userRole", "admin")
1819
c.Next()
1920
return
2021
}

Internal/core/domain/projectdb.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package domain
2+
3+
import "time"
4+
5+
type Project struct {
6+
ID uint `gorm:"primary_key;auto_increment" json:"id"`
7+
Name string `gorm:"size:255;not null" json:"name"`
8+
Desc string `gorm:"size:255;" json:"desc"`
9+
AdminID uint `json:"admin_id"`
10+
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
11+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package services
2+
3+
import (
4+
"errors"
5+
6+
"github.com/Arjuna-Ragil/Localbase/Internal/adapters/repository"
7+
"github.com/Arjuna-Ragil/Localbase/Internal/core/domain"
8+
)
9+
10+
type ProjectService struct {
11+
ProjectRepo *repository.ProjectRepo
12+
}
13+
14+
func NewProjectService(projectRepo *repository.ProjectRepo) *ProjectService {
15+
return &ProjectService{ProjectRepo: projectRepo}
16+
}
17+
18+
type CreateInput struct {
19+
Name string `json:"name"`
20+
Desc string `json:"desc"`
21+
AdminID uint `json:"admin_id"`
22+
}
23+
24+
func (ps *ProjectService) CreateProject(input *CreateInput) (*domain.Project, error) {
25+
projectInfo := domain.Project{
26+
Name: input.Name,
27+
Desc: input.Desc,
28+
AdminID: input.AdminID,
29+
}
30+
project, err := ps.ProjectRepo.CreateProject(&projectInfo)
31+
if err != nil {
32+
return nil, err
33+
}
34+
return project, nil
35+
}
36+
37+
func (ps *ProjectService) GetProjects(Role string) ([]domain.Project, error) {
38+
if Role != "admin" {
39+
return nil, errors.New("permission denied")
40+
}
41+
var projects []domain.Project
42+
project, err := ps.ProjectRepo.FetchProjects(projects)
43+
if err != nil {
44+
return nil, err
45+
}
46+
return project, nil
47+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from "react"
2+
3+
import { cn } from "@/lib/utils"
4+
5+
export interface TextareaProps
6+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { }
7+
8+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
9+
({ className, ...props }, ref) => {
10+
return (
11+
<textarea
12+
className={cn(
13+
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
14+
className
15+
)}
16+
ref={ref}
17+
{...props}
18+
/>
19+
)
20+
}
21+
)
22+
Textarea.displayName = "Textarea"
23+
24+
export { Textarea }

Lb-web/src/features/Login/context/AuthContext.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
22
import api from "@/services/api";
33

4+
interface User {
5+
id: number;
6+
username: string;
7+
email: string;
8+
role: string;
9+
}
10+
411
interface AuthContextType {
512
isAuthenticated: boolean | null;
13+
user: User | null;
614
role: string | null;
715
loading: boolean;
816
login: () => void; // call this after successful login to update state
@@ -13,16 +21,19 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
1321

1422
export function AuthProvider({ children }: { children: ReactNode }) {
1523
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
24+
const [user, setUser] = useState<User | null>(null);
1625
const [role, setRole] = useState<string | null>(null);
1726
const [loading, setLoading] = useState(true);
1827

1928
const checkAuth = async () => {
2029
try {
2130
const response = await api.get('/protected/user/me');
2231
setIsAuthenticated(true);
32+
setUser(response.data.data);
2333
setRole(response.data.data.role);
2434
} catch {
2535
setIsAuthenticated(false);
36+
setUser(null);
2637
setRole(null);
2738
} finally {
2839
setLoading(false);
@@ -45,12 +56,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
4556
console.error("Logout failed", error);
4657
} finally {
4758
setIsAuthenticated(false);
59+
setUser(null);
4860
setRole(null);
4961
}
5062
};
5163

5264
return (
53-
<AuthContext.Provider value={{ isAuthenticated, role, loading, login, logout }}>
65+
<AuthContext.Provider value={{ isAuthenticated, user, role, loading, login, logout }}>
5466
{children}
5567
</AuthContext.Provider>
5668
);

0 commit comments

Comments
 (0)