RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/Cursor rules/madebyaris/poinf-of-sales

Cursor rule

.cursor/rules/frontend-react.mdc

Complete React component patterns, hooks, and state management with shadcn/ui for POS System

Cursor rules

Quality

69/100

Scores the file, not the repository.

Length

2,878 words

35 headings · 19 code blocks

Repository

118

— · pushed 339 days ago

Last changed

3 days ago

First indexed 3 days ago.
madebyaris/poinf-of-sales/.cursor/rules/frontend-react.mdcRawGitHub
1---
2globs: *.tsx,*.ts,*.jsx,*.js
3description: Complete React component patterns, hooks, and state management with shadcn/ui for POS System
4---
5 
6# ⚛️ React Component Patterns & Best Practices
7 
8## 🏗️ Component Architecture
9 
10### shadcn/ui Base Components Pattern
11```typescript
12// Follow the established shadcn/ui pattern from components/ui/
13import * as React from 'react'
14import { cn } from '@/lib/utils'
15import { cva, type VariantProps } from 'class-variance-authority'
16 
17// ✅ CORRECT: Use cva for variant-based styling
18const buttonVariants = cva(
19 "inline-flex items-center justify-center rounded-md font-medium transition-colors",
20 {
21 variants: {
22 variant: {
23 default: "bg-primary text-primary-foreground hover:bg-primary/90",
24 outline: "border border-input bg-background hover:bg-accent",
25 ghost: "hover:bg-accent hover:text-accent-foreground",
26 },
27 size: {
28 default: "h-10 px-4 py-2",
29 sm: "h-9 px-3",
30 lg: "h-11 px-8",
31 },
32 },
33 defaultVariants: {
34 variant: "default",
35 size: "default",
36 },
37 }
38)
39 
40interface ButtonProps
41 extends React.ButtonHTMLAttributes<HTMLButtonElement>,
42 VariantProps<typeof buttonVariants> {
43 asChild?: boolean
44}
45 
46// ✅ CORRECT: Always use forwardRef for reusable components
47const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
48 ({ className, variant, size, asChild = false, ...props }, ref) => {
49 return (
50 <button
51 ref={ref}
52 className={cn(buttonVariants({ variant, size, className }))}
53 {...props}
54 />
55 )
56 }
57)
58Button.displayName = "Button"
59```
60 
61### Business Component Pattern
62```typescript
63// POS-specific business components
64import { useState, useCallback } from 'react'
65import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
66import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
67import { Button } from '@/components/ui/button'
68import { Badge } from '@/components/ui/badge'
69import apiClient from '@/api/client'
70import type { Product, CartItem } from '@/types'
71 
72interface ProductCardProps {
73 product: Product
74 onSelect: (product: Product) => void
75 isSelected?: boolean
76 isInCart?: boolean
77 cartQuantity?: number
78 className?: string
79}
80 
81// ✅ CORRECT: Typed component with proper props interface
82export const ProductCard: React.FC<ProductCardProps> = ({
83 product,
84 onSelect,
85 isSelected = false,
86 isInCart = false,
87 cartQuantity = 0,
88 className
89}) => {
90 // ✅ CORRECT: Use useCallback for event handlers
91 const handleSelect = useCallback(() => {
92 onSelect(product)
93 }, [product, onSelect])
94 
95 // ✅ CORRECT: Early returns for loading/error states
96 if (!product.is_available) {
97 return (
98 <Card className={cn("opacity-50", className)}>
99 <CardContent className="p-4 text-center">
100 <p className="text-muted-foreground">Unavailable</p>
101 </CardContent>
102 </Card>
103 )
104 }
105 
106 return (
107 <Card
108 className={cn(
109 "cursor-pointer transition-all hover:shadow-md",
110 isSelected && "ring-2 ring-primary",
111 className
112 )}
113 onClick={handleSelect}
114 >
115 <CardHeader className="pb-2">
116 <div className="flex items-center justify-between">
117 <CardTitle className="text-lg">{product.name}</CardTitle>
118 {isInCart && (
119 <Badge variant="secondary">{cartQuantity}</Badge>
120 )}
121 </div>
122 </CardHeader>
123 <CardContent>
124 <p className="text-sm text-muted-foreground mb-2">
125 {product.description}
126 </p>
127 <p className="text-xl font-bold">${product.price.toFixed(2)}</p>
128 </CardContent>
129 </Card>
130 )
131}
132```
133 
134## 🎣 Custom Hooks Patterns
135 
136### Data Fetching Hook
137```typescript
138// Custom hook for API data fetching
139import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
140import { toastHelpers } from '@/lib/toast-helpers'
141import apiClient from '@/api/client'
142import type { Product, CreateProductRequest } from '@/types'
143 
144export const useProducts = (categoryId?: string) => {
145 return useQuery({
146 queryKey: ['products', categoryId],
147 queryFn: () => apiClient.getProducts({ category_id: categoryId }),
148 select: (data) => data.data || [], // Transform response
149 staleTime: 5 * 60 * 1000, // 5 minutes
150 })
151}
152 
153export const useCreateProduct = () => {
154 const queryClient = useQueryClient()
155 
156 return useMutation({
157 mutationFn: (product: CreateProductRequest) =>
158 apiClient.createProduct(product),
159 onSuccess: (data) => {
160 // Invalidate and refetch
161 queryClient.invalidateQueries({ queryKey: ['products'] })
162 toastHelpers.success('Product created successfully!')
163 },
164 onError: (error: any) => {
165 toastHelpers.error(`Failed to create product: ${error.message}`)
166 },
167 })
168}
169```
170 
171### Business Logic Hook (Shopping Cart)
172```typescript
173// Custom hook for cart management
174import { useState, useCallback, useMemo } from 'react'
175import type { Product, CartItem } from '@/types'
176 
177export const useCart = () => {
178 const [items, setItems] = useState<CartItem[]>([])
179 
180 // ✅ CORRECT: Memoized calculations
181 const total = useMemo(() => {
182 return items.reduce((sum, item) => sum + (item.product.price * item.quantity), 0)
183 }, [items])
184 
185 const itemCount = useMemo(() => {
186 return items.reduce((count, item) => count + item.quantity, 0)
187 }, [items])
188 
189 // ✅ CORRECT: Optimized with useCallback
190 const addItem = useCallback((product: Product, quantity = 1) => {
191 setItems(prevItems => {
192 const existingItem = prevItems.find(item => item.product.id === product.id)
193
194 if (existingItem) {
195 return prevItems.map(item =>
196 item.product.id === product.id
197 ? { ...item, quantity: item.quantity + quantity }
198 : item
199 )
200 }
201
202 return [...prevItems, { product, quantity, subtotal: product.price * quantity }]
203 })
204 }, [])
205 
206 const updateQuantity = useCallback((productId: string, quantity: number) => {
207 if (quantity <= 0) {
208 removeItem(productId)
209 return
210 }
211 
212 setItems(prevItems =>
213 prevItems.map(item =>
214 item.product.id === productId
215 ? { ...item, quantity, subtotal: item.product.price * quantity }
216 : item
217 )
218 )
219 }, [])
220 
221 const removeItem = useCallback((productId: string) => {
222 setItems(prevItems => prevItems.filter(item => item.product.id !== productId))
223 }, [])
224 
225 const clearCart = useCallback(() => {
226 setItems([])
227 }, [])
228 
229 return {
230 items,
231 total,
232 itemCount,
233 addItem,
234 updateQuantity,
235 removeItem,
236 clearCart,
237 isEmpty: items.length === 0,
238 }
239}
240```
241 
242## 📝 Form Handling Patterns
243 
244### React Hook Form + Zod Integration
245```typescript
246// Form with validation using React Hook Form + Zod
247import { useForm } from 'react-hook-form'
248import { zodResolver } from '@hookform/resolvers/zod'
249import { z } from 'zod'
250import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'
251import { Input } from '@/components/ui/input'
252import { Button } from '@/components/ui/button'
253import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
254 
255// ✅ CORRECT: Define Zod schema with proper validation
256const productSchema = z.object({
257 name: z.string().min(1, "Product name is required").max(100),
258 price: z.number().min(0.01, "Price must be greater than 0"),
259 category_id: z.string().min(1, "Category is required"),
260 description: z.string().optional(),
261 is_available: z.boolean().default(true),
262})
263 
264type ProductFormData = z.infer<typeof productSchema>
265 
266interface ProductFormProps {
267 onSubmit: (data: ProductFormData) => void
268 defaultValues?: Partial<ProductFormData>
269 isLoading?: boolean
270}
271 
272export const ProductForm: React.FC<ProductFormProps> = ({
273 onSubmit,
274 defaultValues,
275 isLoading = false
276}) => {
277 const form = useForm<ProductFormData>({
278 resolver: zodResolver(productSchema),
279 defaultValues: {
280 name: '',
281 price: 0,
282 category_id: '',
283 description: '',
284 is_available: true,
285 ...defaultValues,
286 },
287 })
288 
289 return (
290 <Form {...form}>
291 <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
292 <FormField
293 control={form.control}
294 name="name"
295 render={({ field }) => (
296 <FormItem>
297 <FormLabel>Product Name</FormLabel>
298 <FormControl>
299 <Input placeholder="Enter product name" {...field} />
300 </FormControl>
301 <FormMessage />
302 </FormItem>
303 )}
304 />
305 
306 <FormField
307 control={form.control}
308 name="price"
309 render={({ field }) => (
310 <FormItem>
311 <FormLabel>Price</FormLabel>
312 <FormControl>
313 <Input
314 type="number"
315 step="0.01"
316 placeholder="0.00"
317 {...field}
318 onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)}
319 />
320 </FormControl>
321 <FormMessage />
322 </FormItem>
323 )}
324 />
325 
326 <FormField
327 control={form.control}
328 name="category_id"
329 render={({ field }) => (
330 <FormItem>
331 <FormLabel>Category</FormLabel>
332 <Select onValueChange={field.onChange} defaultValue={field.value}>
333 <FormControl>
334 <SelectTrigger>
335 <SelectValue placeholder="Select a category" />
336 </SelectTrigger>
337 </FormControl>
338 <SelectContent>
339 <SelectItem value="burgers">Burgers</SelectItem>
340 <SelectItem value="drinks">Drinks</SelectItem>
341 <SelectItem value="sides">Sides</SelectItem>
342 </SelectContent>
343 </Select>
344 <FormMessage />
345 </FormItem>
346 )}
347 />
348 
349 <Button type="submit" disabled={isLoading} className="w-full">
350 {isLoading ? 'Creating...' : 'Create Product'}
351 </Button>
352 </form>
353 </Form>
354 )
355}
356```
357 
358### Reusable Form Components
359```typescript
360// Generic form field wrapper for consistent styling
361interface FormFieldWrapperProps<T extends FieldValues> {
362 control: Control<T>
363 name: FieldPath<T>
364 label: string
365 description?: string
366 required?: boolean
367 children: React.ReactNode
368}
369 
370export function FormFieldWrapper<T extends FieldValues>({
371 control,
372 name,
373 label,
374 description,
375 required = false,
376 children,
377}: FormFieldWrapperProps<T>) {
378 return (
379 <FormField
380 control={control}
381 name={name}
382 render={() => (
383 <FormItem>
384 <FormLabel>
385 {label}
386 {required && <span className="text-destructive ml-1">*</span>}
387 </FormLabel>
388 <FormControl>{children}</FormControl>
389 {description && <FormDescription>{description}</FormDescription>}
390 <FormMessage />
391 </FormItem>
392 )}
393 />
394 )
395}
396```
397 
398## 🍳 Enhanced Kitchen Component Patterns
399 
400### As-Ready Service Workflow Components
401```typescript
402// Enhanced kitchen interface with individual item tracking
403import React, { useState, useCallback, useEffect } from 'react'
404import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
405import { Button } from '@/components/ui/button'
406import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
407import { Badge } from '@/components/ui/badge'
408import { CheckCircle } from 'lucide-react'
409import { cn } from '@/lib/utils'
410 
411interface EnhancedKitchenOrderProps {
412 order: Order
413 onItemStatusUpdate: (orderId: string, itemId: string, status: ItemStatus) => void
414 onItemServe: (orderId: string, itemId: string) => void
415 soundEnabled: boolean
416 volume: number
417}
418 
419// ✅ CORRECT: Enhanced kitchen order card with as-ready service
420export const EnhancedKitchenOrderCard: React.FC<EnhancedKitchenOrderProps> = ({
421 order,
422 onItemStatusUpdate,
423 onItemServe,
424 soundEnabled,
425 volume
426}) => {
427 const [checkedItems, setCheckedItems] = useState<Set<string>>(new Set())
428 
429 // Calculate progress with individual item tracking
430 const displayItems = order.items || []
431 const totalItems = displayItems.length
432 const readyItems = checkedItems.size
433 const servedItems = displayItems.filter(item => item.status === 'served').length
434 const progress = totalItems > 0 ? ((readyItems + servedItems) / totalItems) * 100 : 0
435 
436 // ✅ CORRECT: Individual item status management
437 const toggleItem = useCallback((itemId: string) => {
438 setCheckedItems(prev => {
439 const newSet = new Set(prev)
440 if (newSet.has(itemId)) {
441 newSet.delete(itemId)
442 onItemStatusUpdate(order.id, itemId, 'preparing')
443 } else {
444 newSet.add(itemId)
445 onItemStatusUpdate(order.id, itemId, 'ready')
446
447 // Play ready sound notification
448 if (soundEnabled) {
449 playReadySound(volume)
450 }
451 }
452 return newSet
453 })
454 }, [order.id, onItemStatusUpdate, soundEnabled, volume])
455 
456 // ✅ CORRECT: Individual item serving
457 const handleItemServe = useCallback((itemId: string, itemName: string) => {
458 onItemServe(order.id, itemId)
459
460 // Play served sound notification (distinct from ready)
461 if (soundEnabled) {
462 playServedSound(volume)
463 }
464
465 // Remove from checked items
466 setCheckedItems(prev => {
467 const newSet = new Set(prev)
468 newSet.delete(itemId)
469 return newSet
470 })
471 }, [order.id, onItemServe, soundEnabled, volume])
472 
473 // Sound notification functions
474 const playReadySound = (volume: number) => {
475 try {
476 const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
477 const oscillator = audioContext.createOscillator()
478 const gainNode = audioContext.createGain()
479 
480 oscillator.connect(gainNode)
481 gainNode.connect(audioContext.destination)
482 
483 oscillator.frequency.setValueAtTime(1200, audioContext.currentTime) // 1200Hz for ready
484 gainNode.gain.setValueAtTime(volume * 0.3, audioContext.currentTime)
485 
486 oscillator.start()
487 oscillator.stop(audioContext.currentTime + 0.3)
488 } catch (error) {
489 console.log('Sound notification failed:', error)
490 }
491 }
492 
493 const playServedSound = (volume: number) => {
494 try {
495 const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
496 const oscillator = audioContext.createOscillator()
497 const gainNode = audioContext.createGain()
498 
499 oscillator.connect(gainNode)
500 gainNode.connect(audioContext.destination)
501 
502 oscillator.frequency.setValueAtTime(1400, audioContext.currentTime) // 1400Hz for served
503 gainNode.gain.setValueAtTime(volume * 0.2, audioContext.currentTime)
504 
505 oscillator.start()
506 oscillator.stop(audioContext.currentTime + 0.2)
507 } catch (error) {
508 console.log('Sound notification failed:', error)
509 }
510 }
511 
512 return (
513 <Card className="mb-6 border-2 border-blue-200 bg-white shadow-lg">
514 <CardHeader className="pb-4">
515 <div className="flex items-center justify-between">
516 <CardTitle className="text-xl font-bold text-gray-900">
517 Order #{order.order_number}
518 </CardTitle>
519 <Badge variant="outline" className="text-sm">
520 {order.order_type === 'dine_in' ? `Table ${order.table?.table_number}` : 'Takeaway'}
521 </Badge>
522 </div>
523
524 {/* Enhanced progress tracking */}
525 <div className="mt-3">
526 <div className="flex items-center justify-between text-sm text-gray-600 mb-2">
527 <span>Progress</span>
528 <span>{readyItems} ready • {servedItems} served • {totalItems - readyItems - servedItems} cooking ({Math.round(progress)}% complete)</span>
529 </div>
530 <div className="w-full bg-gray-200 rounded-full h-2">
531 <div
532 className="bg-green-500 h-2 rounded-full transition-all duration-300"
533 style={{ width: `${progress}%` }}
534 />
535 </div>
536 </div>
537 </CardHeader>
538 
539 <CardContent>
540 <div className="space-y-3">
541 {displayItems.map((item, index) => {
542 const isServed = item.status === 'served'
543 const isReady = checkedItems.has(item.id)
544 
545 return (
546 <div key={item.id} className={cn(
547 "flex items-start space-x-4 p-4 rounded-lg border-2 transition-colors",
548 isServed ? "bg-gray-50 border-gray-300 opacity-75" : "bg-white hover:border-blue-200"
549 )}>
550 {/* Item checkbox */}
551 <button
552 onClick={() => !isServed && toggleItem(item.id)}
553 disabled={isServed}
554 className={cn(
555 "w-8 h-8 rounded-lg border-2 flex items-center justify-center transition-all mt-1 flex-shrink-0",
556 isServed
557 ? "bg-gray-400 border-gray-400 text-white cursor-not-allowed"
558 : isReady
559 ? "bg-green-500 border-green-500 text-white shadow-lg"
560 : "border-gray-300 hover:border-green-400 hover:bg-green-50"
561 )}
562 >
563 {(isReady || isServed) && <CheckCircle className="w-5 h-5" />}
564 </button>
565 
566 <div className="flex-1 min-w-0">
567 <div className={cn(
568 "font-semibold text-lg mb-2",
569 isServed ? "line-through text-gray-500" : isReady && "line-through text-muted-foreground"
570 )}>
571 {item.quantity}x {item.product?.name || `Item ${index + 1}`}
572 {isServed && <span className="ml-2 text-xs bg-gray-200 text-gray-700 px-2 py-1 rounded">SERVED</span>}
573 </div>
574 
575 {/* Special instructions */}
576 {item.special_instructions && (
577 <div className="text-sm text-orange-600 bg-orange-50 px-2 py-1 rounded mb-2">
578 Note: {item.special_instructions}
579 </div>
580 )}
581 
582 <div className="flex items-center justify-between mt-2">
583 {/* Status indicator */}
584 <div className={cn(
585 "text-xs font-medium px-2 py-1 rounded-full",
586 isServed
587 ? "bg-gray-100 text-gray-600"
588 : isReady
589 ? "bg-green-100 text-green-800"
590 : "bg-orange-100 text-orange-800"
591 )}>
592 {isServed ? '🍽️ Served' : isReady ? '✅ Ready' : '🍳 Cooking'}
593 </div>
594 
595 {/* Individual serve button */}
596 {isReady && !isServed && (
597 <Button
598 size="sm"
599 variant="outline"
600 className="h-6 px-2 text-xs bg-blue-50 hover:bg-blue-100 border-blue-300"
601 onClick={(e) => {
602 e.stopPropagation()
603 handleItemServe(item.id, item.product?.name || 'Item')
604 }}
605 >
606 🍽️ Serve Now
607 </Button>
608 )}
609 </div>
610 </div>
611 </div>
612 )
613 })}
614 </div>
615 </CardContent>
616 </Card>
617 )
618}
619```
620 
621### Sound Notification System
622```typescript
623// Sound notification service for kitchen operations
624class KitchenSoundService {
625 private audioContext: AudioContext | null = null
626 private enabled: boolean = true
627 private volume: number = 0.5
628 
629 async initialize(): Promise<void> {
630 try {
631 this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
632 } catch (error) {
633 console.warn('Audio context not available:', error)
634 }
635 }
636 
637 // ✅ CORRECT: Different sounds for different kitchen events
638 playNewOrderSound(): void {
639 this.playTone(800, 0.5, this.volume * 0.4) // 800Hz for new orders
640 }
641 
642 playAllReadySound(): void {
643 this.playTone(1200, 0.3, this.volume * 0.3) // 1200Hz for all items ready
644 }
645 
646 playItemServedSound(): void {
647 this.playTone(1400, 0.2, this.volume * 0.2) // 1400Hz for item served
648 }
649 
650 private playTone(frequency: number, duration: number, volume: number): void {
651 if (!this.enabled || !this.audioContext) return
652 
653 try {
654 const oscillator = this.audioContext.createOscillator()
655 const gainNode = this.audioContext.createGain()
656 
657 oscillator.connect(gainNode)
658 gainNode.connect(this.audioContext.destination)
659 
660 oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime)
661 gainNode.gain.setValueAtTime(volume, this.audioContext.currentTime)
662 
663 oscillator.start()
664 oscillator.stop(this.audioContext.currentTime + duration)
665 } catch (error) {
666 console.log('Sound playback failed:', error)
667 }
668 }
669 
670 setEnabled(enabled: boolean): void {
671 this.enabled = enabled
672 }
673 
674 setVolume(volume: number): void {
675 this.volume = Math.max(0, Math.min(1, volume))
676 }
677}
678```
679 
680## 🎭 Role-Based Component Patterns
681 
682### Role-Specific Interface Components
683```typescript
684// Role-based component rendering
685import { User } from '@/types'
686 
687interface RoleBasedLayoutProps {
688 user: User
689}
690 
691// ✅ CORRECT: Role-based component switching
692export const RoleBasedLayout: React.FC<RoleBasedLayoutProps> = ({ user }) => {
693 // Admin gets access to all interfaces
694 if (user.role === 'admin') {
695 return <AdminLayout user={user} />
696 }
697
698 // Role-specific interfaces
699 switch (user.role) {
700 case 'server':
701 return <ServerInterface user={user} />
702 case 'counter':
703 return <CounterInterface user={user} />
704 case 'kitchen':
705 return <NewEnhancedKitchenLayout user={user} /> // Enhanced with as-ready service
706 default:
707 return <POSLayout user={user} />
708 }
709}
710 
711// Role-based feature flags
712interface FeatureGateProps {
713 allowedRoles: string[]
714 userRole: string
715 children: React.ReactNode
716 fallback?: React.ReactNode
717}
718 
719export const FeatureGate: React.FC<FeatureGateProps> = ({
720 allowedRoles,
721 userRole,
722 children,
723 fallback = null
724}) => {
725 if (allowedRoles.includes(userRole)) {
726 return <>{children}</>
727 }
728
729 return <>{fallback}</>
730}
731 
732// Usage in components
733<FeatureGate allowedRoles={['admin', 'manager']} userRole={user.role}>
734 <AdminReportsButton />
735</FeatureGate>
736```
737 
738## 🎨 Styling & UI Patterns
739 
740### Conditional Styling with cn()
741```typescript
742// ✅ CORRECT: Use cn() utility for conditional classes
743import { cn } from '@/lib/utils'
744 
745const OrderCard = ({ order, isSelected, status }) => {
746 return (
747 <Card className={cn(
748 "transition-all duration-200",
749 isSelected && "ring-2 ring-primary",
750 status === 'urgent' && "border-destructive",
751 status === 'completed' && "opacity-75"
752 )}>
753 {/* Content */}
754 </Card>
755 )
756}
757```
758 
759### Status-Based Styling
760```typescript
761// Status badge utility
762const getStatusVariant = (status: string) => {
763 const variants = {
764 pending: 'secondary',
765 confirmed: 'default',
766 preparing: 'warning',
767 ready: 'success',
768 served: 'outline',
769 completed: 'success',
770 cancelled: 'destructive',
771 } as const
772 
773 return variants[status] || 'secondary'
774}
775 
776// Usage
777<Badge variant={getStatusVariant(order.status)}>
778 {order.status}
779</Badge>
780```
781 
782## ⚡ Performance Optimization Patterns
783 
784### React.memo for Expensive Components
785```typescript
786// ✅ CORRECT: Memo for components with expensive renders
787export const ProductGrid = React.memo<ProductGridProps>(({
788 products,
789 onProductSelect,
790 selectedProducts
791}) => {
792 return (
793 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
794 {products.map(product => (
795 <ProductCard
796 key={product.id}
797 product={product}
798 onSelect={onProductSelect}
799 isSelected={selectedProducts.includes(product.id)}
800 />
801 ))}
802 </div>
803 )
804}, (prevProps, nextProps) => {
805 // Custom comparison for optimization
806 return (
807 prevProps.products.length === nextProps.products.length &&
808 prevProps.selectedProducts.length === nextProps.selectedProducts.length
809 )
810})
811```
812 
813### Virtualization for Large Lists
814```typescript
815// For large product catalogs or order lists
816import { FixedSizeList as List } from 'react-window'
817 
818const VirtualizedProductList = ({ products, onSelect }) => {
819 const Row = ({ index, style }) => (
820 <div style={style}>
821 <ProductCard
822 product={products[index]}
823 onSelect={onSelect}
824 />
825 </div>
826 )
827 
828 return (
829 <List
830 height={600}
831 itemCount={products.length}
832 itemSize={120}
833 width="100%"
834 >
835 {Row}
836 </List>
837 )
838}
839```
840 
841## 🔄 State Management Patterns
842 
843### Optimistic Updates
844```typescript
845// Optimistic UI updates for better UX
846const useOptimisticOrderStatus = () => {
847 const queryClient = useQueryClient()
848 
849 return useMutation({
850 mutationFn: ({ orderId, status }) =>
851 apiClient.updateOrderStatus(orderId, status),
852
853 onMutate: async ({ orderId, status }) => {
854 // Cancel outgoing refetches
855 await queryClient.cancelQueries({ queryKey: ['orders'] })
856 
857 // Snapshot previous value
858 const previousOrders = queryClient.getQueryData(['orders'])
859 
860 // Optimistically update
861 queryClient.setQueryData(['orders'], (old: any) =>
862 old?.map((order: any) =>
863 order.id === orderId ? { ...order, status } : order
864 )
865 )
866 
867 return { previousOrders }
868 },
869
870 onError: (err, variables, context) => {
871 // Rollback on error
872 if (context?.previousOrders) {
873 queryClient.setQueryData(['orders'], context.previousOrders)
874 }
875 toastHelpers.error('Failed to update order status')
876 },
877
878 onSettled: () => {
879 // Refetch to ensure consistency
880 queryClient.invalidateQueries({ queryKey: ['orders'] })
881 },
882 })
883}
884```
885 
886## 🎯 Error Handling Patterns
887 
888### Error Boundaries
889```typescript
890// Error boundary for catching component errors
891class OrderErrorBoundary extends React.Component {
892 constructor(props) {
893 super(props)
894 this.state = { hasError: false, error: null }
895 }
896 
897 static getDerivedStateFromError(error) {
898 return { hasError: true, error }
899 }
900 
901 componentDidCatch(error, errorInfo) {
902 console.error('Order component error:', error, errorInfo)
903 }
904 
905 render() {
906 if (this.state.hasError) {
907 return (
908 <Card className="p-6 text-center">
909 <CardHeader>
910 <CardTitle className="text-destructive">Something went wrong</CardTitle>
911 </CardHeader>
912 <CardContent>
913 <p className="text-muted-foreground mb-4">
914 We encountered an error while processing your order.
915 </p>
916 <Button
917 onClick={() => this.setState({ hasError: false, error: null })}
918 variant="outline"
919 >
920 Try Again
921 </Button>
922 </CardContent>
923 </Card>
924 )
925 }
926 
927 return this.props.children
928 }
929}
930```
931 
932### Loading States
933```typescript
934// Consistent loading patterns
935const LoadingStates = {
936 Spinner: () => (
937 <div className="flex items-center justify-center p-4">
938 <Loader2 className="h-6 w-6 animate-spin" />
939 </div>
940 ),
941
942 Skeleton: () => (
943 <div className="space-y-3">
944 <div className="h-4 bg-muted animate-pulse rounded" />
945 <div className="h-4 bg-muted animate-pulse rounded w-3/4" />
946 </div>
947 ),
948
949 Button: ({ isLoading, children, ...props }) => (
950 <Button disabled={isLoading} {...props}>
951 {isLoading && <Loader2 className="h-4 w-4 animate-spin mr-2" />}
952 {children}
953 </Button>
954 ),
955}
956```
957 
958## 🛠️ Development Patterns
959 
960### Component Organization
961```typescript
962// ✅ CORRECT: Well-organized component file
963// ProductCard/index.tsx
964export { ProductCard } from './ProductCard'
965export type { ProductCardProps } from './ProductCard'
966 
967// ProductCard/ProductCard.tsx
968import { memo, useCallback } from 'react'
969// ... imports
970 
971interface ProductCardProps {
972 // ... props
973}
974 
975export const ProductCard: React.FC<ProductCardProps> = memo(({
976 // ... implementation
977}))
978 
979// ProductCard/ProductCard.stories.tsx (if using Storybook)
980export default {
981 title: 'Components/ProductCard',
982 component: ProductCard,
983}
984 
985// ProductCard/ProductCard.test.tsx
986describe('ProductCard', () => {
987 // ... tests
988})
989```
990 
991### Type Safety Best Practices
992```typescript
993// ✅ CORRECT: Strict typing throughout
994import type { Product, User, Order } from '@/types'
995 
996// Generic component with proper constraints
997interface DataTableProps<T> {
998 data: T[]
999 columns: Array<{
1000 key: keyof T
1001 header: string
1002 render?: (value: T[keyof T], row: T) => React.ReactNode
1003 }>
1004 onRowClick?: (row: T) => void
1005}
1006 
1007export function DataTable<T extends Record<string, any>>({
1008 data,
1009 columns,
1010 onRowClick
1011}: DataTableProps<T>) {
1012 // Implementation with full type safety
1013}
1014```
1015 
1016## 🚀 Development Commands & Tools
1017 
1018### Essential Commands
1019```bash
1020# Component development
1021npm run dev # Start development server
1022npm run build # Build for production
1023npm run type-check # TypeScript checking
1024 
1025# Quality assurance
1026npm run lint # ESLint checking
1027npm run format # Prettier formatting
1028npm run test # Run component tests
1029 
1030# Component generation (if using generator)
1031npm run generate:component ProductCard
1032```
1033 
1034### Recommended VS Code Extensions
1035- **ES7+ React/Redux/React-Native snippets**
1036- **Auto Rename Tag**
1037- **Bracket Pair Colorizer**
1038- **Tailwind CSS IntelliSense**
1039- **TypeScript Importer**
1040- **React Hook Form DevTools**

Commands it names

  • npm run dev
  • npm run build
  • npm run type-check
  • npm run lint
  • npm run format
  • npm run test
  • npm run generate:component ProductCard

Sections

  • ⚛️ React Component Patterns & Best Practices
  • 🏗️ Component Architecture
  • shadcn/ui Base Components Pattern
  • Business Component Pattern
  • 🎣 Custom Hooks Patterns
  • Data Fetching Hook
  • Business Logic Hook (Shopping Cart)
  • 📝 Form Handling Patterns
  • React Hook Form + Zod Integration
  • Reusable Form Components
  • 🍳 Enhanced Kitchen Component Patterns
  • As-Ready Service Workflow Components
  • Sound Notification System
  • 🎭 Role-Based Component Patterns
  • Role-Specific Interface Components
  • 🎨 Styling & UI Patterns
  • Conditional Styling with cn()
  • Status-Based Styling
  • ⚡ Performance Optimization Patterns
  • React.memo for Expensive Components
  • Virtualization for Large Lists
  • 🔄 State Management Patterns
  • Optimistic Updates
  • 🎯 Error Handling Patterns
  • Error Boundaries
  • Loading States
  • 🛠️ Development Patterns
  • Component Organization
  • Type Safety Best Practices
  • 🚀 Development Commands & Tools
  • Essential Commands
  • Component development
  • Quality assurance
  • Component generation (if using generator)
  • Recommended VS Code Extensions

What it covers

buildtestlint-formatcode-styletypesuiperformanceagent-behaviour

Stack — with the evidence

typescript

(1.00)

react

(1.00)

tailwind

(1.00)

docker

(1.00)

vite

(0.70)

eslint

(0.70)

javascript

(0.50)

Glob targeting

  • *.tsx
  • *.ts
  • *.jsx
  • *.js

Format

Cursor rules

The most expressive format here. Many small .mdc files, each with frontmatter declaring when it should load, so a rule about migrations only enters context when a migration is open. Costs the most to maintain and only one editor reads it.

What the corpus says about it

Repository

Owner
madebyaris
Language
—
License
—
Archived
no

All configs in this repo

Also in madebyaris/poinf-of-sales

Diff this repo’s formats

One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
madebyaris/poinf-of-sales.cursor/rules/admin-interface-patterns.mdc · 118Cursor rulestypescriptreact+5stylearchsecurityapi+262/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/api-patterns.mdc · 118Cursor rulestypescriptreact+5lint-formatstylesecuritydatabase+362/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/authentication-and-security-patterns.mdc · 118Cursor rulestypescriptreact+5setupteststylesecurity+481/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/backend-golang.mdc · 118Cursor rulestypescriptreact+5testlint-formatstylearch+569/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/business-logic-patterns.mdc · 118Cursor rulestypescriptreact+5teststyledatabaseperformance+150/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/database-patterns.mdc · 118Cursor rulestypescriptreact+5stylearchtypessecurity+262/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/development-workflow.mdc · 118Cursor rulestypescriptreact+5setupbuildteststyle+386/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/docker-deployment.mdc · 118Cursor rulestypescriptreact+6setupbuildteststyle+877/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/makefile-scripting.mdc · 118Cursor rulestypescriptreact+5setuplint-formatstylearch+281/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/performance-optimization-patterns.mdc · 118Cursor rulestypescriptreact+5buildteststyledatabase+366/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/project-architecture.mdc · 118Cursor rulestypescriptreact+5setupteststylearch+678/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/react-native-mobile-patterns.mdc · 118Cursor rulestypescriptreact+5buildstylearchui+274/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/role-based-access-patterns.mdc · 118Cursor rulestypescriptreact+5styletypessecuritydatabase+258/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/tech-debt-prevention.mdc · 118Cursor rulestypescriptreact+5styletesting-strategyui50/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/testing-patterns.mdc · 118Cursor rulestypescriptreact+6setupteststylearch+474/1003 days ago
madebyaris/poinf-of-sales.cursor/rules/user-journey-optimization.mdc · 118Cursor rulestypescriptreact+5styleperformanceagent-behaviour50/1003 days ago
Diff against .cursor/rules/admin-interface-patterns.mdc Diff against .cursor/rules/api-patterns.mdc Diff against .cursor/rules/authentication-and-security-patterns.mdc Diff against .cursor/rules/backend-golang.mdc Diff against .cursor/rules/business-logic-patterns.mdc Diff against .cursor/rules/database-patterns.mdc Diff against .cursor/rules/development-workflow.mdc Diff against .cursor/rules/docker-deployment.mdc Diff against .cursor/rules/makefile-scripting.mdc Diff against .cursor/rules/performance-optimization-patterns.mdc Diff against .cursor/rules/project-architecture.mdc Diff against .cursor/rules/react-native-mobile-patterns.mdc Diff against .cursor/rules/role-based-access-patterns.mdc Diff against .cursor/rules/tech-debt-prevention.mdc Diff against .cursor/rules/testing-patterns.mdc Diff against .cursor/rules/user-journey-optimization.mdc

Similar configs

Same format, overlapping stack, ranked by quality.

Same format, overlapping stack, ranked by quality
RepositoryFormatStackCoversScoreChanged
hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126Cursor rulesgobun+5setupbuildtestlint-format+6100/1003 days ago
TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45Cursor rulestypescriptpytest+15testlint-formatstylearch+5100/1003 days ago
markstev/mark-starter.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+14setuptestlint-formatstyle+699/1003 days ago
Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+13setuptestlint-formatstyle+799/1003 days ago
dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+15setuptestlint-formatstyle+799/1003 days ago
deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1Cursor rulestypescriptnextjs+5setuptestlint-formatstyle+799/1003 days ago
langflow-ai/langflow.cursor/rules/docs_development.mdc · 153kCursor rulespythonnode+16setupbuildtestlint-format+797/1003 days ago
TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45Cursor rulestypescriptpytest+15teststyletesting-strategysecurity+397/1003 days ago
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack