Add welcome page and update related styles and HTML#7
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new welcome/landing experience for the frontend and refreshes the UI styling/layout, alongside adding Tailwind + new font dependencies/configuration.
Changes:
- Added a new
WelcomePageclient component and updated the home page flow to show it before the main app. - Refreshed global styling (CSS variables, layout grid, and new AI chat panel styling) and updated multiple UI components to match.
- Added Tailwind configuration and PostCSS setup changes, plus new UI/dependency updates (Geist font, lucide icons).
Reviewed changes
Copilot reviewed 18 out of 27 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/tsconfig.json | Formatting changes and JSX emit mode update. |
| frontend/tailwind.config.js | Adds Tailwind content scanning configuration. |
| frontend/postcss.config.mjs | Modifies PostCSS plugin config (currently empty). |
| frontend/postcss.config.js | Adds Tailwind + Autoprefixer PostCSS plugins. |
| frontend/package.json | Adds Geist/lucide + Tailwind toolchain deps and lint script. |
| frontend/package-lock.json | Lockfile updated for new dependencies. |
| frontend/next.config.js | Minor formatting change. |
| frontend/lib/mockData.js | Makes index “updated” date dynamic at runtime. |
| frontend/jsconfig.json | Minor formatting change. |
| frontend/global.d.ts | Adds a module declaration for *.css. |
| frontend/coverage360_clean.html | Large static HTML/CSS redesign including welcome page + app layout. |
| frontend/components/WelcomePage.jsx | New welcome/landing page component (client). |
| frontend/components/TrustBar.jsx | Updates footer date display to current year. |
| frontend/components/Topbar.jsx | Simplifies top bar to logo + actions; changes props. |
| frontend/components/Sidebar.jsx | Minor formatting change. |
| frontend/components/SearchHero.jsx | Makes recent searches configurable and adds empty state. |
| frontend/components/CoverageTable.jsx | Updates table header/layout styling. |
| frontend/components/ChatWidget.jsx | Redesigns chat widget into right-side “AI panel” layout with textarea input. |
| frontend/app/page.js | Adds welcome gating and adjusts layout (chat panel moved to right column). |
| frontend/app/layout.tsx | Switches font setup to Geist package fonts. |
| frontend/app/layout.js | Minor formatting change (but now coexists with layout.tsx). |
| frontend/app/globals.css | Adds Tailwind directives + major style refresh + AI panel styles. |
| frontend/app/api/payers/route.js | Changes /api/payers behavior to drug lookup (breaking existing consumers). |
| frontend/app/api/chat/route.js | Changes backend proxy/fallback behavior for chat responses. |
Files not reviewed (1)
- frontend/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export default function RootLayout({ children }) { | ||
| return ( | ||
| <html lang="en"> | ||
| <body>{children}</body> | ||
| </html> |
There was a problem hiding this comment.
There are now two root layout files in the same app/ segment (layout.tsx and layout.js). In Next.js App Router this can cause a build/runtime error (multiple root layouts) or unexpected metadata/styling depending on which one is picked up. Remove one of them (typically keep layout.tsx) and merge any needed metadata/markup into the remaining layout.
| plugins: { | ||
| "@tailwindcss/postcss": {}, | ||
| }, | ||
| plugins: {}, |
There was a problem hiding this comment.
postcss.config.mjs now has an empty plugins object while Tailwind directives were added to app/globals.css and a separate postcss.config.js was introduced with Tailwind + Autoprefixer. Having two PostCSS config files (and one disabling plugins) is likely to prevent Tailwind from running depending on which config is resolved. Use a single PostCSS config and ensure it includes tailwindcss and autoprefixer (or delete the unused config file).
| plugins: {}, | |
| plugins: { | |
| tailwindcss: {}, | |
| autoprefixer: {}, | |
| }, |
| import { lookupDrug } from '@/lib/mockData' | ||
|
|
||
| const BACKEND_URL = process.env.BACKEND_URL | ||
| export async function GET(request) { | ||
| const { searchParams } = new URL(request.url) | ||
| const query = searchParams.get('q') ?? '' | ||
|
|
||
| export async function GET() { | ||
| if (!BACKEND_URL) { | ||
| return Response.json(PAYERS.map(name => ({ name }))) | ||
| } | ||
| // TODO: replace with real backend call: | ||
| // const res = await fetch(`${process.env.BACKEND_URL}/search/drug/${encodeURIComponent(query)}`) | ||
| // return Response.json(await res.json()) | ||
|
|
||
| let res | ||
| try { | ||
| res = await fetch(`${BACKEND_URL}/payers`, { cache: 'no-store' }) | ||
| } catch { | ||
| return Response.json(PAYERS.map(name => ({ name }))) | ||
| const drug = lookupDrug(query) | ||
| if (!drug) { | ||
| return Response.json({ error: 'Not found' }, { status: 404 }) | ||
| } | ||
|
|
||
| if (!res.ok) return Response.json(PAYERS.map(name => ({ name }))) | ||
|
|
||
| const data = await res.json() | ||
| return Response.json(data) // [{ id, name, short_name, type, ... }] | ||
| } | ||
| return Response.json(drug) | ||
| } No newline at end of file |
There was a problem hiding this comment.
/api/payers has been repurposed to return a drug lookup result based on q, but other parts of the app (e.g. getPayers() used by app/compare/page.tsx) expect this route to return a list of payers. This will break the compare flow (and any other payer-dependent UI). Please restore /api/payers to return payers and introduce a separate route for drug lookup/search if needed.
| "next": "14.2.5", | ||
| "react": "^18", | ||
| "react-dom": "^18" | ||
| "react": "^18.3.1", | ||
| "react-dom": "^18.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "25.5.2", | ||
| "@types/react": "19.2.14", | ||
| "autoprefixer": "^10.4.27", | ||
| "postcss": "^8.5.8", | ||
| "tailwindcss": "^3.4.19", | ||
| "typescript": "6.0.2" |
There was a problem hiding this comment.
@types/react is pinned to a React 19 types version while react/react-dom are React 18.x. This mismatch can introduce incorrect typings and break TypeScript builds (especially around refs/JSX types). Align @types/react (and usually @types/react-dom) to the installed React major version.
| export async function POST(request) { | ||
| const { question, drug } = await request.json() | ||
| if (!question?.trim()) { | ||
| return Response.json({ error: 'question is required' }, { status: 400 }) | ||
| } | ||
|
|
||
| // Fallback to mock when no backend is configured | ||
| if (!BACKEND_URL) { | ||
| return Response.json(mockChatAnswer(question, drug)) | ||
| } | ||
|
|
||
| let res | ||
| try { | ||
| res = await fetch(`${BACKEND_URL}/chat`, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| // Backend only takes `question`; `drug` context is handled by its RAG retrieval | ||
| body: JSON.stringify({ question }), | ||
| }) | ||
| } catch { | ||
| return Response.json({ error: 'Backend unreachable' }, { status: 502 }) | ||
| if (BACKEND_URL) { | ||
| try { | ||
| const res = await fetch(`${BACKEND_URL}/chat`, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ question, drug }), | ||
| }) | ||
| if (res.ok) return Response.json(await res.json()) | ||
| } catch { | ||
| // fall through to mock | ||
| } | ||
| } | ||
|
|
||
| if (!res.ok) { | ||
| const err = await res.text() | ||
| return Response.json({ error: err || 'Backend error' }, { status: res.status }) | ||
| } | ||
|
|
||
| const data = await res.json() | ||
|
|
||
| // Backend returns sources as string[], UI expects a single string | ||
| const sources = Array.isArray(data.sources) | ||
| ? data.sources.join(' · ') | ||
| : (data.sources ?? '') | ||
|
|
||
| return Response.json({ answer: data.answer, sources }) | ||
| } | ||
| const result = mockChatAnswer(question, drug) | ||
| return Response.json(result) | ||
| } No newline at end of file |
There was a problem hiding this comment.
The chat API now returns backend responses as-is when BACKEND_URL is set, but the UI expects a consistent sources shape (today mockChatAnswer() returns a string; many backends return sources as string[]). Also, backend non-2xx responses currently fall through to the mock, which can mask real failures. Consider (1) validating question and returning 400 for empty input, (2) normalizing sources to a single consistent type, and (3) surfacing backend errors instead of silently swapping to mock data.
| <button className="px-4 py-2 rounded-full bg-white border border-[#e2e8f0] text-[#3e5161] text-xs font-medium hover:bg-[#f7f9fc] hover:border-[#91bfeb] transition-all"> | ||
| Sign In | ||
| </button> | ||
| <button className="px-4 py-2 rounded-full bg-[#3e5161] text-white text-xs font-medium hover:shadow-lg hover:shadow-[#3e5161]/30 transition-all"> |
There was a problem hiding this comment.
The "Get Started" button in the top bar is missing an onClick handler, so it won't do anything. If the intent is to dismiss the welcome screen (and/or start a search), wire this button to onGetStarted (or to handleSearch) to match the rest of the welcome CTA behavior.
| <button className="px-4 py-2 rounded-full bg-[#3e5161] text-white text-xs font-medium hover:shadow-lg hover:shadow-[#3e5161]/30 transition-all"> | |
| <button | |
| onClick={onGetStarted} | |
| className="px-4 py-2 rounded-full bg-[#3e5161] text-white text-xs font-medium hover:shadow-lg hover:shadow-[#3e5161]/30 transition-all" | |
| > |
| policies: 847, | ||
| payers: 3, | ||
| updated: 'Apr 4, 2026', | ||
| updated: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }), |
There was a problem hiding this comment.
INDEX_STATS.updated is now computed from the current date at runtime. This makes the UI show "Updated " even when the underlying indexed-policy snapshot hasn’t changed, and it will vary by user locale/timezone. Consider keeping this value as a fixed timestamp for the dataset (or deriving it from real backend metadata) rather than using new Date().
| updated: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }), | |
| updated: 'Feb 1, 2026', |
| <div class="w-right" style="display:flex;align-items:center;justify-content:flex-end;margin: left 1px;"> | ||
| <img src="public/nurse.png" alt="Medical Professional" style="width:550px;height:auto;filter:drop-shadow(0 20px 40px rgba(62,81,97,0.15));" /> |
There was a problem hiding this comment.
This inline style is invalid CSS (margin: left 1px;) and the image source public/nurse.png likely won’t resolve correctly when served (in Next.js, public assets are typically referenced as /nurse.png). Fix the margin declaration and update the image src to a deploy-safe path.
| <div class="w-right" style="display:flex;align-items:center;justify-content:flex-end;margin: left 1px;"> | |
| <img src="public/nurse.png" alt="Medical Professional" style="width:550px;height:auto;filter:drop-shadow(0 20px 40px rgba(62,81,97,0.15));" /> | |
| <div class="w-right" style="display:flex;align-items:center;justify-content:flex-end;margin-left:1px;"> | |
| <img src="/nurse.png" alt="Medical Professional" style="width:550px;height:auto;filter:drop-shadow(0 20px 40px rgba(62,81,97,0.15));" /> |
| <span className="logo-main">Coverage</span> | ||
| <span className="logo-360">360</span> | ||
| <span className="logo-tag">by Anton Rx · Analyst Portal</span> | ||
| <img src="/logo.png" alt="Coverage360" style={{height:'120px',width:'auto'}} /> |
There was a problem hiding this comment.
subtitle is accepted as a prop but never used, which can mislead callers and makes the component signature harder to understand. Either render the subtitle somewhere in the top bar (e.g., under the logo) or remove the unused prop/default.
| <img src="/logo.png" alt="Coverage360" style={{height:'120px',width:'auto'}} /> | |
| <img src="/logo.png" alt="Coverage360" style={{height:'120px',width:'auto'}} /> | |
| {subtitle && <div>{subtitle}</div>} |
| // Show welcome page first | ||
| if (showWelcome) { | ||
| return <WelcomePage onGetStarted={() => setShowWelcome(false)} /> | ||
| } |
There was a problem hiding this comment.
On the welcome screen, the search box requires a non-empty query to call onGetStarted(), but onGetStarted currently only hides the welcome page and does not propagate the query or trigger handleSearch(). This means users who type a drug on the welcome page still have to re-enter it on the main screen. Consider passing the query up (e.g., onGetStarted(searchQuery)) and calling handleSearch from the parent, or allow the welcome CTA to proceed without implying a search will run.
Policy diff and evidence panel
add alert pipeline, differ, check_updates, github actions
Expanded README to include detailed project overview, features, tech stack, project structure, supported payers, key data fields, and getting started instructions.
Enhance policy diff and evidence panel with alert pipeline and README
No description provided.