login page

This commit is contained in:
2025-11-11 18:47:59 +05:30
parent 661f8c915b
commit 1c964a7fee
2 changed files with 118 additions and 17 deletions

View File

@@ -2,16 +2,22 @@ import * as React from 'react';
import CssBaseline from '@mui/material/CssBaseline'; import CssBaseline from '@mui/material/CssBaseline';
import Container from '@mui/material/Container'; import Container from '@mui/material/Container';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import AppTheme from '../shared-theme/AppTheme'; import AppTheme from '../shared-theme/AppTheme';
import MainContent from './components/MainContent'; import MainContent from './components/MainContent';
import Article from './components/Article'; import Article from './components/Article';
import Latest from './components/Latest'; import Latest from './components/Latest';
import Footer from './components/Footer'; import Footer from './components/Footer';
import Login from './components/Login';
import { useArticles } from './providers/Article'; import { useArticles } from './providers/Article';
import { useAuth } from './providers/Author';
export default function Blog(props: { disableCustomTheme?: boolean }) { export default function Blog(props: { disableCustomTheme?: boolean }) {
const { articles, loading, error } = useArticles(); const { articles, loading, error } = useArticles();
const { currentUser } = useAuth();
const [selectedArticle, setSelectedArticle] = React.useState<number | null>(null); const [selectedArticle, setSelectedArticle] = React.useState<number | null>(null);
const [showLogin, setShowLogin] = React.useState(false);
const handleSelectArticle = (index: number) => { const handleSelectArticle = (index: number) => {
setSelectedArticle(index); setSelectedArticle(index);
@@ -20,6 +26,13 @@ export default function Blog(props: { disableCustomTheme?: boolean }) {
const handleBack = () => setSelectedArticle(null); const handleBack = () => setSelectedArticle(null);
const handleShowLogin = () => {
setShowLogin(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleHideLogin = () => setShowLogin(false);
if (loading) { if (loading) {
return ( return (
<AppTheme {...props}> <AppTheme {...props}>
@@ -64,13 +77,7 @@ export default function Blog(props: { disableCustomTheme?: boolean }) {
<AppTheme {...props}> <AppTheme {...props}>
<CssBaseline enableColorScheme /> <CssBaseline enableColorScheme />
<Box <Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
sx={{
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
}}
>
<Container <Container
maxWidth="lg" maxWidth="lg"
component="main" component="main"
@@ -80,21 +87,26 @@ export default function Blog(props: { disableCustomTheme?: boolean }) {
flexDirection: 'column', flexDirection: 'column',
my: 4, my: 4,
gap: 4, gap: 4,
pb: selectedArticle === null ? 24 : 0, // space for fixed footer on home pb: selectedArticle === null && !showLogin ? 24 : 0,
}} }}
> >
{selectedArticle === null ? ( {showLogin ? (
<Login onBack={handleHideLogin} />
) : selectedArticle === null ? (
<> <>
<MainContent {!currentUser && (
articles={articles} <Box sx={{ display: 'flex', justifyContent: 'flex-end', mb: 2 }}>
onSelectArticle={handleSelectArticle} <Button variant="outlined" color="primary" onClick={handleShowLogin}>
/> Login
</Button>
</Box>
)}
<MainContent articles={articles} onSelectArticle={handleSelectArticle} />
<Latest <Latest
articles={articles} articles={articles}
onSelectArticle={handleSelectArticle} onSelectArticle={handleSelectArticle}
onLoadMore={async (offset, limit) => { onLoadMore={async () => {}}
// Optional pagination call
}}
/> />
</> </>
) : ( ) : (
@@ -102,7 +114,7 @@ export default function Blog(props: { disableCustomTheme?: boolean }) {
)} )}
</Container> </Container>
{selectedArticle === null && ( {selectedArticle === null && !showLogin && (
<Box <Box
component="footer" component="footer"
sx={{ sx={{

View File

@@ -0,0 +1,89 @@
import * as React from 'react';
import { Box, TextField, Button, Typography, IconButton, CircularProgress } from '@mui/material';
import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
import { useAuth } from '../providers/Author';
interface LoginProps {
onBack: () => void;
}
export default function Login({ onBack }: LoginProps) {
const { login, loading, error, currentUser } = useAuth();
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await login(username, password);
};
if (currentUser) {
// ✅ if logged in, auto-return to the article list
onBack();
return null;
}
return (
<Box
sx={{
maxWidth: 400,
mx: 'auto',
mt: 8,
p: 4,
borderRadius: 3,
boxShadow: 3,
bgcolor: 'background.paper',
}}
>
<IconButton onClick={onBack} sx={{ mb: 2 }}>
<ArrowBackRoundedIcon />
</IconButton>
<Typography variant="h4" fontWeight="bold" gutterBottom>
Sign In
</Typography>
<Typography variant="body2" color="text.secondary" gutterBottom>
Please log in to continue
</Typography>
<form onSubmit={handleSubmit}>
<TextField
fullWidth
label="Username"
type="username"
margin="normal"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
<TextField
fullWidth
label="Password"
type="password"
margin="normal"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
{error && (
<Typography color="error" variant="body2" sx={{ mt: 1 }}>
{error}
</Typography>
)}
<Button
fullWidth
type="submit"
variant="contained"
color="primary"
sx={{ mt: 3 }}
disabled={loading}
>
{loading ? <CircularProgress size={24} color="inherit" /> : 'Login'}
</Button>
</form>
</Box>
);
}