Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion backend/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import speakerRoutes from "./routes/speakerRoutes";
import pdfRoutes from "./routes/pdfRoutes";
import emailTemplateRoutes from "./routes/emailTemplateRoutes";
import zoomRoutes from "./routes/zoomRoutes";
import userTypeRoutes from "./routes/userTypeRoutes";

// Import middleware
import { notFound, errorHandler } from "./middlewares/errorMiddleware";
Expand Down Expand Up @@ -94,7 +95,8 @@ app.use("/api/emails", verifyFirebaseAuth, emailRoutes);
app.use("/api/emailTemplates", verifyFirebaseAuth, emailTemplateRoutes);
app.use("/api/speakers", verifyFirebaseAuth, speakerRoutes);
app.use("/api/upload", verifyFirebaseAuth, uploadRoutes);
app.use("/api/zoom", verifyFirebaseAuth, zoomRoutes)
app.use("/api/user-types", userTypeRoutes);
app.use("/api/zoom", verifyFirebaseAuth, zoomRoutes);
app.use("/api/certificatePDFs", verifyFirebaseAuth, pdfRoutes);

app.use(notFound);
Expand Down
7 changes: 5 additions & 2 deletions backend/controllers/loginController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const createUser = async (
company,
progress,
payments,
role = "foster parent", // Default role
role,
isColorado, // Default value
} = req.body;

Expand Down Expand Up @@ -118,6 +118,7 @@ export const createUser = async (
});

const savedUser = await newUser.save();
await savedUser.populate("role");

// // Generate JWT token
// const accessToken = jwt.sign(
Expand Down Expand Up @@ -163,7 +164,9 @@ export const loginUser = async (req: Request, res: Response): Promise<void> => {
}

// Find user in database
let user = await User.findOne({ firebaseId });
let user = await User.findOne({ firebaseId }).populate("role");

console.log(user);

if (!user) {
res.status(404).json({ message: "User not found" });
Expand Down
13 changes: 9 additions & 4 deletions backend/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,17 @@ export const checkAdmin = async (req: AuthenticatedRequest, res: Response) => {
.json({ message: "Unauthorized: No user data found" });
}

const user = await User.findOne({ firebaseId: req.user.uid });
const user = await User.findOne({ firebaseId: req.user.uid }).populate(
"role"
);

if (!user) {
return res.status(404).json({ message: "User not found" });
if (!user || !user.role) {
return res
.status(404)
.json({ message: "User not found or role missing" });
}
const isAdmin = user.role === "staff";

const isAdmin = (user.role as any).name?.toLowerCase() === "staff";

return res.status(200).json({ isAdmin });
} catch (error) {
Expand Down
69 changes: 69 additions & 0 deletions backend/controllers/userTypeController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Request, Response, NextFunction, RequestHandler } from "express";
import UserType from "../models/userTypeModel";

// Fix: Don't return the Response object from the controller function
export const createUserType: RequestHandler = async (req, res, next) => {
try {
const { name, cost } = req.body;

// if (
// !name ||
// !cost ||
// ["foster parent", "staff"].includes(name.toLowerCase())
// ) {
// res.status(400).json({ message: "This role is reserved or invalid." });
// return;
// }

const existing = await UserType.findOne({ name });
if (existing) {
res.status(409).json({ message: "Role already exists." });
return;
}

const newType = await UserType.create({ name, cost });
res.status(201).json({ success: true, data: newType });
} catch (err) {
next(err); // Pass errors to Express error handler
}
};

export const getUserTypes: RequestHandler = async (_req, res, next) => {
try {
const types = await UserType.find().sort({ name: 1 });
res.status(200).json({ success: true, data: types });
} catch (err) {
next(err); // Pass errors to Express error handler
}
};

export const deleteUserType: RequestHandler = async (req, res, next) => {
try {
const deleted = await UserType.findByIdAndDelete(req.params.id);
if (!deleted) {
res.status(404).json({ message: "User type not found" });
return;
}
res.status(200).json({ success: true, message: "User type deleted" });
} catch (err) {
next(err);
}
};

export const updateUserType: RequestHandler = async (req, res, next) => {
try {
const { name, cost } = req.body;
const updated = await UserType.findByIdAndUpdate(
req.params.id,
{ name, cost },
{ new: true }
);
if (!updated) {
res.status(404).json({ message: "User type not found" });
return;
}
res.status(200).json({ success: true, data: updated });
} catch (err) {
next(err);
}
};
36 changes: 9 additions & 27 deletions backend/models/userModel.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import mongoose, { Schema, Document, Model } from "mongoose";
import { IProgress } from "./progressModel";
import { IPayment } from "./paymentModel";
import { IUserType } from "./userTypeModel";

export interface IUser extends Document {
firebaseId: string;
email: string;
isColorado: boolean;
role:
| "foster parent"
| "certified kin"
| "non-certified kin"
| "staff"
| "casa"
| "teacher"
| "county/cpa worker"
| "speaker"
| "former parent"
| "caregiver";
// TODO: update after user types can be created in admin
role: mongoose.Types.ObjectId | IUserType;
name: string;
address1: string;
address2?: string;
Expand All @@ -39,20 +29,8 @@ const userSchema: Schema = new Schema(
email: { type: String, required: true },
isColorado: { type: Boolean, required: true },
role: {
type: String,
enum: [
"foster parent",
"certified kin",
"non-certified kin",
"staff",
"casa",
"teacher",
"county/cpa worker",
"speaker",
"former parent",
"caregiver",
],
required: true,
type: Schema.Types.ObjectId,
ref: "UserType",
},
name: { type: String, required: true },
address1: { type: String, required: true },
Expand All @@ -63,7 +41,11 @@ const userSchema: Schema = new Schema(
certification: { type: String, required: true },
company: { type: String, required: true },
phone: { type: String, required: true },
language: { type: String, enum: ["English", "Spanish"], default: "English" },
language: {
type: String,
enum: ["English", "Spanish"],
default: "English",
},
progress: [
{
type: Schema.Types.ObjectId,
Expand Down
21 changes: 21 additions & 0 deletions backend/models/userTypeModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// backend/models/userTypeModel.ts
import mongoose, { Schema, Document, Model } from "mongoose";

export interface IUserType extends Document {
name: string;
cost: number;
}

const userTypeSchema: Schema = new Schema(
{
name: { type: String, required: true, unique: true },
cost: { type: Number, required: true },
},
{ timestamps: true }
);

const UserType: Model<IUserType> = mongoose.model<IUserType>(
"UserType",
userTypeSchema
);
export default UserType;
17 changes: 17 additions & 0 deletions backend/routes/userTypeRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// backend/routes/userTypeRoutes.ts
import express from "express";
import {
createUserType,
getUserTypes,
deleteUserType,
updateUserType,
} from "../controllers/userTypeController";

const router = express.Router();

router.post("/", createUserType);
router.get("/", getUserTypes);
router.put("/:id", updateUserType);
router.delete("/:id", deleteUserType);

export default router;
2 changes: 1 addition & 1 deletion frontend/src/components/AdminSidebar/AdminSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ interface AdminSidebarProps {
export function AdminSidebar({ isLoggedIn, setIsLoggedIn }: AdminSidebarProps) {
// User Info
const name = isLoggedIn ? JSON.parse(localStorage.user).name : "Log In";
const role = isLoggedIn ? JSON.parse(localStorage.user).role : "Log In";
const role = isLoggedIn ? JSON.parse(localStorage.user).role.name : "Log In";

// State for tracking the active item (can be parent or sub-item href)
const [activeItem, setActiveItem] = useState<string>(
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/Sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import authService from "../../services/authService";
// User information
export const userInfo = {
name: "First L.",
role: localStorage.user ? localStorage.user.role : "No role",
role: localStorage.user ? JSON.parse(localStorage.user).role.name : "No role",
isLoggedIn: false,
isAdmin: localStorage.user
? localStorage.user.role === "staff"
? JSON.parse(localStorage.user).role.name === "Staff"
? true
: false
: false,
Expand Down Expand Up @@ -102,7 +102,7 @@ export function Sidebar({
}: SidebarProps) {
// User Info
const name = isLoggedIn ? JSON.parse(localStorage.user).name : "Log In";
const role = isLoggedIn ? JSON.parse(localStorage.user).role : "Log In";
const role = isLoggedIn ? JSON.parse(localStorage.user).role.name : "Log In";
// Automatically collapse sidebar for narrow screens
useEffect(() => {
const handleResize = () => {
Expand Down Expand Up @@ -187,7 +187,7 @@ export function SidebarItems({
cartItemCount,
}: SidebarItemsProps) {
const [isAdmin, setIsAdmin] = useState(
localStorage.user && JSON.parse(localStorage.user).role === "staff"
localStorage.user && JSON.parse(localStorage.user).role.name === "Staff"
);

// const checkAdmin = async () => {
Expand Down
Loading