Skip to content

Commit 2e120b0

Browse files
committed
add documents
1 parent aeefcea commit 2e120b0

File tree

23 files changed

+791
-4
lines changed

23 files changed

+791
-4
lines changed

Documentation/app/Layout.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Layout
2+
3+
کد فوق یک کامپوننت RootLayout است که برای تنظیمات کلی و طرح‌بندی صفحه استفاده می‌شود. در ادامه، توضیحی مفصل برای هر بخش از کد ارائه خواهم داد:
4+
5+
```javascript
6+
import { library } from '@fortawesome/fontawesome-svg-core';
7+
import { fab } from "@fortawesome/free-brands-svg-icons";
8+
import { fas } from "@fortawesome/free-solid-svg-icons";
9+
import Footer from "../components/Footer";
10+
import Navbar from "../components/Header";
11+
import vazirmatn from "../styles/fonts";
12+
import "./globals.css";
13+
14+
// اضافه کردن آیکون‌های Font Awesome به کتابخانه
15+
library.add(fab, fas);
16+
17+
// تعریف متادیتا برای صفحه
18+
export const metadata = {
19+
title: "دنیز پز",
20+
description: "به دنیز پز خوش آمدید",
21+
};
22+
23+
// تعریف کامپوننت RootLayout
24+
export default function RootLayout({
25+
children,
26+
}: {
27+
children: React.ReactNode;
28+
}) {
29+
// رندر کردن کامپوننت‌های Navbar، children و Footer
30+
return (
31+
<html lang="fa" dir="rtl">
32+
<body
33+
className={`${vazirmatn.className} flex flex-col min-h-screen`}
34+
suppressHydrationWarning={true}
35+
>
36+
<Navbar />
37+
{children}
38+
<Footer />
39+
</body>
40+
</html>
41+
);
42+
}
43+
```
44+
45+
این کامپوننت برای تنظیمات کلی صفحه استفاده می‌شود. در اینجا:
46+
47+
- آیکون‌های Font Awesome با استفاده از توابع `library.add(fab, fas)` به کتابخانه اضافه می‌شوند.
48+
- متادیتا صفحه با استفاده از متغیر `metadata` تعریف شده است که عنوان و توضیحات صفحه را شامل می‌شود.
49+
- کامپوننت `RootLayout` برای طرح‌بندی کلی صفحه استفاده می‌شود. در اینجا، کامپوننت‌های `Navbar`، `children` و `Footer` به ترتیب قرار داده شده‌اند. `Navbar` بالای صفحه و `Footer` در پایین نمایش داده می‌شود.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Get Single Recipe
2+
3+
کد فوق یک فایل سمت سرور برای ایجاد درخواست GET در مورد یک رسپی بر اساس آی‌دی مشخص است. در ادامه، توضیحی مفصل برای هر بخش از کد ارائه خواهم داد:
4+
5+
```javascript
6+
import { NextResponse } from "next/server";
7+
import { Recipe } from "../../../../models";
8+
9+
export const dynamic = 'force-dynamic'
10+
11+
export async function GET(req: Request) {
12+
try {
13+
const { searchParams } = new URL(req.url);
14+
const id = searchParams.get('id');
15+
16+
// جستجوی رسپی بر اساس آی‌دی
17+
const recipe = await Recipe.findOne({
18+
_id: id
19+
});
20+
21+
if (await recipe) {
22+
// اگر رسپی پیدا شد، آن را به عنوان پاسخ JSON برگردانده می‌شود
23+
return NextResponse.json({ status: 200, data: recipe });
24+
} else {
25+
// اگر رسپی پیدا نشد، پاسخی با کد 204 و پیام "No recipe found." برگردانده می‌شود
26+
return NextResponse.json({ status: 204, success: false, message: 'No recipe found.' });
27+
}
28+
} catch (error) {
29+
console.log('Error in getting recipe by id:', error);
30+
// اگر در هنگام دریافت رسپی بر اساس آی‌دی خطایی رخ دهد، پاسخی با کد 500 و پیام خطا برگردانده می‌شود
31+
return NextResponse.json({ status: 500, success: false, message: error });
32+
}
33+
}
34+
```
35+
36+
این فایل سرور برای پردازش درخواست GET در مورد یک رسپی بر اساس آی‌دی استفاده می‌شود.
37+
38+
- تابع `GET` برای دریافت رسپی بر اساس آی‌دی تعریف شده است. دریافت آی‌دی رسپی از آدرس URL و جستجوی رسپی بر اساس این آی‌دی صورت می‌گیرد.
39+
- اگر رسپی پیدا شود، آن را به عنوان پاسخ JSON با کد 200 برگردانده می‌شود.
40+
- اگر رسپی پیدا نشود، پاسخی با کد 204 و پیام "No recipe found." برگردانده می‌شود.
41+
- اگر در هنگام دریافت رسپی بر اساس آی‌دی خطایی رخ دهد، پاسخی با کد 500 و پیام خطا برگردانده می‌شود.
42+
43+
با استفاده از این فایل سرور، می‌توانید درخواست GET مربوط به یک رسپی بر اساس آی‌دی را در سمت سرور پردازش کنید.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Recipes
2+
3+
کد فوق یک فایل سمت سرور برای ایجاد درخواست‌های GET، POST و DELETE در مورد رسپی‌ها ایجاد می‌کند. در ادامه، توضیحی مفصل برای هر بخش از کد ارائه خواهم داد:
4+
5+
```javascript
6+
import { NextResponse } from 'next/server';
7+
import { connectToDatabase } from "../../../lib/mongodb";
8+
import { Recipe } from "../../../models";
9+
import { existsSync } from "fs";
10+
import fs from "fs/promises";
11+
import path from "path";
12+
13+
connectToDatabase();
14+
15+
export async function GET() {
16+
try {
17+
const recipes = Recipe.find();
18+
return NextResponse.json((await recipes).reverse());
19+
} catch (error) {
20+
console.log(error);
21+
return NextResponse.json('error', {
22+
status: 500
23+
});
24+
}
25+
}
26+
27+
export async function POST(req: Request) {
28+
const data = await req.formData();
29+
const file: File | null = data.get('photo') as unknown as File;
30+
31+
const fileArrayBuffer = await file.arrayBuffer();
32+
const destinationDirPath = path.join(process.cwd(), process.env.STORE_PATH!);
33+
34+
if (!existsSync(destinationDirPath)) {
35+
await fs.mkdir(destinationDirPath, { recursive: true });
36+
}
37+
38+
let name = data.get('name');
39+
var fileExtension = file.name.split('.').pop();
40+
let filename = `${name}.${fileExtension}`;
41+
while (existsSync(path.join(destinationDirPath, filename))) {
42+
filename = `(1)` + filename;
43+
}
44+
45+
await fs.writeFile(
46+
path.join(destinationDirPath, filename),
47+
Buffer.from(fileArrayBuffer)
48+
);
49+
50+
try {
51+
const newRecipe = {
52+
name: data.get('name'),
53+
description: data.get('description'),
54+
ingredients: JSON.parse(data.get('ingredients') as string),
55+
steps: data.get('steps'),
56+
photo: `/api/file/${filename}`
57+
}
58+
59+
const recipe = new Recipe(newRecipe);
60+
const save = await recipe.save();
61+
return NextResponse.json({ status: 200, data: save });
62+
} catch (error) {
63+
console.log(error);
64+
return NextResponse.json('error', {
65+
status: 500,
66+
});
67+
}
68+
69+
}
70+
71+
export async function DELETE(req: any) {
72+
const { id } = req.query;
73+
try {
74+
const recipe = await Recipe.findByIdAndRemove(id);
75+
return NextResponse.json({ status: 200, data: recipe });
76+
} catch (error) {
77+
return NextResponse.json({ status: 500, success: false, message: error });
78+
}
79+
}
80+
```
81+
82+
این فایل سرور برای پردازش درخواست‌های GET، POST و DELETE در مورد رسپی‌ها استفاده می‌شود.
83+
84+
- تابع `GET` برای دریافت لیست رسپی‌ها تعریف شده است. لیست رسپی‌ها در معکوس شده و به صورت JSON برگردانده می‌شود.
85+
- تابع `POST` برای ایجاد رسپی جدید استفاده می‌شود. در این تابع، ابتدا عکس رسپی آپلود می‌شود و سپس رسپی جدید با اطلاعات دریافتی ایجاد و در پایگاه داده ذخیره می‌شود.
86+
- تابع `DELETE` برای حذف رسپی بر اساس شناسه آن استفاده می‌شود. رسپی مورد نظر حذف شده و به عنوان پاسخ JSON برگردانده می‌شود.
87+
88+
با استفاده از این فایل سرور، می‌توانید درخواست‌های GET، POST و DELETE مربوط به رسپی‌ها را در سمت سرور پردازش کنید.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Search
2+
3+
کد فوق یک فایل سمت سرور برای صفحه جستجوی داینامیک رسپی‌ها ایجاد می‌کند. در ادامه، توضیحی مفصل برای هر بخش از کد ارائه خواهم داد:
4+
5+
```javascript
6+
import { NextResponse } from "next/server";
7+
import { connectToDatabase } from "../../../lib/mongodb";
8+
import { Recipe } from "../../../models";
9+
10+
// اتصال به پایگاه داده MongoDB
11+
connectToDatabase();
12+
13+
export const dynamic = 'force-dynamic'
14+
15+
export async function GET(req: Request) {
16+
try {
17+
const { searchParams } = new URL(req.url);
18+
const query = searchParams.get('q');
19+
const recipes = await Recipe.find();
20+
21+
// فیلتر کردن رسپی‌ها بر اساس عبارت جستجو
22+
const recipesData = recipes.filter((recipe) => {
23+
return recipe["name"].includes(query);
24+
});
25+
26+
// بازگرداندن داده‌های رسپی‌ها به عنوان پاسخ JSON
27+
return NextResponse.json(recipesData);
28+
} catch (error) {
29+
console.log(error);
30+
}
31+
}
32+
```
33+
34+
این فایل سرور برای بررسی درخواست جستجوی داینامیک رسپی‌ها استفاده می‌شود.
35+
36+
- ابتدا به پایگاه داده MongoDB متصل می‌شویم با فراخوانی تابع `connectToDatabase()` که در فایل `lib/mongodb` تعریف شده است.
37+
- متغیر `dynamic` برای اجرای داینامیک این صفحه تنظیم شده است.
38+
- تابع `GET` برای دریافت درخواست جستجوی رسپی‌ها تعریف شده است. دریافت پارامتر `q` از آدرس URL و جستجوی رسپی‌ها بر اساس این پارامتر صورت می‌گیرد.
39+
- رسپی‌ها به عنوان داده‌های JSON بازگردانده می‌شوند.
40+
41+
با استفاده از این فایل سرور، می‌توانید درخواست جستجوی داینامیک رسپی‌ها را در سمت سرور پردازش کنید.

Documentation/app/page.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Home
2+
3+
کد فوق یک کامپوننت Home است که صفحه اصلی را تعریف می‌کند. در ادامه، توضیحی مفصل برای هر بخش از کد ارائه خواهم داد:
4+
5+
```javascript
6+
import Link from "next/link";
7+
import HeaderBottom from "../components/HeaderBottom";
8+
import LandingSection from "../components/LandingSection";
9+
import LatestRecipes from "../components/LatestRecipes";
10+
import Button from "../components/ui/Button";
11+
12+
// تعریف کامپوننت Home
13+
export default function Home() {
14+
return (
15+
<>
16+
<div className="min-h-screen flex flex-col">
17+
<main className="flex flex-col">
18+
{/* نمایش کامپوننت HeaderBottom */}
19+
<HeaderBottom />
20+
21+
{/* نمایش کامپوننت LandingSection */}
22+
<LandingSection
23+
title={"رسپی‌ها"}
24+
subTitle={"آماده اید تا کلی دسر خوشمزه با هم درست کنیم؟"}
25+
rootClasses={"bg-gray-50"}
26+
>
27+
{/* نمایش کامپوننت LatestRecipes */}
28+
<LatestRecipes limitNumber={4} />
29+
30+
{/* نمایش دکمه "دیدن همه رسپی‌ها" */}
31+
<div className="flex flex-col mx-auto mt-20 items-center">
32+
<Link href={'/recipes'}>
33+
<Button className="py-3 px-4 inline-flex justify-center items-center gap-2 rounded-md border border-transparent font-semibold bg-gray-500 text-white hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-all text-sm dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-offset-gray-800">
34+
دیدن همه رسپی‌ها
35+
</Button>
36+
</Link>
37+
</div>
38+
</LandingSection>
39+
</main>
40+
</div>
41+
</>
42+
);
43+
}
44+
```
45+
46+
این کامپوننت برای تعریف صفحه اصلی استفاده می‌شود. در اینجا:
47+
48+
- کامپوننت `HeaderBottom` به عنوان بخش بالایی صفحه نمایش داده می‌شود.
49+
- کامپوننت `LandingSection` به عنوان بخش اصلی صفحه نمایش داده می‌شود. در اینجا، عنوان و زیرعنوان بخش تعریف شده است و کامپوننت `LatestRecipes` به همراه تعداد محدودی رسپی نمایش داده می‌شود. همچنین، دکمه "دیدن همه رسپی‌ها" نیز در این بخش قرار دارد که با کلیک بر روی آن به صفحه رسپی‌ها هدایت می‌شویم.
File renamed without changes.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# LatestRecipes
2+
3+
کد فوق یک کامپوننت React به نام "LatestRecipes" را تعریف می‌کند که وظیفه نمایش آخرین رسپی‌ها را دارد. در ادامه، توضیحی مفصل برای هر بخش از کد ارائه خواهم داد:
4+
5+
```javascript
6+
"use client";
7+
8+
import React, { useEffect, useState } from "react";
9+
import { GetAllRecipesResponse } from "../types/Recipe";
10+
import RecipeCard from "./RecipeCard";
11+
import SkeletonCard from "./ui/Skeleton";
12+
13+
interface LatestRecipesProps {
14+
limitNumber: number;
15+
}
16+
17+
const LatestRecipes = ({ limitNumber }: LatestRecipesProps) => {
18+
const [allRecipes, setAllRecipes] = useState<
19+
GetAllRecipesResponse[] | undefined
20+
>(undefined);
21+
const [loading, setLoading] = useState(true);
22+
23+
const get_all_recipes = async () => {
24+
setLoading(true);
25+
try {
26+
const res = await fetch("/api/recipes", {
27+
method: "GET",
28+
});
29+
const data = await res.json();
30+
setAllRecipes(data);
31+
setLoading(false);
32+
} catch (error) {
33+
setLoading(false);
34+
console.log("Error in getting all recipes (service) =>", error);
35+
}
36+
};
37+
38+
useEffect(() => {
39+
get_all_recipes();
40+
}, []);
41+
42+
return (
43+
<>
44+
<div className="grid grid-cols-4 gap-4">
45+
{loading &&
46+
Array.from({ length: limitNumber || 4 }, (item, index) => (
47+
<div key={index} className="flex">
48+
<SkeletonCard />
49+
</div>
50+
))}
51+
{!loading &&
52+
allRecipes
53+
?.slice(0, limitNumber ? limitNumber : 4)
54+
?.map((cake) => <RecipeCard key={cake._id} recipe={cake} />)}
55+
</div>
56+
</>
57+
);
58+
};
59+
60+
export default LatestRecipes;
61+
```
62+
63+
کامپوننت "LatestRecipes" وظیفه نمایش آخرین رسپی‌ها را بر عهده دارد. این کامپوننت از ویژگی‌های `limitNumber` برای محدود کردن تعداد رسپی‌ها استفاده می‌کند.
64+
65+
- در این کامپوننت از `useState` برای تعریف وضعیت‌های `allRecipes` و `loading` استفاده می‌شود.
66+
- تابع `get_all_recipes` برای دریافت همه‌ی رسپی‌ها از API استفاده می‌کند. این تابع با فراخوانی API، داده‌های رسپی‌ها را دریافت کرده و وضعیت‌ها را به‌روز می‌کند.
67+
- با استفاده از `useEffect`، تابع `get_all_recipes` در زمان بارگیری صفحه اجرا می‌شود.
68+
- در بخش بصری، اگر در حالت بارگذاری (`loading`) بود، کارت‌های Skeleton نمایش داده می‌شوند. در غیر این صورت، رسپی‌های دریافت شده از API نمایش داده می‌شوند.
69+
70+
با استفاده از کامپوننت "LatestRecipes" می‌توانید آخرین رسپی‌ها را در صفحه خود نمایش دهید.

0 commit comments

Comments
 (0)