From 50473d799d530f0dcce443f162988e92bfc36bbe Mon Sep 17 00:00:00 2001 From: Thanh Quy - wolf <524H0124@student.tdtu.edu.vn> Date: Fri, 3 Apr 2026 20:09:05 +0700 Subject: [PATCH] =?UTF-8?q?T=C3=A1i=20c=E1=BA=A5u=20tr=C3=BAc=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 13 +- ATOMIC_DESIGN_DOCS_INDEX.md | 295 ----- ATOMIC_REDESIGN_SUMMARY.md | 406 ------ QUICK_START_ATOMIC.md | 578 --------- app/(feed)/feed/page.tsx | 125 +- app/(feed)/layout.tsx | 41 +- app/(main)/layout.tsx | 22 +- app/(main)/page.tsx | 146 +-- app/(main)/payment/page.tsx | 2 +- app/(manager)/layout.tsx | 46 +- app/(manager)/manager/page.tsx | 1106 +---------------- components/COMPONENTS.md | 69 +- components/atoms/typography/Heading.tsx | 4 +- components/molecules/cards/Card.types.ts | 18 + components/molecules/cards/ProductCard.tsx | 71 ++ components/molecules/cards/ShopCard.tsx | 42 + components/molecules/cards/index.ts | 3 + components/molecules/index.ts | 7 + .../molecules/search-bar/Search.types.ts | 7 + components/molecules/search-bar/SearchBar.tsx | 34 + components/molecules/search-bar/index.ts | 2 + components/organisms/cart/Cart.types.ts | 3 + components/organisms/cart/CartFab.tsx | 23 + components/organisms/cart/index.ts | 2 + components/organisms/index.ts | 38 + .../organisms/manager/CategoriesTab.tsx | 110 ++ .../organisms/manager/CategoryModal.tsx | 123 ++ components/organisms/manager/ComboModal.tsx | 237 ++++ components/organisms/manager/CombosTab.tsx | 149 +++ .../organisms/manager/DeleteConfirm.tsx | 42 + components/organisms/manager/Manager.types.ts | 31 + components/organisms/manager/ProductModal.tsx | 165 +++ components/organisms/manager/ProductsTab.tsx | 239 ++++ components/organisms/manager/StatusBadge.tsx | 20 + components/organisms/manager/index.ts | 15 + components/organisms/modals/Modal.types.ts | 14 + components/organisms/modals/ReviewModal.tsx | 157 +++ components/organisms/modals/index.ts | 2 + .../organisms/navigation/CategorySidebar.tsx | 116 ++ .../organisms/navigation/Navigation.types.ts | 10 + components/organisms/navigation/index.ts | 2 + .../organisms/product-grid/ProductGrid.tsx | 90 ++ .../product-grid/ProductGrid.types.ts | 4 + components/organisms/product-grid/index.ts | 2 + components/organisms/shop-grid/ShopGrid.tsx | 44 + .../organisms/shop-grid/ShopGrid.types.ts | 4 + components/organisms/shop-grid/index.ts | 2 + .../templates/auth-layout/AuthLayout.tsx | 13 + .../templates/auth-layout/AuthLayout.types.ts | 3 + components/templates/auth-layout/index.ts | 2 + .../templates/feed-layout/FeedLayout.tsx | 40 + .../templates/feed-layout/FeedLayout.types.ts | 3 + components/templates/feed-layout/index.ts | 2 + components/templates/index.ts | 15 + .../templates/main-layout/MainLayout.tsx | 27 + .../templates/main-layout/MainLayout.types.ts | 3 + components/templates/main-layout/index.ts | 2 + .../manager-layout/ManagerLayout.tsx | 49 + .../manager-layout/ManagerLayout.types.ts | 3 + components/templates/manager-layout/index.ts | 2 + 60 files changed, 2136 insertions(+), 2709 deletions(-) delete mode 100644 ATOMIC_DESIGN_DOCS_INDEX.md delete mode 100644 ATOMIC_REDESIGN_SUMMARY.md delete mode 100644 QUICK_START_ATOMIC.md create mode 100644 components/molecules/cards/Card.types.ts create mode 100644 components/molecules/cards/ProductCard.tsx create mode 100644 components/molecules/cards/ShopCard.tsx create mode 100644 components/molecules/cards/index.ts create mode 100644 components/molecules/index.ts create mode 100644 components/molecules/search-bar/Search.types.ts create mode 100644 components/molecules/search-bar/SearchBar.tsx create mode 100644 components/molecules/search-bar/index.ts create mode 100644 components/organisms/cart/Cart.types.ts create mode 100644 components/organisms/cart/CartFab.tsx create mode 100644 components/organisms/cart/index.ts create mode 100644 components/organisms/index.ts create mode 100644 components/organisms/manager/CategoriesTab.tsx create mode 100644 components/organisms/manager/CategoryModal.tsx create mode 100644 components/organisms/manager/ComboModal.tsx create mode 100644 components/organisms/manager/CombosTab.tsx create mode 100644 components/organisms/manager/DeleteConfirm.tsx create mode 100644 components/organisms/manager/Manager.types.ts create mode 100644 components/organisms/manager/ProductModal.tsx create mode 100644 components/organisms/manager/ProductsTab.tsx create mode 100644 components/organisms/manager/StatusBadge.tsx create mode 100644 components/organisms/manager/index.ts create mode 100644 components/organisms/modals/Modal.types.ts create mode 100644 components/organisms/modals/ReviewModal.tsx create mode 100644 components/organisms/modals/index.ts create mode 100644 components/organisms/navigation/CategorySidebar.tsx create mode 100644 components/organisms/navigation/Navigation.types.ts create mode 100644 components/organisms/navigation/index.ts create mode 100644 components/organisms/product-grid/ProductGrid.tsx create mode 100644 components/organisms/product-grid/ProductGrid.types.ts create mode 100644 components/organisms/product-grid/index.ts create mode 100644 components/organisms/shop-grid/ShopGrid.tsx create mode 100644 components/organisms/shop-grid/ShopGrid.types.ts create mode 100644 components/organisms/shop-grid/index.ts create mode 100644 components/templates/auth-layout/AuthLayout.tsx create mode 100644 components/templates/auth-layout/AuthLayout.types.ts create mode 100644 components/templates/auth-layout/index.ts create mode 100644 components/templates/feed-layout/FeedLayout.tsx create mode 100644 components/templates/feed-layout/FeedLayout.types.ts create mode 100644 components/templates/feed-layout/index.ts create mode 100644 components/templates/index.ts create mode 100644 components/templates/main-layout/MainLayout.tsx create mode 100644 components/templates/main-layout/MainLayout.types.ts create mode 100644 components/templates/main-layout/index.ts create mode 100644 components/templates/manager-layout/ManagerLayout.tsx create mode 100644 components/templates/manager-layout/ManagerLayout.types.ts create mode 100644 components/templates/manager-layout/index.ts diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 674bb61..87d58db 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,7 +3,18 @@ "allow": [ "Bash(npx next build)", "Bash(npx tsc --noEmit)", - "Bash(npm run lint)" + "Bash(npm run lint)", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(main\\)/\")", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(feed\\)/\")", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(manager\\)/\")", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(main\\)/payment/\")", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(main\\)/login/\")", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(main\\)/register/\")", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(feed\\)/feed/\")", + "Bash(ls \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(manager\\)/manager/\")", + "Bash(npm run build)", + "Bash(cat \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(manager\\)/manager/page.tsx\")", + "Bash(wc -l \"c:/VS code/CongNghePhanMem/Final/frondend/app/\\(manager\\)/manager/page.tsx\")" ] } } diff --git a/ATOMIC_DESIGN_DOCS_INDEX.md b/ATOMIC_DESIGN_DOCS_INDEX.md deleted file mode 100644 index ff7ccaa..0000000 --- a/ATOMIC_DESIGN_DOCS_INDEX.md +++ /dev/null @@ -1,295 +0,0 @@ -# Atomic Design Documentation Index - -**Updated:** 2026-04-03 **Status:** Ready for Implementation **Branch:** -atomic_design - ---- - -## 📚 Documentation Files - -### 1. **ATOMIC_REDESIGN_SUMMARY.md** 📋 - -**What:** High-level executive summary **Length:** ~300 lines **Best for:** -Quick overview, time estimates, benefits **Read time:** 10-15 minutes - -✅ Current strengths analysis ✅ Key issues breakdown ✅ Implementation timeline -✅ File structure overview ✅ Next steps - ---- - -### 2. **OPTIMIZATION_PLAN.md** 📘 (MAIN DOCUMENT) - -**What:** Comprehensive implementation guide **Length:** ~400+ lines **Best -for:** Detailed planning, technical specs, migration strategy **Read time:** -30-45 minutes - -✅ Full current state analysis ✅ Atomic design mapping (all 5 levels) ✅ -Phase-by-phase breakdown ✅ Code quality recommendations ✅ Testing strategies -✅ Migration checklist ✅ Success criteria ✅ File structure details - -**👉 START HERE for full understanding** - ---- - -### 3. **QUICK_START_ATOMIC.md** ⚡ - -**What:** Step-by-step implementation guide **Length:** ~200 lines **Best for:** -Getting hands-on immediately **Read time:** 20-30 minutes (implementation 2-3 -hours) - -✅ Step 1: Button Atom (30 min) ✅ Step 2: TextInput Atom (20 min) ✅ Step 3: -Typography Atoms (45 min) ✅ Step 4: Badge Atom (15 min) ✅ Step 5: Divider Atom -(5 min) ✅ Step 6: Atoms Index (10 min) ✅ Step 7: Replace existing code ✅ Step -8: Verify everything works - -**👉 FOLLOW THIS to start building today** - ---- - -### 4. **Atomic.md** (Original) - -**What:** Atomic Design pattern specification **Length:** ~600 lines **Best -for:** Understanding the pattern, design principles **Read time:** 45-60 minutes -(reference material) - -✅ Pattern overview ✅ Detailed examples ✅ Best practices ✅ Import patterns ✅ -Testing guidelines ✅ Accessibility notes - ---- - -## 🗺️ Recommended Reading Order - -### For Decision Makers / Team Leads - -1. Start: **ATOMIC_REDESIGN_SUMMARY.md** (15 min) → Understand scope and - benefits -2. Reference: **Atomic.md** sections 8-10 (15 min) → Understand best practices -3. Planning: **OPTIMIZATION_PLAN.md** sections "Implementation Priority" (10 - min) → Review timeline - -**Total: 40 minutes to make informed decision** - ---- - -### For Developers Implementing Now - -1. Start: **QUICK_START_ATOMIC.md** (20 min reading + 2-3 hours implementing) → - Build foundation atoms -2. Reference: **OPTIMIZATION_PLAN.md** sections 1-5 (20 min) → Understand full - scope -3. Checklist: **OPTIMIZATION_PLAN.md** section "Migration Checklist" (5 min) → - Track progress - -**Total: 1-2 hours reading, ~2-3 hours hands-on, repeat 5x for full -implementation** - ---- - -### For Architects / Code Reviewers - -1. Deep dive: **OPTIMIZATION_PLAN.md** (40 min) → Full technical specification -2. Reference: **Atomic.md** (30 min) → Design patterns and standards -3. Implementation: **QUICK_START_ATOMIC.md** (20 min) → Code examples and - structure - -**Total: 90 minutes for full understanding** - ---- - -## 📊 Document Comparison - -| Document | Length | Audience | Purpose | Depth | -| ------------------------------ | ------ | ---------- | ------------------ | --------- | -| **ATOMIC_REDESIGN_SUMMARY.md** | 300 L | Everyone | Quick overview | Shallow | -| **QUICK_START_ATOMIC.md** | 200 L | Developers | Start implementing | Medium | -| **OPTIMIZATION_PLAN.md** | 400+ L | Architects | Full strategy | Deep | -| **Atomic.md** | 600 L | Designers | Pattern spec | Reference | - ---- - -## 🎯 Quick Links by Question - -### "How long will this take?" - -→ **ATOMIC_REDESIGN_SUMMARY.md** → Section "Time Estimates" → -**OPTIMIZATION_PLAN.md** → Section "Implementation Priority" - -### "What's wrong with our current code?" - -→ **ATOMIC_REDESIGN_SUMMARY.md** → Section "Key Issues to Fix" → -**OPTIMIZATION_PLAN.md** → Section "Current State Analysis" - -### "How do I start?" - -→ **QUICK_START_ATOMIC.md** → Just follow steps 1-8 - -### "What will the file structure look like?" - -→ **ATOMIC_REDESIGN_SUMMARY.md** → Section "File Structure After Implementation" -→ **OPTIMIZATION_PLAN.md** → Section "File Structure After Implementation" - -### "What are the benefits?" - -→ **ATOMIC_REDESIGN_SUMMARY.md** → Section "Benefits You'll Get" → -**OPTIMIZATION_PLAN.md** → Introduction - -### "What design principles should I follow?" - -→ **Atomic.md** → Section "Best Practices" + "Common Patterns" - -### "How do I test this?" - -→ **OPTIMIZATION_PLAN.md** → Section "Testing Strategy per Atomic Level" - -### "Will this break existing code?" - -→ **OPTIMIZATION_PLAN.md** → Section "Migration Guide" → -**QUICK_START_ATOMIC.md** → Step 7 "Replace Existing Code (Optional, Gradual)" - ---- - -## 📋 Current Project State - -**Components to migrate:** - -- ✅ `components/CartProduct.tsx` → `molecules/cards/ProductCard.tsx` -- ✅ `components/CartFab.tsx` → `organisms/cart/CartFab.tsx` -- ✅ `components/Navbar.tsx` → `organisms/navigation/Navbar.tsx` -- ✅ `components/ReviewModal.tsx` → `organisms/modals/ReviewModal.tsx` - -**Logic to extract:** - -- ✅ Search bar → `molecules/search-bar/SearchInput.tsx` -- ✅ Category menu → `organisms/navigation/CategoryMenu.tsx` -- ✅ Product grid → `organisms/product-grid/ProductGrid.tsx` -- ✅ Rating logic → `molecules/ratings/RatingInput.tsx` - -**New atoms needed:** 20+ **New molecules needed:** 10+ **New organisms -needed:** 8+ **Templates needed:** 5 - ---- - -## 🔄 Implementation Phases - -### Phase 1: Atoms (2-3 days) - -Foundation library - everything depends on this. **See:** QUICK_START_ATOMIC.md -for hands-on - -### Phase 2: Molecules (2-3 days) - -Combine atoms into reusable UI components. **See:** OPTIMIZATION_PLAN.md Section -2 - -### Phase 3: Organisms (2-3 days) - -Extract existing logic into organized sections. **See:** OPTIMIZATION_PLAN.md -Section 3 - -### Phase 4: Templates (1-2 days) - -Create reusable page layouts. **See:** OPTIMIZATION_PLAN.md Section 4 - -### Phase 5: Pages (1 day) - -Update pages to use new hierarchy. **See:** OPTIMIZATION_PLAN.md Section 5 - -**Total:** 8-10 days for full implementation **Quick wins:** 2.5 hours to get -foundation - ---- - -## ✅ Success Metrics - -After implementation, verify: - -- [ ] 0 components in `components/` root -- [ ] All atoms properly organized -- [ ] All molecules use atoms -- [ ] All organisms use molecules -- [ ] All pages use templates + organisms -- [ ] No lint errors: `npm run lint` -- [ ] Properly formatted: `npm run format` -- [ ] All interactive components work -- [ ] Responsive on all breakpoints -- [ ] Accessibility requirements met - ---- - -## 🚀 Getting Started Today - -### Minimum (2.5 hours) - -1. Read: **QUICK_START_ATOMIC.md** (20 min) -2. Implement: Steps 1-6 (2+ hours) -3. Verify: Step 8 (10 min) - -**Result:** Solid atom foundation - -### Recommended (4-5 hours) - -1. Read: **ATOMIC_REDESIGN_SUMMARY.md** (15 min) -2. Skim: **OPTIMIZATION_PLAN.md** first sections (15 min) -3. Implement: **QUICK_START_ATOMIC.md** (2+ hours) -4. Plan: Next phases using checklist (30 min) - -**Result:** Foundation + clear roadmap - -### Comprehensive (90 min + implementation) - -1. Read: **ATOMIC_REDESIGN_SUMMARY.md** (15 min) -2. Full read: **OPTIMIZATION_PLAN.md** (40 min) -3. Reference: **Atomic.md** sections 8-10 (15 min) -4. Implement: **QUICK_START_ATOMIC.md** (2+ hours) -5. Plan: All 5 phases (20 min) - -**Result:** Full understanding + ready for team - ---- - -## 💬 FAQ - -**Q: Do I have to do all 5 phases?** A: No, start with Phase 1 (atoms). Then -decide if you want molecules, organisms, etc. - -**Q: Can I do this gradually?** A: Yes! Add atoms/molecules as you create new -components. No deadline. - -**Q: Will this break production?** A: No, you can refactor gradually. Existing -code stays unchanged until updated. - -**Q: Do I need tests?** A: Not required, but highly recommended. See -OPTIMIZATION_PLAN.md section on testing. - -**Q: Should the whole team do this?** A: Yes, once atoms are set up, everyone -should use them. Share the docs! - ---- - -## 📞 Questions? - -All answers are in these documents: - -- **Timeline questions:** ATOMIC_REDESIGN_SUMMARY.md -- **Technical questions:** OPTIMIZATION_PLAN.md -- **How-to questions:** QUICK_START_ATOMIC.md -- **Pattern questions:** Atomic.md - ---- - -## 🎓 Learning Path - -1. **Understand the problem** (15 min) → ATOMIC_REDESIGN_SUMMARY.md - -2. **Learn the pattern** (30 min) → Atomic.md sections 1-7 - -3. **See the full plan** (40 min) → OPTIMIZATION_PLAN.md - -4. **Get hands-on** (2-3 hours) → QUICK_START_ATOMIC.md - -5. **Execute the rest** (5-7 days) → Follow OPTIMIZATION_PLAN.md phases - ---- - -**Status:** ✅ Complete Analysis & Documentation **Last Updated:** 2026-04-03 -**Branch:** atomic_design **Next Action:** Start with QUICK_START_ATOMIC.md diff --git a/ATOMIC_REDESIGN_SUMMARY.md b/ATOMIC_REDESIGN_SUMMARY.md deleted file mode 100644 index 4e0c952..0000000 --- a/ATOMIC_REDESIGN_SUMMARY.md +++ /dev/null @@ -1,406 +0,0 @@ -# Atomic Design Restructuring - Executive Summary - -## 📋 What I've Done - -I've analyzed your project against the **Atomic Design pattern** defined in -`Atomic.md` and created a comprehensive optimization plan. Here's what you need -to know: - ---- - -## ✅ Current Strengths - -Your project has **excellent foundations:** - -| Area | Grade | Notes | -| ------------------------ | ---------- | ------------------------------------- | -| **Context API Setup** | ⭐⭐⭐⭐⭐ | Well-organized, proper TypeScript | -| **Responsive Design** | ⭐⭐⭐⭐ | Mobile-first, good breakpoints | -| **Documentation** | ⭐⭐⭐⭐ | Detailed COMPONENTS.md, good comments | -| **App Router Structure** | ⭐⭐⭐⭐ | Perfect route group organization | -| **Component Quality** | ⭐⭐⭐ | Good, but missing atomic structure | - ---- - -## 🔴 Key Issues to Fix - -### 1. **No Atomic Hierarchy** (CRITICAL) - -``` -Current: -components/ -├── CartProduct.tsx -├── CartFab.tsx -├── Navbar.tsx -├── ReviewModal.tsx -└── COMPONENTS.md - -Target: -components/ -├── atoms/ ← Reusable UI blocks (Button, Input, Text, etc.) -├── molecules/ ← Small combinations (ProductCard, SearchBar, etc.) -├── organisms/ ← Complex sections (ProductGrid, Navbar, ReviewModal) -├── templates/ ← Page layouts (MainLayout, AuthLayout, etc.) -└── COMPONENTS.md -``` - -**Impact:** Makes reusability impossible, increases maintenance burden - ---- - -### 2. **Inline Component Code** (HIGH) - -Currently, UI logic is scattered throughout pages: - -❌ **app/(main)/page.tsx** (192 lines) - -- Lines 106-127: Search input with clear button → should be `SearchInput` - molecule -- Lines 131-153: Category menu logic → should be `CategoryMenu` organism -- Lines 156-188: Product grid rendering → should be `ProductGrid` organism - -✅ **Should be:** - -```tsx - -``` - -**Impact:** Pages are bloated, hard to test, hard to reuse - ---- - -### 3. **Missing Atom Library** (HIGH) - -You're creating buttons/inputs inline everywhere: - -❌ **Inline button** (CartProduct.tsx:73-79) - -```tsx - -``` - -❌ **Another button** (ReviewModal.tsx:147-154) - -```tsx - -``` - -❌ **Search input** (app/(main)/page.tsx:108-127) - -```tsx -
- - - {searchQuery && } -
-``` - -✅ **Should be atoms:** - -```tsx - - - -``` - -**Impact:** Inconsistent styling, 100+ lines of duplicate code, hard to update -theme - ---- - -### 4. **No Component Hierarchy** (MEDIUM) - -Components should be composable: - -❌ **Current:** CartProduct is standalone ✅ **Should be:** ProductCard molecule -using atoms (Text, Badge, Button, Image) - -❌ **Current:** ReviewModal contains all form logic ✅ **Should be:** -ReviewModal organism containing ReviewForm molecule with FormField atoms - ---- - -## 📊 Numbers - -| Metric | Current | Target | Benefit | -| ---------------------- | --------- | ---------- | ----------------------- | -| **Components in root** | 5 | 0 | Better organization | -| **Total components** | ~5 | ~40+ | More reusable | -| **Atoms** | 0 | ~20 | Foundation library | -| **Molecules** | 0 | ~10 | Mid-level composability | -| **Organisms** | 0 | ~8 | Complex sections | -| **Templates** | 0 | 5 | Layout reuse | -| **Avg page size** | 192 lines | <100 lines | More maintainable | -| **Button duplication** | 5+ places | 1 atom | DRY principle | - ---- - -## 🎯 Implementation Plan - -### **Phase 1: Atoms** (Foundation - 2-3 days) - -Create reusable UI blocks that everything else depends on: - -- Button, IconButton -- TextInput, SearchInput, Textarea -- Heading, Text, Caption (typography) -- Badge, PriceBadge -- Icon components -- Divider, Spinner, Skeleton - -✅ **First step:** All other components will use these - ---- - -### **Phase 2: Molecules** (Combinations - 2-3 days) - -Combine atoms into small, reusable UI units: - -- **ProductCard** (rename CartProduct) -- **SearchBar** (search input + icon + clear button) -- **FormField** (label + input + error) -- **RatingInput** (5-star interactive rating) -- **PriceTag** (formatted price display) -- Breadcrumb, Tabs, etc. - -✅ **These use atoms from Phase 1** - ---- - -### **Phase 3: Organisms** (Complex Sections - 2-3 days) - -Extract existing logic into organized components: - -- **ProductGrid** (extract from page, use ProductCard) -- **CategoryMenu** (extract from page) -- **Navbar** (move existing) -- **CartFab** (move existing) -- **ReviewModal** (move existing) -- Forms (LoginForm, RegisterForm, etc.) -- ShopGrid, FeaturedSection, etc. - -✅ **These use molecules + atoms** - ---- - -### **Phase 4: Templates** (Layouts - 1-2 days) - -Create page layout structures: - -- MainLayout (header + sidebar + content + footer) -- FeedLayout -- AuthLayout -- CheckoutLayout -- ManagerLayout - -✅ **These wrap pages, use organisms** - ---- - -### **Phase 5: Update Pages** (Integration - 1 day) - -Update pages to use new hierarchy: - -- Replace inline logic with component calls -- Reduce page file sizes -- Update imports - -✅ **Pages become <100 lines of clean composition** - ---- - -## ⏱️ Time Estimates - -| Phase | Scope | Time | Blocker | -| ------------ | ---------------------- | -------------- | ------------ | -| 1️⃣ Atoms | 7 component groups | 2-3 days | None | -| 2️⃣ Molecules | 8 component groups | 2-3 days | Phase 1 done | -| 3️⃣ Organisms | 8 component groups | 2-3 days | Phase 2 done | -| 4️⃣ Templates | 5 component groups | 1-2 days | Phase 3 done | -| 5️⃣ Pages | Page updates + testing | 1 day | Phase 4 done | -| **Total** | **All 40+ components** | **~8-10 days** | - | - -### Quick Win Path (~2.5 hours) - -If you want to start immediately: - -1. Create Button atom (30 min) → unblocks everything -2. Create TextInput atom (20 min) -3. Create Typography atoms (45 min) -4. Move existing components (30 min) -5. Rename CartProduct → ProductCard (15 min) - -This gives you a solid **foundation to build on gradually**. - ---- - -## 📁 File Structure After Implementation - -``` -components/ -├── atoms/ (20 atomic components) -│ ├── buttons/ -│ ├── inputs/ -│ ├── typography/ -│ ├── badges/ -│ ├── icons/ -│ ├── dividers/ -│ ├── loaders/ -│ └── index.ts -├── molecules/ (10 molecular combinations) -│ ├── cards/ -│ ├── form-groups/ -│ ├── ratings/ -│ ├── price-display/ -│ ├── search-bar/ -│ ├── breadcrumb/ -│ ├── tabs/ -│ └── index.ts -├── organisms/ (8 complex sections) -│ ├── navigation/ -│ ├── cart/ -│ ├── product-grid/ -│ ├── forms/ -│ ├── modals/ -│ ├── shop-grid/ -│ ├── hero-section/ -│ ├── featured-section/ -│ └── index.ts -├── templates/ (5 layout templates) -│ ├── main-layout/ -│ ├── feed-layout/ -│ ├── manager-layout/ -│ ├── checkout-layout/ -│ ├── auth-layout/ -│ └── index.ts -└── COMPONENTS.md (updated) -``` - ---- - -## 🎁 Benefits You'll Get - -### Immediate - -- ✅ Consistent button styling across entire app -- ✅ Centralized color/spacing changes -- ✅ Better code organization -- ✅ Clear component boundaries - -### Short-term (1-2 weeks) - -- ✅ 100+ lines of code removed (no duplication) -- ✅ Easier feature additions -- ✅ Pages drop from 192 → <100 lines -- ✅ Team can follow clear patterns - -### Long-term (1-3 months) - -- ✅ 40% faster development -- ✅ 50% easier testing -- ✅ Better for new team members -- ✅ Ready for design system evolution -- ✅ Can add Storybook with confidence - ---- - -## 📖 Documents Created - -### 1. **OPTIMIZATION_PLAN.md** (THIS IS THE MAIN DOCUMENT) - -- Complete analysis of current state -- Detailed breakdown of each phase -- Component mapping (old → new) -- Code quality recommendations -- Testing strategies -- Migration checklist -- Success criteria - -**👉 READ THIS FIRST** - It's 400+ lines but super detailed - -### 2. **This file** (ATOMIC_REDESIGN_SUMMARY.md) - -- High-level overview -- Quick reference guide -- Time estimates -- Benefits summary - ---- - -## 🚀 Next Steps - -### Option A: Start Immediately (Recommended) - -1. Read `OPTIMIZATION_PLAN.md` fully -2. Implement **Quick Wins** section (2.5 hours) -3. Get feedback from team -4. Continue with Phases 1-5 - -### Option B: Plan First - -1. Share `OPTIMIZATION_PLAN.md` with team -2. Discuss timeline and priorities -3. Decide which phases to implement -4. Create sprint backlog - -### Option C: Gradual Refactoring - -1. Start with atoms only -2. Add them as new components needed -3. Gradually move existing components -4. No hard deadline - organic growth - ---- - -## ❓ Questions to Answer - -Before starting, clarify: - -1. **Scope:** Do you want all 5 phases or just atoms/molecules first? -2. **Timeline:** Rush (1 week) or steady (2-3 weeks)? -3. **Testing:** Should we add unit tests for new components? -4. **Breaking changes:** OK to update imports everywhere? -5. **Team:** Will others be working on this too? - ---- - -## 📚 Resources - -- **Main guide:** `OPTIMIZATION_PLAN.md` (comprehensive) -- **Atomic Design reference:** `Atomic.md` (your design spec) -- **Current docs:** `COMPONENTS.md`, `app/APP.md`, `lib/LIB.md` -- **Implementation examples:** Sections 1-5 in OPTIMIZATION_PLAN.md - ---- - -## 💡 Key Insight - -Your code is **good quality** - but it's not following the atomic structure you -defined in `Atomic.md`. Think of it like: - -- ✅ You have **good ingredients** (contexts, hooks, styling) -- ✅ You have a **recipe** (Atomic.md) -- ❌ But you haven't **followed the recipe** - -This document shows you exactly how to follow the recipe step-by-step. - ---- - -## 📞 Support - -When implementing, you'll have detailed guidance in: - -1. **OPTIMIZATION_PLAN.md** - Strategy & structure -2. **Atomic.md** - Design patterns & examples -3. **COMPONENTS.md** - Current component docs (will expand) - -All the pieces are in place. It's just about organizing them correctly. - ---- - -**Status:** ✅ Analysis Complete - Ready for Implementation **Last Updated:** -2026-04-03 **Document:** ATOMIC_REDESIGN_SUMMARY.md diff --git a/QUICK_START_ATOMIC.md b/QUICK_START_ATOMIC.md deleted file mode 100644 index 1b47464..0000000 --- a/QUICK_START_ATOMIC.md +++ /dev/null @@ -1,578 +0,0 @@ -# Quick Start: Atomic Design Implementation - -**TL;DR:** Follow this to start building components the atomic way. Do **Step -1** first (creates foundation), then everything else uses it. - ---- - -## Step 1: Create Button Atom ⚡ (30 minutes) - -This is the **most important** component. Everything else will use it. - -### Create directory - -```bash -mkdir -p components/atoms/buttons -``` - -### Create `components/atoms/buttons/Button.types.ts` - -```typescript -import { ButtonHTMLAttributes } from "react"; - -export interface ButtonProps extends ButtonHTMLAttributes { - variant?: "primary" | "secondary" | "danger" | "ghost"; - size?: "sm" | "md" | "lg"; - icon?: string; // FontAwesome class like "fa-solid fa-cart-plus" - iconPosition?: "left" | "right"; - disabled?: boolean; - children: React.ReactNode; - className?: string; -} -``` - -### Create `components/atoms/buttons/Button.tsx` - -```typescript -import type { ButtonProps } from "./Button.types"; - -export default function Button({ - variant = "primary", - size = "md", - icon, - iconPosition = "left", - disabled = false, - children, - className = "", - ...props -}: ButtonProps) { - const baseStyles = - "font-semibold rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center justify-center gap-1.5"; - - const variants = { - primary: "bg-(--color-primary) text-white hover:bg-(--color-primary-dark) active:scale-95", - secondary: "border border-(--color-border) hover:bg-(--color-border-light) active:scale-95", - danger: "bg-red-500 text-white hover:bg-red-600 active:scale-95", - ghost: "bg-transparent hover:bg-(--color-border-light) active:scale-95", - }; - - const sizes = { - sm: "px-3 py-1.5 text-xs", - md: "px-4 py-2 text-sm", - lg: "px-6 py-3 text-base", - }; - - const iconClasses = `text-sm shrink-0`; - - return ( - - ); -} -``` - -### Create `components/atoms/buttons/index.ts` - -```typescript -export { default as Button } from "./Button"; -export type { ButtonProps } from "./Button.types"; -``` - -### Test it - -```tsx -// Try in any page -import { Button } from "@/components/atoms/buttons"; - - - - -``` - ---- - -## Step 2: Create TextInput Atom (20 minutes) - -### Create directory - -```bash -mkdir -p components/atoms/inputs -``` - -### Create `components/atoms/inputs/Input.types.ts` - -```typescript -import { InputHTMLAttributes } from "react"; - -export interface TextInputProps extends InputHTMLAttributes { - label?: string; - error?: string; - icon?: string; // FontAwesome class - onIconClick?: () => void; - className?: string; -} -``` - -### Create `components/atoms/inputs/TextInput.tsx` - -```typescript -import type { TextInputProps } from "./Input.types"; - -export default function TextInput({ - label, - error, - icon, - onIconClick, - className = "", - ...props -}: TextInputProps) { - return ( -
- {label && ( - - )} -
- - {icon && ( - - )} -
- {error &&

{error}

} -
- ); -} -``` - -### Create `components/atoms/inputs/SearchInput.tsx` - -```typescript -import { InputHTMLAttributes } from "react"; - -export default function SearchInput({ - value, - onChange, - onClear, - ...props -}: InputHTMLAttributes & { - onClear?: () => void; -}) { - return ( -
- - - {value && onClear && ( - - )} -
- ); -} -``` - -### Create `components/atoms/inputs/index.ts` - -```typescript -export { default as TextInput } from "./TextInput"; -export { default as SearchInput } from "./SearchInput"; -export type { TextInputProps } from "./Input.types"; -``` - ---- - -## Step 3: Create Typography Atoms (45 minutes) - -### Create directory - -```bash -mkdir -p components/atoms/typography -``` - -### Create `components/atoms/typography/Typography.types.ts` - -```typescript -import { HTMLAttributes } from "react"; - -export interface HeadingProps extends HTMLAttributes { - level?: 1 | 2 | 3 | 4 | 5 | 6; - children: React.ReactNode; -} - -export interface TextProps extends HTMLAttributes { - variant?: "body1" | "body2" | "caption" | "label"; - children: React.ReactNode; -} - -export interface CaptionProps extends HTMLAttributes { - children: React.ReactNode; -} -``` - -### Create `components/atoms/typography/Heading.tsx` - -```typescript -import type { HeadingProps } from "./Typography.types"; - -export default function Heading({ - level = 2, - children, - className = "", - ...props -}: HeadingProps) { - const sizes = { - 1: "text-3xl font-bold", - 2: "text-2xl font-bold", - 3: "text-xl font-bold", - 4: "text-lg font-semibold", - 5: "text-base font-semibold", - 6: "text-sm font-semibold", - }; - - const Tag = `h${level}` as keyof JSX.IntrinsicElements; - - return ( - - {children} - - ); -} -``` - -### Create `components/atoms/typography/Text.tsx` - -```typescript -import type { TextProps } from "./Typography.types"; - -export default function Text({ - variant = "body1", - children, - className = "", - ...props -}: TextProps) { - const variants = { - body1: "text-base text-(--color-text-primary)", - body2: "text-sm text-(--color-text-secondary)", - caption: "text-xs text-(--color-text-muted)", - label: "text-sm font-medium text-(--color-text-secondary)", - }; - - return ( -

- {children} -

- ); -} -``` - -### Create `components/atoms/typography/Caption.tsx` - -```typescript -import type { CaptionProps } from "./Typography.types"; - -export default function Caption({ - children, - className = "", - ...props -}: CaptionProps) { - return ( - - {children} - - ); -} -``` - -### Create `components/atoms/typography/index.ts` - -```typescript -export { default as Heading } from "./Heading"; -export { default as Text } from "./Text"; -export { default as Caption } from "./Caption"; -export type { HeadingProps, TextProps, CaptionProps } from "./Typography.types"; -``` - ---- - -## Step 4: Create Badge Atom (15 minutes) - -### Create directory - -```bash -mkdir -p components/atoms/badges -``` - -### Create `components/atoms/badges/Badge.types.ts` - -```typescript -import { HTMLAttributes } from "react"; - -export interface BadgeProps extends HTMLAttributes { - variant?: "primary" | "secondary" | "success" | "danger" | "warning"; - size?: "sm" | "md"; - children: React.ReactNode; -} -``` - -### Create `components/atoms/badges/Badge.tsx` - -```typescript -import type { BadgeProps } from "./Badge.types"; - -export default function Badge({ - variant = "primary", - size = "md", - children, - className = "", - ...props -}: BadgeProps) { - const variants = { - primary: "bg-(--color-primary) text-white", - secondary: "bg-(--color-border-light) text-(--color-text-secondary)", - success: "bg-green-100 text-green-700", - danger: "bg-red-100 text-red-700", - warning: "bg-yellow-100 text-yellow-700", - }; - - const sizes = { - sm: "px-2 py-1 text-xs", - md: "px-3 py-1.5 text-sm", - }; - - return ( - - {children} - - ); -} -``` - -### Create `components/atoms/badges/index.ts` - -```typescript -export { default as Badge } from "./Badge"; -export type { BadgeProps } from "./Badge.types"; -``` - ---- - -## Step 5: Create Divider Atom (5 minutes) - -### Create directory - -```bash -mkdir -p components/atoms/dividers -``` - -### Create `components/atoms/dividers/Divider.tsx` - -```typescript -import { HTMLAttributes } from "react"; - -export interface DividerProps extends HTMLAttributes { - orientation?: "horizontal" | "vertical"; -} - -export default function Divider({ - orientation = "horizontal", - className = "", - ...props -}: DividerProps) { - return ( -
- ); -} -``` - ---- - -## Step 6: Create Atoms Index (10 minutes) - -### Create `components/atoms/index.ts` - -```typescript -// Buttons -export { Button } from "./buttons"; -export type { ButtonProps } from "./buttons"; - -// Inputs -export { TextInput, SearchInput } from "./inputs"; -export type { TextInputProps } from "./inputs"; - -// Typography -export { Heading, Text, Caption } from "./typography"; -export type { HeadingProps, TextProps, CaptionProps } from "./typography"; - -// Badges -export { Badge } from "./badges"; -export type { BadgeProps } from "./badges"; - -// Dividers -export { Divider } from "./dividers"; -export type { DividerProps } from "./dividers"; -``` - ---- - -## Step 7: Replace Existing Code (Optional, Gradual) - -Now you have atoms! Start using them: - -### Before (inline) - -```tsx -// components/CartProduct.tsx - -``` - -### After (using atom) - -```tsx -// components/CartProduct.tsx -import { Button } from "@/components/atoms"; - -; -``` - -### Before (inline) - -```tsx -// app/(main)/page.tsx -

- {activeCategoryLabel} -

-

- {filteredProducts.length} món -

-``` - -### After (using atoms) - -```tsx -// app/(main)/page.tsx -import { Heading, Text } from "@/components/atoms"; - -{activeCategoryLabel} -{filteredProducts.length} món -``` - ---- - -## Step 8: Verify Everything Works - -```bash -# Check for TypeScript errors -npm run lint - -# Format code -npm run format - -# Run dev server -npm run dev - -# Visit http://localhost:3000 and check styling -``` - ---- - -## Next: Create Molecules - -Once atoms are solid, create molecules that **use** atoms: - -``` -components/molecules/ -├── cards/ -│ └── ProductCard.tsx (uses Button, Text, Badge atoms) -├── form-groups/ -│ └── FormField.tsx (uses TextInput, Text atoms) -├── ratings/ -│ └── RatingInput.tsx (uses Button, Caption atoms) -└── search-bar/ - └── SearchBar.tsx (uses SearchInput, Button atoms) -``` - -**Each molecule = 2-3 atoms combined + simple state** - ---- - -## Checklist - -- [ ] Created `components/atoms/buttons/` -- [ ] Created `components/atoms/inputs/` -- [ ] Created `components/atoms/typography/` -- [ ] Created `components/atoms/badges/` -- [ ] Created `components/atoms/dividers/` -- [ ] Created `components/atoms/index.ts` (barrel export) -- [ ] Updated at least 1 component to use atoms -- [ ] Run `npm run lint` - no errors -- [ ] Run `npm run format` - code formatted -- [ ] Test in browser - styling looks correct - ---- - -## 💡 Tips - -1. **Start small:** Just do atoms. Don't worry about molecules yet. -2. **Use consistently:** Once Button atom exists, never write ` + + )} + - {/* Card body */} -
- {/* Name + View menu button */} -
-

- {shop.name} -

- - - Xem menu - -
- - {/* Address */} -
- - {shop.address} -
-
- - ))} - - ) : ( - /* Empty state */ -
- -

- Không tìm thấy quán nào phù hợp -

- -
- )} - - {/* Filter / Search bar */} + {/* Filter / Search bar — sticky bottom */}
- {/* Label */}
Lọc quán
- {/* Search by name */} -
- - setSearchName(e.target.value)} - placeholder="Tìm theo tên quán..." - className="bg-background text-foreground focus:ring-opacity-20 w-full rounded-xl border border-(--color-border) py-2.5 pr-9 pl-9 text-sm transition-all duration-150 outline-none placeholder:text-(--color-text-muted) focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary)" - /> - {searchName && ( - - )} -
+ {/* Name search */} + setSearchName("")} + placeholder="Tìm theo tên quán..." + className="min-w-0 flex-1" + /> - {/* Search by address */} + {/* Address search — different icon so not using SearchBar atom */}
- {/* Custom Drinkool header — no login button */} -
-
- - {/* Logo */} -
- Logo Drinkool -
- - {/* Brand name */} - - Drinkool - - -
-
- - {/* Page content */} -
{children}
- - {/* No footer */} - - ); + return {children}; } diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx index 4afba3f..2a6f476 100644 --- a/app/(main)/layout.tsx +++ b/app/(main)/layout.tsx @@ -1,25 +1,9 @@ -import CartFab from "@/components/CartFab"; -import Footer from "@/layouts/footer"; -import Header from "@/layouts/header"; +import { MainLayout } from "@/components/templates/main-layout"; -export default function MainLayout({ +export default function RootLayout({ children, }: { children: React.ReactNode; }) { - return ( - <> - {/* Sticky top header */} -
- - {/* Page content (grows to fill remaining height) */} -
{children}
- - {/* Footer always at bottom */} -
- - {/* Global floating cart button */} - - - ); + return {children}; } diff --git a/app/(main)/page.tsx b/app/(main)/page.tsx index 396fdce..705cfbc 100644 --- a/app/(main)/page.tsx +++ b/app/(main)/page.tsx @@ -1,10 +1,10 @@ "use client"; -import CartProduct from "@/components/CartProduct"; -import Navbar from "@/components/Navbar"; -import { useCart } from "@/lib/cart-context"; -import { MENU_CATEGORIES, MOCK_PRODUCTS } from "@/lib/constants"; +import { CategorySidebar } from "@/components/organisms/navigation"; +import { ProductGrid } from "@/components/organisms/product-grid"; +import { SearchBar } from "@/components/molecules/search-bar"; import { useMenu } from "@/lib/menu-context"; +import { MENU_CATEGORIES } from "@/lib/constants"; import { useEffect, useState } from "react"; /** @@ -16,25 +16,13 @@ import { useEffect, useState } from "react"; * Sidebar state: * - Desktop (≥ 1024px): expanded by default * - Mobile (< 1024px): collapsed by default - * - * Product grid columns (responsive, depends on sidebar state): - * Collapsed sidebar: 2 → sm:2 → lg:3 → xl:4 → 2xl:5 - * Expanded sidebar: 1 → sm:2 → lg:2 → xl:3 → 2xl:4 */ export default function Home() { - /* Shared category state comes from MenuContext so the header mobile menu - * and this sidebar always reflect the same selection. */ const { activeCategory, setActiveCategory } = useMenu(); - const { addToCart } = useCart(); - /* Start collapsed (false) so SSR and client initial render match. - * useEffect sets the correct value after hydration completes. */ const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); - /* After mount: sync sidebar with viewport width, then subscribe to changes. - * The initial setIsSidebarOpen call is intentional (post-hydration only) - * and does not cause cascading renders — suppress the lint rule here. */ useEffect(() => { const mq = window.matchMedia("(min-width: 1024px)"); // eslint-disable-next-line react-hooks/set-state-in-effect @@ -44,45 +32,18 @@ export default function Home() { return () => mq.removeEventListener("change", handler); }, []); - /* Clear search whenever the active category changes (triggered from either - * the sidebar on md+ or the header scrollable menu on < md). - * setState-in-effect is intentional here — suppress the lint rule. */ useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect setSearchQuery(""); }, [activeCategory]); - /* Filter products by availability, active category, and search query. - * p.available defaults to true when undefined (opt-in unavailability). */ - const filteredProducts = MOCK_PRODUCTS.filter((p) => { - const isAvailable = p.available !== false; - const matchesCategory = - activeCategory === "all" || p.category === activeCategory; - const matchesSearch = - searchQuery.trim() === "" || - p.name.toLowerCase().includes(searchQuery.toLowerCase()) || - p.description.toLowerCase().includes(searchQuery.toLowerCase()); - return isAvailable && matchesCategory && matchesSearch; - }); - - /* Active category label */ const activeCategoryLabel = MENU_CATEGORIES.find((c) => c.id === activeCategory)?.name ?? "Tất cả"; - /* Responsive grid class based on sidebar state - * Base (< 480px) : 1 col — very small phones - * min-[480px] : 2 cols — larger phones - * lg+ : 2/3 cols depending on sidebar - */ - const gridCols = isSidebarOpen - ? "grid-cols-1 min-[480px]:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4" - : "grid-cols-1 min-[480px]:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"; - return ( - /* Outer wrapper: flex row, align-items: flex-start so sidebar sticks */
{/* ── Sidebar ── */} - setIsSidebarOpen((prev) => !prev)} activeCategory={activeCategory} @@ -98,94 +59,23 @@ export default function Home() {

{activeCategoryLabel}

-

- {filteredProducts.length} món -

- {/* Search input */} -
- - setSearchQuery(e.target.value)} - placeholder="Tìm kiếm món..." - className="bg-card text-foreground border-border placeholder:text-muted-foreground focus:border-primary focus:ring-primary focus:ring-opacity-20 w-full rounded-xl border py-2 pr-9 pl-9 text-sm transition-all duration-150 outline-none focus:ring-2" - /> - {/* Clear button */} - {searchQuery && ( - - )} -
+ {/* Search bar */} + setSearchQuery("")} + placeholder="Tìm kiếm món..." + className="sm:max-w-xs" + />
- {/* ── Mobile category menu — visible only on < md, below search, above products ── */} -
-
- {MENU_CATEGORIES.map((cat) => { - const isActive = activeCategory === cat.id; - return ( - - ); - })} -
-
- - {/* ── Product grid ── */} - {filteredProducts.length > 0 ? ( -
- {filteredProducts.map((product) => ( - addToCart(product)} - /> - ))} -
- ) : ( - /* Empty state */ -
- -

- {searchQuery - ? `Không tìm thấy món nào cho "${searchQuery}"` - : "Chưa có món trong danh mục này"} -

- {searchQuery && ( - - )} -
- )} + {/* ── Product grid (organism handles mobile category menu + grid) ── */} +
); diff --git a/app/(main)/payment/page.tsx b/app/(main)/payment/page.tsx index 8d0229b..feca381 100644 --- a/app/(main)/payment/page.tsx +++ b/app/(main)/payment/page.tsx @@ -1,6 +1,6 @@ "use client"; -import ReviewModal from "@/components/ReviewModal"; +import { ReviewModal } from "@/components/organisms/modals"; import { useAuth } from "@/lib/auth-context"; import { useCart } from "@/lib/cart-context"; import Link from "next/link"; diff --git a/app/(manager)/layout.tsx b/app/(manager)/layout.tsx index 3348ec4..40f5156 100644 --- a/app/(manager)/layout.tsx +++ b/app/(manager)/layout.tsx @@ -1,49 +1,9 @@ -"use client"; +import { ManagerLayout } from "@/components/templates/manager-layout"; -import { useAuth } from "@/lib/auth-context"; -import { ManagerProvider } from "@/lib/manager-context"; -import Link from "next/link"; -import { useRouter } from "next/navigation"; -import { useEffect } from "react"; - -export default function ManagerLayout({ +export default function RootManagerLayout({ children, }: { children: React.ReactNode; }) { - const { user } = useAuth(); - const router = useRouter(); - - // Redirect non-managers away - useEffect(() => { - if (user !== null && user.role !== "manager") { - router.replace("/"); - } - }, [user, router]); - - // While loading (user is null but not yet checked), show nothing - if (user === null) { - return ( -
-
- -

Đang kiểm tra quyền truy cập...

- - Đăng nhập nếu chưa có tài khoản - -
-
- ); - } - - if (user.role !== "manager") return null; - - return ( - -
{children}
-
- ); + return {children}; } diff --git a/app/(manager)/manager/page.tsx b/app/(manager)/manager/page.tsx index 3f20889..b09a445 100644 --- a/app/(manager)/manager/page.tsx +++ b/app/(manager)/manager/page.tsx @@ -1,1101 +1,13 @@ "use client"; +import { + CategoriesTab, + CombosTab, + ProductsTab, +} from "@/components/organisms/manager"; import { useAuth } from "@/lib/auth-context"; import { useManager } from "@/lib/manager-context"; -import type { Combo, ComboItem, MenuCategory, Product } from "@/lib/types"; import Link from "next/link"; -import { useState } from "react"; - -// ─── Helpers ────────────────────────────────────────────────────────────────── - -function formatPrice(price: number) { - return price.toLocaleString("vi-VN") + "đ"; -} - -// ─── Product Modal ──────────────────────────────────────────────────────────── - -interface ProductModalProps { - product: Product | null; // null = add mode - categories: MenuCategory[]; - onSave: (p: Omit | Product) => void; - onClose: () => void; -} - -function ProductModal({ - product, - categories, - onSave, - onClose, -}: ProductModalProps) { - const isEdit = product !== null; - const [form, setForm] = useState>({ - name: product?.name ?? "", - category: product?.category ?? categories[0]?.id ?? "", - price: product?.price ?? 0, - image: product?.image ?? "/imgs/products/placeholder.jpg", - description: product?.description ?? "", - available: product?.available ?? true, - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (isEdit && product) { - onSave({ ...form, id: product.id }); - } else { - onSave(form); - } - }; - - return ( -
e.target === e.currentTarget && onClose()} - > -
- {/* Header */} -
-

- {isEdit ? "Chỉnh sửa món" : "Thêm món mới"} -

- -
- - {/* Form */} -
- {/* Name */} -
- - setForm({ ...form, name: e.target.value })} - className="text-foreground w-full rounded-xl border border-(--color-border) bg-white px-3 py-2 text-sm transition outline-none focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary)/20" - placeholder="Ví dụ: Cà Phê Đen" - /> -
- - {/* Category + Price row */} -
-
- - -
-
- - - setForm({ ...form, price: Number(e.target.value) }) - } - className="text-foreground w-full rounded-xl border border-(--color-border) bg-white px-3 py-2 text-sm transition outline-none focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary)/20" - placeholder="25000" - /> -
-
- - {/* Description */} -
- -