{"id":50721719,"url":"https://github.com/cloudsteak/kerno-demo","last_synced_at":"2026-06-10T00:30:51.410Z","repository":{"id":355642966,"uuid":"1228004346","full_name":"cloudsteak/kerno-demo","owner":"cloudsteak","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-04T15:18:28.000Z","size":305,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-04T17:17:15.076Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudsteak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-03T13:15:57.000Z","updated_at":"2026-05-04T15:18:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cloudsteak/kerno-demo","commit_stats":null,"previous_names":["cloudsteak/kerno-demo"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/cloudsteak/kerno-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudsteak%2Fkerno-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudsteak%2Fkerno-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudsteak%2Fkerno-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudsteak%2Fkerno-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudsteak","download_url":"https://codeload.github.com/cloudsteak/kerno-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudsteak%2Fkerno-demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34132030,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-10T00:30:50.639Z","updated_at":"2026-06-10T00:30:51.403Z","avatar_url":"https://github.com/cloudsteak.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TaskForge\n\nA production-quality, multi-layer task management application built with Next.js 16, React 18, TypeScript, and Recoil. Created as a real-world codebase for evaluating static analysis and observability tooling (Kerno).\n\n---\n\n## Features\n\n- **Task management** — create, view, edit, and delete tasks with priorities, statuses, assignees, due dates, and tags\n- **Team view** — browse team members with their roles and task assignments\n- **Live stats bar** — six KPI metrics: total tasks, by-status breakdown, and completion rate\n- **Real-time filtering** — filter tasks by status, or search by title, description, and tags\n- **Detail panel** — slide-in panel for full task editing without leaving the list\n- **Keyboard shortcuts** — `N` to create, `Esc` to dismiss\n\n---\n\n## Tech Stack\n\n| Layer     | Technology                             |\n| --------- | -------------------------------------- |\n| Framework  | Next.js 16 (Pages Router)              |\n| UI         | React 18                               |\n| Language   | TypeScript 5 (strict mode)             |\n| State      | Recoil 0.7                             |\n| Validation | Zod 3 (API route input validation)     |\n| Styling    | Pure CSS — no Tailwind, no CSS Modules |\n| Data       | In-memory store (simulated DB)         |\n\n---\n\n## Project Structure\n\nThe codebase is organized into four top-level tiers:\n\n```\nclient/\n├── components/        # React UI components\n│   ├── Sidebar.tsx        # Navigation + status filter\n│   ├── StatsBar.tsx       # KPI bar (6 metrics)\n│   ├── TaskCard.tsx       # Task list item\n│   ├── TaskDetailPanel.tsx  # Slide-in edit panel\n│   ├── CreateTaskModal.tsx  # New task form\n│   └── UsersPage.tsx      # Team grid view\n├── store/\n│   ├── atoms.ts       # Recoil atoms + selectors\n│   └── hooks.ts       # Custom hooks for all API calls\n└── styles/\n    └── globals.css    # Industrial dark theme + CSS variables\n\nserver/\n└── lib/\n    ├── db.ts          # In-memory data store (simulated DB)\n    └── api.ts         # successResponse / errorResponse helpers\n\nshared/\n└── types/\n    └── index.ts       # Shared TypeScript types (Task, User, Stats, ApiResponse)\n\npages/                 # Next.js routing (must stay at root)\n├── api/\n│   ├── tasks/\n│   │   ├── index.ts   # GET (list + filter), POST (create)\n│   │   └── [id].ts    # GET, PUT (partial update), DELETE\n│   ├── users/\n│   │   ├── index.ts   # GET (list, filter by role)\n│   │   └── [id].ts    # GET (user + their tasks)\n│   └── stats.ts       # GET (aggregated stats)\n├── _app.tsx           # RecoilRoot wrapper\n└── index.tsx          # Main SPA entry point\n```\n\n---\n\n## Getting Started\n\n**Requires Node.js 22.**\n\n```bash\nnpm install\nnpm run dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000).\n\n---\n\n## API Reference\n\nAll endpoints return a consistent envelope:\n\n```typescript\n// Success\n{ data: T, success: true, message?: string, timestamp: string }\n\n// Error\n{ data: null, success: false, message: string, timestamp: string }\n```\n\n### Tasks\n\n| Method   | Endpoint         | Description             |\n| -------- | ---------------- | ----------------------- |\n| `GET`    | `/api/tasks`     | List tasks (filterable) |\n| `POST`   | `/api/tasks`     | Create a task           |\n| `GET`    | `/api/tasks/:id` | Get a single task       |\n| `PUT`    | `/api/tasks/:id` | Partial update          |\n| `DELETE` | `/api/tasks/:id` | Delete a task           |\n\n**GET /api/tasks — query parameters:**\n\n| Param        | Type                                     | Description                                 |\n| ------------ | ---------------------------------------- | ------------------------------------------- |\n| `status`     | `todo \\| in_progress \\| blocked \\| done` | Filter by status                            |\n| `priority`   | `low \\| medium \\| high \\| critical`      | Filter by priority                          |\n| `assigneeId` | `string`                                 | Filter by user ID                           |\n| `search`     | `string`                                 | Full-text search (title, description, tags) |\n\n### Users\n\n| Method | Endpoint         | Description                           |\n| ------ | ---------------- | ------------------------------------- |\n| `GET`  | `/api/users`     | List users (optional `?role=` filter) |\n| `GET`  | `/api/users/:id` | Get user with their tasks             |\n\n### Stats\n\n| Method | Endpoint     | Description                |\n| ------ | ------------ | -------------------------- |\n| `GET`  | `/api/stats` | Aggregated task statistics |\n\n**HTTP status codes:** `200` success · `201` created · `400` bad request · `404` not found · `405` method not allowed\n\n---\n\n## Data Types\n\n```typescript\ntype Priority = \"low\" | \"medium\" | \"high\" | \"critical\";\ntype TaskStatus = \"todo\" | \"in_progress\" | \"blocked\" | \"done\";\n\ninterface Task {\n  id: string;\n  title: string;\n  description: string;\n  status: TaskStatus;\n  priority: Priority;\n  assigneeId: string | null;\n  createdAt: string; // ISO 8601\n  updatedAt: string; // ISO 8601\n  dueDate: string | null;\n  tags: string[];\n}\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  role: \"engineer\" | \"lead\" | \"manager\";\n  avatarInitials: string;\n}\n\ninterface Stats {\n  total: number;\n  byStatus: Record\u003cTaskStatus, number\u003e;\n  byPriority: Record\u003cPriority, number\u003e;\n  completionRate: number; // 0–100\n  overdueCount: number;\n}\n```\n\n---\n\n## State Management\n\nRecoil atoms live in `client/store/atoms.ts`. All data fetching goes through custom hooks in `client/store/hooks.ts` — components never call `fetch()` directly.\n\n| Atom                    | Type                      | Purpose                                |\n| ----------------------- | ------------------------- | -------------------------------------- |\n| `tasksAtom`             | `Task[]`                  | All loaded tasks                       |\n| `usersAtom`             | `User[]`                  | All loaded users                       |\n| `selectedTaskIdAtom`    | `string \\| null`          | Currently open task                    |\n| `statusFilterAtom`      | `TaskStatus \\| \"all\"`     | Active status filter                   |\n| `searchQueryAtom`       | `string`                  | Active search string                   |\n| `loadingAtom`           | `Record\u003cstring, boolean\u003e` | Loading states by key                  |\n| `showCreateModalAtom`   | `boolean`                 | Create modal visibility                |\n| `activeViewAtom`        | `\"tasks\" \\| \"users\"`      | Current top-level view                 |\n| `filteredTasksSelector` | `Task[]`                  | Derived: filtered + searched task list |\n\n**Loading key convention:** `\"tasks\"`, `\"users\"`, `\"create\"`, `\"update-{id}\"`, `\"delete-{id}\"`\n\n---\n\n## Development\n\n```bash\nnpm run dev      # Start dev server (http://localhost:3000)\nnpx tsc --noEmit # TypeScript check (must return 0 errors)\nnpm run lint     # ESLint\nnpm run build    # Production build check\n```\n\n### Conventions\n\n- **No `any`**, no `@ts-ignore` — TypeScript strict mode is enforced\n- **No inline styles** except for dynamic CSS variable values\n- **No direct `fetch()`** in components — use hooks only\n- **CSS variables** for all colors: `var(--bg)`, `var(--accent)`, `var(--danger)`, etc.\n- API helpers `successResponse` / `errorResponse` from `server/lib/api.ts` used in every endpoint\n- ESLint uses flat config (`eslint.config.mjs`) — ESLint 9+\n\n---\n\n## Keyboard Shortcuts\n\n| Key   | Action                                           |\n| ----- | ------------------------------------------------ |\n| `N`   | Open \"New Task\" modal (when no input is focused) |\n| `Esc` | Close detail panel or modal                      |\n\n---\n\n## Extending the Data Layer\n\n`server/lib/db.ts` is the only file that touches data. It currently uses in-memory arrays. To swap in a real database, replace only this file — the API routes and everything above it stay unchanged.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudsteak%2Fkerno-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudsteak%2Fkerno-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudsteak%2Fkerno-demo/lists"}