Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# OctoCAT Supply: Copilot Instructions

This is a **GitHub Copilot demo project** showcasing AI-assisted development across full-stack TypeScript. It's a supply chain management system with Express API and React frontend, designed to demonstrate Copilot's capabilities in Agent Mode, MCP servers, custom instructions, and IaC.

## Architecture Overview

**Monorepo structure** (npm workspaces):
- `api/` - Express.js REST API (port 3000) with Swagger/OpenAPI docs
- `frontend/` - React 18+ with Vite (port 5137)

**Data model**: Headquarters β†’ Branch β†’ Order β†’ OrderDetail β†’ OrderDetailDelivery ← Delivery ← Supplier; OrderDetail references Product

**Key principle**: All APIs are documented via JSDoc in route files for auto-generated Swagger (`/api-docs`). The frontend calls APIs through `axios` configured in `frontend/src/api/config.ts`.

## Quick Start Commands

```bash
npm install # Install all dependencies
npm run build # Build API & Frontend (workspaces)
npm run dev # Run both API (3000) & Frontend (5137) concurrently
npm run test # Run tests in both workspaces (vitest)
npm run test:coverage # API coverage with vitest
Comment on lines +21 to +22
```

Alternatively, use VS Code tasks: `Ctrl+Shift+P` β†’ "Run Task" β†’ "Build API/Frontend"

## Critical Developer Patterns

### API Routes (Express + Swagger)
- **Structure**: `api/src/routes/{entity}.ts` files export Router with JSDoc `@swagger` blocks
- **Swagger location**: Routes auto-documented via JSDoc; schema definitions in `api/src/models/{entity}.ts`
- **Example**: `/api/products` β†’ GET (list), POST (create), /{id} for GET/PUT/DELETE
- **CORS**: Configured in `index.ts`; origins include localhost and Codespace domains via `API_CORS_ORIGINS` env var
- **Seed data**: `api/src/seedData.ts` contains initial dataset; routes implement in-memory state (not persistent DB)

### Frontend Components & State
- **Routing**: React Router v7 in `App.tsx`; pages in `src/components/` (Products, About, Welcome, Login)
- **State management**:
- `AuthContext` - login/logout, admin detection (emails ending in `@github.com`)
- `ThemeContext` - dark/light mode toggle, persisted to localStorage
- **Data fetching**: `react-query` + `axios` for API calls; see `Products.tsx` for pattern
- **Styling**: Tailwind CSS with theme-aware classNames (check `darkMode` boolean from `useTheme()`)
- **UI patterns**: Modal windows, dropdown menus, product carousels (via react-slick)

### Testing
- **API**: Vitest + supertest in `api/src/routes/*.test.ts`
- **Pattern**: Create Express app, mount router, test endpoints (GET/POST/PUT/DELETE)
- **Setup**: `beforeEach` resets seed data; tests verify status codes and response bodies
- **Frontend**: Vitest configured but minimal existing tests; use `@testing-library/react`

## Important Conventions

1. **TypeScript**: Strict mode everywhere; interfaces defined in model files, used across routes and components
2. **API contracts**: All entity types in `api/src/models/{entity}.ts` as interfaces with optional fields; reused in frontend
3. **Environment configuration**:
- API: `PORT` (default 3000), `API_CORS_ORIGINS` (comma-separated)
- Frontend: `CODESPACE_NAME` automatically detected; frontend auto-configures API URL (localhost, Codespace, or runtime config)
4. **Admin features**: Check `isAdmin` from `useAuth()` in components; hides/shows admin menu in Navigation
5. **Discount logic**: Product model includes optional `discount` field (decimal, e.g., 0.25 = 25%); use in price calculations
6. **Error handling**: Routes return HTTP 404 when entity not found; frontend catches with axios error handling (see Products.tsx)

## File Organization Reference

```
api/src/
index.ts # Express app, CORS, Swagger setup, route imports
seedData.ts # Initial data for all entities
models/ # TypeScript interfaces + Swagger schemas (JSDoc)
routes/ # Express routers with CRUD endpoints + JSDoc @swagger

frontend/src/
api/config.ts # Axios baseURL configuration (auto-detects environment)
components/ # React components (Navigation, Products, etc.)
context/ # AuthContext, ThemeContext with providers
App.tsx # React Router setup
```

## Integration Points & Common Tasks

**Adding a new API endpoint**:
1. Define interface in `api/src/models/{entity}.ts` with `@swagger` schema block
2. Add route in `api/src/routes/{entity}.ts` with JSDoc `@swagger` operation blocks (GET/POST/PUT/DELETE)
3. Import route in `api/src/index.ts` and mount at `app.use('/api/{entities}', ...)`
4. Swagger docs auto-update at localhost:3000/api-docs

**Connecting frontend to new API**:
1. Import axios and API config in component
2. Use `useQuery('key', () => axios.get(...))` pattern (react-query)
3. Respect `api.baseURL` from config; it auto-switches between localhost/Codespace/production

**Theme-aware styling**:
- Use `const { darkMode } = useTheme()` in component
- Apply conditional classNames: `${darkMode ? 'bg-dark text-light' : 'bg-white text-gray-800'}`
- See Navigation.tsx for comprehensive example

## Demo Scenarios Hints

This repo is built for Copilot demos:
- **Custom Instructions**: Refer to fictional TAO observability framework (docs/tao.md) in your instructions
- **Agent Mode**: Implement Cart page from mockup (docs/design/cart.png) or new Product from image + natural language
- **Test Generation**: Analyze coverage gaps; prompt to add tests for uncovered routes
- **Vision**: Generate components from design files (docs/design/)
- **MCP Servers**: Use Playwright MCP to write BDD tests; GitHub API MCP to create PRs
- **IaC/Deployment**: Check docs/deployment.md for Bicep/Azure Container Apps patterns

## Notes for AI Agents

- **In-memory data**: Seed data resets on each test; routes use module-level arrays (not database)
- **No authentication**: AuthContext is mock (demo only); endpoints not protected
- **No cart persistence**: Cart functionality stub in Products.tsx; implement in new Cart component
- **Discounts**: Implemented in Product model but not applied in UI (add discount calculation when building Cart)
Comment on lines +110 to +111
- **Images**: Product images in `frontend/public/`; referenced by `imgName` field; add new images and update seedData
9 changes: 8 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import Welcome from './components/Welcome';
import About from './components/About';
import Footer from './components/Footer';
import Products from './components/entity/product/Products';
import Cart from './components/entity/cart/Cart';
import CheckoutPlaceholder from './components/entity/cart/CheckoutPlaceholder';
import Login from './components/Login';
import { AuthProvider } from './context/AuthContext';
import { ThemeProvider } from './context/ThemeContext';
import { CartProvider } from './context/CartContext';
import AdminProducts from './components/admin/AdminProducts';
import { useTheme } from './context/ThemeContext';

Expand All @@ -23,6 +26,8 @@ function ThemedApp() {
<Route path="/" element={<Welcome />} />
<Route path="/about" element={<About />} />
<Route path="/products" element={<Products />} />
<Route path="/cart" element={<Cart />} />
<Route path="/checkout" element={<CheckoutPlaceholder />} />
<Route path="/login" element={<Login />} />
<Route path="/admin/products" element={<AdminProducts />} />
</Routes>
Expand All @@ -37,7 +42,9 @@ function App() {
return (
<AuthProvider>
<ThemeProvider>
<ThemedApp />
<CartProvider>
<ThemedApp />
</CartProvider>
</ThemeProvider>
</AuthProvider>
);
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { Link } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import { useTheme } from '../context/ThemeContext';
import { useState } from 'react';
import { useCart } from '../context/CartContext';

export default function Navigation() {
const { isLoggedIn, isAdmin, logout } = useAuth();
const { darkMode, toggleTheme } = useTheme();
const { itemCount } = useCart();
const [adminMenuOpen, setAdminMenuOpen] = useState(false);

return (
Expand All @@ -29,6 +31,7 @@ export default function Navigation() {
<div className="ml-10 flex items-baseline space-x-4">
<Link to="/" className={`${darkMode ? 'text-light hover:text-primary' : 'text-gray-700 hover:text-primary'} px-3 py-2 rounded-md text-sm font-medium transition-colors`}>Home</Link>
<Link to="/products" className={`${darkMode ? 'text-light hover:text-primary' : 'text-gray-700 hover:text-primary'} px-3 py-2 rounded-md text-sm font-medium transition-colors`}>Products</Link>
<Link to="/cart" className={`${darkMode ? 'text-light hover:text-primary' : 'text-gray-700 hover:text-primary'} px-3 py-2 rounded-md text-sm font-medium transition-colors`}>Cart</Link>
<Link to="/about" className={`${darkMode ? 'text-light hover:text-primary' : 'text-gray-700 hover:text-primary'} px-3 py-2 rounded-md text-sm font-medium transition-colors`}>About us</Link>
{isAdmin && (
<div className="relative">
Expand Down Expand Up @@ -68,6 +71,20 @@ export default function Navigation() {
</div>
</div>
<div className="flex items-center space-x-4">
<Link
to="/cart"
className={`relative inline-flex items-center rounded-md px-2 py-2 ${darkMode ? 'text-light hover:text-primary' : 'text-gray-700 hover:text-primary'} transition-colors`}
aria-label="View cart"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="h-5 w-5">
<path d="M3 5h2l2.2 10.2a2 2 0 002 1.6h7.5a2 2 0 002-1.6L21 7H7" />
<circle cx="10" cy="19" r="1.4" />
<circle cx="17" cy="19" r="1.4" />
</svg>
<span className="absolute -right-2 -top-1 min-w-5 rounded-full bg-primary px-1 text-center text-xs font-bold text-white">
{itemCount}
</span>
Comment on lines +84 to +86
</Link>
<button
onClick={toggleTheme}
className="p-2 rounded-full focus:outline-none transition-colors"
Expand Down
Loading