From 8bcd1c2951ae8506363d89af98c5070425031a2a Mon Sep 17 00:00:00 2001 From: DrMesta103 Date: Tue, 7 Apr 2026 10:38:28 +0330 Subject: [PATCH] admin --- README.md | 233 +++++ SETUP.md | 112 +++ USER-GUIDE.md | 168 ++++ app/(admin)/admin/countries/CountryForm.tsx | 208 ++++- .../admin/countries/[id]/edit/page.tsx | 5 +- .../[id]/lineup/DefaultLineupEditor.tsx | 794 ++++++++++++++++++ .../admin/countries/[id]/lineup/page.tsx | 29 + app/(admin)/admin/countries/page.tsx | 31 +- app/(admin)/admin/matches/[id]/edit/page.tsx | 5 +- app/(admin)/admin/players/PlayerForm.tsx | 62 +- app/(admin)/admin/players/[id]/edit/page.tsx | 6 +- .../admin/rounds/ActivateRoundButton.tsx | 14 +- .../admin/rounds/DeleteRoundButton.tsx | 66 ++ app/(admin)/admin/rounds/RoundForm.tsx | 29 +- .../admin/rounds/[id]/DeleteMatchButton.tsx | 57 ++ app/(admin)/admin/rounds/[id]/edit/page.tsx | 23 + .../match/[matchId]/MatchLineupManager.tsx | 239 ++++++ .../rounds/[id]/match/[matchId]/page.tsx | 23 +- app/(admin)/admin/rounds/[id]/page.tsx | 46 +- app/(admin)/admin/rounds/page.tsx | 8 +- app/(user)/countries/[code]/page.tsx | 139 +++ app/(user)/team/TeamBuilder.tsx | 179 ++-- .../admin/matches/[id]/calc-points/route.ts | 5 +- .../matches/[id]/events/[eventId]/route.ts | 5 +- app/api/admin/matches/[id]/events/route.ts | 5 +- app/api/admin/matches/[id]/lineup/route.ts | 7 +- app/api/countries/[id]/route.ts | 10 +- app/api/matches/[id]/route.ts | 15 +- app/api/players/[id]/route.ts | 10 +- app/api/rounds/[id]/activate/route.ts | 24 +- app/api/rounds/route.ts | 31 + app/api/team/route.ts | 14 +- app/api/test-session/route.ts | 29 + app/api/upload/player-image/route.ts | 61 ++ components/CountryFlag.tsx | 25 + package-lock.json | 603 ++++++++++++- package.json | 14 +- prisma/schema.prisma | 42 +- public/imgs/flags/0qf5w0ww.jpg | Bin 0 -> 29305 bytes public/imgs/flags/Flag_of_Algeria.svg.webp | Bin 0 -> 368 bytes public/imgs/flags/Flag_of_Argentina.svg.webp | Bin 0 -> 200 bytes public/imgs/flags/Flag_of_Australia.webp | Bin 0 -> 438 bytes public/imgs/flags/Flag_of_Austria.svg.webp | Bin 0 -> 94 bytes public/imgs/flags/Flag_of_Belgium.webp | Bin 0 -> 134 bytes public/imgs/flags/Flag_of_Brazil.svg.webp | Bin 0 -> 568 bytes public/imgs/flags/Flag_of_Canada.svg.webp | Bin 0 -> 254 bytes public/imgs/flags/Flag_of_Cape_Verde.svg.webp | Bin 0 -> 290 bytes public/imgs/flags/Flag_of_Colombia.svg.webp | Bin 0 -> 96 bytes public/imgs/flags/Flag_of_Croatia.svg.webp | Bin 0 -> 396 bytes public/imgs/flags/Flag_of_Curaçao.svg.webp | Bin 0 -> 200 bytes .../imgs/flags/Flag_of_Côte_d'Ivoire.svg.webp | Bin 0 -> 134 bytes public/imgs/flags/Flag_of_Ecuador.svg.webp | Bin 0 -> 566 bytes public/imgs/flags/Flag_of_Egypt.svg.webp | Bin 0 -> 222 bytes public/imgs/flags/Flag_of_England.svg.webp | Bin 0 -> 94 bytes public/imgs/flags/Flag_of_France.svg.png | Bin 0 -> 340 bytes public/imgs/flags/Flag_of_Germany.svg.webp | Bin 0 -> 56 bytes public/imgs/flags/Flag_of_Ghana.svg.webp | Bin 0 -> 206 bytes public/imgs/flags/Flag_of_Haiti.svg.webp | Bin 0 -> 496 bytes public/imgs/flags/Flag_of_Iran.svg.webp | Bin 0 -> 420 bytes public/imgs/flags/Flag_of_Japan.svg.webp | Bin 0 -> 260 bytes public/imgs/flags/Flag_of_Jordan.svg.webp | Bin 0 -> 240 bytes public/imgs/flags/Flag_of_Mexico.svg.webp | Bin 0 -> 416 bytes public/imgs/flags/Flag_of_Morocco.svg.webp | Bin 0 -> 220 bytes .../imgs/flags/Flag_of_New_Zealand.svg.webp | Bin 0 -> 406 bytes public/imgs/flags/Flag_of_Norway.svg.webp | Bin 0 -> 218 bytes public/imgs/flags/Flag_of_Panama.svg.webp | Bin 0 -> 300 bytes public/imgs/flags/Flag_of_Paraguay.svg.webp | Bin 0 -> 156 bytes public/imgs/flags/Flag_of_Portugal.svg.webp | Bin 0 -> 560 bytes public/imgs/flags/Flag_of_Qatar.svg.webp | Bin 0 -> 252 bytes .../imgs/flags/Flag_of_Saudi_Arabia.svg.webp | Bin 0 -> 384 bytes public/imgs/flags/Flag_of_Scotland.svg.webp | Bin 0 -> 214 bytes public/imgs/flags/Flag_of_Senegal.svg.webp | Bin 0 -> 216 bytes .../imgs/flags/Flag_of_South_Africa.svg.webp | Bin 0 -> 444 bytes .../imgs/flags/Flag_of_South_Korea.svg.webp | Bin 0 -> 690 bytes public/imgs/flags/Flag_of_Spain.svg.webp | Bin 0 -> 448 bytes .../imgs/flags/Flag_of_Switzerland.svg.webp | Bin 0 -> 108 bytes public/imgs/flags/Flag_of_Tunisia.svg.webp | Bin 0 -> 322 bytes public/imgs/flags/Flag_of_Uruguay.svg.webp | Bin 0 -> 470 bytes public/imgs/flags/Flag_of_Uzbekistan.svg.webp | Bin 0 -> 218 bytes .../flags/Flag_of_the_Netherlands.svg.webp | Bin 0 -> 100 bytes .../flags/Flag_of_the_United_States.svg.webp | Bin 0 -> 336 bytes public/imgs/flags/izf03j3p.jpg | Bin 0 -> 7032 bytes public/uploads/players/.gitkeep | 1 + .../players/1775457575652-izf03j3p.jpg | Bin 0 -> 7032 bytes .../players/1775457601278-wdyjvv41.jpg | Bin 0 -> 7390 bytes .../players/1775457633947-npxfb0ze.jpg | Bin 0 -> 6554 bytes .../players/1775457664083-jokccxy4.jpg | Bin 0 -> 24220 bytes .../players/1775457674194-eyatdip1.jpg | Bin 0 -> 75159 bytes .../players/1775457689792-0qf5w0ww.jpg | Bin 0 -> 29305 bytes .../players/1775457723763-jokccxy4.jpg | Bin 0 -> 24220 bytes .../players/1775457740827-h2oiopig.jpg | Bin 0 -> 7244 bytes .../players/1775457773915-tfljtemw.jpg | Bin 0 -> 7314 bytes .../players/1775457801579-u0eya02o.jpg | Bin 0 -> 5974 bytes .../players/1775457816751-gfdffuen.jpg | Bin 0 -> 7108 bytes .../players/1775457847547-lbal0jyw.jpg | Bin 0 -> 6938 bytes .../players/1775457873652-alvxmwu4.jpg | Bin 0 -> 30342 bytes scripts/check-users.ts | 70 ++ scripts/create-admin-user.ts | 44 + scripts/create-test-user.ts | 44 + 99 files changed, 3357 insertions(+), 178 deletions(-) create mode 100644 README.md create mode 100644 SETUP.md create mode 100644 USER-GUIDE.md create mode 100644 app/(admin)/admin/countries/[id]/lineup/DefaultLineupEditor.tsx create mode 100644 app/(admin)/admin/countries/[id]/lineup/page.tsx create mode 100644 app/(admin)/admin/rounds/DeleteRoundButton.tsx create mode 100644 app/(admin)/admin/rounds/[id]/DeleteMatchButton.tsx create mode 100644 app/(admin)/admin/rounds/[id]/edit/page.tsx create mode 100644 app/(admin)/admin/rounds/[id]/match/[matchId]/MatchLineupManager.tsx create mode 100644 app/(user)/countries/[code]/page.tsx create mode 100644 app/api/test-session/route.ts create mode 100644 app/api/upload/player-image/route.ts create mode 100644 components/CountryFlag.tsx create mode 100644 public/imgs/flags/0qf5w0ww.jpg create mode 100644 public/imgs/flags/Flag_of_Algeria.svg.webp create mode 100644 public/imgs/flags/Flag_of_Argentina.svg.webp create mode 100644 public/imgs/flags/Flag_of_Australia.webp create mode 100644 public/imgs/flags/Flag_of_Austria.svg.webp create mode 100644 public/imgs/flags/Flag_of_Belgium.webp create mode 100644 public/imgs/flags/Flag_of_Brazil.svg.webp create mode 100644 public/imgs/flags/Flag_of_Canada.svg.webp create mode 100644 public/imgs/flags/Flag_of_Cape_Verde.svg.webp create mode 100644 public/imgs/flags/Flag_of_Colombia.svg.webp create mode 100644 public/imgs/flags/Flag_of_Croatia.svg.webp create mode 100644 public/imgs/flags/Flag_of_Curaçao.svg.webp create mode 100644 public/imgs/flags/Flag_of_Côte_d'Ivoire.svg.webp create mode 100644 public/imgs/flags/Flag_of_Ecuador.svg.webp create mode 100644 public/imgs/flags/Flag_of_Egypt.svg.webp create mode 100644 public/imgs/flags/Flag_of_England.svg.webp create mode 100644 public/imgs/flags/Flag_of_France.svg.png create mode 100644 public/imgs/flags/Flag_of_Germany.svg.webp create mode 100644 public/imgs/flags/Flag_of_Ghana.svg.webp create mode 100644 public/imgs/flags/Flag_of_Haiti.svg.webp create mode 100644 public/imgs/flags/Flag_of_Iran.svg.webp create mode 100644 public/imgs/flags/Flag_of_Japan.svg.webp create mode 100644 public/imgs/flags/Flag_of_Jordan.svg.webp create mode 100644 public/imgs/flags/Flag_of_Mexico.svg.webp create mode 100644 public/imgs/flags/Flag_of_Morocco.svg.webp create mode 100644 public/imgs/flags/Flag_of_New_Zealand.svg.webp create mode 100644 public/imgs/flags/Flag_of_Norway.svg.webp create mode 100644 public/imgs/flags/Flag_of_Panama.svg.webp create mode 100644 public/imgs/flags/Flag_of_Paraguay.svg.webp create mode 100644 public/imgs/flags/Flag_of_Portugal.svg.webp create mode 100644 public/imgs/flags/Flag_of_Qatar.svg.webp create mode 100644 public/imgs/flags/Flag_of_Saudi_Arabia.svg.webp create mode 100644 public/imgs/flags/Flag_of_Scotland.svg.webp create mode 100644 public/imgs/flags/Flag_of_Senegal.svg.webp create mode 100644 public/imgs/flags/Flag_of_South_Africa.svg.webp create mode 100644 public/imgs/flags/Flag_of_South_Korea.svg.webp create mode 100644 public/imgs/flags/Flag_of_Spain.svg.webp create mode 100644 public/imgs/flags/Flag_of_Switzerland.svg.webp create mode 100644 public/imgs/flags/Flag_of_Tunisia.svg.webp create mode 100644 public/imgs/flags/Flag_of_Uruguay.svg.webp create mode 100644 public/imgs/flags/Flag_of_Uzbekistan.svg.webp create mode 100644 public/imgs/flags/Flag_of_the_Netherlands.svg.webp create mode 100644 public/imgs/flags/Flag_of_the_United_States.svg.webp create mode 100644 public/imgs/flags/izf03j3p.jpg create mode 100644 public/uploads/players/.gitkeep create mode 100644 public/uploads/players/1775457575652-izf03j3p.jpg create mode 100644 public/uploads/players/1775457601278-wdyjvv41.jpg create mode 100644 public/uploads/players/1775457633947-npxfb0ze.jpg create mode 100644 public/uploads/players/1775457664083-jokccxy4.jpg create mode 100644 public/uploads/players/1775457674194-eyatdip1.jpg create mode 100644 public/uploads/players/1775457689792-0qf5w0ww.jpg create mode 100644 public/uploads/players/1775457723763-jokccxy4.jpg create mode 100644 public/uploads/players/1775457740827-h2oiopig.jpg create mode 100644 public/uploads/players/1775457773915-tfljtemw.jpg create mode 100644 public/uploads/players/1775457801579-u0eya02o.jpg create mode 100644 public/uploads/players/1775457816751-gfdffuen.jpg create mode 100644 public/uploads/players/1775457847547-lbal0jyw.jpg create mode 100644 public/uploads/players/1775457873652-alvxmwu4.jpg create mode 100644 scripts/check-users.ts create mode 100644 scripts/create-admin-user.ts create mode 100644 scripts/create-test-user.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d80e3f --- /dev/null +++ b/README.md @@ -0,0 +1,233 @@ +# ⚽ فانتزی فوتبال - Fantasy Football + +یک پلتفرم فانتزی فوتبال کامل با Next.js، Prisma و PostgreSQL + +## 🌟 ویژگی‌ها + +### برای کاربران +- ✅ ثبت‌نام و ورود امن با NextAuth +- ✅ ساخت تیم به صورت استپ‌بای‌استپ +- ✅ انتخاب لوگو و نام تیم +- ✅ انتخاب از 6 ترکیب مختلف +- ✅ مدیریت بودجه (100 میلیون) +- ✅ انتخاب 11 بازیکن اصلی + 4 ذخیره +- ✅ Drag & Drop برای جابجایی بازیکنان +- ✅ انتخاب کاپیتان و نایب کاپیتان +- ✅ فیلتر و جستجوی بازیکنان +- ✅ نمایش زمین فوتبال واقعی +- ✅ پیگیری امتیازات + +### برای ادمین +- ✅ مدیریت کشورها و پرچم‌ها +- ✅ مدیریت بازیکنان +- ✅ مدیریت مسابقات و راند‌ها +- ✅ ثبت رویدادهای بازی (گل، کارت، و...) +- ✅ محاسبه خودکار امتیازات +- ✅ تایید تیم‌های کاربران +- ✅ مدیریت قوانین امتیازدهی +- ✅ آمار و گزارش‌گیری + +## 🚀 نصب و راه‌اندازی + +### پیش‌نیازها +- Node.js 18+ +- PostgreSQL +- npm یا yarn + +### مراحل نصب + +1. **کلون کردن پروژه** +```bash +git clone +cd football-next +``` + +2. **نصب وابستگی‌ها** +```bash +npm install +``` + +3. **تنظیم متغیرهای محیطی** +فایل `.env` را ایجاد کنید: +```env +DATABASE_URL="postgresql://user:password@host:port/database" +NEXTAUTH_SECRET="your-secret-key" +NEXTAUTH_URL="http://localhost:3000" +``` + +4. **راه‌اندازی دیتابیس** +```bash +npm run db:generate +npm run db:push +``` + +5. **ساخت کاربران تست** +```bash +# کاربر عادی +npm run setup:test-user + +# کاربر ادمین +npm run setup:admin +``` + +6. **اجرای پروژه** +```bash +npm run dev +``` + +پروژه روی `http://localhost:3000` اجرا می‌شود. + +## 📋 اسکریپت‌های NPM + +```bash +npm run dev # اجرای development server +npm run build # ساخت برای production +npm run start # اجرای production server +npm run db:push # اعمال تغییرات schema به دیتابیس +npm run db:generate # تولید Prisma Client +npm run db:studio # باز کردن Prisma Studio +npm run setup:test-user # ساخت کاربر تست +npm run setup:admin # ساخت کاربر ادمین +npm run check:users # بررسی کاربران موجود +``` + +## 🔐 کاربران پیش‌فرض + +### کاربر عادی +- ایمیل: `test@test.com` +- رمز عبور: `123456` + +### ادمین +- ایمیل: `admin@admin.com` +- رمز عبور: `admin123` + +## 📁 ساختار پروژه + +``` +football-next/ +├── app/ # Next.js App Router +│ ├── (admin)/ # صفحات ادمین +│ ├── (user)/ # صفحات کاربر +│ └── api/ # API Routes +├── components/ # کامپوننت‌های React +├── lib/ # توابع کمکی +│ ├── auth.ts # تنظیمات NextAuth +│ └── db.ts # Prisma Client +├── prisma/ # Schema و Migrations +├── public/ # فایل‌های استاتیک +├── scripts/ # اسکریپت‌های کمکی +└── styles/ # فایل‌های CSS +``` + +## 🎮 راهنمای استفاده + +### برای کاربران +مستندات کامل در [USER-GUIDE.md](./USER-GUIDE.md) + +### برای توسعه‌دهندگان +مستندات راه‌اندازی در [SETUP.md](./SETUP.md) + +## 🛠️ تکنولوژی‌ها + +- **Framework**: Next.js 16 (App Router) +- **Database**: PostgreSQL +- **ORM**: Prisma +- **Authentication**: NextAuth.js +- **Styling**: Tailwind CSS +- **Language**: TypeScript +- **Drag & Drop**: Native HTML5 + +## 📊 مدل دیتابیس + +### جداول اصلی +- `User` - کاربران +- `Team` - تیم‌های کاربران +- `Player` - بازیکنان +- `Country` - کشورها +- `Match` - مسابقات +- `Round` - راندها +- `MatchEvent` - رویدادهای بازی +- `PlayerMatchStat` - آمار بازیکنان +- `ScoringRule` - قوانین امتیازدهی + +## 🎨 ویژگی‌های طراحی + +- طراحی Responsive +- حالت RTL برای فارسی +- انیمیشن‌های روان +- رنگ‌بندی مدرن +- UX بهینه +- دسترسی‌پذیری + +## 🔄 فرآیند ساخت تیم + +### استپ 1: مشخصات تیم +1. انتخاب لوگو (10 گزینه) +2. وارد کردن نام تیم +3. ساخت تیم + +### استپ 2: انتخاب بازیکنان +1. انتخاب ترکیب +2. اضافه کردن 11 بازیکن اصلی +3. اضافه کردن 4 بازیکن ذخیره +4. انتخاب کاپیتان و نایب کاپیتان +5. ثبت نهایی + +## 🐛 رفع مشکلات + +### خطای Foreign Key +```bash +# بررسی کاربران +npm run check:users + +# ساخت کاربر جدید +npm run setup:test-user +``` + +### مشکل Session +1. از مرورگر خارج شوید +2. Cache را پاک کنید +3. دوباره وارد شوید + +### خطای دیتابیس +```bash +# ریست کردن دیتابیس +npx prisma db push --force-reset + +# تولید مجدد Client +npm run db:generate +``` + +## 📈 TODO + +- [ ] آپلود تصویر برای لوگو تیم +- [ ] پیش‌نمایش تیم +- [ ] انیمیشن‌های بهتر +- [ ] نمایش آمار تفصیلی بازیکنان +- [ ] فیلتر پیشرفته +- [ ] مقایسه بازیکنان +- [ ] پیش‌بینی امتیازات +- [ ] اعلان‌های Real-time +- [ ] چت و نظرات +- [ ] لیگ‌های خصوصی + +## 🤝 مشارکت + +برای مشارکت در پروژه: +1. Fork کنید +2. Branch جدید بسازید +3. تغییرات را Commit کنید +4. Push کنید +5. Pull Request بزنید + +## 📄 لایسنس + +این پروژه تحت لایسنس MIT است. + +## 📞 تماس + +برای سوالات و پشتیبانی، با ما تماس بگیرید. + +--- + +**ساخته شده با ❤️ برای علاقه‌مندان فوتبال** diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..0466631 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,112 @@ +# راهنمای راه‌اندازی پروژه فانتزی فوتبال + +## 🚀 نصب و راه‌اندازی + +### 1. نصب وابستگی‌ها +```bash +npm install +``` + +### 2. تنظیم دیتابیس +```bash +npx prisma generate +npx prisma db push +``` + +### 3. ساخت کاربران تست + +#### کاربر عادی +```bash +npx tsx scripts/create-test-user.ts +``` +- ایمیل: `test@test.com` +- رمز عبور: `123456` + +#### کاربر ادمین +```bash +npx tsx scripts/create-admin-user.ts +``` +- ایمیل: `admin@admin.com` +- رمز عبور: `admin123` + +### 4. اجرای پروژه +```bash +npm run dev +``` + +پروژه روی `http://localhost:3000` اجرا می‌شود. + +## 📋 مراحل ساخت تیم + +### استپ 1: نام تیم و لوگو +1. به صفحه `/team` بروید +2. یک لوگو برای تیم انتخاب کنید (از 10 گزینه موجود) +3. نام تیم را وارد کنید (حداکثر 30 کاراکتر) +4. روی "بعدی: انتخاب ترکیب" کلیک کنید + +### استپ 2: انتخاب ترکیب و بازیکنان +1. یک ترکیب انتخاب کنید (4-3-3، 4-4-2، و...) +2. از لیست سمت راست، بازیکنان را انتخاب کنید +3. باید 11 بازیکن اصلی + 4 بازیکن ذخیره داشته باشید +4. بودجه شما 100 میلیون است +5. می‌توانید با drag & drop بازیکنان را جابجا کنید +6. یک کاپیتان و یک نایب کاپیتان انتخاب کنید +7. وقتی تیم کامل شد، روی "وارد رقابت شو" کلیک کنید + +## 🔧 اسکریپت‌های مفید + +### بررسی کاربران +```bash +npx tsx scripts/check-users.ts +``` + +### تست Session +مراجعه به: `http://localhost:3000/api/test-session` + +## 🎨 ویژگی‌های جدید + +### صفحه ساخت تیم +- ✅ طراحی استپ‌بای‌استپ (2 مرحله) +- ✅ انتخاب لوگو تیم (10 گزینه) +- ✅ انتخاب نام تیم با محدودیت 30 کاراکتر +- ✅ نمایش پیشرفت (Progress Indicator) +- ✅ طراحی مدرن و شماتیک +- ✅ رنگ‌بندی بهتر و gradient ها +- ✅ پیام‌های خطا و موفقیت واضح‌تر +- ✅ راهنمای تکمیل تیم + +### بهبودهای API +- ✅ بررسی وجود کاربر قبل از ساخت تیم +- ✅ ذخیره formation در زمان ساخت تیم +- ✅ پیام‌های خطای بهتر + +## 🐛 رفع مشکلات + +### خطای "Foreign key constraint violated" +این خطا زمانی رخ می‌دهد که userId در دیتابیس وجود ندارد. برای رفع: +1. مطمئن شوید که لاگین کرده‌اید +2. اسکریپت `check-users.ts` را اجرا کنید +3. در صورت نیاز، کاربر جدید بسازید + +### تیم ساخته نمی‌شود +1. از مرورگر خارج شوید (Logout) +2. دوباره وارد شوید (Login) +3. به صفحه `/team` بروید +4. اگر باز هم مشکل دارید، `/api/test-session` را چک کنید + +## 📱 صفحات + +- `/` - صفحه اصلی +- `/login` - ورود +- `/register` - ثبت‌نام +- `/team` - ساخت و مدیریت تیم +- `/admin` - پنل ادمین +- `/profile` - پروفایل کاربر + +## 🎯 TODO + +- [ ] آپلود تصویر برای لوگو تیم +- [ ] پیش‌نمایش تیم قبل از ثبت نهایی +- [ ] انیمیشن‌های بهتر برای drag & drop +- [ ] نمایش آمار بازیکنان در هنگام انتخاب +- [ ] فیلتر پیشرفته بازیکنان (بر اساس قیمت، امتیاز، و...) diff --git a/USER-GUIDE.md b/USER-GUIDE.md new file mode 100644 index 0000000..bf3e457 --- /dev/null +++ b/USER-GUIDE.md @@ -0,0 +1,168 @@ +# 📖 راهنمای کاربر - فانتزی فوتبال + +## 🎮 شروع کار + +### ثبت‌نام و ورود +1. به صفحه `/register` بروید +2. ایمیل و رمز عبور خود را وارد کنید +3. پس از ثبت‌نام، وارد شوید + +یا از کاربران تست استفاده کنید: +- **کاربر عادی**: `test@test.com` / `123456` +- **ادمین**: `admin@admin.com` / `admin123` + +## ⚽ ساخت تیم (2 مرحله) + +### مرحله 1️⃣: مشخصات تیم + +#### انتخاب لوگو +- 10 لوگوی مختلف در دسترس است +- روی هر لوگو کلیک کنید تا انتخاب شود +- لوگوی انتخابی با رنگ سبز هایلایت می‌شود + +#### نام تیم +- یک نام منحصر به فرد برای تیم خود انتخاب کنید +- حداکثر 30 کاراکتر +- مثال: "شیران طلایی"، "عقاب‌های آبی" + +#### دکمه بعدی +- پس از وارد کردن نام، روی "بعدی: انتخاب ترکیب" کلیک کنید +- تیم شما ساخته می‌شود و به مرحله بعد می‌روید + +### مرحله 2️⃣: انتخاب بازیکنان + +#### انتخاب ترکیب +شش ترکیب مختلف در دسترس است: +- **4-3-3**: 4 مدافع، 3 هافبک، 3 مهاجم (متعادل) +- **4-4-2**: 4 مدافع، 4 هافبک، 2 مهاجم (دفاعی) +- **4-5-1**: 4 مدافع، 5 هافبک، 1 مهاجم (خیلی دفاعی) +- **3-5-2**: 3 مدافع، 5 هافبک، 2 مهاجم (کنترل میانه) +- **3-4-3**: 3 مدافع، 4 هافبک، 3 مهاجم (تهاجمی) +- **5-3-2**: 5 مدافع، 3 هافبک، 2 مهاجم (خیلی دفاعی) + +#### بودجه +- بودجه اولیه: **100 میلیون** +- قیمت هر بازیکن از بودجه کم می‌شود +- بودجه باقیمانده در بالای صفحه نمایش داده می‌شود +- اگر بودجه کافی نداشته باشید، نمی‌توانید بازیکن اضافه کنید + +#### انتخاب بازیکنان + +##### فیلتر کردن +- **فیلتر پست**: GK (دروازه‌بان), DEF (مدافع), MID (هافبک), FWD (مهاجم) +- **جستجو**: نام بازیکن یا کشور را جستجو کنید + +##### اضافه کردن بازیکن +1. بازیکن مورد نظر را پیدا کنید +2. روی دکمه **+** کلیک کنید +3. بازیکن به تیم شما اضافه می‌شود + +##### تعداد بازیکنان مورد نیاز +- **11 بازیکن اصلی**: + - 1 دروازه‌بان + - تعداد مدافع، هافبک، مهاجم بر اساس ترکیب +- **4 بازیکن ذخیره** (حداقل): + - از هر پست حداقل 1 نفر + +#### مدیریت بازیکنان + +##### جابجایی (Drag & Drop) +- بازیکن را بگیرید و به جای دیگری بکشید +- می‌توانید بازیکنان اصلی و ذخیره را جابجا کنید + +##### منوی بازیکن +روی هر بازیکن کلیک کنید تا منو باز شود: +- **کاپیتان (©)**: امتیاز 2 برابر می‌شود +- **نایب کاپیتان (VC)**: اگر کاپیتان بازی نکند، جایگزین می‌شود +- **حذف از تیم**: بازیکن را از تیم حذف کنید + +##### بازیکنان حذف شده +- بازیکنانی که تیم ملی‌شان حذف شده با علامت ❌ نمایش داده می‌شوند +- این بازیکنان دیگر امتیازی نمی‌آورند +- بهتر است آنها را حذف کنید + +#### ثبت نهایی تیم +وقتی تیم کامل شد: +1. دکمه سبز "✓ تیم کامله! وارد رقابت شو" نمایش داده می‌شود +2. روی آن کلیک کنید +3. تیم شما برای تایید ادمین ارسال می‌شود +4. پس از تایید، می‌توانید در رقابت شرکت کنید + +## 📊 نمایش اطلاعات + +### در بالای صفحه +- **امتیاز**: مجموع امتیازات تیم شما +- **بودجه**: بودجه باقیمانده +- **بازیکن**: تعداد بازیکنان اصلی / 11 + +### در لیست بازیکنان +- **نام بازیکن** +- **کشور و پرچم** +- **پست** (GK, DEF, MID, FWD) +- **قیمت** (به میلیون) +- **امتیاز** (pts) + +### روی زمین +- **نام کوتاه بازیکن** +- **امتیاز** +- **کاپیتان (©)** یا **نایب کاپیتان (VC)** +- **وضعیت تیم ملی** (حذف شده یا فعال) + +## 💡 نکات مهم + +### استراتژی انتخاب +1. **تعادل**: بازیکنان گران و ارزان را ترکیب کنید +2. **فرم**: بازیکنانی که امتیاز بیشتری دارند را انتخاب کنید +3. **تیم ملی**: از بازیکنان تیم‌های قوی انتخاب کنید +4. **ذخیره**: ذخیره‌های خوب داشته باشید + +### کاپیتان +- کاپیتان امتیاز 2 برابر می‌آورد +- بهترین بازیکن خود را کاپیتان کنید +- نایب کاپیتان را هم انتخاب کنید + +### بودجه +- بودجه را هوشمندانه خرج کنید +- همه بودجه را خرج نکنید (برای تغییرات بعدی) +- بازیکنان ارزان با امتیاز خوب پیدا کنید + +## ❓ سوالات متداول + +### چرا نمی‌توانم بازیکن اضافه کنم؟ +- بودجه کافی ندارید +- تیم شما کامل است (15 بازیکن) +- بازیکن قبلاً در تیم شماست + +### چگونه ترکیب را عوض کنم؟ +- در بالای زمین، روی ترکیب مورد نظر کلیک کنید +- بازیکنان خودکار جابجا نمی‌شوند، باید دستی جابجا کنید + +### چرا تیمم ثبت نمی‌شود؟ +- باید 11 بازیکن اصلی داشته باشید +- باید 4 بازیکن ذخیره داشته باشید +- بودجه باید مثبت باشد + +### بازیکن حذف شده چیست؟ +- بازیکنی که تیم ملی‌اش از مسابقات حذف شده +- دیگر امتیازی نمی‌آورد +- بهتر است حذف شود + +## 🎯 مراحل بعدی + +پس از ساخت تیم: +1. منتظر تایید ادمین بمانید +2. به صفحه `/profile` بروید +3. تیم خود را مدیریت کنید +4. امتیازات را دنبال کنید +5. در جدول رتبه‌بندی شرکت کنید + +## 🆘 پشتیبانی + +اگر مشکلی دارید: +1. از مرورگر خارج شوید و دوباره وارد شوید +2. Cache مرورگر را پاک کنید +3. با ادمین تماس بگیرید + +--- + +**موفق باشید! ⚽🏆** diff --git a/app/(admin)/admin/countries/CountryForm.tsx b/app/(admin)/admin/countries/CountryForm.tsx index f209d9b..55fe171 100644 --- a/app/(admin)/admin/countries/CountryForm.tsx +++ b/app/(admin)/admin/countries/CountryForm.tsx @@ -11,7 +11,19 @@ export default function CountryForm({ countryId, }: { groups: Group[]; - initial?: { name: string; code: string; flagUrl?: string | null; groupId?: string | null }; + initial?: { + name: string; + code: string; + flagUrl?: string | null; + flagImage?: string | null; + groupId?: string | null; + confederation?: string | null; + qualificationMethod?: string | null; + qualificationDate?: string | null; + participationHistory?: string | null; + bestResult?: string | null; + description?: string | null; + }; countryId?: string; }) { const router = useRouter(); @@ -19,52 +31,194 @@ export default function CountryForm({ name: initial?.name ?? "", code: initial?.code ?? "", flagUrl: initial?.flagUrl ?? "", + flagImage: initial?.flagImage ?? "", groupId: initial?.groupId ?? "", + confederation: initial?.confederation ?? "", + qualificationMethod: initial?.qualificationMethod ?? "", + qualificationDate: initial?.qualificationDate ?? "", + participationHistory: initial?.participationHistory ?? "", + bestResult: initial?.bestResult ?? "", + description: initial?.description ?? "", }); const [loading, setLoading] = useState(false); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setLoading(true); - const payload = { ...form, groupId: form.groupId || null, flagUrl: form.flagUrl || null }; + const payload = { + ...form, + groupId: form.groupId || null, + flagUrl: form.flagUrl || null, + flagImage: form.flagImage || null, + confederation: form.confederation || null, + qualificationMethod: form.qualificationMethod || null, + qualificationDate: form.qualificationDate || null, + participationHistory: form.participationHistory || null, + bestResult: form.bestResult || null, + description: form.description || null, + }; const res = await fetch(countryId ? `/api/countries/${countryId}` : "/api/countries", { method: countryId ? "PUT" : "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); - if (res.ok) { router.push("/admin/countries"); router.refresh(); } + if (res.ok) { + router.push("/admin/countries"); + router.refresh(); + } setLoading(false); } return ( -
-
- - setForm({ ...form, name: e.target.value })} - className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" required /> + +

اطلاعات پایه

+ +
+
+ + setForm({ ...form, name: e.target.value })} + className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" + required + /> +
+
+ + setForm({ ...form, code: e.target.value.toUpperCase() })} + maxLength={3} + className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" + required + /> +
-
- - setForm({ ...form, code: e.target.value.toUpperCase() })} - maxLength={3} - className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" required /> + +
+
+ + setForm({ ...form, flagUrl: e.target.value })} + placeholder="🇮🇷" + className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" + /> +
+
+ + setForm({ ...form, flagImage: e.target.value })} + placeholder="Flag_of_Iran.webp" + className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" + /> +

فایل باید در public/imgs/flags باشد

+
-
- - setForm({ ...form, flagUrl: e.target.value })} - placeholder="🇮🇷" - className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" /> + +
+
+ + +
+
+ + +
-
- - + +
+

اطلاعات راه‌یابی

+ +
+
+ + setForm({ ...form, qualificationMethod: e.target.value })} + placeholder="مثلاً: صعود از مرحله مقدماتی" + className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" + /> +
+
+ + setForm({ ...form, qualificationDate: e.target.value })} + placeholder="مثلاً: ۲۵ مارس ۲۰۲۵" + className="w-full border rounded-xl px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-green-500" + /> +
-