Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add teacher management page #24

Merged
merged 7 commits into from
Nov 20, 2024
Merged

add teacher management page #24

merged 7 commits into from
Nov 20, 2024

Conversation

135ze
Copy link
Collaborator

@135ze 135ze commented Jul 27, 2024

Notion ticket link

Unstyled Teacher Management

Implementation description

  • /manage page now contains teacher data
  • can edit teacher role or delete teacher

image

Steps to test

  1. ensure db works and is seeded (either docker or vercel)
  2. go to /manage

What should reviewers focus on?

  • general good code practices

Checklist

  • My PR name is descriptive and in imperative tense
  • My commit messages are descriptive and in imperative tense. My commits are atomic and trivial commits are squashed or fixup'd into non-trivial commits
  • I have run the appropriate linter(s)
  • I have requested a review from the PL, as well as other devs who have background knowledge on this PR or who will be building on top of this PR

@135ze 135ze self-assigned this Jul 27, 2024
Copy link

vercel bot commented Jul 27, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sistema ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 13, 2024 2:42am

Copy link
Member

@ChinemeremChigbo ChinemeremChigbo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look into the failing deployment. Also, it seems like this PR is dependent on the seeding script PR #12 please help @VJagiasi get his PR approved and then request a re-review.

Copy link
Member

@ChinemeremChigbo ChinemeremChigbo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments. Will continue to do a more thorough review.

Dockerfile Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if any of these changes are needed? Does you PR still work without them?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should leave this file unchanged in this PR.

package.json Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should leave this file unchanged in this PR.

@135ze 135ze changed the base branch from main to evelina/teacher-profile-unstyled September 21, 2024 22:03
@135ze 135ze force-pushed the evelina/teacher-profile-unstyled branch 3 times, most recently from c6555f1 to 4be7a37 Compare October 2, 2024 00:49
@135ze 135ze force-pushed the evelina/teacher-management branch from 24a847f to 4e8726a Compare October 5, 2024 22:22
@135ze 135ze changed the base branch from evelina/teacher-profile-unstyled to main October 5, 2024 22:22
@135ze
Copy link
Collaborator Author

135ze commented Oct 30, 2024

Some initial comments. Will continue to do a more thorough review.

Hey @ChinemeremChigbo , should be good to go now with the deployment and removals of extra changes

import { NextApiResponse } from 'next';
import { prisma } from '../../../utils/prisma';

export default async function handler(req, res: NextApiResponse) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add type for req

Suggested change
export default async function handler(req, res: NextApiResponse) {
export default async function handler(req: NextApiRequest, res: NextApiResponse) {

import { prisma } from '../../../utils/prisma';

export default async function handler(req, res: NextApiResponse) {
const users = await prisma.user.findMany();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should have a try catch here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to throw a 500 if there's an error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise this can occur:

 ⨯ PrismaClientKnownRequestError: 
Invalid `prisma.user.findMany()` invocation:


The table `public.User` does not exist in the current database.
    at Mn.handleRequestError (/Users/chinemerem/Documents/GitHub/sistema/node_modules/@prisma/client/runtime/library.js:121:7753)
    at Mn.handleAndLogRequestError (/Users/chinemerem/Documents/GitHub/sistema/node_modules/@prisma/client/runtime/library.js:121:7061)
    at Mn.request (/Users/chinemerem/Documents/GitHub/sistema/node_modules/@prisma/client/runtime/library.js:121:6745)
    at async l (/Users/chinemerem/Documents/GitHub/sistema/node_modules/@prisma/client/runtime/library.js:130:9633)
    at async handler (webpack-internal:///(api)/./src/pages/api/users.ts:8:19)
    at async K (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/compiled/next-server/pages-api.runtime.dev.js:21:2871)
    at async U.render (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/compiled/next-server/pages-api.runtime.dev.js:21:3955)
    at async DevServer.runApi (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/server/next-server.js:598:9)
    at async NextNodeServer.handleCatchallRenderRequest (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/server/next-server.js:269:37)
    at async DevServer.handleRequestImpl (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/server/base-server.js:810:17)
    at async /Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/server/dev/next-dev-server.js:339:20
    at async Span.traceAsyncFn (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/trace/trace.js:154:20)
    at async DevServer.handleRequest (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/server/dev/next-dev-server.js:336:24)
    at async invokeRender (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/server/lib/router-server.js:173:21)
    at async handleRequest (/Users/chinemerem/Documents/GitHub/sistema/node_modules/next/dist/server/lib/router-server.js:350:24) {
  code: 'P2021',
  clientVersion: '5.20.0',
  meta: { modelName: 'User', table: 'public.User' },
  page: '/api/users'
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah makes sense, but I think that error is only when the database isn't seeded? So that would be a larger issue haha

Copy link
Member

@ChinemeremChigbo ChinemeremChigbo Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion for this file:

import type { NextApiRequest, NextApiResponse } from 'next';
import { prisma } from '../../../utils/prisma';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const users = await prisma.user.findMany();
    res.status(200).json(users);
  } catch (error) {
    console.error('Error fetching users:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense! Added here: 2c7bce5.

Didn't add type of req originally since it's not being used 😅 but yeah I guess it's nicer

Comment on lines +35 to +37
if (Number.isNaN(id)) {
return res.status(400).json({ error: 'Invalid ID provided' });
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same logic as in the GET block. I would extract it and run it before.

}

return res.status(200).json(user);
} catch {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be helpful to have a console.error here as well

}

return res.status(200).json(updatedUser);
} catch {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a console.error here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recommended file changes

import type { NextApiRequest, NextApiResponse } from 'next';
import { prisma } from '../../../../utils/prisma';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const id = Number(req.query.id);

  if (Number.isNaN(id)) {
    return res.status(400).json({ error: 'Invalid ID provided' });
  }

  if (req.method === 'GET') {
    const getAbsences = req.query.getAbsences === 'true';

    try {
      const user = await prisma.user.findUnique({
        where: { id },
        include: { absences: getAbsences },
      });

      if (!user) {
        return res.status(404).json({ error: 'User not found' });
      }

      return res.status(200).json(user);
    } catch (error) {
      console.error('Error fetching user:', error);
      return res.status(500).json({ error: 'Internal server error' });
    }
  } else if (req.method === 'PATCH') {
    const { email, firstName, lastName, role, status } = req.body;
    const id = Number(req.query.id as string);

    if (Number.isNaN(id)) {
      return res.status(400).json({ error: 'Invalid ID provided' });
    }

    try {
      const updatedUser = await prisma.user.update({
        where: { id },
        data: { email, firstName, lastName, role, status },
      });

      if (!updatedUser) {
        return res.status(400).json({ error: 'User not found' });
      }

      return res.status(200).json(updatedUser);
    } catch (error) {
      console.error('Error updating user:', error);
      return res.status(500).json({ error: 'Internal server error' });
    }
  } else {
    return res.status(405).json({ error: 'Method not allowed' });
  }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep fair! Added: 2c7bce5

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion on this file:

import { useEffect, useState } from 'react';
interface User {
  id: number;
  firstName: string;
  lastName: string;
  email: string;
  role: string;
  status: string;
  numOfAbsences: string;
}

export default function AnotherPage() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    const fetchUsers = async () => {
      const apiUrl = `/api/users/`;

      try {
        const response = await fetch(apiUrl);
        if (!response.ok) {
          throw new Error(response.statusText);
        }
        const data: User[] = await response.json();
        setUsers(data);
      } catch (error: unknown) {
        if (error instanceof Error) {
          console.error('Error fetching users:', error.message);
        }
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []);

  const updateUserRole = async (userId: number, newRole: string) => {
    const confirmed = window.confirm('Confirm change role to ' + newRole);
    if (!confirmed) return;

    const apiUrl = `/api/users/${userId}`;
    const originalUsers = [...users];

    setUsers((prevUsers) =>
      prevUsers.map((user) =>
        user.id === userId ? { ...user, role: newRole } : user
      )
    );

    try {
      const response = await fetch(apiUrl, {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ role: newRole }),
      });

      if (!response.ok) {
        setUsers(originalUsers);
        throw new Error(response.statusText);
      }
    } catch (error: unknown) {
      if (error instanceof Error) {
        console.error('Error updating user:', error.message);
      }
    }
  };

  return (
    <div>
      <h1>Admin Management</h1>
      {loading ? (
        <p>Loading...</p>
      ) : (
        <table>
          <thead>
            <tr>
              <th>Name</th>
              <th>Email</th>
              <th>Role</th>
            </tr>
          </thead>
          <tbody>
            {users.map((user) => (
              <tr key={user.id}>
                <td>
                  {user.firstName} {user.lastName}
                </td>
                <td>{user.email}</td>
                <td>
                  <select
                    value={user.role}
                    onChange={(e) => updateUserRole(user.id, e.target.value)}
                  >
                    <option value="TEACHER">Teacher</option>
                    <option value="ADMIN">Admin</option>
                  </select>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it more to have a separate loading state.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah loading is nice, done: 2c7bce5

@135ze 135ze requested review from a team and VJagiasi and removed request for a team November 15, 2024 16:50
@135ze 135ze merged commit 650fe98 into main Nov 20, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants