Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
H
hrsystem
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
hrsystem
Commits
ba81ccf6
Commit
ba81ccf6
authored
Apr 01, 2026
by
Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update 17 files via Son of Anton
parent
f9cd0fe6
Changes
17
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
1807 additions
and
0 deletions
+1807
-0
app.module.ts
backend/src/app.module.ts
+4
-0
board-template.service.ts
backend/src/modules/boards/board-template.service.ts
+157
-0
boards.controller.ts
backend/src/modules/boards/boards.controller.ts
+174
-0
boards.module.ts
backend/src/modules/boards/boards.module.ts
+11
-0
boards.service.ts
backend/src/modules/boards/boards.service.ts
+668
-0
board-filter.dto.ts
backend/src/modules/boards/dto/board-filter.dto.ts
+18
-0
board-member.dto.ts
backend/src/modules/boards/dto/board-member.dto.ts
+30
-0
board-response.dto.ts
backend/src/modules/boards/dto/board-response.dto.ts
+52
-0
board-template.dto.ts
backend/src/modules/boards/dto/board-template.dto.ts
+29
-0
create-board.dto.ts
backend/src/modules/boards/dto/create-board.dto.ts
+66
-0
update-board.dto.ts
backend/src/modules/boards/dto/update-board.dto.ts
+57
-0
columns.controller.ts
backend/src/modules/columns/columns.controller.ts
+62
-0
columns.module.ts
backend/src/modules/columns/columns.module.ts
+10
-0
columns.service.ts
backend/src/modules/columns/columns.service.ts
+318
-0
create-column.dto.ts
backend/src/modules/columns/dto/create-column.dto.ts
+29
-0
update-column.dto.ts
backend/src/modules/columns/dto/update-column.dto.ts
+40
-0
schema-boards.prisma
prisma/schema-boards.prisma
+82
-0
No files found.
backend/src/app.module.ts
View file @
ba81ccf6
...
...
@@ -17,6 +17,8 @@ import { SettingsModule } from './modules/settings/settings.module';
import
{
AuditTrailModule
}
from
'./modules/audit-trail/audit-trail.module'
;
import
{
UsersModule
}
from
'./modules/users/users.module'
;
import
{
OnboardingModule
}
from
'./modules/onboarding/onboarding.module'
;
import
{
BoardsModule
}
from
'./modules/boards/boards.module'
;
import
{
ColumnsModule
}
from
'./modules/columns/columns.module'
;
import
{
JwtAuthGuard
}
from
'./common/guards/jwt-auth.guard'
;
import
{
RolesGuard
}
from
'./common/guards/roles.guard'
;
...
...
@@ -40,6 +42,8 @@ import { RateLimitMiddleware } from './common/middleware/rate-limit.middleware';
AuditTrailModule
,
UsersModule
,
OnboardingModule
,
BoardsModule
,
ColumnsModule
,
],
providers
:
[
{
provide
:
APP_GUARD
,
useClass
:
JwtAuthGuard
},
...
...
backend/src/modules/boards/board-template.service.ts
0 → 100644
View file @
ba81ccf6
import
{
Injectable
,
NotFoundException
,
ForbiddenException
,
Logger
,
}
from
'@nestjs/common'
;
import
{
PrismaService
}
from
'../../prisma/prisma.service'
;
import
{
CreateBoardTemplateDto
,
UpdateBoardTemplateDto
}
from
'./dto/board-template.dto'
;
import
{
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
@
Injectable
()
export
class
BoardTemplateService
{
private
readonly
logger
=
new
Logger
(
BoardTemplateService
.
name
);
constructor
(
private
readonly
prisma
:
PrismaService
)
{}
async
saveAsTemplate
(
dto
:
CreateBoardTemplateDto
,
currentUser
:
RequestUser
):
Promise
<
any
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
&&
currentUser
.
role
!==
'ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin and Admin can create board templates'
);
}
const
board
=
await
this
.
prisma
.
board
.
findFirst
({
where
:
{
id
:
dto
.
boardId
,
deletedAt
:
null
},
include
:
{
columns
:
{
orderBy
:
{
position
:
'asc'
}
},
labels
:
{
where
:
{
boardId
:
dto
.
boardId
},
select
:
{
name
:
true
,
color
:
true
,
textColor
:
true
},
},
},
});
if
(
!
board
)
{
throw
new
NotFoundException
(
'Board not found'
);
}
const
boardConfig
=
{
visibility
:
board
.
visibility
,
allowContractorCreation
:
board
.
allowContractorCreation
,
autoArchiveDoneCardsDays
:
board
.
autoArchiveDoneCardsDays
,
deadlineExcludesHolidays
:
board
.
deadlineExcludesHolidays
,
columns
:
board
.
columns
.
map
((
col
)
=>
({
name
:
col
.
name
,
icon
:
col
.
icon
,
position
:
col
.
position
,
type
:
col
.
type
,
isDone
:
col
.
isDone
,
isDefault
:
col
.
isDefault
,
wipLimit
:
col
.
wipLimit
,
wipLimitTotal
:
col
.
wipLimitTotal
,
color
:
col
.
color
,
})),
};
const
labelConfig
=
(
board
as
any
).
labels
?.
map
((
l
:
any
)
=>
({
name
:
l
.
name
,
color
:
l
.
color
,
textColor
:
l
.
textColor
,
}))
||
[];
const
template
=
await
this
.
prisma
.
boardTemplate
.
create
({
data
:
{
name
:
dto
.
name
,
description
:
dto
.
description
||
null
,
boardConfig
,
labelConfig
,
createdById
:
currentUser
.
id
,
},
});
this
.
logger
.
log
(
`Board template "
${
dto
.
name
}
" created from board
${
dto
.
boardId
}
by
${
currentUser
.
email
}
`
);
return
template
;
}
async
findAll
(
currentUser
:
RequestUser
):
Promise
<
any
[]
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
&&
currentUser
.
role
!==
'ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin and Admin can view board templates'
);
}
return
this
.
prisma
.
boardTemplate
.
findMany
({
orderBy
:
{
createdAt
:
'desc'
},
include
:
{
createdBy
:
{
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
,
username
:
true
},
},
},
});
}
async
findById
(
id
:
string
,
currentUser
:
RequestUser
):
Promise
<
any
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
&&
currentUser
.
role
!==
'ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin and Admin can view board templates'
);
}
const
template
=
await
this
.
prisma
.
boardTemplate
.
findUnique
({
where
:
{
id
},
include
:
{
createdBy
:
{
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
,
username
:
true
},
},
},
});
if
(
!
template
)
{
throw
new
NotFoundException
(
'Board template not found'
);
}
return
template
;
}
async
update
(
id
:
string
,
dto
:
UpdateBoardTemplateDto
,
currentUser
:
RequestUser
):
Promise
<
any
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
&&
currentUser
.
role
!==
'ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin and Admin can edit board templates'
);
}
const
template
=
await
this
.
prisma
.
boardTemplate
.
findUnique
({
where
:
{
id
}
});
if
(
!
template
)
{
throw
new
NotFoundException
(
'Board template not found'
);
}
const
updateData
:
any
=
{};
if
(
dto
.
name
!==
undefined
)
updateData
.
name
=
dto
.
name
;
if
(
dto
.
description
!==
undefined
)
updateData
.
description
=
dto
.
description
;
return
this
.
prisma
.
boardTemplate
.
update
({
where
:
{
id
},
data
:
updateData
,
});
}
async
delete
(
id
:
string
,
currentUser
:
RequestUser
):
Promise
<
void
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
&&
currentUser
.
role
!==
'ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin and Admin can delete board templates'
);
}
const
template
=
await
this
.
prisma
.
boardTemplate
.
findUnique
({
where
:
{
id
}
});
if
(
!
template
)
{
throw
new
NotFoundException
(
'Board template not found'
);
}
await
this
.
prisma
.
boardTemplate
.
delete
({
where
:
{
id
}
});
this
.
logger
.
log
(
`Board template
${
id
}
deleted by
${
currentUser
.
email
}
`
);
}
async
getTemplateConfig
(
templateId
:
string
):
Promise
<
{
boardConfig
:
any
;
labelConfig
:
any
}
>
{
const
template
=
await
this
.
prisma
.
boardTemplate
.
findUnique
({
where
:
{
id
:
templateId
}
});
if
(
!
template
)
{
throw
new
NotFoundException
(
'Board template not found'
);
}
return
{
boardConfig
:
template
.
boardConfig
as
any
,
labelConfig
:
template
.
labelConfig
as
any
,
};
}
}
\ No newline at end of file
backend/src/modules/boards/boards.controller.ts
0 → 100644
View file @
ba81ccf6
import
{
Controller
,
Get
,
Post
,
Put
,
Delete
,
Body
,
Param
,
Query
,
HttpCode
,
HttpStatus
,
}
from
'@nestjs/common'
;
import
{
BoardsService
}
from
'./boards.service'
;
import
{
BoardTemplateService
}
from
'./board-template.service'
;
import
{
CreateBoardDto
}
from
'./dto/create-board.dto'
;
import
{
UpdateBoardDto
}
from
'./dto/update-board.dto'
;
import
{
BoardFilterDto
}
from
'./dto/board-filter.dto'
;
import
{
AddBoardMemberDto
,
AddBoardMembersBulkDto
,
UpdateBoardMemberRoleDto
,
}
from
'./dto/board-member.dto'
;
import
{
CreateBoardTemplateDto
,
UpdateBoardTemplateDto
}
from
'./dto/board-template.dto'
;
import
{
Roles
}
from
'../../common/decorators/roles.decorator'
;
import
{
CurrentUser
,
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
@
Controller
(
'boards'
)
export
class
BoardsController
{
constructor
(
private
readonly
boardsService
:
BoardsService
,
private
readonly
boardTemplateService
:
BoardTemplateService
,
)
{}
// ─── BOARD CRUD ──────────────────────────────────────────
@
Post
()
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
create
(@
Body
()
dto
:
CreateBoardDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
boardsService
.
create
(
dto
,
user
);
}
@
Get
()
async
findAll
(@
Query
()
filter
:
BoardFilterDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
boardsService
.
findAll
(
filter
,
user
);
}
@
Get
(
':id'
)
async
findById
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
boardsService
.
findById
(
id
,
user
);
}
@
Put
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
update
(
@
Param
(
'id'
)
id
:
string
,
@
Body
()
dto
:
UpdateBoardDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
boardsService
.
update
(
id
,
dto
,
user
);
}
@
Post
(
':id/archive'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
archive
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
await
this
.
boardsService
.
archive
(
id
,
user
);
return
{
message
:
'Board archived'
};
}
@
Post
(
':id/restore'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
restore
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
await
this
.
boardsService
.
restore
(
id
,
user
);
return
{
message
:
'Board restored'
};
}
@
Delete
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
permanentDelete
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
await
this
.
boardsService
.
permanentDelete
(
id
,
user
);
return
{
message
:
'Board permanently deleted'
};
}
// ─── MEMBER MANAGEMENT ──────────────────────────────────
@
Get
(
':id/members'
)
async
getMembers
(@
Param
(
'id'
)
id
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
boardsService
.
getMembers
(
id
,
user
);
}
@
Post
(
':id/members'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
addMember
(
@
Param
(
'id'
)
id
:
string
,
@
Body
()
dto
:
AddBoardMemberDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
boardsService
.
addMember
(
id
,
dto
,
user
);
}
@
Post
(
':id/members/bulk'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
addMembersBulk
(
@
Param
(
'id'
)
id
:
string
,
@
Body
()
dto
:
AddBoardMembersBulkDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
boardsService
.
addMembersBulk
(
id
,
dto
,
user
);
}
@
Put
(
':id/members/:userId/role'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
updateMemberRole
(
@
Param
(
'id'
)
id
:
string
,
@
Param
(
'userId'
)
userId
:
string
,
@
Body
()
dto
:
UpdateBoardMemberRoleDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
boardsService
.
updateMemberRole
(
id
,
userId
,
dto
,
user
);
}
@
Delete
(
':id/members/:userId'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
removeMember
(
@
Param
(
'id'
)
id
:
string
,
@
Param
(
'userId'
)
userId
:
string
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
await
this
.
boardsService
.
removeMember
(
id
,
userId
,
user
);
return
{
message
:
'Member removed from board'
};
}
// ─── TEMPLATES ──────────────────────────────────────────
@
Post
(
'templates'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
saveAsTemplate
(@
Body
()
dto
:
CreateBoardTemplateDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
boardTemplateService
.
saveAsTemplate
(
dto
,
user
);
}
@
Get
(
'templates/list'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
listTemplates
(@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
boardTemplateService
.
findAll
(
user
);
}
@
Get
(
'templates/:templateId'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
getTemplate
(@
Param
(
'templateId'
)
templateId
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
boardTemplateService
.
findById
(
templateId
,
user
);
}
@
Put
(
'templates/:templateId'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
updateTemplate
(
@
Param
(
'templateId'
)
templateId
:
string
,
@
Body
()
dto
:
UpdateBoardTemplateDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
boardTemplateService
.
update
(
templateId
,
dto
,
user
);
}
@
Delete
(
'templates/:templateId'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
deleteTemplate
(@
Param
(
'templateId'
)
templateId
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
await
this
.
boardTemplateService
.
delete
(
templateId
,
user
);
return
{
message
:
'Board template deleted'
};
}
}
\ No newline at end of file
backend/src/modules/boards/boards.module.ts
0 → 100644
View file @
ba81ccf6
import
{
Module
}
from
'@nestjs/common'
;
import
{
BoardsController
}
from
'./boards.controller'
;
import
{
BoardsService
}
from
'./boards.service'
;
import
{
BoardTemplateService
}
from
'./board-template.service'
;
@
Module
({
controllers
:
[
BoardsController
],
providers
:
[
BoardsService
,
BoardTemplateService
],
exports
:
[
BoardsService
,
BoardTemplateService
],
})
export
class
BoardsModule
{}
\ No newline at end of file
backend/src/modules/boards/boards.service.ts
0 → 100644
View file @
ba81ccf6
This diff is collapsed.
Click to expand it.
backend/src/modules/boards/dto/board-filter.dto.ts
0 → 100644
View file @
ba81ccf6
import
{
IsOptional
,
IsString
,
IsBoolean
}
from
'class-validator'
;
import
{
Type
}
from
'class-transformer'
;
import
{
PaginationDto
}
from
'../../../common/dto/pagination.dto'
;
export
class
BoardFilterDto
extends
PaginationDto
{
@
IsOptional
()
@
IsString
()
visibility
?:
string
;
@
IsOptional
()
@
Type
(()
=>
Boolean
)
@
IsBoolean
()
isArchived
?:
boolean
;
@
IsOptional
()
@
IsString
()
memberId
?:
string
;
}
\ No newline at end of file
backend/src/modules/boards/dto/board-member.dto.ts
0 → 100644
View file @
ba81ccf6
import
{
IsString
,
IsOptional
,
IsEnum
,
IsArray
}
from
'class-validator'
;
export
class
AddBoardMemberDto
{
@
IsString
()
userId
:
string
;
@
IsOptional
()
@
IsString
()
role
?:
string
;
}
export
class
AddBoardMembersBulkDto
{
@
IsArray
()
@
IsString
({
each
:
true
})
userIds
:
string
[];
@
IsOptional
()
@
IsString
()
role
?:
string
;
}
export
class
UpdateBoardMemberRoleDto
{
@
IsString
()
role
:
string
;
}
export
class
RemoveBoardMemberDto
{
@
IsString
()
userId
:
string
;
}
\ No newline at end of file
backend/src/modules/boards/dto/board-response.dto.ts
0 → 100644
View file @
ba81ccf6
export
class
BoardSummaryResponseDto
{
id
:
string
;
name
:
string
;
description
:
string
|
null
;
key
:
string
;
visibility
:
string
;
color
:
string
|
null
;
icon
:
string
|
null
;
isArchived
:
boolean
;
memberCount
:
number
;
cardCount
:
number
;
allowContractorCreation
:
boolean
;
autoArchiveDoneCardsDays
:
number
;
deadlineExcludesHolidays
:
boolean
;
createdById
:
string
;
createdAt
:
string
;
updatedAt
:
string
;
}
export
class
BoardDetailResponseDto
extends
BoardSummaryResponseDto
{
columns
:
ColumnResponseDto
[];
members
:
BoardMemberResponseDto
[];
}
export
class
ColumnResponseDto
{
id
:
string
;
name
:
string
;
icon
:
string
|
null
;
position
:
number
;
type
:
string
;
isDone
:
boolean
;
isDefault
:
boolean
;
wipLimit
:
number
|
null
;
wipLimitTotal
:
number
|
null
;
color
:
string
|
null
;
cardCount
:
number
;
}
export
class
BoardMemberResponseDto
{
id
:
string
;
userId
:
string
;
role
:
string
;
joinedAt
:
string
;
user
:
{
id
:
string
;
firstName
:
string
;
lastName
:
string
;
displayName
:
string
|
null
;
avatar
:
string
|
null
;
role
:
string
;
};
}
\ No newline at end of file
backend/src/modules/boards/dto/board-template.dto.ts
0 → 100644
View file @
ba81ccf6
import
{
IsString
,
IsOptional
,
MinLength
,
MaxLength
}
from
'class-validator'
;
export
class
CreateBoardTemplateDto
{
@
IsString
()
@
MinLength
(
1
)
@
MaxLength
(
100
)
name
:
string
;
@
IsOptional
()
@
IsString
()
@
MaxLength
(
500
)
description
?:
string
;
@
IsString
()
boardId
:
string
;
}
export
class
UpdateBoardTemplateDto
{
@
IsOptional
()
@
IsString
()
@
MinLength
(
1
)
@
MaxLength
(
100
)
name
?:
string
;
@
IsOptional
()
@
IsString
()
@
MaxLength
(
500
)
description
?:
string
;
}
\ No newline at end of file
backend/src/modules/boards/dto/create-board.dto.ts
0 → 100644
View file @
ba81ccf6
import
{
IsString
,
IsOptional
,
IsBoolean
,
IsInt
,
IsArray
,
MinLength
,
MaxLength
,
Matches
,
Min
,
Max
,
}
from
'class-validator'
;
export
class
CreateBoardDto
{
@
IsString
()
@
MinLength
(
1
)
@
MaxLength
(
100
)
name
:
string
;
@
IsOptional
()
@
IsString
()
@
MaxLength
(
500
)
description
?:
string
;
@
IsOptional
()
@
IsString
()
@
MinLength
(
2
)
@
MaxLength
(
10
)
@
Matches
(
/^
[
A-Z0-9_
]
+$/
,
{
message
:
'Board key must be uppercase alphanumeric with underscores'
})
key
?:
string
;
@
IsOptional
()
@
IsString
()
visibility
?:
string
;
@
IsOptional
()
@
IsString
()
color
?:
string
;
@
IsOptional
()
@
IsString
()
icon
?:
string
;
@
IsOptional
()
@
IsBoolean
()
allowContractorCreation
?:
boolean
;
@
IsOptional
()
@
IsInt
()
@
Min
(
1
)
@
Max
(
365
)
autoArchiveDoneCardsDays
?:
number
;
@
IsOptional
()
@
IsBoolean
()
deadlineExcludesHolidays
?:
boolean
;
@
IsOptional
()
@
IsArray
()
@
IsString
({
each
:
true
})
memberUserIds
?:
string
[];
@
IsOptional
()
@
IsString
()
templateId
?:
string
;
}
\ No newline at end of file
backend/src/modules/boards/dto/update-board.dto.ts
0 → 100644
View file @
ba81ccf6
import
{
IsString
,
IsOptional
,
IsBoolean
,
IsInt
,
MinLength
,
MaxLength
,
Matches
,
Min
,
Max
,
}
from
'class-validator'
;
export
class
UpdateBoardDto
{
@
IsOptional
()
@
IsString
()
@
MinLength
(
1
)
@
MaxLength
(
100
)
name
?:
string
;
@
IsOptional
()
@
IsString
()
@
MaxLength
(
500
)
description
?:
string
;
@
IsOptional
()
@
IsString
()
@
MinLength
(
2
)
@
MaxLength
(
10
)
@
Matches
(
/^
[
A-Z0-9_
]
+$/
,
{
message
:
'Board key must be uppercase alphanumeric with underscores'
})
key
?:
string
;
@
IsOptional
()
@
IsString
()
visibility
?:
string
;
@
IsOptional
()
@
IsString
()
color
?:
string
;
@
IsOptional
()
@
IsString
()
icon
?:
string
;
@
IsOptional
()
@
IsBoolean
()
allowContractorCreation
?:
boolean
;
@
IsOptional
()
@
IsInt
()
@
Min
(
1
)
@
Max
(
365
)
autoArchiveDoneCardsDays
?:
number
;
@
IsOptional
()
@
IsBoolean
()
deadlineExcludesHolidays
?:
boolean
;
}
\ No newline at end of file
backend/src/modules/columns/columns.controller.ts
0 → 100644
View file @
ba81ccf6
import
{
Controller
,
Get
,
Post
,
Put
,
Delete
,
Body
,
Param
,
Query
,
HttpCode
,
HttpStatus
,
}
from
'@nestjs/common'
;
import
{
ColumnsService
}
from
'./columns.service'
;
import
{
CreateColumnDto
}
from
'./dto/create-column.dto'
;
import
{
UpdateColumnDto
,
ReorderColumnsDto
,
DeleteColumnDto
}
from
'./dto/update-column.dto'
;
import
{
Roles
}
from
'../../common/decorators/roles.decorator'
;
import
{
CurrentUser
,
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
@
Controller
(
'columns'
)
export
class
ColumnsController
{
constructor
(
private
readonly
columnsService
:
ColumnsService
)
{}
@
Get
(
'board/:boardId'
)
async
findByBoard
(@
Param
(
'boardId'
)
boardId
:
string
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
columnsService
.
findByBoard
(
boardId
,
user
);
}
@
Post
()
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
createCustomColumn
(@
Body
()
dto
:
CreateColumnDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
columnsService
.
createCustomColumn
(
dto
,
user
);
}
@
Put
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
update
(
@
Param
(
'id'
)
id
:
string
,
@
Body
()
dto
:
UpdateColumnDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
return
this
.
columnsService
.
update
(
id
,
dto
,
user
);
}
@
Delete
(
':id'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
deleteCustomColumn
(
@
Param
(
'id'
)
id
:
string
,
@
Body
()
dto
:
DeleteColumnDto
,
@
CurrentUser
()
user
:
RequestUser
,
)
{
await
this
.
columnsService
.
deleteCustomColumn
(
id
,
dto
,
user
);
return
{
message
:
'Column deleted and cards migrated'
};
}
@
Post
(
'reorder'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
reorderColumns
(@
Body
()
dto
:
ReorderColumnsDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
columnsService
.
reorderColumns
(
dto
,
user
);
}
}
\ No newline at end of file
backend/src/modules/columns/columns.module.ts
0 → 100644
View file @
ba81ccf6
import
{
Module
}
from
'@nestjs/common'
;
import
{
ColumnsController
}
from
'./columns.controller'
;
import
{
ColumnsService
}
from
'./columns.service'
;
@
Module
({
controllers
:
[
ColumnsController
],
providers
:
[
ColumnsService
],
exports
:
[
ColumnsService
],
})
export
class
ColumnsModule
{}
\ No newline at end of file
backend/src/modules/columns/columns.service.ts
0 → 100644
View file @
ba81ccf6
This diff is collapsed.
Click to expand it.
backend/src/modules/columns/dto/create-column.dto.ts
0 → 100644
View file @
ba81ccf6
import
{
IsString
,
IsOptional
,
IsInt
,
Min
,
Max
,
MinLength
,
MaxLength
}
from
'class-validator'
;
export
class
CreateColumnDto
{
@
IsString
()
boardId
:
string
;
@
IsString
()
@
MinLength
(
1
)
@
MaxLength
(
50
)
name
:
string
;
@
IsOptional
()
@
IsString
()
icon
?:
string
;
@
IsOptional
()
@
IsInt
()
@
Min
(
0
)
wipLimit
?:
number
;
@
IsOptional
()
@
IsInt
()
@
Min
(
0
)
wipLimitTotal
?:
number
;
@
IsOptional
()
@
IsString
()
color
?:
string
;
}
\ No newline at end of file
backend/src/modules/columns/dto/update-column.dto.ts
0 → 100644
View file @
ba81ccf6
import
{
IsString
,
IsOptional
,
IsInt
,
Min
,
MaxLength
,
IsArray
}
from
'class-validator'
;
export
class
UpdateColumnDto
{
@
IsOptional
()
@
IsString
()
@
MaxLength
(
50
)
name
?:
string
;
@
IsOptional
()
@
IsString
()
icon
?:
string
;
@
IsOptional
()
@
IsInt
()
@
Min
(
0
)
wipLimit
?:
number
;
@
IsOptional
()
@
IsInt
()
@
Min
(
0
)
wipLimitTotal
?:
number
;
@
IsOptional
()
@
IsString
()
color
?:
string
;
}
export
class
ReorderColumnsDto
{
@
IsString
()
boardId
:
string
;
@
IsArray
()
@
IsString
({
each
:
true
})
columnIds
:
string
[];
}
export
class
DeleteColumnDto
{
@
IsString
()
migrateCardsToColumnId
:
string
;
}
\ No newline at end of file
prisma/schema-boards.prisma
0 → 100644
View file @
ba81ccf6
//
============================================================
//
BOARDS
+
COLUMNS
—
Add
these
models
to
your
main
schema
.
prisma
//
============================================================
model
Board
{
id
String
@
id
@
default
(
uuid
())
name
String
description
String
?
key
String
@
unique
//
e
.
g
.
"PROJ"
—
used
for
card
numbering
visibility
String
@
default
(
"PRIVATE"
)
//
PUBLIC
,
PRIVATE
,
TEAM
color
String
?
icon
String
?
allowContractorCreation
Boolean
@
default
(
true
)
autoArchiveDoneCardsDays
Int
@
default
(
30
)
deadlineExcludesHolidays
Boolean
@
default
(
false
)
nextCardNumber
Int
@
default
(
1
)
isArchived
Boolean
@
default
(
false
)
archivedAt
DateTime
?
deletedAt
DateTime
?
createdById
String
createdBy
User
@
relation
(
"BoardCreator"
,
fields
:
[
createdById
],
references
:
[
id
],
onDelete
:
RESTRICT
)
columns
Column
[]
members
BoardMember
[]
labels
Label
[]
@
relation
(
"BoardLabels"
)
createdAt
DateTime
@
default
(
now
())
updatedAt
DateTime
@
updatedAt
@@
index
([
createdById
])
@@
index
([
isArchived
])
@@
index
([
deletedAt
])
@@
index
([
name
])
}
model
Column
{
id
String
@
id
@
default
(
uuid
())
boardId
String
board
Board
@
relation
(
fields
:
[
boardId
],
references
:
[
id
],
onDelete
:
CASCADE
)
name
String
icon
String
?
position
Int
type
String
//
BACKLOG
,
TODO
,
DOING
,
FROZEN
,
IN_REVIEW
,
DONE
,
CUSTOM
isDone
Boolean
@
default
(
false
)
isDefault
Boolean
@
default
(
true
)
wipLimit
Int
?
//
per
-
user
WIP
limit
wipLimitTotal
Int
?
//
total
WIP
limit
for
the
column
color
String
?
createdAt
DateTime
@
default
(
now
())
updatedAt
DateTime
@
updatedAt
@@
index
([
boardId
])
@@
index
([
boardId
,
position
])
}
model
BoardMember
{
id
String
@
id
@
default
(
uuid
())
boardId
String
board
Board
@
relation
(
fields
:
[
boardId
],
references
:
[
id
],
onDelete
:
CASCADE
)
userId
String
user
User
@
relation
(
fields
:
[
userId
],
references
:
[
id
],
onDelete
:
CASCADE
)
role
String
@
default
(
"MEMBER"
)
//
OWNER
,
ADMIN
,
MEMBER
,
VIEWER
joinedAt
DateTime
@
default
(
now
())
createdAt
DateTime
@
default
(
now
())
updatedAt
DateTime
@
updatedAt
@@
unique
([
boardId
,
userId
])
@@
index
([
userId
])
@@
index
([
boardId
])
}
model
BoardTemplate
{
id
String
@
id
@
default
(
uuid
())
name
String
description
String
?
boardConfig
Json
//
column
config
,
board
settings
labelConfig
Json
?
//
label
definitions
createdById
String
createdBy
User
@
relation
(
"BoardTemplateCreator"
,
fields
:
[
createdById
],
references
:
[
id
],
onDelete
:
RESTRICT
)
createdAt
DateTime
@
default
(
now
())
updatedAt
DateTime
@
updatedAt
@@
index
([
createdById
])
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment