seo update

This commit is contained in:
2026-04-08 11:01:53 +05:30
parent de7c1d5ad8
commit eefdf32aa8
16 changed files with 607 additions and 29 deletions

View File

@@ -2917,11 +2917,17 @@
font-weight: 600 !important;
}
@keyframes tour-btn-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.6; transform: scale(0.96); }
}
.gj-tour-popover .driver-popover-next-btn {
background: var(--color-primary, #22c55e) !important;
color: #fff !important;
border: none !important;
text-shadow: none !important;
animation: tour-btn-pulse 1.2s ease-in-out infinite !important;
}
.gj-tour-popover .driver-popover-prev-btn {

View File

@@ -12,8 +12,9 @@ function App() {
<AuthProvider>
<BrowserRouter>
<Routes>
<Route path="/" element={<LoginPage />} />
<Route
path="/"
path="/write"
element={
<ProtectedRoute>
<HomePage />
@@ -36,8 +37,7 @@ function App() {
</ProtectedRoute>
}
/>
<Route path="/login" element={<LoginPage />} />
<Route path="*" element={<Navigate to="/login" replace />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</BrowserRouter>
</AuthProvider>

View File

@@ -21,8 +21,8 @@ export default function BottomNav() {
{/* Write */}
<button
type="button"
className={`bottom-nav-btn ${isActive('/') ? 'bottom-nav-btn-active' : ''}`}
onClick={() => navigate('/')}
className={`bottom-nav-btn ${isActive('/write') ? 'bottom-nav-btn-active' : ''}`}
onClick={() => navigate('/write')}
aria-label="Write"
>
{/* Pencil / edit icon */}

View File

@@ -16,7 +16,7 @@ export function ProtectedRoute({ children }: Props) {
}
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />
return <Navigate to="/" state={{ from: location }} replace />
}
return <>{children}</>

View File

@@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react'
import { useCallback, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { driver, type DriveStep } from 'driver.js'
import 'driver.js/dist/driver.css'
@@ -128,14 +128,17 @@ function getSettingsSteps(isMobile: boolean): DriveStep[] {
export function useOnboardingTour() {
const navigate = useNavigate()
const driverRef = useRef<ReturnType<typeof driver> | null>(null)
const [isTourActive, setIsTourActive] = useState(false)
const startTour = useCallback(() => {
const isMobile = window.innerWidth < 860
setIsTourActive(true)
const driverObj = driver({
...driverDefaults(),
onDestroyStarted: () => {
clearPendingTourStep()
setIsTourActive(false)
driverObj.destroy()
},
onNextClick: () => {
@@ -145,6 +148,7 @@ export function useOnboardingTour() {
// Last home step → navigate to /history
if (activeIndex === steps.length - 1) {
localStorage.setItem(TOUR_PENDING_KEY, 'history')
setIsTourActive(false)
driverObj.destroy()
navigate('/history')
return
@@ -206,7 +210,7 @@ export function useOnboardingTour() {
if (activeIndex === steps.length - 1) {
clearPendingTourStep()
driverObj.destroy()
navigate('/')
navigate('/write')
return
}
@@ -219,5 +223,5 @@ export function useOnboardingTour() {
setTimeout(() => driverObj.drive(), 300)
}, [navigate])
return { startTour, continueTourOnHistory, continueTourOnSettings }
return { startTour, continueTourOnHistory, continueTourOnSettings, isTourActive }
}

View File

@@ -43,7 +43,7 @@ export default function HomePage() {
const titleInputRef = useRef<HTMLInputElement>(null)
const contentTextareaRef = useRef<HTMLTextAreaElement>(null)
const { startTour } = useOnboardingTour()
const { startTour, isTourActive } = useOnboardingTour()
// Check if onboarding should be shown after login
useEffect(() => {
@@ -52,6 +52,14 @@ export default function HomePage() {
}
}, [loading, user, userId, mongoUser])
// On-demand tour triggered from Settings (no DB read/write)
useEffect(() => {
if (!loading && user && localStorage.getItem('gj-force-tour') === 'true') {
localStorage.removeItem('gj-force-tour')
setTimeout(() => startTour(), 150)
}
}, [loading, user, startTour])
async function markTutorialDone() {
if (!user || !userId) return
const token = await user.getIdToken()
@@ -78,7 +86,7 @@ export default function HomePage() {
<div className="home-page" style={{ alignItems: 'center', justifyContent: 'center', gap: '1rem' }}>
<h1 style={{ fontFamily: '"Sniglet", system-ui', color: 'var(--color-text)' }}>Grateful Journal</h1>
<p style={{ color: 'var(--color-text-muted)' }}>Sign in to start your journal.</p>
<Link to="/login" className="home-login-link">Go to login</Link>
<Link to="/" className="home-login-link">Go to login</Link>
</div>
)
}
@@ -178,7 +186,7 @@ export default function HomePage() {
onKeyDown={handleTitleKeyDown}
enterKeyHint="next"
ref={titleInputRef}
disabled={phase !== 'idle'}
disabled={phase !== 'idle' || isTourActive}
/>
<textarea
id="tour-content-textarea"
@@ -188,7 +196,7 @@ export default function HomePage() {
onChange={(e) => setEntry(e.target.value)}
enterKeyHint="enter"
ref={contentTextareaRef}
disabled={phase !== 'idle'}
disabled={phase !== 'idle' || isTourActive}
/>
</div>
@@ -227,7 +235,7 @@ export default function HomePage() {
id="tour-save-btn"
className="journal-write-btn"
onClick={handleWrite}
disabled={phase !== 'idle' || !title.trim() || !entry.trim()}
disabled={phase !== 'idle' || isTourActive || !title.trim() || !entry.trim()}
>
{phase === 'saving' ? 'Saving...' : 'Save Entry'}
</button>

View File

@@ -13,7 +13,7 @@ export default function LoginPage() {
useEffect(() => {
if (loading) return
if (user) navigate('/', { replace: true })
if (user) navigate('/write', { replace: true })
}, [user, loading, navigate])
async function handleGoogleSignIn() {

View File

@@ -71,9 +71,8 @@ export default function SettingsPage() {
}, [])
const handleSeeTutorial = () => {
localStorage.removeItem('gj-onboarding-done')
localStorage.removeItem('gj-tour-pending-step')
navigate('/')
localStorage.setItem('gj-force-tour', 'true')
navigate('/write')
}
const displayName = mongoUser?.displayName || user?.displayName || 'User'