mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-16 17:51:43 +10:00
- designing the dashboard
- resume preview - create resume modal
This commit is contained in:
34
src/components/dashboard/CreateResume.js
Normal file
34
src/components/dashboard/CreateResume.js
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { useContext } from "react";
|
||||
import { MdAdd } from "react-icons/md";
|
||||
import styles from "./CreateResume.module.css";
|
||||
import ModalContext from "../../contexts/ModalContext";
|
||||
|
||||
const CreateResume = () => {
|
||||
const { createResumeModal } = useContext(ModalContext);
|
||||
|
||||
const handleClick = () => {
|
||||
createResumeModal.setOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.resume}>
|
||||
<div className={styles.backdrop}>
|
||||
<MdAdd color="#FFF" size="48" />
|
||||
</div>
|
||||
<div
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
className={styles.page}
|
||||
onClick={handleClick}
|
||||
onKeyDown={() => {}}
|
||||
>
|
||||
<MdAdd color="#444" size="48" />
|
||||
</div>
|
||||
<div className={styles.meta}>
|
||||
<p>Create New Resume</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateResume;
|
||||
31
src/components/dashboard/CreateResume.module.css
Normal file
31
src/components/dashboard/CreateResume.module.css
Normal file
@ -0,0 +1,31 @@
|
||||
.resume {
|
||||
@apply relative flex flex-col items-center;
|
||||
}
|
||||
|
||||
.resume > .backdrop {
|
||||
max-width: 184px;
|
||||
height: 260px;
|
||||
@apply rounded absolute w-full bg-black shadow;
|
||||
@apply absolute text-gray-500 flex justify-center items-center;
|
||||
}
|
||||
|
||||
.resume > .page {
|
||||
max-width: 184px;
|
||||
height: 260px;
|
||||
@apply rounded absolute w-full bg-white;
|
||||
@apply transition-opacity duration-200 ease-in-out;
|
||||
@apply cursor-pointer absolute text-gray-500 flex justify-center items-center;
|
||||
}
|
||||
|
||||
.resume > .page:hover {
|
||||
@apply transition-opacity duration-200 ease-in-out opacity-25;
|
||||
}
|
||||
|
||||
.resume > .meta {
|
||||
margin-top: 260px;
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
.resume > .meta p {
|
||||
@apply mt-3 font-medium leading-normal;
|
||||
}
|
||||
64
src/components/dashboard/ResumePreview.js
Normal file
64
src/components/dashboard/ResumePreview.js
Normal file
@ -0,0 +1,64 @@
|
||||
import React, { useState } from "react";
|
||||
import { MdMoreHoriz, MdOpenInNew } from "react-icons/md";
|
||||
import { Menu, MenuItem } from "@material-ui/core";
|
||||
import styles from "./ResumePreview.module.css";
|
||||
|
||||
const ResumePreview = ({ title, subtitle }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
const handleClick = () => {
|
||||
console.log("Hello, World!");
|
||||
};
|
||||
|
||||
const handleMenuClick = (event) => {
|
||||
event.stopPropagation();
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.resume}>
|
||||
<div className={styles.backdrop}>
|
||||
<img
|
||||
src="https://source.unsplash.com/random/210x297"
|
||||
alt="Resume Preview"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.page}>
|
||||
<MdOpenInNew
|
||||
color="#fff"
|
||||
size="48"
|
||||
className="cursor-pointer"
|
||||
onClick={handleClick}
|
||||
/>
|
||||
<MdMoreHoriz
|
||||
color="#fff"
|
||||
size="48"
|
||||
className="cursor-pointer"
|
||||
aria-haspopup="true"
|
||||
onClick={handleMenuClick}
|
||||
/>
|
||||
<Menu
|
||||
keepMounted
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
>
|
||||
<MenuItem onClick={handleMenuClose}>Duplicate</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose}>
|
||||
<span className="text-red-600">Delete</span>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<div className={styles.meta}>
|
||||
<p>{title}</p>
|
||||
<span>{subtitle}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResumePreview;
|
||||
40
src/components/dashboard/ResumePreview.module.css
Normal file
40
src/components/dashboard/ResumePreview.module.css
Normal file
@ -0,0 +1,40 @@
|
||||
.resume {
|
||||
@apply relative flex flex-col items-center;
|
||||
}
|
||||
|
||||
.resume > .backdrop {
|
||||
max-width: 184px;
|
||||
height: 260px;
|
||||
@apply rounded absolute w-full bg-black shadow;
|
||||
}
|
||||
|
||||
.resume > .backdrop img {
|
||||
max-width: 184px;
|
||||
height: 260px;
|
||||
@apply w-full object-cover rounded;
|
||||
}
|
||||
|
||||
.resume > .page {
|
||||
max-width: 184px;
|
||||
height: 260px;
|
||||
@apply rounded absolute w-full bg-black;
|
||||
@apply opacity-0 transition-opacity duration-200 ease-in-out;
|
||||
@apply absolute text-gray-500 flex flex-col justify-evenly items-center;
|
||||
}
|
||||
|
||||
.resume > .page:hover {
|
||||
@apply opacity-75 transition-opacity duration-200 ease-in-out;
|
||||
}
|
||||
|
||||
.resume > .meta {
|
||||
margin-top: 260px;
|
||||
@apply flex flex-col items-center;
|
||||
}
|
||||
|
||||
.resume > .meta p {
|
||||
@apply mt-3 font-medium leading-normal;
|
||||
}
|
||||
|
||||
.resume > .meta span {
|
||||
font-size: 10px;
|
||||
}
|
||||
41
src/components/dashboard/TopNavbar.js
Normal file
41
src/components/dashboard/TopNavbar.js
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { useContext } from "react";
|
||||
import Logo from "../shared/Logo";
|
||||
import UserContext from "../../contexts/UserContext";
|
||||
import styles from "./TopNavbar.module.css";
|
||||
import { navigate, Link } from "gatsby";
|
||||
|
||||
const TopNavbar = () => {
|
||||
const { user, logout } = useContext(UserContext);
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.navbar}>
|
||||
<div className="container">
|
||||
<Link to="/">
|
||||
<Logo size="40px" />
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
className="text-primary font-semibold focus:outline-none hover:underline"
|
||||
onClick={handleLogout}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
|
||||
<img
|
||||
className="ml-8 h-12 rounded-full"
|
||||
src={user.photoURL}
|
||||
alt={user.displayName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopNavbar;
|
||||
8
src/components/dashboard/TopNavbar.module.css
Normal file
8
src/components/dashboard/TopNavbar.module.css
Normal file
@ -0,0 +1,8 @@
|
||||
.navbar {
|
||||
height: 65px;
|
||||
@apply w-full shadow;
|
||||
}
|
||||
|
||||
.navbar > div {
|
||||
@apply h-full flex items-center justify-between;
|
||||
}
|
||||
@ -5,6 +5,7 @@ import ModalContext from "../../contexts/ModalContext";
|
||||
import UserContext from "../../contexts/UserContext";
|
||||
import Button from "../shared/Button";
|
||||
import Logo from "../shared/Logo";
|
||||
import { navigate } from "gatsby";
|
||||
|
||||
const Hero = () => {
|
||||
const { user, loading } = useContext(UserContext);
|
||||
@ -13,9 +14,11 @@ const Hero = () => {
|
||||
|
||||
const handleLogin = () => authModal.setOpen(true);
|
||||
|
||||
const handleGotoApp = () => navigate("/app/dashboard");
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Logo size="256px" />
|
||||
<Logo className="shadow-lg" size="256px" />
|
||||
|
||||
<div className="ml-12">
|
||||
<h1 className="text-5xl font-bold">Reactive Resume</h1>
|
||||
@ -27,7 +30,7 @@ const Hero = () => {
|
||||
{user ? (
|
||||
<Button
|
||||
title="Go to App"
|
||||
onClick={handleLogin}
|
||||
onClick={handleGotoApp}
|
||||
isLoading={loading || authModal.isOpen}
|
||||
/>
|
||||
) : (
|
||||
|
||||
19
src/components/router/LoadingScreen.js
Normal file
19
src/components/router/LoadingScreen.js
Normal file
@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import Modal from "@material-ui/core/Modal";
|
||||
import Loader from "react-loader-spinner";
|
||||
import Logo from "../shared/Logo";
|
||||
|
||||
const LoadingScreen = () => {
|
||||
return (
|
||||
<Modal open hideBackdrop>
|
||||
<div className="w-screen h-screen flex justify-center items-center outline-none">
|
||||
<div className="flex flex-col items-center">
|
||||
<Logo size="48px" className="mb-4" />
|
||||
<Loader type="ThreeDots" color="#AAA" height={32} width={48} />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingScreen;
|
||||
21
src/components/router/PrivateRoute.js
Normal file
21
src/components/router/PrivateRoute.js
Normal file
@ -0,0 +1,21 @@
|
||||
import React, { useContext } from "react";
|
||||
import { navigate } from "gatsby";
|
||||
import UserContext from "../../contexts/UserContext";
|
||||
import LoadingScreen from "./LoadingScreen";
|
||||
|
||||
const PrivateRoute = ({ component: Component, location, ...props }) => {
|
||||
const { user, loading } = useContext(UserContext);
|
||||
|
||||
if (loading) {
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
navigate("/");
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Component {...props} />;
|
||||
};
|
||||
|
||||
export default PrivateRoute;
|
||||
32
src/components/shared/Input.js
Normal file
32
src/components/shared/Input.js
Normal file
@ -0,0 +1,32 @@
|
||||
import React from "react";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import styles from "./Input.module.css";
|
||||
|
||||
const Input = ({
|
||||
label,
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
type = "text",
|
||||
}) => {
|
||||
const uuid = uuidv4();
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<label htmlFor={uuid}>
|
||||
<span>{label}</span>
|
||||
<input
|
||||
id={uuid}
|
||||
name={name}
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Input;
|
||||
15
src/components/shared/Input.module.css
Normal file
15
src/components/shared/Input.module.css
Normal file
@ -0,0 +1,15 @@
|
||||
.container > label {
|
||||
@apply flex flex-col;
|
||||
}
|
||||
|
||||
.container > label > span {
|
||||
@apply mb-1 text-gray-600 font-medium text-sm uppercase;
|
||||
}
|
||||
|
||||
.container > label > input {
|
||||
@apply py-4 px-4 rounded bg-gray-200 border border-gray-200;
|
||||
}
|
||||
|
||||
.container > label > input:focus {
|
||||
@apply outline-none border-gray-500;
|
||||
}
|
||||
@ -2,7 +2,7 @@ import React from "react";
|
||||
import { useStaticQuery, graphql } from "gatsby";
|
||||
import GatsbyImage from "gatsby-image";
|
||||
|
||||
const Logo = ({ size = "256px" }) => {
|
||||
const Logo = ({ size = "256px", className }) => {
|
||||
const { file } = useStaticQuery(graphql`
|
||||
query {
|
||||
file(relativePath: { eq: "logo.png" }) {
|
||||
@ -18,7 +18,7 @@ const Logo = ({ size = "256px" }) => {
|
||||
return (
|
||||
<GatsbyImage
|
||||
loading="eager"
|
||||
className="shadow-md rounded"
|
||||
className={`rounded ${className}`}
|
||||
style={{ width: size, height: size }}
|
||||
fluid={file.childImageSharp.fluid}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user