Skip to content

Commit

Permalink
created profile page in the admin side using ngrx
Browse files Browse the repository at this point in the history
  • Loading branch information
albindavidc committed Feb 4, 2025
1 parent 2d95874 commit c30311d
Show file tree
Hide file tree
Showing 14 changed files with 175 additions and 68 deletions.
15 changes: 9 additions & 6 deletions faculty-management-backend/Middleware/authMiddleware.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import jwt from "jsonwebtoken";

export default authMiddleware = (req, res, next) => {
const token = res.header("Authorization");
if (!token) return res.status(401).json({ message: "You are not authorized" });

const authMiddleware = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ message: "Unauthorized" });
}
const token = authHeader.split(" ")[1];
try {
const decoded = jwt.verify(token, process.env.SECRET_KEY);
req.user = decoded;

next();
} catch (error) {
res.status(401).json({ message: "Invalid Token" });
return res.status(401).json({ message: "Invalid token" });
}
};

export default authMiddleware;
13 changes: 8 additions & 5 deletions faculty-management-backend/Routes/AdminProfile.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import express from "express";
const router = express.Router();
import User from "../Models/UserModels.js";
import authMiddleware from "../Middleware/authMiddleware.js";

export default router.get("/profile", authMiddleware, async (req, res) => {
const router = express.Router();

router.get("/profile", authMiddleware, async (req, res) => {
try {
const user = await User.findById(req.user._id);
const user = await User.findById(req.user.id).select("-password");
if (!user) {
res.status(404).json({ message: "User not found" });
return res.status(404).json({ message: "User not found" });
}

res.json(user);
} catch (error) {
console.error("Profile Error:", error);
res.status(500).json({ message: "Server error" });
}
});

export default router;
3 changes: 3 additions & 0 deletions faculty-management-backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import cors from "cors";
import adminRoutes from "./Routes/AdminRoutes.js";
import signupRoute from "./Routes/SignupRoutes.js";
import loginRoute from "./Routes/LoginRouter.js";
import AdminProfile from "./Routes/AdminProfile.js";

dotenv.config();
db();
Expand All @@ -26,4 +27,6 @@ app.use("/admin", adminRoutes);
app.use("/admin", signupRoute);
app.use("/admin", loginRoute);

app.use("/admin", AdminProfile);

export default app;
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
Faculty
</a>

<a class="p-3" (click)="showFacultyData()">
<a class="p-3" (click)="showProfileData()">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
width="20"
height="20"
fill="currentColor"
class="bi bi-person-circle"
class="bi bi-person-circle me-3"
viewBox="0 0 16 16"
>
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0" />
Expand Down Expand Up @@ -66,6 +66,7 @@

<div class="col-sm-10 bg-white">
<app-faculty-data *ngIf="faculty"></app-faculty-data>
<app-admin-profile *ngIf="profile"></app-admin-profile>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { Component, OnInit } from '@angular/core';
import { FacultyDataComponent } from './faculty-data/faculty-data.component';
import { CommonModule } from '@angular/common';
import { Router } from '@angular/router';
import { AdminProfileComponent } from '../admin-profile/admin-profile.component';

@Component({
selector: 'app-admin-dashboard',
imports: [FacultyDataComponent, CommonModule],
imports: [FacultyDataComponent, CommonModule, AdminProfileComponent],
templateUrl: './admin-dashboard.component.html',
styleUrl: './admin-dashboard.component.css',
})
export class AdminDashboardComponent implements OnInit {
training: boolean = false;
profile: boolean = false;
faculty: boolean = false;

constructor(private router: Router) {}
Expand All @@ -20,7 +21,7 @@ export class AdminDashboardComponent implements OnInit {
}

setOff(): void {
this.training = false;
this.profile = false;
this.faculty = false;
}

Expand All @@ -29,9 +30,9 @@ export class AdminDashboardComponent implements OnInit {
this.faculty = true;
}

showTrainingData() {
showProfileData() {
this.setOff();
this.training = true;
this.profile = true;
}

signOut() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div class="container-fluid">
<div *ngIf="loading$ | async; else content">
<p>Loading profile data...</p>
</div>

<ng-template #content>
<!-- <pre>{{ admin$ | async | json }}</pre> -->

<div *ngIf="admin$ | async as admin; else error">
<h2>Admin Profile</h2>
<p><strong>Name:</strong> {{ admin.first_name }} {{ admin.last_name }}</p>
<p><strong>Email:</strong> {{ admin.username }}</p>
</div>

<ng-template #error>
<div *ngIf="error$ | async as error">
<p>Error loading profile: {{ error }}</p>
</div>
</ng-template>
</ng-template>
</div>
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import {
selectProfile,
selectProfileLoading,
selectProfileError,
} from './store/profile.selectors';
import { ProfileActions } from './store/profile.actions';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-admin-profile',
imports: [],
imports: [CommonModule],
templateUrl: './admin-profile.component.html',
styleUrl: './admin-profile.component.css'
styleUrls: ['./admin-profile.component.css'],
})
export class AdminProfileComponent {
export class AdminProfileComponent implements OnInit {
admin$: Observable<any>;
loading$: Observable<boolean>;
error$: Observable<string | null>;

constructor(private store: Store) {
this.admin$ = this.store.select(selectProfile);
this.loading$ = this.store.select(selectProfileLoading);
this.error$ = this.store.select(selectProfileError);
}

ngOnInit(): void {
this.store.dispatch(ProfileActions.loadProfiles());
console.log('this is the frontend' + this.admin$);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const ProfileActions = createActionGroup({
source: 'Profile',
events: {
'Load Profiles': emptyProps(),
'Load Profiles Success': props<{ data: unknown }>(),
'Load Profiles Failure': props<{ error: unknown }>(),
}
'Load Profiles Success': props<{ profile: any }>(),
'Load Profiles Failure': props<{ error: string }>(),
},
});
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, concatMap } from 'rxjs/operators';
import { Observable, EMPTY, of } from 'rxjs';
import { ProfileActions } from './profile.actions';

import { catchError, map, mergeMap, of } from 'rxjs';
import * as ProfileActions from '../store/profile.actions';
import { Action } from '@ngrx/store';
import { AdminAuthService } from '../../../service/admin-auth.service';

@Injectable()
export class ProfileEffects {
constructor(private actions$: Actions, private adminService: AdminAuthService) {}

loadProfiles$ = createEffect(() => {
return this.actions$.pipe(

ofType(ProfileActions.loadProfiles),
concatMap(() =>
/** An EMPTY observable only emits completion. Replace with your own observable API request */
EMPTY.pipe(
map(data => ProfileActions.loadProfilesSuccess({ data })),
catchError(error => of(ProfileActions.loadProfilesFailure({ error }))))
loadProfile$ = createEffect(() =>
this.actions$.pipe(
ofType(ProfileActions.ProfileActions.loadProfiles),
mergeMap(() =>
this.adminService.getProfile().pipe(
map((profile): Action => ProfileActions.ProfileActions.loadProfilesSuccess({ profile })), // Ensure explicit Action type
catchError((error) =>
of(ProfileActions.ProfileActions.loadProfilesFailure({ error: error.message })) // Wrap in `of()` to return an observable
)
)
)
);
});


constructor(private actions$: Actions) {}
)
);
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import { createFeature, createReducer, on } from '@ngrx/store';
import { createReducer, on } from '@ngrx/store';
import { ProfileActions } from './profile.actions';

export const profileFeatureKey = 'profile';

export interface State {

export interface ProfileState {
profile: any | null;
loading: boolean;
error: string | null;
}

export const initialState: State = {

const initialState: ProfileState = {
profile: null,
loading: false,
error: null,
};

export const reducer = createReducer(
export const profileReducer = createReducer(
initialState,
on(ProfileActions.loadProfiles, state => state),
on(ProfileActions.loadProfilesSuccess, (state, action) => state),
on(ProfileActions.loadProfilesFailure, (state, action) => state),
on(ProfileActions.loadProfiles, (state) => ({
...state,
loading: true,
error: null,
})),
on(ProfileActions.loadProfilesSuccess, (state, { profile }) => ({
...state,
profile,
loading: false,
})),
on(ProfileActions.loadProfilesFailure, (state, { error }) => ({
...state,
loading: false,
error,
}))
);

export const profileFeature = createFeature({
name: profileFeatureKey,
reducer,
});

Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromProfile from './profile.reducer';

export const selectProfileState = createFeatureSelector<fromProfile.State>(
fromProfile.profileFeatureKey
export const selectProfileState =
createFeatureSelector<fromProfile.ProfileState>('profile');

// selector to get the profile data
export const selectProfile = createSelector(
selectProfileState,
(state) => state.profile
);

// selector to get the loading state
export const selectProfileLoading = createSelector(
selectProfileState,
(state) => state.loading
);

// selector to get the error state
export const selectProfileError = createSelector(
selectProfileState,
(state) => state.error
);
20 changes: 19 additions & 1 deletion faculty-management/src/app/admin/service/admin-auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,25 @@ export class AdminAuthService {
userName: string;
password: string;
}): Observable<{ token: string }> {
return this.http.post<{ token: string }>(`${this.apiUrl}/newLogin`, loginData);
return this.http.post<{ token: string }>(
`${this.apiUrl}/newLogin`,
loginData
);
}

getProfile(): Observable<any> {
const token = localStorage.getItem('token');

if (!token) {
console.warn('No token found in localStorage');
throw new Error('Unauthorized: No token provided');
}

const headers = new HttpHeaders({
Authorization: `Bearer ${token}`,
});

return this.http.get(`${this.apiUrl}/profile`, { headers });
}

// adminUserName: string = environment.adminUserName;
Expand Down
12 changes: 9 additions & 3 deletions faculty-management/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@ import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
import { EffectsModule, provideEffects } from '@ngrx/effects';
import { provideEffects } from '@ngrx/effects';
import { FacultyEffects } from './admin/components/admin-dashboard/faculty-data/store/faculty.effects';
import { provideState, provideStore } from '@ngrx/store';
import { facultyReducer } from './admin/components/admin-dashboard/faculty-data/store/faculty.reducer';
import { SignupEffects } from './admin/components/admin-signup/store/signup.effects';
import { signupFeature } from './admin/components/admin-signup/store/signup.reducer';
import { profileReducer } from './admin/components/admin-profile/store/profile.reducer';
import { ProfileEffects } from './admin/components/admin-profile/store/profile.effects';

export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(),

provideStore({ faculty: facultyReducer, signup: signupFeature.reducer }),
provideEffects(FacultyEffects, SignupEffects),
provideStore({
faculty: facultyReducer,
signup: signupFeature.reducer,
profile: profileReducer,
}),
provideEffects(FacultyEffects, SignupEffects, ProfileEffects),
provideState({ name: 'faculty', reducer: facultyReducer }),
],
};
Loading

0 comments on commit c30311d

Please sign in to comment.