[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C2DHT1Q0Q87WRYBCBH3QHG":3,"skill-same:01M6C2DHT1Q0Q87WRYBCBH3QHG":90},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":40,"linkOnly":41,"markdownContent":42,"markdownBody":43,"frontMatter":44,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":52,"repositoryHtmlUrl":53,"repositoryHomepageUrl":54,"ownerHtmlUrl":55,"ownerBlogUrl":6,"ownerDescription":56,"updatedAt":57,"install":58,"resources":71},"01M6C2DHT1Q0Q87WRYBCBH3QHG","frontend-context",null,"Frontend architecture, patterns and conventions for the Next.js 15 app. Trigger: When working on frontend code, components, pages, or API integration.","davidleonmayor\u002Fproyecto-grado-unimayor","davidleonmayor","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F142275316?v=4","01M6C2D07NAZV2VQE96MD4HWYW","David León","01M6C2D0BN278C193FYXQB3TME",3,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Ffrontend-context\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39],"frontend","context","frontend context","components","architecture","code","frontend architecture","integration","api","Next.js","js","2026-11-10 10:12:30",false,"---\nname: frontend-context\ndescription: >\n  Frontend architecture, patterns and conventions for the Next.js 15 app.\n  Trigger: When working on frontend code, components, pages, or API integration.\nlicense: Apache-2.0\nmetadata:\n  author: davidleonmayor\n  version: \"1.0\"\n  scope: [frontend]\n---\n\n## Stack\n\n| Technology | Version | Purpose |\n|------------|---------|---------|\n| Next.js | 15 (App Router) | Framework |\n| React | 19 | UI library |\n| TypeScript | 5 | Language |\n| Tailwind CSS | 4 | Styling |\n| shadcn\u002Fui | new-york style | Component library |\n| Zod | 4 | Schema validation |\n| react-hook-form | 7 | Form handling |\n| Recharts | 3 | Charts |\n| react-big-calendar | 1 | Calendar views |\n| Lucide React | - | Icons |\n| SweetAlert2 | 11 | Alert dialogs |\n| jspdf \u002F pdfjs-dist | - | PDF handling |\n| xlsx | 0.18 | Spreadsheet handling |\n\n**File Naming:**\n- ✅ `useAuth.ts` (hooks use camelCase with `use` prefix)\n- ✅ `auth.service.ts` (services use dot notation)\n- ✅ `ProjectHistory.tsx` (components use PascalCase)\n- ✅ `base-client.ts` (lib utilities use kebab-case)\n- ✅ `common.ts` (types use camelCase)\n- ❌ `Auth_Service.ts` (WRONG — no underscores)\n\n## Scope Detection (ASK IF AMBIGUOUS)\n\n| User Says | Action |\n|-----------|--------|\n| \"add component\" | Create in `modules\u002F{domain}\u002Fcomponents\u002F` or `shared\u002Fcomponents\u002F` |\n| \"add page\", \"new page\" | Create in `app\u002F(dashboard)\u002F` with proper layout |\n| \"add service\", \"connect API\" | Create in `modules\u002F{domain}\u002Fservices\u002F` extending BaseApiClient |\n| \"add hook\" | Create in `modules\u002F{domain}\u002Fhooks\u002F` or `shared\u002Fhooks\u002F` |\n| \"add form\" | Use react-hook-form + zod, InputField component |\n| \"fix styling\" | Check Tailwind classes, `globals.css` theme vars |\n\n**Examples:**\n- \"Add a student profile page\" → ONE page in `app\u002F(dashboard)\u002Flist\u002Fstudents\u002F[id]\u002F`\n- \"Connect events to the API\" → ONE service in `modules\u002Fevents\u002Fservices\u002F`\n- \"Add a search component\" → Reuse `shared\u002Fcomponents\u002Fui\u002FTableSearch.tsx` or create new\n\n## Directory Structure\n\n```\nfrontend\u002F\n├── app\u002F                          # Next.js App Router\n│   ├── layout.tsx                # Root layout (Geist font, metadata)\n│   ├── page.tsx                  # Auth check → redirect to role dashboard\n│   ├── globals.css               # Tailwind + custom theme vars\n│   ├── sign-in\u002F                  # Public auth pages\n│   ├── register\u002F\n│   ├── forgot-password\u002F\n│   ├── reset-password\u002F[token]\u002F\n│   ├── confirm-account\u002F\n│   ├── hooks\u002F                    # ⚠ Legacy hooks (use modules\u002F instead)\n│   ├── lib\u002F                      # ⚠ Legacy services (use modules\u002F instead)\n│   ├── components\u002F               # ⚠ Legacy components (use modules\u002F or shared\u002F)\n│   └── (dashboard)\u002F              # Route group — requires auth\n│       ├── layout.tsx            # Sidebar + Navbar + ProtectedRoute\n│       ├── admin\u002F                # Admin dashboard (stats, charts)\n│       ├── dean\u002F                 # Dean dashboard\n│       ├── teacher\u002F              # Teacher dashboard (calendar)\n│       ├── student\u002F              # Student dashboard (calendar)\n│       ├── social-outreach\u002F      # Social outreach module\n│       ├── projects\u002F             # Project CRUD + bulk upload\n│       │   ├── admin\u002F            # Admin project management\n│       │   │   ├── new\u002F\n│       │   │   └── edit\u002F[id]\u002F\n│       │   ├── bulk-upload\u002F\n│       │   └── [id]\u002F             # Project detail\n│       └── list\u002F                 # Entity listings\n│           ├── careers\u002F\n│           ├── degreeOptions\u002F\n│           ├── events\u002F\n│           ├── messages\u002F\n│           ├── students\u002F[id]\u002F\n│           └── teachers\u002F[id]\u002F\n├── modules\u002F                      # ✅ Domain modules (preferred)\n│   ├── auth\u002F                     # Authentication — [details](references\u002Fauth-context.md)\n│   │   ├── hooks\u002FuseAuth.ts\n│   │   ├── services\u002Fauth.service.ts\n│   │   └── types\u002F\n│   ├── events\u002F                   # Event management\n│   │   ├── services\u002Fevents.service.ts\n│   │   └── types\u002F\n│   ├── persons\u002F                  # Teachers & students\n│   │   ├── services\u002Fpersons.service.ts\n│   │   └── types\u002F\n│   └── projects\u002F                 # Project management — [details](references\u002Fprojects-context.md)\n│       ├── components\u002F\n│       ├── services\u002Fprojects.service.ts\n│       └── types\u002F\n├── shared\u002F                       # ✅ Cross-cutting utilities — [details](references\u002Fshared-context.md)\n│   ├── components\u002F\n│   │   ├── ui\u002F                   # InputField, Table, Pagination, TableSearch\n│   │   └── layout\u002F               # Menu, Navbar, ProtectedRoute, RoleProtectedRoute\n│   ├── hooks\u002FuseUserRole.ts\n│   ├── lib\u002F\n│   │   ├── api\u002Fbase-client.ts    # BaseApiClient (fetch wrapper)\n│   │   └── auth\u002Ftoken-manager.ts # JWT token storage\n│   ├── types\u002Fcommon.ts\n│   └── index.ts                  # Barrel exports\n├── components\u002Fui\u002F                # shadcn\u002Fui primitives (auto-generated)\n├── lib\u002Futils.ts                  # cn() helper\n└── public\u002F\n```\n\n## Architecture\n\nThe frontend uses a **modular architecture** with two layers:\n\n**Module pattern** — Self-contained domain modules under `modules\u002F` with own services, hooks, components, and types. Each module owns its full vertical slice.\n\n| Module | Path | Description |\n|--------|------|-------------|\n| `auth` | `modules\u002Fauth\u002F` | Authentication, JWT, password flows — [details](references\u002Fauth-context.md) |\n| `projects` | `modules\u002Fprojects\u002F` | Project CRUD, iterations, reviews, bulk upload — [details](references\u002Fprojects-context.md) |\n| `events` | `modules\u002Fevents\u002F` | Event CRUD |\n| `persons` | `modules\u002Fpersons\u002F` | Teachers & students listing |\n\n**Shared layer** — Cross-cutting utilities in `shared\u002F` used by all modules — [details](references\u002Fshared-context.md).\n\n**Data flow:**\n\n```\nPage (app\u002F) → Hook (modules\u002F*\u002Fhooks\u002F) → Service (modules\u002F*\u002Fservices\u002F) → BaseApiClient → Backend API\n                                                                              ↓\n                                                                     TokenManager (auth)\n```\n\n**Layered responsibilities:**\n\n| Layer | Responsibility | Location |\n|-------|---------------|----------|\n| Pages | Route definition, layout, data fetching | `app\u002F` |\n| Hooks | State management, side effects, business logic | `modules\u002F*\u002Fhooks\u002F`, `shared\u002Fhooks\u002F` |\n| Services | API calls, request formatting | `modules\u002F*\u002Fservices\u002F` |\n| Components | UI rendering, event handling | `modules\u002F*\u002Fcomponents\u002F`, `shared\u002Fcomponents\u002F` |\n| Types | TypeScript interfaces | `modules\u002F*\u002Ftypes\u002F`, `shared\u002Ftypes\u002F` |\n| Lib | Infrastructure (API client, token, utils) | `shared\u002Flib\u002F` |\n\n## Critical Patterns\n\n### Module Structure\n\nEach domain module follows:\n\n```\nmodules\u002F{name}\u002F\n├── index.ts           # Barrel exports (public API)\n├── hooks\u002F             # React hooks\n├── services\u002F          # API service classes extending BaseApiClient\n├── components\u002F        # Module-specific components\n└── types\u002F             # TypeScript interfaces\n```\n\nImport from barrel: `import { useAuth, authService } from '@\u002Fmodules\u002Fauth'`\n\n### API Services\n\nAll services extend `BaseApiClient` which wraps `fetch` with auth headers:\n\n```typescript\nimport { BaseApiClient } from '@\u002Fshared\u002Flib\u002Fapi\u002Fbase-client';\n\nexport class MyService extends BaseApiClient {\n  async getItems(): Promise\u003CItem[]> {\n    return this.request\u003CItem[]>('\u002Fapi\u002Fitems', { requiresAuth: true });\n  }\n\n  async createItem(data: ItemData): Promise\u003CItem> {\n    return this.request\u003CItem>('\u002Fapi\u002Fitems', {\n      method: 'POST',\n      body: data,\n      requiresAuth: true\n    });\n  }\n\n  async uploadFile(id: string, file: File): Promise\u003Cvoid> {\n    const formData = new FormData();\n    formData.append('file', file);\n    return this.requestFormData(`\u002Fapi\u002Fitems\u002F${id}\u002Fupload`, formData, {\n      requiresAuth: true\n    });\n  }\n}\n\nexport const myService = new MyService();\n```\n\nBackend URL: `NEXT_PUBLIC_API_URL` (defaults to `http:\u002F\u002Flocalhost:4000`).\n\n### Authentication\n\n- JWT stored in `localStorage` via `TokenManager`\n- `useAuth()` hook provides: `user`, `login`, `logout`, `register`, `isAuth`\n- `ProtectedRoute` wraps dashboard layout — redirects to `\u002Fsign-in` if unauthenticated\n- `RoleProtectedRoute` checks user role (student, teacher, admin, dean)\n- Role determined by project membership, not a user field\n- See [auth-context.md](references\u002Fauth-context.md) for full details\n\n### Roles\n\n| Role | Route | Determined by |\n|------|-------|---------------|\n| `student` | `\u002Fstudent` | All projects have role \"Estudiante\" |\n| `teacher` | `\u002Fteacher` | Has projects but not privileged |\n| `admin` | `\u002Fadmin` | Privileged role (Director, Jurado, Coordinador de Carrera) |\n| `dean` | `\u002Fdean` | Has role \"Decano\" in any project |\n\nPriority: dean > admin > teacher > student\n\n### Route Protection\n\n```typescript\n\u002F\u002F Auth-only page (any authenticated user)\n\u002F\u002F Wrap in dashboard layout which uses ProtectedRoute\n\n\u002F\u002F Role-specific page\nimport { RoleProtectedRoute } from '@\u002Fshared\u002Fcomponents\u002Flayout\u002FRoleProtectedRoute';\n\nexport default function AdminPage() {\n  return (\n    \u003CRoleProtectedRoute allowedRoles={['admin']}>\n      {\u002F* page content *\u002F}\n    \u003C\u002FRoleProtectedRoute>\n  );\n}\n```\n\n### Path Alias\n\n`@\u002F*` maps to `frontend\u002F*` root. Use: `import { X } from '@\u002Fshared'`\n\n### Styling\n\n- Tailwind CSS 4 with custom theme in `globals.css`\n- Custom colors: `principal` (yellow #fcdf5d), `secondary` (blue #0ea5e9), `tertiary` (purple #a2a1f0)\n- shadcn\u002Fui components in `components\u002Fui\u002F` — do NOT edit manually, use `npx shadcn@latest add`\n- Shared UI components in `shared\u002Fcomponents\u002Fui\u002F` — these ARE custom and editable\n- See [pages-context.md](references\u002Fpages-context.md) for full theme variables\n\n### Forms\n\n- `react-hook-form` + `zod` for validation\n- `@hookform\u002Fresolvers` for zod integration\n- Custom `InputField` component wraps inputs with react-hook-form register\n\n```typescript\nimport { useForm } from 'react-hook-form';\nimport { z } from 'zod';\nimport { zodResolver } from '@hookform\u002Fresolvers\u002Fzod';\nimport { InputField } from '@\u002Fshared\u002Fcomponents\u002Fui\u002FInputField';\n\nconst schema = z.object({\n  name: z.string().min(1, 'Required'),\n  email: z.string().email('Invalid email'),\n});\n\ntype FormData = z.infer\u003Ctypeof schema>;\n\nexport default function MyForm() {\n  const { register, handleSubmit, formState: { errors } } = useForm\u003CFormData>({\n    resolver: zodResolver(schema),\n  });\n\n  return (\n    \u003Cform onSubmit={handleSubmit(onSubmit)}>\n      \u003CInputField label=\"Name\" name=\"name\" register={register} error={errors.name} \u002F>\n      \u003CInputField label=\"Email\" name=\"email\" register={register} error={errors.email} \u002F>\n    \u003C\u002Fform>\n  );\n}\n```\n\n### Alerts & Notifications\n\nSweetAlert2 for user feedback:\n\n```typescript\nimport Swal from 'sweetalert2';\n\nSwal.fire({ icon: 'success', title: 'Done', text: 'Operation completed' });\nSwal.fire({ icon: 'error', title: 'Error', text: error.message });\n```\n\n### Dashboard Layout\n\nTwo-column layout (sidebar 14-16% + content 84-86%):\n- Left: Logo + Menu (role-based items)\n- Right: Navbar (user info, search, logout) + page content\n- Background: `#F7F8FA`\n- See [pages-context.md](references\u002Fpages-context.md) for full routing details\n\n## Commands\n\n```bash\npnpm dev              # Dev server (port 3000)\npnpm build            # Production build (standalone output)\npnpm start            # Start production server\nnpx shadcn@latest add \u003Ccomponent>  # Add shadcn\u002Fui component\n```\n\n## ⚠ Legacy Code\n\n`app\u002Fhooks\u002F`, `app\u002Flib\u002F`, `app\u002Fcomponents\u002F` contain legacy code that duplicates `modules\u002F` and `shared\u002F`. When modifying existing pages that use legacy imports, prefer migrating to `modules\u002F`\u002F`shared\u002F` patterns.\n\n| Legacy Location | Modern Equivalent |\n|-----------------|-------------------|\n| `app\u002Fhooks\u002FuseAuth.ts` | `modules\u002Fauth\u002Fhooks\u002FuseAuth.ts` |\n| `app\u002Fhooks\u002FuseUserRole.ts` | `shared\u002Fhooks\u002FuseUserRole.ts` |\n| `app\u002Flib\u002Fapi.ts` | `modules\u002F*\u002Fservices\u002F*.service.ts` |\n| `app\u002Flib\u002Fauth.ts` | `shared\u002Flib\u002Fauth\u002Ftoken-manager.ts` |\n| `app\u002Fcomponents\u002F` | `shared\u002Fcomponents\u002F` or `modules\u002F*\u002Fcomponents\u002F` |\n\n## Resources\n\n- **Modules**: See [references\u002F](references\u002F) for detailed context on each module\n  - [auth-context.md](references\u002Fauth-context.md) — Auth module: hooks, services, pages, token flow\n  - [projects-context.md](references\u002Fprojects-context.md) — Projects module: CRUD, iterations, bulk upload\n  - [shared-context.md](references\u002Fshared-context.md) — Shared infrastructure: BaseApiClient, components, types\n  - [pages-context.md](references\u002Fpages-context.md) — Pages & routing: route map, layouts, theme, dashboards\n","## Stack\n\n| Technology | Version | Purpose |\n|------------|---------|---------|\n| Next.js | 15 (App Router) | Framework |\n| React | 19 | UI library |\n| TypeScript | 5 | Language |\n| Tailwind CSS | 4 | Styling |\n| shadcn\u002Fui | new-york style | Component library |\n| Zod | 4 | Schema validation |\n| react-hook-form | 7 | Form handling |\n| Recharts | 3 | Charts |\n| react-big-calendar | 1 | Calendar views |\n| Lucide React | - | Icons |\n| SweetAlert2 | 11 | Alert dialogs |\n| jspdf \u002F pdfjs-dist | - | PDF handling |\n| xlsx | 0.18 | Spreadsheet handling |\n\n**File Naming:**\n- ✅ `useAuth.ts` (hooks use camelCase with `use` prefix)\n- ✅ `auth.service.ts` (services use dot notation)\n- ✅ `ProjectHistory.tsx` (components use PascalCase)\n- ✅ `base-client.ts` (lib utilities use kebab-case)\n- ✅ `common.ts` (types use camelCase)\n- ❌ `Auth_Service.ts` (WRONG — no underscores)\n\n## Scope Detection (ASK IF AMBIGUOUS)\n\n| User Says | Action |\n|-----------|--------|\n| \"add component\" | Create in `modules\u002F{domain}\u002Fcomponents\u002F` or `shared\u002Fcomponents\u002F` |\n| \"add page\", \"new page\" | Create in `app\u002F(dashboard)\u002F` with proper layout |\n| \"add service\", \"connect API\" | Create in `modules\u002F{domain}\u002Fservices\u002F` extending BaseApiClient |\n| \"add hook\" | Create in `modules\u002F{domain}\u002Fhooks\u002F` or `shared\u002Fhooks\u002F` |\n| \"add form\" | Use react-hook-form + zod, InputField component |\n| \"fix styling\" | Check Tailwind classes, `globals.css` theme vars |\n\n**Examples:**\n- \"Add a student profile page\" → ONE page in `app\u002F(dashboard)\u002Flist\u002Fstudents\u002F[id]\u002F`\n- \"Connect events to the API\" → ONE service in `modules\u002Fevents\u002Fservices\u002F`\n- \"Add a search component\" → Reuse `shared\u002Fcomponents\u002Fui\u002FTableSearch.tsx` or create new\n\n## Directory Structure\n\n```\nfrontend\u002F\n├── app\u002F                          # Next.js App Router\n│   ├── layout.tsx                # Root layout (Geist font, metadata)\n│   ├── page.tsx                  # Auth check → redirect to role dashboard\n│   ├── globals.css               # Tailwind + custom theme vars\n│   ├── sign-in\u002F                  # Public auth pages\n│   ├── register\u002F\n│   ├── forgot-password\u002F\n│   ├── reset-password\u002F[token]\u002F\n│   ├── confirm-account\u002F\n│   ├── hooks\u002F                    # ⚠ Legacy hooks (use modules\u002F instead)\n│   ├── lib\u002F                      # ⚠ Legacy services (use modules\u002F instead)\n│   ├── components\u002F               # ⚠ Legacy components (use modules\u002F or shared\u002F)\n│   └── (dashboard)\u002F              # Route group — requires auth\n│       ├── layout.tsx            # Sidebar + Navbar + ProtectedRoute\n│       ├── admin\u002F                # Admin dashboard (stats, charts)\n│       ├── dean\u002F                 # Dean dashboard\n│       ├── teacher\u002F              # Teacher dashboard (calendar)\n│       ├── student\u002F              # Student dashboard (calendar)\n│       ├── social-outreach\u002F      # Social outreach module\n│       ├── projects\u002F             # Project CRUD + bulk upload\n│       │   ├── admin\u002F            # Admin project management\n│       │   │   ├── new\u002F\n│       │   │   └── edit\u002F[id]\u002F\n│       │   ├── bulk-upload\u002F\n│       │   └── [id]\u002F             # Project detail\n│       └── list\u002F                 # Entity listings\n│           ├── careers\u002F\n│           ├── degreeOptions\u002F\n│           ├── events\u002F\n│           ├── messages\u002F\n│           ├── students\u002F[id]\u002F\n│           └── teachers\u002F[id]\u002F\n├── modules\u002F                      # ✅ Domain modules (preferred)\n│   ├── auth\u002F                     # Authentication — [details](references\u002Fauth-context.md)\n│   │   ├── hooks\u002FuseAuth.ts\n│   │   ├── services\u002Fauth.service.ts\n│   │   └── types\u002F\n│   ├── events\u002F                   # Event management\n│   │   ├── services\u002Fevents.service.ts\n│   │   └── types\u002F\n│   ├── persons\u002F                  # Teachers & students\n│   │   ├── services\u002Fpersons.service.ts\n│   │   └── types\u002F\n│   └── projects\u002F                 # Project management — [details](references\u002Fprojects-context.md)\n│       ├── components\u002F\n│       ├── services\u002Fprojects.service.ts\n│       └── types\u002F\n├── shared\u002F                       # ✅ Cross-cutting utilities — [details](references\u002Fshared-context.md)\n│   ├── components\u002F\n│   │   ├── ui\u002F                   # InputField, Table, Pagination, TableSearch\n│   │   └── layout\u002F               # Menu, Navbar, ProtectedRoute, RoleProtectedRoute\n│   ├── hooks\u002FuseUserRole.ts\n│   ├── lib\u002F\n│   │   ├── api\u002Fbase-client.ts    # BaseApiClient (fetch wrapper)\n│   │   └── auth\u002Ftoken-manager.ts # JWT token storage\n│   ├── types\u002Fcommon.ts\n│   └── index.ts                  # Barrel exports\n├── components\u002Fui\u002F                # shadcn\u002Fui primitives (auto-generated)\n├── lib\u002Futils.ts                  # cn() helper\n└── public\u002F\n```\n\n## Architecture\n\nThe frontend uses a **modular architecture** with two layers:\n\n**Module pattern** — Self-contained domain modules under `modules\u002F` with own services, hooks, components, and types. Each module owns its full vertical slice.\n\n| Module | Path | Description |\n|--------|------|-------------|\n| `auth` | `modules\u002Fauth\u002F` | Authentication, JWT, password flows — [details](references\u002Fauth-context.md) |\n| `projects` | `modules\u002Fprojects\u002F` | Project CRUD, iterations, reviews, bulk upload — [details](references\u002Fprojects-context.md) |\n| `events` | `modules\u002Fevents\u002F` | Event CRUD |\n| `persons` | `modules\u002Fpersons\u002F` | Teachers & students listing |\n\n**Shared layer** — Cross-cutting utilities in `shared\u002F` used by all modules — [details](references\u002Fshared-context.md).\n\n**Data flow:**\n\n```\nPage (app\u002F) → Hook (modules\u002F*\u002Fhooks\u002F) → Service (modules\u002F*\u002Fservices\u002F) → BaseApiClient → Backend API\n                                                                              ↓\n                                                                     TokenManager (auth)\n```\n\n**Layered responsibilities:**\n\n| Layer | Responsibility | Location |\n|-------|---------------|----------|\n| Pages | Route definition, layout, data fetching | `app\u002F` |\n| Hooks | State management, side effects, business logic | `modules\u002F*\u002Fhooks\u002F`, `shared\u002Fhooks\u002F` |\n| Services | API calls, request formatting | `modules\u002F*\u002Fservices\u002F` |\n| Components | UI rendering, event handling | `modules\u002F*\u002Fcomponents\u002F`, `shared\u002Fcomponents\u002F` |\n| Types | TypeScript interfaces | `modules\u002F*\u002Ftypes\u002F`, `shared\u002Ftypes\u002F` |\n| Lib | Infrastructure (API client, token, utils) | `shared\u002Flib\u002F` |\n\n## Critical Patterns\n\n### Module Structure\n\nEach domain module follows:\n\n```\nmodules\u002F{name}\u002F\n├── index.ts           # Barrel exports (public API)\n├── hooks\u002F             # React hooks\n├── services\u002F          # API service classes extending BaseApiClient\n├── components\u002F        # Module-specific components\n└── types\u002F             # TypeScript interfaces\n```\n\nImport from barrel: `import { useAuth, authService } from '@\u002Fmodules\u002Fauth'`\n\n### API Services\n\nAll services extend `BaseApiClient` which wraps `fetch` with auth headers:\n\n```typescript\nimport { BaseApiClient } from '@\u002Fshared\u002Flib\u002Fapi\u002Fbase-client';\n\nexport class MyService extends BaseApiClient {\n  async getItems(): Promise\u003CItem[]> {\n    return this.request\u003CItem[]>('\u002Fapi\u002Fitems', { requiresAuth: true });\n  }\n\n  async createItem(data: ItemData): Promise\u003CItem> {\n    return this.request\u003CItem>('\u002Fapi\u002Fitems', {\n      method: 'POST',\n      body: data,\n      requiresAuth: true\n    });\n  }\n\n  async uploadFile(id: string, file: File): Promise\u003Cvoid> {\n    const formData = new FormData();\n    formData.append('file', file);\n    return this.requestFormData(`\u002Fapi\u002Fitems\u002F${id}\u002Fupload`, formData, {\n      requiresAuth: true\n    });\n  }\n}\n\nexport const myService = new MyService();\n```\n\nBackend URL: `NEXT_PUBLIC_API_URL` (defaults to `http:\u002F\u002Flocalhost:4000`).\n\n### Authentication\n\n- JWT stored in `localStorage` via `TokenManager`\n- `useAuth()` hook provides: `user`, `login`, `logout`, `register`, `isAuth`\n- `ProtectedRoute` wraps dashboard layout — redirects to `\u002Fsign-in` if unauthenticated\n- `RoleProtectedRoute` checks user role (student, teacher, admin, dean)\n- Role determined by project membership, not a user field\n- See [auth-context.md](references\u002Fauth-context.md) for full details\n\n### Roles\n\n| Role | Route | Determined by |\n|------|-------|---------------|\n| `student` | `\u002Fstudent` | All projects have role \"Estudiante\" |\n| `teacher` | `\u002Fteacher` | Has projects but not privileged |\n| `admin` | `\u002Fadmin` | Privileged role (Director, Jurado, Coordinador de Carrera) |\n| `dean` | `\u002Fdean` | Has role \"Decano\" in any project |\n\nPriority: dean > admin > teacher > student\n\n### Route Protection\n\n```typescript\n\u002F\u002F Auth-only page (any authenticated user)\n\u002F\u002F Wrap in dashboard layout which uses ProtectedRoute\n\n\u002F\u002F Role-specific page\nimport { RoleProtectedRoute } from '@\u002Fshared\u002Fcomponents\u002Flayout\u002FRoleProtectedRoute';\n\nexport default function AdminPage() {\n  return (\n    \u003CRoleProtectedRoute allowedRoles={['admin']}>\n      {\u002F* page content *\u002F}\n    \u003C\u002FRoleProtectedRoute>\n  );\n}\n```\n\n### Path Alias\n\n`@\u002F*` maps to `frontend\u002F*` root. Use: `import { X } from '@\u002Fshared'`\n\n### Styling\n\n- Tailwind CSS 4 with custom theme in `globals.css`\n- Custom colors: `principal` (yellow #fcdf5d), `secondary` (blue #0ea5e9), `tertiary` (purple #a2a1f0)\n- shadcn\u002Fui components in `components\u002Fui\u002F` — do NOT edit manually, use `npx shadcn@latest add`\n- Shared UI components in `shared\u002Fcomponents\u002Fui\u002F` — these ARE custom and editable\n- See [pages-context.md](references\u002Fpages-context.md) for full theme variables\n\n### Forms\n\n- `react-hook-form` + `zod` for validation\n- `@hookform\u002Fresolvers` for zod integration\n- Custom `InputField` component wraps inputs with react-hook-form register\n\n```typescript\nimport { useForm } from 'react-hook-form';\nimport { z } from 'zod';\nimport { zodResolver } from '@hookform\u002Fresolvers\u002Fzod';\nimport { InputField } from '@\u002Fshared\u002Fcomponents\u002Fui\u002FInputField';\n\nconst schema = z.object({\n  name: z.string().min(1, 'Required'),\n  email: z.string().email('Invalid email'),\n});\n\ntype FormData = z.infer\u003Ctypeof schema>;\n\nexport default function MyForm() {\n  const { register, handleSubmit, formState: { errors } } = useForm\u003CFormData>({\n    resolver: zodResolver(schema),\n  });\n\n  return (\n    \u003Cform onSubmit={handleSubmit(onSubmit)}>\n      \u003CInputField label=\"Name\" name=\"name\" register={register} error={errors.name} \u002F>\n      \u003CInputField label=\"Email\" name=\"email\" register={register} error={errors.email} \u002F>\n    \u003C\u002Fform>\n  );\n}\n```\n\n### Alerts & Notifications\n\nSweetAlert2 for user feedback:\n\n```typescript\nimport Swal from 'sweetalert2';\n\nSwal.fire({ icon: 'success', title: 'Done', text: 'Operation completed' });\nSwal.fire({ icon: 'error', title: 'Error', text: error.message });\n```\n\n### Dashboard Layout\n\nTwo-column layout (sidebar 14-16% + content 84-86%):\n- Left: Logo + Menu (role-based items)\n- Right: Navbar (user info, search, logout) + page content\n- Background: `#F7F8FA`\n- See [pages-context.md](references\u002Fpages-context.md) for full routing details\n\n## Commands\n\n```bash\npnpm dev              # Dev server (port 3000)\npnpm build            # Production build (standalone output)\npnpm start            # Start production server\nnpx shadcn@latest add \u003Ccomponent>  # Add shadcn\u002Fui component\n```\n\n## ⚠ Legacy Code\n\n`app\u002Fhooks\u002F`, `app\u002Flib\u002F`, `app\u002Fcomponents\u002F` contain legacy code that duplicates `modules\u002F` and `shared\u002F`. When modifying existing pages that use legacy imports, prefer migrating to `modules\u002F`\u002F`shared\u002F` patterns.\n\n| Legacy Location | Modern Equivalent |\n|-----------------|-------------------|\n| `app\u002Fhooks\u002FuseAuth.ts` | `modules\u002Fauth\u002Fhooks\u002FuseAuth.ts` |\n| `app\u002Fhooks\u002FuseUserRole.ts` | `shared\u002Fhooks\u002FuseUserRole.ts` |\n| `app\u002Flib\u002Fapi.ts` | `modules\u002F*\u002Fservices\u002F*.service.ts` |\n| `app\u002Flib\u002Fauth.ts` | `shared\u002Flib\u002Fauth\u002Ftoken-manager.ts` |\n| `app\u002Fcomponents\u002F` | `shared\u002Fcomponents\u002F` or `modules\u002F*\u002Fcomponents\u002F` |\n\n## Resources\n\n- **Modules**: See [references\u002F](references\u002F) for detailed context on each module\n  - [auth-context.md](references\u002Fauth-context.md) — Auth module: hooks, services, pages, token flow\n  - [projects-context.md](references\u002Fprojects-context.md) — Projects module: CRUD, iterations, bulk upload\n  - [shared-context.md](references\u002Fshared-context.md) — Shared infrastructure: BaseApiClient, components, types\n  - [pages-context.md](references\u002Fpages-context.md) — Pages & routing: route map, layouts, theme, dashboards\n",{"name":5,"license":15,"metadata":45,"description":48},{"scope":46,"author":9,"version":47},[29],"1.0","Frontend architecture, patterns and conventions for the Next.js 15 app. Trigger: When working on frontend code, components, pages, or API integration.\n","main","3d76dc018cdded158c4f49df5edb56829229d265","skills\u002Ffrontend-context",0,"https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor","https:\u002F\u002Fproyecto-grado-unimayor-8rtknmpjt-davidleonmayors-projects.vercel.app\u002Fadmin","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor","Full-stack developer focused on microservices backend","2026-11-11 00:05:51",{"source":8,"skillName":5,"commands":59,"prompt":69,"zipAvailable":70},[60,63,66],{"tool":61,"command":62},"npx","npx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill frontend-context",{"tool":64,"command":65},"bunx","bunx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill frontend-context",{"tool":67,"command":68},"pnpm","pnpm dlx skills add davidleonmayor\u002Fproyecto-grado-unimayor --skill frontend-context","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Ffrontend-context\u002FSKILL.md",true,[72,78,82,86],{"path":73,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":76,"sourceHtmlUrl":77},"references\u002Fauth-context.md","REFERENCE","参考文档",6593,"https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Ffrontend-context\u002Freferences\u002Fauth-context.md",{"path":79,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":80,"sourceHtmlUrl":81},"references\u002Fpages-context.md",6053,"https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Ffrontend-context\u002Freferences\u002Fpages-context.md",{"path":83,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":84,"sourceHtmlUrl":85},"references\u002Fprojects-context.md",5412,"https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Ffrontend-context\u002Freferences\u002Fprojects-context.md",{"path":87,"resourceType":74,"resourceTypeDesc":75,"sizeBytes":88,"sourceHtmlUrl":89},"references\u002Fshared-context.md",5939,"https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Ffrontend-context\u002Freferences\u002Fshared-context.md",[91,107,128,145,156,177],{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":100,"publishedAt":40},"01M6C2DVB157H69PFWB1XXBVZS","skill-sync","Synchronizes skill metadata to AGENTS.md Auto-invoke sections. Trigger: When syncing skill metadata to AGENTS.md.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fskill-sync\u002FSKILL.md",[97],{"categoryNo":98,"categoryName":99,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[101,102,103,104,105,106],"skill sync","sync","metadata","cloud","prowler","auto",{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":114,"publishedAt":40},"01M6C2DRSV8QKEFA2J94YQKWK1","skill-creator","Creates new AI agent skills following the Agent Skills spec. Trigger: When user asks to create a new skill, add agent instructions, or document patterns for AI.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fskill-creator\u002FSKILL.md",[113],{"categoryNo":98,"categoryName":99,"taxonomyType":21,"taxonomyTypeDesc":22},[115,116,117,118,104,105,119,120,121,122,123,124,125,126,127],"skill creator","ai agent","spec","create","Bash","grep","read","write","edit","instructions","user","webfetch","add agent",{"skillNo":129,"name":130,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":136,"publishedAt":40},"01M6C2DP81DY28MKM0RKFWJQHX","skill-commit","Creates professional git commits following conventional-commits format. Trigger: When creating commits, after completing code changes, when user asks to commit.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fskill-commit\u002FSKILL.md",[134,135],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":98,"categoryName":99,"taxonomyType":21,"taxonomyTypeDesc":22},[137,138,139,29,34,140,141,142,125,143,144],"skill commit","commit","git commit","git","commits","format","landing","backend",{"skillNo":146,"name":147,"nameZh":6,"description":148,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":149,"categories":150,"keywords":151,"publishedAt":40},"01M6C2DMHK8HST60TFQHMK8D94","project-info","General monorepo structure and component overview. Trigger: When AI needs project context or component locations.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fproject-info\u002FSKILL.md",[],[152,153,154,30,155],"project info","component","monorepo","overview",{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":168,"publishedAt":40},"01M6C2DESFJR14Z3G38F4KZCSG","express-mocks-testing","Unit testing pattern for Express 5 controllers using node-mocks-http and @prisma\u002Fclient mocks. Trigger: When writing unit tests for backend controllers, testing Express routes, or avoiding Supertest\u002FServer instantiation.","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fexpress-mocks-testing\u002FSKILL.md",[162,165],{"categoryNo":163,"categoryName":164,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":166,"categoryName":167,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",[169,170,144,171,172,173,174,175,176],"express mocks testing","testing","programming","server","http","node","unit tests","writing",{"skillNo":178,"name":179,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":180,"licenseFileUrl":6,"sourceHtmlUrl":181,"categories":182,"keywords":184,"publishedAt":40},"01M6C2DCXFSYWZCQWMPGWWQJQ7","backend-service","MIT","https:\u002F\u002Fgithub.com\u002Fdavidleonmayor\u002Fproyecto-grado-unimayor\u002Fblob\u002Fmain\u002Fskills\u002Fbackend-service\u002FSKILL.md",[183],{"categoryNo":166,"categoryName":167,"taxonomyType":21,"taxonomyTypeDesc":22},[185,144],"backend service"]