Finished MUI for the most part

This commit is contained in:
QkoSad
2024-09-27 21:43:06 +03:00
parent be03889ff3
commit 9bc2015426
29 changed files with 1762 additions and 4102 deletions
+1224 -3148
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -5,8 +5,8 @@
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.3",
"@mui/material": "^5.14.4",
"@mui/icons-material": "^6.1.1",
"@mui/material": "^6.1.1",
"@reduxjs/toolkit": "^1.9.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
-584
View File
@@ -1,584 +0,0 @@
/* Global Styles */
:root {
--primary-color: #17a2b8;
--dark-color: #343a40;
--light-color: #f4f4f4;
--danger-color: #dc3545;
--success-color: #28a745;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: "Raleway", sans-serif;
font-size: 1rem;
line-height: 1.6;
background-color: #fff;
color: #333;
}
a {
color: var(--primary-color);
text-decoration: none;
}
ul {
list-style: none;
}
img {
width: 100%;
}
/* Utilities */
.container {
max-width: 1100px;
margin: auto;
overflow: hidden;
padding: 0 2rem;
margin-top: 6rem;
margin-bottom: 3rem;
}
/* Text Styles*/
.x-large {
font-size: 4rem;
line-height: 1.2;
margin-bottom: 1rem;
}
.large {
font-size: 3rem;
line-height: 1.2;
margin-bottom: 1rem;
}
.lead {
font-size: 1.5rem;
margin-bottom: 1rem;
}
.text-center {
text-align: center;
}
.text-primary {
color: var(--primary-color);
}
.text-dark {
color: var(--dark-color);
}
/* Padding */
.p {
padding: 0.5rem;
}
.p-1 {
padding: 1rem;
}
.p-2 {
padding: 2rem;
}
.p-3 {
padding: 3rem;
}
.py {
padding: 0.5rem 0;
}
.py-1 {
padding: 1rem 0;
}
.py-2 {
padding: 2rem 0;
}
.py-3 {
padding: 3rem 0;
}
/* Margin */
.m {
margin: 0.5rem;
}
.m-1 {
margin: 1rem;
}
.m-2 {
margin: 2rem;
}
.m-3 {
margin: 3rem;
}
.my {
margin: 0.5rem 0;
}
.my-1 {
margin: 1rem 0;
}
.my-2 {
margin: 2rem 0;
}
.my-3 {
margin: 3rem 0;
}
.btn {
display: inline-block;
background: var(--light-color);
color: #333;
padding: 0.4rem 1.3rem;
font-size: 1rem;
border: none;
cursor: pointer;
margin-right: 0.5rem;
transition: opacity 0.2s ease-in;
outline: none;
}
.badge {
font-size: 0.8rem;
padding: 0.1rem;
text-align: center;
margin: 0.3rem;
background: var(--light-color);
color: #333;
}
.alert {
padding: 0.8rem;
margin: 1rem 0;
opacity: 0.9;
background: var(--light-color);
color: #333;
}
.btn-primary,
.bg-primary,
.badge-primary,
.alert-primary {
background: var(--primary-color);
color: #fff;
}
.btn-light,
.bg-light,
.badge-light,
.alert-light {
background: var(--light-color);
color: #333;
}
.btn-dark,
.bg-dark,
.badge-dark,
.alert-dark {
background: var(--dark-color);
color: #fff;
}
.btn-danger,
.bg-danger,
.badge-danger,
.alert-danger {
background: var(--danger-color);
color: #fff;
}
.btn-success,
.bg-success,
.badge-success,
.alert-success {
background: var(--success-color);
color: #fff;
}
.btn-white,
.bg-white,
.badge-white,
.alert-white {
background: #fff;
color: #333;
border: #ccc solid 1px;
}
.btn:hover {
opacity: 0.8;
}
.bg-light,
.badge-light {
border: #ccc solid 1px;
}
.round-img {
border-radius: 50%;
}
.line {
height: 1px;
background: #ccc;
margin: 1.5rem 0;
}
/* Overlay */
.dark-overlay {
background-color: rgba(0, 0, 0, 0.7);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* Forms */
.form .form-group {
margin: 1.2rem 0;
}
.form .form-text {
display: block;
margin-top: 0.3rem;
color: #888;
}
.form input[type="text"],
.form input[type="email"],
.form input[type="password"],
.form input[type="date"],
.form select,
.form textarea {
display: block;
width: 100%;
padding: 0.4rem;
font-size: 1.2rem;
border: 1px solid #ccc;
}
.form input[type="submit"],
button {
font: inherit;
}
.form .social-input {
display: flex;
}
.form .social-input i {
padding: 0.5rem;
width: 4rem;
}
.form .social-input i.fa-twitter {
color: #38a1f3;
}
.form .social-input i.fa-facebook {
color: #3b5998;
}
.form .social-input i.fa-instagram {
color: #3f729b;
}
.form .social-input i.fa-youtube {
color: #c4302b;
}
.form .social-input i.fa-linkedin {
color: #0077b5;
}
.table th,
.table td {
padding: 1rem;
text-align: left;
}
.table th {
background: var(--light-color);
}
/* Navbar */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.7rem 2rem;
position: fixed;
z-index: 1;
width: 100%;
top: 0;
border-bottom: solid 1px var(--primary-color);
opacity: 0.9;
}
.navbar ul {
display: flex;
}
.navbar a {
color: #fff;
padding: 0.45rem;
margin: 0 0.25rem;
}
.navbar a:hover {
color: var(--primary-color);
}
.navbar .welcome span {
margin-right: 0.6rem;
}
/* Landing Page */
.landing {
position: relative;
background: url("./img/vimCheatSheet.jpg") no-repeat center center/cover;
height: 100vh;
}
.landing-inner {
color: #fff;
height: 100%;
width: 80%;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
/* Profiles Page */
.profile {
display: grid;
grid-template-columns: 2fr 4fr 2fr;
align-items: center;
grid-gap: 2rem;
padding: 1rem;
line-height: 1.8;
margin-bottom: 1rem;
}
/* Profile Page */
.profile-grid {
display: grid;
grid-template-areas:
"top top"
"about about"
"exp edu"
"github github";
grid-gap: 1rem;
}
.profile-top {
grid-area: top;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.profile-top img {
width: 250px;
}
.profile-top .icons a {
color: #fff;
margin: 0 0.3rem;
}
.profile-top .icons a:hover {
color: var(--dark-color);
}
.profile-about {
grid-area: about;
text-align: center;
}
.profile-about .skills {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.profile-exp {
grid-area: exp;
}
.profile-edu {
grid-area: edu;
}
.profile-exp h2,
.profile-edu h2 {
margin-bottom: 1rem;
}
.profile-exp > div,
.profile-edu > div {
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: #ccc 1px dotted;
}
.profile-exp > div:last-child,
.profile-edu > div:last-child {
border: 0;
}
.profile-exp p,
.profile-edu p {
margin: 0.5rem 0;
}
.profile-github {
grid-area: github;
}
.profile-github .repo {
display: flex;
}
.profile-github .repo > div:first-child {
flex: 7;
flex-basis: 70%;
}
.profile-github > div:last-child {
flex: 3;
flex-basis: 20%;
}
/* Posts Page */
.post-form .post-form-header {
background: var(--primary-color);
padding: 0.5rem;
}
.post {
display: grid;
grid-template-columns: 1fr 4fr;
grid-gap: 2rem;
align-items: center;
}
.post > div:first-child {
text-align: center;
}
.post img {
width: 100px;
}
.post .comment-count {
background: var(--light-color);
color: var(--primary-color);
padding: 0.1rem 0.2rem;
border-radius: 5px;
font-size: 0.8rem;
}
.post .post-date {
color: #aaa;
font-size: 0.8rem;
margin-bottom: 0.5rem;
}
/* Mobile Styles */
@media (max-width: 700px) {
.container {
margin-top: 8rem;
}
.hide-sm {
display: none;
}
/* Text Styles */
.x-large {
font-size: 3rem;
}
.large {
font-size: 2rem;
}
.lead {
font-size: 1rem;
}
/* Navbar */
.navbar {
display: block;
text-align: center;
}
.navbar ul {
text-align: center;
justify-content: center;
}
.navbar h1 {
margin-bottom: 1rem;
}
.navbar .welcome {
display: none;
}
/* Profiles Page */
.profile {
grid-template-columns: 1fr;
text-align: center;
}
.profile ul {
display: none;
}
/* Profile Page */
.profile-top img,
.profile img {
width: 200px;
margin: auto;
}
.profile-grid {
grid-template-areas:
"top"
"about"
"exp"
"edu"
"github";
}
.profile-about .skills {
flex-direction: column;
}
.dash-buttons a {
display: block;
width: 100%;
margin-bottom: 0.2rem;
}
.post {
grid-template-columns: 1fr;
}
.post a,
.post button {
padding: 0.3rem 0.4rem;
}
}
.alert-wrapper {
position: fixed;
top: 4rem;
right: 2rem;
display: inline-block;
}
-16
View File
@@ -1,16 +0,0 @@
.datePicker :hover {
border-color: "#212121";
}
.datePicker :focus {
border-color: "#1976d2";
border-width: "2px";
}
.datePicker{
color:"red";
background:"red";
}
.colormebaby{
background-color:"red";
color:"red"
}
+6 -6
View File
@@ -7,7 +7,7 @@ import * as React from "react";
import Avatar from "@mui/material/Avatar";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import Grid from "@mui/material/Grid";
import Grid from "@mui/material/Grid2";
import Box from "@mui/material/Box";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
import Typography from "@mui/material/Typography";
@@ -54,7 +54,7 @@ export default function SignUp() {
</Typography>
<Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 3 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Grid size={{ xs: 12 }}>
<TextField
name="name"
required
@@ -64,7 +64,7 @@ export default function SignUp() {
autoFocus
/>
</Grid>
<Grid item xs={12}>
<Grid size={{ xs: 12 }}>
<TextField
required
fullWidth
@@ -73,7 +73,7 @@ export default function SignUp() {
name="email"
/>
</Grid>
<Grid item xs={12}>
<Grid size={{ xs: 12 }}>
<TextField
required
fullWidth
@@ -83,7 +83,7 @@ export default function SignUp() {
id="password"
/>
</Grid>
<Grid item xs={12}>
<Grid size={{ xs: 12 }}>
<TextField
required
fullWidth
@@ -103,7 +103,7 @@ export default function SignUp() {
Sign Up
</Button>
<Grid container justifyContent="flex-end">
<Grid item>
<Grid>
Already have an account? <Link to={"/login"}>Sign in</Link>
</Grid>
</Grid>
+10 -8
View File
@@ -6,6 +6,7 @@ import Experience from "./Experience";
import Education from "./Education";
import { getCurrentProfile, deleteAccount } from "../../actions/profile";
import { Box, Button, Typography } from "@mui/material";
import { compose } from "redux";
const Dashboard = () => {
const dispatch = useAppDispatch();
@@ -19,13 +20,14 @@ const Dashboard = () => {
const profile = useAppSelector((state) => state.profile.profile);
return (
<Box
component="main"
justifyContent="center"
flexDirection="column"
minHeight="50vh"
display="flex"
alignItems="center"
gap="1rem"
sx={{
justifyContent: "center",
flexDirection: "column",
minHeight: "50vh",
display: "flex",
alignItems: "center",
gap: "1rem",
}}
>
<Typography variant="h3" component="h2">
Dashboard{" "}
@@ -38,7 +40,7 @@ const Dashboard = () => {
<DashboardActions />
<Experience experience={profile.experience} />
<Education education={profile.education} />
<Box sx={{ marginTop: "10vh" }}>
<Box sx={{ marginTop: "2vh" }}>
<Button
variant="contained"
color="error"
@@ -4,7 +4,12 @@ import { Link } from "react-router-dom";
const DashboardActions = () => {
return (
<Box>
<Box
sx={{
display: { xs: "grid", md: "flex" },
rowGap: { xs: "5px" },
}}
>
<Button
component={Link}
to="/edit-profile"
+13 -12
View File
@@ -21,19 +21,26 @@ const Education = ({ education }: { education: EducationType[] }) => {
const educations = education.map((edu) => (
<TableRow key={edu._id}>
<TableCell
// style={{
// wordWrap: "break-word",
// }}
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{edu.school}
</TableCell>
<TableCell
// style={{ wordWrap: "break-word" }}
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{edu.degree}
</TableCell>
<TableCell
// style={{ wordWrap: "break-word" }}
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{formatDate(edu.from)} - {edu.to ? formatDate(edu.to) : "Now"}
</TableCell>
@@ -56,13 +63,7 @@ const Education = ({ education }: { education: EducationType[] }) => {
return (
<Box>
<Typography variant="h2">Education Credentials</Typography>
<Table
// style={{
// tableLayout: "fixed",
// width: "50%",
// alignSelf: "left",
// }}
>
<Table>
<TableHead>
<TableRow>
<TableCell>School</TableCell>
+22 -3
View File
@@ -20,9 +20,28 @@ const Experience = ({ experience }: { experience: ExperienceType[] }) => {
if (!experience) return <></>;
const experiences = experience.map((exp) => (
<TableRow key={exp._id}>
<TableCell>{exp.company}</TableCell>
<TableCell>{exp.title}</TableCell>
<TableCell>
<TableCell
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{exp.company}
</TableCell>
<TableCell
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{exp.title}
</TableCell>
<TableCell
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{formatDate(exp.from)} - {exp.to ? formatDate(exp.to) : "Now"}
</TableCell>
<TableCell>
-1
View File
@@ -148,7 +148,6 @@ const Navbar = () => {
Logout
</a>
</MenuItem>
,
</div>
) : (
guestLinks.map((el) => (
+9 -2
View File
@@ -1,5 +1,6 @@
import { Box, Button, Container, TextField, Typography } from "@mui/material";
import React, { useState } from "react";
import { createAlert } from "../../actions/alert";
import { addComment } from "../../actions/post";
import { useAppDispatch } from "../../utils/hooks";
@@ -18,8 +19,14 @@ const CommentForm = ({ postId }: { postId: string }) => {
gap="1rem"
onSubmit={(e) => {
e.preventDefault();
dispatch(addComment(postId, { text }));
setText("");
if (text.length > 250) {
dispatch(
createAlert("Comment longer than 250 characters", "danger"),
);
} else {
dispatch(addComment(postId, { text }));
setText("");
}
}}
>
<TextField
+6 -1
View File
@@ -40,7 +40,12 @@ const CommentItem = ({
</Button>
)}
<CardContent>
<Typography variant="subtitle1">{text}</Typography>
<Typography
variant="subtitle1"
sx={{ textWrap: "wrap", wordBreak: "break-word" }}
>
{text}
</Typography>
</CardContent>
</Card>
</Container>
+10 -4
View File
@@ -1,12 +1,12 @@
import { Box, Button, Container, TextField, Typography } from "@mui/material";
import React, { useState } from "react";
import { createAlert } from "../../actions/alert";
import { addPost } from "../../actions/post";
import { useAppDispatch } from "../../utils/hooks";
const PostForm = () => {
const [text, setText] = useState("");
const dispatch = useAppDispatch();
const handleSumbit = {};
return (
<Container>
<Typography>Say Something...</Typography>
@@ -18,8 +18,12 @@ const PostForm = () => {
sx={{ mt: 3, mb: 3 }}
onSubmit={(e) => {
e.preventDefault();
dispatch(addPost({ text }));
setText("");
if (text.length > 250) {
dispatch(createAlert("Post longer than 250 characters", "danger"));
} else {
dispatch(addPost({ text }));
setText("");
}
}}
>
<TextField
@@ -27,7 +31,9 @@ const PostForm = () => {
label="Create a post"
fullWidth
value={text}
onChange={(e) => setText(e.target.value)}
onChange={(e) => {
return setText(e.target.value);
}}
multiline
rows={3}
required
+35 -40
View File
@@ -6,14 +6,12 @@ import { Post } from "../../types";
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
import {
Avatar,
Box,
Button,
Card,
CardContent,
CardHeader,
Container,
Table,
TableCell,
TableRow,
Typography,
} from "@mui/material";
import ThumbDownIcon from "@mui/icons-material/ThumbDown";
@@ -36,45 +34,42 @@ const PostItem = ({
subheader={formatDate(date)}
/>
<CardContent>
<Typography variant="subtitle1">{text}</Typography>
<Typography
variant="subtitle1"
sx={{ textWrap: "wrap", wordBreak: "break-word" }}
>
{text}
</Typography>
</CardContent>
</Card>
<Table>
<TableRow>
<TableCell>
<Button onClick={async () => await dispatch(addLike(_id))}>
<ThumbUpIcon />
</Button>
</TableCell>
<TableCell>
<Typography display="inline-flex" variant="button">
{likes.length}
</Typography>
</TableCell>
<TableCell>
<Button onClick={async () => await dispatch(removeLike(_id))}>
<ThumbDownIcon />
</Button>
</TableCell>
<TableCell>
<Button component={Link} to={`/posts/${_id}`}>
<Typography>Comments </Typography>
</Button>
</TableCell>
<TableCell>
<Typography display="inline-flex" variant="button">
{comments.length}
</Typography>
</TableCell>
<TableCell>
{!auth.loading && auth.user !== null && user === auth.user._id && (
<Button onClick={async () => await dispatch(deletePost(_id))}>
<DeleteIcon />
</Button>
)}
</TableCell>
</TableRow>
</Table>
<Box
sx={{
display: "flex",
justifyContent: "space-evenly",
alignItems: "center",
}}
>
<Button onClick={async () => await dispatch(addLike(_id))}>
<ThumbUpIcon />
</Button>
<Typography display="inline-flex" variant="button">
{likes.length}
</Typography>
<Button onClick={async () => await dispatch(removeLike(_id))}>
<ThumbDownIcon />
</Button>
<Button component={Link} to={`/posts/${_id}`}>
<Typography>Comments </Typography>
</Button>
<Typography display="inline-flex" variant="button">
{comments.length}
</Typography>
{!auth.loading && auth.user !== null && user === auth.user._id && (
<Button onClick={async () => await dispatch(deletePost(_id))}>
<DeleteIcon />
</Button>
)}
</Box>
</Container>
);
};
@@ -1,15 +1,8 @@
import styled from "@emotion/styled";
import {
Button,
Box,
Container,
TextField,
Typography,
Checkbox,
FormControlLabel,
} from "@mui/material";
import { Button, Box, Container, TextField, Typography } from "@mui/material";
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { createAlert } from "../../actions/alert";
import { addEducation } from "../../actions/profile";
import { useAppDispatch } from "../../utils/hooks";
@@ -51,6 +44,21 @@ const AddEducation = () => {
const onChange = (
event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,
) => setFormData({ ...formData, [event.target.name]: event.target.value });
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (formData.degree.length > 50) {
dispatch(createAlert("Degree is longer 50 characters", "danger"));
} else if (formData.fieldofstudy.length > 50) {
dispatch(createAlert("Field of study is longer 50 characters", "danger"));
} else if (formData.description.length > 250) {
dispatch(
createAlert("Description name is longer 250 characters", "danger"),
);
} else if (formData.school.length > 50) {
dispatch(createAlert("School name is longer 50 characters", "danger"));
} else
await dispatch(addEducation(formData)).then(() => navigate("/dashboard"));
};
return (
<Container maxWidth="sm">
@@ -68,12 +76,7 @@ const AddEducation = () => {
noValidate
maxWidth="500px"
sx={{ mt: 3 }}
onSubmit={async (e) => {
e.preventDefault();
await dispatch(addEducation(formData)).then(() =>
navigate("/dashboard"),
);
}}
onSubmit={onSubmit}
>
<TextField
name="school"
@@ -10,6 +10,7 @@ import {
import { Container } from "@mui/system";
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { createAlert } from "../../actions/alert";
import { addExperience } from "../../actions/profile";
import { useAppDispatch } from "../../utils/hooks";
@@ -69,9 +70,30 @@ const AddExperience = () => {
sx={{ mt: 3 }}
onSubmit={async (e) => {
e.preventDefault();
await dispatch(addExperience(formData)).then(() =>
navigate("/dashboard"),
);
if (formData.description.length > 250) {
dispatch(
createAlert("Job description is longer 250 characters", "danger"),
);
} else if (formData.location.length > 50) {
dispatch(
createAlert("Location is longer than 50 characters", "danger"),
);
} else if (formData.title.length > 50) {
dispatch(
createAlert("Job title is longer than 50 characters", "danger"),
);
} else if (formData.company.length > 50) {
dispatch(
createAlert(
"Company name is longer than 50 characters",
"danger",
),
);
} else {
await dispatch(addExperience(formData)).then(() =>
navigate("/dashboard"),
);
}
}}
>
<TextField
@@ -2,16 +2,16 @@ import {
Button,
Box,
Container,
CssBaseline,
Grid,
InputLabel,
MenuItem,
Select,
TextField,
Typography,
} from "@mui/material";
import React, { Fragment, useState, useEffect } from "react";
import Grid from "@mui/material/Grid2";
import React, { useState, useEffect } from "react";
import { Link, useMatch, useNavigate } from "react-router-dom";
import { createAlert } from "../../actions/alert";
import { createProfile, getCurrentProfile } from "../../actions/profile";
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
@@ -86,28 +86,52 @@ const ProfileForm = () => {
const linkedin = data.get("linkedin") as string;
const instagram = data.get("instagram") as string;
const status = data.get("status") as string;
await dispatch(
createProfile(
{
website,
location,
skills,
githubusername,
company,
bio,
twitter,
facebook,
youtube,
linkedin,
instagram,
status,
},
editing,
),
).then((res) => {
console.log(res);
if (!editing) navigate("/dashboard");
});
if (facebook?.length > 100) {
dispatch(createAlert("Facebook link is longer 100 characters", "danger"));
} else if (linkedin?.length > 100) {
dispatch(createAlert("LinkedIn link is longer 100 characters", "danger"));
} else if (youtube?.length > 100) {
dispatch(createAlert("Youtube link is longer 100 characters", "danger"));
} else if (instagram?.length > 100) {
dispatch(
createAlert("Instagram link is longer 100 characters", "danger"),
);
} else if (website?.length > 100) {
dispatch(createAlert("Website link is longer 100 characters", "danger"));
} else if (skills.length > 100) {
dispatch(createAlert("Skills is longer 100 characters", "danger"));
} else if (location?.length > 50) {
dispatch(createAlert("Location is longer 100 characters", "danger"));
} else if (githubusername?.length > 50) {
dispatch(
createAlert("Github username is longer 50 characters", "danger"),
);
} else if (company?.length > 50) {
dispatch(createAlert("Company name is longer 50 characters", "danger"));
} else if (bio?.length > 250) {
dispatch(createAlert("Bio name is longer 250 characters", "danger"));
} else
await dispatch(
createProfile(
{
website,
location,
skills,
githubusername,
company,
bio,
twitter,
facebook,
youtube,
linkedin,
instagram,
status,
},
editing,
),
).then(() => {
if (!editing) navigate("/dashboard");
});
};
return (
@@ -123,48 +147,48 @@ const ProfileForm = () => {
<Typography variant="body2">* = required field</Typography>
<Box component="form" noValidate onSubmit={onSubmit} sx={{ mt: 3 }}>
<Grid container spacing={2}>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<TextField name="company" fullWidth label="Company" autoFocus />
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<Typography paddingY="1rem">
Could be your own company or one you work for
</Typography>
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<TextField name="website" fullWidth label="Website" />
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<Typography paddingY="1rem">
Could be your own a or a company website
</Typography>
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<TextField name="location" fullWidth label="Location" />
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<Typography paddingY="1rem">
City & state suggest(eg. Boston MA)
</Typography>
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<TextField name="skills" required fullWidth label="Skills" />
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<Typography>
Please use comma separeted values (eg. HTML, CSS, JavaScript, PHP)
</Typography>
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<TextField name="githubUser" fullWidth label="Github Username" />
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<Typography>
If you want your latest repositories, add a Github link and
include your username
</Typography>
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<InputLabel id="status">Status *</InputLabel>
<Select
fullWidth
@@ -187,15 +211,15 @@ const ProfileForm = () => {
<MenuItem value="Other">Other</MenuItem>
</Select>
</Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<Typography paddingTop="2.3rem">
Select Profesional Status
</Typography>
</Grid>
<Grid item xs={12}>
<Grid size={{ xs: 12 }}>
<Typography>Tell us a little about yourself</Typography>
</Grid>
<Grid item xs={12}>
<Grid size={{ xs: 12 }}>
<TextField
name="bio"
multiline
@@ -204,7 +228,7 @@ const ProfileForm = () => {
label="A short bio of yourself"
/>
</Grid>
<Grid item xs={12}>
<Grid size={{ xs: 12 }}>
<Button
variant="contained"
onClick={() => toggleSocialInputs(!displaySocialInputs)}
@@ -214,33 +238,33 @@ const ProfileForm = () => {
</Grid>
{displaySocialInputs ? (
<>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}>
<TextField fullWidth name="twitter" label="Twitter URL" />
</Grid>
<Grid item xs={6}></Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}></Grid>
<Grid size={{ xs: 6 }}>
<TextField fullWidth name="facebook" label="FaceBook URL" />
</Grid>
<Grid item xs={6}></Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}></Grid>
<Grid size={{ xs: 6 }}>
<TextField fullWidth name="youtube" label="YouTube URL" />
</Grid>
<Grid item xs={6}></Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}></Grid>
<Grid size={{ xs: 6 }}>
<TextField fullWidth name="linkedin" label="Linkedin URL" />
</Grid>
<Grid item xs={6}></Grid>
<Grid item xs={6}>
<Grid size={{ xs: 6 }}></Grid>
<Grid size={{ xs: 6 }}>
<TextField fullWidth name="instagram" label="Instagram URL" />
</Grid>
</>
) : null}
<Grid item xs={8}>
<Grid size={{ xs: 8 }}>
<Button variant="contained" type="submit">
Submit Changes
</Button>
</Grid>
<Grid item xs={2}>
<Grid size={{ xs: 2 }}>
<Button variant="outlined">
<Link
style={{ color: "inherit", textDecoration: "none" }}
+53 -24
View File
@@ -9,7 +9,8 @@ import ProfileGithub from "./ProfileGithub";
import { getProfileById } from "../../actions/profile";
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
import { EducationType, ExperienceType } from "../../types";
import { Box, Button, Card, Paper, Typography } from "@mui/material";
import { Box, Button, Card, Container, Paper, Typography } from "@mui/material";
import Grid from "@mui/material/Grid2";
const Profile = () => {
const profile = useAppSelector((state) => state.profile.profile);
@@ -25,26 +26,54 @@ const Profile = () => {
}, [dispatch, id]);
return (
<Box display="flex" flexDirection="column" alignItems="center">
<>
{profile === null ? (
<Spinner />
) : (
<>
<Button component={Link} to="/profiles">
Back To Profiles
</Button>
{auth.isAuthenticated &&
auth.loading === false &&
auth.user !== null &&
auth.user._id === profile.user._id && (
<Button component={Link} to="/edit-profile">
Edit Profile
</Button>
)}
<Paper elevation={9} sx={{ width: "90%" }}>
<ProfileTop profile={profile} />
<ProfileAbout profile={profile} />
<Box>
<Paper sx={{ display: "grid", justifyItems: "center", marginY: "5px" }}>
<div>
<Button
variant="contained"
component={Link}
to="/profiles"
sx={{
width: "70px",
margin: "5px",
}}
>
Back
</Button>
{auth.isAuthenticated &&
auth.loading === false &&
auth.user !== null &&
auth.user._id === profile.user._id && (
<Button
component={Link}
to="/edit-profile"
variant="outlined"
sx={{
width: "70px",
}}
>
Edit
</Button>
)}
</div>
<Grid
container
spacing={2}
rowSpacing={"30px"}
sx={{
marginX: "20%",
}}
>
<Grid size={{ sm: 12, lg: 4 }}>
<ProfileTop profile={profile} />
</Grid>
<Grid size={{ sm: 12, lg: 8 }}>
<ProfileAbout profile={profile} />
</Grid>
<Grid size={{ xs: 12, md: 6 }}>
<Typography variant="h2">Experience</Typography>
{profile.experience.length > 0 ? (
<>
@@ -58,8 +87,8 @@ const Profile = () => {
) : (
<Typography variant="h4">No experience credentials</Typography>
)}
</Box>
<Box>
</Grid>
<Grid size={6}>
<Typography variant="h2">Education</Typography>
{profile.education.length > 0 ? (
<>
@@ -73,14 +102,14 @@ const Profile = () => {
) : (
<Typography variant="h4">No education credentials</Typography>
)}
</Box>
</Grid>
{profile.githubusername && (
<ProfileGithub username={profile.githubusername} />
)}
</Paper>
</>
</Grid>
</Paper>
)}
</Box>
</>
);
};
+20 -9
View File
@@ -1,4 +1,4 @@
import { Box, Typography } from "@mui/material";
import { Box, List, ListItem, Typography } from "@mui/material";
import React, { Fragment } from "react";
import { ProfileType } from "../../types";
@@ -11,21 +11,32 @@ const ProfileAbout = ({
}: {
profile: ProfileType;
}) => (
<Box>
<Box sx={{ textWrap: "wrap", maxWidth: "100%" }}>
{bio && (
<>
<Typography>{name.trim().split(" ")[0]}s Bio</Typography>
<Typography>{bio}</Typography>
<Typography color="info" variant="h5">
<b>{name.trim().split(" ")[0]}'s Bio</b>
</Typography>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{bio}
</Typography>
</>
)}
<Typography>Skill Set</Typography>
<Box>
<Typography color="info" variant="h5">
<b>Skill Set</b>
</Typography>
<List>
{skills.map((skill, index) => (
<Box key={index}>
<ListItem key={index}>
<Typography>{skill}</Typography>
</Box>
</ListItem>
))}
</Box>
</List>
</Box>
);
@@ -1,4 +1,4 @@
import { Box, Typography } from "@mui/material";
import { Paper, Typography } from "@mui/material";
import React from "react";
import { EducationType } from "../../types";
import formatDate from "../../utils/formatDate";
@@ -8,15 +8,49 @@ const ProfileEducation = ({
}: {
education: EducationType;
}) => (
<Box>
<Typography variant="h3">{school}</Typography>
<Typography>
<Paper elevation={12} sx={{ margin: "10px", padding: "4px" }}>
<Typography
variant="h4"
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{school}
</Typography>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
{formatDate(from)} - {to ? formatDate(to) : "Now"}
</Typography>
<Typography>Degree: {degree}</Typography>
<Typography>Field Of Study: {fieldofstudy}</Typography>
<Typography>Description: {description}</Typography>
</Box>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
<b>Degree:</b> {degree}
</Typography>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
<b>Field Of Study:</b> {fieldofstudy}
</Typography>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
<b>Description:</b> {description}
</Typography>
</Paper>
);
export default ProfileEducation;
@@ -1,4 +1,4 @@
import { Box, Typography } from "@mui/material";
import { Paper, Typography } from "@mui/material";
import React from "react";
import { ExperienceType } from "../../types";
import formatDate from "../../utils/formatDate";
@@ -8,15 +8,43 @@ const ProfileExperience = ({
}: {
experience: ExperienceType;
}) => (
<Box>
<Typography variant="h3">{company}</Typography>
<Paper elevation={12} sx={{ margin: "10px", padding: "4px" }}>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
<b>{company}</b>
</Typography>
<Typography>
{formatDate(from)} - {to ? formatDate(to) : "Now"}
</Typography>
<Typography>Position: {title}</Typography>
<Typography>Location: {location}</Typography>
<Typography>Description: {description}</Typography>
</Box>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
<b>Position:</b> {title}
</Typography>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
<b>Location:</b> {location}
</Typography>
<Typography
sx={{
textWrap: "wrap",
wordBreak: "break-word",
}}
>
<b>Description:</b> {description}
</Typography>
</Paper>
);
export default ProfileExperience;
@@ -13,10 +13,10 @@ const ProfileGithub = ({ username }: { username: string }) => {
}, [dispatch, username]);
return (
<div className="profile-github">
<h2 className="text-primary my-1">Github Repos</h2>
<div>
<h2>Github Repos</h2>
{repos.map((repo) => (
<div key={repo.id} className="repo bg-white p-1 my-1">
<div key={repo.id}>
<div>
<h4>
<a href={repo.html_url} target="_blank" rel="noopener noreferrer">
@@ -27,13 +27,9 @@ const ProfileGithub = ({ username }: { username: string }) => {
</div>
<div>
<ul>
<li className="badge badge-primary">
Stars: {repo.stargazers_count}
</li>
<li className="badge badge-dark">
Watchers: {repo.watchers_count}
</li>
<li className="badge badge-light">Forks: {repo.forks_count}</li>
<li>Stars: {repo.stargazers_count}</li>
<li>Watchers: {repo.watchers_count}</li>
<li>Forks: {repo.forks_count}</li>
</ul>
</div>
</div>
+8 -7
View File
@@ -1,3 +1,4 @@
import { BorderAll } from "@mui/icons-material";
import { Avatar, Box, Card, CardHeader, Typography } from "@mui/material";
import React from "react";
import { ProfileType } from "../../types";
@@ -14,19 +15,18 @@ const ProfileTop = ({
}: {
profile: ProfileType;
}) => {
console.log(avatar)
return (
<>
<img className="round-img my-1" src={avatar} alt="" />
<Typography variant="h1">{name}</Typography>
<Typography>
<img src={avatar} alt="" />
<Typography variant="h2">{name}</Typography>
<Typography sx={{ textWrap: "wrap" }}>
{status} {company ? <span> at {company}</span> : null}
</Typography>
<Typography>{location ? <span>{location}</span> : null}</Typography>
{location ? <Typography>{location}</Typography> : null}
<Box>
{website ? (
<a href={website} target="_blank" rel="noopener noreferrer">
<i className="fas fa-globe fa-2x" />
<i>{website}</i>
</a>
) : null}
{social
@@ -38,8 +38,9 @@ const ProfileTop = ({
href={value}
target="_blank"
rel="noopener noreferrer"
style={{ display: "block" }}
>
<i className={`fab fa-${key} fa-2x`}></i>
<i>{value}</i>
</a>
))
: null}
+14 -11
View File
@@ -22,18 +22,21 @@ const ProfileItem = ({
profile: ProfileType;
}) => {
return (
<Paper sx={{ padding: "100px" }}>
<Paper sx={{ width: "250px", paddingX: "10px" }}>
<img src={avatar} alt="" />
<Box>
<Typography>{name}</Typography>
<Typography>
{status} {company && <span> at {company}</span>}
</Typography>
<Typography>{location && <span>{location}</span>}</Typography>
<Button component={Link} to={`/profile/${_id}`}>
View Profile
</Button>
</Box>
<Typography color="info">
<b>{name}</b>
</Typography>
<Typography>
{status} {company && <span> at {company}</span>}
</Typography>
<Typography>{location && <span>{location}</span>}</Typography>
<Button component={Link} to={`/profile/${_id}`} variant="outlined">
View Profile
</Button>
<Typography color="info">
<b>Skills:</b>
</Typography>
<List>
{skills.slice(0, 4).map((skill, index) => (
<ListItemText key={index}>{skill}</ListItemText>
+27 -22
View File
@@ -17,28 +17,33 @@ const Profiles = () => {
const { profiles, loading } = useAppSelector((state) => state.profile);
return (
<Container maxWidth="sm">
<Box display="flex" flexDirection="column" alignItems="center">
{loading ? (
<Spinner />
) : (
<>
<Typography variant="h2">Developers</Typography>
<Typography variant="h6">
Browse and connect with developers
</Typography>
<Box display="flex" flexDirection="row">
{profiles.length > 0 && Array.isArray(profiles) ? (
profiles.map((profile) => (
<ProfileItem key={profile._id} profile={profile} />
))
) : (
<Typography>No profiles found...</Typography>
)}
</Box>
</>
)}
</Box>
<Container>
{loading ? (
<Spinner />
) : (
<>
<Typography variant="h2">Developers</Typography>
<Typography variant="h6">
Browse and connect with developers
</Typography>
<Box
sx={{
display: "flex",
justifyContent: "left",
gap: "10px",
flexWrap: "wrap",
}}
>
{profiles.length > 0 && Array.isArray(profiles) ? (
profiles.map((profile) => (
<ProfileItem key={profile._id} profile={profile} />
))
) : (
<Typography>No profiles found...</Typography>
)}
</Box>
</>
)}
</Container>
);
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 MiB

-1
View File
@@ -2,7 +2,6 @@ import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./App2.css";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
+96 -106
View File
@@ -1,41 +1,41 @@
import express from 'express';
import axios from 'axios';
import config from 'config';
import express from "express";
import axios from "axios";
import config from "config";
import auth from '../../middleware/auth';
import { check, validationResult } from 'express-validator';
import auth from "../../middleware/auth";
import { check, validationResult } from "express-validator";
// bring in normalize to give us a proper url, regardless of what user entered
import normalize from 'normalize-url';
import checkObjectId from '../../middleware/checkObjectId';
import normalize from "normalize-url";
import checkObjectId from "../../middleware/checkObjectId";
import Profile from '../../models/Profile';
import User from '../../models/User';
import Post from '../../models/Post';
import { isUserId } from '../../utils';
import Profile from "../../models/Profile";
import User from "../../models/User";
import Post from "../../models/Post";
import { isUserId } from "../../utils";
const router = express.Router();
// @route GET api/profile/me
// @desc Get current users profile
// @access Private
router.get('/me', auth, async (req, res) => {
router.get("/me", auth, async (req, res) => {
try {
if (isUserId(req)) {
const profile = await Profile.findOne({
user: req.user.id
}).populate('user', ['name', 'avatar']);
user: req.user.id,
}).populate("user", ["name", "avatar"]);
if (!profile) {
return res.status(400).json({ msg: 'There is no profile for this user' });
return res
.status(400)
.json({ msg: "There is no profile for this user" });
}
res.json(profile);
}
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
res.status(500).send('Server Error');
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
res.status(500).send("Server Error");
}
});
@@ -43,10 +43,10 @@ router.get('/me', auth, async (req, res) => {
// @desc Create or update user profile
// @access Private
router.post(
'/',
"/",
auth,
check('status', 'Status is required').notEmpty(),
check('skills', 'Skills is required').notEmpty(),
check("status", "Status is required").notEmpty(),
check("skills", "Skills is required").notEmpty(),
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
@@ -71,17 +71,23 @@ router.post(
const profileFields = {
user: req.user.id,
website:
website && website !== ''
website && website !== ""
? normalize(website, { forceHttps: true })
: '',
: "",
skills: Array.isArray(skills)
? skills
: skills.split(',').map((skill: string) => ' ' + skill.trim()),
...rest
: skills.split(",").map((skill: string) => " " + skill.trim()),
...rest,
};
// Build socialFields object
const socialFields: { [key: string]: any } = { youtube, twitter, instagram, linkedin, facebook };
const socialFields: { [key: string]: any } = {
youtube,
twitter,
instagram,
linkedin,
facebook,
};
// normalize social fields to ensure valid url
for (const [key, value] of Object.entries(socialFields)) {
@@ -96,33 +102,29 @@ router.post(
let profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true, upsert: true, setDefaultsOnInsert: true }
{ new: true, upsert: true, setDefaultsOnInsert: true },
);
return res.json(profile);
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
return res.status(500).send('Server Error');
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
return res.status(500).send("Server Error");
}
}
}
},
);
// @route GET api/profile
// @desc Get all profiles
// @access Public
router.get('/', async (req, res) => {
router.get("/", async (req, res) => {
try {
const profiles = await Profile.find().populate('user', ['name', 'avatar']);
const profiles = await Profile.find().populate("user", ["name", "avatar"]);
res.json(profiles);
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
res.status(500).send('Server Error');
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
res.status(500).send("Server Error");
}
});
@@ -130,31 +132,29 @@ router.get('/', async (req, res) => {
// @desc Get profile by user ID
// @access Public
router.get(
'/user/:user_id',
checkObjectId('user_id'),
"/user/:user_id",
checkObjectId("user_id"),
async ({ params: { user_id } }, res) => {
try {
const profile = await Profile.findOne({
user: user_id
}).populate('user', ['name', 'avatar']);
user: user_id,
}).populate("user", ["name", "avatar"]);
if (!profile) return res.status(400).json({ msg: 'Profile not found' });
if (!profile) return res.status(400).json({ msg: "Profile not found" });
return res.json(profile);
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
return res.status(500).json({ msg: 'Server error' });
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
return res.status(500).json({ msg: "Server error" });
}
}
},
);
// @route DELETE api/profile
// @desc Delete profile, user & posts
// @access Private
router.delete('/', auth, async (req, res) => {
router.delete("/", auth, async (req, res) => {
try {
// Remove user posts
// Remove profile
@@ -163,16 +163,14 @@ router.delete('/', auth, async (req, res) => {
await Promise.all([
Post.deleteMany({ user: req.user.id }),
Profile.findOneAndRemove({ user: req.user.id }),
User.findOneAndRemove({ _id: req.user.id })
User.findOneAndRemove({ _id: req.user.id }),
]);
res.json({ msg: 'User deleted' });
res.json({ msg: "User deleted" });
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
res.status(500).send('Server Error');
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
res.status(500).send("Server Error");
}
});
@@ -180,11 +178,11 @@ router.delete('/', auth, async (req, res) => {
// @desc Add profile experience
// @access Private
router.put(
'/experience',
"/experience",
auth,
check('title', 'Title is required').notEmpty(),
check('company', 'Company is required').notEmpty(),
check('from', 'From date is required and needs to be from the past')
check("title", "Title is required").notEmpty(),
check("company", "Company is required").notEmpty(),
check("from", "From date is required and needs to be from the past")
.notEmpty()
.custom((value, { req }) => (req.body.to ? value < req.body.to : true)),
async (req, res) => {
@@ -205,26 +203,24 @@ router.put(
res.json(profile);
}
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
res.status(500).send('Server Error');
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
res.status(500).send("Server Error");
}
}
},
);
// @route DELETE api/profile/experience/:exp_id
// @desc Delete experience from profile
// @access Private
router.delete('/experience/:exp_id', auth, async (req, res) => {
router.delete("/experience/:exp_id", auth, async (req, res) => {
try {
if (isUserId(req)) {
const foundProfile = await Profile.findOne({ user: req.user.id });
if (foundProfile) {
foundProfile.experience = foundProfile.experience.filter(
(exp: any) => exp._id.toString() !== req.params.exp_id
(exp: any) => exp._id.toString() !== req.params.exp_id,
);
await foundProfile.save();
@@ -232,11 +228,9 @@ router.delete('/experience/:exp_id', auth, async (req, res) => {
return res.status(200).json(foundProfile);
}
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
return res.status(500).json({ msg: 'Server error' });
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
return res.status(500).json({ msg: "Server error" });
}
});
@@ -244,12 +238,12 @@ router.delete('/experience/:exp_id', auth, async (req, res) => {
// @desc Add profile education
// @access Private
router.put(
'/education',
"/education",
auth,
check('school', 'School is required').notEmpty(),
check('degree', 'Degree is required').notEmpty(),
check('fieldofstudy', 'Field of study is required').notEmpty(),
check('from', 'From date is required and needs to be from the past')
check("school", "School is required").notEmpty(),
check("degree", "Degree is required").notEmpty(),
check("fieldofstudy", "Field of study is required").notEmpty(),
check("from", "From date is required and needs to be from the past")
.notEmpty()
.custom((value, { req }) => (req.body.to ? value < req.body.to : true)),
async (req, res) => {
@@ -269,62 +263,58 @@ router.put(
res.json(profile);
}
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
res.status(500).send('Server Error');
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
res.status(500).send("Server Error");
}
}
},
);
// @route DELETE api/profile/education/:edu_id
// @desc Delete education from profile
// @access Private
router.delete('/education/:edu_id', auth, async (req, res) => {
router.delete("/education/:edu_id", auth, async (req, res) => {
try {
if (isUserId(req)) {
const foundProfile = await Profile.findOne({ user: req.user.id });
if (foundProfile) {
foundProfile.education = foundProfile.education.filter(
(edu: any) => edu._id.toString() !== req.params.edu_id
(edu: any) => edu._id.toString() !== req.params.edu_id,
);
await foundProfile.save();
}
return res.status(200).json(foundProfile);
}
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
return res.status(500).json({ msg: 'Server error' });
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
return res.status(500).json({ msg: "Server error" });
}
});
// @route GET api/profile/github/:username
// @desc Get user repos from Github
// @access Public
router.get('/github/:username', async (req, res) => {
router.get("/github/:username", async (req, res) => {
console.log(config.get("githubToken"));
console.log(req.params.username);
try {
const uri = encodeURI(
`https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc`
`https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc`,
);
const headers = {
'user-agent': 'node.js',
Authorization: `token ${config.get('githubToken')}`
"user-agent": "node.js",
Authorization: `token ${config.get("githubToken")}`,
};
const gitHubResponse = await axios.get(uri, { headers });
return res.json(gitHubResponse.data);
} catch (err: unknown) {
if (typeof err === 'string')
console.error(err)
else if (err instanceof Error)
console.error(err.message);
return res.status(404).json({ msg: 'No Github profile found' });
if (typeof err === "string") console.error(err);
else if (err instanceof Error) console.error(err.message);
return res.status(404).json({ msg: "No Github profile found" });
}
});
module.exports = router
module.exports = router;