
يحتوي هذا المستودع على الكود المقابل لبرنامج تعليمي متعمق متوفر على قناة YouTube الخاصة بنا ، JavaScript Mastery .
إذا كنت تفضل التعلم البصري ، فهذا هو المورد المثالي لك. اتبع البرنامج التعليمي الخاص بنا لمعرفة كيفية بناء مشاريع مثل هذه الخطوة بخطوة بطريقة صديقة للمبتدئين!

يتميز استنساخ كامل المكدس الذي تم تطويره باستخدام Next.js و GraphQL و Auth Next و TypeScript و TailwindCSS جميع الميزات اللازمة للمشاريع من المشاركة والعرض.
إذا كنت تبدأ وتحتاج إلى مساعدة أو تواجه أي أخطاء ، انضم إلى مجتمع Discord النشط مع أكثر من 27 ألف عضو. إنه مكان يساعد فيه الناس بعضهم البعض.

الصفحة الرئيسية للتصميم الحديث : يتميز بتصميم نظيف وحديث يشبه Dribbble ، مع واجهة جذابة عرض معاينات المشروع والتنقل.
التصفح والتراجع : تصفح مشاريع مختلفة ، وتصفيةها حسب الفئة ، وتجربة ترقيم تراجع سلس لاستكشاف البيانات السلس.
نظام المصادقة والترخيص : يتيح نظام المصادقة والتفويض يعمل بكامل طاقته للمستخدمين تسجيل الدخول بشكل آمن باستخدام مصادقة JWT و Google.
إنشاء صفحة نشر : يوفر مساحة مخصصة للمستخدمين لمشاركة مشاريعهم مع المجتمع. ويشمل الحقول للحصول على تفاصيل المشروع والصور والمعلومات الأخرى ذات الصلة.
تفاصيل المشروع والمشاريع ذات الصلة : طريقة عرض مفصلة مع وظائف المشاريع ذات الصلة ، وتمكين المستخدمين من استكشاف المزيد من المشاريع في نفس الفئة أو السمة.
تحرير وإعادة تحميل الصور : يتمتع المستخدمون بالقدرة على تحرير المشاريع التي تم إنشاؤها مسبقًا ، بما في ذلك القدرة على إعادة تحميل الصور من أجهزتهم إلى السحابة للحصول على التحديثات.
حذف المشاريع : تعمل وظيفة الحذف على تبسيط إزالة المشروع من خلال عملية نقرة واحدة ، مما يؤدي إلى تبسيط تجربة المستخدم.
صفحة ملف تعريف المستخدم على غرار المحفظة : تتبنى صفحة ملف تعريف ملف تعريف المستخدم تصميمًا على غرار المحفظة ، وتعرض مشاريع المستخدم جنبًا إلى جنب مع ملفات تعريف المشروع للمستخدمين الآخرين لسهولة الاستكشاف.
طرق API الخلفية : طرق API الخلفية للتعامل مع إدارة الرمز المميز JWT من أجل المصادقة الآمنة وتحميل الصور ، ودعم التكامل السلس مع الواجهة الأمامية.
وغيرها الكثير ، بما في ذلك العمارة الكود وإعادة الاستخدام
اتبع الخطوات هذه لإعداد المشروع محليًا على جهازك.
المتطلبات الأساسية
تأكد من تثبيت ما يلي على جهازك:
استنساخ المستودع
git clone https://github.com/adrianhajdin/project_nextjs13_flexibble.git
cd project_nextjs13_flexibbleتثبيت
تثبيت تبعيات المشروع باستخدام NPM:
npm installإعداد متغيرات البيئة
قم بإنشاء ملف جديد يسمى .env في جذر مشروعك وأضف المحتوى التالي:
GOOGLE_CLIENT_ID =
GOOGLE_CLIENT_SECRET =
NEXTAUTH_URL =
NEXTAUTH_SECRET =
CLOUDINARY_NAME =
CLOUDINARY_KEY =
CLOUDINARY_SECRET =
GRAFBASE_API_URL =
GRAFBASE_API_KEY =استبدل قيم العنصر النائب ببيانات اعتمادك الفعلية. يمكنك الحصول على بيانات الاعتماد هذه من خلال الاشتراك على مواقع الويب المقابلة من Google Cloud و Clationalinary و Grafbase.
لسر المصادقة التالي ، يمكنك إنشاء أي سر عشوائي باستخدام Crytool.
تشغيل المشروع
npm run devافتح http: // localhost: 3000 في متصفحك لعرض المشروع.
common.types.ts 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 ;
}constants.ts 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' ,
] ,
} ,
] ;globals.css @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 : 5 px ;
height : 4 px ;
}
:: -webkit-scrollbar-thumb {
background : # 888 ;
border-radius : 12 px ;
}
. 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-[ 210 px ] min-h-[ 197 px ];
}
. 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-[ 300 px ] 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-[ 300 px ] 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-[ 400 px ] min-h-[ 200 px ] 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;
}graphqlQueriesAndMutations.ts export const createProjectMutation = `
mutation CreateProject($input: ProjectCreateInput!) {
projectCreate(input: $input) {
project {
id
title
description
createdBy {
email
name
}
}
}
}
` ;
export const updateProjectMutation = `
mutation UpdateProject($id: ID!, $input: ProjectUpdateInput!) {
projectUpdate(by: { id: $id }, input: $input) {
project {
id
title
description
createdBy {
email
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
email
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
email
name
avatarUrl
}
}
}
}
}
` ;
export const getProjectByIdQuery = `
query GetProjectById($id: ID!) {
project(by: { id: $id }) {
id
title
description
image
liveSiteUrl
githubUrl
category
createdBy {
id
name
email
avatarUrl
}
}
}
` ;
export const getUserQuery = `
query GetUser($email: String!) {
user(by: { email: $email }) {
id
name
email
avatarUrl
description
githubUrl
linkedinUrl
}
}
` ;
export const getProjectsOfUserQuery = `
query getUserProjects($id: ID!, $last: Int = 4) {
user(by: { id: $id }) {
id
name
email
description
avatarUrl
githubUrl
linkedinUrl
projects(last: $last) {
edges {
node {
id
title
image
}
}
}
}
}
` ;ProfileMenu.tsx "use client"
import Link from "next/link" ;
import Image from "next/image" ;
import { signOut } from "next-auth/react" ;
import { Fragment , useState } from "react" ;
import { Menu , Transition } from "@headlessui/react" ;
import { SessionInterface } from "@/common.types" ;
const ProfileMenu = ( { session } : { session : SessionInterface } ) => {
const [ openModal , setOpenModal ] = useState ( false ) ;
return (
< div className = "flexCenter z-10 flex-col relative" >
< Menu as = "div" >
< Menu . Button className = "flexCenter" onMouseEnter = { ( ) => setOpenModal ( true ) } >
{ session ?. user ?. image && (
< Image
src = { session . user . image }
width = { 40 }
height = { 40 }
className = "rounded-full"
alt = "user profile image"
/ >
) }
< / Menu . Button >
< Transition
show = { openModal }
as = { Fragment }
enter = "transition ease-out duration-200"
enterFrom = "transform opacity-0 scale-95"
enterTo = "transform opacity-100 scale-100"
leave = "transition ease-in duration-75"
leaveFrom = "transform opacity-100 scale-100"
leaveTo = "transform opacity-0 scale-95"
>
< Menu . Items
static
className = "flexStart profile_menu-items"
onMouseLeave = { ( ) => setOpenModal ( false ) }
>
< div className = "flex flex-col items-center gap-y-4" >
{ session ? . user ?. image && (
< Image
src = { session ? . user ? . image }
className = "rounded-full"
width = { 80 }
height = { 80 }
alt = "profile Image"
/ >
) }
< p className = "font-semibold" > { session ? . user ?. name } < / p>
< / div >
< div className = "flex flex-col gap-3 pt-10 items-start w-full" >
< Menu . Item >
< Link href = { `/profile/${ session ? . user ? . id } `} className="text-sm">Work Preferences</Link>
</Menu.Item>
<Menu.Item>
<Link href={` / profile / $ { session ?. user ?. id } `} className="text-sm">Settings</Link>
</Menu.Item>
<Menu.Item>
<Link href={` / profile / $ { session ?. user ?. id } ` } className = "text-sm" > Profile < / Link>
< / Menu.Item>
< / div >
< div className = "w-full flexStart border-t border-nav-border mt-5 pt-5" >
< Menu . Item >
< button type = "button" className = "text-sm" onClick = { ( ) => signOut ( ) } >
Sign out
< / button>
< / Menu.Item>
< / div >
< / Menu . Items >
< / Transition >
< / Menu >
< / div >
)
}
export default ProfileMenuProfilePage.tsx 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 ProfilePageprojectPage.tsx 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 Projecttailwind.config.ts tailwind . config . ts
/** @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 : [ ] ,
} ; الأصول المستخدمة في المشروع هنا
تقدم مهاراتك مع دورة Next.js 14 Pro
استمتعت بإنشاء هذا المشروع؟ الغوص أعمق في دوراتنا المحترفة لمغامرة تعليمية أكثر ثراء. إنها مليئة بتفسيرات مفصلة وميزات رائعة وتمارين لزيادة مهاراتك. أعطها!

تسريع رحلتك المهنية مع برنامج تدريب الخبراء
وإذا كنت جائعًا لأكثر من مجرد دورة تدريبية وتريد أن تفهم كيف نتعلم ونتعامل مع تحديات التكنولوجيا ، قفز إلى Masterclass المخصصة لدينا. نغطي أفضل الممارسات ، ومهارات الويب المختلفة ، ونقدم الإرشاد لتعزيز ثقتك بنفسك. دعونا نتعلم وننمو معًا!