RL-Project / frontend /src /App.jsx
Shubham Sattigeri
fix: SSO dotenv path and auth routes
c5f36f3
Raw
History Blame Contribute Delete
8.31 kB
import React, { useState, useEffect } from 'react'
import {
initiateLogin,
initiateLogout,
getCurrentUser,
recommend,
feedback,
stats,
getUsers,
getContents,
} from './api'
export default function App() {
const [authUser, setAuthUser] = useState(null)
const [authChecked, setAuthChecked] = useState(false)
const [ssoEnabled, setSsoEnabled] = useState(false)
const [userId, setUserId] = useState('')
const [contentId, setContentId] = useState('')
const [algorithm, setAlgorithm] = useState('linucb')
const [users, setUsers] = useState([])
const [contents, setContents] = useState([])
const [result, setResult] = useState(null)
const [impressionId, setImpressionId] = useState(null)
const [statsData, setStatsData] = useState(null)
const [statusMsg, setStatusMsg] = useState('')
async function refreshSession() {
setStatusMsg('Checking authentication...')
try {
const auth = await getCurrentUser()
setAuthUser(auth.user)
setSsoEnabled(auth.sso_enabled)
if (auth.user) {
await loadLists()
}
setStatusMsg('')
} catch (e) {
setStatusMsg('Unable to verify authentication: ' + (e.message || e))
} finally {
setAuthChecked(true)
}
}
async function loadLists() {
try {
const [u, c] = await Promise.all([getUsers(), getContents()])
setUsers(u || [])
setContents(c || [])
if (u?.length && !userId) setUserId(u[0].user_id)
if (c?.length && !contentId) setContentId(c[0].content_id)
} catch (e) {
setStatusMsg('Failed to load users or contents: ' + (e.message || e))
}
}
async function handleRecommend() {
setStatusMsg('Requesting recommendation...')
try {
const res = await recommend(userId, contentId, algorithm)
setResult(res)
setImpressionId(res.impression_id)
setStatsData(null)
setStatusMsg(`Recommendation delivered in ${Math.round(res.latency_ms)} ms`)
} catch (e) {
setStatusMsg('Error: ' + (e.message || e))
}
}
async function handleFeedback(reward = 1.0) {
if (!impressionId) {
setStatusMsg('No recommendation available for feedback')
return
}
setStatusMsg('Sending feedback...')
try {
await feedback(impressionId, reward)
setStatusMsg('Feedback recorded successfully')
} catch (e) {
setStatusMsg('Error: ' + (e.message || e))
}
}
async function handleStats() {
setStatusMsg('Loading production stats...')
try {
const s = await stats()
setStatsData(s)
setStatusMsg('Stats loaded')
} catch (e) {
setStatusMsg('Error: ' + (e.message || e))
}
}
useEffect(() => {
refreshSession()
}, [])
const isReady = authChecked && (!!authUser || !authUser)
return (
<div className="container">
<header className="topbar">
<div>
<p className="eyebrow">Artwork Bandit</p>
<h1>Recommendation Studio</h1>
<p className="subtitle">A modern SSO dashboard for artwork recommendation, feedback, and performance insights.</p>
</div>
<div className="profile-bar">
{authChecked ? (
authUser ? (
<>
<div className="profile-info">
<span>{authUser.name}</span>
<small>{authUser.email}</small>
</div>
<button className="secondary" onClick={initiateLogout}>Sign out</button>
</>
) : ssoEnabled ? (
<button className="primary" onClick={initiateLogin}>Sign in with SSO</button>
) : (
<div className="profile-info">
<span>SSO is not configured</span>
<small>Please set SSO environment variables to enable login.</small>
</div>
)
) : (
<span>Checking login...</span>
)}
</div>
</header>
{!authUser ? (
<main className="hero-card">
<div>
<h2>Secure access with SSO</h2>
<p>Sign in to access the artwork recommendation engine, track model performance, and share feedback securely.</p>
<button className="primary" onClick={initiateLogin} disabled={!ssoEnabled}>
{ssoEnabled ? 'Start with SSO' : 'SSO not configured'}
</button>
</div>
<div className="hero-aside">
<p>Configured backend SSO keeps your recommendation experiments protected while preserving the same fast feedback loop.</p>
{!ssoEnabled && <p className="empty-state">Set <code>SSO_DISCOVERY_URL</code>, <code>SSO_CLIENT_ID</code>, and <code>SSO_CLIENT_SECRET</code> to enable login.</p>}
</div>
</main>
) : (
<main>
<section className="dashboard-grid">
<section className="panel controls-panel">
<h2>Recommendation controls</h2>
<label>
Dataset user
<select value={userId} onChange={(e) => setUserId(e.target.value)}>
{users.map((u) => (
<option key={u.user_id} value={u.user_id}>{u.user_id}{u.mood ? ` — ${u.mood}` : ''}</option>
))}
</select>
</label>
<label>
Content topic
<select value={contentId} onChange={(e) => setContentId(e.target.value)}>
{contents.map((c) => (
<option key={c.content_id} value={c.content_id}>{c.title || c.content_id}</option>
))}
</select>
</label>
<label>
Algorithm
<select value={algorithm} onChange={(e) => setAlgorithm(e.target.value)}>
<option value="linucb">LinUCB</option>
<option value="thompson">Thompson Sampling</option>
</select>
</label>
<div className="actions">
<button className="primary" onClick={handleRecommend}>Recommend artwork</button>
<button onClick={() => handleFeedback(1.0)}>Submit click</button>
<button onClick={() => handleFeedback(0.0)}>Submit no click</button>
<button className="secondary" onClick={handleStats}>View stats</button>
</div>
</section>
<section className="panel result-panel">
<h2>Latest recommendation</h2>
{result ? (
<div className="card result-card">
<img src={result.artwork_image || ''} alt={result.artwork_id} />
<div className="card-body">
<h3>{result.artwork_id}</h3>
<p>Algorithm: {result.algorithm}</p>
<p>Impression ID: {result.impression_id}</p>
<p>Latency: {Math.round(result.latency_ms)} ms</p>
</div>
</div>
) : (
<p className="empty-state">No recommendation requested yet. Use the controls to generate your first prediction.</p>
)}
</section>
</section>
<section className="panel stats-panel">
<h2>Live KPI dashboard</h2>
{statsData ? (
<div className="stats-grid">
<div className="stat-card">
<span>Total impressions</span>
<strong>{statsData.total_impressions}</strong>
</div>
<div className="stat-card">
<span>Total clicks</span>
<strong>{statsData.total_clicks}</strong>
</div>
<div className="stat-card">
<span>Overall CTR</span>
<strong>{(statsData.overall_ctr * 100).toFixed(1)}%</strong>
</div>
</div>
) : (
<p className="empty-state">Run the stats query to inspect production performance.</p>
)}
</section>
</main>
)}
<footer>
<p>{statusMsg || 'Ready'}</p>
<small>Backend session auth is enabled when SSO credentials are configured.</small>
</footer>
</div>
)
}