اگر آپ نے ایک رات پہلے کبھی ٹیسٹ کے لیے چکر لگایا ہے، تو آپ جانتے ہیں کہ ہر چیز کو یاد رکھنا کتنا مشکل ہے۔
فلیش کارڈز سیکھنے کے سب سے موثر ٹولز میں سے ایک ہیں۔ فعال یاد: غیر فعال طور پر نوٹس پڑھنے کے بجائے، فعال طور پر جوابات کو یاد رکھنے کی کوشش کریں۔ مطالعات سے پتہ چلتا ہے کہ یہ آپ کی یادداشت کو مضبوط کرتا ہے اور آپ کو معلومات کو برقرار رکھنے میں مدد کرتا ہے۔
اس ٹیوٹوریل میں، آپ ایک فل اسٹیک فلیش کارڈ ایپ بناتے ہیں جو طلباء کو اجازت دیتا ہے:
-
ایک موضوع بنائیں (مثال کے طور پر ‘حیاتیات 101’ یا ‘کیلکولس’)
-
فلیش کارڈ شامل کریں۔ سامنے سوال، پیچھے جواب
-
مطالعہ درست یا غلط کی نشاندہی کرنے کے لیے کارڈ کو پلٹ کر
-
اپنی پیشرفت کو ٹریک کریں۔ یہ جاننے کے لیے کہ وہ کتنا اچھا کر رہے ہیں۔
آخر میں، آپ کے پاس ایک کام کرنے والی ایپ ہوگی جو MongoDB میں ڈیٹا اسٹور کرتی ہے اور Next.js میں چلتی ہے۔ ان ٹولز کے ساتھ کسی پیشگی تجربے کی ضرورت نہیں ہے۔ جب ہم ساتھ چلتے ہیں تو میں سب کچھ بتاؤں گا۔
انڈیکس
جو آپ سیکھیں گے۔
-
TypeScript کا استعمال کرتے ہوئے Next.js پروجیکٹ کو کیسے ترتیب دیا جائے۔
-
MongoDB سے کیسے جڑیں اور ڈیٹا اسٹور کریں۔
-
ڈیٹا بنانے، پڑھنے، اپ ڈیٹ کرنے اور حذف کرنے کے لیے API روٹس کیسے بنائیں
-
فارمز، فہرستوں اور انٹرایکٹو فلیش کارڈز کا استعمال کرتے ہوئے React UI کو کیسے بنایا جائے۔
-
فلپ اینیمیشن اور پروگریس ٹریکنگ کیسے شامل کریں۔
ٹیکنالوجی اسٹیک کا جائزہ
کوڈنگ شروع کرنے سے پہلے، یہ ہے کہ ہم کیا استعمال کرتے ہیں اور کیوں۔
Next.js
Next.js ویب ایپلیکیشنز بنانے کے لیے ایک ری ایکٹ فریم ورک ہے۔ یہ روٹنگ، سرور سائیڈ رینڈرنگ، اور API روٹس کو باکس سے باہر ہینڈل کرتا ہے۔
علیحدہ فرنٹ اینڈ اور بیک اینڈ بنانے کے بجائے، Next.js آپ کو ان دونوں کو ایک پروجیکٹ میں ڈالنے دیتا ہے۔ API پاتھ، جیسے /api/flashcards) وہ صفحات جو ڈیٹا بیس کے ساتھ بات چیت کرتے ہیں اور وہ صفحات جو UI کو ظاہر کرتے ہیں دونوں ایک ہی کوڈ بیس میں ہیں۔
منگو ڈی بی
MongoDB ایک NoSQL ڈیٹا بیس ہے جو ڈیٹا کو JSON جیسی دستاویزات کے طور پر اسٹور کرتا ہے۔ قطاروں اور کالموں والی روایتی جدولوں کے برعکس، وہ لچکدار "دستاویزات” کو "مجموعوں” میں محفوظ کرتے ہیں۔
MongoDB ابتدائی طور پر دوستانہ ہے، JavaScript/TypeScript کے ساتھ اچھی طرح کام کرتا ہے، اور اس کے پاس فراخدلی سے مفت درجے (MongoDB Atlas) ہے یا Docker کا استعمال کرتے ہوئے مقامی طور پر چلایا جا سکتا ہے۔
منگوز
Mongoose ایک لائبریری ہے جو آپ کو MongoDB اسکیما اور ماڈل کی وضاحت کرنے کی اجازت دیتی ہے۔ غلطی سے غلط ڈیٹا کو ذخیرہ کرنے سے بچنے کے لیے ساخت اور توثیق شامل کریں۔
منگوز کے بغیر، آپ خام MongoDB سوالات لکھ رہے ہوں گے۔ منگوز آپ کو اپنے "فلیش کارڈ” ماڈل کی ایک بار وضاحت کرنے اور اس طرح کا ایک آسان طریقہ استعمال کرنے کی اجازت دیتا ہے: Flashcard.create() یا Flashcard.find().
ٹیل ونڈ سی ایس ایس
Tailwind ایک افادیت کا پہلا CSS فریم ورک ہے۔ اپنی مرضی کے مطابق سی ایس ایس لکھنے کے بجائے، اس طرح کی کلاس شامل کریں: rounded-xl یا bg-blue-500 براہ راست HTML سے۔
ٹیل ونڈ اسٹائلنگ کو تیز کرتا ہے اور ڈیزائن کی مستقل مزاجی کو برقرار رکھتا ہے۔ Next.js باکس سے باہر اس کی حمایت کرتا ہے۔
پروجیکٹ کی ترتیبات
مرحلہ 1: ایک Next.js پروجیکٹ بنائیں
ٹرمینل کھولیں اور چلائیں:
npx create-next-app@latest flash-cards --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm
جب اشارہ کیا جائے تو اگلا منتخب کریں۔
-
ٹائپ اسکرپٹ: ہاں
-
ESL لنٹ: ہاں
-
Tailwind CSS: ہاں
-
src/ڈائریکٹری: ہاں -
ایپ راؤٹر: ہاں
-
عرف حاصل کریں: @/*
یہ اس طرح ایک نیا فولڈر بنائے گا: flash-cards پہلے سے طے شدہ Next.js ایپ استعمال کریں۔
مرحلہ 2: منگوز انسٹال کریں۔
منگوز بطور ڈیفالٹ شامل نہیں ہے۔ انسٹال کرنے کے لیے نیچے دی گئی کمانڈ کو چلائیں۔
cd flash-cards
npm install mongoose
مرحلہ 3: MongoDB سیٹ اپ کریں۔
دو اختیارات ہیں:
اختیار A: ڈاکر (مقامی ترقی کے لیے تجویز کردہ)
نامی ایک فائل بنائیں docker-compose.yml منصوبے کی جڑ میں:
services:
mongodb:
image: mongo:7
container_name: flashstudy-mongodb
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
volumes:
mongodb_data:
پھر چلائیں:
docker compose up -d
یہ پس منظر میں MongoDB شروع کرے گا۔ ڈیٹا ڈوکر والیوم میں محفوظ ہوتا ہے، لہذا یہ برقرار رہتا ہے یہاں تک کہ اگر آپ کنٹینر کو روکتے ہیں۔
آپشن B: MongoDB Atlas (Cloud)
-
mongodb.com/cloud/atlas پر جائیں۔
-
ایک مفت اکاؤنٹ اور کلسٹر بنائیں
-
ایک ڈیٹا بیس صارف بنائیں اور کنکشن سٹرنگ حاصل کریں۔
-
اپنے IP کو نیٹ ورک تک رسائی کی فہرست میں شامل کریں۔
مرحلہ 4: ماحول کی فائل بنائیں
نام کی ایک فائل بنائیں۔ .env.local پروجیکٹ کی جڑ سے (اس فائل کو حفاظتی وجوہات کی بناء پر گٹ نے نظرانداز کیا ہے):
MONGODB_URI=mongodb://localhost:27017/flashcards
اگر آپ Atlas استعمال کر رہے ہیں تو اسے اپنی کنکشن سٹرنگ سے بدل دیں۔ مثال کے طور پر:
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/flashcards?retryWrites=true&w=majority
مرحلہ 5: فولڈر کی ساخت کو سمجھیں۔
سیٹ اپ کے بعد، پروجیکٹ اس طرح لگتا ہے:
flash-cards/
├── src/
│ ├── app/ # Pages and API routes
│ │ ├── api/ # Backend API endpoints
│ │ ├── subjects/ # Subject list and detail pages
│ │ ├── study/ # Study mode page
│ │ └── progress/ # Progress tracking page
│ ├── components/ # Reusable UI components
│ └── lib/ # Utilities and database code
│ ├── db.ts # MongoDB connection
│ └── models/ # Mongoose schemas
├── .env.local # Environment variables (you create this)
├── docker-compose.yml # Docker config for MongoDB
└── package.json
کہ app فولڈر Next.js ایپ راؤٹر استعمال کرتا ہے۔ ہر فولڈر ہے۔ page.tsx (UI) اور route.ts (API اختتامی نقطہ)۔ ہم قدم بہ قدم اس کی تعمیر کریں گے۔
عمارت کی خصوصیات
حصہ 1: MongoDB سے جڑنا
فلیش کارڈز کو ذخیرہ کرنے یا بازیافت کرنے سے پہلے، آپ کی درخواست کو MongoDB سے منسلک ہونا چاہیے۔
ہم اس کنکشن کو ہینڈل کرنے کے لیے ایک چھوٹا ڈیٹا بیس یوٹیلیٹی بنائیں گے۔ چونکہ Next.js ترقی کے دوران متعدد درخواستوں کو ہینڈل کر سکتا ہے اور ماڈیولز کو دوبارہ لوڈ کر سکتا ہے، اس لیے میں ہر بار API روٹ چلنے پر ایک نیا MongoDB کنکشن نہیں بنانا چاہتا۔ اس کے بجائے، یہ کنکشن کیش کرتا ہے اور جب بھی ممکن ہو انہیں دوبارہ استعمال کرتا ہے۔
پہلے db.ts فائلوں کے اندر src/lib ڈائریکٹری
import mongoose from "mongoose";
const MONGODB_URI = process.env.MONGODB_URI || "mongodb://localhost:27017/flashcards";
interface MongooseCache {
conn: typeof mongoose | null;
promise: Promise | null;
}
declare global {
var mongoose: MongooseCache | undefined;
}
let cached: MongooseCache = global.mongoose || { conn: null, promise: null };
if (!global.mongoose) {
global.mongoose = cached;
}
async function dbConnect(): Promise {
if (cached.conn) return cached.conn;
if (!cached.promise) {
cached.promise = mongoose.connect(MONGODB_URI, {
bufferCommands: false,
});
}
cached.conn = await cached.promise;
return cached.conn;
}
export default dbConnect;
ذیل میں اس کوڈ کی لائن بہ لائن وضاحت ہے۔
-
MONGODB_URI: کنکشن سٹرنگ پڑھیں۔.env.local. اگر سیٹ نہیں کیا گیا تو یہ واپس مقامی MongoDB پر آ جائے گا۔ -
MongooseCache: ٹائپ اسکرپٹ انٹرفیس جو کیشے کو بیان کرتا ہے: کنکشن(conn) یا ایک وعدہ جو آخر کار ہمیں ایک دے گا۔ -
global.mongoose: Next.js ترقی کے دوران ماڈیولز کو دوبارہ لوڈ کر سکتا ہے۔ استعمال کریںglobalڈپلیکیٹ کنکشن بنانے سے بچنے کے لیے دوبارہ لوڈز کے درمیان کیشے کو برقرار رکھتا ہے۔ -
dbConnect(): اگر پہلے سے جڑا ہوا ہو تو لوٹتا ہے۔ اگر نہیں، تو ایک بنائیں، اسے کیش کریں، اور اسے واپس کریں۔ تمام API روٹس کو بلایا جاتا ہے۔await dbConnect()ڈیٹا بیس کو چھونے سے پہلے۔
حصہ 2: ڈیٹا ماڈل کی وضاحت
اب جب کہ ہم اپنی ایپلیکیشن کو MongoDB سے جوڑ سکتے ہیں، آئیے اس ڈیٹا کی وضاحت کریں جسے ہم ڈیٹا بیس میں اسٹور کرنا چاہتے ہیں۔
فلیش کارڈ ایپس کو تین قسم کے ڈیٹا کی ضرورت ہوتی ہے:
-
مضامین: یہ حیاتیات 101 یا کیلکولس کی طرح ایک زمرہ ہے۔
-
فلیش کارڈ: موضوع سے متعلق سوالات اور جوابات۔
-
ترقی: ریکارڈ کرتا ہے کہ صارف نے کتنی اچھی طرح سے مطالعہ کیا۔
ہم ہر ڈیٹا کی قسم کی ساخت کی وضاحت کے لیے منگوز سکیما کا استعمال کرتے ہیں۔ ایک اسکیما ان فیلڈز کی وضاحت کرتا ہے جو ایک دستاویز پر مشتمل ہو سکتی ہے اور ڈیٹا کی اقسام کو ہر فیلڈ میں ہونا چاہیے۔
آئیے شروع کرتے ہیں۔ Subject ماڈل
موضوع ماڈل
موضوعات فلیش کارڈز کے زمروں کی نمائندگی کرتے ہیں۔ مثال کے طور پر، ایک طالب علم اس طرح کا موضوع بنا سکتا ہے: حیاتیات 101 اپنے بیالوجی فلیش کارڈز کو ترتیب دینے کے لیے ان کا استعمال کریں۔
ہر موضوع کا ایک نام، اختیاری تفصیل، اور UI میں موضوع کو ظاہر کرتے وقت استعمال کرنے کے لیے ایک رنگ ہوتا ہے۔
پہلے models اندرونی ڈائریکٹری src/lib اگر آپ کے پاس پہلے ہی نہیں ہے۔ پھر Subject.ts اس میں
import mongoose, { Schema, model, models } from "mongoose";
export interface ISubject {
_id: string;
name: string;
description?: string;
color: string;
createdAt: Date;
updatedAt: Date;
}
const SubjectSchema = new Schema(
{
name: { type: String, required: true },
description: { type: String },
color: { type: String, default: "#6366f1" },
},
{ timestamps: true }
);
export default models.Subject || model("Subject", SubjectSchema);
اس کوڈ میں:
-
ISubject: ٹائپ اسکرپٹ انٹرفیس۔ بیان کرتا ہے کہ آپ کی ایپ میں سبجیکٹ آبجیکٹ کیسا لگتا ہے۔ -
SubjectSchema– منگوز سکیما۔nameاگرچہ ضرورت ہے،descriptionاورcolorاختیاریcolorپہلے سے طے شدہ جامنی ہیکساڈیسیمل ہے۔ -
timestamps: true: منگوز خود بخود شامل کرتا ہے۔createdAtاورupdatedAtتمام دستاویزات پر۔ -
models.Subject || model(...): ماڈیولز کو ترقی کے دوران دوبارہ لوڈ کیا جا سکتا ہے۔ اس طرح، اگر آپ کے پاس موجودہ ماڈل ہے، تو آپ اسے دوبارہ استعمال کر سکتے ہیں اور "ماڈل پہلے سے طے شدہ” غلطیوں سے بچ سکتے ہیں۔
فلیش کارڈ ماڈل
فلیش کارڈز ایک موضوع سے تعلق رکھتے ہیں اور ان کے سامنے سوالات اور پیچھے جوابات ہوتے ہیں۔
اگلا Flashcard ماڈل اندر src/lib/modelsنام کی ایک فائل بنائیں۔ Flashcard.ts اور نیچے کوڈ شامل کریں۔
import mongoose, { Schema, model, models } from "mongoose";
export interface IFlashcard {
_id: string;
subjectId: string;
front: string;
back: string;
createdAt: Date;
updatedAt: Date;
}
const FlashcardSchema = new Schema(
{
subjectId: { type: Schema.Types.ObjectId, ref: "Subject", required: true },
front: { type: String, required: true },
back: { type: String, required: true },
},
{ timestamps: true }
);
export default models.Flashcard || model("Flashcard", FlashcardSchema);
اس کوڈ میں:
-
subjectId: ایک موضوع کے طور پر ایک موضوع سے مراد._id.ref: "Subject"منگوز کو اس فیلڈ کو آباد کرنے کی اجازت دیتا ہے (درآمد کرتے وقت ID کو مکمل موضوع آبجیکٹ سے تبدیل کرنا)۔ -
frontاورback: سوال و جواب کا متن۔
ترقی ماڈل
جیسا کہ صارفین مطالعہ کرتے ہیں، انہیں ریکارڈ کرنا چاہیے کہ آیا انہوں نے ہر فلیش کارڈ پر صحیح جواب دیا یا غلط۔ ہم اس معلومات کو اپنے ڈیش بورڈ پر پیشرفت ظاہر کرنے کے لیے استعمال کریں گے۔
اگلا Progress ماڈل اندر src/lib/modelsنام کی ایک فائل بنائیں۔ Progress.ts اور نیچے کوڈ شامل کریں۔
import mongoose, { Schema, model, models } from "mongoose";
export interface IProgress {
_id: string;
flashcardId: string;
subjectId: string;
correct: boolean;
reviewedAt: Date;
}
const ProgressSchema = new Schema(
{
flashcardId: { type: Schema.Types.ObjectId, ref: "Flashcard", required: true },
subjectId: { type: Schema.Types.ObjectId, ref: "Subject", required: true },
correct: { type: Boolean, required: true },
reviewedAt: { type: Date, default: Date.now },
},
{ timestamps: true }
);
export default models.Progress || model("Progress", ProgressSchema);
اس کوڈ میں:
-
correct:trueاگر صارف جواب جانتا ہے۔falseاگر نہیں. -
reviewedAt: وہ وقت جب جائزہ لیا گیا۔ ہم اسے مستقبل کی خصوصیات کے لیے استعمال کریں گے جیسے چھانٹنا اور وقفہ دہرانا۔
ٹاپک API – فہرست بنائیں اور بنائیں
اب جب کہ ہم نے اپنے ڈیٹا ماڈل کی وضاحت کر دی ہے، آئیے ایک API روٹ بنائیں جو ہماری ایپلی کیشن کو اس ڈیٹا کو استعمال کرنے کی اجازت دیتا ہے۔
API روٹ فرنٹ اینڈ سے درخواستوں کو ہینڈل کرتا ہے اور MongoDB کے ساتھ بات چیت کرتا ہے۔ اس سیکشن میں، آپ عنوانات اور فلیش کارڈز بنانے اور بازیافت کرنے کا راستہ بنائیں گے۔
آئیے ٹاپک API کے ساتھ شروع کرتے ہیں۔ یہ راستہ دو کاموں کی حمایت کرتا ہے:
اندر src/app/api/subjectsنام کی ایک فائل بنائیں۔ route.ts اور نیچے کوڈ شامل کریں۔
import { NextResponse } from "next/server";
import dbConnect from "@/lib/db";
import Subject from "@/lib/models/Subject";
export async function GET() {
try {
await dbConnect();
const subjects = await Subject.find({}).sort({ createdAt: -1 });
return NextResponse.json(subjects);
} catch (error) {
console.error("Error fetching subjects:", error);
return NextResponse.json(
{ error: "Failed to fetch subjects" },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
try {
await dbConnect();
const body = await request.json();
const { name, description, color } = body;
if (!name) {
return NextResponse.json(
{ error: "Subject name is required" },
{ status: 400 }
);
}
const subject = await Subject.create({
name,
description: description || "",
color: color || "#6366f1",
});
return NextResponse.json(subject);
} catch (error) {
console.error("Error creating subject:", error);
return NextResponse.json(
{ error: "Failed to create subject" },
{ status: 500 }
);
}
}
اس کوڈ میں:
-
GET: سب سے پہلے سب سے حالیہ کے لحاظ سے تمام موضوعات کو ترتیب دیں۔find({})اس کا مطلب ہے "سب تلاش کریں”۔ JSON کے بطور لوٹتا ہے۔ -
POST: ایک نیا موضوع بنائیں۔ پڑھیںname,descriptionاورcolorدرخواست کے جسم میں۔ ہم اس کی تصدیق کرتے ہیں۔nameیہ موجود ہے۔ استعمال کریںSubject.create()اسے MongoDB میں محفوظ کریں۔ تخلیق کردہ موضوع لوٹاتا ہے۔ -
status: 400: غلط درخواست (لاپتہ ڈیٹا)۔status: 500: سرور کی خرابی (جیسے ڈیٹا بیس کی خرابی)۔
Flashcards API – فہرست بنائیں اور بنائیں
اگلا، آئیے فلیش کارڈز کے ساتھ کام کرنے کے لیے ایک API روٹ بنائیں۔ یہ راستہ آپ کو موجودہ فلیش کارڈز کو براؤز کرنے اور نئے بنانے کی اجازت دیتا ہے۔
اندر src/app/api/flashcardsنام کی ایک فائل بنائیں۔ route.ts ایک کوڈ بلاک شامل کریں۔
import { NextResponse } from "next/server";
import dbConnect from "@/lib/db";
import Flashcard from "@/lib/models/Flashcard";
export async function GET(request: Request) {
try {
await dbConnect();
const { searchParams } = new URL(request.url);
const subjectId = searchParams.get("subjectId");
const query = subjectId ? { subjectId } : {};
const flashcards = await Flashcard.find(query)
.populate("subjectId", "name color")
.sort({ createdAt: -1 });
return NextResponse.json(flashcards);
} catch (error) {
console.error("Error fetching flashcards:", error);
return NextResponse.json(
{ error: "Failed to fetch flashcards" },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
try {
await dbConnect();
const body = await request.json();
const { subjectId, front, back } = body;
if (!subjectId || !front || !back) {
return NextResponse.json(
{ error: "Subject, front, and back are required" },
{ status: 400 }
);
}
const flashcard = await Flashcard.create({
subjectId,
front,
back,
});
const populated = await Flashcard.findById(flashcard._id).populate(
"subjectId",
"name color"
);
return NextResponse.json(populated);
} catch (error) {
console.error("Error creating flashcard:", error);
return NextResponse.json(
{ error: "Failed to create flashcard" },
{ status: 500 }
);
}
}
اس کوڈ میں:
-
searchParams.get("subjectId"): کے لیےGET /api/flashcards?subjectId=abc123موضوع کے لحاظ سے فلٹر کریں۔ اگر نہیںsubjectIdتمام فلیش کارڈز واپس کرتا ہے۔ -
.populate("subjectId", "name color"): خام آئی ڈی کو موضوع آبجیکٹ سے بدلتا ہے، لیکن صرف اسے شامل کرتا ہے۔nameاورcolor. آپ اپنے UI میں مضامین کے نام آسانی سے ڈسپلے کر سکتے ہیں۔ -
POST:ضرورت ہے۔subjectId,frontاورback. تخلیق کے بعد، فلیش کارڈز کو استعمال کرتے ہوئے دوبارہ درآمد کریں:populateلہذا، جواب میں مضمون کی تفصیلات شامل ہیں۔
حصہ 4: ترمیم اور حذف کریں (متحرک API پاتھ)
انفرادی مضامین اور فلیش کارڈز کے لیے، استعمال کریں: متحرک راستہ. Next.js میں [id] فولڈر کے نام میں ایک راستہ بنایا گیا ہے جو دیگر IDs کو سنبھال سکتا ہے۔ مثال کے طور پر /api/subjects/123 اور /api/subjects/456 آپ وہی راستہ اختیار کر سکتے ہیں۔
ٹاپک API پاتھ
آئیے انفرادی عنوانات کے لیے متحرک راستے بنا کر شروع کریں۔ یہ راستہ آپ کو موضوعات کو تلاش کرنے، اپ ڈیٹ کرنے یا حذف کرنے کی اجازت دیتا ہے۔
اندر src/app/api/subjects/[id]نام کی ایک فائل بنائیں۔ route.ts اور نیچے کوڈ شامل کریں۔
import { NextResponse } from "next/server";
import dbConnect from "@/lib/db";
import Subject from "@/lib/models/Subject";
import Flashcard from "@/lib/models/Flashcard";
import Progress from "@/lib/models/Progress";
export async function GET(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
await dbConnect();
const { id } = await params;
const subject = await Subject.findById(id);
if (!subject) {
return NextResponse.json({ error: "Subject not found" }, { status: 404 });
}
return NextResponse.json(subject);
} catch (error) {
console.error("Error fetching subject:", error);
return NextResponse.json(
{ error: "Failed to fetch subject" },
{ status: 500 }
);
}
}
export async function PUT(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
await dbConnect();
const { id } = await params;
const body = await request.json();
const { name, description, color } = body;
const subject = await Subject.findByIdAndUpdate(
id,
{ name, description, color },
{ new: true }
);
if (!subject) {
return NextResponse.json({ error: "Subject not found" }, { status: 404 });
}
return NextResponse.json(subject);
} catch (error) {
console.error("Error updating subject:", error);
return NextResponse.json(
{ error: "Failed to update subject" },
{ status: 500 }
);
}
}
export async function DELETE(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
await dbConnect();
const { id } = await params;
await Flashcard.deleteMany({ subjectId: id });
await Progress.deleteMany({ subjectId: id });
const subject = await Subject.findByIdAndDelete(id);
if (!subject) {
return NextResponse.json({ error: "Subject not found" }, { status: 404 });
}
return NextResponse.json({ message: "Subject deleted" });
} catch (error) {
console.error("Error deleting subject:", error);
return NextResponse.json(
{ error: "Failed to delete subject" },
{ status: 500 }
);
}
}
اس کوڈ میں:
-
params: Next.js 15+ میںparamsیہ ایک وعدہ ہے۔ ہمawaitجو آپ کو ملتا ہے۔{ id: "abc123" }. -
findByIdAndUpdate(id, updates, { new: true }): دستاویز کو اپ ڈیٹ کرتا ہے اور اپ ڈیٹ شدہ ورژن لوٹاتا ہے۔{ new: true }اس کا مطلب ہے "نئی دستاویز واپس کریں، پرانی دستاویز نہیں۔” -
DELETE: اگر آپ کسی مضمون کو حذف کرتے ہیں، تو اس موضوع کے فلیش کارڈز اور پیش رفت کے ریکارڈ بھی حذف کر دیے جائیں گے۔ دوسری صورت میں، یتیم ڈیٹا واقع ہو جائے گا.
فلیش کارڈز بذریعہ ID – درآمد کریں، اپ ڈیٹ کریں، حذف کریں۔
اب آئیے انفرادی فلیش کارڈز کے لیے متحرک راستے بناتے ہیں۔ یہ راستہ آپ کو فلیش کارڈز کو بازیافت کرنے، اپ ڈیٹ کرنے یا حذف کرنے کی اجازت دیتا ہے۔
اندر src/app/api/flashcards/[id]نام کی ایک فائل بنائیں۔ route.ts اور نیچے کوڈ شامل کریں۔
import { NextResponse } from "next/server";
import dbConnect from "@/lib/db";
import Flashcard from "@/lib/models/Flashcard";
export async function GET(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
await dbConnect();
const { id } = await params;
const flashcard = await Flashcard.findById(id).populate(
"subjectId",
"name color"
);
if (!flashcard) {
return NextResponse.json(
{ error: "Flashcard not found" },
{ status: 404 }
);
}
return NextResponse.json(flashcard);
} catch (error) {
console.error("Error fetching flashcard:", error);
return NextResponse.json(
{ error: "Failed to fetch flashcard" },
{ status: 500 }
);
}
}
export async function PUT(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
await dbConnect();
const { id } = await params;
const body = await request.json();
const { front, back } = body;
const flashcard = await Flashcard.findByIdAndUpdate(
id,
{ front, back },
{ new: true }
).populate("subjectId", "name color");
if (!flashcard) {
return NextResponse.json(
{ error: "Flashcard not found" },
{ status: 404 }
);
}
return NextResponse.json(flashcard);
} catch (error) {
console.error("Error updating flashcard:", error);
return NextResponse.json(
{ error: "Failed to update flashcard" },
{ status: 500 }
);
}
}
export async function DELETE(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
await dbConnect();
const { id } = await params;
const flashcard = await Flashcard.findByIdAndDelete(id);
if (!flashcard) {
return NextResponse.json(
{ error: "Flashcard not found" },
{ status: 404 }
);
}
return NextResponse.json({ message: "Flashcard deleted" });
} catch (error) {
console.error("Error deleting flashcard:", error);
return NextResponse.json(
{ error: "Failed to delete flashcard" },
{ status: 500 }
);
}
}
حصہ 5: پروگریس ٹریکنگ API
اگر کوئی صارف سیکھنے کے سیشن کے دوران فلیش کارڈ کو درست یا غلط کے طور پر نشان زد کرتا ہے، تو نتائج کو محفوظ کرنا ضروری ہے۔ ہم اس ڈیٹا کو ترقی کے اعدادوشمار کا حساب لگانے کے لیے بھی استعمال کرتے ہیں، جیسے کہ ہر مضمون کے لیے درست فیصد۔
اگلا، آئیے ترقی کو ٹریک کرنے کے لیے ایک API روٹ بنائیں۔ اندر src/app/api/progressنام کی ایک فائل بنائیں۔ route.ts اور نیچے کوڈ شامل کریں۔
import { NextResponse } from "next/server";
import dbConnect from "@/lib/db";
import Progress from "@/lib/models/Progress";
export async function GET(request: Request) {
try {
await dbConnect();
const { searchParams } = new URL(request.url);
const subjectId = searchParams.get("subjectId");
const query = subjectId ? { subjectId } : {};
const progress = await Progress.find(query).sort({ reviewedAt: -1 });
const stats = await Progress.aggregate([
{ $match: query },
{
$group: {
_id: "$subjectId",
total: { $sum: 1 },
correct: { $sum: { $cond: ["$correct", 1, 0] } },
},
},
]);
return NextResponse.json({ progress, stats });
} catch (error) {
console.error("Error fetching progress:", error);
return NextResponse.json(
{ error: "Failed to fetch progress" },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
try {
await dbConnect();
const body = await request.json();
const { flashcardId, subjectId, correct } = body;
if (!flashcardId || !subjectId || typeof correct !== "boolean") {
return NextResponse.json(
{ error: "flashcardId, subjectId, and correct are required" },
{ status: 400 }
);
}
const progress = await Progress.create({
flashcardId,
subjectId,
correct,
});
return NextResponse.json(progress);
} catch (error) {
console.error("Error recording progress:", error);
return NextResponse.json(
{ error: "Failed to record progress" },
{ status: 500 }
);
}
}
اس کوڈ میں:
-
aggregate: MongoDB کی مجموعی پائپ لائن۔ ہم گروہ در گروہsubjectIdجائزوں کی کل تعداد اور درست جوابات کی تعداد کا حساب لگائیں۔$cond: ["$correct", 1, 0]اس کا مطلب ہے "اگر درست ہو تو 1 کا اضافہ کریں، ورنہ 0 کا اضافہ کریں۔” -
stats: کچھ اس طرح لوٹاتا ہے:[{ _id: "subjectId123", total: 20, correct: 16 }]. فرنٹ اینڈ اسے موضوع کے لحاظ سے "80% درستگی” ظاہر کرنے کے لیے استعمال کرتا ہے۔
UI کا نفاذ
اب آئیے وہ صفحہ بنائیں جو صارف کو نظر آئے گا۔ ہم ری ایکٹ ہکس کا استعمال کرتے ہیںuseState, useEffect) ڈیٹا کا نظم کریں اور fetch API کو کال کریں۔
موضوع کا صفحہ
لوڈ ہونے پر، موضوع API سے حاصل کیا جاتا ہے۔ یہ دکھاتا ہے کہ نیا موضوع کیسے بنایا جائے۔ ہر موضوع ایک کارڈ ہے جو ایک تفصیلی صفحہ سے لنک کرتا ہے۔
بنیادی منطق:
-
useEffectجب نصب کیا جائے اور پکارا جائے تو ایک بار چلتا ہے۔fetch("/api/subjects"). -
اسٹائلائزڈ
onSubmitفون کالfetch("/api/subjects", { method: "POST", ... }). -
کامیابی کے ساتھ بننے کے بعد، ہم فارم کو صاف کرتے ہیں اور کال کرتے ہیں۔
fetchSubjects()فہرست کو دوبارہ تازہ کرنے کے لیے
// Simplified structure - see full code in src/app/subjects/page.tsx
const [subjects, setSubjects] = useState([]);
const [showForm, setShowForm] = useState(false);
useEffect(() => {
fetch("/api/subjects")
.then((res) => res.json())
.then((data) => setSubjects(data));
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
await fetch("/api/subjects", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, description, color }),
});
fetchSubjects();
};

موضوع کی تفصیلات کا صفحہ (فلیش کارڈز بنائیں اور ان میں ترمیم کریں)
یہ صفحہ ایک عنوان اور فلیش کارڈز دکھاتا ہے۔ صارفین نئے کارڈز شامل کر سکتے ہیں یا موجودہ کارڈز میں ترمیم/حذف کر سکتے ہیں۔ URL ہے: /subjects/[id]تو ہم useParams() موضوع کی شناخت حاصل کرنے کے لیے۔
بنیادی منطق:
-
useParams()ہمیں دیتا ہےidURL سے۔ -
ہم ماؤنٹ سے عنوانات اور فلیش کارڈز لیتے ہیں۔
-
"Flashcard شامل کریں” فارم ظاہر ہوگا۔ POST کے ذریعے جمع کروائیں۔
/api/flashcardsکے ساتھsubjectId,frontاورback. -
ہر کارڈ میں ترمیم اور حذف کے بٹن ہوتے ہیں۔ ان لائن فارم میں سوئچز میں ترمیم کریں، اور ڈیلیٹ کال واپس آجائے گی۔
DELETE /api/flashcards/[id].
// Creating a flashcard
const handleCreate = async (e) => {
e.preventDefault();
await fetch("/api/flashcards", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ subjectId: id, front, back }),
});
fetchFlashcards(); // Refresh
};
// Updating a flashcard
const handleUpdate = async (e) => {
e.preventDefault();
await fetch(`/api/flashcards/${editingId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ front: editFront, back: editBack }),
});
setEditingId(null);
fetchFlashcards();
};

تحقیقی صفحہ – پلٹائیں کارڈ
تحقیقی صفحات کی تین اہم حالتیں ہیں:
-
لے لو: صارف مطالعہ کے لیے موضوع کا انتخاب کرتا ہے۔
-
شروع کرنے کے لیے تیار ہیں۔: کارڈز کی تعداد کے ساتھ ‘Start Study’ دکھاتا ہے۔
-
مطالعہ: ایک وقت میں ایک کارڈ دکھائیں۔ صارف اسے پلٹانے کے لیے کلک کرتا ہے اور پھر اشارہ کرتا ہے کہ آیا یہ درست ہے یا غلط۔ اگلے کارڈ پر جائیں اور اپنی پیشرفت ریکارڈ کریں۔
حرکت پذیری پلٹائیں
ہم CSS 3D ٹرانسفارمز کا استعمال کرتے ہوئے ایک فلپ اینیمیشن بناتے ہیں۔ فلیش کارڈز کے دو چہرے ہوتے ہیں۔ سامنے سوالوں کے لیے ہے اور پیچھے جوابات کے لیے۔ جب flipped ہے trueکارڈ کنٹینر کو 180 ڈگری گھماتا ہے۔ ہم بھی استعمال کرتے ہیں backface-visibility: hidden اس بات کو یقینی بناتا ہے کہ گردش کے دوران صرف مناسب چہرے ہی نظر آئیں۔
اپنی فلپ اینیمیشن میں اسٹائلز شامل کرنے کے لیے، کھولیں: src/app/globals.css اور درج ذیل کوڈ شامل کریں:
/* Flashcard flip animation */
.perspective-1000 {
perspective: 1000px;
}
.preserve-3d {
transform-style: preserve-3d;
}
.backface-hidden {
backface-visibility: hidden;
}
/* Lined paper effect for the card background */
.lined-paper {
background-image: repeating-linear-gradient(
transparent,
transparent 27px,
#e5e7eb 27px,
#e5e7eb 28px
);
}
کہ lined-paper کلاس افقی سرمئی لکیریں (جیسے نوٹ بک پیپر) بنانے کے لیے دہرانے والے گریڈینٹ کا استعمال کرتی ہے۔ یہ فلیش کارڈز کو ایک مانوس، سیکھنے کے لیے دوستانہ احساس دیتا ہے۔
کارڈ کی ساخت:
{/* Front face - Question */}
{currentCard.front}
{/* Back face - Answer */}
{currentCard.back}
جب صارف کارڈ پر کلک کرتا ہے تو یہ بدل جاتا ہے۔ flipped. پیرنٹ گھماتا ہے اور صحیح طرف دکھاتا ہے۔

ریکارڈنگ جاری ہے۔
جب صارف "میں سمجھتا ہوں!” پر کلک کرتا ہے۔ یا "میں نہیں جانتا”، ہم:
-
پر پوسٹ کیا گیا:
/api/progressکے ساتھflashcardId,subjectIdاورcorrect. -
مقامی حیثیت کی تازہ کاری (
sessionCorrectیاsessionWrong) حقیقی وقت کے اعدادوشمار چیک کریں۔ -
اگلے کارڈ پر جائیں۔ ایک بار جب آپ تمام کارڈز مکمل کر لیں گے، ‘اسٹارٹ اسٹڈی’ اسکرین دوبارہ نمودار ہوگی۔
const handleKnow = () => {
recordProgress(true);
setFlipped(false);
if (currentIndex < flashcards.length - 1) {
setCurrentIndex((i) => i + 1);
} else {
setStudyStarted(false);
setCurrentIndex(0);
}
};
ترقی کا صفحہ
یہاں ہمیں موضوع اور پیش رفت کے اعداد و شمار ملتے ہیں۔ ہر موضوع کے لیے، اس کے اعدادوشمار (جائزوں کی کل تعداد، درست شمار) دیکھیں اور فیصد کا حساب لگائیں۔ آپ کو اوپری حصے میں مجموعی اعداد و شمار اور ذیل میں موضوعاتی خرابی نظر آئے گی۔
const getSubjectStats = (subjectId) => {
const stat = stats.find((s) => s._id === subjectId);
return stat
? {
total: stat.total,
correct: stat.correct,
pct: Math.round((stat.correct / stat.total) * 100),
}
: null;
};
اختیاری: قطار والا کاغذ اور پیپر کلپ کا آئیکن
آپ کے فلیش کارڈز کو مزید ٹچائل کا احساس دلانے کے لیے ایپ میں ایک قطار والا کاغذ کا اثر اور ایک پیپر کلپ کا آئیکن شامل ہے۔ ایک کلپ ایک سادہ SVG جزو ہے۔
// src/components/PaperclipIcon.tsx
export default function PaperclipIcon({ className }: { className?: string }) {
return (
);
}
اسے فلیش کارڈ کے اوپری مرکز میں رکھیں۔ کہ lined-paper لیپ ٹاپ کے اثر کے لیے کارڈ کے مواد والے حصے پر ایک کلاس لگائی گئی تھی۔
بہترین طرز عمل
کوڈ آرگنائزیشن
-
ماڈل کو
lib/models/: فی ماڈل ایک فائل۔ اپنا سکیما ایک جگہ پر رکھیں۔ -
API کا راستہ کو
app/api/: وسائل کے لحاظ سے گروپ (موضوع، فلیش کارڈز، پیشرفت)۔ استعمال کریں[id]متحرک راستوں کے لیے۔ -
دوبارہ قابل استعمال اجزاء:
PaperclipIconیہ اندر ہے۔components/. دہرائے جانے والے کسی بھی UI کے لیے اس پیٹرن کا استعمال کریں۔
ہینڈلنگ میں خرابی
-
API کا راستہ: ہمیشہ منطق کو لپیٹتا ہے۔
try/catch. مناسب اسٹیٹس کوڈ واپس کریں (خراب ان پٹ کے لیے 400، نہ ملنے پر 404، سرور کی خرابی کے لیے 500)۔ -
سامنے کا اختتام: چیک کریں۔
res.okاستعمال سے پہلےres.json(). صارف کو لوڈنگ اور خرابی کی حیثیت دکھائیں۔
کارکردگی کی تجاویز
-
ڈیٹا بیس کنکشن: کنکشن دوبارہ استعمال کریں (ہمارا
dbConnectیہ کرو)۔ جب بھی درخواست کی جائے تو رابطہ نہ کریں۔ -
تھوڑا سا بھریں۔: صرف
.populate()جس فیلڈ کی آپ کو ضرورت ہے۔ درج ذیل فیلڈز کی وضاحت کریں:.populate("subjectId", "name color"). -
لوڈ کی حیثیت: درآمد کے دوران اسپنر دکھائیں۔ لے آؤٹ کی تبدیلیوں کو روکتا ہے اور تاثرات فراہم کرتا ہے۔
نتیجہ
میں نے اس کا استعمال کرتے ہوئے ایک فل اسٹیک فلیش کارڈ ایپ بنائی:
-
Next.js ایپ اور API روٹس کے لیے
-
MongoDB + Mongoose مضامین، فلیش کارڈز اور پیش رفت کو بچانے کے لیے
-
رد عمل فارمز، فہرستوں اور فلپ اینیمیشنز کے ساتھ UI کے لیے
-
ٹیل ونڈ سی ایس ایس اسٹائل کے لیے
ممکنہ بہتری
اس ایپ کو بہتر بنانے کے لیے کئی فیچرز بنائے جا سکتے ہیں۔
سب سے پہلے، آپ تصدیق شامل کر سکتے ہیں۔ لاگ ان شامل کریں تاکہ ہر صارف کا اپنا موضوع اور کارڈ ہو۔ NextAuth.js یا کلرک پر غور کریں۔
دوسرا، آپ فاصلہ دہرانے کی فعالیت شامل کر سکتے ہیں۔ زیادہ سے زیادہ وقفوں پر کارڈز ڈسپلے کرنے کے لیے پروگریس ڈیٹا کا استعمال کرتا ہے (مثلاً وہ کارڈ جو اکثر غلط ہوتے ہیں)۔
اگلا، آپ اینیمیشنز شامل کر سکتے ہیں، جیسے کہ کارڈز کے درمیان ٹرانزیشن یا سیشن مکمل ہونے پر کنفیٹی اثرات۔
آپ موبائل ریسپانسیوینس بھی بنا سکتے ہیں۔ موجودہ ترتیب ڈیسک ٹاپ پر کام کرتی ہے، لیکن مطالعہ کا منظر موبائل فونز کے لیے بہتر بنایا جا سکتا ہے۔
آخر میں، برآمد/درآمد کی فعالیت مفید ہو سکتی ہے۔ صارف بیک اپ کے لیے فلیش کارڈز کو JSON یا CSV کے بطور ایکسپورٹ کر سکتے ہیں۔
اگلے اقدامات
مزید جانے کے لیے، چلائیں: npm run dev اور ایپ کو دریافت کریں۔ کچھ مضامین اور فلیش کارڈز شامل کریں، پھر سیکھنے کا موڈ آزمائیں۔
پھر اپنے ڈیٹا کا معائنہ کرنے کے لیے MongoDB Compass یا Atlas کھولیں۔ اپنے کوڈ کے ساتھ تجربہ کریں۔ رنگ تبدیل کریں، فیلڈز شامل کریں، اور فلپ اینیمیشن کو ایڈجسٹ کریں۔
مطالعہ کرنے میں مزہ آئے!