Ryan Christian D. Deniega commited on
Commit
f7e9353
·
1 Parent(s): 376fe09

ux: improve landing — fix step visuals, add try-it teaser input, expand footer with nav links

Browse files
frontend/src/index.css CHANGED
@@ -308,6 +308,11 @@ a.nav-link-item:hover > div {
308
  .landing-steps {
309
  flex-direction: column;
310
  }
 
 
 
 
 
311
  }
312
 
313
  /* ── Verdict breaking-news banner ──────────────────────── */
 
308
  .landing-steps {
309
  flex-direction: column;
310
  }
311
+
312
+ .footer-grid {
313
+ grid-template-columns: 1fr !important;
314
+ gap: 32px !important;
315
+ }
316
  }
317
 
318
  /* ── Verdict breaking-news banner ──────────────────────── */
frontend/src/pages/LandingPage.jsx CHANGED
@@ -1,5 +1,6 @@
1
- import { Link } from 'react-router-dom'
2
- import { FileText, Link2, Image, Video, ArrowRight, Database, ShieldCheck } from 'lucide-react'
 
3
  import { PAGE_STYLE } from '../App.jsx'
4
 
5
  /* ─── data ──────────────────────────────────────────────────── */
@@ -50,11 +51,23 @@ const STEPS = [
50
  const STATS = [
51
  { value: '70M+', label: 'Filipinos online', sub: 'one of the highest social media usage rates in the world' },
52
  { value: '6×', label: 'faster spread', sub: 'misinformation travels six times faster than verified news' },
53
- { value: '2026', label: 'election year', sub: 'disinformation campaigns at all-time high during election cycles' },
54
  ]
55
 
56
  /* ─── component ─────────────────────────────────────────────── */
57
  export default function LandingPage() {
 
 
 
 
 
 
 
 
 
 
 
 
58
  return (
59
  <div style={{ background: 'var(--bg-base)', overflowX: 'hidden' }}>
60
 
@@ -367,6 +380,67 @@ export default function LandingPage() {
367
  </div>
368
  </section>
369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  {/* ── How It Works ─────────────────────────────────────── */}
371
  <section style={{ borderBottom: '1px solid var(--border)', background: 'var(--bg-surface)' }}>
372
  <div style={{ ...PAGE_STYLE, paddingTop: 80, paddingBottom: 80 }}>
@@ -410,53 +484,61 @@ export default function LandingPage() {
410
  padding: '0 40px 0 0',
411
  borderRight: i < STEPS.length - 1 ? '1px solid var(--border)' : 'none',
412
  marginRight: i < STEPS.length - 1 ? 40 : 0,
413
- paddingBottom: 0,
414
  }}
415
  >
416
- <div
417
- style={{
418
- display: 'flex',
419
- alignItems: 'center',
420
- gap: 12,
421
- marginBottom: 24,
422
- }}
423
- >
424
- <span
425
- style={{
426
- fontFamily: 'var(--font-mono)',
427
- fontSize: 'clamp(2rem, 4vw, 3rem)',
428
- fontWeight: 700,
429
- color: 'var(--bg-elevated)',
430
- letterSpacing: '-0.04em',
431
- lineHeight: 1,
432
- WebkitTextStroke: '1px var(--border-light)',
433
- }}
434
- >
435
- {num}
436
- </span>
437
  <div
438
  style={{
439
- width: 1,
440
  height: 40,
441
- background: 'var(--border)',
 
 
 
 
 
 
442
  }}
443
- />
 
 
 
 
444
  <div>
445
- <div
 
 
 
 
 
 
 
 
 
 
 
 
446
  style={{
447
  fontFamily: 'var(--font-display)',
448
- fontSize: 11,
449
  fontWeight: 700,
450
- letterSpacing: '0.25em',
451
  textTransform: 'uppercase',
452
- color: 'var(--accent-red)',
453
  }}
454
  >
455
  {label}
456
- </div>
457
- <Icon size={18} strokeWidth={1.5} style={{ color: 'var(--text-secondary)', marginTop: 4 }} />
458
  </div>
459
  </div>
 
 
 
 
 
 
 
460
  <p
461
  style={{
462
  fontFamily: 'var(--font-body)',
@@ -541,45 +623,137 @@ export default function LandingPage() {
541
  </section>
542
 
543
  {/* ── Footer ───────────────────────────────────────────── */}
544
- <footer
545
- style={{
546
- borderTop: '1px solid var(--border)',
547
- background: 'var(--bg-base)',
548
- }}
549
- >
550
  <div
551
  style={{
552
  ...PAGE_STYLE,
553
- paddingTop: 32,
554
- paddingBottom: 32,
555
- display: 'flex',
556
- justifyContent: 'space-between',
557
- alignItems: 'center',
558
- flexWrap: 'wrap',
559
- gap: 16,
560
  }}
 
561
  >
562
- <span
563
- style={{
 
564
  fontFamily: 'var(--font-display)',
565
  fontSize: 13,
566
  fontWeight: 800,
567
  letterSpacing: '0.1em',
 
 
 
 
 
 
 
 
568
  color: 'var(--text-muted)',
569
- }}
570
- >
571
- PHIL·VERIFY
572
- </span>
573
- <span
574
- style={{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  fontFamily: 'var(--font-mono)',
576
- fontSize: 11,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
  color: 'var(--text-muted)',
578
  letterSpacing: '0.1em',
579
- }}
580
- >
581
- ML2 Final Project &nbsp;·&nbsp; MIT License &nbsp;·&nbsp; {new Date().getFullYear()}
582
- </span>
583
  </div>
584
  </footer>
585
 
 
1
+ import { useState } from 'react'
2
+ import { Link, useNavigate } from 'react-router-dom'
3
+ import { FileText, Link2, Image, Video, ArrowRight, Database, ShieldCheck, Github, BookOpen, Clock, TrendingUp } from 'lucide-react'
4
  import { PAGE_STYLE } from '../App.jsx'
5
 
6
  /* ─── data ──────────────────────────────────────────────────── */
 
51
  const STATS = [
52
  { value: '70M+', label: 'Filipinos online', sub: 'one of the highest social media usage rates in the world' },
53
  { value: '6×', label: 'faster spread', sub: 'misinformation travels six times faster than verified news' },
54
+ { value: '3.8B', label: 'fake engagements', sub: 'estimated fake-news interactions on PH social media annually' },
55
  ]
56
 
57
  /* ─── component ─────────────────────────────────────────────── */
58
  export default function LandingPage() {
59
+ const navigate = useNavigate()
60
+ const [tryInput, setTryInput] = useState('')
61
+
62
+ function handleTrySubmit(e) {
63
+ e.preventDefault()
64
+ if (tryInput.trim()) {
65
+ navigate('/verify', { state: { prefill: tryInput.trim() } })
66
+ } else {
67
+ navigate('/verify')
68
+ }
69
+ }
70
+
71
  return (
72
  <div style={{ background: 'var(--bg-base)', overflowX: 'hidden' }}>
73
 
 
380
  </div>
381
  </section>
382
 
383
+ {/* ── Inline CTA Teaser ─────────────────────────────── */}
384
+ <section style={{ borderBottom: '1px solid var(--border)', background: 'var(--bg-elevated)' }}>
385
+ <div style={{ ...PAGE_STYLE, paddingTop: 48, paddingBottom: 48 }}>
386
+ <p style={{
387
+ fontFamily: 'var(--font-mono)',
388
+ fontSize: 11,
389
+ letterSpacing: '0.2em',
390
+ textTransform: 'uppercase',
391
+ color: 'var(--text-muted)',
392
+ marginBottom: 16,
393
+ }}>
394
+ Try it now — no account needed
395
+ </p>
396
+ <form onSubmit={handleTrySubmit} style={{ display: 'flex', gap: 0, maxWidth: 640 }}>
397
+ <input
398
+ type="text"
399
+ value={tryInput}
400
+ onChange={e => setTryInput(e.target.value)}
401
+ placeholder="Paste any claim, headline, or URL…"
402
+ style={{
403
+ flex: 1,
404
+ background: 'var(--bg-surface)',
405
+ border: '1px solid var(--border)',
406
+ borderRight: 'none',
407
+ color: 'var(--text-primary)',
408
+ fontFamily: 'var(--font-body)',
409
+ fontSize: 15,
410
+ padding: '14px 20px',
411
+ outline: 'none',
412
+ }}
413
+ onFocus={e => e.target.style.borderColor = 'var(--accent-red)'}
414
+ onBlur={e => e.target.style.borderColor = 'var(--border)'}
415
+ />
416
+ <button
417
+ type="submit"
418
+ style={{
419
+ background: 'var(--accent-red)',
420
+ color: '#fff',
421
+ border: 'none',
422
+ fontFamily: 'var(--font-display)',
423
+ fontSize: 12,
424
+ fontWeight: 700,
425
+ letterSpacing: '0.15em',
426
+ textTransform: 'uppercase',
427
+ padding: '14px 24px',
428
+ cursor: 'pointer',
429
+ display: 'flex',
430
+ alignItems: 'center',
431
+ gap: 8,
432
+ whiteSpace: 'nowrap',
433
+ transition: 'background 0.2s ease-out',
434
+ }}
435
+ onMouseEnter={e => e.currentTarget.style.background = '#b91c1c'}
436
+ onMouseLeave={e => e.currentTarget.style.background = 'var(--accent-red)'}
437
+ >
438
+ Verify <ArrowRight size={14} strokeWidth={2.5} />
439
+ </button>
440
+ </form>
441
+ </div>
442
+ </section>
443
+
444
  {/* ── How It Works ─────────────────────────────────────── */}
445
  <section style={{ borderBottom: '1px solid var(--border)', background: 'var(--bg-surface)' }}>
446
  <div style={{ ...PAGE_STYLE, paddingTop: 80, paddingBottom: 80 }}>
 
484
  padding: '0 40px 0 0',
485
  borderRight: i < STEPS.length - 1 ? '1px solid var(--border)' : 'none',
486
  marginRight: i < STEPS.length - 1 ? 40 : 0,
 
487
  }}
488
  >
489
+ {/* Step number pill */}
490
+ <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 20 }}>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  <div
492
  style={{
493
+ width: 40,
494
  height: 40,
495
+ borderRadius: 2,
496
+ background: i === 0 ? 'var(--accent-red)' : 'var(--bg-elevated)',
497
+ border: i === 0 ? 'none' : '1px solid var(--border-light)',
498
+ display: 'flex',
499
+ alignItems: 'center',
500
+ justifyContent: 'center',
501
+ flexShrink: 0,
502
  }}
503
+ >
504
+ <Icon size={16} strokeWidth={1.8}
505
+ style={{ color: i === 0 ? '#fff' : 'var(--text-secondary)' }}
506
+ />
507
+ </div>
508
  <div>
509
+ <span
510
+ style={{
511
+ fontFamily: 'var(--font-mono)',
512
+ fontSize: 10,
513
+ fontWeight: 700,
514
+ color: 'var(--text-muted)',
515
+ letterSpacing: '0.2em',
516
+ display: 'block',
517
+ }}
518
+ >
519
+ {num}
520
+ </span>
521
+ <span
522
  style={{
523
  fontFamily: 'var(--font-display)',
524
+ fontSize: 12,
525
  fontWeight: 700,
526
+ letterSpacing: '0.2em',
527
  textTransform: 'uppercase',
528
+ color: i === 0 ? 'var(--accent-red)' : 'var(--text-primary)',
529
  }}
530
  >
531
  {label}
532
+ </span>
 
533
  </div>
534
  </div>
535
+ {/* Connector line under icon for non-last items */}
536
+ <div style={{
537
+ width: 24,
538
+ height: 2,
539
+ background: i === 0 ? 'var(--accent-red)' : 'var(--border)',
540
+ marginBottom: 20,
541
+ }} />
542
  <p
543
  style={{
544
  fontFamily: 'var(--font-body)',
 
623
  </section>
624
 
625
  {/* ── Footer ───────────────────────────────────────────── */}
626
+ <footer style={{ borderTop: '1px solid var(--border)', background: 'var(--bg-base)' }}>
 
 
 
 
 
627
  <div
628
  style={{
629
  ...PAGE_STYLE,
630
+ paddingTop: 48,
631
+ paddingBottom: 48,
632
+ display: 'grid',
633
+ gridTemplateColumns: 'auto 1fr auto',
634
+ gap: 48,
635
+ alignItems: 'start',
 
636
  }}
637
+ className="footer-grid"
638
  >
639
+ {/* Brand */}
640
+ <div>
641
+ <div style={{
642
  fontFamily: 'var(--font-display)',
643
  fontSize: 13,
644
  fontWeight: 800,
645
  letterSpacing: '0.1em',
646
+ color: 'var(--text-primary)',
647
+ marginBottom: 8,
648
+ }}>
649
+ PHIL<span style={{ color: 'var(--accent-red)' }}>VERIFY</span>
650
+ </div>
651
+ <div style={{
652
+ fontFamily: 'var(--font-mono)',
653
+ fontSize: 10,
654
  color: 'var(--text-muted)',
655
+ letterSpacing: '0.1em',
656
+ lineHeight: 1.6,
657
+ }}>
658
+ ML2 Final Project<br />
659
+ MIT License · {new Date().getFullYear()}
660
+ </div>
661
+ </div>
662
+
663
+ {/* Nav links */}
664
+ <div style={{ display: 'flex', gap: 48, flexWrap: 'wrap' }}>
665
+ <div>
666
+ <div style={{
667
+ fontFamily: 'var(--font-display)',
668
+ fontSize: 10,
669
+ fontWeight: 700,
670
+ letterSpacing: '0.2em',
671
+ textTransform: 'uppercase',
672
+ color: 'var(--text-muted)',
673
+ marginBottom: 12,
674
+ }}>App</div>
675
+ {[
676
+ { label: 'Verify', to: '/verify', internal: true },
677
+ { label: 'History', to: '/history', internal: true },
678
+ { label: 'Trends', to: '/trends', internal: true },
679
+ ].map(({ label, to, internal }) => (
680
+ <div key={label} style={{ marginBottom: 8 }}>
681
+ {internal
682
+ ? <Link to={to} style={{
683
+ fontFamily: 'var(--font-body)',
684
+ fontSize: 13,
685
+ color: 'var(--text-secondary)',
686
+ textDecoration: 'none',
687
+ transition: 'color 0.15s ease-out',
688
+ }}
689
+ onMouseEnter={e => e.currentTarget.style.color = 'var(--text-primary)'}
690
+ onMouseLeave={e => e.currentTarget.style.color = 'var(--text-secondary)'}
691
+ >{label}</Link>
692
+ : null
693
+ }
694
+ </div>
695
+ ))}
696
+ </div>
697
+ <div>
698
+ <div style={{
699
+ fontFamily: 'var(--font-display)',
700
+ fontSize: 10,
701
+ fontWeight: 700,
702
+ letterSpacing: '0.2em',
703
+ textTransform: 'uppercase',
704
+ color: 'var(--text-muted)',
705
+ marginBottom: 12,
706
+ }}>Resources</div>
707
+ {[
708
+ { label: 'API Docs', href: 'https://semiautomat1c-philverify-api.hf.space/docs' },
709
+ { label: 'GitHub', href: 'https://github.com/SemiAutomat1c/philverify' },
710
+ ].map(({ label, href }) => (
711
+ <div key={label} style={{ marginBottom: 8 }}>
712
+ <a href={href} target="_blank" rel="noopener noreferrer" style={{
713
+ fontFamily: 'var(--font-body)',
714
+ fontSize: 13,
715
+ color: 'var(--text-secondary)',
716
+ textDecoration: 'none',
717
+ transition: 'color 0.15s ease-out',
718
+ }}
719
+ onMouseEnter={e => e.currentTarget.style.color = 'var(--text-primary)'}
720
+ onMouseLeave={e => e.currentTarget.style.color = 'var(--text-secondary)'}
721
+ >{label}</a>
722
+ </div>
723
+ ))}
724
+ </div>
725
+ </div>
726
+
727
+ {/* Disclaimer */}
728
+ <div style={{ maxWidth: 220 }}>
729
+ <div style={{
730
  fontFamily: 'var(--font-mono)',
731
+ fontSize: 10,
732
+ color: 'var(--text-muted)',
733
+ lineHeight: 1.6,
734
+ letterSpacing: '0.03em',
735
+ }}>
736
+ For research and educational purposes only. Use responsibly when verifying information on social media.
737
+ </div>
738
+ </div>
739
+ </div>
740
+
741
+ {/* Bottom rule */}
742
+ <div style={{ borderTop: '1px solid var(--border)' }}>
743
+ <div style={{
744
+ ...PAGE_STYLE,
745
+ paddingTop: 16,
746
+ paddingBottom: 16,
747
+ }}>
748
+ <span style={{
749
+ fontFamily: 'var(--font-mono)',
750
+ fontSize: 10,
751
  color: 'var(--text-muted)',
752
  letterSpacing: '0.1em',
753
+ }}>
754
+ Built with FastAPI · React · Whisper · Tesseract · NewsAPI
755
+ </span>
756
+ </div>
757
  </div>
758
  </footer>
759
 
frontend/src/pages/VerifyPage.jsx CHANGED
@@ -1,4 +1,5 @@
1
  import { useState, useRef, useId, useCallback, useEffect } from 'react'
 
2
  import { api } from '../api'
3
  import { scoreColor, VERDICT_MAP, scoreInterpretation, mlConfidenceExplanation, evidenceExplanation } from '../utils/format.js'
4
  import { PAGE_STYLE } from '../App.jsx'
@@ -233,9 +234,16 @@ function saveState(state) {
233
  /* ── Main Page ──────────────────────────────────────────── */
234
  export default function VerifyPage() {
235
  const persisted = loadPersistedState()
 
 
236
 
237
  const [tab, setTab] = useState(persisted?.tab ?? 'text')
238
- const [input, setInput] = useState(persisted?.input ?? '')
 
 
 
 
 
239
  const [file, setFile] = useState(null)
240
  const [fileObjectUrl, setFileObjectUrl] = useState(null)
241
  const [dragOver, setDragOver] = useState(false)
 
1
  import { useState, useRef, useId, useCallback, useEffect } from 'react'
2
+ import { useLocation } from 'react-router-dom'
3
  import { api } from '../api'
4
  import { scoreColor, VERDICT_MAP, scoreInterpretation, mlConfidenceExplanation, evidenceExplanation } from '../utils/format.js'
5
  import { PAGE_STYLE } from '../App.jsx'
 
234
  /* ── Main Page ──────────────────────────────────────────── */
235
  export default function VerifyPage() {
236
  const persisted = loadPersistedState()
237
+ const location = useLocation()
238
+ const prefill = location.state?.prefill ?? null
239
 
240
  const [tab, setTab] = useState(persisted?.tab ?? 'text')
241
+ const [input, setInput] = useState(prefill ?? persisted?.input ?? '')
242
+
243
+ // Clear navigation state so refresh doesn't re-prefill
244
+ useEffect(() => {
245
+ if (prefill) window.history.replaceState({}, '')
246
+ }, [prefill])
247
  const [file, setFile] = useState(null)
248
  const [fileObjectUrl, setFileObjectUrl] = useState(null)
249
  const [dragOver, setDragOver] = useState(false)