Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
Hire-Guru
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Rishikumar
Hire-Guru
Commits
d2648061
Commit
d2648061
authored
Apr 15, 2026
by
AravindR-K
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed quiz genreation bugs + editing bugs
parent
1d54e11a
Changes
14
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
885 additions
and
759 deletions
+885
-759
Backend/.env
Backend/.env
+1
-1
Backend/models/Quiz.js
Backend/models/Quiz.js
+3
-3
Backend/package.json
Backend/package.json
+1
-1
Backend/routes/admin.js
Backend/routes/admin.js
+532
-488
Backend/routes/auth.js
Backend/routes/auth.js
+1
-1
Backend/server.js
Backend/server.js
+77
-77
Frontend/src/app/interceptors/auth.interceptor.ts
Frontend/src/app/interceptors/auth.interceptor.ts
+1
-1
Frontend/src/app/pages/admin/edit-quiz/edit-quiz.ts
Frontend/src/app/pages/admin/edit-quiz/edit-quiz.ts
+42
-23
Frontend/src/app/pages/admin/generate-quiz/generate-quiz.html
...tend/src/app/pages/admin/generate-quiz/generate-quiz.html
+64
-36
Frontend/src/app/pages/admin/generate-quiz/generate-quiz.ts
Frontend/src/app/pages/admin/generate-quiz/generate-quiz.ts
+25
-5
Frontend/src/app/pages/admin/user-history/user-history.css
Frontend/src/app/pages/admin/user-history/user-history.css
+1
-1
Frontend/src/app/services/auth.service.ts
Frontend/src/app/services/auth.service.ts
+8
-8
Frontend/src/app/services/quiz.service.ts
Frontend/src/app/services/quiz.service.ts
+114
-114
Frontend/src/app/services/theme.service.ts
Frontend/src/app/services/theme.service.ts
+15
-0
No files found.
Backend/.env
View file @
d2648061
...
...
@@ -2,4 +2,4 @@ PORT=5000
#MONGODB_URI=mongodb://localhost:27017/quiz_app
MONGODB_URI=mongodb://127.0.0.1:27017/quiz_app
JWT_SECRET=quiz_app_super_secret_key_2026
JWT_EXPIRES_IN=
7
d
JWT_EXPIRES_IN=
1
d
Backend/models/Quiz.js
View file @
d2648061
...
...
@@ -43,8 +43,8 @@ const quizSchema = new mongoose.Schema({
}],
difficulty
:
{
type
:
String
,
enum
:
[
'
Beginner
'
,
'
Intermediate
'
,
'
Advance
d
'
],
default
:
'
Intermediate
'
enum
:
[
'
easy
'
,
'
medium
'
,
'
har
d
'
],
default
:
'
medium
'
},
topic
:
{
type
:
String
,
...
...
Backend/package.json
View file @
d2648061
...
...
@@ -4,7 +4,7 @@
"main"
:
"server.js"
,
"scripts"
:
{
"start"
:
"node server.js"
,
"dev"
:
"nodemon server.js"
,
"dev"
:
"nodemon
--ignore uploads/
server.js"
,
"seed"
:
"node seedAdmin.js"
,
"test"
:
"echo
\"
Error: no test specified
\"
&& exit 1"
},
...
...
Backend/routes/admin.js
View file @
d2648061
This diff is collapsed.
Click to expand it.
Backend/routes/auth.js
View file @
d2648061
...
...
@@ -7,7 +7,7 @@ const router = express.Router();
// Generate JWT Token
const
generateToken
=
(
id
)
=>
{
return
jwt
.
sign
({
id
},
process
.
env
.
JWT_SECRET
,
{
expiresIn
:
process
.
env
.
JWT_EXPIRES_IN
||
'
7
d
'
expiresIn
:
process
.
env
.
JWT_EXPIRES_IN
||
'
1
d
'
});
};
...
...
Backend/server.js
View file @
d2648061
const
express
=
require
(
'
express
'
);
const
cors
=
require
(
'
cors
'
);
const
cookieParser
=
require
(
'
cookie-parser
'
);
const
dotenv
=
require
(
'
dotenv
'
);
const
connectDB
=
require
(
'
./config/db
'
);
const
express
=
require
(
'
express
'
);
const
cors
=
require
(
'
cors
'
);
const
cookieParser
=
require
(
'
cookie-parser
'
);
const
dotenv
=
require
(
'
dotenv
'
);
const
connectDB
=
require
(
'
./config/db
'
);
// Load env vars
dotenv
.
config
();
// Load env vars
dotenv
.
config
();
// Connect to database
connectDB
();
// Connect to database
connectDB
();
const
app
=
express
();
const
app
=
express
();
// Middleware
const
allowedOrigins
=
[
// Middleware
const
allowedOrigins
=
[
'
http://localhost:4200
'
,
'
http://localhost:3000
'
,
process
.
env
.
FRONTEND_URL
].
filter
(
Boolean
);
].
filter
(
Boolean
);
app
.
use
(
cors
({
app
.
use
(
cors
({
origin
:
function
(
origin
,
callback
)
{
// Allow requests with no origin (mobile apps, curl, Postman, server-to-server)
if
(
!
origin
)
return
callback
(
null
,
true
);
...
...
@@ -41,27 +41,27 @@ app.use(cors({
credentials
:
true
,
methods
:
[
'
GET
'
,
'
POST
'
,
'
PUT
'
,
'
DELETE
'
,
'
OPTIONS
'
],
allowedHeaders
:
[
'
Content-Type
'
,
'
Authorization
'
]
}));
app
.
use
(
express
.
json
());
app
.
use
(
express
.
urlencoded
({
extended
:
true
}));
app
.
use
(
cookieParser
());
// Routes
app
.
use
(
'
/api/auth
'
,
require
(
'
./routes/auth
'
));
app
.
use
(
'
/api/admin
'
,
require
(
'
./routes/admin
'
));
app
.
use
(
'
/api/hr
'
,
require
(
'
./routes/hr
'
));
app
.
use
(
'
/api/candidate
'
,
require
(
'
./routes/candidate
'
));
// Keep backward compatibility for old student endpoints
app
.
use
(
'
/api/student
'
,
require
(
'
./routes/candidate
'
));
// Health check
app
.
get
(
'
/api/health
'
,
(
req
,
res
)
=>
{
}));
app
.
use
(
express
.
json
());
app
.
use
(
express
.
urlencoded
({
extended
:
true
}));
app
.
use
(
cookieParser
());
// Routes
app
.
use
(
'
/api/auth
'
,
require
(
'
./routes/auth
'
));
app
.
use
(
'
/api/admin
'
,
require
(
'
./routes/admin
'
));
app
.
use
(
'
/api/hr
'
,
require
(
'
./routes/hr
'
));
app
.
use
(
'
/api/candidate
'
,
require
(
'
./routes/candidate
'
));
// Keep backward compatibility for old student endpoints
app
.
use
(
'
/api/student
'
,
require
(
'
./routes/candidate
'
));
// Health check
app
.
get
(
'
/api/health
'
,
(
req
,
res
)
=>
{
res
.
json
({
status
:
'
OK
'
,
message
:
'
QuizMaster Pro API is running
'
});
});
});
// Error handling middleware
app
.
use
((
err
,
req
,
res
,
next
)
=>
{
// Error handling middleware
app
.
use
((
err
,
req
,
res
,
next
)
=>
{
console
.
error
(
err
.
stack
);
if
(
err
.
name
===
'
MulterError
'
)
{
...
...
@@ -72,10 +72,10 @@ app.use((err, req, res, next) => {
}
res
.
status
(
500
).
json
({
message
:
'
Something went wrong!
'
,
error
:
err
.
message
});
});
});
const
PORT
=
process
.
env
.
PORT
||
5000
;
const
PORT
=
process
.
env
.
PORT
||
5000
;
app
.
listen
(
PORT
,
()
=>
{
app
.
listen
(
PORT
,
()
=>
{
console
.
log
(
`Server running on port
${
PORT
}
`
);
});
});
Frontend/src/app/interceptors/auth.interceptor.ts
View file @
d2648061
import
{
HttpInterceptorFn
}
from
'
@angular/common/http
'
;
export
const
authInterceptor
:
HttpInterceptorFn
=
(
req
,
next
)
=>
{
const
token
=
localStorage
.
getItem
(
'
token
'
);
const
token
=
sessionStorage
.
getItem
(
'
token
'
);
if
(
token
)
{
const
cloned
=
req
.
clone
({
...
...
Frontend/src/app/pages/admin/edit-quiz/edit-quiz.ts
View file @
d2648061
...
...
@@ -56,20 +56,40 @@ export class EditQuizComponent implements OnInit {
}
});
}
onSave
():
void
{
if
(
!
this
.
title
.
trim
())
{
this
.
error
.
set
(
'
Title is required
'
);
return
;
}
onSave
():
void
{
if
(
!
this
.
title
.
trim
())
{
this
.
error
.
set
(
'
Title is required
'
);
return
;
}
this
.
saving
.
set
(
true
);
this
.
error
.
set
(
''
);
// 🔥 Format questions correctly
const
formattedQuestions
=
this
.
questions
().
map
(
q
=>
{
const
options
=
q
.
options
;
const
correctIndex
=
options
.
findIndex
(
(
opt
:
string
)
=>
opt
==
q
.
correctAnswer
);
return
{
question
:
q
.
question
,
options
,
correctAnswers
:
[
correctIndex
.
toString
()],
type
:
'
single
'
};
});
// ✅ closes map()
// 🔥 Final data
const
data
=
{
title
:
this
.
title
,
timer
:
this
.
timer
,
category
:
this
.
category
,
difficulty
:
this
.
difficulty
,
topic
:
this
.
topic
,
questions
:
this
.
questions
()
};
questions
:
formattedQuestions
};
// ✅ closes data object
this
.
quizService
.
updateQuiz
(
this
.
quizId
,
data
).
subscribe
({
next
:
()
=>
{
...
...
@@ -81,9 +101,8 @@ export class EditQuizComponent implements OnInit {
this
.
saving
.
set
(
false
);
this
.
error
.
set
(
err
.
error
?.
message
||
'
Failed to update quiz
'
);
}
});
}
});
// ✅ closes subscribe
}
// ✅ closes function
updateQuestion
(
index
:
number
,
field
:
string
,
value
:
any
):
void
{
const
q
=
[...
this
.
questions
()];
q
[
index
]
=
{
...
q
[
index
],
[
field
]:
value
};
...
...
Frontend/src/app/pages/admin/generate-quiz/generate-quiz.html
View file @
d2648061
<div
class=
"dashboard-layout"
>
<aside
class=
"sidebar"
>
<div
class=
"sidebar-header"
>
<span
class=
"logo-icon"
>
📝
</span><h2>
QuizMaster
</h2><span
class=
"role-badge"
>
Admin
</span>
<span
class=
"logo-icon"
>
📝
</span>
<h2>
QuizMaster
</h2><span
class=
"role-badge"
>
Admin
</span>
</div>
<nav
class=
"sidebar-nav"
>
<a
routerLink=
"/admin/dashboard"
class=
"nav-item"
><span
class=
"nav-icon"
>
🏠
</span><span>
Dashboard
</span></a>
<a
routerLink=
"/admin/users"
class=
"nav-item"
><span
class=
"nav-icon"
>
👥
</span><span>
Users
</span></a>
<a
routerLink=
"/admin/generate-quiz"
class=
"nav-item active"
><span
class=
"nav-icon"
>
➕
</span><span>
Generate Quiz
</span></a>
<a
routerLink=
"/admin/generate-quiz"
class=
"nav-item active"
><span
class=
"nav-icon"
>
➕
</span><span>
Generate
Quiz
</span></a>
</nav>
<div
class=
"sidebar-footer"
>
<div
class=
"user-info"
>
<div
class=
"user-avatar"
>
{{ authService.currentUser()?.name?.charAt(0) || 'A' }}
</div>
<div
class=
"user-details"
><span
class=
"user-name"
>
{{ authService.currentUser()?.name }}
</span><span
class=
"user-email"
>
{{ authService.currentUser()?.email }}
</span></div>
<div
class=
"user-details"
><span
class=
"user-name"
>
{{ authService.currentUser()?.name }}
</span><span
class=
"user-email"
>
{{ authService.currentUser()?.email }}
</span></div>
</div>
<button
class=
"logout-btn"
(click)=
"logout()"
><span>
🚪
</span>
Logout
</button>
</div>
...
...
@@ -35,7 +38,8 @@
<div
class=
"form-row"
>
<div
class=
"form-group"
>
<label
for=
"title"
>
Quiz Name
</label>
<input
type=
"text"
id=
"title"
[(ngModel)]=
"title"
name=
"title"
placeholder=
"e.g., JavaScript Fundamentals"
/>
<input
type=
"text"
id=
"title"
[(ngModel)]=
"title"
name=
"title"
placeholder=
"e.g., JavaScript Fundamentals"
/>
</div>
<div
class=
"form-group"
>
<label
for=
"timer"
>
Timer (minutes)
</label>
...
...
@@ -59,20 +63,44 @@
</div>
</div>
<button
type=
"submit"
class=
"btn btn-primary"
[disabled]=
"loading()"
>
<div
class=
"form-group"
>
<label>
Assign Quiz
</label>
<div
style=
"margin-bottom: 10px;"
>
<label>
<input
type=
"checkbox"
[checked]=
"assignToAll()"
(change)=
"assignToAll.set(!assignToAll())"
/>
Assign to All Users
</label>
</div>
@if (!assignToAll()) {
<div
class=
"group-list"
>
@for (group of groups(); track group._id) {
<label
style=
"display:block; margin-bottom:6px;"
>
<input
type=
"checkbox"
[value]=
"group.name"
(change)=
"onGroupToggle(group.name, $event)"
/>
{{ group.name }}
</label>
}
</div>
}
</div>
<button
mat-raised-button
color=
"primary"
type=
"submit"
class=
"btn btn-primary"
[disabled]=
"loading()"
>
@if (loading()) {
<span
class=
"spinner"
></span>
Creating Quiz...
} @else {
🚀 Create Quiz
}
</button>
</form>
</div>
<!-- Existing Quizzes -->
<h2
class=
"section-title"
>
Existing Quizzes
</h2>
@if (loadingQuizzes()) {
<div
class=
"loading-state"
><div
class=
"loader"
></div></div>
<div
class=
"loading-state"
>
<div
class=
"loader"
></div>
</div>
} @else if (quizzes().length === 0) {
<div
class=
"empty-state"
>
<span
class=
"empty-icon"
>
📋
</span>
...
...
Frontend/src/app/pages/admin/generate-quiz/generate-quiz.ts
View file @
d2648061
...
...
@@ -4,11 +4,12 @@ import { FormsModule } from '@angular/forms';
import
{
RouterLink
}
from
'
@angular/router
'
;
import
{
AuthService
}
from
'
../../../services/auth.service
'
;
import
{
QuizService
}
from
'
../../../services/quiz.service
'
;
import
{
MatButtonModule
}
from
'
@angular/material/button
'
;
@
Component
({
selector
:
'
app-generate-quiz
'
,
standalone
:
true
,
imports
:
[
CommonModule
,
FormsModule
,
RouterLink
],
imports
:
[
CommonModule
,
FormsModule
,
RouterLink
,
MatButtonModule
],
templateUrl
:
'
./generate-quiz.html
'
,
styleUrl
:
'
./generate-quiz.css
'
})
...
...
@@ -17,7 +18,9 @@ export class GenerateQuizComponent implements OnInit {
timer
=
30
;
selectedFile
:
File
|
null
=
null
;
fileName
=
signal
<
string
>
(
''
);
groups
=
signal
<
any
[]
>
([]);
selectedGroups
=
signal
<
string
[]
>
([]);
assignToAll
=
signal
<
boolean
>
(
true
);
loading
=
signal
<
boolean
>
(
false
);
success
=
signal
<
string
>
(
''
);
error
=
signal
<
string
>
(
''
);
...
...
@@ -25,11 +28,18 @@ export class GenerateQuizComponent implements OnInit {
quizzes
=
signal
<
any
[]
>
([]);
loadingQuizzes
=
signal
<
boolean
>
(
true
);
constructor
(
public
authService
:
AuthService
,
private
quizService
:
QuizService
)
{}
constructor
(
public
authService
:
AuthService
,
private
quizService
:
QuizService
)
{
}
ngOnInit
():
void
{
console
.
log
(
"
Component loaded
"
);
this
.
loadQuizzes
();
this
.
quizService
.
getAdminGroups
().
subscribe
({
next
:
(
res
)
=>
{
this
.
groups
.
set
(
res
.
groups
||
[]);
},
error
:
()
=>
{
console
.
log
(
"
Failed to load groups
"
);
}
});
}
loadQuizzes
():
void
{
...
...
@@ -72,7 +82,8 @@ export class GenerateQuizComponent implements OnInit {
formData
.
append
(
'
title
'
,
this
.
title
);
formData
.
append
(
'
timer
'
,
this
.
timer
.
toString
());
formData
.
append
(
'
questionsFile
'
,
this
.
selectedFile
);
formData
.
append
(
'
assignToAll
'
,
this
.
assignToAll
().
toString
());
formData
.
append
(
'
assignedGroups
'
,
JSON
.
stringify
(
this
.
selectedGroups
()));
this
.
quizService
.
createQuiz
(
formData
).
subscribe
({
next
:
(
res
)
=>
{
this
.
loading
.
set
(
false
);
...
...
@@ -98,6 +109,15 @@ export class GenerateQuizComponent implements OnInit {
});
}
}
onGroupToggle
(
groupName
:
string
,
event
:
any
):
void
{
const
selected
=
this
.
selectedGroups
();
if
(
event
.
target
.
checked
)
{
this
.
selectedGroups
.
set
([...
selected
,
groupName
]);
}
else
{
this
.
selectedGroups
.
set
(
selected
.
filter
(
g
=>
g
!==
groupName
));
}
}
logout
():
void
{
this
.
authService
.
logout
();
...
...
Frontend/src/app/pages/admin/user-history/user-history.css
View file @
d2648061
...
...
@@ -148,7 +148,7 @@
.main-content
{
flex
:
1
;
margin-left
:
26
0px
;
margin-left
:
0px
;
padding
:
24px
32px
;
}
...
...
Frontend/src/app/services/auth.service.ts
View file @
d2648061
...
...
@@ -31,8 +31,8 @@ export class AuthService {
}
private
loadUserFromStorage
():
void
{
const
token
=
local
Storage
.
getItem
(
'
token
'
);
const
user
=
localStorage
.
getItem
(
'
user
'
);
const
token
=
session
Storage
.
getItem
(
'
token
'
);
const
user
=
sessionStorage
.
getItem
(
'
user
'
);
if
(
token
&&
user
)
{
this
.
currentUser
.
set
(
JSON
.
parse
(
user
));
this
.
isLoggedIn
.
set
(
true
);
...
...
@@ -49,7 +49,7 @@ export class AuthService {
}
logout
():
void
{
const
token
=
localStorage
.
getItem
(
'
token
'
);
const
token
=
sessionStorage
.
getItem
(
'
token
'
);
if
(
token
)
{
this
.
http
.
post
(
`
${
this
.
apiUrl
}
/logout`
,
{}).
subscribe
({
complete
:
()
=>
this
.
clearAuth
(),
...
...
@@ -62,23 +62,23 @@ export class AuthService {
private
handleAuth
(
res
:
AuthResponse
):
void
{
if
(
res
.
token
)
{
local
Storage
.
setItem
(
'
token
'
,
res
.
token
);
session
Storage
.
setItem
(
'
token
'
,
res
.
token
);
}
localStorage
.
setItem
(
'
user
'
,
JSON
.
stringify
(
res
.
user
));
sessionStorage
.
setItem
(
'
user
'
,
JSON
.
stringify
(
res
.
user
));
this
.
currentUser
.
set
(
res
.
user
);
this
.
isLoggedIn
.
set
(
true
);
}
private
clearAuth
():
void
{
local
Storage
.
removeItem
(
'
token
'
);
local
Storage
.
removeItem
(
'
user
'
);
session
Storage
.
removeItem
(
'
token
'
);
session
Storage
.
removeItem
(
'
user
'
);
this
.
currentUser
.
set
(
null
);
this
.
isLoggedIn
.
set
(
false
);
this
.
router
.
navigate
([
'
/login
'
]);
}
getToken
():
string
|
null
{
return
localStorage
.
getItem
(
'
token
'
);
return
sessionStorage
.
getItem
(
'
token
'
);
}
getUserRole
():
string
|
null
{
...
...
Frontend/src/app/services/quiz.service.ts
View file @
d2648061
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
HttpClient
}
from
'
@angular/common/http
'
;
import
{
Observable
}
from
'
rxjs
'
;
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
HttpClient
}
from
'
@angular/common/http
'
;
import
{
Observable
}
from
'
rxjs
'
;
@
Injectable
({
@
Injectable
({
providedIn
:
'
root
'
})
export
class
QuizService
{
})
export
class
QuizService
{
private
adminUrl
=
'
http://localhost:5000/api/admin
'
;
private
hrUrl
=
'
http://localhost:5000/api/hr
'
;
private
candidateUrl
=
'
http://localhost:5000/api/candidate
'
;
...
...
@@ -150,4 +150,4 @@ export class QuizService {
getResultDetails
(
submissionId
:
string
):
Observable
<
any
>
{
return
this
.
http
.
get
(
`
${
this
.
candidateUrl
}
/results/
${
submissionId
}
`
);
}
}
}
Frontend/src/app/services/theme.service.ts
View file @
d2648061
...
...
@@ -33,6 +33,21 @@ export class ThemeService {
}
private
applyTheme
(
theme
:
ThemeMode
):
void
{
// Existing (your custom theme)
document
.
documentElement
.
setAttribute
(
'
data-theme
'
,
theme
);
// NEW (Material theme)
const
body
=
document
.
body
;
body
.
classList
.
remove
(
'
azure-theme
'
,
'
magenta-theme
'
,
'
cyan-theme
'
,
'
rose-theme
'
);
// Map your themes → material themes
const
themeMap
:
Record
<
ThemeMode
,
string
>
=
{
light
:
'
azure-theme
'
,
dark
:
'
magenta-theme
'
,
blue
:
'
cyan-theme
'
};
body
.
classList
.
add
(
themeMap
[
theme
]
||
'
azure-theme
'
);
}
}
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