Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
finwise-miniapp-be
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
ThinhNC
finwise-miniapp-be
Commits
8e152a7a
Commit
8e152a7a
authored
Jul 02, 2026
by
ThinhNC
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: CURD Intern
parent
54175469
Changes
12
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
675 additions
and
17 deletions
+675
-17
migration.sql
...702152557_add_soft_delete_to_intern_profile/migration.sql
+2
-0
migration.sql
...60702153821_rename_intern_profile_to_intern/migration.sql
+65
-0
schema.prisma
prisma/schema.prisma
+9
-8
seed.ts
prisma/seed.ts
+44
-5
swagger.config.ts
src/config/swagger.config.ts
+165
-4
intern.controller.ts
src/modules/interns/intern.controller.ts
+58
-0
intern.dto.ts
src/modules/interns/intern.dto.ts
+43
-0
intern.repository.ts
src/modules/interns/intern.repository.ts
+128
-0
intern.route.ts
src/modules/interns/intern.route.ts
+53
-0
intern.service.ts
src/modules/interns/intern.service.ts
+52
-0
intern.validation.ts
src/modules/interns/intern.validation.ts
+54
-0
index.ts
src/routes/index.ts
+2
-0
No files found.
prisma/migrations/20260702152557_add_soft_delete_to_intern_profile/migration.sql
0 → 100644
View file @
8e152a7a
-- AlterTable
ALTER
TABLE
"intern_profiles"
ADD
COLUMN
"deleted_at"
TIMESTAMP
(
3
);
prisma/migrations/20260702153821_rename_intern_profile_to_intern/migration.sql
0 → 100644
View file @
8e152a7a
/*
Warnings:
- You are about to drop the `intern_profiles` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER
TABLE
"daily_reports"
DROP
CONSTRAINT
"daily_reports_intern_id_fkey"
;
-- DropForeignKey
ALTER
TABLE
"intern_profiles"
DROP
CONSTRAINT
"intern_profiles_leader_id_fkey"
;
-- DropForeignKey
ALTER
TABLE
"intern_profiles"
DROP
CONSTRAINT
"intern_profiles_user_id_fkey"
;
-- DropForeignKey
ALTER
TABLE
"task_assignments"
DROP
CONSTRAINT
"task_assignments_intern_id_fkey"
;
-- DropForeignKey
ALTER
TABLE
"weekly_evaluations"
DROP
CONSTRAINT
"weekly_evaluations_intern_id_fkey"
;
-- DropTable
DROP
TABLE
"intern_profiles"
;
-- CreateTable
CREATE
TABLE
"interns"
(
"id"
UUID
NOT
NULL
,
"user_id"
UUID
NOT
NULL
,
"leader_id"
UUID
,
"full_name"
TEXT
NOT
NULL
,
"phone"
TEXT
NOT
NULL
,
"department"
TEXT
NOT
NULL
,
"position"
TEXT
NOT
NULL
,
"start_date"
TIMESTAMP
(
3
)
NOT
NULL
,
"duration"
INTEGER
NOT
NULL
,
"discord_username"
TEXT
,
"discord_role_granted"
BOOLEAN
NOT
NULL
DEFAULT
false
,
"status"
"InternStatus"
NOT
NULL
DEFAULT
'ACTIVE'
,
"deleted_at"
TIMESTAMP
(
3
),
"created_at"
TIMESTAMP
(
3
)
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
,
"updated_at"
TIMESTAMP
(
3
)
NOT
NULL
,
CONSTRAINT
"interns_pkey"
PRIMARY
KEY
(
"id"
)
);
-- CreateIndex
CREATE
UNIQUE
INDEX
"interns_user_id_key"
ON
"interns"
(
"user_id"
);
-- CreateIndex
CREATE
INDEX
"interns_leader_id_idx"
ON
"interns"
(
"leader_id"
);
-- AddForeignKey
ALTER
TABLE
"interns"
ADD
CONSTRAINT
"interns_user_id_fkey"
FOREIGN
KEY
(
"user_id"
)
REFERENCES
"users"
(
"id"
)
ON
DELETE
CASCADE
ON
UPDATE
CASCADE
;
-- AddForeignKey
ALTER
TABLE
"interns"
ADD
CONSTRAINT
"interns_leader_id_fkey"
FOREIGN
KEY
(
"leader_id"
)
REFERENCES
"users"
(
"id"
)
ON
DELETE
SET
NULL
ON
UPDATE
CASCADE
;
-- AddForeignKey
ALTER
TABLE
"task_assignments"
ADD
CONSTRAINT
"task_assignments_intern_id_fkey"
FOREIGN
KEY
(
"intern_id"
)
REFERENCES
"interns"
(
"id"
)
ON
DELETE
CASCADE
ON
UPDATE
CASCADE
;
-- AddForeignKey
ALTER
TABLE
"daily_reports"
ADD
CONSTRAINT
"daily_reports_intern_id_fkey"
FOREIGN
KEY
(
"intern_id"
)
REFERENCES
"interns"
(
"id"
)
ON
DELETE
CASCADE
ON
UPDATE
CASCADE
;
-- AddForeignKey
ALTER
TABLE
"weekly_evaluations"
ADD
CONSTRAINT
"weekly_evaluations_intern_id_fkey"
FOREIGN
KEY
(
"intern_id"
)
REFERENCES
"interns"
(
"id"
)
ON
DELETE
CASCADE
ON
UPDATE
CASCADE
;
prisma/schema.prisma
View file @
8e152a7a
...
...
@@ -38,8 +38,8 @@ model User {
//
Relations
approvedApplications
Application
[]
@
relation
(
"ApprovedApplications"
)
intern
Profile
InternProfile
?
@
relation
(
"InternUser"
)
leadedInterns
Intern
Profile
[]
@
relation
(
"LeaderInterns"
)
intern
Intern
?
@
relation
(
"InternUser"
)
leadedInterns
Intern
[]
@
relation
(
"LeaderInterns"
)
notificationSetting
NotificationSetting
?
createdTasks
Task
[]
@
relation
(
"CreatedTasks"
)
assignedTasks
TaskAssignment
[]
@
relation
(
"AssignedTasks"
)
...
...
@@ -81,14 +81,14 @@ model Application {
@@
map
(
"applications"
)
}
//
4.
Intern
Profile
//
4.
Intern
enum
InternStatus
{
ACTIVE
COMPLETED
DROPPED
}
model
Intern
Profile
{
model
Intern
{
id
String
@
id
@
default
(
uuid
())
@
db
.
Uuid
userId
String
@
unique
@
map
(
"user_id"
)
@
db
.
Uuid
leaderId
String
?
@
map
(
"leader_id"
)
@
db
.
Uuid
...
...
@@ -101,6 +101,7 @@ model InternProfile {
discordUsername
String
?
@
map
(
"discord_username"
)
discordRoleGranted
Boolean
@
default
(
false
)
@
map
(
"discord_role_granted"
)
status
InternStatus
@
default
(
ACTIVE
)
deletedAt
DateTime
?
@
map
(
"deleted_at"
)
//
Soft
delete
createdAt
DateTime
@
default
(
now
())
@
map
(
"created_at"
)
updatedAt
DateTime
@
updatedAt
@
map
(
"updated_at"
)
...
...
@@ -113,7 +114,7 @@ model InternProfile {
weeklyEvaluations
WeeklyEvaluation
[]
@@
index
([
leaderId
])
@@
map
(
"intern
_profile
s"
)
@@
map
(
"interns"
)
}
//
5.
NotificationSetting
...
...
@@ -173,7 +174,7 @@ model TaskAssignment {
updatedAt
DateTime
@
updatedAt
@
map
(
"updated_at"
)
task
Task
@
relation
(
fields
:
[
taskId
],
references
:
[
id
],
onDelete
:
Cascade
)
intern
Intern
Profile
@
relation
(
fields
:
[
internId
],
references
:
[
id
],
onDelete
:
Cascade
)
intern
Intern
@
relation
(
fields
:
[
internId
],
references
:
[
id
],
onDelete
:
Cascade
)
assigner
User
@
relation
(
"AssignedTasks"
,
fields
:
[
assignedBy
],
references
:
[
id
],
onDelete
:
Cascade
)
submissions
TaskSubmission
[]
...
...
@@ -221,7 +222,7 @@ model DailyReport {
createdAt
DateTime
@
default
(
now
())
@
map
(
"created_at"
)
updatedAt
DateTime
@
updatedAt
@
map
(
"updated_at"
)
intern
Intern
Profile
@
relation
(
fields
:
[
internId
],
references
:
[
id
],
onDelete
:
Cascade
)
intern
Intern
@
relation
(
fields
:
[
internId
],
references
:
[
id
],
onDelete
:
Cascade
)
@@
index
([
internId
])
@@
map
(
"daily_reports"
)
...
...
@@ -242,7 +243,7 @@ model WeeklyEvaluation {
createdAt
DateTime
@
default
(
now
())
@
map
(
"created_at"
)
updatedAt
DateTime
@
updatedAt
@
map
(
"updated_at"
)
intern
Intern
Profile
@
relation
(
fields
:
[
internId
],
references
:
[
id
],
onDelete
:
Cascade
)
intern
Intern
@
relation
(
fields
:
[
internId
],
references
:
[
id
],
onDelete
:
Cascade
)
leader
User
@
relation
(
"LeaderEvaluations"
,
fields
:
[
leaderId
],
references
:
[
id
],
onDelete
:
Cascade
)
@@
unique
([
internId
,
week
])
...
...
prisma/seed.ts
View file @
8e152a7a
...
...
@@ -4,7 +4,6 @@ import bcrypt from 'bcryptjs';
const
prisma
=
new
PrismaClient
();
async
function
main
()
{
// 1. Seed Roles
const
roles
=
[
'ADMIN'
,
'LEADER'
,
'INTERN'
];
const
roleMap
:
Record
<
string
,
string
>
=
{};
...
...
@@ -18,26 +17,66 @@ async function main() {
console
.
log
(
`Role
${
roleName
}
upserted with ID
${
role
.
id
}
`
);
}
// 2. Seed default admin User
const
adminPassword
=
await
bcrypt
.
hash
(
'Admin@123456'
,
10
);
const
adminEmail
=
'admin@nexcampus.local'
;
const
adminPassword
=
await
bcrypt
.
hash
(
'Admin@123456'
,
10
);
const
leaderEmail
=
'leader@nexcampus.local'
;
const
leaderPassword
=
await
bcrypt
.
hash
(
'Leader@123456'
,
10
);
const
adminUser
=
await
prisma
.
user
.
upsert
({
const
internEmail
=
'intern@nexcampus.local'
;
const
internPassword
=
await
bcrypt
.
hash
(
'Intern@123456'
,
10
);
await
prisma
.
user
.
upsert
({
where
:
{
email
:
adminEmail
},
update
:
{
password
:
adminPassword
,
fullName
:
'Admin'
,
roleId
:
roleMap
[
'ADMIN'
],
isActive
:
true
,
},
create
:
{
email
:
adminEmail
,
password
:
adminPassword
,
fullName
:
'Admin'
,
roleId
:
roleMap
[
'ADMIN'
],
isActive
:
true
,
},
});
console
.
log
(
`Default admin user seeded:
${
adminUser
.
email
}
`
);
await
prisma
.
user
.
upsert
({
where
:
{
email
:
leaderEmail
},
update
:
{
password
:
leaderPassword
,
fullName
:
'Leader'
,
roleId
:
roleMap
[
'LEADER'
],
isActive
:
true
,
},
create
:
{
email
:
leaderEmail
,
password
:
leaderPassword
,
fullName
:
'Leader'
,
roleId
:
roleMap
[
'LEADER'
],
isActive
:
true
,
},
});
await
prisma
.
user
.
upsert
({
where
:
{
email
:
internEmail
},
update
:
{
password
:
internPassword
,
fullName
:
'Intern'
,
roleId
:
roleMap
[
'INTERN'
],
isActive
:
true
,
},
create
:
{
email
:
internEmail
,
password
:
internPassword
,
fullName
:
'Intern'
,
roleId
:
roleMap
[
'INTERN'
],
isActive
:
true
,
},
});
console
.
log
(
'Seed completed successfully'
);
}
...
...
src/config/swagger.config.ts
View file @
8e152a7a
This diff is collapsed.
Click to expand it.
src/modules/interns/intern.controller.ts
0 → 100644
View file @
8e152a7a
import
{
Request
,
Response
,
NextFunction
}
from
'express'
;
import
{
InternService
}
from
'./intern.service'
;
import
{
InternQueryDto
}
from
'./intern.dto'
;
export
class
InternController
{
private
readonly
service
=
new
InternService
();
findAll
=
async
(
req
:
Request
,
res
:
Response
,
next
:
NextFunction
)
=>
{
try
{
const
query
=
req
.
query
as
unknown
as
InternQueryDto
;
const
result
=
await
this
.
service
.
findAll
(
query
);
res
.
json
({
success
:
true
,
...
result
});
}
catch
(
error
)
{
next
(
error
);
}
};
findById
=
async
(
req
:
Request
,
res
:
Response
,
next
:
NextFunction
)
=>
{
try
{
const
result
=
await
this
.
service
.
findById
(
req
.
params
.
id
);
res
.
json
({
success
:
true
,
data
:
result
});
}
catch
(
error
)
{
next
(
error
);
}
};
create
=
async
(
req
:
Request
,
res
:
Response
,
next
:
NextFunction
)
=>
{
try
{
const
result
=
await
this
.
service
.
create
(
req
.
body
);
res
.
status
(
201
).
json
({
success
:
true
,
data
:
result
});
}
catch
(
error
)
{
next
(
error
);
}
};
update
=
async
(
req
:
Request
,
res
:
Response
,
next
:
NextFunction
)
=>
{
try
{
const
result
=
await
this
.
service
.
update
(
req
.
params
.
id
,
req
.
body
);
res
.
json
({
success
:
true
,
data
:
result
});
}
catch
(
error
)
{
next
(
error
);
}
};
delete
=
async
(
req
:
Request
,
res
:
Response
,
next
:
NextFunction
)
=>
{
try
{
await
this
.
service
.
delete
(
req
.
params
.
id
);
res
.
json
({
success
:
true
,
message
:
'Intern deleted successfully'
});
}
catch
(
error
)
{
next
(
error
);
}
};
}
src/modules/interns/intern.dto.ts
0 → 100644
View file @
8e152a7a
import
{
InternStatus
}
from
'@prisma/client'
;
export
interface
InternQueryDto
{
fullName
?:
string
;
department
?:
string
;
position
?:
string
;
status
?:
InternStatus
;
leaderId
?:
string
;
discordRoleGranted
?:
boolean
;
startDateFrom
?:
string
;
startDateTo
?:
string
;
sortBy
?:
'createdAt'
|
'fullName'
|
'startDate'
|
'status'
;
order
?:
'asc'
|
'desc'
;
page
?:
number
;
limit
?:
number
;
}
export
interface
CreateInternDto
{
userId
:
string
;
leaderId
?:
string
;
fullName
:
string
;
phone
:
string
;
department
:
string
;
position
:
string
;
startDate
:
string
;
duration
:
number
;
discordUsername
?:
string
;
}
export
interface
UpdateInternDto
{
leaderId
?:
string
|
null
;
fullName
?:
string
;
phone
?:
string
;
department
?:
string
;
position
?:
string
;
startDate
?:
string
;
duration
?:
number
;
discordUsername
?:
string
|
null
;
discordRoleGranted
?:
boolean
;
status
?:
InternStatus
;
}
src/modules/interns/intern.repository.ts
0 → 100644
View file @
8e152a7a
import
{
InternStatus
,
Prisma
}
from
'@prisma/client'
;
import
{
prisma
}
from
'../../database/prisma.client'
;
import
{
InternQueryDto
,
CreateInternDto
,
UpdateInternDto
}
from
'./intern.dto'
;
const
userSelect
=
{
id
:
true
,
email
:
true
,
fullName
:
true
,
isActive
:
true
,
};
const
defaultInclude
=
{
user
:
{
select
:
userSelect
},
leader
:
{
select
:
userSelect
},
};
export
class
InternRepository
{
async
findAll
(
query
:
InternQueryDto
)
{
const
{
fullName
,
department
,
position
,
status
,
leaderId
,
discordRoleGranted
,
startDateFrom
,
startDateTo
,
sortBy
=
'createdAt'
,
order
=
'desc'
,
page
=
1
,
limit
=
20
,
}
=
query
;
const
where
:
Prisma
.
InternWhereInput
=
{
deletedAt
:
null
,
...(
fullName
?
{
fullName
:
{
contains
:
fullName
,
mode
:
'insensitive'
}
}
:
{}),
...(
department
?
{
department
:
{
contains
:
department
,
mode
:
'insensitive'
}
}
:
{}),
...(
position
?
{
position
:
{
contains
:
position
,
mode
:
'insensitive'
}
}
:
{}),
...(
status
?
{
status
}
:
{}),
...(
leaderId
?
{
leaderId
}
:
{}),
...(
discordRoleGranted
!==
undefined
?
{
discordRoleGranted
}
:
{}),
...(
startDateFrom
||
startDateTo
?
{
startDate
:
{
...(
startDateFrom
?
{
gte
:
new
Date
(
startDateFrom
)
}
:
{}),
...(
startDateTo
?
{
lte
:
new
Date
(
startDateTo
)
}
:
{}),
},
}
:
{}),
};
const
skip
=
(
page
-
1
)
*
limit
;
const
[
data
,
total
]
=
await
prisma
.
$transaction
([
prisma
.
intern
.
findMany
({
where
,
include
:
defaultInclude
,
orderBy
:
{
[
sortBy
]:
order
},
skip
,
take
:
limit
,
}),
prisma
.
intern
.
count
({
where
}),
]);
return
{
data
,
meta
:
{
total
,
page
,
limit
,
totalPages
:
Math
.
ceil
(
total
/
limit
)
},
};
}
findById
(
id
:
string
)
{
return
prisma
.
intern
.
findFirst
({
where
:
{
id
,
deletedAt
:
null
},
include
:
defaultInclude
,
});
}
findByUserId
(
userId
:
string
)
{
return
prisma
.
intern
.
findFirst
({
where
:
{
userId
,
deletedAt
:
null
},
include
:
defaultInclude
,
});
}
create
(
data
:
CreateInternDto
)
{
return
prisma
.
intern
.
create
({
data
:
{
userId
:
data
.
userId
,
leaderId
:
data
.
leaderId
,
fullName
:
data
.
fullName
,
phone
:
data
.
phone
,
department
:
data
.
department
,
position
:
data
.
position
,
startDate
:
new
Date
(
data
.
startDate
),
duration
:
data
.
duration
,
discordUsername
:
data
.
discordUsername
,
},
include
:
defaultInclude
,
});
}
update
(
id
:
string
,
data
:
UpdateInternDto
)
{
return
prisma
.
intern
.
update
({
where
:
{
id
},
data
:
{
...(
data
.
leaderId
!==
undefined
?
{
leaderId
:
data
.
leaderId
}
:
{}),
...(
data
.
fullName
!==
undefined
?
{
fullName
:
data
.
fullName
}
:
{}),
...(
data
.
phone
!==
undefined
?
{
phone
:
data
.
phone
}
:
{}),
...(
data
.
department
!==
undefined
?
{
department
:
data
.
department
}
:
{}),
...(
data
.
position
!==
undefined
?
{
position
:
data
.
position
}
:
{}),
...(
data
.
startDate
!==
undefined
?
{
startDate
:
new
Date
(
data
.
startDate
)}
:
{}),
...(
data
.
duration
!==
undefined
?
{
duration
:
data
.
duration
}
:
{}),
...(
data
.
discordUsername
!==
undefined
?
{
discordUsername
:
data
.
discordUsername
}
:
{}),
...(
data
.
discordRoleGranted
!==
undefined
?
{
discordRoleGranted
:
data
.
discordRoleGranted
}
:
{}),
...(
data
.
status
!==
undefined
?
{
status
:
data
.
status
}
:
{}),
},
include
:
defaultInclude
,
});
}
softDelete
(
id
:
string
)
{
return
prisma
.
intern
.
update
({
where
:
{
id
},
data
:
{
deletedAt
:
new
Date
()
},
include
:
defaultInclude
,
});
}
}
src/modules/interns/intern.route.ts
0 → 100644
View file @
8e152a7a
import
{
Router
}
from
'express'
;
import
{
InternController
}
from
'./intern.controller'
;
import
{
authMiddleware
}
from
'../../middlewares/auth.middleware'
;
import
{
requireRole
}
from
'../../middlewares/role.middleware'
;
import
{
validate
}
from
'../../middlewares/validate.middleware'
;
import
{
findAllInternSchema
,
createInternSchema
,
updateInternSchema
,
}
from
'./intern.validation'
;
const
router
=
Router
();
const
controller
=
new
InternController
();
router
.
get
(
'/'
,
authMiddleware
,
requireRole
(
'ADMIN'
,
'LEADER'
),
validate
(
findAllInternSchema
,
'query'
),
controller
.
findAll
,
);
router
.
get
(
'/:id'
,
authMiddleware
,
requireRole
(
'ADMIN'
,
'LEADER'
),
controller
.
findById
,
);
router
.
post
(
'/'
,
authMiddleware
,
requireRole
(
'ADMIN'
,
'LEADER'
),
validate
(
createInternSchema
),
controller
.
create
,
);
router
.
put
(
'/:id'
,
authMiddleware
,
requireRole
(
'ADMIN'
,
'LEADER'
),
validate
(
updateInternSchema
),
controller
.
update
,
);
router
.
delete
(
'/:id'
,
authMiddleware
,
requireRole
(
'ADMIN'
),
controller
.
delete
,
);
export
default
router
;
src/modules/interns/intern.service.ts
0 → 100644
View file @
8e152a7a
import
{
InternRepository
}
from
'./intern.repository'
;
import
{
AppError
}
from
'../../common/errors/app-error'
;
import
{
ERROR_CODE
}
from
'../../common/errors/error-code'
;
import
{
InternQueryDto
,
CreateInternDto
,
UpdateInternDto
,
}
from
'./intern.dto'
;
export
class
InternService
{
private
readonly
repository
=
new
InternRepository
();
async
findAll
(
query
:
InternQueryDto
)
{
return
this
.
repository
.
findAll
(
query
);
}
async
findById
(
id
:
string
)
{
const
profile
=
await
this
.
repository
.
findById
(
id
);
if
(
!
profile
)
{
throw
new
AppError
(
'Intern not found'
,
404
,
ERROR_CODE
.
NOT_FOUND
);
}
return
profile
;
}
async
create
(
data
:
CreateInternDto
)
{
const
existing
=
await
this
.
repository
.
findByUserId
(
data
.
userId
);
if
(
existing
)
{
throw
new
AppError
(
'This user already has an intern profile'
,
409
,
ERROR_CODE
.
DUPLICATE_ENTRY
,
);
}
return
this
.
repository
.
create
(
data
);
}
async
update
(
id
:
string
,
data
:
UpdateInternDto
)
{
await
this
.
findById
(
id
);
return
this
.
repository
.
update
(
id
,
data
);
}
async
delete
(
id
:
string
)
{
await
this
.
findById
(
id
);
return
this
.
repository
.
softDelete
(
id
);
}
}
src/modules/interns/intern.validation.ts
0 → 100644
View file @
8e152a7a
import
{
z
}
from
'zod'
;
export
const
findAllInternSchema
=
z
.
object
({
fullName
:
z
.
string
().
optional
(),
department
:
z
.
string
().
optional
(),
position
:
z
.
string
().
optional
(),
status
:
z
.
enum
([
'ACTIVE'
,
'COMPLETED'
,
'DROPPED'
]).
optional
(),
leaderId
:
z
.
string
().
uuid
(
'Invalid leaderId'
).
optional
(),
discordRoleGranted
:
z
.
enum
([
'true'
,
'false'
])
.
transform
((
v
)
=>
v
===
'true'
)
.
optional
(),
startDateFrom
:
z
.
string
()
.
refine
((
v
)
=>
!
isNaN
(
Date
.
parse
(
v
)),
{
message
:
'startDateFrom must be a valid ISO date'
})
.
optional
(),
startDateTo
:
z
.
string
()
.
refine
((
v
)
=>
!
isNaN
(
Date
.
parse
(
v
)),
{
message
:
'startDateTo must be a valid ISO date'
})
.
optional
(),
sortBy
:
z
.
enum
([
'createdAt'
,
'fullName'
,
'startDate'
,
'status'
]).
optional
(),
order
:
z
.
enum
([
'asc'
,
'desc'
]).
optional
(),
page
:
z
.
coerce
.
number
().
int
().
positive
().
optional
().
default
(
1
),
limit
:
z
.
coerce
.
number
().
int
().
min
(
1
).
max
(
100
).
optional
().
default
(
20
),
});
export
const
createInternSchema
=
z
.
object
({
userId
:
z
.
string
().
uuid
(
'Invalid userId'
),
leaderId
:
z
.
string
().
uuid
(
'Invalid leaderId'
).
optional
(),
fullName
:
z
.
string
().
min
(
1
,
'Full name is required'
).
max
(
100
),
phone
:
z
.
string
().
min
(
9
).
max
(
15
),
department
:
z
.
string
().
min
(
1
,
'Department is required'
),
position
:
z
.
string
().
min
(
1
,
'Position is required'
),
startDate
:
z
.
string
().
refine
((
v
)
=>
!
isNaN
(
Date
.
parse
(
v
)),
{
message
:
'startDate must be a valid ISO date'
,
}),
duration
:
z
.
number
().
int
().
positive
(
'Duration must be a positive integer'
),
discordUsername
:
z
.
string
().
optional
(),
});
export
const
updateInternSchema
=
z
.
object
({
leaderId
:
z
.
string
().
uuid
().
nullable
().
optional
(),
fullName
:
z
.
string
().
min
(
1
).
max
(
100
).
optional
(),
phone
:
z
.
string
().
min
(
9
).
max
(
15
).
optional
(),
department
:
z
.
string
().
optional
(),
position
:
z
.
string
().
optional
(),
startDate
:
z
.
string
().
refine
((
v
)
=>
!
isNaN
(
Date
.
parse
(
v
)),
{
message
:
'Invalid date'
}).
optional
(),
duration
:
z
.
number
().
int
().
positive
().
optional
(),
discordUsername
:
z
.
string
().
nullable
().
optional
(),
discordRoleGranted
:
z
.
boolean
().
optional
(),
status
:
z
.
enum
([
'ACTIVE'
,
'COMPLETED'
,
'DROPPED'
]).
optional
(),
});
src/routes/index.ts
View file @
8e152a7a
...
...
@@ -2,6 +2,7 @@ import { Router } from 'express';
import
authRoute
from
'../modules/auth/auth.route'
;
import
userRoute
from
'../modules/users/user.route'
;
import
applicationRoute
from
'../modules/applications/application.route'
;
import
internRoute
from
'../modules/interns/intern.route'
;
const
router
=
Router
();
...
...
@@ -12,5 +13,6 @@ router.get('/health', (req, res) => {
router
.
use
(
'/auth'
,
authRoute
);
router
.
use
(
'/users'
,
userRoute
);
router
.
use
(
'/applications'
,
applicationRoute
);
router
.
use
(
'/interns'
,
internRoute
);
export
default
router
;
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