Skip to content

Prof Invites#425

Open
AdamFipke wants to merge 30 commits intomainfrom
adam/prof-invite-link
Open

Prof Invites#425
AdamFipke wants to merge 30 commits intomainfrom
adam/prof-invite-link

Conversation

@AdamFipke
Copy link
Collaborator

@AdamFipke AdamFipke commented Nov 7, 2025

Description

  • Adds Professor Invites (admin-only)
    • Expires (default after 1 week)
    • Has X number of uses (defaults to 1)
    • Emails the admin who created the invite when the invite gets consumed (as well as many edge and error cases, including names of those that attempt to use it)
    • Works the same as course invites, meaning it works with users that don't have accounts yet, aren't logged in, etc.
    • Note that admins don't need to actually be in the course to create a prof invite.
image image image
  • Remembered to showcase it in the Organization Member Role History
image image
  • Comes with an admin view to show all prof invites (partially for fun, partially so that us admins can easily track what all of us are doing)
image
  • Transformed the ProfessorSelector into its own component (it's used in 3 areas).
    • Added emails (since some admins have multiple accounts)
    • Fixed the search with it too
image
  • After creating a course, admins now immediately get directed to creating a professor invite
image
  • Added a highlightedCourse query param to /courses for a slight highlight. Right now it's only used when an org prof creates a course to highlight it. Could probably be used in some other situations.
image image
  • Refactored Queue+Course invites to use new QUERY_PARAMS constant (to easily connect query params between frontend and backend).
  • Made it so organization-roles.guard.ts can take an orgId param (not just oid), and to also throw a 500error if no orgId was given. Throws a 400 error if !Number(orgId)

Closes #267

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update
  • This requires a run of yarn install
  • This change requires an addition/change to the production .env variables. These changes are below:
  • This change requires developers to add new .env variables. The file and variables needed are below:
  • This change requires a database query to update old data on production. This query is below:

How Has This Been Tested?

  • Integration tests

  • Service tests

  • Modified expectEmailsSent to allow one to test the email body & subject (and will test to make sure there's no "null" or "undefined" or "object Object" or "{}" in the body & subject.

Checklist:

  • I have performed a code review of my own code (under the "Files Changed" tab on github) to ensure nothing is committed that shouldn't be (e.g. leftover console.logs, leftover unused logic, or anything else that was accidentally committed)
  • I have commented my code where needed
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have checked that new and existing tests pass locally with my changes
  • Any work that this PR is dependent on has been merged into the main branch
  • Any UI changes have been checked to work on desktop, tablet, and mobile

…_PARAMS object for organizing query params throughout codebase (mostly to make it easier to find where a particular query param is used)
…ImplicitConversion for req() inside our api, allowing us to deseralize dates automatically again.
…to the professor selector (to identify them better). Also fixed a bug where the search wasn't working (optionFilterProp was 'label' even though 'label' is a react element. For future devs, just add a custom data to the Select items like I did here).

Integration tests are located in the `test` folder.

`yarn test` at root level runs all tests, but you can also selectively run tests by running `yarn test` while inside a package.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It took me so long to figure out what this meant and now I feel silly. It's literally just saying "if you run yarn test in packages/frontend it will run frontend tests. if you run yarn test in packages/server it will run backend tests."

We don't have frontend tests, so I just removed it to reduce confusion

Comment on lines +28 to +39
// trying out useSWRImmutable as an experiment.
// It's basically the same as having 3 useState variables (data, error, isLoading) condensed into one
// Difference between "useSWR" and "useSWRImmutable" is that this disables all the extra API calls and validation n whatnot that comes loaded with swr
const {
data: profInvites,
error,
isLoading,
mutate: mutateProfInvites,
} = useSWRImmutable(
`organization/${orgId}/profInvites`,
async () => await API.profInvites.getAll(orgId),
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this ended up working pretty good i think! It will still repeatedly call the backend if it fails, but other than that it's pretty much a condensed version of having 3 state variables. Might try this more in the future


expect(invite).not.toHaveProperty('organization');

expect(res.body).toMatchSnapshot([inviteMatcher]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is hella nice, certainly gonna be using this more. Makes testing for certain properties way nicer.

Also for what inviteMatcher is: from the code above:

// used when testing with snapshots to say "hey, don't test for exact values for these" since they're gonna be different each time
  const inviteMatcher = {
    code: expect.any(String),
    createdAt: expect.any(String),
    expiresAt: expect.any(String),
  };

So in this instance it's saying "hey, the response body should match this snapshot that is an array with 1 object in it with a code, createdAt, and expiresAt properties"

@AdamFipke AdamFipke marked this pull request as ready for review December 23, 2025 00:54
@AdamFipke AdamFipke requested a review from bhunt02 December 23, 2025 00:54
OrgRoleChangeReasonMap[
history.OrganizationRoleHistory_changeReason
],
changeReason: history.OrganizationRoleHistory_roleChangeReason,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this change was needed because the roleChangeReason wasn't actually getting returned to the frontend

constructor(
private jwtService: JwtService,
private configService: ConfigService,
private profInviteService: ProfInviteService,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just wanted to double check with you @bhunt02 since I know this service isn't normal due to the LTI stuff. Will adding profInviteService (which depends on OrganizationService) as one if its nestjs dependencies break anything with the LTI stuff? It seems to work fine (like there's no nestjs errors) but I just want to make sure I'm not missing something else in another file.

Only mentioning this since it seems enter() wants to pass in courseService rather than have it as a dependency up here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

IIRC, I made CourseService get optionally passed in to enter() as the LTI stuff does not want courseservice-induced redirects to occur, so this wouldn't be a problem I think? it's just due to cookies that might be hanging around in the courseservice case

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah I see leftover cookies does make a compelling case (this sentence sounds like I'm talking about proving the existence of Santa lol).

This does make me want to double check some things, like if i'm clearing the cookie on error cases

@AdamFipke
Copy link
Collaborator Author

okay finally got around and double-checked to make sure the prof invites were still working after the LTI merge and it seems like it, should be good to review now @bhunt02

@AdamFipke AdamFipke requested review from bhunt02 and removed request for bhunt02 February 3, 2026 06:25
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.

Prof invite link

2 participants