-
-
Save adrianhajdin/a453d745c2361ae4183b421f577a0715 to your computer and use it in GitHub Desktop.
import { User, Session } from 'next-auth' | |
export type FormState = { | |
title: string; | |
description: string; | |
image: string; | |
liveSiteUrl: string; | |
githubUrl: string; | |
category: string; | |
}; | |
export interface ProjectInterface { | |
title: string; | |
description: string; | |
image: string; | |
liveSiteUrl: string; | |
githubUrl: string; | |
category: string; | |
id: string; | |
createdBy: { | |
name: string; | |
email: string; | |
avatarUrl: string; | |
id: string; | |
}; | |
} | |
export interface UserProfile { | |
id: string; | |
name: string; | |
email: string; | |
description: string | null; | |
avatarUrl: string; | |
githubUrl: string | null; | |
linkedinUrl: string | null; | |
projects: { | |
edges: { node: ProjectInterface }[]; | |
pageInfo: { | |
hasPreviousPage: boolean; | |
hasNextPage: boolean; | |
startCursor: string; | |
endCursor: string; | |
}; | |
}; | |
} | |
export interface SessionInterface extends Session { | |
user: User & { | |
id: string; | |
name: string; | |
email: string; | |
avatarUrl: string; | |
}; | |
} | |
export interface ProjectForm { | |
title: string; | |
description: string; | |
image: string; | |
liveSiteUrl: string; | |
githubUrl: string; | |
category: string; | |
} |
export const NavLinks = [ | |
{ href: '/', key: 'Inspiration', text: 'Inspiration' }, | |
{ href: '/', key: 'Find Projects', text: 'Find Projects' }, | |
{ href: '/', key: 'Learn Development', text: 'Learn Development' }, | |
{ href: '/', key: 'Career Advancement', text: 'Career Advancement' }, | |
{ href: '/', key: 'Hire Developers', text: 'Hire Developers' } | |
]; | |
export const categoryFilters = [ | |
"Frontend", | |
"Backend", | |
"Full-Stack", | |
"Mobile", | |
"UI/UX", | |
"Game Dev", | |
"DevOps", | |
"Data Science", | |
"Machine Learning", | |
"Cybersecurity", | |
"Blockchain", | |
"E-commerce", | |
"Chatbots" | |
] | |
export const footerLinks = [ | |
{ | |
title: 'For developers', | |
links: [ | |
'Go Pro!', | |
'Explore development work', | |
'Development blog', | |
'Code podcast', | |
'Open-source projects', | |
'Refer a Friend', | |
'Code of conduct', | |
], | |
}, | |
{ | |
title: 'Hire developers', | |
links: [ | |
'Post a job opening', | |
'Post a freelance project', | |
'Search for developers', | |
], | |
}, | |
{ | |
title: 'Brands', | |
links: [ | |
'Advertise with us', | |
], | |
}, | |
{ | |
title: 'Company', | |
links: [ | |
'About', | |
'Careers', | |
'Support', | |
'Media kit', | |
'Testimonials', | |
'API', | |
'Terms of service', | |
'Privacy policy', | |
'Cookie policy', | |
], | |
}, | |
{ | |
title: 'Directories', | |
links: [ | |
'Development jobs', | |
'Developers for hire', | |
'Freelance developers for hire', | |
'Tags', | |
'Places', | |
], | |
}, | |
{ | |
title: 'Development assets', | |
links: [ | |
'Code Marketplace', | |
'GitHub Marketplace', | |
'NPM Registry', | |
'Packagephobia', | |
], | |
}, | |
{ | |
title: 'Development Resources', | |
links: [ | |
'Freelancing', | |
'Development Hiring', | |
'Development Portfolio', | |
'Development Education', | |
'Creative Process', | |
'Development Industry Trends', | |
], | |
}, | |
]; | |
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"); | |
@tailwind base; | |
@tailwind components; | |
@tailwind utilities; | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: Inter; | |
} | |
.flexCenter { | |
@apply flex justify-center items-center; | |
} | |
.flexBetween { | |
@apply flex justify-between items-center; | |
} | |
.flexStart { | |
@apply flex items-center justify-start; | |
} | |
.text-small { | |
@apply text-sm font-medium; | |
} | |
.paddings { | |
@apply lg:px-20 py-6 px-5; | |
} | |
::-webkit-scrollbar { | |
width: 5px; | |
height: 4px; | |
} | |
::-webkit-scrollbar-thumb { | |
background: #888; | |
border-radius: 12px; | |
} | |
.modal-head-text { | |
@apply md:text-5xl text-3xl font-extrabold text-left max-w-5xl w-full; | |
} | |
.no-result-text { | |
@apply w-full text-center my-10 px-2; | |
} | |
/* Project Details */ | |
.user-actions_section { | |
@apply fixed max-md:hidden flex gap-4 flex-col right-10 top-20; | |
} | |
.user-info { | |
@apply flex flex-wrap whitespace-nowrap text-sm font-normal gap-2 w-full; | |
} | |
/* Home */ | |
.projects-grid { | |
@apply grid xl:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-10 mt-10 w-full; | |
} | |
/* Project Actions */ | |
.edit-action_btn { | |
@apply p-3 text-gray-100 bg-light-white-400 rounded-lg text-sm font-medium; | |
} | |
.delete-action_btn { | |
@apply p-3 text-gray-100 hover:bg-red-600 rounded-lg text-sm font-medium; | |
} | |
/* Related Project Card */ | |
.related_project-card { | |
@apply flex-col rounded-2xl min-w-[210px] min-h-[197px]; | |
} | |
.related_project-card_title { | |
@apply justify-end items-end w-full h-1/3 bg-gradient-to-b from-transparent to-black/50 rounded-b-2xl gap-2 absolute bottom-0 right-0 font-semibold text-lg text-white p-4; | |
} | |
.related_projects-grid { | |
@apply grid xl:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-8 mt-5; | |
} | |
/* Custom Menu */ | |
.custom_menu-btn { | |
@apply gap-4 w-full rounded-md bg-light-white-100 p-4 text-base outline-none capitalize; | |
} | |
.custom_menu-items { | |
@apply flex-col absolute left-0 mt-2 xs:min-w-[300px] w-fit max-h-64 origin-top-right rounded-xl bg-white border border-nav-border shadow-menu overflow-y-auto; | |
} | |
.custom_menu-item { | |
@apply text-left w-full px-5 py-2 text-sm hover:bg-light-white-100 self-start whitespace-nowrap capitalize; | |
} | |
/* Footer */ | |
.footer { | |
@apply flex-col paddings w-full gap-20 bg-light-white; | |
} | |
.footer_copyright { | |
@apply max-sm:flex-col w-full text-sm font-normal; | |
} | |
.footer_column { | |
@apply flex-1 flex flex-col gap-3 text-sm min-w-max; | |
} | |
/* Form Field */ | |
.form_field-input { | |
@apply w-full outline-0 bg-light-white-100 rounded-xl p-4; | |
} | |
/* Modal */ | |
.modal { | |
@apply fixed z-10 left-0 right-0 top-0 bottom-0 mx-auto bg-black/80; | |
} | |
.modal_wrapper { | |
@apply flex justify-start items-center flex-col absolute h-[95%] w-full bottom-0 bg-white rounded-t-3xl lg:px-40 px-8 pt-14 pb-72 overflow-auto; | |
} | |
/* Navbar */ | |
.navbar { | |
@apply py-5 px-8 border-b border-nav-border gap-4; | |
} | |
/* Profile Menu */ | |
.profile_menu-items { | |
@apply flex-col absolute right-1/2 translate-x-1/2 mt-3 p-7 sm:min-w-[300px] min-w-max rounded-xl bg-white border border-nav-border shadow-menu; | |
} | |
/* Profile Card */ | |
.profile_card-title { | |
@apply justify-end items-end w-full h-1/3 bg-gradient-to-b from-transparent to-black/50 rounded-b-2xl gap-2 absolute bottom-0 right-0 font-semibold text-lg text-white p-4; | |
} | |
/* Project Form */ | |
.form { | |
@apply flex-col w-full lg:pt-24 pt-12 gap-10 text-lg max-w-5xl mx-auto; | |
} | |
.form_image-container { | |
@apply w-full lg:min-h-[400px] min-h-[200px] relative; | |
} | |
.form_image-label { | |
@apply z-10 text-center w-full h-full p-20 text-gray-100 border-2 border-gray-50 border-dashed; | |
} | |
.form_image-input { | |
@apply absolute z-30 w-full opacity-0 h-full cursor-pointer; | |
} | |
/* Profile Projects */ | |
.profile_projects { | |
@apply grid xl:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-8 mt-5; | |
} |
export const createProjectMutation = ` | |
mutation CreateProject($input: ProjectCreateInput!) { | |
projectCreate(input: $input) { | |
project { | |
id | |
title | |
description | |
createdBy { | |
name | |
} | |
} | |
} | |
} | |
`; | |
export const updateProjectMutation = ` | |
mutation UpdateProject($id: ID!, $input: ProjectUpdateInput!) { | |
projectUpdate(by: { id: $id }, input: $input) { | |
project { | |
id | |
title | |
description | |
createdBy { | |
name | |
} | |
} | |
} | |
} | |
`; | |
export const deleteProjectMutation = ` | |
mutation DeleteProject($id: ID!) { | |
projectDelete(by: { id: $id }) { | |
deletedId | |
} | |
} | |
`; | |
export const createUserMutation = ` | |
mutation CreateUser($input: UserCreateInput!) { | |
userCreate(input: $input) { | |
user { | |
name | |
avatarUrl | |
description | |
githubUrl | |
linkedinUrl | |
id | |
} | |
} | |
} | |
`; | |
export const projectsQuery = ` | |
query getProjects($category: String, $endCursor: String) { | |
projectSearch(first: 8, after: $endCursor, filter: {category: {eq: $category}}) { | |
pageInfo { | |
hasNextPage | |
hasPreviousPage | |
startCursor | |
endCursor | |
} | |
edges { | |
node { | |
title | |
githubUrl | |
description | |
liveSiteUrl | |
id | |
image | |
category | |
createdBy { | |
id | |
name | |
avatarUrl | |
} | |
} | |
} | |
} | |
} | |
`; | |
export const getProjectByIdQuery = ` | |
query GetProjectById($id: ID!) { | |
project(by: { id: $id }) { | |
id | |
title | |
description | |
image | |
liveSiteUrl | |
githubUrl | |
category | |
createdBy { | |
id | |
name | |
avatarUrl | |
} | |
} | |
} | |
`; | |
export const getUserQuery = ` | |
query GetUser($email: String!) { | |
user(by: { email: $email }) { | |
id | |
name | |
avatarUrl | |
description | |
githubUrl | |
linkedinUrl | |
} | |
} | |
`; | |
export const getProjectsOfUserQuery = ` | |
query getUserProjects($id: ID!, $last: Int = 4) { | |
user(by: { id: $id }) { | |
id | |
name | |
description | |
avatarUrl | |
githubUrl | |
linkedinUrl | |
projects(last: $last) { | |
edges { | |
node { | |
id | |
title | |
image | |
} | |
} | |
} | |
} | |
} | |
`; |
import { ProjectInterface, UserProfile } from '@/common.types' | |
import Image from 'next/image' | |
import Link from 'next/link' | |
import Button from "./Button"; | |
import ProjectCard from './ProjectCard'; | |
type Props = { | |
user: UserProfile; | |
} | |
const ProfilePage = ({ user }: Props) => ( | |
<section className='flexCenter flex-col max-w-10xl w-full mx-auto paddings'> | |
<section className="flexBetween max-lg:flex-col gap-10 w-full"> | |
<div className='flex items-start flex-col w-full'> | |
<Image src={user?.avatarUrl} width={100} height={100} className="rounded-full" alt="user image" /> | |
<p className="text-4xl font-bold mt-10">{user?.name}</p> | |
<p className="md:text-5xl text-3xl font-extrabold md:mt-10 mt-5 max-w-lg">I’m Software Engineer at JSM 👋</p> | |
<div className="flex mt-8 gap-5 w-full flex-wrap"> | |
<Button | |
title="Follow" | |
leftIcon="/plus-round.svg" | |
bgColor="bg-light-white-400 !w-max" | |
textColor="text-black-100" | |
/> | |
<Link href={`mailto:${user?.email}`}> | |
<Button title="Hire Me" leftIcon="/email.svg" /> | |
</Link> | |
</div> | |
</div> | |
{user?.projects?.edges?.length > 0 ? ( | |
<Image | |
src={user?.projects?.edges[0]?.node?.image} | |
alt="project image" | |
width={739} | |
height={554} | |
className='rounded-xl object-contain' | |
/> | |
) : ( | |
<Image | |
src="/profile-post.png" | |
width={739} | |
height={554} | |
alt="project image" | |
className='rounded-xl' | |
/> | |
)} | |
</section> | |
<section className="flexStart flex-col lg:mt-28 mt-16 w-full"> | |
<p className="w-full text-left text-lg font-semibold">Recent Work</p> | |
<div className="profile_projects"> | |
{user?.projects?.edges?.map( | |
({ node }: { node: ProjectInterface }) => ( | |
<ProjectCard | |
key={`${node?.id}`} | |
id={node?.id} | |
image={node?.image} | |
title={node?.title} | |
name={user.name} | |
avatarUrl={user.avatarUrl} | |
userId={user.id} | |
/> | |
) | |
)} | |
</div> | |
</section> | |
</section> | |
) | |
export default ProfilePage |
import Image from "next/image" | |
import Link from "next/link" | |
import { getCurrentUser } from "@/lib/session" | |
import { getProjectDetails } from "@/lib/actions" | |
import Modal from "@/components/Modal" | |
// import ProjectActions from "@/components/ProjectActions" | |
import RelatedProjects from "@/components/RelatedProjects" | |
import { ProjectInterface } from "@/common.types" | |
import ProjectActions from "@/components/ProjectActions" | |
const Project = async ({ params: { id } }: { params: { id: string } }) => { | |
const session = await getCurrentUser() | |
const result = await getProjectDetails(id) as { project?: ProjectInterface} | |
if (!result?.project) return ( | |
<p className="no-result-text">Failed to fetch project info</p> | |
) | |
const projectDetails = result?.project | |
const renderLink = () => `/profile/${projectDetails?.createdBy?.id}` | |
return ( | |
<Modal> | |
<section className="flexBetween gap-y-8 max-w-4xl max-xs:flex-col w-full"> | |
<div className="flex-1 flex items-start gap-5 w-full max-xs:flex-col"> | |
<Link href={renderLink()}> | |
<Image | |
src={projectDetails?.createdBy?.avatarUrl} | |
width={50} | |
height={50} | |
alt="profile" | |
className="rounded-full" | |
/> | |
</Link> | |
<div className="flex-1 flexStart flex-col gap-1"> | |
<p className="self-start text-lg font-semibold"> | |
{projectDetails?.title} | |
</p> | |
<div className="user-info"> | |
<Link href={renderLink()}> | |
{projectDetails?.createdBy?.name} | |
</Link> | |
<Image src="/dot.svg" width={4} height={4} alt="dot" /> | |
<Link href={`/?category=${projectDetails.category}`} className="text-primary-purple font-semibold"> | |
{projectDetails?.category} | |
</Link> | |
</div> | |
</div> | |
</div> | |
{session?.user?.email === projectDetails?.createdBy?.email && ( | |
<div className="flex justify-end items-center gap-2"> | |
<ProjectActions projectId={projectDetails?.id} /> | |
</div> | |
)} | |
</section> | |
<section className="mt-14"> | |
<Image | |
src={`${projectDetails?.image}`} | |
className="object-cover rounded-2xl" | |
width={1064} | |
height={798} | |
alt="poster" | |
/> | |
</section> | |
<section className="flexCenter flex-col mt-20"> | |
<p className="max-w-5xl text-xl font-normal"> | |
{projectDetails?.description} | |
</p> | |
<div className="flex flex-wrap mt-5 gap-5"> | |
<Link href={projectDetails?.githubUrl} target="_blank" rel="noreferrer" className="flexCenter gap-2 tex-sm font-medium text-primary-purple"> | |
🖥 <span className="underline">Github</span> | |
</Link> | |
<Image src="/dot.svg" width={4} height={4} alt="dot" /> | |
<Link href={projectDetails?.liveSiteUrl} target="_blank" rel="noreferrer" className="flexCenter gap-2 tex-sm font-medium text-primary-purple"> | |
🚀 <span className="underline">Live Site</span> | |
</Link> | |
</div> | |
</section> | |
<section className="flexCenter w-full gap-8 mt-28"> | |
<span className="w-full h-0.5 bg-light-white-200" /> | |
<Link href={renderLink()} className="min-w-[82px] h-[82px]"> | |
<Image | |
src={projectDetails?.createdBy?.avatarUrl} | |
className="rounded-full" | |
width={82} | |
height={82} | |
alt="profile image" | |
/> | |
</Link> | |
<span className="w-full h-0.5 bg-light-white-200" /> | |
</section> | |
<RelatedProjects userId={projectDetails?.createdBy?.id} projectId={projectDetails?.id} /> | |
</Modal> | |
) | |
} | |
export default Project |
/** @type {import('tailwindcss').Config} */ | |
module.exports = { | |
content: [ | |
'./pages/**/*.{js,ts,jsx,tsx,mdx}', | |
'./components/**/*.{js,ts,jsx,tsx,mdx}', | |
'./app/**/*.{js,ts,jsx,tsx,mdx}', | |
], | |
theme: { | |
extend: { | |
colors: { | |
'nav-border': '#EBEAEA', | |
'light-white': '#FAFAFB', | |
'light-white-100': '#F1F4F5', | |
'light-white-200': '#d7d7d7', | |
'light-white-300': '#F3F3F4', | |
'light-white-400': '#E2E5F1', | |
'light-white-500': '#E4E4E4', | |
gray: '#4D4A4A', | |
'gray-100': '#3d3d4e', | |
'black-100': '#252525', | |
'primary-purple': '#9747FF', | |
'gray-50': '#D9D9D9', | |
}, | |
boxShadow: { | |
menu: '0px 159px 95px rgba(13,12,34,0.01), 0px 71px 71px rgba(13,12,34,0.02), 0px 18px 39px rgba(13,12,34,0.02), 0px 0px 0px rgba(13,12,34,0.02)', | |
}, | |
screens: { | |
'xs': '400px', | |
}, | |
maxWidth: { | |
'10xl': '1680px' | |
} | |
}, | |
}, | |
plugins: [], | |
}; |
You should add Token folder and add NEXTAUTH_SECRET in .env
for this error
[next-auth][error][JWT_SESSION_ERROR] https://next-auth.js.org/errors#jwt_session_error jwt malformed { message: 'jwt malformed', stack: 'JsonWebTokenError: jwt malformed\n' + ' at module.exports [as verify] (webpack-internal:///(rsc)/./node_modules/jsonwebtoken/verify.js:72:21)\n' + ' at Object.decode (webpack-internal:///(rsc)/./lib/session.ts:33:86)\n' + ' at Object.session (webpack-internal:///(rsc)/./node_modules/next-auth/core/routes/session.js:25:44)\n' + ' at AuthHandler (webpack-internal:///(rsc)/./node_modules/next-auth/core/index.js:161:50)\n' + ' at async getServerSession (webpack-internal:///(rsc)/./node_modules/next-auth/next/index.js:126:21)\n' + ' at async getCurrentUser (webpack-internal:///(rsc)/./lib/session.ts:74:21)\n' + ' at async Navbar (webpack-internal:///(rsc)/./components/Navbar.tsx:21:21)', name: 'JsonWebTokenError' }
Solve
You should add Token folder and add NEXTAUTH_SECRET in .env
what you mean by Token folder?
I am also facing this. You should add Token folder and add NEXTAUTH_SECRET in .env
Anyone wanted to help me?
there is no token folder, just the two .env files in the root and in the grafbase folder.
[next-auth][error][JWT_SESSION_ERROR] https://next-auth.js.org/errors#jwt_session_error jwt malformed { message: 'jwt malformed', stack: 'JsonWebTokenError: jwt malformed\n' + ' at module.exports [as verify] (webpack-internal:///(rsc)/./node_modules/jsonwebtoken/verify.js:72:21)\n' + ' at Object.decode (webpack-internal:///(rsc)/./lib/session.ts:33:86)\n' + ' at Object.session (webpack-internal:///(rsc)/./node_modules/next-auth/core/routes/session.js:25:44)\n' + ' at AuthHandler (webpack-internal:///(rsc)/./node_modules/next-auth/core/index.js:161:50)\n' + ' at async getServerSession (webpack-internal:///(rsc)/./node_modules/next-auth/next/index.js:126:21)\n' + ' at async getCurrentUser (webpack-internal:///(rsc)/./lib/session.ts:74:21)\n' + ' at async Navbar (webpack-internal:///(rsc)/./components/Navbar.tsx:21:21)', name: 'JsonWebTokenError' }
jwt malformed
i literally tried everything i know,
i put the auth secret with the provider and i've checked everything, but i get access denied after tryin to signin with google,
nothing works for me.