Skip to content

Feature/dental UI saving and auth#5958

Open
bitbossing wants to merge 14 commits into
OHIF:masterfrom
bitbossing:feature/dental-ui-saving-and-auth
Open

Feature/dental UI saving and auth#5958
bitbossing wants to merge 14 commits into
OHIF:masterfrom
bitbossing:feature/dental-ui-saving-and-auth

Conversation

@bitbossing
Copy link
Copy Markdown

@bitbossing bitbossing commented Apr 13, 2026

C) User Preference Saving & Authentication
📝 Create & clone a branch to work on Task C
📝 Setup Firebase Project and Create a Login Page
📝 Create Logout button
📝 Save User Preference on Theme

Greptile Summary

This PR adds Google/Firebase authentication (login page, auth guard, user-info dropdown, logout) and a dental-theme preference saved to localStorage. The integration is feature-flagged behind REACT_APP_FIREBASE_API_KEY and is mutually exclusive with existing OIDC, keeping the default path unaffected.

Three issues need attention before merge:

  • The <div> separator in Header.tsx is unconditional, adding a phantom divider in every non-Firebase build.
  • WorkList always passes <FirebaseUserInfo /> as UserInfo regardless of config, amplifying the divider problem.
  • onSignIn() in LoginPage is not awaited, so popup-dismissed / network errors are silently swallowed and the button stays permanently disabled.

Confidence Score: 3/5

Not safe to merge — the unconditional header divider is a visible regression for all non-dental users, and the login error-handling bug leaves users stuck with a disabled button.

Three P1 issues: a UI regression in the shared Header component (affects all users), an always-on Firebase import in WorkList (compounded divider issue), and a broken error-propagation path in the login flow that produces a stuck UI state. The .env tracking issue is a latent security risk. All are straightforward fixes but the header regression alone touches the critical shared path.

platform/ui-next/src/components/Header/Header.tsx, platform/app/src/routes/WorkList/WorkList.tsx, platform/app/src/routes/LoginPage.tsx, platform/app/.env

Security Review

  • Credential exposure risk (platform/app/.env): Firebase API keys are added to a git-tracked .env file. Adding .env to .gitignore does not untrack an already-tracked file, so if a developer fills in real credentials, they will be committed to history. Use .env.example as the template and untrack .env with git rm --cached.

Important Files Changed

Filename Overview
platform/ui-next/src/components/Header/Header.tsx Adds UserInfo slot to header but the separator <div> renders unconditionally, producing a spurious divider for all non-Firebase builds.
platform/app/src/routes/WorkList/WorkList.tsx Always passes <FirebaseUserInfo /> as UserInfo regardless of whether Firebase is configured, causing spurious dividers in non-Firebase deployments.
platform/app/src/routes/LoginPage.tsx New Google sign-in page; onSignIn() is not awaited so async errors are never caught, leaving the button permanently disabled on failure with no error shown.
platform/app/.env Adds Firebase env-var stubs to the tracked .env file; .gitignore change is ineffective for already-tracked files, risking future credential exposure.
platform/app/src/utils/FirebaseAuthRoutes.tsx New Firebase auth wrapper with Google sign-in; correctly uses onAuthStateChanged for state management; minor dependency-array gap in useEffect.
platform/app/src/components/FirebaseUserInfo.tsx New user-info dropdown with logout; safely returns empty fragment when no user is present; uses referrerPolicy="no-referrer" on avatar image appropriately.
platform/app/src/App.tsx Integrates Firebase auth behind a feature flag (REACT_APP_FIREBASE_API_KEY) that is mutually exclusive with OIDC; logic is clean and non-breaking for existing auth flows.
extensions/24x7-dental-ui/src/dentalThemeManager.ts Module-singleton theme manager using localStorage and DOM class; restore() is called at module-evaluation time as a side effect, which is acceptable for browser builds.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: platform/ui-next/src/components/Header/Header.tsx
Line: 124-125

Comment:
**Divider always renders when UserInfo is undefined**

The separator `<div>` is placed unconditionally before `{UserInfo}`. When Firebase is disabled (the common case), `UserInfo` is `undefined`, but the divider still renders — adding a spurious vertical rule in the header for every non-Firebase user.

```suggestion
            {UserInfo && (
              <>
                <div className="border-muted mx-1.5 h-[25px] border-r"></div>
                {UserInfo}
              </>
            )}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: platform/app/src/routes/WorkList/WorkList.tsx
Line: 561

Comment:
**`FirebaseUserInfo` rendered unconditionally in WorkList**

`<FirebaseUserInfo />` is always passed as `UserInfo` regardless of whether Firebase is configured. In non-Firebase deployments `FirebaseUserInfo` returns `<></>`, but the separator `<div>` in `Header.tsx` will still render (see related header comment). The component should only be passed when Firebase is actually enabled — mirroring the guard used in `App.tsx`.

```suggestion
        UserInfo={process.env.REACT_APP_FIREBASE_API_KEY ? <FirebaseUserInfo /> : undefined}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: platform/app/src/routes/LoginPage.tsx
Line: 14-22

Comment:
**Sign-in errors are never surfaced to the user**

`onSignIn()` is not awaited and the `onSignIn` interface declares `() => void`, so any rejection from `signInWithPopup` is swallowed inside `FirebaseAuthRoutes.handleSignIn`. The `catch` block in `handleGoogleSignIn` never fires on async failures. When the popup is dismissed or a network error occurs, `isSigningIn` stays `true` permanently — the button shows "Redirecting…" with no way to retry. `onSignIn` should be typed as `() => Promise<void>` and awaited here so errors can be caught and displayed.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: platform/app/.env
Line: 12-17

Comment:
**`.env` is tracked in git — `.gitignore` change is ineffective**

`platform/app/.env` is already a tracked file, so adding it to `.gitignore` in this PR will not stop git from tracking future changes to it. Any developer who fills in real Firebase API keys will commit them to history. The conventional approach is:

1. Rename `platform/app/.env``platform/app/.env.example` (keep tracked as a template with empty values).
2. Have each developer create their own `platform/app/.env` locally (untracked).
3. Run `git rm --cached platform/app/.env` to stop tracking the current file.

The `.gitignore` entry for `.env` is correct as written, but it only takes effect once the file is untracked.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: platform/app/src/utils/FirebaseAuthRoutes.tsx
Line: 41-49

Comment:
**Missing dependency in `useEffect`**

`userAuthenticationService` is referenced inside the first `useEffect` but is not listed in its dependency array. ESLint's `exhaustive-deps` rule would flag this. While the component is likely mounted once, adding the dependency makes the intent explicit and prevents subtle bugs if the service reference ever changes.

```suggestion
  useEffect(() => {
    userAuthenticationService.set({ enabled: true });
    userAuthenticationService.setServiceImplementation({
      getAuthorizationHeader,
      handleUnauthenticated: () => null,
      reset: () => signOut(firebaseAuth),
    });
  }, [userAuthenticationService]);
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "📝 Save User Preference on Theme" | Re-trigger Greptile

Greptile also left 5 inline comments on this PR.

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

📝 Create a new mode [dental-ui] "Dental Mode" base on Longitudinal Mode and to refrain from editing original code
…o refrain from editing original code

📝 Add a button to toggle theme on the Header (change UI's color, font, icon)
○ Top-left: Current image
○ Top-right: Prior exam (Empty if none)
○ Bottom: Bitewing placeholders)
📝 Create new set of Measurement tool for Dental Mode which save its measurement on the new Dental Measurement Panel that automatically renames depending on tool name with auto increment.
○ PA Length : uses Periapical Length Tool
○ Canal Angle : uses Canal Angle Tool
○ Crown Width : uses Periapical Length Tool
○ Root Length : uses Periapical Length Tool
📝 Setup Firebase Project and Create a Login Page
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 13, 2026

Deploy Preview for ohif-dev ready!

Name Link
🔨 Latest commit d668220
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/69dcccb499765200080a45ca
😎 Deploy Preview https://deploy-preview-5958--ohif-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Comment on lines +124 to +125
<div className="border-muted mx-1.5 h-[25px] border-r"></div>
{UserInfo}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Divider always renders when UserInfo is undefined

The separator <div> is placed unconditionally before {UserInfo}. When Firebase is disabled (the common case), UserInfo is undefined, but the divider still renders — adding a spurious vertical rule in the header for every non-Firebase user.

Suggested change
<div className="border-muted mx-1.5 h-[25px] border-r"></div>
{UserInfo}
{UserInfo && (
<>
<div className="border-muted mx-1.5 h-[25px] border-r"></div>
{UserInfo}
</>
)}
Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/ui-next/src/components/Header/Header.tsx
Line: 124-125

Comment:
**Divider always renders when UserInfo is undefined**

The separator `<div>` is placed unconditionally before `{UserInfo}`. When Firebase is disabled (the common case), `UserInfo` is `undefined`, but the divider still renders — adding a spurious vertical rule in the header for every non-Firebase user.

```suggestion
            {UserInfo && (
              <>
                <div className="border-muted mx-1.5 h-[25px] border-r"></div>
                {UserInfo}
              </>
            )}
```

How can I resolve this? If you propose a fix, please make it concise.

isReturnEnabled={false}
WhiteLabeling={appConfig.whiteLabeling}
showPatientInfo={PatientInfoVisibility.DISABLED}
UserInfo={<FirebaseUserInfo />}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 FirebaseUserInfo rendered unconditionally in WorkList

<FirebaseUserInfo /> is always passed as UserInfo regardless of whether Firebase is configured. In non-Firebase deployments FirebaseUserInfo returns <></>, but the separator <div> in Header.tsx will still render (see related header comment). The component should only be passed when Firebase is actually enabled — mirroring the guard used in App.tsx.

Suggested change
UserInfo={<FirebaseUserInfo />}
UserInfo={process.env.REACT_APP_FIREBASE_API_KEY ? <FirebaseUserInfo /> : undefined}
Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/app/src/routes/WorkList/WorkList.tsx
Line: 561

Comment:
**`FirebaseUserInfo` rendered unconditionally in WorkList**

`<FirebaseUserInfo />` is always passed as `UserInfo` regardless of whether Firebase is configured. In non-Firebase deployments `FirebaseUserInfo` returns `<></>`, but the separator `<div>` in `Header.tsx` will still render (see related header comment). The component should only be passed when Firebase is actually enabled — mirroring the guard used in `App.tsx`.

```suggestion
        UserInfo={process.env.REACT_APP_FIREBASE_API_KEY ? <FirebaseUserInfo /> : undefined}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +14 to +22
try {
onSignIn();
} catch (err: any) {
console.error('Google sign-in error:', err);
setError(err.message || 'Sign-in failed. Please try again.');
setIsSigningIn(false);
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Sign-in errors are never surfaced to the user

onSignIn() is not awaited and the onSignIn interface declares () => void, so any rejection from signInWithPopup is swallowed inside FirebaseAuthRoutes.handleSignIn. The catch block in handleGoogleSignIn never fires on async failures. When the popup is dismissed or a network error occurs, isSigningIn stays true permanently — the button shows "Redirecting…" with no way to retry. onSignIn should be typed as () => Promise<void> and awaited here so errors can be caught and displayed.

Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/app/src/routes/LoginPage.tsx
Line: 14-22

Comment:
**Sign-in errors are never surfaced to the user**

`onSignIn()` is not awaited and the `onSignIn` interface declares `() => void`, so any rejection from `signInWithPopup` is swallowed inside `FirebaseAuthRoutes.handleSignIn`. The `catch` block in `handleGoogleSignIn` never fires on async failures. When the popup is dismissed or a network error occurs, `isSigningIn` stays `true` permanently — the button shows "Redirecting…" with no way to retry. `onSignIn` should be typed as `() => Promise<void>` and awaited here so errors can be caught and displayed.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread platform/app/.env
Comment on lines +12 to +17

REACT_APP_FIREBASE_API_KEY=
REACT_APP_FIREBASE_AUTH_DOMAIN=
REACT_APP_FIREBASE_PROJECT_ID=
REACT_APP_FIREBASE_STORAGE_BUCKET=
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 security .env is tracked in git — .gitignore change is ineffective

platform/app/.env is already a tracked file, so adding it to .gitignore in this PR will not stop git from tracking future changes to it. Any developer who fills in real Firebase API keys will commit them to history. The conventional approach is:

  1. Rename platform/app/.envplatform/app/.env.example (keep tracked as a template with empty values).
  2. Have each developer create their own platform/app/.env locally (untracked).
  3. Run git rm --cached platform/app/.env to stop tracking the current file.

The .gitignore entry for .env is correct as written, but it only takes effect once the file is untracked.

Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/app/.env
Line: 12-17

Comment:
**`.env` is tracked in git — `.gitignore` change is ineffective**

`platform/app/.env` is already a tracked file, so adding it to `.gitignore` in this PR will not stop git from tracking future changes to it. Any developer who fills in real Firebase API keys will commit them to history. The conventional approach is:

1. Rename `platform/app/.env``platform/app/.env.example` (keep tracked as a template with empty values).
2. Have each developer create their own `platform/app/.env` locally (untracked).
3. Run `git rm --cached platform/app/.env` to stop tracking the current file.

The `.gitignore` entry for `.env` is correct as written, but it only takes effect once the file is untracked.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +41 to +49
userAuthenticationService.set({ enabled: true });
userAuthenticationService.setServiceImplementation({
getAuthorizationHeader,
handleUnauthenticated: () => null,
reset: () => signOut(firebaseAuth),
});
}, []);

useEffect(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing dependency in useEffect

userAuthenticationService is referenced inside the first useEffect but is not listed in its dependency array. ESLint's exhaustive-deps rule would flag this. While the component is likely mounted once, adding the dependency makes the intent explicit and prevents subtle bugs if the service reference ever changes.

Suggested change
userAuthenticationService.set({ enabled: true });
userAuthenticationService.setServiceImplementation({
getAuthorizationHeader,
handleUnauthenticated: () => null,
reset: () => signOut(firebaseAuth),
});
}, []);
useEffect(() => {
useEffect(() => {
userAuthenticationService.set({ enabled: true });
userAuthenticationService.setServiceImplementation({
getAuthorizationHeader,
handleUnauthenticated: () => null,
reset: () => signOut(firebaseAuth),
});
}, [userAuthenticationService]);
Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/app/src/utils/FirebaseAuthRoutes.tsx
Line: 41-49

Comment:
**Missing dependency in `useEffect`**

`userAuthenticationService` is referenced inside the first `useEffect` but is not listed in its dependency array. ESLint's `exhaustive-deps` rule would flag this. While the component is likely mounted once, adding the dependency makes the intent explicit and prevents subtle bugs if the service reference ever changes.

```suggestion
  useEffect(() => {
    userAuthenticationService.set({ enabled: true });
    userAuthenticationService.setServiceImplementation({
      getAuthorizationHeader,
      handleUnauthenticated: () => null,
      reset: () => signOut(firebaseAuth),
    });
  }, [userAuthenticationService]);
```

How can I resolve this? If you propose a fix, please make it concise.

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.

1 participant