├── .eslintrc.json
├── src
├── app
│ ├── favicon.ico
│ ├── globals.css
│ ├── api
│ │ └── contact
│ │ │ └── route.js
│ ├── blog
│ │ ├── page.tsx
│ │ └── [category]
│ │ │ ├── page.tsx
│ │ │ └── [slug]
│ │ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── styles
│ └── fonts.css
└── components
│ ├── BackButton.tsx
│ ├── ScrollToTopButton.tsx
│ ├── ArticleCard.tsx
│ ├── SkillCard.tsx
│ ├── NavigationMenu.tsx
│ ├── CategoryClient.tsx
│ ├── ArticleClient.tsx
│ ├── Pagination.tsx
│ ├── ArticlesClient.tsx
│ ├── ArticleNavigation.tsx
│ ├── AboutSection.tsx
│ ├── Slider.tsx
│ ├── ContactForm.tsx
│ ├── ArticleLayout.tsx
│ ├── ArticleSlider.tsx
│ ├── Footer.tsx
│ └── TopNav.tsx
├── public
├── images
│ ├── skill
│ │ ├── css.jpeg
│ │ ├── php.jpeg
│ │ ├── seo.jpeg
│ │ ├── ui.jpeg
│ │ ├── github.jpeg
│ │ ├── html.jpeg
│ │ ├── nextjs.jpeg
│ │ ├── react.jpeg
│ │ ├── redux.jpeg
│ │ ├── scrum.jpeg
│ │ ├── restapi.jpeg
│ │ ├── javascript.jpeg
│ │ ├── tailwindcss.jpeg
│ │ ├── typescript.jpeg
│ │ └── campainadvertising.jpeg
│ ├── about
│ │ ├── azadeh.jpeg
│ │ ├── azadeh.webp
│ │ └── logo.webp
│ ├── slider
│ │ ├── header.jpeg
│ │ ├── header1.jpeg
│ │ ├── header3.jpeg
│ │ └── header4.jpeg
│ ├── nextjs
│ │ └── what-is-the-next.jpeg
│ ├── react
│ │ └── what-is-the-react.jpeg
│ ├── redux
│ │ └── what-is-the-redux.jpeg
│ ├── github
│ │ └── what-is-the-github.jpeg
│ ├── tailwindcss
│ │ └── what-is-the-tailwind.jpeg
│ └── typescript
│ │ └── what-is-the-typescript.jpeg
└── fonts
│ ├── Vazir-FD-WOL.eot
│ ├── Vazir-FD-WOL.ttf
│ ├── Vazir-FD-WOL.woff
│ ├── Vazir-FD-WOL.woff2
│ ├── Vazir-Bold-FD-WOL.eot
│ ├── Vazir-Bold-FD-WOL.ttf
│ ├── Vazir-Bold-FD-WOL.woff
│ ├── Vazir-Bold-FD-WOL.woff2
│ ├── Vazir-Light-FD-WOL.eot
│ ├── Vazir-Light-FD-WOL.ttf
│ ├── Vazir-Light-FD-WOL.woff
│ ├── Vazir-Medium-FD-WOL.eot
│ ├── Vazir-Medium-FD-WOL.ttf
│ ├── Vazir-Thin-FD-WOL.eot
│ ├── Vazir-Thin-FD-WOL.ttf
│ ├── Vazir-Thin-FD-WOL.woff
│ ├── Vazir-Thin-FD-WOL.woff2
│ ├── Vazir-Light-FD-WOL.woff2
│ ├── Vazir-Medium-FD-WOL.woff
│ └── Vazir-Medium-FD-WOL.woff2
├── postcss.config.mjs
├── next.config.js
├── data
└── contacts.json
├── tailwind.config.ts
├── .gitignore
├── package.json
├── tsconfig.json
└── README.md
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/src/app/favicon.ico
--------------------------------------------------------------------------------
/public/images/skill/css.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/css.jpeg
--------------------------------------------------------------------------------
/public/images/skill/php.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/php.jpeg
--------------------------------------------------------------------------------
/public/images/skill/seo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/seo.jpeg
--------------------------------------------------------------------------------
/public/images/skill/ui.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/ui.jpeg
--------------------------------------------------------------------------------
/public/fonts/Vazir-FD-WOL.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-FD-WOL.eot
--------------------------------------------------------------------------------
/public/fonts/Vazir-FD-WOL.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-FD-WOL.ttf
--------------------------------------------------------------------------------
/public/fonts/Vazir-FD-WOL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-FD-WOL.woff
--------------------------------------------------------------------------------
/public/fonts/Vazir-FD-WOL.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-FD-WOL.woff2
--------------------------------------------------------------------------------
/public/images/about/azadeh.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/about/azadeh.jpeg
--------------------------------------------------------------------------------
/public/images/about/azadeh.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/about/azadeh.webp
--------------------------------------------------------------------------------
/public/images/about/logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/about/logo.webp
--------------------------------------------------------------------------------
/public/images/skill/github.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/github.jpeg
--------------------------------------------------------------------------------
/public/images/skill/html.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/html.jpeg
--------------------------------------------------------------------------------
/public/images/skill/nextjs.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/nextjs.jpeg
--------------------------------------------------------------------------------
/public/images/skill/react.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/react.jpeg
--------------------------------------------------------------------------------
/public/images/skill/redux.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/redux.jpeg
--------------------------------------------------------------------------------
/public/images/skill/scrum.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/scrum.jpeg
--------------------------------------------------------------------------------
/public/images/skill/restapi.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/restapi.jpeg
--------------------------------------------------------------------------------
/public/images/slider/header.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/slider/header.jpeg
--------------------------------------------------------------------------------
/public/images/slider/header1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/slider/header1.jpeg
--------------------------------------------------------------------------------
/public/images/slider/header3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/slider/header3.jpeg
--------------------------------------------------------------------------------
/public/images/slider/header4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/slider/header4.jpeg
--------------------------------------------------------------------------------
/public/fonts/Vazir-Bold-FD-WOL.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Bold-FD-WOL.eot
--------------------------------------------------------------------------------
/public/fonts/Vazir-Bold-FD-WOL.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Bold-FD-WOL.ttf
--------------------------------------------------------------------------------
/public/fonts/Vazir-Bold-FD-WOL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Bold-FD-WOL.woff
--------------------------------------------------------------------------------
/public/fonts/Vazir-Bold-FD-WOL.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Bold-FD-WOL.woff2
--------------------------------------------------------------------------------
/public/fonts/Vazir-Light-FD-WOL.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Light-FD-WOL.eot
--------------------------------------------------------------------------------
/public/fonts/Vazir-Light-FD-WOL.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Light-FD-WOL.ttf
--------------------------------------------------------------------------------
/public/fonts/Vazir-Light-FD-WOL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Light-FD-WOL.woff
--------------------------------------------------------------------------------
/public/fonts/Vazir-Medium-FD-WOL.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Medium-FD-WOL.eot
--------------------------------------------------------------------------------
/public/fonts/Vazir-Medium-FD-WOL.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Medium-FD-WOL.ttf
--------------------------------------------------------------------------------
/public/fonts/Vazir-Thin-FD-WOL.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Thin-FD-WOL.eot
--------------------------------------------------------------------------------
/public/fonts/Vazir-Thin-FD-WOL.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Thin-FD-WOL.ttf
--------------------------------------------------------------------------------
/public/fonts/Vazir-Thin-FD-WOL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Thin-FD-WOL.woff
--------------------------------------------------------------------------------
/public/fonts/Vazir-Thin-FD-WOL.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Thin-FD-WOL.woff2
--------------------------------------------------------------------------------
/public/images/skill/javascript.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/javascript.jpeg
--------------------------------------------------------------------------------
/public/images/skill/tailwindcss.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/tailwindcss.jpeg
--------------------------------------------------------------------------------
/public/images/skill/typescript.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/typescript.jpeg
--------------------------------------------------------------------------------
/public/fonts/Vazir-Light-FD-WOL.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Light-FD-WOL.woff2
--------------------------------------------------------------------------------
/public/fonts/Vazir-Medium-FD-WOL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Medium-FD-WOL.woff
--------------------------------------------------------------------------------
/public/fonts/Vazir-Medium-FD-WOL.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/fonts/Vazir-Medium-FD-WOL.woff2
--------------------------------------------------------------------------------
/public/images/nextjs/what-is-the-next.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/nextjs/what-is-the-next.jpeg
--------------------------------------------------------------------------------
/public/images/react/what-is-the-react.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/react/what-is-the-react.jpeg
--------------------------------------------------------------------------------
/public/images/redux/what-is-the-redux.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/redux/what-is-the-redux.jpeg
--------------------------------------------------------------------------------
/public/images/skill/campainadvertising.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/skill/campainadvertising.jpeg
--------------------------------------------------------------------------------
/public/images/github/what-is-the-github.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/github/what-is-the-github.jpeg
--------------------------------------------------------------------------------
/public/images/tailwindcss/what-is-the-tailwind.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/tailwindcss/what-is-the-tailwind.jpeg
--------------------------------------------------------------------------------
/public/images/typescript/what-is-the-typescript.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frau-azadeh/sunflower-dev/HEAD/public/images/typescript/what-is-the-typescript.jpeg
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | // next.config.js
2 | /** @type {import('next').NextConfig} */
3 | const nextConfig = {
4 | reactStrictMode: true,
5 | distDir: 'build',
6 | };
7 |
8 | module.exports = nextConfig;
9 |
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | @apply font-vazir;
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/data/contacts.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "name": "آزاده شریفی سلطانی",
5 | "phone": "09122764435",
6 | "message": "سایت بسیار زیبایی دارید موفق باشید "
7 | },
8 | {
9 | "id": 2,
10 | "name": "آزاده شریفی سلطانی",
11 | "phone": "09122764435",
12 | "message": "موفق باشی و به درجات بالا برسی"
13 | },
14 | {
15 | "id": 3,
16 | "name": "آرزو شریفی سلطانی",
17 | "phone": "09223539879",
18 | "message": "موفق باشی خواهر عزیز تر از جانم"
19 | }
20 | ]
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | fontFamily:{
16 | vazir:['Vazir'],
17 | }
18 | },
19 | },
20 | plugins: [],
21 | };
22 | export default config;
23 |
--------------------------------------------------------------------------------
/src/styles/fonts.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Vazir';
3 | src: url('../../public/fonts/Vazir-Bold-FD-WOL.woff2') format('woff2');
4 | font-weight: 400;
5 | font-style: normal;
6 | }
7 |
8 | @font-face {
9 | font-family: 'Vazir';
10 | src: url('../../public/fonts/Vazir-Bold-FD-WOL.woff2') format('woff2');
11 | font-weight: 700;
12 | font-style: normal;
13 | }
14 |
15 | @font-face {
16 | font-family: 'Vazir';
17 | src: url('../../public/fonts/Vazir-Bold-FD-WOL.woff2') format('woff2');
18 | font-weight: 300;
19 | font-style: normal;
20 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 |
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for commiting if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sunflower",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@types/react-slick": "^0.23.13",
13 | "axios": "^1.7.7",
14 | "gray-matter": "^4.0.3",
15 | "next": "^14.2.17",
16 | "react": "^18.3.1",
17 | "react-dom": "^18.3.1",
18 | "react-icons": "^5.3.0",
19 | "react-slick": "^0.30.2",
20 | "slick-carousel": "^1.8.1",
21 | "swiper": "^11.1.14"
22 | },
23 | "devDependencies": {
24 | "@types/node": "^20",
25 | "@types/react": "^18",
26 | "@types/react-dom": "^18",
27 | "eslint": "^8",
28 | "eslint-config-next": "15.0.2",
29 | "postcss": "^8",
30 | "tailwindcss": "^3.4.1",
31 | "typescript": "^5"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "noEmit": true,
13 | "esModuleInterop": true,
14 | "module": "esnext",
15 | "moduleResolution": "bundler",
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "jsx": "preserve",
19 | "incremental": true,
20 | "plugins": [
21 | {
22 | "name": "next"
23 | }
24 | ],
25 | "paths": {
26 | "@/*": [
27 | "./src/*"
28 | ]
29 | }
30 | },
31 | "include": [
32 | "next-env.d.ts",
33 | "**/*.ts",
34 | "**/*.tsx",
35 | ".next/types/**/*.ts",
36 | "next.config.js",
37 | "build/types/**/*.ts"
38 | ],
39 | "exclude": [
40 | "node_modules"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/BackButton.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { useEffect, useState } from "react";
3 |
4 | const BackButton: React.FC = () => {
5 | const [isVisible, setIsVisible] = useState(false);
6 |
7 | const goBack = () => {
8 | window.history.back();
9 | };
10 |
11 | const handleScroll = () => {
12 | if (window.scrollY > 100) {
13 | setIsVisible(true);
14 | } else {
15 | setIsVisible(false);
16 | }
17 | };
18 |
19 | useEffect(() => {
20 | window.addEventListener("scroll", handleScroll);
21 | return () => window.removeEventListener("scroll", handleScroll);
22 | }, []);
23 |
24 | return (
25 |
31 | صفحه قبل
32 |
33 | );
34 | };
35 |
36 | export default BackButton;
--------------------------------------------------------------------------------
/src/app/api/contact/route.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 |
4 | const contactsFilePath = path.join(process.cwd(), 'data', 'contacts.json');
5 |
6 | export async function POST(req) {
7 | const { name, phone, message } = await req.json();
8 |
9 | if (!name || !phone || !message) {
10 | return new Response(JSON.stringify({ error: 'همه فیلدها الزامی هستند' }), { status: 400 });
11 | }
12 |
13 | // خواندن دادهها از فایل JSON
14 | const fileData = fs.readFileSync(contactsFilePath, 'utf-8');
15 | const contacts = JSON.parse(fileData);
16 |
17 | // اضافه کردن تماس جدید
18 | const newContact = { id: contacts.length + 1, name, phone, message };
19 | contacts.push(newContact);
20 |
21 | // نوشتن دادهها در فایل JSON
22 | fs.writeFileSync(contactsFilePath, JSON.stringify(contacts, null, 2));
23 |
24 | return new Response(JSON.stringify({ message: 'پیام با موفقیت ذخیره شد' }), {
25 | status: 201,
26 | headers: { 'Content-Type': 'application/json' },
27 | });
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/ScrollToTopButton.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { useEffect, useState } from "react";
3 | import { FaAngleDoubleUp } from "react-icons/fa";
4 |
5 | const ScrollToTopButton: React.FC = () => {
6 | const [isVisible, setIsVisible] = useState(false);
7 |
8 | useEffect(() => {
9 | const toggleVisibility = () => {
10 | if (window.scrollY > 300) {
11 | setIsVisible(true);
12 | } else {
13 | setIsVisible(false);
14 | }
15 | };
16 | window.addEventListener("scroll", toggleVisibility);
17 | return () => window.removeEventListener("scroll", toggleVisibility);
18 | }, []);
19 |
20 | const scrollToTop = () => {
21 | window.scrollTo({
22 | top: 0,
23 | behavior: "smooth",
24 | });
25 | };
26 |
27 | return (
28 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default ScrollToTopButton;
--------------------------------------------------------------------------------
/src/components/ArticleCard.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 | import Image from 'next/image'
3 | interface ArticleCardProps {
4 | title: string;
5 | description: string;
6 | date: string;
7 | image: string;
8 | href: string;
9 | }
10 |
11 | export default function ArticleCard({
12 | title,
13 | description,
14 | date,
15 | image,
16 | href,
17 | }: ArticleCardProps) {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
{title}
25 |
{description}
26 |
27 |
28 | {date}
29 |
30 |
31 | ادامه مطلب
32 |
33 |
34 |
35 | );
36 | }
--------------------------------------------------------------------------------
/src/components/SkillCard.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Image from 'next/image';
3 |
4 | interface SkillCardProps {
5 | title: string;
6 | description: string;
7 | imgSrc: string;
8 | proficiency: string;
9 | }
10 |
11 | const SkillCard: React.FC = ({ title, description, imgSrc, proficiency }) => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
{title}
19 |
{description}
20 |
میزان تسلط من:
21 |
22 |
23 | {proficiency}
24 |
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default SkillCard;
--------------------------------------------------------------------------------
/src/components/NavigationMenu.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FaLinkedin, FaGithubSquare, FaPhoneSquare } from "react-icons/fa";
3 | import { AiFillInstagram } from "react-icons/ai";
4 |
5 | const NavigationMenu: React.FC = () => {
6 | return (
7 |
36 | );
37 | };
38 |
39 | export default NavigationMenu;
40 |
41 |
--------------------------------------------------------------------------------
/src/components/CategoryClient.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useEffect, useState } from "react";
3 | import ArticleCard from "@/components/ArticleCard";
4 |
5 | interface ArticleData {
6 | title: string;
7 | description: string;
8 | date: string;
9 | image: string;
10 | slug: string;
11 | category: string;
12 | }
13 |
14 | interface CategoryClientProps {
15 | articles: ArticleData[];
16 | category: string;
17 | }
18 |
19 | export default function CategoryClient({
20 | articles,
21 | category,
22 | }: CategoryClientProps) {
23 | const [filteredArticles, setFilteredArticles] = useState([]);
24 |
25 | useEffect(() => {
26 | if (category && articles.length > 0) {
27 | const filtered = articles.filter(
28 | (article) => article.category.toLowerCase() === category.toLowerCase()
29 | );
30 | setFilteredArticles(filtered);
31 | }
32 | }, [category, articles]);
33 |
34 | return (
35 |
36 | {filteredArticles.length > 0 ? (
37 | filteredArticles.map((article) => (
38 |
46 | ))
47 | ) : (
48 |
...
49 | )}
50 |
51 | );
52 | }
--------------------------------------------------------------------------------
/src/components/ArticleClient.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useEffect, useState } from "react";
3 | import ArticleLayout from "@/components/ArticleLayout";
4 | import ArticleNavigation from "@/components/ArticleNavigation";
5 |
6 | interface ArticleData {
7 | title: string;
8 | description: string;
9 | date: string;
10 | author: string;
11 | reading_time: string;
12 | image: string;
13 | content: { section_title: string; paragraphs: string[] }[];
14 | category: string;
15 | slug: string;
16 | }
17 |
18 | interface ArticleClientProps {
19 | article: ArticleData | null;
20 | articles: ArticleData[];
21 | }
22 |
23 | export default function ArticleClient({
24 | article,
25 | articles,
26 | }: ArticleClientProps) {
27 | const [loading, setLoading] = useState(true);
28 |
29 | useEffect(() => {
30 | if (article) {
31 | setLoading(false);
32 | }
33 | }, [article]);
34 |
35 | if (loading) {
36 | return در حال بارگذاری...
;
37 | }
38 |
39 | if (!article) {
40 | return مقاله مورد نظر یافت نشد.
;
41 | }
42 |
43 | return (
44 | <>
45 |
55 |
56 | >
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/Pagination.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | interface PaginationProps {
4 | currentPage: number;
5 | totalItems: number;
6 | itemsPerPage: number;
7 | onPageChange: (page: number) => void;
8 | }
9 |
10 | export default function Pagination({
11 | currentPage,
12 | totalItems,
13 | itemsPerPage,
14 | onPageChange,
15 | }: PaginationProps) {
16 | const totalPages = Math.ceil(totalItems / itemsPerPage);
17 |
18 | const handlePageClick = (page: number) => {
19 | if (page > 0 && page <= totalPages) {
20 | onPageChange(page);
21 | }
22 | };
23 |
24 | return (
25 |
26 | handlePageClick(currentPage - 1)}
28 | disabled={currentPage === 1}
29 | className="px-3 py-1 text-gray-400"
30 | >
31 | قبلی
32 |
33 | {[...Array(totalPages)].map((_, index) => (
34 | handlePageClick(index + 1)}
37 | className={`px-3 py-1 rounded ${
38 | currentPage === index + 1 ? 'bg-blue-500 text-white' : 'bg-gray-200 hover:bg-gray-300'
39 | }`}
40 | >
41 | {index + 1}
42 |
43 | ))}
44 | handlePageClick(currentPage + 1)}
46 | disabled={currentPage === totalPages}
47 | className="px-3 py-1 text-gray-400"
48 | >
49 | بعدی
50 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/ArticlesClient.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useState } from "react";
3 | import ArticleCard from "@/components/ArticleCard";
4 | import Pagination from "@/components/Pagination";
5 |
6 | interface ArticleData {
7 | title: string;
8 | description: string;
9 | date: string;
10 | image: string;
11 | slug: string;
12 | category: string;
13 | href: string;
14 | }
15 | interface ArticlesClientProps {
16 | articles: ArticleData[];
17 | }
18 |
19 | export default function ArticlesClient({ articles }: ArticlesClientProps) {
20 | const [currentPage, setCurrentPage] = useState(1);
21 | const itemsPerPage = 3;
22 |
23 | const handlePageChange = (page: number) => {
24 | setCurrentPage(page);
25 | };
26 |
27 | const startIndex = (currentPage - 1) * itemsPerPage;
28 | const currentArticles = articles.slice(startIndex, startIndex + itemsPerPage);
29 |
30 | return (
31 |
32 |
33 | مقالات تخصصی برنامه نویسی :
34 |
35 |
36 | {currentArticles.length > 0 ? (
37 | currentArticles.map((article) => (
38 |
46 | ))
47 | ) : (
48 |
به روز رسانی ...
49 | )}
50 |
51 | {articles.length > itemsPerPage && (
52 |
58 | )}
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/ArticleNavigation.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 |
3 | interface Article {
4 | slug: string;
5 | category: string;
6 | title: string;
7 | date: string;
8 | }
9 |
10 | interface ArticleNavigationProps {
11 | articles: Article[];
12 | currentArticle: Article;
13 | }
14 |
15 | const ArticleNavigation: React.FC = ({ articles, currentArticle }) => {
16 | const sortedArticles = [...articles].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
17 | const currentIndex = sortedArticles.findIndex(article => article.slug === currentArticle.slug && article.category === currentArticle.category);
18 |
19 | const previousArticle = sortedArticles[currentIndex + 1];
20 | const nextArticle = sortedArticles[currentIndex - 1];
21 |
22 | return (
23 |
24 |
مقالات پیشنهادی:
25 |
26 | {previousArticle ? (
27 |
28 | ← {previousArticle.title}
29 |
30 | ) : (
31 |
32 | )}
33 | {nextArticle ? (
34 |
35 | {nextArticle.title} →
36 |
37 | ) : (
38 |
39 | )}
40 |
41 |
42 | );
43 | };
44 |
45 | export default ArticleNavigation;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🌻 Sunflower Website
2 |
3 | A beautifully crafted website using the latest web technologies. Built with passion, creativity, and modern tools, **Sunflower** is designed to deliver a seamless user experience.
4 |
5 | ## 🚀 Features
6 |
7 | - **⚡ Next.js 14**: Leveraging the power of React with server-side rendering and API routes.
8 | - **🎨 Tailwind CSS**: For fast, responsive, and modern styling.
9 | - **🛠️ TypeScript**: Type-safe development for maintainable code.
10 | - **📡 Mockfly API**: Mock data service powering the dynamic content of the site.
11 | - **🌐 Vercel Deployment**: Deployed on Vercel for fast and reliable hosting.
12 |
13 | ## 🔗 Live Demo
14 |
15 | 👉 [Sunflower Website on Vercel](https://sunflowerdev.vercel.app)
16 |
17 | ---
18 |
19 | ## 🛠️ Tech Stack
20 |
21 | | Technology | Description |
22 | |---------------|--------------------------------------|
23 | | **Next.js** | Framework for React |
24 | | **Tailwind** | Utility-first CSS Framework |
25 | | **TypeScript**| Static Typing for JavaScript |
26 | | **Mockfly** | API for mock data |
27 | | **Vercel** | Hosting and deployment platform |
28 |
29 | ---
30 |
31 | ## 📦 Installation
32 |
33 | ### Prerequisites
34 | - Node.js (v18 or higher)
35 | - npm or yarn
36 |
37 | ### Steps
38 | 1. Clone the repository:
39 | ```bash
40 | git clone https://github.com/frau-azadeh/sunflower-dev.git
41 | cd sunflower-dev
42 |
43 |
44 | ### Install dependencies
45 | npm install
46 | or
47 | yarn install
48 |
49 | ### Run the development server
50 | npm run dev
51 |
52 | ### Open the app your browser
53 | http://localhost:3000
54 |
55 | ## 🤝 Contributing
56 | Contributions are welcome! To contribute:
57 |
58 | Fork the repository.
59 | Create a new branch: git checkout -b feature/my-feature.
60 | Commit your changes: git commit -m "Add my feature".
61 | Push to the branch: git push origin feature/my-feature.
62 | Submit a pull request.
63 |
64 | Author : Azadeh Sharifi Soltani
65 |
--------------------------------------------------------------------------------
/src/components/AboutSection.tsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image';
2 | import { FaLaptopCode } from "react-icons/fa";
3 |
4 | const AboutSection = () => {
5 | return (
6 | <>
7 | درباره من
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
22 |
آزاده شریفی سلطانی
23 |
24 | من آزاده شریفی سلطانی هستم و یک علاقه مند به برنامه نویسی و زبان آلمانی . اگه بخوام خودم رو کمی معرفی کنم تحصیلاتم رو در مقطع کاردانی تو رشته نرم افزار کامپیوتر در دانشگاه جامع علمی کاربردی به اتمام رسوندم و کارشناسی رو در شهر سمنان در دانشگاه کومش ادامه دادم. بعد از اون رشته آموزش زبان آلمانی رو برای کارشناسی ارشد انتخاب کردم.
25 |
26 |
27 | من در ابتدا در شرکت نگین سبز خاورمیانه، شرکت پارس و فولاد حامیران مشغول به کار بودم، و در حال حاضر دارم تجربیات خودم رو تو زمینه برنامه نویسی افزایش میدم و همچنین برای توسعه فردی خودم در رشته MBA دانشگاه تهران مشغول به ادامه تحصیل می باشم.
28 |
29 |
30 |
31 | >
32 |
33 | );
34 | };
35 |
36 | export default AboutSection;
--------------------------------------------------------------------------------
/src/components/Slider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { useState, useEffect } from 'react';
3 | import Image from 'next/image';
4 | import { FaAngleDoubleLeft, FaAngleDoubleRight } from 'react-icons/fa';
5 | interface Slide {
6 | image: string;
7 | title: string;
8 | description: string;
9 | }
10 |
11 | const slides: Slide[] = [
12 | {
13 | image: '/images/slider/header.jpeg',
14 | title: 'اسلاید ۱',
15 | description: 'توضیحات اسلاید ۱',
16 | },
17 | {
18 | image: '/images/slider/header1.jpeg',
19 | title: 'اسلاید ۲',
20 | description: 'توضیحات اسلاید ۲',
21 | },
22 | {
23 | image: '/images/slider/header3.jpeg',
24 | title: 'اسلاید ۳',
25 | description: 'توضیحات اسلاید ۳',
26 | },
27 | {
28 | image: '/images/slider/header4.jpeg',
29 | title: 'اسلاید 4',
30 | description: 'توضیحات اسلاید 4',
31 | },
32 | ];
33 |
34 | const Slider = () => {
35 | const [currentIndex, setCurrentIndex] = useState(0);
36 |
37 | useEffect(() => {
38 | const interval = setInterval(() => {
39 | setCurrentIndex((prevIndex) =>
40 | prevIndex === slides.length - 1 ? 0 : prevIndex + 1
41 | );
42 | }, 5000);
43 |
44 | return () => clearInterval(interval);
45 | }, []);
46 |
47 | const handlePrev = () => {
48 | const newIndex = currentIndex === 0 ? slides.length - 1 : currentIndex - 1;
49 | setCurrentIndex(newIndex);
50 | };
51 |
52 | const handleNext = () => {
53 | const newIndex = currentIndex === slides.length - 1 ? 0 : currentIndex + 1;
54 | setCurrentIndex(newIndex);
55 | };
56 |
57 | return (
58 |
59 |
60 |
67 |
68 |
69 |
73 |
74 |
75 |
79 |
80 |
81 |
82 | );
83 | };
84 |
85 | export default Slider;
--------------------------------------------------------------------------------
/src/app/blog/page.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next";
2 | import ArticlesClient from "@/components/ArticlesClient";
3 | import TopNav from "@/components/TopNav";
4 | import Footer from "@/components/Footer";
5 | import BackButton from "@/components/BackButton";
6 | import ScrollToTopButton from "@/components/ScrollToTopButton";
7 | import NavigationMenu from "@/components/NavigationMenu";
8 | interface ArticleData {
9 | title: string;
10 | description: string;
11 | date: string;
12 | image: string;
13 | slug: string;
14 | category: string;
15 | href: string;
16 | }
17 |
18 | // data for seo
19 | export const metadata: Metadata = {
20 | title: "بلاگ - مقالات تخصصی برنامه نویسی",
21 | description: "در این بخش از وبسایت آزاده شریفی سلطانی، مقالات تخصصی و آموزنده در زمینه برنامه نویسی و توسعه وب منتشر میشود.",
22 | keywords: ["بلاگ", "برنامه نویسی", "مقالات تخصصی", "توسعه وب", "آموزش برنامه نویسی"],
23 | openGraph: {
24 | title: "بلاگ - مقالات تخصصی برنامه نویسی",
25 | description: "در این بخش از وبسایت آزاده شریفی سلطانی، مقالات تخصصی و آموزنده در زمینه برنامه نویسی و توسعه وب منتشر میشود.",
26 | url: "https://sunflower-dev.com/blog",
27 | siteName: "وب سایت آزاده شریفی سلطانی",
28 | images: [
29 | {
30 | url: "/images/react/what-is-the-react.jpeg",
31 | width: 1200,
32 | height: 630,
33 | alt: "تصویری از مقالات بلاگ",
34 | },
35 | ],
36 | locale: "fa_IR",
37 | type: "website",
38 | },
39 | twitter: {
40 | card: "summary_large_image",
41 | title: "بلاگ - مقالات تخصصی برنامه نویسی",
42 | description: "در این بخش از وبسایت آزاده شریفی سلطانی، مقالات تخصصی و آموزنده در زمینه برنامه نویسی و توسعه وب منتشر میشود.",
43 | creator: "@Azadeh_sharifi",
44 | images: ["/images/react/what-is-the-react.jpeg"],
45 | },
46 | };
47 |
48 | async function fetchArticles(): Promise {
49 | const response = await fetch(
50 | "https://api.mockfly.dev/mocks/ef8e4ba5-5dc1-4b36-9bca-5f59afb45ebe/article",
51 | { cache: "no-store" }
52 | );
53 | const data = await response.json();
54 | return Array.isArray(data) ? data : data.articles || [];
55 | }
56 |
57 | export default async function ArticlesPage() {
58 | const articles = await fetchArticles();
59 |
60 | return (
61 | <>
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | >
71 | );
72 | }
--------------------------------------------------------------------------------
/src/components/ContactForm.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useState } from "react";
3 | import axios from "axios";
4 |
5 | const ContactForm: React.FC = () => {
6 | const [formData, setFormData] = useState({
7 | name: "",
8 | phone: "",
9 | message: "",
10 | });
11 | const [error, setError] = useState("");
12 | const [success, setSuccess] = useState("");
13 |
14 | const handleChange = (e: React.ChangeEvent) => {
15 | setFormData({ ...formData, [e.target.name]: e.target.value });
16 | };
17 |
18 | const handleSubmit = async (e: React.FormEvent) => {
19 | e.preventDefault();
20 | setSuccess(""); // delete previous massage
21 | setError(""); // delete previous error
22 |
23 | const phoneRegex = /^09\d{9}$/;
24 |
25 | if (!formData.name || !formData.phone || !formData.message) {
26 | setError("لطفاً تمامی فیلدها را پر کنید.");
27 | return;
28 | }
29 |
30 | if (!phoneRegex.test(formData.phone)) {
31 | setError("شماره تماس باید با 09 شروع شود و 11 رقم باشد.");
32 | return;
33 | }
34 |
35 | try {
36 | await axios.post("/api/contact", formData);
37 | setSuccess("پیام شما با موفقیت ارسال شد."); // ok
38 | setFormData({ name: "", phone: "", message: "" });
39 | } catch (error) {
40 | console.error("Error submitting form:", error);
41 | setError("خطایی در ارسال پیام رخ داده است.");
42 | }
43 | };
44 |
45 | return (
46 |
84 | );
85 | };
86 |
87 | export default ContactForm;
88 |
--------------------------------------------------------------------------------
/src/app/blog/[category]/page.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next";
2 | import CategoryClient from "@/components/CategoryClient";
3 | import TopNav from "@/components/TopNav";
4 | import Footer from "@/components/Footer";
5 | import BackButton from "@/components/BackButton";
6 | import ScrollToTopButton from "@/components/ScrollToTopButton";
7 | import NavigationMenu from "@/components/NavigationMenu";
8 | interface ArticleData {
9 | title: string;
10 | description: string;
11 | date: string;
12 | image: string;
13 | slug: string;
14 | category: string;
15 | }
16 |
17 | async function fetchArticles(): Promise {
18 | const response = await fetch(
19 | "https://api.mockfly.dev/mocks/ef8e4ba5-5dc1-4b36-9bca-5f59afb45ebe/article",
20 | { cache: "no-store" }
21 | );
22 | const data = await response.json();
23 | return Array.isArray(data.articles) ? data.articles : [];
24 | }
25 |
26 | // SEO
27 | export async function generateMetadata({
28 | params,
29 | }: {
30 | params: { category: string };
31 | }): Promise {
32 | const category = params.category;
33 | return {
34 | title: `دستهبندی ${category} - وبلاگ`,
35 | description: `مقالات دستهبندی ${category} در وبلاگ ما. با جدیدترین مقالات تخصصی در حوزه برنامهنویسی و توسعه وب آشنا شوید.`,
36 | openGraph: {
37 | title: `دستهبندی ${category} - وبلاگ`,
38 | description: `مقالات دستهبندی ${category} در وبلاگ ما. با جدیدترین مقالات تخصصی در حوزه برنامهنویسی و توسعه وب آشنا شوید.`,
39 | url: `https://sunflower-dev.com/blog/${category}`,
40 | siteName: "وبلاگ برنامه نویسی",
41 | images: [
42 | {
43 | url: "/images/react/what-is-the-react.jpeg",
44 | width: 1200,
45 | height: 630,
46 | alt: `تصویری از دستهبندی ${category}`,
47 | },
48 | ],
49 | locale: "fa_IR",
50 | type: "website",
51 | },
52 | twitter: {
53 | card: "summary_large_image",
54 | title: `دستهبندی ${category} - وبلاگ`,
55 | description: `مقالات دستهبندی ${category} در وبلاگ ما. با جدیدترین مقالات تخصصی در حوزه برنامهنویسی و توسعه وب آشنا شوید.`,
56 | images: ["/images/react/what-is-the-react.jpeg"],
57 | },
58 | };
59 | }
60 |
61 | export default async function CategoryPage({
62 | params,
63 | }: {
64 | params: { category: string };
65 | }) {
66 | const articles = await fetchArticles();
67 | const category = params.category;
68 |
69 | return (
70 | <>
71 |
72 |
73 |
74 | مقالات دستهبندی: {category}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | >
83 | );
84 | }
--------------------------------------------------------------------------------
/src/components/ArticleLayout.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import Image from 'next/image';
3 |
4 | interface ArticleLayoutProps {
5 | title: string;
6 | description: string;
7 | date: string;
8 | author: string;
9 | readingTime: string;
10 | image: string;
11 | category: string;
12 | content: { section_title: string; paragraphs: string[] }[];
13 | }
14 |
15 | const ArticleLayout: FC = ({
16 | title,
17 | date,
18 | author,
19 | readingTime,
20 | image,
21 | category,
22 | content,
23 | }) => {
24 | return (
25 |
26 |
27 | {/* Poster Section */}
28 |
35 |
36 | {/* Info Section */}
37 |
38 |
39 |
منتشر شده در تاریخ:
40 |
{date}
41 |
42 |
43 |
نویسنده:
44 |
{author}
45 |
46 |
47 |
دسته بندی:
48 |
{category}
49 |
50 |
51 |
مدت زمان خواندن:
52 |
{readingTime}
53 |
54 |
55 |
56 | {/* Content Section */}
57 |
58 | {content.map((section, index) => (
59 |
60 | {section.section_title}
61 | {section.paragraphs.map((paragraph, pIndex) => (
62 |
63 | {paragraph}
64 |
65 | ))}
66 |
67 | ))}
68 |
69 |
70 |
71 | );
72 | };
73 |
74 | export default ArticleLayout;
75 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import "./globals.css";
3 | import "../styles/fonts.css";
4 |
5 | export const metadata: Metadata = {
6 | title: "وب سایت آزاده شریفی سلطانی",
7 | description: "من آزاده شریفی سلطانی هستم. سایت من در زمینه برنامه نویسی هست و خوشحال میشم با هم اطلاعاتمون رو اینجا به اشتراک بزاریم",
8 | authors: [{ name: "آزاده شریفی سلطانی", url: "https://sunflower-dev.com" }],
9 | keywords: [
10 | "آزاده شریفی سلطانی",
11 | "برنامه نویسی",
12 | "توسعه وب",
13 | "آموزش برنامه نویسی",
14 | "فرانت اند",
15 | ],
16 | openGraph: {
17 | title: "وب سایت آزاده شریفی سلطانی",
18 | description:
19 | "سایت من در زمینه برنامه نویسی هست و خوشحال میشم با هم اطلاعاتمون رو اینجا به اشتراک بزاریم.",
20 | url: "https://sunflower-dev.com",
21 | siteName: "وب سایت آزاده شریفی سلطانی",
22 | images: [
23 | {
24 | url: "/images/about/azadeh.webp",
25 | width: 1200,
26 | height: 630,
27 | alt: "آزاده شریفی سلطانی",
28 | },
29 | ],
30 | locale: "fa_IR",
31 | type: "website",
32 | },
33 | twitter: {
34 | card: "summary_large_image",
35 | title: "وب سایت آزاده شریفی سلطانی",
36 | description:
37 | "سایت من در زمینه برنامه نویسی هست و خوشحال میشم با هم اطلاعاتمون رو اینجا به اشتراک بزاریم.",
38 | creator: "@Azadeh_sharifi",
39 | images: ["/images/about/azadeh.webp"],
40 | },
41 | };
42 |
43 | export default function RootLayout({
44 | children,
45 | }: Readonly<{
46 | children: React.ReactNode;
47 | }>) {
48 | return (
49 |
50 |
51 | {/* JSON-LD */}
52 |
75 |
91 |
92 |
93 | {children}
94 |
95 |
96 | );
97 | }
--------------------------------------------------------------------------------
/src/app/blog/[category]/[slug]/page.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next";
2 | import ArticleClient from "@/components/ArticleClient";
3 | import TopNav from "@/components/TopNav";
4 | import Footer from "@/components/Footer";
5 | import BackButton from "@/components/BackButton";
6 | import ScrollToTopButton from "@/components/ScrollToTopButton";
7 | import NavigationMenu from "@/components/NavigationMenu";
8 |
9 | interface ArticleData {
10 | title: string;
11 | description: string;
12 | date: string;
13 | author: string;
14 | reading_time: string;
15 | image: string;
16 | content: { section_title: string; paragraphs: string[] }[];
17 | category: string;
18 | slug: string;
19 | }
20 |
21 | async function fetchArticles(): Promise {
22 | const response = await fetch(
23 | "https://api.mockfly.dev/mocks/ef8e4ba5-5dc1-4b36-9bca-5f59afb45ebe/article",
24 | { cache: "no-store" }
25 | );
26 | const data = await response.json();
27 | return Array.isArray(data.articles) ? data.articles : [];
28 | }
29 |
30 | // پیشدریافت پارامترها برای صفحات استاتیک
31 | export async function generateStaticParams() {
32 | const articles = await fetchArticles();
33 | return articles.map((article) => ({
34 | category: article.category,
35 | slug: article.slug,
36 | }));
37 | }
38 |
39 |
40 | export async function generateMetadata({
41 | params,
42 | }: {
43 | params: { category: string; slug: string };
44 | }): Promise {
45 | const articles = await fetchArticles();
46 | const article = articles.find(
47 | (a) =>
48 | a.category.toLowerCase() === params.category.toLowerCase() &&
49 | a.slug === params.slug
50 | );
51 |
52 | return {
53 | title: article ? `${article.title} - وبلاگ` : "مقاله - وبلاگ",
54 | description: article
55 | ? article.description
56 | : "مقالهای درباره برنامهنویسی و توسعه وب.",
57 | openGraph: {
58 | title: article ? `${article.title} - وبلاگ` : "مقاله - وبلاگ",
59 | description: article
60 | ? article.description
61 | : "مقالهای درباره برنامهنویسی و توسعه وب.",
62 | url: `https://sunflower-dev.com/blog/${params.category}/${params.slug}`,
63 | siteName: "وبلاگ برنامه نویسی",
64 | images: [
65 | {
66 | url: article ? article.image : "/default-image.jpg",
67 | width: 1200,
68 | height: 630,
69 | alt: article ? article.title : "مقاله",
70 | },
71 | ],
72 | locale: "fa_IR",
73 | type: "article",
74 | },
75 | twitter: {
76 | card: "summary_large_image",
77 | title: article ? `${article.title} - وبلاگ` : "مقاله - وبلاگ",
78 | description: article
79 | ? article.description
80 | : "مقالهای درباره برنامهنویسی و توسعه وب.",
81 | images: [article ? article.image : "/default-image.jpg"],
82 | },
83 | };
84 | }
85 |
86 |
87 | export default async function ArticlePage({
88 | params,
89 | }: {
90 | params: { category: string; slug: string };
91 | }) {
92 | const articles = await fetchArticles();
93 | const article = articles.find(
94 | (a) =>
95 | a.category.toLowerCase() === params.category.toLowerCase() &&
96 | a.slug === params.slug
97 | );
98 |
99 | return (
100 | <>
101 |
102 |
105 |
106 |
107 |
108 |
109 | >
110 | );
111 | }
112 |
--------------------------------------------------------------------------------
/src/components/ArticleSlider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useEffect, useState } from 'react';
3 | import Slider, { Settings, CustomArrowProps } from 'react-slick';
4 | import "slick-carousel/slick/slick.css";
5 | import "slick-carousel/slick/slick-theme.css";
6 | import axios from 'axios';
7 | import Image from 'next/image';
8 | import { FaAngleDoubleLeft, FaAngleDoubleRight } from "react-icons/fa";
9 |
10 | interface Article {
11 | slug: string;
12 | image: string;
13 | title: string;
14 | description: string;
15 | date: string;
16 | link: string;
17 | category:string;
18 | }
19 |
20 | const PrevArrow: React.FC = ({ onClick }) => (
21 |
25 |
26 |
27 | );
28 |
29 | const NextArrow: React.FC = ({ onClick }) => (
30 |
34 |
35 |
36 | );
37 |
38 | const ArticleSlider: React.FC = () => {
39 | const [articles, setArticles] = useState([]);
40 |
41 | useEffect(() => {
42 | const fetchArticles = async () => {
43 | try {
44 | const response = await axios.get("https://api.mockfly.dev/mocks/ef8e4ba5-5dc1-4b36-9bca-5f59afb45ebe/article");
45 |
46 | const articlesData = Array.isArray(response.data) ? response.data : response.data.articles;
47 |
48 | if (Array.isArray(articlesData)) {
49 | setArticles(articlesData.slice(-6));
50 | } else {
51 | console.error("دادههای دریافتی از API به صورت آرایه نیست.");
52 | }
53 | } catch (error) {
54 | console.error("خطا در دریافت مقالات:", error);
55 | }
56 | };
57 |
58 | fetchArticles();
59 | }, []);
60 |
61 | const settings: Settings = {
62 | dots: false,
63 | infinite: true,
64 | speed: 500,
65 | slidesToShow: 4,
66 | slidesToScroll: 1,
67 | autoplay: true,
68 | autoplaySpeed: 3000,
69 | arrows: true,
70 | prevArrow: ,
71 | nextArrow: ,
72 | responsive: [
73 | {
74 | breakpoint: 1024,
75 | settings: {
76 | slidesToShow: 2,
77 | }
78 | },
79 | {
80 | breakpoint: 768,
81 | settings: {
82 | slidesToShow: 1,
83 | }
84 | },
85 | {
86 | breakpoint: 480,
87 | settings: {
88 | slidesToShow: 1,
89 | }
90 | }
91 | ]
92 | };
93 |
94 | return (
95 |
96 |
97 | {articles.map((article) => (
98 |
99 |
100 |
101 |
108 |
109 |
110 |
{article.title}
111 |
{article.description}
112 |
113 | {article.date}
114 |
115 |
119 | ادامه مطلب
120 |
121 |
122 |
123 |
124 | ))}
125 |
126 |
127 | );
128 | };
129 |
130 | export default ArticleSlider;
--------------------------------------------------------------------------------
/src/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useEffect, useState } from "react";
3 | import { FaLinkedin, FaGithubSquare, FaPhoneSquare } from "react-icons/fa";
4 | import { AiFillInstagram } from "react-icons/ai";
5 | import Link from "next/link";
6 | import ContactForm from "./ContactForm";
7 |
8 | interface ArticleData {
9 | title: string;
10 | slug: string;
11 | category: string;
12 | }
13 |
14 | const Footer: React.FC = () => {
15 | const [articles, setArticles] = useState([]);
16 |
17 | useEffect(() => {
18 | const fetchArticles = async () => {
19 | try {
20 | const response = await fetch(
21 | "https://api.mockfly.dev/mocks/ef8e4ba5-5dc1-4b36-9bca-5f59afb45ebe/article"
22 | );
23 | const data = await response.json();
24 | setArticles(data.articles.slice(0, 4));
25 | } catch (error) {
26 | console.error("Error fetching articles:", error);
27 | }
28 | };
29 | fetchArticles();
30 | }, []);
31 |
32 | return (
33 |
140 | );
141 | };
142 |
143 | export default Footer;
144 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import TopNav from '@/components/TopNav';
2 | import Slider from '@/components/Slider';
3 | import React from 'react';
4 | import AboutSection from '../components/AboutSection';
5 | import SkillCard from '../components/SkillCard';
6 | import ScrollToTopButton from '../components/ScrollToTopButton';
7 | import { HiAcademicCap } from "react-icons/hi";
8 | import Footer from "../components/Footer";
9 | import NavigationMenu from "../components/NavigationMenu";
10 | import ArticleSlider from "../components/ArticleSlider";
11 | import { GrArticle } from "react-icons/gr";
12 |
13 | const page = () => {
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | مهارتهای من
22 |
23 |
24 |
25 |
26 |
27 |
28 |
34 |
40 |
46 |
52 |
58 |
64 |
70 |
76 |
82 |
88 |
94 |
100 |
106 |
112 |
118 |
119 |
120 |
121 |
122 |
مقالات جدید :
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | )
132 | }
133 |
134 | export default page;
135 |
--------------------------------------------------------------------------------
/src/components/TopNav.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useState, useEffect } from 'react';
3 | import { usePathname } from 'next/navigation';
4 | import Link from 'next/link';
5 |
6 | interface Article {
7 | slug: string;
8 | category: string;
9 | title: string;
10 | }
11 |
12 | const baseSiteUrl = "https://sunflower-dev.com"; // insert domain
13 |
14 | const TopNav = () => {
15 | const [isOpen, setIsOpen] = useState(false);
16 | const [searchTerm, setSearchTerm] = useState("");
17 | const [allArticles, setAllArticles] = useState([]);
18 | const [searchResults, setSearchResults] = useState([]);
19 | const [noResults, setNoResults] = useState(false);
20 | const pathname = usePathname();
21 |
22 | const toggleMenu = () => {
23 | setIsOpen(!isOpen);
24 | };
25 |
26 | useEffect(() => {
27 | const fetchArticles = async () => {
28 | try {
29 | const response = await fetch(`https://api.mockfly.dev/mocks/ef8e4ba5-5dc1-4b36-9bca-5f59afb45ebe/article`);
30 | const data = await response.json();
31 |
32 | if (Array.isArray(data)) {
33 | setAllArticles(data);
34 | } else if (data && Array.isArray(data.articles)) {
35 | setAllArticles(data.articles);
36 | } else {
37 | setAllArticles([]);
38 | }
39 | } catch (error) {
40 | console.error("خطا در دریافت مقالات:", error);
41 | }
42 | };
43 |
44 | fetchArticles();
45 | }, []);
46 |
47 | const handleSearch = (event: React.ChangeEvent) => {
48 | const term = event.target.value;
49 | setSearchTerm(term);
50 |
51 | if (term.length > 2) {
52 | const filteredResults = allArticles.filter(article =>
53 | article.title.toLowerCase().includes(term.toLowerCase())
54 | );
55 | setSearchResults(filteredResults);
56 | setNoResults(filteredResults.length === 0);
57 | } else {
58 | setSearchResults([]);
59 | setNoResults(false);
60 | }
61 | };
62 |
63 | const isActive = (href: string) => pathname === href;
64 |
65 | return (
66 |
67 |
68 |
69 |
70 |
75 | صفحه اصلی
76 |
77 |
82 | درباره من
83 |
84 |
89 | مهارتهای من
90 |
91 |
96 | ارتباط با من
97 |
98 |
103 | مقالات من
104 |
105 |
106 |
107 | {/* search*/}
108 |
109 |
116 |
117 | {searchResults.length > 0 ? (
118 | searchResults.map((article) => (
119 |
121 | {article.title}
122 |
123 | ))
124 | ) : (
125 | noResults && (
126 |
127 | مقالهای پیدا نشد.
128 |
129 | )
130 | )}
131 |
132 |
133 |
134 | {/* respansive for mobile*/}
135 |
136 |
140 |
147 | {isOpen ? (
148 |
154 | ) : (
155 |
161 | )}
162 |
163 |
164 |
165 |
166 |
167 |
168 | {/* show nav mobile*/}
169 | {isOpen && (
170 |
171 |
172 | صفحه اصلی
173 |
174 |
175 | درباره من
176 |
177 |
178 | مهارتهای من
179 |
180 |
181 |
182 | ارتباط با من
183 |
184 |
185 | مقالات من
186 |
187 |
188 | )}
189 |
190 | );
191 | };
192 |
193 | export default TopNav;
194 |
--------------------------------------------------------------------------------