Merge branch 'main' into feature/user-auth
Resolved merge conflicts integrating PR #13 (Home Page with MUI v2): Backend changes: - settings.py: Combined INSTALLED_APPS (accounts + jobs + corsheaders), kept HEAD's REST_FRAMEWORK (AllowAny + throttling) and SIMPLE_JWT, added origin/main's CORS config - urls.py: Combined admin/, api/auth/, api/ routes - pyproject.toml: Combined all dependencies (simplejwt + cors-headers) - uv.lock: Regenerated with updated dependencies Frontend changes: - package.json: Combined all deps (axios + MUI + emotion) - App.tsx: Integrated AuthProvider with MUI AppLayout, all routes - HomePage.tsx: Show landing view when unauthenticated, MUI dashboard when authenticated - main.tsx, tsconfig.json, vite-env.d.ts: Combined both versions - yarn.lock: Kept origin/main's version (regenerated on install)
This commit is contained in:
+133
-35
@@ -1,41 +1,139 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { type ReactNode } from "react";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import styles from "./HomePage.module.css";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Alert from "@mui/material/Alert";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import UpdateCard from "../components/UpdateCard";
|
||||
import MetricsPanel from "../components/MetricsPanel";
|
||||
import LoadingSkeleton from "../components/LoadingSkeleton";
|
||||
import { useDashboardData } from "../hooks/useDashboardData";
|
||||
|
||||
export default function HomePage() {
|
||||
const { state, logout } = useAuth();
|
||||
function LandingView(): ReactNode {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: "60vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h3" component="h1" gutterBottom sx={{ fontWeight: 700 }}>
|
||||
Job Tracker
|
||||
</Typography>
|
||||
<Typography variant="h6" color="text.secondary" sx={{ mb: 4, maxWidth: 500 }}>
|
||||
Track your job applications, interviews, and offers all in one place.
|
||||
</Typography>
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
component={Link}
|
||||
to="/login"
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
component={Link}
|
||||
to="/register"
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function DashboardView(): ReactNode {
|
||||
const navigate = useNavigate();
|
||||
const { data, loading, error } = useDashboardData();
|
||||
|
||||
if (loading) {
|
||||
return <LoadingSkeleton />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.card}>
|
||||
<h1 className={styles.heading}>Job Tracker</h1>
|
||||
<p className={styles.welcomeText}>
|
||||
Welcome to the Job Tracker application.
|
||||
</p>
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h2"
|
||||
gutterBottom
|
||||
sx={{ fontWeight: 600 }}
|
||||
>
|
||||
Dashboard
|
||||
</Typography>
|
||||
|
||||
{state.isAuthenticated && state.user ? (
|
||||
<div>
|
||||
<p className={styles.signedInText}>
|
||||
Signed in as{" "}
|
||||
<strong className={styles.emailStrong}>
|
||||
{state.user.email}
|
||||
</strong>
|
||||
</p>
|
||||
<button onClick={logout} className={styles.logoutButton}>
|
||||
Sign Out
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.authLinks}>
|
||||
<Link to="/login" className={styles.signInLink}>
|
||||
Sign In
|
||||
</Link>
|
||||
<Link to="/register" className={styles.registerLink}>
|
||||
Register
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<Alert severity="error" sx={{ mb: 3 }}>
|
||||
Failed to load dashboard data: {error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{data && (
|
||||
<>
|
||||
<Box sx={{ mb: 4 }}>
|
||||
<MetricsPanel metrics={data.metrics} />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{ mb: 3, display: "flex", justifyContent: "flex-end" }}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
startIcon={<AddIcon />}
|
||||
onClick={() => navigate("/applications/new")}
|
||||
>
|
||||
Create New Job Application
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Typography
|
||||
variant="h5"
|
||||
component="h3"
|
||||
gutterBottom
|
||||
sx={{ fontWeight: 600 }}
|
||||
>
|
||||
Recent Updates
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
{data.updates.map((update) => (
|
||||
<Grid key={update.id} size={{ xs: 12, sm: 6, md: 4 }}>
|
||||
<UpdateCard update={update} />
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{data.updates.length === 0 && (
|
||||
<Typography
|
||||
variant="body1"
|
||||
color="text.secondary"
|
||||
sx={{ mt: 2 }}
|
||||
>
|
||||
No updates yet. Create your first job application to get started.
|
||||
</Typography>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function HomePage(): ReactNode {
|
||||
const { state } = useAuth();
|
||||
|
||||
if (!state.isAuthenticated) {
|
||||
return <LandingView />;
|
||||
}
|
||||
|
||||
return <DashboardView />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user