-
-
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: [], | |
}; |
@adrianhajdin Hello Adrian, first thank you for this amazing build, it was a nice coding journey. But when I deployed the app I got this devastating error on the logs and the app shows a blank page with this message (Application error: a client-side exception has occurred (see the browser console for more information). Digest: 3220988503):
The error : ClientError: GraphQL Error (Code: 503): {"response":{"error":"config worker not available","status":503,"headers":{}},"request":{"query":"\n query getProjects($category: String, $endcursor: String) {\n projectSearch(first: 8, after: $endcursor, filter: {category: {eq: $category}}) {\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n edges {\n node {\n title\n githubUrl\n description\n liveSiteUrl\n id\n image\n category\n createdBy {\n id\n email\n name\n avatarUrl\n }\n }\n }\n }\n }\n","variables":{}}} at makeRequest (/var/task/node_modules/graphql-request/build/cjs/index.js:310:15) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async Home (/var/task/.next/server/app/page.js:649:18) { response: { error: 'config worker not available', status: 503, headers: Headers { [Symbol(map)]: [Object: null prototype] } }, request: { query: '\n' + ' query getProjects($category: String, $endcursor: String) {\n' + ' projectSearch(first: 8, after: $endcursor, filter: {category: {eq: $category}}) {\n' + ' pageInfo {\n' + ' hasNextPage\n' + ' hasPreviousPage\n' + ' startCursor\n' + ' endCursor\n' + ' }\n' + ' edges {\n' + ' node {\n' + ' title\n' + ' githubUrl\n' + ' description\n' + ' liveSiteUrl\n' + ' id\n' + ' image\n' + ' category\n' + ' createdBy {\n' + ' id\n' + ' email\n' + ' name\n' + ' avatarUrl\n' + ' }\n' + ' }\n' + ' }\n' + ' }\n' + ' }\n', variables: { category: undefined, endcursor: undefined } } } [Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.] { digest: '3220988503' }
PS: It would be much appreciated if you took the time with this one.I guess I found the problem and its with grafbase deployment, this time the error is on grafbase logs : Failed to build the TypeScript config!?
Hi did you find any workaround regarding this bug ?
hey did you find the solution for this
[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.
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.
did you find any solution