@@ -7,20 +7,24 @@ import {
77 sendRuntimeMessage ,
88 type ExtensionMessage ,
99} from "@/lib/browser/messages" ;
10- import { PlusIcon , SpinnerIcon } from "@phosphor-icons/react" ;
10+ import { PlusIcon , SignInIcon , SpinnerIcon } from "@phosphor-icons/react" ;
1111import React , { useCallback , useEffect , useState } from "react" ;
1212import { browser } from "wxt/browser" ;
1313import Button from "@/components/ui/button" ;
1414import logo from "@/public/logo.png" ;
15+ import { isLoggedIn } from "@/features/audit-scraping/audit-history-sync" ;
1516import PopupAuditCard from "./popup-audit-card" ;
1617
18+ const AUDIT_HOME_URL = "https://utdirect.utexas.edu/apps/degree/audits/" ;
19+
1720export default function App ( ) {
1821 const [ audits , setAudits ] = useState < AuditHistoryEntry [ ] > ( [ ] ) ;
1922 const [ loading , setLoading ] = useState ( true ) ;
2023 const [ error , setError ] = useState < string | null > ( null ) ;
2124 const [ showAll , setShowAll ] = useState ( false ) ;
2225 const [ runningAudit , setRunningAudit ] = useState ( false ) ;
2326 const [ isSyncing , setIsSyncing ] = useState ( false ) ;
27+ const [ loggedIn , setLoggedIn ] = useState < boolean | null > ( null ) ;
2428
2529 const applyAuditHistory = useCallback ( ( data : AuditHistoryData | null ) => {
2630 if ( data ?. error ) {
@@ -35,21 +39,19 @@ export default function App() {
3539 // Load audit history from cached storage
3640 // Storage is updated ONLY when user visits UT Direct audits home page
3741 // This allows popup to work from any page using cached data
38- const refreshAudits = useCallback ( async ( ) => {
39- try {
40- applyAuditHistory ( await getAuditHistory ( ) ) ;
41- } catch ( e ) {
42- console . error ( "Error loading audit history:" , e ) ;
43- setError ( "Failed to load audit history" ) ;
44- }
42+ useEffect ( ( ) => {
43+ getAuditHistory ( )
44+ . then ( applyAuditHistory )
45+ . catch ( ( ) => setError ( "Failed to load audit history" ) )
46+ . finally ( ( ) => setLoading ( false ) ) ;
4547 } , [ applyAuditHistory ] ) ;
4648
4749 useEffect ( ( ) => {
48- refreshAudits ( ) . finally ( ( ) => setLoading ( false ) ) ;
49- } , [ refreshAudits ] ) ;
50+ void isLoggedIn ( ) . then ( setLoggedIn ) ;
51+ } , [ ] ) ;
5052
5153 useEffect ( ( ) => {
52- // get sycn status for ui
54+ // get sync status for ui
5355 sendRuntimeMessage ( { type : "GET_SYNC_STATUS" } )
5456 . then ( ( response ) => {
5557 if ( response ?. isSyncing ) {
@@ -92,9 +94,19 @@ export default function App() {
9294 sendRuntimeMessage ( { type : "RUN_NEW_AUDIT" } ) ;
9395 } ;
9496
97+ const handleLogin = ( ) => {
98+ void browser . tabs . create ( { url : AUDIT_HOME_URL , active : true } ) ;
99+ } ;
100+
95101 // Determine which audits to display
96102 const displayedAudits = showAll ? audits : audits . slice ( 0 , 3 ) ;
97103 const hasMoreAudits = audits . length > 3 ;
104+ const needsLogin = loggedIn === false && audits . length === 0 ;
105+ // An authenticated empty history is a valid state, even if an older sync cached an error.
106+ const hasAuthenticatedEmptyHistory = loggedIn === true && audits . length === 0 ;
107+ // Only the empty state depends on auth (Login vs. empty); with cached audits we
108+ // can render immediately. So wait for auth ONLY when there's nothing to show yet.
109+ const resolvingLogin = loggedIn === null && audits . length === 0 ;
98110
99111 return (
100112 < div className = "w-[438px] h-full min-h-[300px] max-h-[600px] bg-background font-sans overflow-hidden flex flex-col border border-gray-100" >
@@ -113,8 +125,20 @@ export default function App() {
113125 </ div >
114126
115127 < div className = "flex items-center space-x-3" >
116- < Button className = "rounded-md" onClick = { handleRerunAudit } >
117- { runningAudit ? (
128+ < Button
129+ className = "rounded-md"
130+ onClick = { needsLogin ? handleLogin : handleRerunAudit }
131+ >
132+ { resolvingLogin ? (
133+ < div className = "flex items-center space-x-2" >
134+ < SpinnerIcon size = { 24 } className = "animate-spin-slow" />
135+ </ div >
136+ ) : needsLogin ? (
137+ < div className = "flex items-center space-x-2" >
138+ < SignInIcon size = { 24 } />
139+ < p className = "text-lg font-bold" > Login</ p >
140+ </ div >
141+ ) : runningAudit ? (
118142 < div className = "flex items-center space-x-2" >
119143 < SpinnerIcon size = { 24 } className = "animate-spin-slow" />
120144 < p className = "text-lg font-bold" > Running Audit...</ p >
@@ -125,6 +149,7 @@ export default function App() {
125149 < p className = "text-lg font-bold" > Run New Audit</ p >
126150 </ div >
127151 ) }
152+
128153 </ Button >
129154 </ div >
130155 </ header >
@@ -149,8 +174,13 @@ export default function App() {
149174
150175 { loading ? (
151176 < div className = "flex flex-col gap-2 items-center justify-center text-center mb-6 py-8" >
152- < p className = "text-base text-dap-gray-light" >
153- Loading audit history...
177+ < p className = "text-base text-dap-gray-light" > Syncing...</ p >
178+ </ div >
179+ ) : needsLogin ? (
180+ < div className = "flex flex-col gap-2 items-center justify-center text-center mb-6 py-8" >
181+ < p className = "text-base text-dap-gray-light tracking-[0.32px] max-w-[300px]" >
182+ Log in to UT Direct, then visit the Degree Audit page to load your
183+ audits.
154184 </ p >
155185 </ div >
156186 ) : error ? (
0 commit comments