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