Skip to content

[OA][Fullstack] - OA Websockets#278

Merged
LOTaher merged 4 commits into
mainfrom
273/heartbeat
Apr 7, 2026
Merged

[OA][Fullstack] - OA Websockets#278
LOTaher merged 4 commits into
mainfrom
273/heartbeat

Conversation

@LOTaher
Copy link
Copy Markdown
Collaborator

@LOTaher LOTaher commented Apr 6, 2026

[OA][Fullstack] - OA Websockets

Changes

  • Added POST /api/token to generate a JWT that the WS server will verify
  • Updated WS server to handle client connections using JWT authentication.
  • Created useHeartbeat hook that allows the client to make continuous websocket requests to the server
  • Created LostConnectionModal component to be displayed when the candidate disconnects. This also displays the AssessmentSkeleton component in the background.
  • When disconnected the isConnected value from the useHeartbeat hook triggers to false and changes the state of the page. This changes back to true when the client and server can establish a connection again

Notes

  • There is currently no time bank on the websocket server side. This is v1 of the implementation which simply lets the candidate know they are disconnected. On the first disconnection, the OA will submit, and when reconnected, the candidate should be brought to the AssessmentOutro component FOR NOW.
  • Our JWT token is hardcoded for easy testing

Screenshots

https://sandboxneu.slack.com/archives/C09E7L2RK16/p1775507945604729?thread_ts=1775507637.130809&cid=C09E7L2RK16


Checklist

Please go through all items before requesting reviewers:

  • All commits are tagged with the ticket number
  • No linting errors / newline warnings
  • All code follows repository-configured formatting
  • No merge conflicts
  • All checks passing
  • Screenshots included for UI changes
  • Remove non-applicable sections of this template
  • PR assigned to yourself
  • Reviewers requested & Slack ping sent
  • PR linked to the issue (fill in 'Closes #')
  • If design-related, notify the designer in Slack

Closes

Closes #273

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sarge Ready Ready Preview, Comment Apr 7, 2026 3:32am

Request Review


return (
<div className="flex h-screen w-full flex-col overflow-hidden">
{/* onOpenChange is returning nothing as we don't have recovery implemented just yet */}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

when you say "recovery," do you mean a client disconnects and then reconnects after a brief delay?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this is to tee us nicely for handling us having a seperate countdown for clients who have disconnected, and give them a chance to reconnect.

@@ -0,0 +1,61 @@
import { Skeleton } from '@/lib/components/ui/Skeleton';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Something to note (I saw this post abt this package that does exact skeleton according to the content - not really needed here but could be something so that skeleton is just a wrapper and we don't have to do this in the future) not prio tho

<ResizableHandle className="bg-sarge-gray-200 w-px" />

<ResizablePanel defaultSize={65} minSize={40}>
<div className="flex h-full flex-col overflow-hidden bg-[#3a414f]">
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

are we just doing this hex bc we have nothing in the design system? (idt we have skeleton so would be fine if not)

Copy link
Copy Markdown
Collaborator Author

@LOTaher LOTaher Apr 6, 2026

Choose a reason for hiding this comment

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

This hex is the same color as our monacode text editor theme

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think eventually we'll make a design token for this then I know olivia has new designs for the editor

<button
type="button"
<Button
variant="icon"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

yeah this is better


return {
...ws,
isConnected: ws.readyState === ReadyState.OPEN,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this will show the modal when we start the oa - the is connected starts as false

(I think it will correct itself) but we should show this modal if there has been a connection established and then lost

could add a hasconnected flag that becomes true on the WS reaches open state

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not too sure if I understand what you're saying here. This shows the Modal if the WS connection is not open. The server will always be open on our end, so if the client disconnects, it goes to a CLOSED state, meaning that isConnected would be false

@bderbs30
Copy link
Copy Markdown
Collaborator

bderbs30 commented Apr 6, 2026

Also I think the timer will still keep counting down when they're disconnected

@bderbs30
Copy link
Copy Markdown
Collaborator

bderbs30 commented Apr 6, 2026

I think opening multiple tabs could cause issues as well since the map is keyed by the email

so if we have tab A connected, then tab B connects which overwrites Tab A's entry??? so then if Tab A disconnects (which removes the entry) then Tab b's socket would be left without a mapping

idk if this is intended, but I know I've accidentally opened two tabs during an oa so this would kinda break this instance (not common, but could be something that would happen)


const jwt = await new SignJWT({ email })
.setProtectedHeader({ alg: 'HS256' })
.setExpirationTime('2h')
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

does the jwt regen throughout the oa at all bc couldn't an oa be longer than 2h so this would cut someone off early

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No it doesn't! What's the "max time" you think an OA would be right now. We need some sort of limit just for token lifetimes and WS stuff.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

hmm ig the longest I've taken is 90 minutes so maybe 2 hours is fine for now

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ok! Could totally be temporary


export async function POST(request: NextRequest) {
try {
// TODO: add route security
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have we made a ticket for this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not yet. Will be written, but not urgent for showcase

Comment thread src/lib/schemas/token.schema.ts Outdated
@@ -0,0 +1,5 @@
import z from 'zod';

export const TokenSchema = z.object({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should name this to something that signifies it is for the OA only.

@LOTaher LOTaher merged commit 2a0f43e into main Apr 7, 2026
4 checks passed
cherman23 pushed a commit that referenced this pull request Apr 12, 2026
assessment outro

update outro

update intro

make prettier

[OA][Fullstack] - OA Websockets (#278)

* progress

* modal + handling skeleton and stateful code editor

* prettier

* update tokenschema name

[OA][Fullstack] - OA Websockets - follow up (#278)

window unfocused modal (#279)

* window unfocused modal

* PR fixes

update button
cherman23 pushed a commit that referenced this pull request Apr 12, 2026
assessment outro

update outro

update intro

make prettier

[OA][Fullstack] - OA Websockets (#278)

* progress

* modal + handling skeleton and stateful code editor

* prettier

* update tokenschema name

[OA][Fullstack] - OA Websockets - follow up (#278)

window unfocused modal (#279)

* window unfocused modal

* PR fixes

update button
Anzhuo-W added a commit that referenced this pull request Apr 12, 2026
assessment outro

update outro

update intro

make prettier

[OA][Fullstack] - OA Websockets (#278)

* progress

* modal + handling skeleton and stateful code editor

* prettier

* update tokenschema name

[OA][Fullstack] - OA Websockets - follow up (#278)

window unfocused modal (#279)

* window unfocused modal

* PR fixes

update button
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.

[OA][Fullstack] - OA Websockets

3 participants