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)
140 lines
3.5 KiB
TypeScript
140 lines
3.5 KiB
TypeScript
import { type ReactNode } from "react";
|
|
import { useNavigate, Link } from "react-router-dom";
|
|
import { useAuth } from "../contexts/AuthContext";
|
|
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";
|
|
|
|
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 (
|
|
<Box>
|
|
<Typography
|
|
variant="h4"
|
|
component="h2"
|
|
gutterBottom
|
|
sx={{ fontWeight: 600 }}
|
|
>
|
|
Dashboard
|
|
</Typography>
|
|
|
|
{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 />;
|
|
}
|