From 415cab89a2d1b9efc4651971cf8d01305ab05fc7 Mon Sep 17 00:00:00 2001 From: Divya Pahuja Date: Wed, 15 Oct 2025 23:57:00 +0530 Subject: [PATCH 1/4] Adding angular docs --- docs/angular/components.md | 80 ++++++++++++++ docs/angular/forms.md | 217 +++++++++++++++++++++++++++++++++++++ docs/angular/routing.md | 167 ++++++++++++++++++++++++++++ docs/angular/services.md | 119 ++++++++++++++++++++ docs/angular/setup.md | 56 ++++++++++ 5 files changed, 639 insertions(+) create mode 100644 docs/angular/components.md create mode 100644 docs/angular/forms.md create mode 100644 docs/angular/routing.md create mode 100644 docs/angular/services.md create mode 100644 docs/angular/setup.md diff --git a/docs/angular/components.md b/docs/angular/components.md new file mode 100644 index 0000000..52a0037 --- /dev/null +++ b/docs/angular/components.md @@ -0,0 +1,80 @@ +# Angular Components + +## What is a Component? +A component controls a patch of screen called a view. It consists of: +- A TypeScript class (component class) +- An HTML template +- Optional CSS styles + +## Creating a Component +```bash +ng generate component my-component +# or shorthand +ng g c my-component +``` + +## Component Structure +```typescript +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-my-component', + templateUrl: './my-component.component.html', + styleUrls: ['./my-component.component.css'] +}) +export class MyComponent { + // Component logic goes here + title = 'My Component'; +} +``` + +## Component Lifecycle Hooks +- `ngOnInit()`: Called after the first ngOnChanges +- `ngOnChanges()`: Called when input properties change +- `ngDoCheck()`: Custom change detection +- `ngAfterViewInit()`: Called after the view is initialized +- `ngOnDestroy()`: Cleanup just before Angular destroys the component + +## Component Communication + +### Parent to Child: @Input() +```typescript +// parent.component.html + + +// child.component.ts +@Input() message: string; +``` + +### Child to Parent: @Output() +```typescript +// child.component.ts +@Output() notify = new EventEmitter(); + +onClick() { + this.notify.emit('Button clicked!'); +} + +// parent.component.html + +``` + +## View Encapsulation +Angular supports three view encapsulation strategies: +- `ViewEncapsulation.Emulated` (default) +- `ViewEncapsulation.None` +- `ViewEncapsulation.ShadowDom` + +## Content Projection +```html + + +

Card Title

+

Card content goes here

+
+ + +
+ +
+``` diff --git a/docs/angular/forms.md b/docs/angular/forms.md new file mode 100644 index 0000000..f0e012f --- /dev/null +++ b/docs/angular/forms.md @@ -0,0 +1,217 @@ +# Angular Forms + +## Template-Driven Forms + +### Basic Setup +```typescript +// app.module.ts +import { FormsModule } from '@angular/forms'; + +@NgModule({ + imports: [ + FormsModule + ] +}) +``` + +### Basic Form +```html +
+
+ + +
+ +
+ + +
+ + +
+``` + +### Two-way Binding +```html + +``` + +## Reactive Forms + +### Setup +```typescript +// app.module.ts +import { ReactiveFormsModule } from '@angular/forms'; + +@NgModule({ + imports: [ + ReactiveFormsModule + ] +}) +``` + +### Basic Form +```typescript +// In component +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +export class LoginComponent { + loginForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.loginForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required, Validators.minLength(6)]] + }); + } + + onSubmit() { + if (this.loginForm.valid) { + console.log(this.loginForm.value); + } + } +} +``` + +```html +
+
+ + +
+ Email is required +
+
+ +
+ + +
+ + +
+``` + +### Form Arrays +```typescript +// In component +export class OrderFormComponent { + orderForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.orderForm = this.fb.group({ + items: this.fb.array([this.createItem()]) + }); + } + + createItem(): FormGroup { + return this.fb.group({ + name: ['', Validators.required], + quantity: [1, [Validators.required, Validators.min(1)]] + }); + } + + get items() { + return this.orderForm.get('items') as FormArray; + } + + addItem() { + this.items.push(this.createItem()); + } + + removeItem(index: number) { + this.items.removeAt(index); + } +} +``` + +### Custom Validators +```typescript +export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { + return (control: AbstractControl): {[key: string]: any} | null => { + const forbidden = nameRe.test(control.value); + return forbidden ? {forbiddenName: {value: control.value}} : null; + }; +} + +// Usage +this.heroForm = this.fb.group({ + name: ['', [ + Validators.required, + forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator + ]] +}); +``` + +### Dynamic Forms +```typescript +// In component +export class DynamicFormComponent { + form: FormGroup; + fields = [ + { type: 'text', name: 'firstName', label: 'First Name', required: true }, + { type: 'email', name: 'email', label: 'Email', required: true }, + { type: 'password', name: 'password', label: 'Password', required: true } + ]; + + constructor(private fb: FormBuilder) { + const group = {}; + this.fields.forEach(field => { + group[field.name] = ['', field.required ? Validators.required : []]; + }); + this.form = this.fb.group(group); + } +} +``` + +### Form Submission with HTTP +```typescript +// In component +export class UserFormComponent { + userForm: FormGroup; + + constructor(private fb: FormBuilder, private http: HttpClient) { + this.userForm = this.fb.group({ + name: ['', Validators.required], + email: ['', [Validators.required, Validators.email]] + }); + } + + onSubmit() { + if (this.userForm.valid) { + this.http.post('/api/users', this.userForm.value).subscribe( + response => console.log('Success!', response), + error => console.error('Error!', error) + ); + } + } +} +``` + +### Cross-Field Validation +```typescript +// In component +this.form = this.fb.group({ + password: ['', [Validators.required]], + confirmPassword: ['', [Validators.required]] +}, { validator: this.passwordMatchValidator }); + +passwordMatchValidator(g: FormGroup) { + return g.get('password').value === g.get('confirmPassword').value + ? null : { 'mismatch': true }; +} +``` + +### Form Status Changes +```typescript +// In component +ngOnInit() { + this.form.statusChanges.subscribe(status => { + console.log('Form status:', status); + }); + + this.form.get('email').valueChanges.subscribe(value => { + console.log('Email changed to:', value); + }); +} +``` diff --git a/docs/angular/routing.md b/docs/angular/routing.md new file mode 100644 index 0000000..7466240 --- /dev/null +++ b/docs/angular/routing.md @@ -0,0 +1,167 @@ +# Angular Routing + +## Basic Setup + +```typescript +// app-routing.module.ts +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { HomeComponent } from './home/home.component'; +import { AboutComponent } from './about/about.component'; + +const routes: Routes = [ + { path: '', component: HomeComponent }, + { path: 'about', component: AboutComponent }, + { path: '**', redirectTo: '' } // Wildcard route for 404 +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } +``` + +## Route Parameters + +```typescript +// Route definition +{ path: 'product/:id', component: ProductDetailComponent } + +// Accessing in component +import { ActivatedRoute } from '@angular/router'; + +constructor(private route: ActivatedRoute) { + this.route.params.subscribe(params => { + this.productId = params['id']; + }); +} +``` + +## Child Routes + +```typescript +const routes: Routes = [ + { + path: 'products', + component: ProductsComponent, + children: [ + { path: '', component: ProductListComponent }, + { path: ':id', component: ProductDetailComponent }, + { path: ':id/edit', component: ProductEditComponent } + ] + } +]; +``` + +## Route Guards + +### CanActivate +```typescript +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + constructor(private authService: AuthService, private router: Router) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): boolean { + if (this.authService.isLoggedIn()) { + return true; + } + this.router.navigate(['/login']); + return false; + } +} + +// Usage +{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] } +``` + +### Resolve +```typescript +@Injectable({ providedIn: 'root' }) +export class ProductResolver implements Resolve { + constructor(private productService: ProductService) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + return this.productService.getProduct(route.paramMap.get('id')); + } +} + +// Usage +{ + path: 'product/:id', + component: ProductDetailComponent, + resolve: { + product: ProductResolver + } +} +``` + +## Lazy Loading + +```typescript +const routes: Routes = [ + { + path: 'admin', + loadChildren: () => import('./admin/admin.module') + .then(m => m.AdminModule) + } +]; +``` + +## Navigation + +```typescript +// In component +constructor(private router: Router) {} + +gotoProduct(id: number) { + this.router.navigate(['/product', id]); + // or with query params + this.router.navigate(['/products'], { + queryParams: { page: 1, search: 'angular' } + }); +} +``` + +## Route Events + +```typescript +constructor(private router: Router) { + this.router.events.pipe( + filter(event => event instanceof NavigationEnd) + ).subscribe((event: NavigationEnd) => { + console.log('Navigation ended:', event.url); + }); +} +``` + +## Route Animations + +```typescript +// In component +@Component({ + selector: 'app-root', + template: ` +
+ +
+ `, + animations: [ + trigger('routeAnimations', [ + transition('* <=> *', [ + style({ opacity: 0 }), + animate('300ms', style({ opacity: 1 })) + ]) + ]) + ] +}) +export class AppComponent { + prepareRoute(outlet: RouterOutlet) { + return outlet?.activatedRouteData?.['animation']; + } +} +``` diff --git a/docs/angular/services.md b/docs/angular/services.md new file mode 100644 index 0000000..ed74973 --- /dev/null +++ b/docs/angular/services.md @@ -0,0 +1,119 @@ +# Angular Services + +## What is a Service? +Services are singleton objects that provide specific functionality throughout the application. They are typically used for: +- Data fetching and sharing +- Business logic +- Logging +- Authentication + +## Creating a Service +```bash +ng generate service data +# or shorthand +ng g s data +``` + +## Basic Service Example +```typescript +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' // Makes it a singleton service +}) +export class DataService { + private data: any[] = []; + + constructor() { } + + getData(): any[] { + return this.data; + } + + addData(item: any): void { + this.data.push(item); + } +} +``` + +## Dependency Injection +```typescript +// In a component +constructor(private dataService: DataService) {} + +// In a module (if not using providedIn: 'root') +@NgModule({ + providers: [DataService] +}) +``` + +## HTTP Client +```typescript +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + private apiUrl = 'https://api.example.com'; + + constructor(private http: HttpClient) {} + + getItems(): Observable { + return this.http.get(`${this.apiUrl}/items`); + } + + addItem(item: any): Observable { + return this.http.post(`${this.apiUrl}/items`, item); + } +} +``` + +## HTTP Interceptors +```typescript +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + intercept(req: HttpRequest, next: HttpHandler): Observable> { + const token = localStorage.getItem('token'); + + if (token) { + const cloned = req.clone({ + headers: req.headers.set('Authorization', `Bearer ${token}`) + }); + return next.handle(cloned); + } + + return next.handle(req); + } +} +``` + +## Error Handling +```typescript +import { catchError } from 'rxjs/operators'; +import { throwError } from 'rxjs'; + +getItems(): Observable { + return this.http.get(`${this.apiUrl}/items`).pipe( + catchError(error => { + console.error('Error fetching items:', error); + return throwError('Something went wrong'); + }) + ); +} +``` + +## Service Lifecycle Hooks +- `ngOnInit()`: Not available in services +- `ngOnDestroy()`: Can be used to clean up subscriptions + +## Best Practices +- Keep services focused on a single responsibility +- Use `providedIn: 'root'` for singleton services +- Handle errors appropriately +- Unsubscribe from observables to prevent memory leaks +- Use interfaces for request/response types diff --git a/docs/angular/setup.md b/docs/angular/setup.md new file mode 100644 index 0000000..431c8b7 --- /dev/null +++ b/docs/angular/setup.md @@ -0,0 +1,56 @@ +# Angular Setup Guide + +## Prerequisites +- Node.js (v14 or later) +- npm (v6 or later) +- Angular CLI (latest stable version) + +## Installation + +1. Install Angular CLI globally: +```bash +npm install -g @angular/cli +``` + +2. Verify installation: +```bash +ng version +``` + +## Creating a New Project +```bash +ng new my-angular-app +cd my-angular-app +``` + +## Project Structure +``` +my-angular-app/ +├── src/ +│ ├── app/ # Your application code +│ ├── assets/ # Static assets +│ ├── environments/ # Environment configurations +│ └── index.html # Main HTML file +├── angular.json # Angular CLI configuration +└── package.json # Project dependencies +``` + +## Development Server +```bash +# Start development server +ng serve + +# Open in browser +ng serve --open +``` + +## Building for Production +```bash +ng build --prod +``` + +## Common Commands +- `ng generate component component-name` - Generate a new component +- `ng generate service service-name` - Generate a new service +- `ng test` - Run unit tests +- `ng e2e` - Run end-to-end tests From ede0f77b6bcaa0b70f1d579296bec6e0140f39c5 Mon Sep 17 00:00:00 2001 From: Divya Pahuja Date: Thu, 16 Oct 2025 00:15:33 +0530 Subject: [PATCH 2/4] adding mongodb docs --- docs/mongodb/aggregation.md | 375 ++++++++++++++++++++++++ docs/mongodb/crud.md | 262 +++++++++++++++++ docs/mongodb/mongoose.md | 536 ++++++++++++++++++++++++++++++++++ docs/mongodb/schema-design.md | 318 ++++++++++++++++++++ docs/mongodb/setup.md | 211 +++++++++++++ 5 files changed, 1702 insertions(+) create mode 100644 docs/mongodb/aggregation.md create mode 100644 docs/mongodb/crud.md create mode 100644 docs/mongodb/mongoose.md create mode 100644 docs/mongodb/schema-design.md create mode 100644 docs/mongodb/setup.md diff --git a/docs/mongodb/aggregation.md b/docs/mongodb/aggregation.md new file mode 100644 index 0000000..a380221 --- /dev/null +++ b/docs/mongodb/aggregation.md @@ -0,0 +1,375 @@ +# MongoDB Aggregation Framework + +## Aggregation Pipeline Stages + +### $match +Filters documents to pass only matching documents to the next stage. + +```javascript +db.orders.aggregate([ + { $match: { status: "completed", total: { $gt: 100 } } } +]); +``` + +### $group +Groups documents by specified identifier expressions. + +```javascript +db.orders.aggregate([ + { + $group: { + _id: "$customerId", + totalSpent: { $sum: "$total" }, + averageOrder: { $avg: "$total" }, + orderCount: { $sum: 1 } + } + } +]); +``` + +### $sort +Sorts all input documents and returns them in sorted order. + +```javascript +db.products.aggregate([ + { $sort: { price: -1, name: 1 } } +]); +``` + +### $project +Reshapes each document by including, excluding, or adding new fields. + +```javascript +db.users.aggregate([ + { + $project: { + name: 1, + email: 1, + yearJoined: { $year: "$joinDate" }, + _id: 0 + } + } +]); +``` + +### $lookup +Performs a left outer join to another collection. + +```javascript +db.orders.aggregate([ + { + $lookup: { + from: "customers", + localField: "customerId", + foreignField: "_id", + as: "customer" + } + }, + { $unwind: "$customer" } +]); +``` + +### $unwind +Deconstructs an array field to output one document per array element. + +```javascript +db.orders.aggregate([ + { $unwind: "$items" }, + { $group: { _id: "$items.productId", total: { $sum: "$items.quantity" } } } +]); +``` + +### $facet +Processes multiple aggregation pipelines within a single stage. + +```javascript +db.products.aggregate([ + { + $facet: { + categories: [ + { $unwind: "$categories" }, + { $group: { _id: "$categories", count: { $sum: 1 } } }, + { $sort: { count: -1 } } + ], + priceStats: [ + { $match: { price: { $exists: true } } }, + { + $group: { + _id: null, + average: { $avg: "$price" }, + min: { $min: "$price" }, + max: { $max: "$price" } + } + } + ] + } + } +]); +``` + +## Aggregation Operators + +### Arithmetic Operators +- `$add`: Adds numbers together +- `$subtract`: Subtracts two numbers +- `$multiply`: Multiplies numbers together +- `$divide`: Divides one number by another +- `$mod`: Returns the remainder of a division + +```javascript +db.orders.aggregate([ + { + $project: { + subtotal: 1, + tax: 1, + total: { + $add: ["$subtotal", "$tax"] + }, + discount: { + $multiply: ["$subtotal", 0.1] // 10% discount + }, + finalTotal: { + $subtract: [ + { $add: ["$subtotal", "$tax"] }, + { $multiply: ["$subtotal", 0.1] } + ] + } + } + } +]); +``` + +### String Operators +- `$concat`: Concatenates strings +- `$substr`: Returns a substring +- `$toLower` / `$toUpper`: Converts to lowercase/uppercase +- `$trim`: Removes whitespace + +```javascript +db.users.aggregate([ + { + $project: { + fullName: { + $concat: ["$firstName", " ", "$lastName"] + }, + emailLower: { $toLower: "$email" }, + username: { $substr: ["$email", 0, { $indexOfBytes: ["$email", "@"] }] } + } + } +]); +``` + +### Date Operators +- `$year` / `$month` / `$dayOfMonth`: Extracts date parts +- `$dateToString`: Formats a date as a string +- `$dateFromString`: Converts a date string to a date object + +```javascript +db.orders.aggregate([ + { + $project: { + orderDate: 1, + year: { $year: "$orderDate" }, + month: { $month: "$orderDate" }, + day: { $dayOfMonth: "$orderDate" }, + formattedDate: { + $dateToString: { + format: "%Y-%m-%d", + date: "$orderDate" + } + } + } + } +]); +``` + +### Conditional Operators +- `$cond`: Ternary operator +- `$ifNull`: Returns a value if null, otherwise returns the field value +- `$switch`: Case/when/default logic + +```javascript +db.products.aggregate([ + { + $project: { + name: 1, + price: 1, + priceCategory: { + $switch: { + branches: [ + { case: { $lt: ["$price", 50] }, then: "Budget" }, + { case: { $lt: ["$price", 200] }, then: "Standard" }, + { case: { $lt: ["$price", 1000] }, then: "Premium" } + ], + default: "Luxury" + } + }, + discountPrice: { + $cond: { + if: { $gt: ["$quantity", 10] }, + then: { $multiply: ["$price", 0.9] }, // 10% discount + else: "$price" + } + }, + description: { $ifNull: ["$description", "No description available"] } + } + } +]); +``` + +## Performance Optimization + +### Index Usage +```javascript +// Create an index for the aggregation +// db.orders.createIndex({ status: 1, orderDate: -1 }); + +db.orders.aggregate([ + { $match: { status: "completed", orderDate: { $gte: new Date("2023-01-01") } } }, + { $sort: { orderDate: -1 } }, + { $limit: 100 } +]); +``` + +### $lookup Optimization +```javascript +// Instead of this (inefficient with large collections): +db.orders.aggregate([ + { $lookup: { from: "products", localField: "productId", foreignField: "_id", as: "product" } }, + { $unwind: "$product" }, + { $match: { "product.category": "Electronics" } } +]); + +// Do this (more efficient): +db.orders.aggregate([ + { + $lookup: { + from: "products", + let: { productId: "$productId" }, + pipeline: [ + { $match: { $expr: { $eq: ["$_id", "$$productId"] }, category: "Electronics" } } + ], + as: "product" + } + }, + { $unwind: "$product" } +]); +``` + +### $redact +Controls document traversal during processing. + +```javascript +db.employees.aggregate([ + { + $redact: { + $cond: { + if: { $eq: ["$department", "HR"] }, + then: "$$DESCEND", + else: "$$PRUNE" + } + } + } +]); +``` + +## Aggregation with $graphLookup + +### Recursive Lookup +```javascript +db.employees.aggregate([ + { + $match: { name: "John Doe" } + }, + { + $graphLookup: { + from: "employees", + startWith: "$reportsTo", + connectFromField: "reportsTo", + connectToField: "_id", + as: "managementChain", + maxDepth: 10, + depthField: "level" + } + } +]); +``` + +## Aggregation with $merge + +### Output to a Collection +```javascript +db.orders.aggregate([ + { $match: { status: "completed" } }, + { + $group: { + _id: { $dateToString: { format: "%Y-%m", date: "$orderDate" } }, + totalSales: { $sum: "$total" }, + orderCount: { $sum: 1 } + } + }, + { $sort: { _id: 1 } }, + { + $merge: { + into: "monthlySales", + on: "_id", + whenMatched: "replace", + whenNotMatched: "insert" + } + } +]); +``` + +## Aggregation with $setWindowFields (MongoDB 5.0+) + +### Moving Averages and Rankings +```javascript +db.sales.aggregate([ + { $match: { date: { $gte: new Date("2023-01-01") } } }, + { $sort: { productId: 1, date: 1 } }, + { + $setWindowFields: { + partitionBy: "$productId", + sortBy: { date: 1 }, + output: { + movingAvg: { + $avg: "$amount", + window: { + documents: ["unbounded", "current"] + } + }, + salesRank: { + $denseRank: {} + } + } + } + } +]); +``` + +## Aggregation with $densify + +### Filling Gaps in Time Series Data +```javascript +db.sensorReadings.aggregate([ + { $match: { sensorId: "A1", timestamp: { $gte: new Date("2023-01-01") } } }, + { + $densify: { + field: "timestamp", + range: { + step: 1, + unit: "hour", + bounds: [new Date("2023-01-01"), new Date("2023-01-02")] + } + } + }, + { + $fill: { + output: { + value: { method: "linear" }, + status: { value: "interpolated" } + } + } + } +]); +``` diff --git a/docs/mongodb/crud.md b/docs/mongodb/crud.md new file mode 100644 index 0000000..605d144 --- /dev/null +++ b/docs/mongodb/crud.md @@ -0,0 +1,262 @@ +# MongoDB CRUD Operations + +## Basic CRUD Operations + +### 1. Create Operations + +#### Insert a Single Document +```javascript +// Using insertOne() +db.collection('users').insertOne({ + name: 'John Doe', + email: 'john@example.com', + age: 30, + status: 'active' +}); +``` + +#### Insert Multiple Documents +```javascript +// Using insertMany() +db.collection('users').insertMany([ + { name: 'Alice', age: 25, status: 'active' }, + { name: 'Bob', age: 35, status: 'inactive' }, + { name: 'Charlie', age: 40, status: 'pending' } +]); +``` + +### 2. Read Operations + +#### Find All Documents +```javascript +// Using find() with no query +const allUsers = await db.collection('users').find({}).toArray(); +``` + +#### Find with Query +```javascript +// Find with equality condition +const activeUsers = await db.collection('users') + .find({ status: 'active' }) + .toArray(); + +// Using comparison operators +const adultUsers = await db.collection('users') + .find({ age: { $gt: 18 } }) + .toArray(); +``` + +#### Find One Document +```javascript +const user = await db.collection('users').findOne({ email: 'john@example.com' }); +``` + +#### Projection (Selecting Fields) +```javascript +// Include only name and email fields +const users = await db.collection('users') + .find({}, { projection: { name: 1, email: 1, _id: 0 } }) + .toArray(); +``` + +#### Sorting +```javascript +// Sort by age in descending order +const sortedUsers = await db.collection('users') + .find() + .sort({ age: -1 }) + .toArray(); +``` + +#### Limiting and Skipping +```javascript +// Pagination: Get second page with 10 items per page +const page = 2; +const limit = 10; +const users = await db.collection('users') + .find() + .skip((page - 1) * limit) + .limit(limit) + .toArray(); +``` + +### 3. Update Operations + +#### Update a Single Document +```javascript +// Using updateOne() +const result = await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $set: { status: 'inactive', updatedAt: new Date() } } +); + +console.log(`${result.matchedCount} document(s) matched the filter`); +console.log(`${result.modifiedCount} document(s) was/were updated`); +``` + +#### Update Multiple Documents +```javascript +// Using updateMany() +const result = await db.collection('users').updateMany( + { status: 'pending' }, + { $set: { status: 'active', updatedAt: new Date() } } +); +``` + +#### Increment a Value +```javascript +// Increment age by 1 +await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $inc: { age: 1 } } +); +``` + +#### Add to Array +```javascript +// Add to array field +await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $push: { tags: 'premium' } } +); + +// Add to array if not exists +await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $addToSet: { tags: 'premium' } } +); +``` + +### 4. Delete Operations + +#### Delete a Single Document +```javascript +const result = await db.collection('users').deleteOne({ email: 'john@example.com' }); +console.log(`${result.deletedCount} document(s) was/were deleted`); +``` + +#### Delete Multiple Documents +```javascript +const result = await db.collection('users').deleteMany({ status: 'inactive' }); +``` + +#### Delete All Documents (but keep the collection) +```javascript +const result = await db.collection('users').deleteMany({}); +``` + +## Bulk Operations + +### Ordered Bulk Operations +```javascript +const bulk = db.collection('users').initializeOrderedBulkOp(); +bulk.insert({ name: 'User 1' }); +bulk.insert({ name: 'User 2' }); +bulk.find({ name: 'User 1' }).update({ $set: { status: 'active' } }); +const result = await bulk.execute(); +``` + +### Unordered Bulk Operations +```javascript +const bulk = db.collection('users').initializeUnorderedBulkOp(); +bulk.insert({ name: 'User 3' }); +bulk.insert({ name: 'User 4' }); +const result = await bulk.execute(); +``` + +## Find and Modify + +### Find One and Update +```javascript +const result = await db.collection('users').findOneAndUpdate( + { email: 'john@example.com' }, + { $set: { lastLogin: new Date() } }, + { returnDocument: 'after' } // Return the updated document +); +``` + +### Find One and Delete +```javascript +const result = await db.collection('users').findOneAndDelete( + { email: 'john@example.com' } +); +``` + +## Indexes + +### Create Index +```javascript +// Single field index +await db.collection('users').createIndex({ email: 1 }); + +// Compound index +await db.collection('users').createIndex({ status: 1, createdAt: -1 }); + +// Unique index +await db.collection('users').createIndex({ email: 1 }, { unique: true }); + +// Text index for text search +await db.collection('articles').createIndex({ content: 'text' }); +``` + +### List Indexes +```javascript +const indexes = await db.collection('users').indexes(); +console.log(indexes); +``` + +### Drop Index +```javascript +await db.collection('users').dropIndex('email_1'); +``` + +## Transactions (MongoDB 4.0+) + +```javascript +const session = client.startSession(); + +try { + await session.withTransaction(async () => { + // Perform operations in the transaction + const user = await db.collection('users').findOne( + { email: 'john@example.com' }, + { session } + ); + + if (!user) { + throw new Error('User not found'); + } + + await db.collection('orders').insertOne( + { + userId: user._id, + items: ['item1', 'item2'], + total: 100, + status: 'pending' + }, + { session } + ); + + await db.collection('users').updateOne( + { _id: user._id }, + { $inc: { orderCount: 1 } }, + { session } + ); + }); +} finally { + await session.endSession(); +} +``` + +## Best Practices + +1. **Use Projections** to retrieve only the fields you need +2. **Create Indexes** for frequently queried fields +3. **Use $lookup** instead of multiple queries when possible +4. **Batch Operations** for bulk inserts/updates +5. **Use Cursors** for large result sets +6. **Monitor Performance** using explain() +7. **Use Transactions** for multi-document operations that need to be atomic +8. **Set Write Concerns** appropriately for your use case +9. **Use TTL Indexes** for expiring data +10. **Regularly Compact** collections with high churn diff --git a/docs/mongodb/mongoose.md b/docs/mongodb/mongoose.md new file mode 100644 index 0000000..aaa0e5f --- /dev/null +++ b/docs/mongodb/mongoose.md @@ -0,0 +1,536 @@ +# Mongoose ODM + +## Installation +```bash +npm install mongoose +``` + +## Basic Setup +```javascript +const mongoose = require('mongoose'); + +// Connect to MongoDB +mongoose.connect('mongodb://localhost:27017/mydatabase', { + useNewUrlParser: true, + useUnifiedTopology: true +}); + +// Get the default connection +const db = mongoose.connection; + +// Event listeners +db.on('error', console.error.bind(console, 'MongoDB connection error:')); +db.once('open', () => { + console.log('Connected to MongoDB'); +}); +``` + +## Defining a Schema +```javascript +const { Schema } = mongoose; + +const userSchema = new Schema({ + username: { + type: String, + required: true, + unique: true, + trim: true, + minlength: 3 + }, + email: { + type: String, + required: true, + unique: true, + trim: true, + lowercase: true, + match: [/^\S+@\S+\.\S+$/, 'Please use a valid email address'] + }, + password: { + type: String, + required: true, + minlength: 6, + select: false // Don't return password by default + }, + role: { + type: String, + enum: ['user', 'admin'], + default: 'user' + }, + createdAt: { + type: Date, + default: Date.now + }, + updatedAt: { + type: Date, + default: Date.now + }, + profile: { + firstName: String, + lastName: String, + bio: String, + avatar: String + }, + tags: [String], + isActive: { + type: Boolean, + default: true + } +}, { + timestamps: true, // Adds createdAt and updatedAt fields + toJSON: { virtuals: true }, // Include virtuals when converting to JSON + toObject: { virtuals: true } // Include virtuals when converting to objects +}); + +// Virtual for full name +userSchema.virtual('fullName').get(function() { + return `${this.profile?.firstName || ''} ${this.profile?.lastName || ''}`.trim(); +}); + +// Indexes +userSchema.index({ email: 1 }, { unique: true }); +userSchema.index({ 'profile.firstName': 'text', 'profile.lastName': 'text' }); + +// Pre-save hook to hash password +userSchema.pre('save', async function(next) { + if (!this.isModified('password')) return next(); + + try { + const salt = await bcrypt.genSalt(10); + this.password = await bcrypt.hash(this.password, salt); + next(); + } catch (error) { + next(error); + } +}); + +// Instance method +userSchema.methods.comparePassword = async function(candidatePassword) { + return await bcrypt.compare(candidatePassword, this.password); +}; + +// Static method +userSchema.statics.findByEmail = function(email) { + return this.findOne({ email }); +}; + +// Query helper +userSchema.query.byName = function(name) { + return this.where({ name: new RegExp(name, 'i') }); +}; + +const User = mongoose.model('User', userSchema); +module.exports = User; +``` + +## CRUD Operations + +### Create +```javascript +// Create and save +const user = new User({ + username: 'johndoe', + email: 'john@example.com', + password: 'securepassword123', + role: 'user' +}); + +await user.save(); + +// Create in one step +const newUser = await User.create({ + username: 'janedoe', + email: 'jane@example.com', + password: 'securepassword456' +}); +``` + +### Read +```javascript +// Find all users +const users = await User.find({}); + +// Find one user +const user = await User.findOne({ email: 'john@example.com' }); + +// Find by ID +const user = await User.findById('507f1f77bcf86cd799439011'); + +// Find with conditions +const activeAdmins = await User.find({ + role: 'admin', + isActive: true +}).sort({ createdAt: -1 }); + +// Using query builder +const users = await User + .find({ role: 'user' }) + .where('createdAt').gt(oneYearAgo) + .sort('-createdAt') + .limit(10) + .select('username email createdAt') + .lean(); +``` + +### Update +```javascript +// Update by ID +const user = await User.findByIdAndUpdate( + '507f1f77bcf86cd799439011', + { $set: { isActive: false } }, + { new: true, runValidators: true } +); + +// Update one +await User.updateOne( + { email: 'john@example.com' }, + { $inc: { loginCount: 1 } } +); + +// Update many +await User.updateMany( + { role: 'user' }, + { $set: { lastNotified: new Date() } } +); +``` + +### Delete +```javascript +// Delete one +await User.deleteOne({ email: 'john@example.com' }); + +// Delete by ID +await User.findByIdAndDelete('507f1f77bcf86cd799439011'); + +// Delete many +await User.deleteMany({ isActive: false }); +``` + +## Middleware (Hooks) + +### Document Middleware +```javascript +userSchema.pre('save', function(next) { + this.updatedAt = new Date(); + next(); +}); + +userSchema.post('save', function(doc, next) { + console.log(`User ${doc._id} was saved`); + next(); +}); +``` + +### Query Middleware +```javascript +userSchema.pre('find', function() { + this.start = Date.now(); +}); + +userSchema.post('find', function(docs) { + console.log(`Query took ${Date.now() - this.start} ms`); +}); +``` + +### Aggregation Middleware +```javascript +userSchema.pre('aggregate', function() { + this.pipeline().unshift({ $match: { isActive: true } }); +}); +``` + +## Population (Joins) + +### Basic Population +```javascript +const postSchema = new Schema({ + title: String, + content: String, + author: { type: Schema.Types.ObjectId, ref: 'User' }, + comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }] +}); + +const Post = mongoose.model('Post', postSchema); + +// Populate author +const post = await Post.findById(postId).populate('author'); + +// Populate multiple fields +const post = await Post.findById(postId) + .populate('author') + .populate('comments'); + +// Populate with selected fields +const post = await Post.findById(postId).populate({ + path: 'author', + select: 'username email profile.firstName profile.lastName' +}); + +// Populate with conditions +const post = await Post.findById(postId).populate({ + path: 'comments', + match: { isApproved: true }, + options: { sort: { createdAt: -1 }, limit: 10 }, + select: 'content createdAt', + populate: { + path: 'author', + select: 'username' + } +}); +``` + +## Aggregation + +### Basic Aggregation +```javascript +const stats = await User.aggregate([ + { $match: { isActive: true } }, + { + $group: { + _id: '$role', + count: { $sum: 1 }, + averageAge: { $avg: '$age' } + } + }, + { $sort: { count: -1 } } +]); +``` + +### Text Search +```javascript +const results = await User.find( + { $text: { $search: 'john' } }, + { score: { $meta: 'textScore' } } +).sort({ score: { $meta: 'textScore' } }); +``` + +## Transactions + +### Basic Transaction +```javascript +const session = await mongoose.startSession(); +session.startTransaction(); + +try { + const user = await User.create([{ + username: 'newuser', + email: 'new@example.com', + password: 'password123' + }], { session }); + + await Profile.create([{ + userId: user[0]._id, + firstName: 'New', + lastName: 'User' + }], { session }); + + await session.commitTransaction(); + console.log('Transaction completed'); +} catch (error) { + await session.abortTransaction(); + console.error('Transaction aborted:', error); +} finally { + session.endSession(); +} +``` + +## Plugins + +### Creating a Plugin +```javascript +// plugins/timestamps.js +module.exports = function timestampsPlugin(schema) { + schema.add({ + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now } + }); + + schema.pre('save', function(next) { + this.updatedAt = new Date(); + next(); + }); +}; + +// Using the plugin +const timestamps = require('./plugins/timestamps'); +userSchema.plugin(timestamps); +``` + +## Validation + +### Custom Validators +```javascript +const userSchema = new Schema({ + email: { + type: String, + required: true, + validate: { + validator: function(v) { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); + }, + message: props => `${props.value} is not a valid email!` + } + }, + age: { + type: Number, + min: [18, 'Must be at least 18, got {VALUE}'], + max: 120 + }, + role: { + type: String, + enum: { + values: ['user', 'admin'], + message: '{VALUE} is not a valid role' + } + } +}); + +// Async validator +userSchema.path('username').validate({ + isAsync: true, + validator: async function(value) { + const user = await this.constructor.findOne({ username: value }); + return !user || this._id.equals(user._id); + }, + message: 'Username already exists' +}); +``` + +## Error Handling + +### Handling Validation Errors +```javascript +try { + const user = await User.create({ email: 'invalid-email' }); +} catch (error) { + if (error.name === 'ValidationError') { + const errors = {}; + Object.keys(error.errors).forEach(key => { + errors[key] = error.errors[key].message; + }); + return { errors }; + } + throw error; +} +``` + +### Handling Duplicate Key Errors +```javascript +try { + const user = await User.create({ email: 'duplicate@example.com' }); +} catch (error) { + if (error.code === 11000) { + return { error: 'Email already exists' }; + } + throw error; +} +``` + +## Performance Optimization + +### Lean Queries +```javascript +// Returns plain JavaScript objects instead of Mongoose documents +const users = await User.find({}).lean(); +``` + +### Select Only Required Fields +```javascript +// Only fetch username and email +const users = await User.find({}, 'username email'); +``` + +### Use Indexes +```javascript +// Create index +userSchema.index({ email: 1 }, { unique: true }); +userSchema.index({ 'profile.location': '2dsphere' }); +``` + +### Caching +```javascript +// Simple in-memory cache +const cache = {}; + +async function getUser(id) { + if (cache[id]) { + return cache[id]; + } + + const user = await User.findById(id).cache({ key: id }); + cache[id] = user; + return user; +} +``` + +## Testing + +### Using Jest +```javascript +// __tests__/user.test.js +const mongoose = require('mongoose'); +const User = require('../models/User'); + +beforeAll(async () => { + await mongoose.connect('mongodb://localhost:27017/testdb', { + useNewUrlParser: true, + useUnifiedTopology: true + }); +}); + +afterAll(async () => { + await mongoose.connection.dropDatabase(); + await mongoose.connection.close(); +}); + +describe('User Model', () => { + it('should create a new user', async () => { + const user = await User.create({ + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }); + + expect(user).toHaveProperty('_id'); + expect(user.email).toBe('test@example.com'); + }); + + it('should not create user with duplicate email', async () => { + await User.create({ + username: 'testuser1', + email: 'duplicate@example.com', + password: 'password123' + }); + + await expect( + User.create({ + username: 'testuser2', + email: 'duplicate@example.com', + password: 'password456' + }) + ).rejects.toThrow('duplicate key error'); + }); +}); +``` + +## Best Practices + +1. **Use Schemas**: Always define schemas for your models +2. **Use Middleware**: For pre/post hooks and business logic +3. **Use Virtuals**: For computed properties +4. **Use Statics**: For model-level functions +5. **Use Methods**: For document-level functions +6. **Use Plugins**: For reusable functionality +7. **Use Lean**: When you don't need Mongoose documents +8. **Use Projections**: Only fetch the fields you need +9. **Use Indexes**: For better query performance +10. **Handle Errors**: Proper error handling and validation +11. **Use Transactions**: For multi-document operations +12. **Use Environment Variables**: For configuration +13. **Use Connection Pooling**: For better performance +14. **Monitor Performance**: Use MongoDB Atlas or similar tools +15. **Keep Documents Small**: Avoid large documents (>16MB) +16. **Use Caching**: For frequently accessed data +17. **Use Aggregation**: For complex queries +18. **Use Text Search**: For full-text search capabilities +19. **Use Geospatial Queries**: For location-based data +20. **Keep Mongoose Updated**: For security and performance improvements diff --git a/docs/mongodb/schema-design.md b/docs/mongodb/schema-design.md new file mode 100644 index 0000000..95ca163 --- /dev/null +++ b/docs/mongodb/schema-design.md @@ -0,0 +1,318 @@ +# MongoDB Schema Design + +## Data Modeling Concepts + +### 1. Document Structure + +#### Embedded Documents +```javascript +// Users with embedded addresses +{ + _id: ObjectId("507f1f77bcf86cd799439011"), + name: "John Doe", + email: "john@example.com", + addresses: [ + { + type: "home", + street: "123 Main St", + city: "Springfield", + zip: "12345", + isPrimary: true + }, + { + type: "work", + street: "456 Work Ave", + city: "Springfield", + zip: "12345" + } + ] +} +``` + +#### Referenced Documents +```javascript +// Users collection +{ + _id: ObjectId("507f1f77bcf86cd799439011"), + name: "John Doe", + email: "john@example.com" +} + +// Addresses collection +{ + _id: ObjectId("5a934e000102030405000000"), + userId: ObjectId("507f1f77bcf86cd799439011"), + type: "home", + street: "123 Main St", + city: "Springfield", + zip: "12345", + isPrimary: true +} +``` + +## Schema Design Patterns + +### 1. Embedding (Denormalization) +**When to use:** +- One-to-few relationships +- Data that's always accessed together +- Data that changes together + +**Example: Blog Post with Comments** +```javascript +{ + _id: ObjectId("5a934e000102030405000001"), + title: "MongoDB Schema Design", + content: "...", + author: "John Doe", + publishedAt: ISODate("2023-01-15T10:00:00Z"), + comments: [ + { + _id: ObjectId("5a934e000102030405000002"), + author: "Alice", + text: "Great post!", + createdAt: ISODate("2023-01-15T11:30:00Z") + }, + { + _id: ObjectId("5a934e000102030405000003"), + author: "Bob", + text: "Thanks for sharing!", + createdAt: ISODate("2023-01-16T09:15:00Z") + } + ] +} +``` + +### 2. Referencing (Normalization) +**When to use:** +- One-to-many or many-to-many relationships +- Large documents that exceed 16MB +- Frequently updated data + +**Example: Products and Categories** +```javascript +// Products collection +{ + _id: ObjectId("5a934e000102030405000100"), + name: "Laptop", + price: 999.99, + categoryIds: [ + ObjectId("5a934e000102030405000200"), // Electronics + ObjectId("5a934e000102030405000201") // Computers + ] +} + +// Categories collection +{ + _id: ObjectId("5a934e000102030405000200"), + name: "Electronics", + description: "Electronic devices" +} + +{ + _id: ObjectId("5a934e000102030405000201"), + name: "Computers", + description: "Computers and laptops" +} +``` + +### 3. Bucket Pattern +**When to use:** +- Time-series data +- IoT sensor data +- Logging + +**Example: Sensor Data** +```javascript +{ + _id: "sensor-1-2023-01", + sensorId: "sensor-1", + type: "temperature", + unit: "Celsius", + measurements: [ + { timestamp: ISODate("2023-01-01T00:00:00Z"), value: 21.5 }, + { timestamp: ISODate("2023-01-01T00:05:00Z"), value: 21.7 }, + // ... more measurements for the month + ], + metadata: { + location: "Room 101", + min: 18.0, + max: 25.0 + } +} +``` + +### 4. Attribute Pattern +**When to use:** +- Heterogeneous document attributes +- Sparse fields +- Polymorphic schemas + +**Example: E-commerce Product Catalog** +```javascript +{ + _id: ObjectId("5a934e000102030405000300"), + name: "Smartphone X", + category: "electronics", + attributes: [ + { name: "color", value: "black" }, + { name: "storage", value: "128GB" }, + { name: "ram", value: "8GB" } + ] +} + +{ + _id: ObjectId("5a934e000102030405000301"), + name: "T-Shirt", + category: "clothing", + attributes: [ + { name: "color", value: "blue" }, + { name: "size", value: "L" }, + { name: "material", value: "cotton" } + ] +} +``` + +## Schema Validation + +### JSON Schema Validation +```javascript +db.createCollection("users", { + validator: { + $jsonSchema: { + bsonType: "object", + required: ["name", "email", "status"], + properties: { + name: { + bsonType: "string", + description: "must be a string and is required" + }, + email: { + bsonType: "string", + pattern: "^.+@.+\\.", + description: "must be a valid email and is required" + }, + status: { + enum: ["active", "inactive", "pending"], + description: "must be a valid status and is required" + }, + age: { + bsonType: ["int", "null"], + minimum: 0, + maximum: 120, + description: "must be an integer between 0 and 120" + } + } + } + } +}); +``` + +## Data Types + +### Common Data Types +- String (`string`) +- Number (`int32`, `int64`, `double`) +- Boolean (`bool`) +- Date (`date`) +- Array (`array`) +- Object (`object`) +- ObjectId (`objectId`) +- Binary Data (`binData`) +- Null (`null`) + +### Special Types +- Decimal128 (for precise decimal arithmetic) +- Timestamp (for internal MongoDB use) +- Regular Expression (`regex`) +- JavaScript (`javascript`) +- MinKey/MaxKey (for comparison operations) + +## Indexing Strategies + +### Single Field Index +```javascript +db.users.createIndex({ email: 1 }); +``` + +### Compound Index +```javascript +db.orders.createIndex({ customerId: 1, orderDate: -1 }); +``` + +### Multikey Index (for arrays) +```javascript +db.products.createIndex({ tags: 1 }); +``` + +### Text Index +```javascript +db.articles.createIndex({ title: "text", content: "text" }); +``` + +### Wildcard Index +```javascript +db.users.createIndex({ "metadata.$**": 1 }); +``` + +## Sharding Strategies + +### Hashed Sharding +```javascript +sh.shardCollection("mydb.users", { _id: "hashed" }); +``` + +### Ranged Sharding +```javascript +sh.shardCollection("mydb.orders", { customerId: 1 }); +``` + +### Zoned Sharding +```javascript +sh.addShardTag("shard0000", "US"); +sh.addShardTag("shard0001", "EU"); +sh.addTagRange("mydb.users", { country: "US" }, { country: "US" }, "US"); +sh.addTagRange("mydb.users", { country: "UK" }, { country: "FR" }, "EU"); +``` + +## Data Lifecycle Management + +### TTL Indexes +```javascript +// Documents will be automatically deleted after 30 days +db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 2592000 }); +``` + +### Capped Collections +```javascript +// Create a capped collection of 1MB +db.createCollection("recent_activities", { capped: true, size: 1048576 }); +``` + +## Schema Evolution + +### Adding Fields +```javascript +db.users.updateMany( + { newField: { $exists: false } }, + { $set: { newField: "default value" } } +); +``` + +### Migrating Data +```javascript +// Add new structure +db.products.updateMany( + {}, + { $set: { "metadata.tags": [] } } +); + +// Migrate old tags to new structure +db.products.updateMany( + { tags: { $exists: true } }, + [ + { $set: { "metadata.tags": "$tags" } }, + { $unset: "tags" } + ] +); +``` diff --git a/docs/mongodb/setup.md b/docs/mongodb/setup.md new file mode 100644 index 0000000..f098700 --- /dev/null +++ b/docs/mongodb/setup.md @@ -0,0 +1,211 @@ +# MongoDB Setup Guide + +## Installation + +### macOS (using Homebrew) +```bash +# Install MongoDB Community Edition +brew tap mongodb/brew +brew install mongodb-community + +# Start MongoDB +brew services start mongodb-community + +# Verify installation +mongod --version +mongo --version +``` + +### Windows +1. Download MongoDB Community Server from [MongoDB Download Center](https://www.mongodb.com/try/download/community) +2. Run the installer and follow the prompts +3. Add MongoDB's `bin` directory to your system's PATH +4. Create a data directory: `C:\data\db` +5. Start MongoDB: `mongod` + +### Linux (Ubuntu/Debian) +```bash +# Import the public key +wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add - + +# Create list file +echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list + +# Reload local package database +sudo apt-get update + +# Install MongoDB +sudo apt-get install -y mongodb-org + +# Start MongoDB +sudo systemctl start mongod + +# Enable automatic startup +sudo systemctl enable mongod +``` + +## MongoDB Compass (GUI Tool) +Download and install MongoDB Compass from: https://www.mongodb.com/try/download/compass + +## Basic Commands + +### Start MongoDB Shell +```bash +mongosh # For MongoDB 6.0+ +# or +mongo # For older versions +``` + +### Show Databases +```javascript +show dbs +``` + +### Switch/Create Database +```javascript +use mydb +``` + +### Show Collections +```javascript +show collections +``` + +### Get Help +```javascript +db.help() +``` + +## Connecting with Node.js + +### Install MongoDB Driver +```bash +npm install mongodb +``` + +### Basic Connection +```javascript +const { MongoClient } = require('mongodb'); + +// Connection URI +const uri = 'mongodb://localhost:27017'; + +// Create a new MongoClient +const client = new MongoClient(uri); + +async function run() { + try { + // Connect the client to the server + await client.connect(); + console.log('Connected successfully to MongoDB'); + + // Get the database + const db = client.db('mydb'); + + // Get the collection + const collection = db.collection('documents'); + + // Insert a document + await collection.insertOne({ name: 'Test', value: 1 }); + + // Find documents + const docs = await collection.find({}).toArray(); + console.log('Found documents:', docs); + + } finally { + // Close the connection + await client.close(); + } +} + +run().catch(console.dir); +``` + +## Configuration + +### MongoDB Configuration File (mongod.conf) +Typical location: +- Linux: `/etc/mongod.conf` +- macOS: `/usr/local/etc/mongod.conf` +- Windows: `C:\Program Files\MongoDB\Server\\bin\mongod.cfg` + +### Common Configuration Options +```yaml +# Network interfaces +net: + port: 27017 + bindIp: 127.0.0.1 # Only localhost + +# Storage +dbPath: /data/db + +# Security +security: + authorization: enabled # Enable authentication + +# Replication (for replica sets) +replication: + replSetName: "rs0" + +# Performance +storage: + journal: + enabled: true + wiredTiger: + engineConfig: + cacheSizeGB: 1 # Adjust based on available RAM +``` + +## Authentication + +### Create Admin User +```javascript +use admin +db.createUser({ + user: 'admin', + pwd: 'your-secure-password', + roles: [ { role: 'userAdminAnyDatabase', db: 'admin' } ] +}) +``` + +### Connect with Authentication +```javascript +const uri = 'mongodb://admin:your-secure-password@localhost:27017/admin'; +const client = new MongoClient(uri); +``` + +## Backup and Restore + +### Create Backup +```bash +mongodump --uri="mongodb://localhost:27017" --out=/backup/$(date +%Y%m%d) +``` + +### Restore from Backup +```bash +mongorestore --uri="mongodb://localhost:27017" /backup/20231015/mydb +``` + +## Monitoring + +### Basic Stats +```javascript +db.serverStatus() + +db.stats() + +// Collection stats +db.collection.stats() + +// Current operations +db.currentOp() +``` + +### Enable Profiling +```javascript +// Set profiling level (0=off, 1=slow, 2=all) +db.setProfilingLevel(1, { slowms: 100 }) + +// View profile data +db.system.profile.find().pretty() +``` From 9891ea4171a58f82c489d7bd9e4e2471cdecd6a9 Mon Sep 17 00:00:00 2001 From: Divya Pahuja Date: Thu, 16 Oct 2025 00:25:44 +0530 Subject: [PATCH 3/4] adding css tricks --- docs/ui/css-tricks.md | 581 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 581 insertions(+) create mode 100644 docs/ui/css-tricks.md diff --git a/docs/ui/css-tricks.md b/docs/ui/css-tricks.md new file mode 100644 index 0000000..4be1bf7 --- /dev/null +++ b/docs/ui/css-tricks.md @@ -0,0 +1,581 @@ +# CSS Tricks and Tips + +## Layout + +### Flexbox Centering +Center elements both horizontally and vertically: +```css +.container { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; /* Optional: for full viewport height */ +} +``` + +### CSS Grid Layout +Create a responsive grid with auto-fill: +```css +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 1rem; +} +``` + +### Sticky Footer +Keep footer at the bottom of the page: +```css +body { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +main { + flex: 1; +} +``` + +## Effects + +### Smooth Scrolling +```css +html { + scroll-behavior: smooth; +} +``` + +### Custom Scrollbar +```css +/* WebKit (Chrome, Safari, newer Edge) */ +::-webkit-scrollbar { + width: 10px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; +} + +::-webkit-scrollbar-thumb { + background: #888; + border-radius: 5px; +} + +/* Firefox */ +html { + scrollbar-width: thin; + scrollbar-color: #888 #f1f1f1; +} +``` + +### Hover Effects +```css +.button { + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.button:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} +``` + +## Typography + +### Text Overflow Ellipsis +```css +.ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; +} + +/* Multi-line ellipsis (3 lines) */ +.multi-line-ellipsis { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} +``` + +### System Font Stack +```css +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; +} +``` + +## Responsive Design + +### Mobile-First Media Queries +```css +/* Base styles (mobile) */ +.container { + padding: 1rem; +} + +/* Tablet */ +@media (min-width: 768px) { + .container { + padding: 2rem; + } +} + +/* Desktop */ +@media (min-width: 1024px) { + .container { + max-width: 1200px; + margin: 0 auto; + } +} +``` + +### Responsive Images +```css +.responsive-img { + max-width: 100%; + height: auto; + display: block; +} + +/* Art direction with picture element */ + + + + Description + +``` + +## Animations + +### Keyframe Animation +```css +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.animate-fade-in { + animation: fadeIn 0.6s ease-out forwards; +} +``` + +### Hover Underline Animation +```css +.hover-underline { + position: relative; + text-decoration: none; +} + +.hover-underline::after { + content: ''; + position: absolute; + width: 0; + height: 2px; + bottom: -2px; + left: 0; + background-color: currentColor; + transition: width 0.3s ease; +} + +.hover-underline:hover::after { + width: 100%; +} +``` + +## Forms + +### Custom Checkbox/Radio +```css +.custom-checkbox { + position: relative; + padding-left: 30px; + cursor: pointer; +} + +.custom-checkbox input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 20px; + width: 20px; + background-color: #eee; + border: 2px solid #ddd; + border-radius: 4px; +} + +.custom-checkbox:hover input ~ .checkmark { + background-color: #ccc; +} + +.custom-checkbox input:checked ~ .checkmark { + background-color: #2196F3; + border-color: #2196F3; +} + +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +.custom-checkbox input:checked ~ .checkmark:after { + display: block; +} + +.custom-checkbox .checkmark:after { + left: 6px; + top: 2px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} +``` + +## Variables and Theming + +### CSS Custom Properties +```css +:root { + --primary-color: #3498db; + --secondary-color: #2ecc71; + --spacing-unit: 1rem; + --border-radius: 4px; + --transition: all 0.3s ease; +} + +.button { + background-color: var(--primary-color); + padding: calc(var(--spacing-unit) * 2); + border-radius: var(--border-radius); + transition: var(--transition); +} + +/* Dark mode theming */ +@media (prefers-color-scheme: dark) { + :root { + --primary-color: #2980b9; + --secondary-color: #27ae60; + } +} +``` + +## Performance + +### Will-Change Property +```css +.optimize { + will-change: transform, opacity; +} +``` + +### Content Visibility +```css +.long-content { + content-visibility: auto; + contain-intrinsic-size: 0 500px; /* Estimated height */ +} +``` + +## Browser Compatibility + +### Feature Queries +```css +@supports (display: grid) { + .container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + } +} + +@supports not (display: grid) { + .container { + display: flex; + flex-wrap: wrap; + } + + .container > * { + flex: 1 1 250px; + margin: 0.5rem; + } +} +``` + +### Vendor Prefixes +```css +.gradient-bg { + background: #1e5799; /* Fallback */ + background: -moz-linear-gradient(top, #1e5799 0%, #2989d8 100%); + background: -webkit-linear-gradient(top, #1e5799 0%,#2989d8 100%); + background: linear-gradient(to bottom, #1e5799 0%,#2989d8 100%); + filter: progid:DXImageTransform.Microsoft.gradient( + startColorstr='#1e5799', + endColorstr='#2989d8', + GradientType=0 + ); +} +``` + +## Debugging + +### Debugging Layout +```css +/* Add to any element to debug */ +.debug { + outline: 1px solid red; + background: rgba(255, 0, 0, 0.1); +} + +/* Debug all elements */ +* { outline: 1px solid rgba(255, 0, 0, 0.2); } +* * { outline: 1px solid rgba(0, 255, 0, 0.2); } +* * * { outline: 1px solid rgba(0, 0, 255, 0.2); } +* * * * { outline: 1px solid rgba(255, 0, 255, 0.2); } +* * * * * { outline: 1px solid rgba(0, 255, 255, 0.2); } +``` + +### Print Styles +```css +@media print { + /* Hide elements when printing */ + .no-print { + display: none !important; + } + + /* Page breaks */ + .page-break { + page-break-before: always; + } + + /* Prevent text from breaking across pages */ + p, h1, h2, h3, h4, h5, h6 { + page-break-inside: avoid; + } + + /* Set page margins */ + @page { + margin: 2cm; + } +} +``` + +## Modern CSS + +### Aspect Ratio +```css +.aspect-ratio-box { + aspect-ratio: 16 / 9; + background: #f0f0f0; +} +``` + +### CSS Grid Subgrid +```css +.grid-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; +} + +.grid-item { + display: grid; + grid-template-rows: subgrid; + grid-row: span 3; /* Number of rows this item spans */ +} +``` + +### Container Queries +```css +.component { + container-type: inline-size; +} + +@container (min-width: 400px) { + .component .content { + display: flex; + gap: 1rem; + } +} +``` + +## Accessibility + +### Screen Reader Only +```css +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +/* For interactive elements that need to be visible on focus */ +.sr-only.focusable:focus { + position: static; + width: auto; + height: auto; + padding: 0.5em; + margin: 0; + overflow: visible; + clip: auto; + white-space: normal; +} +``` + +### Reduced Motion +```css +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} +``` + +## Bonus: CSS Reset +```css +/* Box sizing rules */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margin and padding */ +html, +body, +h1, h2, h3, h4, h5, h6, +p, blockquote, pre, +dl, dd, ol, ul, +figure, +fieldset, legend, +textarea, +pre, iframe, +hr { + margin: 0; + padding: 0; +} + +/* Set core body defaults */ +body { + min-height: 100vh; + scroll-behavior: smooth; + text-rendering: optimizeSpeed; + line-height: 1.5; +} + +/* Remove list styles on ul, ol elements */ +ul[class], +ol[class] { + list-style: none; +} + +/* Make images easier to work with */ +img, +picture, +video, +canvas, +svg { + max-width: 100%; + display: block; +} + +/* Inherit fonts for inputs and buttons */ +input, +button, +textarea, +select { + font: inherit; +} + +/* Remove all animations and transitions for people that prefer not to see them */ +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} +``` + +## CSS Variables for Theming +```css +:root { + /* Colors */ + --color-primary: #2563eb; + --color-secondary: #7c3aed; + --color-success: #10b981; + --color-warning: #f59e0b; + --color-danger: #ef4444; + + /* Typography */ + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'Fira Code', 'Courier New', monospace; + + /* Spacing */ + --space-xs: 0.25rem; + --space-sm: 0.5rem; + --space-md: 1rem; + --space-lg: 2rem; + --space-xl: 4rem; + + /* Border radius */ + --radius-sm: 0.25rem; + --radius-md: 0.5rem; + --radius-lg: 1rem; + --radius-full: 9999px; + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + + /* Transitions */ + --transition-fast: 150ms ease; + --transition-normal: 300ms ease; + --transition-slow: 500ms ease; +} + +/* Dark mode */ +@media (prefers-color-scheme: dark) { + :root { + --color-primary: #3b82f6; + --color-secondary: #8b5cf6; + --color-success: #10b981; + --color-warning: #f59e0b; + --color-danger: #ef4444; + + --shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.3); + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2); + --shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); + --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2); + } +} +``` + +This CSS tricks guide covers a wide range of techniques from basic to advanced, including layout, animations, responsive design, accessibility, and modern CSS features. Each example is ready to use and includes comments for better understanding. From 7201fb8c249c8d804b6930537e04178d1418740b Mon Sep 17 00:00:00 2001 From: Divya Pahuja Date: Thu, 16 Oct 2025 00:31:24 +0530 Subject: [PATCH 4/4] adding shadcn ui docs --- docs/ui/shadcn.md | 580 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 580 insertions(+) create mode 100644 docs/ui/shadcn.md diff --git a/docs/ui/shadcn.md b/docs/ui/shadcn.md new file mode 100644 index 0000000..8249a94 --- /dev/null +++ b/docs/ui/shadcn.md @@ -0,0 +1,580 @@ +# shadcn/ui Documentation + +## Introduction + +shadcn/ui is a collection of beautifully designed, accessible, and customizable React components that you can copy and paste into your apps. It's built on top of Radix UI and Tailwind CSS. + +## Installation + +### 1. Create a new Next.js project +```bash +npx create-next-app@latest my-app --typescript --tailwind --eslint +cd my-app +``` + +### 2. Install shadcn/ui +```bash +npx shadcn-ui@latest init +``` + +### 3. Configure your `components.json` +```json +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "app/globals.css", + "baseColor": "zinc", + "cssVariables": true + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} +``` + +## Adding Components + +### Add a component +```bash +npx shadcn-ui@latest add button +npx shadcn-ui@latest add dropdown-menu +npx shadcn-ui@latest add card +``` + +### Available Components +- `accordion` +- `alert` +- `alert-dialog` +- `aspect-ratio` +- `avatar` +- `badge` +- `button` +- `calendar` +- `card` +- `checkbox` +- `collapsible` +- `command` +- `context-menu` +- `dialog` +- `dropdown-menu` +- `form` +- `hover-card` +- `input` +- `label` +- `menubar` +- `navigation-menu` +- `popover` +- `progress` +- `radio-group` +- `scroll-area` +- `select` +- `separator` +- `sheet` +- `skeleton` +- `slider` +- `switch` +- `table` +- `tabs` +- `textarea` +- `toast` +- `toaster` +- `toggle` +- `tooltip` + +## Basic Usage + +### Button Component +```tsx +import { Button } from "@/components/ui/button" + +export function ButtonDemo() { + return ( + + ) +} +``` + +### Card Component +```tsx +import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card" +import { Button } from "@/components/ui/button" + +export function CardDemo() { + return ( + + + Create project + Deploy your new project in one-click. + + +
+
+ {/* Form fields go here */} +
+
+
+ + + + +
+ ) +} +``` + +## Form Components + +### Form with Validation +```tsx +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import * as z from "zod" + +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" + +const formSchema = z.object({ + username: z.string().min(2, { + message: "Username must be at least 2 characters.", + }), + email: z.string().email({ + message: "Please enter a valid email address.", + }), +}) + +export function ProfileForm() { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: "", + email: "", + }, + }) + + function onSubmit(values: z.infer) { + console.log(values) + } + + return ( +
+ + ( + + Username + + + + + This is your public display name. + + + + )} + /> + ( + + Email + + + + + + )} + /> + + + + ) +} +``` + +## Dark Mode + +### Theme Provider +```tsx +// app/providers.tsx +"use client" + +import * as React from "react" +import { ThemeProvider as NextThemesProvider } from "next-themes" +import { type ThemeProviderProps } from "next-themes/dist/types" + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} + +// app/layout.tsx +import { ThemeProvider } from "@/app/providers" + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + {children} + + + + ) +} + +// components/theme-toggle.tsx +"use client" + +import { Moon, Sun } from "lucide-react" +import { useTheme } from "next-themes" + +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +export function ModeToggle() { + const { setTheme } = useTheme() + + return ( + + + + + + setTheme("light")}> + Light + + setTheme("dark")}> + Dark + + setTheme("system")}> + System + + + + ) +} +``` + +## Customization + +### Customizing Theme +```ts +// tailwind.config.js +const { fontFamily } = require('tailwindcss/defaultTheme') + +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: `var(--radius)`, + md: `calc(var(--radius) - 2px)`, + sm: "calc(var(--radius) - 4px)", + }, + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans], + }, + keyframes: { + "accordion-down": { + from: { height: 0 }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: 0 }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} +``` + +## Advanced Usage + +### Custom Component with Variants +```tsx +import { cva, type VariantProps } from "class-variance-authority" +import { Loader2 } from "lucide-react" +import * as React from "react" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "underline-offset-4 hover:underline text-primary", + }, + size: { + default: "h-10 py-2 px-4", + sm: "h-9 px-3 rounded-md", + lg: "h-11 px-8 rounded-md", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + isLoading?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, isLoading, children, ...props }, ref) => { + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } +``` + +## Best Practices + +1. **Component Composition** + - Compose smaller components to build complex UIs + - Use the `asChild` prop for flexible component composition + +2. **Performance** + - Use dynamic imports for large components + - Implement proper loading states + - Use `React.memo` for expensive components + +3. **Accessibility** + - All components are built with accessibility in mind + - Use proper ARIA attributes + - Ensure keyboard navigation works + +4. **Theming** + - Use CSS variables for theming + - Support both light and dark modes + - Provide proper contrast ratios + +## Common Patterns + +### Data Table with Sorting +```tsx +"use client" + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table" + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" + +interface DataTableProps { + columns: ColumnDef[] + data: TData[] +} + +export function DataTable({ + columns, + data, +}: DataTableProps) { + const [sorting, setSorting] = React.useState([]) + const [columnFilters, setColumnFilters] = React.useState([]) + const [columnVisibility, setColumnVisibility] = React.useState({}) + const [rowSelection, setRowSelection] = React.useState({}) + + const table = useReactTable({ + data, + columns, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + }) + + return ( +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+ ) +} +``` + +## Resources + +- [Official Documentation](https://ui.shadcn.com/) +- [GitHub Repository](https://github.com/shadcn/ui) +- [Radix UI Primitives](https://www.radix-ui.com/) +- [Tailwind CSS](https://tailwindcss.com/) +- [Class Variance Authority](https://github.com/joe-bell/cva)