diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..c5cdd56 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/ISSUE_TEMPLATE/bug_template.md b/.github/ISSUE_TEMPLATE/bug_template.md deleted file mode 100644 index 88e16cc..0000000 --- a/.github/ISSUE_TEMPLATE/bug_template.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: bug -assignees: "" ---- - -PLEASE READ GUIDELINES [HERE](https://github.com/neudesic/neutelligent-ai/blob/main/docs/CONTRIBUTING.md) BEFORE SUBMITTING A BUG! - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: - -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - -- OS: [e.g. iOS] -- Browser [e.g. chrome, safari] -- Version [e.g. 22] - -**Smartphone (please complete the following information):** - -- Device: [e.g. iPhone6] -- OS: [e.g. iOS8.1] -- Browser [e.g. stock browser, safari] -- Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 7983e80..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: "[FEATURE]" -labels: enhancement -type: feature -assignees: "" ---- - -PLEASE READ GUIDELINES [HERE](https://github.com/neudesic/neutelligent-ai/blob/main/docs/CONTRIBUTING.md) BEFORE SUBMITTING AN ENHANCEMENT! - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/issue_template.md b/.github/ISSUE_TEMPLATE/issue_template.md deleted file mode 100644 index ff64c46..0000000 --- a/.github/ISSUE_TEMPLATE/issue_template.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: Issue Template -about: For submitting general User Story level issues -title: "" -labels: "" -assignees: "" ---- - -## Overview - -As a a {ROLE}, I need the {GOAL}, so I can {Value}. - -## Acceptance Criteria - -| AC# | Details | -| --- | -------------------- | -| AC1 | Description for AC 1 | -| AC2 | Description for AC 2 | - -## Definition of Done - -- [ ] DOD 1 -- [ ] DOD 2 - -## Testing Criteria - -- [ ] Unit Tests: Unit Test Requirements -- [ ] Integration Tests: Integration Test Requirements -- [ ] Security Tests: Security Test Requirements -- [ ] Load Testing: Load Test Requirements diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index e0518f4..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,38 +0,0 @@ -# Pull Request Template (CHANGE ME) - -## Description - -_Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change._ - -> Consider using GitHub Copilot for this. - -## Issue(s) - -_Use the `#` key and add issue(s) here_ - -## Type of Change - -_Please delete options that are not relevant._ - -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] This change requires a documentation update - -## How Has This Been Tested? - -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. - -- [ ] Passed Unit Tests -- [ ] Passed CI Build -- [ ] Demo to Team - -## Checklist - -- [ ] My code follows the style guidelines of this project -- [ ] I have performed a self-review of my own code -- [ ] I have made corresponding changes to the documentation -- [ ] My changes generate no new warnings -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] New and existing unit tests pass locally with my changes -- [ ] Any dependent changes have been merged and published in downstream modules diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..02362d8 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,80 @@ +# GitHub Copilot Learning Repository - Code Instructions + +## 🎯 General Guidelines + +- **Always use TypeScript** for new JavaScript/React code with strict mode enabled +- **Prefer functional programming patterns** over imperative approaches +- **Include comprehensive error handling** for all async operations +- **Add JSDoc comments** for all public APIs and complex functions +- **Follow semantic naming conventions** for variables, functions, and files + +## πŸ“ File Organization + +- **Use kebab-case** for file and folder names (e.g., `user-profile.tsx`) +- **Group related files** in dedicated folders with index files for exports +- **Separate concerns** with clear boundaries between components, services, and utilities +- **Place tests** adjacent to source files with `.test.ts` or `.spec.ts` extensions + +## βš›οΈ React Development Standards + +- **Use functional components** with React hooks instead of class components +- **Implement proper prop validation** with TypeScript interfaces +- **Extract custom hooks** for reusable stateful logic +- **Use React.memo** for performance optimization when appropriate +- **Implement proper error boundaries** for component error handling + +## πŸ”§ Code Quality + +- **Follow SOLID principles** in component and service design +- **Use composition over inheritance** patterns +- **Implement proper separation of concerns** between UI and business logic +- **Add comprehensive unit tests** with minimum 80% coverage +- **Use meaningful variable names** that clearly express intent + +## 🚨 Security Practices + +- **Validate all user inputs** on both client and server sides +- **Sanitize data** before displaying in UI components +- **Use environment variables** for sensitive configuration +- **Implement proper authentication checks** for protected routes +- **Follow OWASP security guidelines** for web applications + +## πŸ“š Documentation Standards + +- **Include README files** for all major features and modules +- **Document API endpoints** with clear request/response examples +- **Add inline comments** for complex business logic +- **Create user guides** for new features and tools +- **Maintain up-to-date setup instructions** for development environment + +## πŸ§ͺ Testing Requirements + +- **Write tests first** for critical business logic (TDD approach) +- **Test user interactions** with React Testing Library +- **Mock external dependencies** in unit tests +- **Include accessibility tests** for UI components +- **Create integration tests** for complete user workflows + +## 🎨 Styling Guidelines + +- **Use CSS Modules** or styled-components for component styling +- **Follow BEM methodology** for CSS class naming when using regular CSS +- **Implement responsive design** patterns for mobile compatibility +- **Use design tokens** for consistent spacing, colors, and typography +- **Ensure WCAG 2.1 AA compliance** for accessibility standards + +## πŸ”„ State Management + +- **Use React Context** for application-wide state management +- **Implement proper state normalization** for complex data structures +- **Use reducers** for complex state update logic +- **Cache API responses** appropriately to reduce network requests +- **Handle loading and error states** consistently across the application + +## πŸ“¦ Dependencies + +- **Prefer established libraries** with active maintenance and community support +- **Keep dependencies updated** and audit for security vulnerabilities +- **Use exact version pinning** for critical dependencies +- **Document reasons** for major dependency choices +- **Minimize bundle size** by avoiding unnecessary dependencies \ No newline at end of file diff --git a/.github/instructions/commit-messages.md b/.github/instructions/commit-messages.md new file mode 100644 index 0000000..031334e --- /dev/null +++ b/.github/instructions/commit-messages.md @@ -0,0 +1,88 @@ +--- +applyTo: + - commitMessageGeneration +--- + +# Commit Message Instructions + +## Format Structure + +Use the **Conventional Commits** format for all commit messages: + +``` +[optional scope]: + +[optional body] + +[optional footer(s)] +``` + +## Commit Types + +- **feat**: A new feature for the user +- **fix**: A bug fix for the user +- **docs**: Documentation changes only +- **style**: Code style changes (formatting, missing semi-colons, etc.) +- **refactor**: Code changes that neither fix bugs nor add features +- **perf**: Performance improvements +- **test**: Adding or updating tests +- **chore**: Maintenance tasks, dependency updates +- **ci**: Changes to CI/CD configuration +- **build**: Changes to build system or external dependencies + +## Scope Guidelines + +Use descriptive scopes to indicate the area of change: +- **components**: React component changes +- **api**: Backend API changes +- **auth**: Authentication/authorization +- **ui**: User interface updates +- **docs**: Documentation updates +- **config**: Configuration changes + +## Description Rules + +- **Use imperative mood** ("add feature" not "added feature") +- **Limit to 50 characters** for the subject line +- **Start with lowercase** unless it's a proper noun +- **No period** at the end of the subject line +- **Be specific and descriptive** about what changed + +## Body Guidelines + +- **Wrap at 72 characters** per line +- **Explain what and why** not how +- **Include motivation** for the change +- **Reference related issues** using #issue-number +- **Separate paragraphs** with blank lines + +## Footer Usage + +- **Breaking changes**: Start with "BREAKING CHANGE: " +- **Issue references**: "Closes #123", "Fixes #456" +- **Co-authored by**: For pair programming + +## Examples + +``` +feat(auth): add OAuth2 login integration + +Implement Google OAuth2 authentication to allow users to login +using their Google accounts. This provides a more secure and +convenient authentication method. + +- Add OAuth2 configuration +- Implement callback handling +- Update user model to store OAuth tokens + +Closes #142 +``` + +``` +fix(components): resolve memory leak in UserProfile component + +The UserProfile component was not properly cleaning up event +listeners in useEffect, causing memory leaks on route changes. + +BREAKING CHANGE: UserProfile now requires cleanup prop +``` \ No newline at end of file diff --git a/.github/instructions/react-development.md b/.github/instructions/react-development.md new file mode 100644 index 0000000..392292d --- /dev/null +++ b/.github/instructions/react-development.md @@ -0,0 +1,83 @@ +--- +applyTo: + - codeGeneration + - testGeneration + - reviewSelection +filePatterns: + - "**/*.tsx" + - "**/*.ts" + - "**/*.jsx" + - "**/*.js" +keywords: + - react + - component + - hook + - jsx + - tsx +--- + +# React Development Instructions + +## Component Architecture + +- **Always use functional components** with hooks instead of class components +- **Create custom hooks** for reusable stateful logic across components +- **Use React.memo** for components that receive the same props frequently +- **Implement proper prop drilling solutions** with Context API or state management +- **Keep components small and focused** on a single responsibility + +## TypeScript Integration + +- **Define explicit interfaces** for all component props +- **Use generic types** for reusable components where appropriate +- **Avoid using `any` type** - prefer `unknown` or proper type definitions +- **Create union types** for component variants and states +- **Use type guards** for runtime type checking when needed + +## State Management + +- **Use useState** for simple local component state +- **Use useReducer** for complex state logic with multiple sub-values +- **Implement Context API** for application-wide state that multiple components need +- **Consider external state management** (Redux, Zustand) for complex applications +- **Keep state as close to where it's used** as possible + +## Performance Optimization + +- **Use useMemo** for expensive calculations that depend on specific props/state +- **Use useCallback** for functions passed as props to prevent unnecessary re-renders +- **Implement code splitting** with React.lazy and Suspense for large components +- **Optimize re-renders** by avoiding object/array creation in render methods +- **Profile performance** using React DevTools Profiler + +## Error Handling + +- **Implement Error Boundaries** for graceful error handling in component trees +- **Use try-catch blocks** in async functions and event handlers +- **Provide fallback UI** for error states and loading states +- **Log errors appropriately** without exposing sensitive information +- **Handle network errors** gracefully with retry mechanisms + +## Testing Standards + +- **Test component behavior** rather than implementation details +- **Use React Testing Library** for component testing +- **Mock external dependencies** and API calls +- **Test accessibility** with jest-axe +- **Maintain test coverage** above 80% for critical components + +## Styling Guidelines + +- **Use CSS Modules** for component-scoped styling +- **Follow BEM methodology** when using regular CSS +- **Implement design tokens** for consistent spacing, colors, and typography +- **Ensure responsive design** with mobile-first approach +- **Test across different devices** and browsers + +## Hook Best Practices + +- **Place hooks at the top level** of function components +- **Use dependency arrays correctly** in useEffect to avoid infinite loops +- **Clean up side effects** in useEffect return functions +- **Create custom hooks** for complex logic that uses multiple built-in hooks +- **Follow hook naming convention** with "use" prefix for custom hooks \ No newline at end of file diff --git a/.github/instructions/security-review.md b/.github/instructions/security-review.md new file mode 100644 index 0000000..606b0e5 --- /dev/null +++ b/.github/instructions/security-review.md @@ -0,0 +1,61 @@ +--- +applyTo: + - reviewSelection + - codeGeneration +filePatterns: + - "**/*.ts" + - "**/*.js" + - "**/*.tsx" + - "**/*.jsx" + - "**/*.cs" + - "**/*.py" +keywords: + - security + - auth + - api + - endpoint + - validation + - sanitize +--- + +# Security Review Instructions + +## Authentication & Authorization + +- **Verify authentication mechanisms** are properly implemented on all protected endpoints +- **Check authorization levels** ensure users can only access resources they're permitted to +- **Validate JWT tokens** are properly signed, not expired, and contain required claims +- **Review session management** for proper timeout and secure storage +- **Ensure role-based access control** (RBAC) is consistently applied + +## Input Validation & Sanitization + +- **Validate all user inputs** on both client and server sides +- **Check for SQL injection vulnerabilities** in database queries +- **Review XSS prevention** measures for user-generated content +- **Validate file upload security** including type restrictions and size limits +- **Check parameter tampering protection** for URL and form parameters + +## Data Protection + +- **Review sensitive data encryption** both at rest and in transit +- **Check password hashing** uses secure algorithms (bcrypt, Argon2) +- **Validate PII handling** complies with privacy regulations +- **Review data retention policies** and secure deletion practices +- **Check for data exposure** in logs, error messages, and debug output + +## Network Security + +- **Review CORS configuration** for appropriate origin restrictions +- **Check HTTPS enforcement** and secure cookie settings +- **Validate security headers** (CSP, HSTS, X-Frame-Options) +- **Review rate limiting** implementation for API endpoints +- **Check for information disclosure** in HTTP responses + +## Error Handling + +- **Ensure error messages** don't expose sensitive system information +- **Review exception handling** doesn't leak stack traces in production +- **Check logging practices** don't record sensitive data +- **Validate error codes** provide appropriate information without revealing internals +- **Review debug information** is disabled in production environments \ No newline at end of file diff --git a/.github/prompts/api-security-review.prompt.md b/.github/prompts/api-security-review.prompt.md new file mode 100644 index 0000000..99e4525 --- /dev/null +++ b/.github/prompts/api-security-review.prompt.md @@ -0,0 +1,78 @@ +--- +mode: 'ask' +model: Claude Sonnet 4 +description: 'Perform comprehensive REST API security review' +--- + +# API Security Review + +Perform a comprehensive security review of the selected API code and provide actionable recommendations. + +## Security Areas to Review + +### πŸ” Authentication & Authorization +- Verify **proper authentication** mechanisms are implemented +- Check **authorization controls** for all endpoints +- Review **JWT token validation** and expiration handling +- Ensure **role-based access control** (RBAC) is properly implemented +- Validate **session management** security + +### πŸ“ Input Validation & Sanitization +- Check **input validation** for all user-provided data +- Verify **SQL injection prevention** measures +- Review **XSS protection** for output data +- Validate **file upload security** if applicable +- Check **parameter tampering** protection + +### 🚦 Rate Limiting & DoS Protection +- Verify **rate limiting** implementation +- Check **request throttling** mechanisms +- Review **resource exhaustion** protections +- Validate **concurrent request handling** + +### πŸ” Error Handling & Information Disclosure +- Review **error message security** (no sensitive data exposure) +- Check **stack trace protection** in production +- Verify **logging practices** don't expose sensitive information +- Validate **HTTP response codes** are appropriate + +### 🌐 Network Security +- Review **CORS configuration** and security +- Check **HTTPS enforcement** and TLS settings +- Verify **secure headers** implementation (CSP, HSTS, etc.) +- Validate **API versioning** security considerations + +### πŸ—„οΈ Data Protection +- Check **sensitive data encryption** at rest and in transit +- Review **password hashing** and storage practices +- Verify **PII handling** compliance +- Validate **data retention** policies + +## Output Format + +Provide results in the following format: + +### 🚨 Critical Issues +List any critical security vulnerabilities that need immediate attention. + +### ⚠️ High Priority Issues +List high-priority security concerns that should be addressed soon. + +### πŸ“‹ Medium Priority Recommendations +List medium-priority improvements and best practices. + +### βœ… Security Strengths +Highlight what the API does well from a security perspective. + +### πŸ“ Action Items +Provide a prioritized TODO list with: +- **Issue description** +- **Risk level** (Critical/High/Medium/Low) +- **Remediation steps** +- **Estimated effort** + +## Additional Context +API Framework: ${input:framework:What framework/technology is being used?} +Authentication Method: ${input:authMethod:JWT, OAuth, API Keys, etc.} + +Reference our security guidelines in [copilot-instructions.md](../copilot-instructions.md) for additional context. \ No newline at end of file diff --git a/.github/prompts/azure.prompt.md b/.github/prompts/azure.prompt.md deleted file mode 100644 index 2cc5c9b..0000000 --- a/.github/prompts/azure.prompt.md +++ /dev/null @@ -1,62 +0,0 @@ -# Azure Bicep Assistant - -You are an expert Azure Bicep assistant, specializing in Infrastructure as Code (IaC) for Azure resources. You have deep knowledge of Azure Bicep, Azure Resource Manager (ARM), and Azure best practices. - -## Your Capabilities - -1. **Best Practices Guidance**: - - - Suggest resource naming conventions that follow Azure best practices - - Recommend proper use of modules, parameters, variables, and outputs - - Guide on securing secrets and sensitive data - - Advise on resource organization and dependency management - - Help with implementing the principle of least privilege - - Suggest appropriate tagging strategies - - Recommend deployment strategies - -2. **Code Review**: - - - Identify security vulnerabilities and suggest fixes - - Spot performance issues or potential cost optimizations - - Check for compliance with organizational or industry standards - - Validate resource configurations against Azure best practices - - Suggest improvements for readability and maintainability - - Identify potential deployment errors or circular dependencies - - Recommend more efficient or idiomatic Bicep patterns - -3. **Code Generation**: - - Create Bicep templates for specific Azure resources or solutions - - Convert ARM templates to Bicep - - Generate parameter files for different environments - - Create modules for reusable components - - Implement complex deployment scenarios - - Generate deployment scripts or pipelines - -## How to Interact with Me - -- **For best practices**: Ask about recommended approaches or specific guidance for your Bicep implementation -- **For code reviews**: Share your Bicep code and ask for feedback or specific aspects to check -- **For code generation**: Describe the Azure resources you need to deploy, and I'll generate appropriate Bicep code - -## Expected Context - -When asking for help, please provide: - -1. The purpose of your deployment -2. Any specific Azure regions or environments -3. Security or compliance requirements -4. Any existing infrastructure constraints -5. Resource naming conventions you follow - -## Sample Interactions - -**Best Practices Request Example:** -"What's the best way to handle secrets in my Bicep templates for a production environment?" - -**Code Review Request Example:** -"Can you review this Bicep code for a web app with SQL backend and suggest improvements?" - -**Code Generation Request Example:** -"Please generate Bicep code for a standard three-tier architecture with web, API, and database layers following best practices." - -I'll respond with clear, actionable advice, code samples, and explanations tailored to your specific scenario. diff --git a/.github/prompts/c-sharp.prompt.md b/.github/prompts/c-sharp.prompt.md deleted file mode 100644 index 54efe0a..0000000 --- a/.github/prompts/c-sharp.prompt.md +++ /dev/null @@ -1,15 +0,0 @@ -# C# Prompts and Style Guide - -## C# Standards and Practices - -- Follow Microsoft's C# Coding Conventions -- Use Pascal Case for class names, properties, and methods -- Use Camel Case for local variables and parameters -- Prefer async/await over direct Task manipulation -- Use XML documentation comments for public APIs -- Implement IDisposable for classes that own disposable resources -- Use nullable reference types -- Prefer LINQ queries for collection transformations -- Write unit tests using xUnit -- Follow SOLID principles -- No minimal API, use controllers and services diff --git a/.github/prompts/create-documentation.prompt.md b/.github/prompts/create-documentation.prompt.md new file mode 100644 index 0000000..cf76504 --- /dev/null +++ b/.github/prompts/create-documentation.prompt.md @@ -0,0 +1,116 @@ +--- +mode: 'agent' +model: GPT-4.1 +description: 'Generate comprehensive documentation for code, APIs, or features' +--- + +# Create Documentation + +Generate comprehensive, user-friendly documentation for the selected code, API, or feature. + +## Documentation Type +${input:docType:README, API docs, User guide, Developer guide, etc.} + +## Documentation Requirements + +### πŸ“ Content Structure + +#### **For README Files:** +- **Project title** and brief description +- **Installation instructions** with prerequisites +- **Quick start guide** with basic examples +- **Usage examples** with code snippets +- **Configuration options** and environment variables +- **Contributing guidelines** and development setup +- **License information** and acknowledgments + +#### **For API Documentation:** +- **Endpoint overview** with HTTP methods and URLs +- **Request/response examples** with sample data +- **Authentication requirements** and header format +- **Error codes** and error response formats +- **Rate limiting** and usage guidelines +- **SDK examples** in multiple programming languages + +#### **For User Guides:** +- **Step-by-step instructions** with screenshots/diagrams +- **Common use cases** and workflows +- **Troubleshooting section** with FAQs +- **Best practices** and tips +- **Glossary** of terms and concepts + +#### **For Developer Guides:** +- **Architecture overview** and design decisions +- **Code organization** and file structure +- **Development workflow** and contribution process +- **Testing strategies** and requirements +- **Deployment procedures** and environments + +### 🎨 Formatting Standards + +#### **Markdown Best Practices:** +- Use **clear headings** with proper hierarchy (H1, H2, H3) +- Include **table of contents** for longer documents +- Use **code blocks** with syntax highlighting +- Add **badges** for build status, version, license +- Include **screenshots** and **diagrams** where helpful + +#### **Code Examples:** +- Provide **working code samples** that can be copy-pasted +- Include **error handling** in examples +- Use **realistic data** in examples, not placeholder text +- Show **before and after** states where relevant +- Include **multiple approaches** for complex scenarios + +#### **Visual Elements:** +- Add **emojis** for section headers (πŸ“, πŸš€, βš™οΈ, etc.) +- Use **tables** for structured information +- Include **flowcharts** or **diagrams** for complex processes +- Add **callout boxes** for important notes and warnings + +### 🎯 Quality Standards + +#### **Clarity and Accessibility:** +- Write in **clear, concise language** avoiding jargon +- Use **active voice** instead of passive voice +- Include **definitions** for technical terms +- Provide **context** for why something is important +- Structure content **logically** from basic to advanced + +#### **Completeness:** +- Cover **all major features** and use cases +- Include **edge cases** and limitations +- Provide **troubleshooting** information +- Add **links to related resources** +- Keep information **up-to-date** with current version + +#### **User Experience:** +- Start with **quick wins** to get users engaged +- Provide **multiple learning paths** (beginner to advanced) +- Include **search-friendly** headings and keywords +- Add **internal links** for easy navigation +- Test instructions with **fresh eyes** + +## Target Audience +${input:audience:Developers, End users, System administrators, etc.} + +## Additional Context +- **Technology stack**: ${input:techStack:Languages, frameworks, tools used} +- **Complexity level**: ${input:complexity:Beginner, Intermediate, Advanced} +- **Documentation scope**: ${input:scope:What specific aspects to cover} + +## Special Requirements +${input:requirements:Any specific formatting, style, or content requirements} + +## References +- Follow our documentation standards in [copilot-instructions.md](../copilot-instructions.md) +- Consider accessibility guidelines for inclusive documentation +- Include links to official documentation for external dependencies + +## Output Format +Generate the documentation in **Markdown format** with: +- **Proper heading structure** +- **Working code examples** +- **Clear step-by-step instructions** +- **Helpful visual elements** +- **Professional formatting** \ No newline at end of file diff --git a/.github/prompts/create-react-component.prompt.md b/.github/prompts/create-react-component.prompt.md new file mode 100644 index 0000000..d7c5638 --- /dev/null +++ b/.github/prompts/create-react-component.prompt.md @@ -0,0 +1,44 @@ +--- +mode: 'agent' +model: Claude Sonnet 4 +description: 'Create a new React component with TypeScript and tests' +tools: ['terminal'] +--- + +# Create React Component + +Create a new React component named **${input:componentName:Component name}** with the following requirements: + +## Component Structure +- Use **TypeScript** with strict typing and proper interfaces +- Implement as a **functional component** with hooks +- Include **proper prop validation** with TypeScript interfaces +- Add **JSDoc documentation** for the component and its props +- Use **kebab-case** for the folder and file names + +## Styling +- Include **CSS Modules** for component-specific styling +- Implement **responsive design** patterns +- Follow **WCAG 2.1 AA** accessibility standards +- Use **semantic HTML** elements + +## Testing +- Create **comprehensive unit tests** using React Testing Library +- Include **accessibility tests** with jest-axe +- Test **user interactions** and **state changes** +- Mock any **external dependencies** + +## File Organization +Create the component in: `src/components/${input:componentName}/` + +### Required files: +- `${input:componentName}.tsx` - Main component file +- `${input:componentName}.module.css` - Component styles +- `${input:componentName}.test.tsx` - Unit tests +- `index.ts` - Export file +- `README.md` - Component documentation + +## Additional Context +Component purpose: ${input:purpose:Brief description of what this component does} + +Reference the [copilot-instructions.md](../copilot-instructions.md) for coding standards and best practices. \ No newline at end of file diff --git a/.github/prompts/generate-unit-tests.prompt.md b/.github/prompts/generate-unit-tests.prompt.md new file mode 100644 index 0000000..e204024 --- /dev/null +++ b/.github/prompts/generate-unit-tests.prompt.md @@ -0,0 +1,123 @@ +--- +mode: 'agent' +model: Claude Sonnet 4 +description: 'Generate comprehensive unit tests for selected code' +tools: ['terminal'] +--- + +# Generate Unit Tests + +Generate comprehensive unit tests for the selected code with full coverage and best practices. + +## Test Requirements + +### πŸ§ͺ Testing Framework +- Use **Jest** as the primary testing framework +- Use **React Testing Library** for component testing +- Include **@testing-library/jest-dom** for enhanced matchers +- Add **@testing-library/user-event** for user interaction testing + +### πŸ“‹ Test Coverage Areas + +#### **Functionality Testing** +- Test all **public methods** and **functions** +- Cover all **conditional branches** and **edge cases** +- Test **error handling** and **exception scenarios** +- Validate **return values** and **side effects** + +#### **Component Testing** (for React components) +- Test **component rendering** with different props +- Test **user interactions** (clicks, form inputs, etc.) +- Test **state changes** and **effect hooks** +- Test **conditional rendering** scenarios +- Test **accessibility** with jest-axe + +#### **Integration Points** +- Mock **external dependencies** appropriately +- Test **API calls** and **async operations** +- Test **event handlers** and **callbacks** +- Test **context providers** and **consumers** + +### πŸ—οΈ Test Structure + +#### **Organize tests using:** +```javascript +describe('ComponentName/FunctionName', () => { + describe('when condition', () => { + it('should do something specific', () => { + // Test implementation + }); + }); +}); +``` + +#### **Include test categories:** +- **Happy path** scenarios +- **Edge cases** and **boundary conditions** +- **Error scenarios** and **failure cases** +- **Performance considerations** (if applicable) + +### 🎯 Test Quality Standards + +#### **Best Practices:** +- Use **descriptive test names** that explain the scenario +- Follow **Arrange-Act-Assert** pattern +- Keep tests **independent** and **isolated** +- Use **meaningful assertions** with clear error messages +- Mock **external dependencies** but avoid over-mocking + +#### **Accessibility Testing:** +- Test **keyboard navigation** +- Test **screen reader compatibility** +- Test **ARIA labels** and **roles** +- Test **focus management** + +#### **Performance Testing:** +- Test **component re-render optimization** +- Test **memory leak prevention** +- Test **async operation cleanup** + +## Mock Requirements + +### **What to Mock:** +- **External API calls** +- **Browser APIs** (localStorage, fetch, etc.) +- **Third-party libraries** +- **File system operations** +- **Timer functions** (setTimeout, setInterval) + +### **What NOT to Mock:** +- **Internal utility functions** +- **React hooks** (unless testing custom hooks) +- **Component props** (test with real data) + +## Test File Organization + +### **File Naming:** +- Use `.test.tsx` for React component tests +- Use `.test.ts` for utility function tests +- Use `.spec.ts` for integration tests + +### **File Location:** +- Place tests **adjacent** to source files +- Use `__tests__` folder for complex test suites +- Create `test-utils.ts` for shared testing utilities + +## Additional Requirements + +### **Code Coverage:** +- Aim for **minimum 80%** code coverage +- Achieve **100%** coverage for critical business logic +- Include **branch coverage** and **function coverage** + +### **Test Documentation:** +- Add **JSDoc comments** for complex test setups +- Include **README** for test suite explanation +- Document **mock strategies** and **test data** + +## Context +File/Component being tested: ${selection} +Testing framework preference: ${input:framework:Jest, Vitest, etc.} +Special requirements: ${input:requirements:Any specific testing requirements?} + +Reference our testing guidelines in [copilot-instructions.md](../copilot-instructions.md) for additional standards. \ No newline at end of file diff --git a/.github/prompts/security.prompt.md b/.github/prompts/security.prompt.md deleted file mode 100644 index 098bc2a..0000000 --- a/.github/prompts/security.prompt.md +++ /dev/null @@ -1,21 +0,0 @@ -# Security Review - -You are a security expert. Please review the code and provide feedback on security best practices, vulnerabilities, and potential improvements. - -Do not make any changes to the code. Just provide feedback. - -## General Security Review: - -* Ensure sensitive data is encrypted at rest and in transit -* Use secure coding practices to prevent common vulnerabilities (e.g., SQL injection, XSS, CSRF) -* Implement proper error handling and logging -* Regularly update dependencies and libraries to patch known vulnerabilities -* Use secure authentication and authorization mechanisms (e.g., OAuth, JWT) -* Implement security headers (e.g., Content Security Policy, X-Content-Type-Options) - -## Secure REST API review - -* Ensure all endpoints are protected by authentication and authorization -* Validate all user inputs and sanitize data -* Implement rate limiting and throttling -* Implement logging and monitoring for security events diff --git a/.github/workflows/markdown-lint.yaml b/.github/workflows/markdown-lint.yaml deleted file mode 100644 index 775d14a..0000000 --- a/.github/workflows/markdown-lint.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: Lint all markdown files - -on: - pull_request: - branches: [main] - push: - branches-ignore: [main] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 # Checkout the repository - - name: Markdown lint - uses: DavidAnson/markdownlint-cli2-action@v9 - with: - globs: | - README.md - CHANGELOG.md - docs/*.md diff --git a/.vscode/settings.json b/.vscode/settings.json index c2f78cb..e02b45b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,11 @@ { "github.copilot.enable": { - "*": false, + "*": true, "plaintext": false, "markdown": true, "scminput": false, "bicep": true, - "csharp": true + "csharp": true, + "typescriptreact": true } } diff --git a/README.md b/README.md index 18a6dbe..b8c8d49 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,83 @@ Learn How to Effectively Use the GitHub Copilot Family of Tools. +## Goal + +In this workshop, you will learn how to effectively use GitHub Copilot tools, including Copilot Chat, Copilot Ask, Copilot Edit, and Copilot Agent. The workshop is structured into lessons and labs that provide both conceptual understanding and hands-on experience. + ## Overview +This repository contains lessons and labs designed to help you learn how to effectively use GitHub Copilot tools, including Copilot Chat, Copilot Ask, Copilot Edit, and Copilot Agent. The lessons cover foundational topics, best practices, and real-world examples, while the labs provide hands-on exercises to apply what you've learned. + +## Repository Structure + +- **lessons/**: Structured learning modules covering GitHub Copilot fundamentals, tools, and advanced techniques + - **01-Installation-and-Setup/**: Installation guides and environment setup + - **02-Getting-Started/**: Core Copilot tools and features with language-specific labs + - **03-Context-and-Prompt/**: Advanced prompting techniques and context management + - **04-Advanced-Tools/**: Advanced features including MCP (Model Context Protocol) +- **sample-code/**: Practical applications for hands-on learning + - **eShop/**: Enterprise e-commerce application (.NET) + - **Orders/**: Simple order management API (.NET) + - **SimpleFullStack/**: Full-stack application with .NET API and web frontend + ## Getting Started -1. Clone this repo. -1. Install recommended extensions. -1. Review the lessons. -1. Review the sample apps. +1. Clone this repository +2. Install recommended VS Code extensions (GitHub Copilot, GitHub Copilot Chat) +3. Review the lessons in order +4. Practice with the sample applications +5. Complete the hands-on labs +6. Explore advanced topics and contribute to the repository + +## Lessons + +### 01 - Installation and Setup + +- **[Installing GitHub Copilot](./lessons/01-Installation-and-Setup/01-installing-copilot.md)**: Complete setup guide with troubleshooting and enterprise considerations + +### 02 - Getting Started with GitHub Copilot Tools + +- **[GitHub Copilot Tools Overview](./lessons/02-Getting-Started/react/2.0-github-copilot-tools.md)**: Introduction to the Copilot tool family +- **[GitHub Copilot Web Features](./lessons/02-Getting-Started/github-copilot-web-features.md)**: Platform features beyond the IDE +- **[Code Review with Copilot](./lessons/02-Getting-Started/copilot-code-review.md)**: AI-assisted code review workflows +- **[Team Adoption Strategy](./lessons/02-Getting-Started/implementation-roadmap.md)**: An 8-week roadmap for successful team adoption + +#### Language-Specific Labs + +**C# / .NET:** + +- **[Exploring @workspace](./lessons/02-Getting-Started/c%23/2.0-exploring-workspace.md)**: Comprehensive workspace analysis and coding standards +- **[Copilot Ask Lab](./lessons/02-Getting-Started/c%23/2.1-exploring-copilot-ask.md)**: Targeted development assistance with Orders API +- **[Copilot Agent Lab](./lessons/02-Getting-Started/c%23/2.2-exploring-copilot-agent.md)**: End-to-end feature development +- **[Copilot Inline Lab](./lessons/02-Getting-Started/c%23/2.3-exploring-copilot-inline.md)**: Rapid code generation and editing + +**React / JavaScript:** + +- **[Copilot Ask (React)](./lessons/02-Getting-Started/react/2.1-exploring-copilot-ask-(react).md)**: React-specific assistance and optimization +- **[Copilot Edit (React)](./lessons/02-Getting-Started/react/2.2-exploring-copilot-edit-(react).md)**: Component modernization and enhancement +- **[Copilot Agent (React)](./lessons/02-Getting-Started/react/2.3-exploring-copilot-agent-(react).md)**: Full-scale React application development +- **[Copilot Inline (React)](./lessons/02-Getting-Started/react/2.4-exploring-copilot-inline-(react).md)**: Rapid iterations and inline development + +### 03 - Context and Prompt Engineering + +- **[Managing Context Effectively](./lessons/03-Context-and-Prompt/3.1-managing-context-effectively.md)**: Master context with #-mentions, @-participants, and external references +- **[Custom Instructions & Workspace Configuration](./lessons/03-Context-and-Prompt/3.2-custom-instructions-workspace.md)**: Align Copilot with team standards and project requirements +- **[Advanced Prompt Engineering](./lessons/03-Context-and-Prompt/3.3-prompt-engineering-advanced.md)**: Apply advanced prompting techniques for complex scenarios + +### 04 - Advanced Tools + +- **[Understanding MCP](./lessons/04-Advanced-Tools/4.1-understanding-mcp.md)**: Model Context Protocol fundamentals +- **[GitHub MCP Lab](./lessons/04-Advanced-Tools/4.2-github-mcp-lab.md)**: Hands-on MCP implementation + +## Sample Applications -### Lessons +The repository includes three sample applications for hands-on practice: -- [Lesson 1: Installing and Configuring GitHub Copilot](docs/1-installing-copilot.md) -- [Lesson 2: Development with GitHub Copilot](docs/2-development-with-copilot.md) -- [Lesson 3: Testing with GitHub Copilot](docs/3-testing-with-copilot.md) -- [Lesson 4: Creating Documentation with GitHub Copilot](docs/4-creating-documentation-with-copilot.md) -- [Lesson 5: DevOps with GitHub Copilot](docs/5-devops-with-copilot.md) -- [Lesson 6: Best practices with GitHub Copilot](docs/6-best-practices-with-copilot.md) -- [Lesson 7: Hot keys and commands](docs/7-hot-keys-and-commands.md) -- [Lesson 8: Using GitHub Copilot Agent](docs/8-using-github-copilot-agent.md) +1. **eShop**: A comprehensive .NET enterprise e-commerce application with microservices architecture +2. **Orders**: A simple .NET Web API for order management, perfect for learning Copilot basics +3. **SimpleFullStack**: A full-stack application combining .NET API with modern web frontend -## Directory Structure +## Contributing -1. `docs` - holds lessons and other documentation -1. `samples` - holds sample code for lessons +This is an educational repository. Feel free to suggest improvements or additional examples that would enhance the learning experience. diff --git a/_schedule/00-copilot_setup.md b/_schedule/00-copilot_setup.md deleted file mode 100644 index 4e90641..0000000 --- a/_schedule/00-copilot_setup.md +++ /dev/null @@ -1,26 +0,0 @@ -## βœ… Prerequisite: Setup and Installation - -> This document should be provided **before** the workshops begin. - -### 1. Account Setup - -- GitHub Account: Personal or Enterprise -- Copilot licensing: Individual vs Team - -### 2. IDE Integration - -- VS Code setup (recommended) -- JetBrains integration (if needed) -- Other: Neovim, Visual Studio - -### 3. CLI and Web Interface - -- Install and configure Copilot CLI -- Explore Copilot in the GitHub web UI - -### 4. Troubleshooting - -- Authentication & authorization errors -- Proxy, firewall, or connectivity issues - ---- diff --git a/_schedule/01-github_copilot.md b/_schedule/01-github_copilot.md deleted file mode 100644 index 68afdf6..0000000 --- a/_schedule/01-github_copilot.md +++ /dev/null @@ -1,45 +0,0 @@ -## πŸš€ Workshop 1: Getting Started with GitHub Copilot - -**Duration:** 90–120 minutes - -### Objectives - -- Understand what Copilot is and how it works -- Explore all the ways you can use Copilot -- Learn to interact with Copilot through examples - -### Agenda - -#### 1. Introduction to GitHub Copilot (20 min) - -- What is Copilot? -- LLM basics (GPT-4 vs GPT-3.5) -- Where Copilot fits in your workflow - -#### 2. Overview of Copilot Tools (10 min) - -- IDE usage: Inline, panel, chat -- CLI commands and GitHub web UI - -#### 3. Hands-On: Exploring Copilot Modes (30 min) - -- Inline suggestions: writing functions, completing code -- Copilot Chat: asking for explanations or fixing bugs -- Copilot Edit: refactor an existing function -- Copilot Agent (demo): task automation - -#### 4. Example Use Cases (20 min) - -- Generate CRUD endpoints -- Add comments and docstrings -- Translate Python to JavaScript -- Use with shell scripts and Dockerfiles - -#### 5. Best Practices (10 min) - -- Use comments for guidance -- Write partial code for better completions - -#### 6. Open Q&A (10–15 min) - ---- diff --git a/_schedule/02-prompt_engineering.md b/_schedule/02-prompt_engineering.md deleted file mode 100644 index 46ca067..0000000 --- a/_schedule/02-prompt_engineering.md +++ /dev/null @@ -1,44 +0,0 @@ -## 🧠 Workshop 2: Prompt Engineering for GitHub Copilot - -**Duration:** 90–120 minutes - -### Objectives - -- Learn how to guide Copilot with effective prompts -- Understand context, tokens, and prompt structure -- Practice prompt writing and optimization - -### Agenda - -#### 1. What is Prompt Engineering? (15 min) - -- Why prompting matters -- Real-world impact on Copilot results - -#### 2. Understanding Tokens & Context (15 min) - -- What are tokens? -- Context window limitations -- File/folder scope and history - -#### 3. Writing Effective Prompts (20 min) - -- Clear intent, short and specific -- Provide relevant context -- Examples: `"Write a React form for..."` - -#### 4. Advanced Prompt Techniques (20 min) - -- Iterative refinement -- Step-by-step prompting -- Role prompting: `"Act as a senior developer..."` - -#### 5. Hands-On Lab: Prompt Writing (30 min) - -- Improve bad prompts -- Write prompts for: - - Unit tests - - Markdown documentation - - Code translations - -#### 6. Discussion & Q&A (10–15 min) diff --git a/_schedule/03-design_patterns.md b/_schedule/03-design_patterns.md deleted file mode 100644 index c166035..0000000 --- a/_schedule/03-design_patterns.md +++ /dev/null @@ -1,40 +0,0 @@ -## πŸ—οΈ Workshop 3: Copilot Design Patterns and Best Practices - -**Duration:** 90–120 minutes - -### Objectives - -- Learn how to structure Copilot use for full projects -- Work through design patterns with Copilot -- Explore architectural workflows and documentation use - -### Agenda - -#### 1. Scaffolding with Copilot (15 min) - -- Start with directory and file structure -- Initial app scaffolding prompts - -#### 2. Working Incrementally (20 min) - -- Break big tasks into small prompts -- Focus on one logical step at a time - -#### 3. Using Libraries and Conventions (15 min) - -- Encourage Copilot to follow best practices -- Integrate common libraries (React, Flask, etc.) - -#### 4. Coding Standards & Linters (10 min) - -- Setup eslint/prettier/black/etc. -- Enforce style for better completions - -#### 5. Hands-On Design Pattern Labs (30–40 min) - -- TDD with Copilot: write test, then implementation -- Refactor a function using Copilot Edit -- Use Copilot for documentation-driven design -- Generate mock data and error handling stubs - -#### 6. Recap and Feedback (10–15 min) diff --git a/_schedule/outline.md b/_schedule/outline.md deleted file mode 100644 index 47d92b0..0000000 --- a/_schedule/outline.md +++ /dev/null @@ -1,188 +0,0 @@ -# Comprehensive GitHub Copilot Training Workshop Outline - ---- - -## 1. Setup and Installation - -### Account Setup - -- Creating GitHub accounts (Enterprise or Personal) -- Licensing and subscription details (individual vs. team) - -### IDE Integration - -- VS Code extension setup -- JetBrains IDE integration (if applicable) -- Other supported IDEs (Visual Studio, Neovim) - -### CLI and Web Interface - -- Copilot CLI setup -- Copilot web setup and configuration - -### Troubleshooting Common Setup Issues - -- Authentication and authorization errors -- Connectivity and firewall considerations - ---- - -## 2. Introduction to GitHub Copilot - -### What is GitHub Copilot? - -- Overview of Large Language Models (LLMs) -- How Copilot utilizes OpenAI's GPT models - -### Core Components - -- **Tools/Integrations:** IDE, CLI, Web interface -- **Language Models:** Different models available and their strengths (e.g., GPT-4 vs GPT-3.5) - -### Understanding Copilot’s Capabilities and Limitations - -- Realistic expectations -- Responsible usage (security considerations, sensitive data handling) - ---- - -## 3. Detailed Exploration of Copilot Features - -### a. Where and How to Use Copilot - -- IDE (Inline completions, Copilot panel, Chat) -- CLI (commands, tasks, workflow automation) -- Web (quick edits, exploring concepts) - -### b. Selecting Your Copilot Model - -- Differences between available models - - Performance and accuracy considerations - - Cost vs. benefit analysis - -### c. Providing Context to Copilot - -- Importance of Context (why it matters) -- Context indicators: `@file`, `@folder` -- Practical examples of effective context usage - -### d. Copilot Chat - -- Overview of Copilot Chat functionality -- Typical chat use cases: - - Code brainstorming - - Debugging help - - Conceptual explanations - -### e. Copilot Edit - -- Overview of Copilot Edit functionality -- Edit use cases: - - Refactoring and optimizing code - - Code transformations (e.g., language conversions) - - Bulk editing tasks - -### f. Copilot Agent - -- Overview of Copilot Agent functionality -- Agent use cases: - - Automating common tasks - - Advanced workflow integration - - Multi-step reasoning - -### g. Inline Suggestions - -- Overview of inline code suggestions -- Use cases and best practices: - - Incremental code completion - - Reducing boilerplate - - Efficiency in coding tasks - -### h. Fix Error - -- Overview of error identification and correction -- Use cases: - - Debugging syntax or logic errors - - Automatic error resolution - ---- - -## 4. Effective Prompt Engineering - -### Introduction to Prompt Engineering - -- Why it matters for Copilot -- Impacts on results accuracy and consistency - -### Tokens and Context Windows - -- What are tokens (explained simply and practically) -- Understanding context windows and their limitations -- Strategies to manage and optimize context usage - -### Crafting Effective Prompts - -- Attributes of high-quality prompts: - - Clarity and specificity - - Contextual completeness - - Structured prompting (step-by-step, role prompting) - -### Advanced Prompt Techniques - -- Iterative prompting -- Using Copilot/LLMs to generate and improve prompts - ---- - -## 5. Recommended Copilot Design Patterns and Best Practices - -### Architectural Scaffolding - -- Defining project structure first -- Using Copilot for initial high-level architecture - -### Working Incrementally - -- Breaking complex problems into manageable chunks -- Enhancing accuracy by sequential task focus - -### Leveraging Established Patterns and Libraries - -- Using Copilot with accepted coding standards, frameworks, and libraries -- Ensuring code reliability and maintainability - -### Contextual Markdown Documentation - -- Maintaining `.md` files with source code -- Enhancing Copilot’s understanding via documentation - ---- - -## 6. Coding Standards and Conventions - -- Importance of consistent naming conventions -- Uniform coding style enforcement -- Integration with automated linters and formatters - ---- - -## 7. Practical Use Cases and Examples - -- Generating unit tests from existing code -- Test-Driven Development (TDD) with Copilot -- Refactoring validated by Copilot-generated tests -- Project outlining and documentation via `.md` files -- Automated coding from acceptance criteria -- Mock data generation -- Automated generation of company-required documentation: - - Requirements documentation - - Unit test documentation - - Technical documentation and user guides - ---- - -## 8. Q&A and Hands-On Lab - -- Open Q&A session -- Guided practical exercises and examples -- Feedback session diff --git a/docs/1-installing-copilot.md b/docs/1-installing-copilot.md deleted file mode 100644 index 8526dd4..0000000 --- a/docs/1-installing-copilot.md +++ /dev/null @@ -1,109 +0,0 @@ -# Lesson 1: Installing and Configuring GitHub Copilot - -## Overview - -**Goal:** -In this lab, you will learn how to install: - -- GitHub Copilot extensions in Visual Studio Code -- GitHub Copilot CLI - -**Estimated Duration:** -10-15 minutes - -**Audience:** - Developers, QA testers, DevOps engineers, and Technical Writers. - -**Prerequisites:** - -- Installed Visual Studio Code, -- GitHub Copilot license. - -### Step 1: Install GitHub Copilot in VS Code - -First, we will install GitHub Copilot and its corresponding Visual Studio Code extensions. - -1. Open Visual Studio Code. -2. Open Extensions view by clicking the Extensions icon in the Activity Bar -3. Enter 'GitHub Copilot' in the Search Extensions in Marketplace. -4. Click Install button on the **GitHub Copilot** extension by GitHub. - Wait until GitHub Copilot is installed. - It will also install **GitHub Copilot Chat** by GitHub extension. -5. Next, we will install the following Copilot Extensions that are needed for the remainder of the training: - - **Install GitHub Copilot for Azure** - - While in the Extensions view, search for **GitHub Copilot for Azure** by Microsoft. - - Click the **Install** button. - - With this extension, you can ask **@azure** questions about Azure services and receive assistance with Azure-specific tasks. - - **Install VS Code Speech (Optional)** - - In the **Search Extensions in Marketplace** field, type **Voice**. - - Locate the **VS Code Speech** extension by Microsoft and click the **Install** button. - - This extension adds speech-to-text and text-to-speech functionality, allowing you to interact with GitHub Copilot using natural language speech. - -**Expected Outcome:** -Both GitHub Copilot and GitHub Copilot Chat extensions are installed in Visual Studio Code. - ---- - -## Install GitHub Copilot CLI - -**Description:** -You will install GitHub CLI (_gh_) and GitHub Copilot extension in Windows using _winget_. -For Windows _gh_ is available via [WinGet](https://github.com/microsoft/winget-cli), [scoop](https://scoop.sh/), [Chocolatey](https://chocolatey.org/), [Conda](https://github.com/cli/cli?tab=readme-ov-file#conda), [Webi](https://github.com/cli/cli?tab=readme-ov-file#webi), and as downloadable MSI. -For other platforms, see the instructions [here](https://github.com/cli/cli?tab=readme-ov-file#installation). - -**Instructions:** - -1. Open Visual Studio Code shell (Ctrl-`). -2. In the Visual Studio Code terminal window, enter the following command: - - `winget install --id GitHub.cli` - -3. Once GitHub CLI installation completes, login to GitHub: - - `gh auth login` - -4. When prompted, select the following options by using the arrows keys and pressing Enter and follow the instructions: - - - Where do you use GitHub? **GitHub.com** - - What is your preferred protocol for Git operations on this host? **HTTPS** - - Authenticate Git with your GitHub credentials? **Yes** - - How would you like to authenticate GitHub CLI? **Login with a web browser** - - First copy your one-time code: _XXXX-XXXX_ - - Press Enter to open in your browser... - - (this will navigate the default browser to the GitHub Device Activation page to authenticate and give necessary authorizations) - -5. In the Visual Studio Code terminal window, enter the following command: - - `gh extension install github/gh-copilot` - -**Expected Outcome:** -GitHub CLI and GitHub Copilot extension are installed and connected to your GitHub account. ---- - -## Verification - -1. Test the installed Copilot extension to GitHub CLI. - - `gh copilot --help` - -2. Read the response and try suggested examples. - ---- - -## Troubleshooting - -- **Issue 1:** Unable to authenticate with GitHub. - **Solution:** Ensure you have GitHub account and you have entered correct credentials. - ---- - -## Conclusion - -**Summary:** -You have learned how to install GitHub Copilot in Visual Studio Code and GitHub CLI and have it installed on your computer. - -**Next Steps:** - -- [Lesson 2: Development with GitHub Copilot](2-development-with-copilot.md) - ---- diff --git a/docs/2-development-with-copilot.md b/docs/2-development-with-copilot.md deleted file mode 100644 index 4e61c3b..0000000 --- a/docs/2-development-with-copilot.md +++ /dev/null @@ -1,188 +0,0 @@ -# Lesson 2: Development with GitHub Copilot - -## Notes (@skyarkitekten - fix this @mikeholdorf) - -- fork -- clone -- add your lab partner as collaborator -- can watch and do later, can drive with me - -## Overview - -**Goal:** -In this lab, you will explore and utilize various GitHub Copilot features, including autocomplete, inline chat, chat view, Copilot CLI, and Copilot Edits (preview), to enhance the **Ordering.Domain**. You will add a method to calculate order totals, incorporating discounts and taxes. Additionally, you will learn how to use Copilot to: - -- Generate new code and improve existing code. -- Understand code efficiently using chat-based interactions. -- Streamline shell command creation with Copilot CLI. -- Refactor and optimize code using Copilot Edit. - -By the end of this lab, you will have enhanced the `OrderAggregate` with a comprehensive `GetTotal` method and gained practical experience with Copilot's full suite of features. - -**Estimated Duration:** -30-45 minutes - -**Audience:** - Developers, QA testers, DevOps engineers, and Technical Writers. - -**Prerequisites:** -To successfully complete this lab, ensure you have the following: - -- **Access to GitHub Copilot:** You must have an active GitHub Copilot subscription. -- **Visual Studio Code (VS Code):** Installed and set up for development. -- **GitHub Copilot Extension for VS Code:** Installed and properly configured in VS Code. -- **GitHub CLI (gh):** Installed on your local machine. -- **GitHub Copilot CLI Extension:** Installed and connected to your GitHub account via GitHub CLI. - -> **Note:** If you have not completed any of the above steps, please refer to - [Lesson 1: Installing and Configuring GitHub Copilot](docs/1-installing-copilot.md) for detailed instructions. - -## Autocomplete with Code Completions (Ghost Text mode) - -GitHub Copilot's autocomplete feature provides intelligent code suggestions as you type, helping you write code faster and with fewer errors. In this step, we will explore how to use autocomplete effectively. - -1. Open the file `samples/Orders/Orders.Domain/Order.cs` in Visual Studio Code. -2. Hit enter a few times and see the autocomplete suggestion. It likely suggests a method signature similar to the following: - - ```csharp - public void RemoveOrderItem(OrderItem item) - ``` - -3. Press the Tab key to accept the suggestion. -4. Chat completions are nice for rapid prototyping, but they can be a bit of a distraction for most developers. You can disable it by going to status bar, clicking on the GitHub Copilot icon, and unchecking code completions. - -## Inline Chat - -Now, let’s start by enhancing the OrderingService by leveraging inline chat feature. Our task is to add a method for calculating order totals, considering discounts and taxes. - -1. In Visual Studio Code, navigate to the `samples/eshop/src/Ordering.Domain/AggregatesModel/OrderAggregate` folder. Open the `Order` class file for modification. -2. Before we add a method named CalculatedTotal that includes taxes, we need to rename existing GetTotal method to GetSubTotal - Use GitHub Copilot to rename the `GetTotal` method to `GetSubTotal`. - - - Scroll to the bottom of the Order class and select `GetTotal` public member - - Open inline Co-pilot Prompt with (`Ctrl+I`), and enter the following prompt: - - ```plaintext - Rename symbol `GetTotal` to `GetSubTotal` - ``` - -3. In the inline chat, enter the following prompt: - - ```plaintext - Add a new public member named GetTotal that is calculated as sum of order items and a new public property of type decimal named taxes and subtracts the sum of items in a public property of type List named Discounts - ``` - -4. Now lets update the logic for discount to use the Discount property of `OrderItem` class. - - - In the inline chat, enter the following prompt: - - ```plaintext - Add an empty public member named GetOrderDiscounts that returns a decimal - ``` - - - Click Accept of the inline chat to close the window. - - delete the return statement - - Observe "ghost text" suggested by GitHub Copilot. - - You can accept by pressing Tab key, or accept part word by word by using Ctrl-Arrow keys. Press Tab to accept the code. - - Open Inline chat again (`Ctrl+I`), enter the following prompt - - ```plaintext - Update `GetOrderDiscounts` method to use use `Discount` property in OrderItem - ``` - - - This would replace the logic to calculate the order discount by adding up the discounts on each line item - - Finally, in the `GetTotal` method, if you delete the value assigned to `totalDiscounts` variable, you should see the new `GetOrderDiscounts` method. Enter Tab to accept changes - -## Copilot Chat - Ask - -GitHub Copilot Chat provides an interactive way to engage with Copilot, offering more space and deeper ways to interact with your code. With the Chat View, you can ask natural language questions about the current context in your editor, request code suggestions, and optimize your code with Copilot's help. - -1. Open the Copilot Chat pane in Visual Studio Code by clicking the Copilot icon in the menu bar or using the keyboard shortcut (`Ctrl+Alt+I`). In the chat settings, ensure the model is set to GPT 4o. If it’s not already selected, switch the model to GPT 4o in the Copilot settings. - >**Note**: We will use this model for this lesson. In later lessons we will experiment with utilizing other models for various tasks such as testing and documentation. -2. In the Chat View prompt, you can use natural language to ask about current context (files open in the editor) - - - For example, browse to `src/Ordering.API/Apis` folder and open 'OrdersApi.cs' file. - - Enter "how does this service work?" and press Enter. - - Observe the GitHub Copilot response. - - Alternatively, you can just enter one of the / commands, like /explain to get similar result. - - You can continue dialog within the same context. -  For example, enter "how can I optimize logging in this code?" and press Enter. - - Observe the suggested code changes. - -3. (Optional) You can copy parts of or the whole suggested code and paste in the editor manually, or click appropriate icon above the code view to Insert at Cursor, Apply in Editor, or just copy to clipboard. -4. (Optional) Save the file. - -**Additional Notes** -For more details on these, see [Getting started with prompts for Copilot Chat](https://docs.github.com/en/copilot/using-github-copilot/guides-on-using-github-copilot/getting-started-with-prompts-for-copilot-chat) - -## Copilot Chat - Edit - -Using inline chat is very helpful in make quick updates on the go as you're working thru a user story or resolving a bug. However, you might've encountered some challenges with context especially when changes involve large code changes. - -In this section we will repeat the previous steps using Copilot Edits to showcase how we could accomplish the same results more efficiently. - -1. Undo all changes made to `Order` class -2. Open chat by clicking on the Copilot button at the bottom right hand side corner, this will open up a Copilot menu, select GitHub Copilot chat -3. Select the Copilot Edits button (see below) at the top left corner of the chat window: - ![copilot edit](./images/copilot-edits.png) -4. Click `+Add Files` and select Order.cs to add the `Order` class to the context -5. Now add the following prompt to refactor the `GetTotal` public member to include taxes and discounts in calculation of order total - -```plaintext -Refactor the GetTotal method to include taxes and discounts. get the taxes from a new public property named Taxes and the discounts is sum of discount property for each `OrderItem` in the order -``` - -You will notice that instead of generating code snippets like `Inline Chat`, copilot edits parses the entire Order class and modifies the logic accordingly. - -## Copilot Chat - Agent - -(This section needs to be updated to reflect the latest changes in Copilot Agent) - -## Copilot CLI - -GitHub Copilot CLI offers an efficient way to interact with Copilot directly from the command line. With Copilot CLI, you can generate shell commands and automate tasks with ease, making it a powerful tool for developers who prefer working in a terminal environment. - -1. In VS Code, press Ctrl+\` to open a new terminal if necessary. -2. In the terminal window enter the following command: - - ```bash - gh copilot --help - ``` - -3. Observe the command usage help and try some of the commands, e.g.: - - ```bash - gh copilot suggest "list commits in the last 3 days" - ``` - - When prompted: - ? What kind of command can I help you with? - > generic shell command -  gh command -  git command - press Enter to select generic shell command - - When prompted: - ? Select an option (Use arrows to move, type to filter) - > Copy command to clipboard -  Explain command -  Execute command -  Revise command -  Rate response -  Exit - - You can select any of the listed options using up-down arrow keys and pressing Enter. - Try few different options and select Exit to end. - -## Summary - -You have learned basics of several major features of GitHub Copilot, including: - -- Autocomplete -- Inline Chat -- Copilot Chat -- Copilot Edits -- Copilot CLI - -**Next Steps:** - -- [Lesson 3: Testing with GitHub Copilot](3-testing-with-copilot.md) diff --git a/docs/3-testing-with-copilot.md b/docs/3-testing-with-copilot.md deleted file mode 100644 index 4b5defd..0000000 --- a/docs/3-testing-with-copilot.md +++ /dev/null @@ -1,161 +0,0 @@ - -# Hands-On Lab: Testing with GitHub Copilot - -## Overview - -**Goal:** -By the end of this session, you will learn how you can use GitHub Copilot Edits (preview) and other features to enhance test projects by creating new unit and integration test cases. -You will create unit tests for the BasketItem class in the Basket.UnitTests, as well as cover additional scenario in Catalog.FunctionalTests projects. - -By the end of this lab, you will have enhanced two test projects in the solution and gained practical experience with Copilot Edits (preview) and other features. - -**Estimated Duration:** -30-45 minutes - -**Audience:** -Developers, QA testers, DevOps engineers, and Technical Writers. - -**Prerequisites:** - -- Completed [Lesson 1: Installing and Configuring GitHub Copilot](docs/lesson1.md), or -- Access to GitHub Copilot (subscription), -- Visual Studio Code, -- GitHub Copilot extension to VS Code. - ---- - -## Table of Contents - -1. [Lab Objectives](#lab-objectives) -2. [Environment Setup](#environment-setup) -3. [Walkthrough](#walkthrough) - - [Step 1: Generating Unit Tests](#step-1-generating-unit-tests) - - [Step 2: Working with Integration Tests](#step-2-working-with-integration-tests) -4. [Troubleshooting](#troubleshooting) -5. [Conclusion](#conclusion) - ---- - -## Lab Objectives - -- Understand how to leverage GitHub Copilot to generate and execute unit and integration tests. -- Learn how incremental test improvements can be achieved. - ---- - -## Environment Setup - -### Tools and Resources - -- GitHub Copilot enabled on your GitHub account. -- Visual Studio Code with GitHub Copilot extension installed. -- Code repository for the lab: [GitHub Copilot](https://github.com/neudesic/learning-github-copilot). - -### Steps to Prepare - -1. Clone the repository: - - ```bash - git clone https://github.com/neudesic/learning-github-copilot.git - cd learning-github-copilot/samples/eShop - ``` - -2. Open the project in your IDE: - - ```bash - code . - ``` - ---- - -## Walkthrough - -### Step 1: Generating Unit Tests - -**Description:** -In this step, you will create a new unit tests class with some simple test cases for BasketItem class. - -**Instructions:** - - 1. In VS Code, open file **src/Basket.API/Model/BasketItem.cs** - 2. Open Copilot Edits by pressing Ctrl-Shift-I. - 3. Verify **BasketItem.cs** file is included in the Working Set, and click + add **Basket.UnitTests.csproj** file. - 4. Ensure model **o1-mini (Preview)** is selected (right corner of the Copilot Edits command bar down below). - 5. Enter following request `Generate unit tests for BasketItem methods and save in the Basket.UnitTests.csproj. Include edge cases.` and press Enter. -In a few moments, unit tests code will be generated in a new file **BasketItemTests.cs** that will show as unsaved file in the editor.. - 6. Observe the generated code. - 7. (Optional) If you find that further improvements can be made, you can ask Copilot Edits and iterate prompting until desired qualities are met. - 8. Click **Accept** to accept the generated code. - 9. Save the file. (If asked to confirm you want to save code generated by GitHub Copilot, confirm). -10. At the Terminal prompt navigate to the Basket.UnitTests project and run the tests: - - ```plaintext - cd tests/Basket.UnitTests - dotnet test - ``` - -11. Click **Done** to complete Copilot Edits session. - -**Expected Outcome:** -The existing and new unit tests should pass. If some test cases fail, you can use GitHub Copilot to fix the code or the failing unit tests. - -**NOTE**: Generated code may not be correct as GitHub Copilot context grasp is limited and may use incorrect assumptions. In general, it will correctly identify the testing framework for the project. -Use your skills and GitHub Copilot to resolve the issue by varying your prompts and focus in an iterative manner. If you need to clear context and reset focus by using `/clear` command. - ---- - -### Step 2: Working with Integration Tests - -**Description:** -In this step, you will work with GitHub Copilot to add a test case to the existing Catalog.API integration tests suite. - -**Instructions:** - -1. In VS Code, open file **tests/Catalog.FunctionalTests/CatalogApiTests.cs** -2. Open Copilot Edits by pressing Ctrl-Shift-I. -3. Verify **CatalogApiTests.cs** file is included in the Working Set -4. Ensure model **o1-mini (Preview)** is selected (right corner of the Copilot Edits command bar down below). -5. In the **CatalogApiTests.cs** file, scroll down and select test case *GetCatalogItemWithId()*. -6. In Copilot Edits prompt field, enter `Consider selected test case. It expects record with the given id. Can you make a test case where no item is returned with the given id?` and press Enter. -In a few moments, the requested tests case will be generated in **CatalogApiTests.cs**. -7. Observe the generated code. The changes are highlighted. -8. (Optional) You may further ask about reasoning why no items will be returned, which can lead into discussion on where 400 or 404 is more appropriate! -9. Click **Accept** to accept the generated code. -10. Save the changes made to the file. -11. At the Terminal prompt navigate to the Catalog.FunctionalTests project and run the tests: - - ```plaintext - cd tests/Catalog.FunctionalTests - dotnet test - ``` - -12. Click **Done** to complete Copilot Edits session. - -**Expected Outcome:** -The existing and new test case should pass. This being integration tests, it may take a few moments for test run to complete. If some test cases fail, you can use GitHub Copilot to fix the code or the failing unit tests. - -**NOTE**: Generated code may not be correct as GitHub Copilot context grasp is limited and may use incorrect assumptions. In general, it will correctly identify the testing framework for the project. -Use your skills and GitHub Copilot to resolve the issue by varying your prompts and focus in an iterative manner. - -**NOTE**: Generated code may not be correct as GitHub Copilot context grasp is limited and may use incorrect assumptions. In general, it will correctly identify the testing framework and mocking library for the project. -Use your skills and GitHub Copilot to resolve the issue by varying your prompts and focus in an iterative manner. - ---- - -## Troubleshooting - -- **Suggested command/script fails:** GitHub Copilot is great, but is not perfect. - **Solution:** Use your skills and GitHub Copilot to help resolve the issue in an iterative manner. This is extra challenge and learning opportunity! - ---- - -## Conclusion - -**Summary:** -In this lab you should have learned and gained practical experience with Copilot Edits (preview) and other features you can use with different types of testing cases. - -**Next Steps:** - -- [Lesson 4: Creating Documentation with GitHub Copilot](4-creating-documentation-with-copilot.md) - ---- diff --git a/docs/4-creating-documentation-with-copilot.md b/docs/4-creating-documentation-with-copilot.md deleted file mode 100644 index 6843573..0000000 --- a/docs/4-creating-documentation-with-copilot.md +++ /dev/null @@ -1,133 +0,0 @@ -# Lesson 4: Creating Documentation with Copilot - -## Overview - -**Goal:** -In this lab, you will learn how to use GitHub Copilot to create and refine documentation for the **OrderingService**. You will begin by generating code-level comments using Copilot Chat and refining them with Copilot Edits. Then, you will generate project-level documentation using the **Ordering.API** project as a reference. Finally, you will learn to write API documentation for the `api/orders` resource by creating and integrating OpenAPI specifications in YAML and JSON formats. - -By the end of this lab, you will have gained the following skills: - -- Generating and refining code-level comments using Copilot Chat and Edits. -- Creating project-level documentation to describe functionality and architecture. -- Writing API documentation with OpenAPI specifications for clear and structured communication. - -**Estimated Duration:** -30-45 minutes - -**Audience:** - Developers, QA testers, DevOps engineers, and Technical Writers. - -**Prerequisites:** -To successfully complete this lab, ensure you have the following: - -- **Access to GitHub Copilot:** You must have an active GitHub Copilot subscription. -- **Visual Studio Code (VS Code):** Installed and set up for development. -- **GitHub Copilot Extension for VS Code:** Installed and properly configured in VS Code. -- **GitHub CLI (gh):** Installed on your local machine. -- **GitHub Copilot CLI Extension:** Installed and connected to your GitHub account via GitHub CLI. - -> **Note:** If you have not completed any of the above steps, please refer to - [Lesson 1: Installing and Configuring GitHub Copilot](docs/1-installing-copilot.md) for detailed instructions. - -## Code Documentation - -In this step, we will use Copilot Chat add comments for all public members in the `Order.cs` class. - -1. In Visual Studio Code, navigate to the `samples/eshop/src/Ordering.Domain/AggregatesModel/OrderAggregate` folder. Open the `Order` class to add `/* */` comments -2. Open the Copilot Chat pane in Visual Studio Code by clicking the Copilot icon in the sidebar or using the command palette (`Ctrl+Alt+I`). In the chat settings, ensure the model is set to `o1-mini (Preview)`. If it’s not already selected, switch the model to `o1-mini (Preview)` in the Copilot settings. -3. Observe that Order.cs file is already in the Working Set. (1 file). You can manage the Working Set by adding and removing files. -4. In the prompt field, enter the following prompt: - - ```plaintext - add /* */ comments to all public members - ``` - -5. GitHub Copilot generates inline comments for every public member. -6. You might notice that the generated comments are incomplete, with a `// ...existing code...` comment at the end of the class. -7. In order to resolve this issue, click on the edit Icon at the end of the generated response in the Copilot Chat. In the Command dropdown select the last one request. -8. Copilot Edits the `Order.cs` and adds comments on all the public members in that class. -9. Review comments generated by Copilot. -10. Now lets say we wanted XML Comments, Click **Discard** button and enter the following prompt in copilot edits: - - ```plaintext - add xml comments on all public members - ``` - -*Additional Notes* - When it comes to adding comments always leverage the power of Copilot Edits that helps set a large context across multiple files. - -## Project Documentation - -In this step, we will use Copilot Edits to generate project documentation. - -1. In Visual Studio Code, navigate to the `samples/eshop/src/Ordering.API` folder. -2. Open the Copilot Chat pane in Visual Studio Code by clicking the Copilot icon in the sidebar or using the command palette (`Ctrl+Alt+I`). In the chat settings, ensure the model is set to `o1-mini (Preview)`. If it’s not already selected, switch the model to `o1-mini (Preview)` in the Copilot settings. -3. Generate a readme markdown file for the project by entering the following prompt: - - ```plaintext - Generate technical documentation for the`Ordering.API` project that can be used by business analysts to understand the scope of the project. Save it as Ordering.API.md file - ``` - -4. GitHub Copilot confirms the plan and creates requested documentation to the Ordering.API.md file. -5. Observe the content of the file. You can open the file preview by pressing `Ctrl-Shift-V`. -6. Make modification to the markdown file by using the following prompt: - - ```plaintext - add dependencies - ``` - -7. GitHub Copilot confirms the plan and adds dependencies under Technical Overview. -8. Click **Accept** button and save the file. - -**Additional Notes** It is important to review and ensure documentation generated is accurate and made according to the responsible AI principles. e.g. [Responsible AI with GitHub Copilot](https://learn.microsoft.com/en-us/training/modules/responsible-ai-with-github-copilot/). Perform due diligence when generating documentation using Copilot and always perform peer review before publishing content. - -## API Documentation - -In this step, we will use Copilot Edits to generate API documentation for `api/orders` resource. - -1. In Visual Studio Code, navigate to the `samples/eshop/src/Ordering.API` folder. -2. Open the Copilot Edits pane in Visual Studio Code by clicking the Copilot icon in the sidebar or using the command palette (`Ctrl+Shift+I`). In the chat settings, Lets select `o1-preview` model for API documentation. -3. Open `Orders.Api.cs` and `Ordering.API.csproj` files. -Generate a readme markdown file for the project by entering the following prompt: - - ```plaintext - Generate API documentation for api/orders resource using OpenAPI Specification in YAML format and save it in Ordering.API.YAML file - ``` - -4. GitHub Copilot confirms the plan and creates requested documentation to the Ordering.API.md file. -5. Observe the content of the file. -6. Sometimes copilot generates more methods and other times it might not generate all the methods for the API. Add the following prompt to explicitly specify the number of methods in the API: - - ```plaintext - generate the documentation to only the 7 methods specified in `OrdersApi.cs` - ``` - -7. Review and Confirm the documentation is created accurately. -8. Click **Accept** button and save the file. -9. Now lets generate the same content in JSON format. We can do so by by specifying the format and the file name as follows: - - ```plaintext - Generate API documentation for the 7 methods in api/orders resource using OpenAPI Specification in JSON format and save it in Ordering.API.JSON file - ``` - -10. Observe the content of the file. - -### Explore Further - -Take your learning to the next level with these optional exercises: - -1. Try asking Copilot Edits to generate API documentation in HTML. -2. Next ask copilot to generate sample code in multiple languages (C#, Java, Go, python, etc.) to consume these APIs. Make sure the code snippets are generated for all methods in the resource. - -## Troubleshooting - -- **Generated response is truncated or looks incomplete:** LLM models have finite set of tokens to use which includes current scope and previous prompts. - **Solution:** You may try selecting a different model, or try clearing previous context (if it is irrelevant to the current prompt). Use /clear command. - -## Summary - -In this lab, you explored how to use GitHub Copilot to create high-quality documentation at different levels. You started by generating code-level comments using Copilot Chat and refining them with Copilot Edits to improve clarity and accuracy. Next, you learned to produce comprehensive project documentation using the **Ordering.API** project, capturing its functionality and architecture. Finally, you focused on writing API documentation for the `api/orders` resource by creating OpenAPI specifications in YAML and JSON formats and integrating them into the project. - -This lab provided hands-on experience in leveraging GitHub Copilot to streamline the documentation process, making it easier to document code, projects, and APIs efficiently and effectively. - -**Next Steps:** - -- [Lesson 5: DevOps with GitHub Copilot](5-devops-with-copilot.md) diff --git a/docs/5-devops-with-copilot.md b/docs/5-devops-with-copilot.md deleted file mode 100644 index 5d8d9a5..0000000 --- a/docs/5-devops-with-copilot.md +++ /dev/null @@ -1,198 +0,0 @@ -# Lesson 5: DevOps with GitHub Copilot - -## Overview - -**Goal:** -In this lab, you will learn how to leverage GitHub Copilot for DevOps tasks in the **OrderingService**. Specifically, you will: - -- Create a parameterized GitHub Actions build pipeline for CI -- Develop a deployment workflow for Azure App Service -- Set up Docker containerization with: - - Dockerfile creation - - Container image build and publish workflow -- Implement Kubernetes deployment with: - - K8s manifest generation - - AKS deployment workflow - - Monitoring and rollback configurations - -By the end of this lab, you will have hands-on experience using GitHub Copilot to create end-to-end CI/CD pipelines, containerization, and cloud deployment workflows. - -**Estimated Duration:** -45-60 minutes - -**Audience:** -DevOps Engineers, Platform Engineers, and Developers interested in DevOps practices. - -**Prerequisites:** -To successfully complete this lab, ensure you have the following: - -- **Access to GitHub Copilot:** You must have an active GitHub Copilot subscription. -- **Visual Studio Code (VS Code):** Installed and set up for development. -- **GitHub Copilot Extension for VS Code:** Installed and properly configured in VS Code. -- **GitHub CLI (gh):** Installed on your local machine. -- **GitHub Copilot CLI Extension:** Installed and connected to your GitHub account via GitHub CLI. - -> **Note:** If you have not completed any of the above steps, please refer to - [Lesson 1: Installing and Configuring GitHub Copilot](docs/1-installing-copilot.md) for detailed instructions. - -## Create a Build Pipeline - -In this step we will generate a build pipeline for the `OrdersApi` using GitHub Actions. - -1. In Visual Studio Code, navigate to the `samples/eshop/src/Ordering.API` folder. -2. Open the Copilot Edits pane in Visual Studio Code by clicking the Copilot icon in the sidebar or using the command palette (`Ctrl+Shift+I`). In the chat settings, let's select `Claude 3.5 Sonnet (Preview)` model for API documentation. -3. Open `Orders.Api.cs` and `Ordering.API.csproj` files. -4. Next we will develop a GitHub action that will create a YAML file to generate a build for Orders API. Enter the following prompt in the chat: - - ```plaintext - add a github action to generate build for Ordering.API and name it `ordering-api-build.yml` and place it under eshop/.github/workflows folder - ``` - -5. Observe the content of the file. -6. This looks good but we would like to parameterize some of the elements in this pipeline (for example: the artifact location). -Use the following prompt to parameterize the GitHub Action: - - ``` plaintext - parameterize the workflow so it takes inputs such as the branch name to build, the build configuration (e.g., Debug or Release). Use on: workflow_dispatch to accept these inputs and include steps for checking out the code, running the build using a script, and uploading the build artifacts. The workflow should use environment variables derived from the inputs to control the build process - ``` - -7. Observe the changes made in YAML. -8. Add a prompt to add any other missing parameters. - -## Create a Deployment pipeline - -**Description:** -In this step we will generate a deployment pipeline for the `OrdersApi` using GitHub Actions. - -1. In Visual Studio Code, navigate to the `samples/eshop/src/Ordering.API` folder. -2. Open the Copilot Edits pane in Visual Studio Code using the command palette (`Ctrl+Shift+I`). Keep the `Claude 3.5 Sonnet (Preview)` model selected. -3. Open `Orders.Api.cs` and `Ordering.API.csproj` files. -4. Let's create a deployment workflow that will deploy our API to Azure App Service. Enter the following prompt in the chat: - - ```plaintext - create a github action named ordering-api-deploy.yml that deploys Ordering.API to Azure App Service. Include parameters for environment selection (dev/staging/prod), Azure credentials, and App Service name. Add necessary secrets handling - ``` - -5. Observe the content of the workflow file. -6. Let's parameterize the deployment workflow to make it more flexible. Use this prompt: - - ```plaintext - modify the deployment workflow to include parameters for: - - Azure region - - Service plan tier - - Application settings - - Deployment slots - Add environment-specific configurations and approval gates for production - ``` - -7. (Optional) Add any additional security measures with this prompt: - - ```plaintext - enhance the deployment workflow with: - - Security scanning steps - - SSL certificate handling - - IP restrictions - ``` - -**Additional Notes:** Always review the generated workflows carefully and ensure all sensitive information is properly handled through GitHub Secrets. - -## Create a pipeline to Build a Container Image - -In the previous steps, we've developed workflows to create a build and deploy that build to a Azure App Service. In this step we will create a docker image for `Ordering.API` project. In the next step we will deploy it to Azure Kubernetes Services. - -1. In Visual Studio Code, navigate to the `samples/eshop/src/Ordering.API` folder. -2. Open the Copilot Edits pane in Visual Studio Code using the command palette (`Ctrl+Shift+I`). Keep the `Claude 3.5 Sonnet (Preview)` model selected. -3. Open `Orders.Api.cs` and `Ordering.API.csproj` files. -4. Next, create a Dockerfile for the project using the following prompt: - - ```plaintext - Create a dockerfile for Ordering.API.csproj and add docker support for the project. Save the dockerfile in the project folder - ``` - -5. Confirm that Copilot created a Dockerfile, a .dockerignore file. -6. Next create a build Image pipeline that creates an image using the Dockerfile and publishes the image to Azure Container Registry. Check to make sure the project file and the docker files are still in the Copilot Edits' working set. Enter the following in Copilot Edits' chat: - - ```plaintext - generate a github action workflow to build the docker image for Ordering.API using the Dockerfile and publish the image to Azure container registry. Parameterize the workflow file for all azure inputs and build configurations. save it in a file named ordering-api-build-image.yml under eshop/.github/workflows folder - ``` - -7. Review the generated workflow file. - -## Deploy image to an AKS Cluster - -In this step we will deploy the `Ordering.API` image to AKS cluster - -1. In Visual Studio Code, ensure you're still in the `samples/eshop/src/Ordering.API` folder. -2. We'll need to create Kubernetes deployment manifests first. Enter this prompt in the Copilot chat: - - ```plaintext - create kubernetes deployment and service manifests for Ordering.API. Include configuration for horizontal pod autoscaling, resource limits, and health probes. Save them in a k8s folder under the project - ``` - -3. Review the generated Kubernetes manifests in the `k8s` folder. -4. Now, let's create a deployment workflow. Use this prompt in Copilot: - - ```plaintext - create a github action workflow named ordering-api-deploy-aks.yml that deploys the Ordering.API container to AKS. Include: - - Parameters for AKS cluster name, resource group, and namespace - - Kubernetes manifest application - - Helm chart support (optional) - - Configuration for different environments - - Health checks after deployment - ``` - -5. The workflow should now appear in the `.github/workflows` folder. Review it carefully. -6. To add monitoring and rollback capabilities, use this prompt: - - ```plaintext - enhance the AKS deployment workflow with: - - Deployment health monitoring - - Automatic rollback on failure - - Integration with Azure Monitor - ``` - -## Additional Notes - -- **Review Generated Code**: Always review Copilot-generated code for accuracy and relevance. -- **Test Locally**: Test workflows and scripts in a local or staging environment before deploying. -- **Validate YAML Syntax**: Use a YAML validator to check for syntax errors in GitHub Actions workflows. -- **Parameterize Configurations**: Use environment variables and input parameters for flexibility. -- **Security Considerations**: Store secrets securely using GitHub Secrets; avoid hardcoding credentials. -- **Continuous Improvement**: Regularly review and update workflows based on feedback and best practices. -- **Error Handling**: Implement error handling to manage failures gracefully. - -## Introduction to GitHub Copilot for Azure - -In the previous steps, we've worked directly with GitHub Copilot to create a build and deploy that build to a Azure App Service. In this part of the lab we will explore how GitHub Copilot for Azure Chat Assistant can help. - -1. In Visual Studio Code, navigate to the `samples/eshop` folder. -2. Open the Copilot Chat using the command shortcut (`Ctrl+Alt+I`). Keep the `Claude 3.5 Sonnet (Preview)` model selected. -3. In Chat view, type `@azure how can you help with Azure?` or similar prompt and press Enter. - GitHub will response with a sample list of the services it can help with. -4. Next, type `@azure which subscriptions I can use?` and press Enter. - The assistant will reply with available subscriptions, if any. -5. Next, type `@azure can you create the files I need to deploy this solution?` and press Enter. - The Assistant will suggest requested files, such as .bicep file for AKS cluster and manifests for deployments and services, as well as the steps to deploy them. -6. Review the files and suggested steps. - -## Summary - -You have learned how to leverage GitHub Copilot for various DevOps tasks, including: - -- Creating parameterized CI/CD pipelines with GitHub Actions - - Build pipeline configuration - - Deployment workflow setup - - Environment-specific configurations -- Containerization workflows - - Dockerfile generation - - Container image build and publish - - Container registry integration -- Kubernetes deployment - - Manifest generation - - AKS deployment configuration - - Monitoring and rollback setup - -Throughout this lab, you've gained practical experience in using GitHub Copilot to streamline DevOps processes - -**Next Steps:** - -- [Lesson 6: Best practices with GitHub Copilot](6-best-practices-with-copilot) diff --git a/docs/6-best-practices-with-copilot.md b/docs/6-best-practices-with-copilot.md deleted file mode 100644 index d96990c..0000000 --- a/docs/6-best-practices-with-copilot.md +++ /dev/null @@ -1,58 +0,0 @@ -# Best Practices for Using GitHub Copilot - -## Provide the Right Context - -When you use GitHub Copilot, it's important to provide the right context for the code you want to generate. - -1. Provide a clear description of the problem you are trying to solve in the prompt. - -2. Include samples of the input and output data when possible. This is called [Few-Shot Learning](https://en.wikipedia.org/wiki/Few-shot_learning). - -3. Open relevant files in your workspace to give GitHub Copilot more context. Copilot uses the open files in your workspace to generate suggestions. - -4. Use top-level file comments to provide additional context for the code you want to generate. - - > Note: here are two examples of top-level comments that you can use file. This was very common in C++ and older languages, but is less common in modern languages. What is old is new again! - - ```csharp - //######################################################################## - // .cs - // - // Description: This file contains the implementation of the `Foo` class. - //######################################################################## - ``` - - ```javascript - //######################################################################## - // .js - // - // Description: Handles user authentication and session management - //######################################################################## - ``` - -5. Include the appropriate imports and references in your code. This helps GitHub Copilot understand the dependencies of your code. - -6. Follow good coding practices. This includes writing clean, readable code with descriptive variable and class names. GitHub Copilot tries to generate code that follows the same style as the code you provide- so the better your code, the better the suggestions. - -## Using GitHub Copilot Chat and Edits - -1. Make heavy use of the chat participants and slash commands to guide Copilot in the right direction. - -## Custom Instructions to GitHub Copilot - -1. Different models at different times can generate code that does not take every details in your solution into account, depending on the current context and model capacity. - - For example, open the file **src/Basket.API/Grpc/BasketService.cs** and press `Ctrl-Alt-I` to open Chat view and ask to `create unit tests for this class` and observe the generated code. - In most cases, for .NET solutions, it will generate code using `Moq` library for mocking dependencies. - -2. You can create custom instructions file to automatically add information to all questions you ask Copilot. - To this end, create a file **.github/copilot-instructions.md** and add instructions like `When mocking dependencies in tests, use NSubstitute.`. - Close the file and retry the question. Observe two things: - - GitHub Copilot used two references, the file in focus and the custom instructions file. - - Dependencies are mocked using `NSubstitute` library. - -3. Since custom instructions consume model tokens, you should try to write [effective instructions](https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot#writing-effective-custom-instructions). - -4. in VS Code, you can temporary switch custom instructions on and off by using the Setting editor (shortcut `Ctrl + ,` (Linux/Windows) / `Command + ,` (Mac). - - Type `instruction file` in the search box and select or clear the **Use Instruction Files** checkbox. diff --git a/docs/7-hot-keys-and-commands.md b/docs/7-hot-keys-and-commands.md deleted file mode 100644 index 2b6a02c..0000000 --- a/docs/7-hot-keys-and-commands.md +++ /dev/null @@ -1,32 +0,0 @@ -# Common Hot Keys and Commands for GitHub Copilot - -> Note: These are work in progress and have not been validated. Validate then remove this note. - -## General Commands - -| Windows | Mac | Purpose | -| ------------------ | ----------------- | ------------------------ | -| `Ctrl + I` | `Cmd + I` | Open inline chat | -| `Ctrl + Shift + I` | `Cmd + Shift + I` | Open chat view | -| `Tab` | `Tab` | Accept suggestion | -| `Esc` | `Esc` | Dismiss suggestion | -| `Alt + [` | `Option + [` | Show previous suggestion | -| `Alt + ]` | `Option + ]` | Show next suggestion | - -## Chat Participants - -| Participant | Purpose | -| ------------ | ---------------------------------- | -| `@workspace` | References files in your workspace | -| `@vscode` | Ask about VS Code features | -| `@terminal` | Ask about terminal commands | - -## Slash Commands - -| Command | Purpose | -| ---------- | -------------------------- | -| `/help` | Show available commands | -| `/tests` | Generate unit tests | -| `/fix` | Propose fixes for problems | -| `/explain` | Explain selected code | -| `/doc` | Generate documentation | diff --git a/docs/8-using-github-copilot-agent.md b/docs/8-using-github-copilot-agent.md deleted file mode 100644 index 6c07783..0000000 --- a/docs/8-using-github-copilot-agent.md +++ /dev/null @@ -1,420 +0,0 @@ -# Lesson 8: Using GitHub Copilot Agent - -> **Availability Note:** GitHub Copilot Agent is currently available in Visual Studio Code Insiders as part of the nightly build. In the future, it may be included as a standard feature in Visual Studio Code. The features and interface described in this document may evolve as GitHub Copilot Agent moves toward general availability. - -GitHub Copilot Agent takes AI assistance to the next level by enabling deeper and more interactive collaboration on your development projects. This guide will walk you through using GitHub Copilot Agent to create an ASP.NET Core Web API with MediatR pattern implementation. - -## What is GitHub Copilot Agent? - -Copilot Agent is an AI-powered assistant that can help you: - -- Generate and implement entire features -- Create project structures and scaffolding -- Install and configure packages -- Implement architecture patterns -- Create tests - -## Creating a C# ASP.NET Core Web API Project - -Let's start by using GitHub Copilot Agent to create a new ASP.NET Core Web API project. - -### Step 1: Launch GitHub Copilot Agent - -To use GitHub Copilot Agent: - -1. Open GitHub Copilot Edits by pressing `Ctrl+Shift+I` (Windows/Linux) or `Cmd+Shift+I` (Mac) -![Copilot Edits](images/open-copilot-edits.png) -2. In the Copilot Edits panel, click the dropdown menu in the prompt box -![Copilot Agent](images/copilot-edit-agent.png) -3. Select "Agent" from the dropdown options - -Now you're using Copilot Agent mode, which provides more interactive capabilities than the standard Copilot Chat. - -Ask the Agent: - -```plaintext -Create an ASP.NET Core WebAPI project in the samples/Agent directory. Name the project 'Agent.Api'. Use dotnet 8.0. -``` - -### Step 2: Follow the agent's instructions to create the project - -Copilot will suggest using the .NET CLI to create the project. - -### Step 3: Explore the project structure - -Ask Copilot to explain the generated project structure: - -```plaintext -Can you explain what files were created and their purposes? -``` - -Copilot will explain the key files in an ASP.NET Core Web API project: - -The following are samples. (Your results may vary.) - -- **Program.cs**: The entry point for the application -- **appsettings.json**: Configuration settings -- **Controllers/WeatherForecastController.cs**: Sample API controller -- **WeatherForecast.cs**: Sample model class -- **Properties/launchSettings.json**: Debug and launch configurations - -## Installing and Setting Up MediatR - -MediatR is a popular library for implementing the mediator pattern in .NET applications. Let's use Copilot Agent to add it to our project. - -### Step 1: Ask Copilot Agent to add MediatR - -Ask Copilot Chat: - -```plaintext -I want to add MediatR to Agent.Api. Help me install the necessary packages and set it up. -``` - -Copilot Agent will suggest installing the MediatR packages and will offer to run the commands for you. It will typically suggest: - -```bash -dotnet add package MediatR -dotnet add package MediatR.Extensions.Microsoft.DependencyInjection -``` - -Allow Copilot to run these commands for you by responding with "Yes, please run these commands." - -### Step 2: Ask Copilot to help configure MediatR - -After the packages are installed, ask Copilot: - -```plaintext -Help me configure MediatR in Program.cs. -``` - -Copilot will analyze your Program.cs file and suggest the necessary code changes to register MediatR services, such as: - -```csharp -using MediatR; - -// ...existing code... - -// Add MediatR services -builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining()); - -// ...existing code... -``` - -Allow Copilot to make these changes for you or follow its guidance to update the file manually. - -## Implementing a Query and Handler with MediatR - -Now let's create a simple query and handler pattern using MediatR with Copilot's help. - -### Step 1: Ask Copilot to create a folder structure - -Ask Copilot: - -```plaintext -I want to implement CQRS with MediatR. Help me create the necessary folders and files. -``` - -Copilot will suggest creating the following folder structure: - -- Queries -- Commands -- Handlers -- Models - -It may offer to run the commands to create these folders for you. - -### Step 2: Ask Copilot to implement a query - -Ask Copilot: - -```plaintext -Create a WeatherForecastQuery class that uses MediatR. -``` - -Copilot will help you create a file named `Queries/WeatherForecastQuery.cs` with content similar to: - -```csharp -using MediatR; -using Agent.API.Models; - -namespace Agent.API.Queries -{ - public class WeatherForecastQuery : IRequest> - { - // Query parameters can go here if needed - } -} -``` - -### Step 3: Ask Copilot to create a response model - -Ask Copilot: - -```plaintext -Can you create a WeatherForecastResponse model class? -``` - -Copilot will help you create a file named `Models/WeatherForecastResponse.cs` with content similar to: - -```csharp -namespace Agent.API.Models -{ - public class WeatherForecastResponse - { - public DateOnly Date { get; set; } - public int TemperatureC { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - public string? Summary { get; set; } - } -} -``` - -### Step 4: Ask Copilot to create a query handler - -Ask Copilot: - -```plaintext -Can you create a handler for the WeatherForecastQuery? -``` - -Copilot will help you create a file named `Handlers/WeatherForecastQueryHandler.cs` with content similar to: - -```csharp -using MediatR; -using Agent.API.Models; -using Agent.API.Queries; - -namespace Agent.API.Handlers -{ - public class WeatherForecastQueryHandler : IRequestHandler> - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - public Task> Handle(WeatherForecastQuery request, CancellationToken cancellationToken) - { - var forecast = Enumerable.Range(1, 5).Select(index => - new WeatherForecastResponse - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - - return Task.FromResult>(forecast); - } - } -} -``` - -### Step 5: Ask Copilot to create a controller - -Ask Copilot: - -```plaintext -Can you create an AgentController that uses the WeatherForecastQuery with MediatR? -``` - -Copilot will help you create a file named `Controllers/AgentController.cs` with content similar to: - -```csharp -using MediatR; -using Microsoft.AspNetCore.Mvc; -using Agent.API.Models; -using Agent.API.Queries; - -namespace Agent.API.Controllers -{ - [ApiController] - [Route("[controller]")] - public class AgentController : ControllerBase - { - private readonly IMediator _mediator; - private readonly ILogger _logger; - - public AgentController(IMediator mediator, ILogger logger) - { - _mediator = mediator; - _logger = logger; - } - - [HttpGet("weather")] - public async Task>> GetWeatherForecast() - { - _logger.LogInformation("Retrieving weather forecast via MediatR"); - var query = new WeatherForecastQuery(); - var result = await _mediator.Send(query); - return Ok(result); - } - } -} -``` - -## Testing Your API - -Ask Copilot: - -```plaintext -How can I run and test my API? -``` - -Copilot will suggest running your API with: - -```bash -dotnet run -``` - -And will explain how to access: - -- Swagger UI: [https://localhost:YOUR_PROJECT_SPECIFIC_PORT/swagger/index.html](https://localhost:YOUR_PROJECT_SPECIFIC_PORT/swagger/index.html) (The agent might show a different path. prompt to fix the path - "The swagger path in the url is incorrect. Give me the correct path to open Swagger") -- Direct API endpoint: [https://localhost:YOUR_PROJECT_SPECIFIC_PORT/agent/weather](https://localhost:YOUR_PROJECT_SPECIFIC_PORT/agent/weather) - -It might also suggest using tools like Postman or curl to test your endpoint. - -## Best Practices When Working with Copilot Agent - -1. **Be specific in your requests** - The more details you provide, the more accurate the responses will be -2. **Break down complex tasks** - Ask for help with one component at a time -3. **Ask for explanations** - When Copilot generates code, ask it to explain how it works -4. **Iteratively refine** - If the first suggestion isn't perfect, provide feedback and ask for improvements - -## Advanced Agent Techniques - -### Code Refactoring - -```plaintext -Can you help me refactor this controller to follow best practices? -``` - -### Generating Tests - -```plaintext -Can you generate unit tests for my query handler? -``` - -### Documentation Generation - -```plaintext -Can you generate XML documentation for my API endpoints? -``` - -## Extending the Project with More Features - -Once you have the basic project set up, you can ask Copilot Agent to help you add more features: - -1. **Add a Command Pattern**: - - ```plaintext - Can you create a CreateWeatherForecastCommand and handler? - ``` - -2. **Implement Validation**: - - ```plaintext - Can you add FluentValidation to validate my commands? - ``` - -3. **Add Authentication**: - - ```plaintext - How do I add JWT authentication to my API? - ``` - -4. **Implement a Repository Pattern**: - - ```plaintext - Can you help me implement a repository pattern for data access? - ``` - -## Source Control with GitHub - -GitHub Copilot Agent can help you manage source control operations efficiently. Here are some example prompts you can use to ask Copilot Agent to handle common GitHub operations: - -### Branch Management - -1. **Create a new branch:** - - ```plaintext - Create a new branch called 'feature/add-weather-validation' from main for implementing request validation in the weather forecast API - ``` - -2. **Check your current branch and status:** - - ```plaintext - Show me my current branch and what files are modified - ``` - -3. **Switch between branches:** - - ```plaintext - Switch to the main branch and then back to my feature branch - ``` - -### Committing Code - -1. **Stage and commit specific files:** - - ```plaintext - Stage and commit the changes to the Controllers and Validators folders with the message "Add validation to weather forecast endpoints" - ``` - -2. **Generate a meaningful commit message:** - - ```plaintext - Help me write a descriptive commit message for my changes to the MediatR handlers - ``` - -3. **Stage and commit all changes:** - - ```plaintext - Stage all my changes and commit them with a message describing what I've accomplished - ``` - -### Pushing and Pull Requests - -1. **Push your branch:** - - ```plaintext - Push my feature/add-weather-validation branch to the remote repository - ``` - -2. **Create a pull request description:** - - ```plaintext - Generate a pull request description for my feature/add-weather-validation branch that explains the validation features I added - ``` - -3. **Check the status before pushing:** - - ```plaintext - Run through a pre-push checklist: verify tests pass and check for uncommitted changes - ``` - -### Complete Workflow Examples - -1. **Full feature development workflow:** - - ```plaintext - I finished implementing the weather validation feature. Help me create a new branch called 'feature/add-weather-validation', commit my changes with a descriptive message, and push to the remote repository - ``` - -2. **Issue-based workflow:** - - ```plaintext - I'm working on GitHub issue #42 to add pagination. Create a branch called 'feature/add-pagination', help me commit my changes, and push to GitHub - ``` - -3. **Prepare for code review:** - - ```plaintext - I need to submit my code for review. Help me ensure all changes are committed to my 'feature/user-preferences' branch and push it to GitHub - ``` - -## Conclusion - -GitHub Copilot Agent dramatically accelerates your development workflow by providing interactive assistance throughout your development process. By leveraging its capabilities, you can focus more on business logic and less on boilerplate code and configuration. - -Remember that while Copilot Agent provides valuable assistance, always review the code it generates to ensure it meets your specific requirements and security standards. diff --git a/docs/image.png b/docs/image.png deleted file mode 100644 index 6cdc1d8..0000000 Binary files a/docs/image.png and /dev/null differ diff --git a/docs/images/copilot-edit-agent.png b/docs/images/copilot-edit-agent.png deleted file mode 100644 index 9ae0e8b..0000000 Binary files a/docs/images/copilot-edit-agent.png and /dev/null differ diff --git a/docs/images/copilot-edits.png b/docs/images/copilot-edits.png deleted file mode 100644 index 86a61eb..0000000 Binary files a/docs/images/copilot-edits.png and /dev/null differ diff --git a/docs/images/open-copilot-edits.png b/docs/images/open-copilot-edits.png deleted file mode 100644 index cc9f453..0000000 Binary files a/docs/images/open-copilot-edits.png and /dev/null differ diff --git a/lessons/.DS_Store b/lessons/.DS_Store new file mode 100644 index 0000000..0fe6e09 Binary files /dev/null and b/lessons/.DS_Store differ diff --git a/lessons/01-Installation-and-Setup/01-installing-copilot.md b/lessons/01-Installation-and-Setup/01-installing-copilot.md new file mode 100644 index 0000000..c4aef2a --- /dev/null +++ b/lessons/01-Installation-and-Setup/01-installing-copilot.md @@ -0,0 +1,322 @@ +# πŸš€ Lesson 1: Installing and Configuring GitHub Copilot + +--- + +## πŸ“ Overview + +**Goal:** + +> Learn how to install and configure GitHub Copilot in Visual Studio Code and Visual Studio 2022. + +**Estimated Duration:** 10-15 minutes +**Audience:** Developers, QA testers, DevOps engineers, Technical Writers +**Prerequisites:** + +- Visual Studio Code (version 1.101.1 or higher) and/or Visual Studio 2022 installed +- GitHub Copilot license +- Active internet connection for updates and authentication + +--- + +## πŸ› οΈ How to Install GitHub Copilot in VS Code + +### πŸ”„ Update Visual Studio Code (If Already Installed) + +If you already have VS Code installed, ensure you're running the latest version: + +1. **Open Visual Studio Code.** +2. Go to **Help** > **Check for Updates** (or **Code** > **Check for Updates** on macOS). +3. If an update is available, click **Download Update** and restart VS Code. +4. Alternatively, you can check your version by going to **Help** > **About** and compare it with the latest version on the [VS Code website](https://code.visualstudio.com/). + +### πŸ“¦ Install GitHub Copilot Extension + +1. **Open Visual Studio Code.** +2. Go to the **Extensions** view by clicking the square icon in the sidebar or pressing `Ctrl+Shift+X`. +3. Search for **GitHub Copilot**. +4. Click **Install** on the GitHub Copilot extension by GitHub. +5. After installation, sign in with your GitHub account when prompted. + +### βž• Add Your GitHub Account to VS Code + +1. Open the **Command Palette** (`Ctrl+Shift+P`). +2. Type and select **"GitHub: Sign in"**. +3. Follow the prompts to sign in with your GitHub account. +4. Authorize VS Code to access your GitHub Copilot subscription. + +> ℹ️ [Install GitHub Copilot in VS Code – Official Docs](https://docs.github.com/en/copilot/getting-started-with-github-copilot/getting-started-with-github-copilot-in-visual-studio-code) + +## πŸ’¬ How to Open Copilot Chat in VS Code + +To access Copilot Chat in Visual Studio Code: + +1. **Method 1:** Click the **Chat** icon in the Activity Bar (left sidebar). +2. **Method 2:** Open the **Command Palette** (`Ctrl+Shift+P`) and type **"GitHub Copilot: Open Chat"**. +3. **Method 3:** Use the keyboard shortcut `Ctrl+Shift+I` (or `Cmd+Shift+I` on macOS). + +The Copilot Chat panel will open, where you can: +- Ask questions about your code +- Request code explanations +- Get help with debugging +- Generate code snippets + +> ℹ️ [GitHub Copilot Chat in VS Code – Official Docs](https://code.visualstudio.com/docs/copilot/copilot-chat) + +## πŸ› οΈ How to Install GitHub Copilot in Visual Studio 2022 + +### πŸ”„ Update Visual Studio 2022 (If Already Installed) + +If you already have Visual Studio 2022 installed, ensure you're running the latest version: + +1. **Open Visual Studio Installer** (search for it in the Start menu). +2. If Visual Studio Installer needs an update, it will prompt you to update it first. +3. Find your Visual Studio 2022 installation and click **Update** if available. +4. Wait for the update to complete and restart Visual Studio if prompted. +5. Alternatively, within Visual Studio 2022, go to **Help** > **Check for Updates**. + +> ⚠️ **Important:** For the best GitHub Copilot experience, use the latest version of Visual Studio 2022. + +### πŸ“¦ Install GitHub Copilot Extension + +1. **Open Visual Studio Installer.** +2. Click **Modify** for your Visual Studio 2022 installation. +3. In the **Workloads** tab, select any workload (e.g., **ASP.NET and web development**). +4. Under **Optional components**, check **GitHub Copilot**. +5. Click **Modify** to install the tool. + +### βž• Add Your GitHub Account to Visual Studio + +1. Open Visual Studio 2022. +2. Go to **File** > **Account Settings**. +3. Click **Add an account**. +4. Select **GitHub** and sign in. +5. Authorize Visual Studio to access your GitHub account. + +> ℹ️ [Install and Manage GitHub Copilot in Visual Studio](https://learn.microsoft.com/en-us/visualstudio/ide/visual-studio-github-copilot-install-and-states?view=vs-2022) +> ℹ️ [Add your GitHub account to Visual Studio](https://learn.microsoft.com/en-us/visualstudio/ide/work-with-github-accounts?view=vs-2022#add-a-github-account-from-the-account-settings-dialog) + +--- + +## πŸ’¬ How to Open Copilot Chat in Visual Studio 2022 + +To open the Copilot chat: + +1. Click the Copilot icon in the top right corner of Visual Studio. +2. Select **Open Chat Window**. + +> ℹ️ [Visual Studio GitHub Copilot Chat Documentation](https://learn.microsoft.com/en-us/visualstudio/ide/visual-studio-github-copilot-chat?view=vs-2022) + +![Open Copilot Chat](./images/open-gh-cp-chat.png) + +The Copilot chat window will appear on the right side. The header includes: + +- **Chat thread dropdown:** View chat history. +- **Create new thread button:** Start a new chat thread. +- **Edit thread button:** Edit the current chat. +- **Delete thread button:** Remove the current thread. + +![Copilot Chat Window Header](./images/gh-cp-header.png) + +--- + +## βœ… Verification and Testing + +### πŸ” Verify Installation Success + +After installation, verify that GitHub Copilot is working correctly: + +**For VS Code:** +1. Open any code file (e.g., `.js`, `.py`, `.cs`) +2. Start typing a function or comment +3. You should see Copilot suggestions appear in gray text +4. Press `Tab` to accept a suggestion +5. Open Copilot Chat and ask: "Hello, are you working?" + +**For Visual Studio 2022:** +1. Open or create a new C# file +2. Start typing a method or class +3. Look for Copilot suggestions appearing as you type +4. Open Copilot Chat and verify it responds to queries +5. Check that the Copilot icon shows as active (not grayed out) + +### 🚨 Quick Test Commands + +Try these test prompts in Copilot Chat to ensure everything is working: + +```text +// Test basic functionality +Write a hello world function in C# + +// Test workspace awareness (if you have a project open) +@workspace What programming language is this project using? + +// Test code explanation +/explain [select any piece of code and run this command] +``` + +--- + +## πŸ”§ Troubleshooting Common Issues + +### ❌ Copilot Not Showing Suggestions + +**Possible Causes & Solutions:** + +1. **Extension Not Enabled:** + - VS Code: Check Extensions panel, ensure GitHub Copilot is enabled + - Visual Studio: Go to Extensions > Manage Extensions, verify Copilot is installed and enabled + +2. **Not Signed In:** + - Verify you're signed into GitHub with a Copilot-enabled account + - Check account status in the bottom status bar + +3. **Network/Firewall Issues:** + - Ensure internet connectivity + - Check corporate firewall settings allow GitHub domains + - Try disabling VPN temporarily + +4. **File Type Not Supported:** + - Copilot works with most programming languages + - Try creating a `.cs`, `.js`, or `.py` file for testing + +### ❌ "Copilot Disabled" or Grayed Out Icon + +**Solutions:** +1. **License Check:** Verify your GitHub account has an active Copilot subscription +2. **Org Policies:** Check if your organization has disabled Copilot for your repositories +3. **Repository Settings:** Some repositories may have Copilot disabled via `.copilotignore` files + +### ❌ Chat Not Responding + +**Solutions:** +1. **Restart IDE:** Close and reopen VS Code or Visual Studio +2. **Clear Chat:** Start a new chat thread +3. **Check Network:** Ensure stable internet connection +4. **Update Extension:** Check for Copilot extension updates + +--- + +## 🏒 Enterprise and Team Considerations + +### πŸ”’ Security and Compliance + +**For Enterprise Environments:** + +1. **Data Privacy Settings:** + - Review GitHub Copilot's data usage policies + - Configure appropriate settings for proprietary code + - Consider GitHub Copilot Business for enhanced privacy controls + +2. **Organization Policies:** + - Work with IT to ensure GitHub domains are allowlisted + - Understand your organization's AI tool usage policies + - Configure appropriate access controls for team members + +3. **Code Review Processes:** + - Establish guidelines for reviewing Copilot-generated code + - Train team members on validating AI suggestions + - Implement code quality checks for AI-assisted development + +### πŸ‘₯ Team Setup Best Practices + +1. **Consistent Configuration:** + - Ensure all team members use the same Copilot version + - Share workspace settings and preferences + - Document team coding standards for Copilot usage + +2. **Training and Onboarding:** + - Provide Copilot training for new team members + - Establish best practices for prompt engineering + - Share effective usage patterns across the team + +--- + +## βš™οΈ Advanced Configuration + +### πŸŽ›οΈ Customizing Copilot Settings + +**VS Code Settings:** +1. Open Settings (`Ctrl+,`) +2. Search for "Copilot" +3. Configure preferences such as: + - Enable/disable suggestions for specific languages + - Adjust suggestion delay + - Configure chat model preferences + +**Visual Studio Settings:** +1. Go to **Tools** > **Options** +2. Navigate to **IntelliCode** > **GitHub Copilot** +3. Adjust settings for: + - Suggestion behavior + - Chat preferences + - Model selection + +### πŸ”§ Workspace-Specific Settings + +Create a `.vscode/settings.json` file in your project root: + +```json +{ + "github.copilot.enable": { + "*": true, + "yaml": false, + "plaintext": false + }, + "github.copilot.advanced": { + "length": 500, + "temperature": 0.1 + } +} +``` + +--- + +## πŸš€ Performance Optimization + +### ⚑ Improving Response Times + +1. **Network Optimization:** + - Use stable, high-speed internet connection + - Consider proximity to GitHub servers for better latency + +2. **IDE Performance:** + - Close unnecessary extensions + - Ensure adequate system resources (RAM, CPU) + - Keep IDE updated to latest version + +3. **Usage Patterns:** + - Be specific in your prompts for faster, more relevant responses + - Use context-specific commands (@workspace, /explain, etc.) + - Break complex requests into smaller, focused prompts + +--- + +## πŸ“Š Monitoring and Analytics + +### πŸ“ˆ Usage Tracking + +**GitHub Copilot provides usage analytics:** + +1. **Personal Usage:** + - Visit [github.com/settings/copilot](https://github.com/settings/copilot) + - Review suggestion acceptance rates + - Monitor language usage patterns + +2. **Organization Analytics:** + - Organization owners can view team usage statistics + - Track adoption rates across development teams + - Monitor productivity improvements + +--- + +## βœ… Next Steps + +You are now ready to use GitHub Copilot in your favorite IDE! Explore Copilot's features to boost your productivity. + +**Recommended Next Actions:** +1. **Complete the Getting Started Labs:** Explore @workspace, Ask, Edit, and Agent features +2. **Configure Team Settings:** Set up consistent configurations for your development team +3. **Practice Effective Prompting:** Learn best practices for communicating with Copilot +4. **Integrate with Workflow:** Incorporate Copilot into your daily development routine + +--- diff --git a/lessons/01-Installation-and-Setup/images/gh-cp-header.png b/lessons/01-Installation-and-Setup/images/gh-cp-header.png new file mode 100644 index 0000000..5e1abbf Binary files /dev/null and b/lessons/01-Installation-and-Setup/images/gh-cp-header.png differ diff --git a/lessons/01-Installation-and-Setup/images/open-gh-cp-chat.png b/lessons/01-Installation-and-Setup/images/open-gh-cp-chat.png new file mode 100644 index 0000000..4b5300a Binary files /dev/null and b/lessons/01-Installation-and-Setup/images/open-gh-cp-chat.png differ diff --git a/lessons/02-Getting-Started/.DS_Store b/lessons/02-Getting-Started/.DS_Store new file mode 100644 index 0000000..bdfb9cc Binary files /dev/null and b/lessons/02-Getting-Started/.DS_Store differ diff --git a/lessons/02-Getting-Started/c#/.DS_Store b/lessons/02-Getting-Started/c#/.DS_Store new file mode 100644 index 0000000..246b32e Binary files /dev/null and b/lessons/02-Getting-Started/c#/.DS_Store differ diff --git a/lessons/02-Getting-Started/c#/2.0-exploring-workspace.md b/lessons/02-Getting-Started/c#/2.0-exploring-workspace.md new file mode 100644 index 0000000..f3e3d38 --- /dev/null +++ b/lessons/02-Getting-Started/c#/2.0-exploring-workspace.md @@ -0,0 +1,505 @@ +# πŸ§ͺ Lab: Exploring @workspace Command in Visual Studio 2022 + +--- + +## πŸ“ Overview + +**Goal:** +Master the `@workspace` command in GitHub Copilot to understand, navigate, and enhance your entire .NET project using Visual Studio 2022. Learn how to leverage context-aware AI assistance that understands your complete project structure. + +**Estimated Duration:** 20-25 minutes + +**Audience:** +.NET developers, Software Architects, Team Leads, and Technical Writers working with Visual Studio projects. + +**Prerequisites:** +- Visual Studio 2022 (version 17.6 or later) +- GitHub Copilot extension enabled in Visual Studio +- Access to GitHub Copilot Chat (requires a Copilot subscription) +- Access to a .NET Core Web API sample project or any existing Visual Studio solution +- Basic familiarity with Visual Studio IDE and project structure + +--- + +## 🎯 What is @workspace? + +The `@workspace` command is GitHub Copilot's most powerful feature for understanding your entire project context. Unlike regular chat prompts that work with individual files, `@workspace` enables Copilot to: + +- **Analyze your entire solution structure** - including all projects, dependencies, and configurations +- **Understand relationships between files** - how controllers connect to services, models, and data access layers +- **Maintain coding patterns and conventions** - consistent with your existing codebase architecture +- **Suggest project-wide improvements** - identifying cross-cutting concerns and architectural opportunities + +**Key Benefits:** +- πŸ” **Project-wide understanding** - Copilot sees the big picture of your solution +- 🎯 **Context-aware responses** - Suggestions align with your project's patterns and structure +- πŸš€ **Architectural guidance** - Get insights about design patterns and project organization +- πŸ“‹ **Comprehensive analysis** - Review dependencies, configurations, and project health + +--- + +## 🧭 Lab Steps + +### 1️⃣ Launch Copilot Chat and Explore Project Structure + +**Open Visual Studio 2022 with your solution loaded** + +1. **Launch Copilot Chat:** + - Click the Copilot icon in the top-right corner of Visual Studio + - Select **Open Chat Window** from the dropdown menu + - Alternatively, use the keyboard shortcut `Ctrl + \, C` + +2. **Explore your project structure:** + +```text +@workspace, Please analyze my solution structure and provide an overview of the projects, their purposes, and how they relate to each other. +``` + +**What you'll observe:** +- Copilot will scan all projects in your solution +- You'll receive a detailed breakdown of each project's role +- Dependencies and relationships between projects will be highlighted +- Architectural patterns (if any) will be identified + +--- + +### 2️⃣ Understand Project Dependencies and Architecture + +**Analyze project dependencies and architectural patterns:** + +```text +@workspace, What architectural patterns are being used in this solution? Analyze the dependency flow between projects and identify any potential improvements or concerns. +``` + +**Follow up with specific questions:** + +```text +@workspace, Show me all the NuGet packages used across the solution and explain their purposes. Are there any outdated packages or security vulnerabilities I should be aware of? +``` + +**What you'll learn:** +- Complete dependency mapping of your solution +- Identification of architectural patterns (MVC, Clean Architecture, etc.) +- NuGet package analysis and recommendations +- Potential architectural improvements + +--- + +### 3️⃣ Explore Code Patterns and Conventions + +**Analyze coding patterns across your project:** + +```text +@workspace, What coding patterns and conventions are consistently used throughout this codebase? Identify any inconsistencies or areas where patterns could be improved. +``` + +**Ask about specific patterns:** + +```text +@workspace, How is error handling implemented across different layers of this application? Show me examples and suggest improvements if needed. +``` + +**Benefits:** +- Understand your team's coding standards +- Identify inconsistencies in implementation +- Get suggestions for better pattern adherence +- Learn about cross-cutting concerns + +--- + +### 3.5️⃣ **COMPREHENSIVE Best Practices and Coding Standards Analysis** + +**This is a critical section for establishing and maintaining code quality across your entire solution.** + +#### πŸ” **Identify Current Coding Standards** + +**Analyze existing coding conventions:** + +```text +@workspace, Analyze this solution and identify the coding standards and conventions currently being followed: + +1. Naming conventions (classes, methods, variables, constants) +2. Code organization patterns (folder structure, namespace organization) +3. Architecture patterns (MVC, Clean Architecture, Repository, etc.) +4. Dependency injection patterns and service registration approaches +5. Error handling and logging strategies +6. Data access patterns and Entity Framework usage +7. API design patterns (REST conventions, response formatting) +8. Authentication and authorization patterns + +Provide specific examples of each pattern and rate the consistency across the codebase. +``` + +#### πŸ“Š **Standards Compliance Assessment** + +**Check adherence to industry best practices:** + +```text +@workspace, Evaluate this .NET solution against Microsoft's official coding standards and .NET best practices: + +1. C# Coding Conventions compliance +2. .NET Framework Design Guidelines adherence +3. ASP.NET Core best practices implementation +4. Entity Framework Core best practices +5. Security best practices (OWASP guidelines) +6. Performance best practices +7. Testing best practices + +For each area, provide: +- Current compliance level (Good/Needs Improvement/Poor) +- Specific examples of violations +- Actionable recommendations for improvement +``` + +#### πŸ—οΈ **Architecture Pattern Analysis** + +**Deep dive into architectural consistency:** + +```text +@workspace, Analyze the architectural patterns in this solution: + +1. Is there a consistent architectural pattern (Clean Architecture, Onion, Layered, etc.)? +2. How are cross-cutting concerns handled (logging, validation, caching)? +3. Are SOLID principles being followed? Provide specific examples of violations. +4. How is separation of concerns implemented across layers? +5. What design patterns are used and are they applied consistently? +6. How is dependency management structured? + +Suggest improvements for better architectural consistency. +``` + +#### πŸ”§ **Code Quality Standards** + +**Evaluate code quality metrics:** + +```text +@workspace, Perform a comprehensive code quality analysis: + +1. Method complexity analysis - identify overly complex methods +2. Class cohesion - are classes focused on single responsibilities? +3. Code duplication - identify repeated code patterns that should be refactored +4. Comment quality - are comments helpful and up-to-date? +5. Variable and method naming - are names descriptive and consistent? +6. Magic numbers and strings - identify hardcoded values that should be constants +7. Dead code - identify unused methods, classes, or variables + +Prioritize findings by impact and provide refactoring suggestions. +``` + +#### πŸ›‘οΈ **Security Standards Assessment** + +**Review security implementation patterns:** + +```text +@workspace, Evaluate security best practices implementation: + +1. Input validation patterns - how is user input validated and sanitized? +2. Authentication implementation - is it consistent and secure? +3. Authorization patterns - how are permissions checked? +4. Data protection - how is sensitive data handled? +5. SQL injection prevention - are parameterized queries used consistently? +6. Cross-site scripting (XSS) prevention measures +7. Secrets management - how are API keys and passwords handled? +8. Error handling - does error information leak sensitive data? + +Provide specific security improvement recommendations. +``` + +#### πŸ“‹ **Team Standards Documentation** + +**Generate team coding standards:** + +```text +@workspace, Based on the analysis of this codebase, create a comprehensive coding standards document that includes: + +1. **Naming Conventions Guide** + - Classes, interfaces, methods, properties, variables + - File and folder naming standards + - Namespace organization rules + +2. **Code Structure Standards** + - Method length guidelines + - Class organization patterns + - File organization within projects + +3. **Architecture Guidelines** + - Layer responsibilities and boundaries + - Dependency injection patterns to follow + - Design pattern usage guidelines + +4. **Quality Requirements** + - Code review checklist items + - Performance considerations + - Security requirements + +5. **Documentation Standards** + - XML documentation requirements + - README and technical documentation guidelines + +Format this as a practical team reference guide. +``` + +#### πŸ”„ **Consistency Improvement Recommendations** + +**Identify and fix inconsistencies:** + +```text +@workspace, Focus on consistency improvements across the solution: + +1. **Inconsistent Patterns**: Identify where similar functionality is implemented differently across the codebase +2. **Naming Inconsistencies**: Find variations in naming conventions and suggest standardizations +3. **Error Handling Variations**: Show different approaches to error handling and recommend a unified strategy +4. **Data Access Inconsistencies**: Identify different approaches to database operations and suggest standardization +5. **API Response Patterns**: Analyze API endpoint responses for consistency in structure and format +6. **Validation Approaches**: Review how input validation is handled across different controllers/services + +For each inconsistency, provide: +- Examples of the variations found +- Recommended standard approach +- Specific files that need to be updated +``` + +#### 🎯 **Best Practice Implementation Roadmap** + +**Create an improvement plan:** + +```text +@workspace, Create a prioritized roadmap for implementing best practices improvements: + +**Phase 1 (Critical - Fix First):** +- Security vulnerabilities +- Performance bottlenecks +- Major architectural violations + +**Phase 2 (Important - Fix Soon):** +- Code consistency issues +- Documentation gaps +- Testing coverage improvements + +**Phase 3 (Good to Have - Future Sprint):** +- Code style standardization +- Refactoring opportunities +- Advanced optimization + +For each phase, provide: +- Estimated effort (hours/days) +- Risk level if not addressed +- Specific action items with file names +- Success criteria for completion +``` + +#### πŸ“ˆ **Continuous Improvement Monitoring** + +**Set up ongoing standards monitoring:** + +```text +@workspace, Suggest tools and processes for maintaining coding standards: + +1. **Automated Tools Integration** + - Code analysis tools (SonarQube, CodeMaid, etc.) + - Linting and formatting tools + - Security scanning tools + +2. **Development Workflow Integration** + - Pre-commit hooks for standard checking + - Pull request templates with quality checklist + - Automated code review suggestions + +3. **Team Training Recommendations** + - Areas where team training would be most beneficial + - Specific best practices that need reinforcement + - Knowledge sharing session topics + +4. **Metrics to Track** + - Code quality metrics to monitor + - Standards compliance measurements + - Team adoption indicators +``` + +--- + +### 4️⃣ Project Health and Quality Analysis + +**Perform a comprehensive project health check:** + +```text +@workspace, Perform a health check on this solution. Analyze code quality, identify potential technical debt, security concerns, and suggest modernization opportunities. +``` + +**Ask for specific recommendations:** + +```text +@workspace, What are the top 5 improvements I should make to this codebase to enhance maintainability, performance, and security? +``` + +**What Copilot will analyze:** +- Code quality metrics and patterns +- Security vulnerabilities and best practices +- Performance optimization opportunities +- Modernization suggestions (latest .NET features, etc.) +- Technical debt identification + +--- + +### 5️⃣ Generate Project Documentation + +**Create comprehensive project documentation:** + +```text +@workspace, Generate a README.md file for this solution that includes project overview, architecture explanation, setup instructions, and API documentation if applicable. +``` + +**Generate development guidelines:** + +```text +@workspace, Create a development guidelines document that new team members can follow. Include coding standards, project structure explanation, and common development workflows for this solution. +``` + +**Documentation benefits:** +- Automated documentation generation +- Consistent with actual codebase +- Includes setup and development instructions +- Team onboarding resources + +--- + +### 6️⃣ Advanced Workspace Analysis + +**Perform advanced analysis for enterprise scenarios:** + +```text +@workspace, I need to prepare this solution for enterprise deployment. Analyze configuration management, logging strategies, monitoring capabilities, and deployment readiness. Suggest improvements. +``` + +**Security and compliance review:** + +```text +@workspace, Perform a security review of this solution. Check for common vulnerabilities, authentication/authorization patterns, data protection measures, and compliance with security best practices. +``` + +**Performance analysis:** + +```text +@workspace, Analyze this solution for performance bottlenecks. Look at database queries, API endpoints, caching strategies, and suggest optimizations. +``` + +--- + +## πŸ”§ Visual Studio Integration Tips + +### Using @workspace with Solution Explorer +- **Select specific projects** before asking questions to focus Copilot's analysis +- **Right-click on files/folders** and use "Ask Copilot" for targeted analysis +- **Use with multiple projects** open to get comprehensive cross-project insights + +### Combining with Visual Studio Features +- **IntelliSense integration:** @workspace responses work with VS IntelliSense +- **Error List integration:** Ask Copilot to analyze build errors and warnings +- **Debug mode:** Use @workspace to understand debug sessions and variable states + +### Code Navigation Enhancement +```text +@workspace, Show me all implementations of IRepository pattern in this solution and how they're used across different controllers. +``` + +--- + +## πŸ’‘ Best Practices for @workspace + +### 🎯 Effective Prompting Strategies + +**Be Specific About Scope:** +```text +βœ… Good: @workspace, Analyze the data access layer in the ProductService project +❌ Vague: @workspace, look at my code +``` + +**Ask Progressive Questions:** +```text +1. @workspace, What's the overall architecture? +2. Follow up: How is dependency injection configured? +3. Then: What improvements can be made to the DI container setup? +``` + +**Combine with File Context:** +```text +@workspace, I'm looking at ProductController.cs. How does this controller fit into the overall API design pattern used in this solution? +``` + +### πŸ… **Coding Standards-Specific Best Practices** + +**Start with High-Level Standards Analysis:** +```text +βœ… Effective: @workspace, What are the dominant coding patterns in this solution? Rate the consistency of naming conventions, error handling, and architectural patterns across all projects. + +❌ Too Narrow: @workspace, check if this one method follows naming conventions +``` + +**Use Comparative Analysis for Standards:** +```text +βœ… Powerful: @workspace, Compare how similar functionalities (like CRUD operations) are implemented across different controllers. Are they following consistent patterns? + +βœ… Also Good: @workspace, Analyze how exceptions are handled in the Controllers vs Services vs Repository layers. Is there a consistent approach? +``` + +**Request Specific Standards Documentation:** +```text +βœ… Actionable: @workspace, Extract the implicit coding standards from this codebase and create an explicit team guidelines document that new developers can follow. + +βœ… Team-Focused: @workspace, What would be the top 5 coding standard rules that would most improve consistency in this codebase? +``` + +**Standards Violation Detection:** +```text +βœ… Comprehensive: @workspace, Scan this solution for deviations from Microsoft C# coding conventions. Focus on naming, organization, and design pattern usage. + +βœ… Security-Focused: @workspace, Identify security best practice violations across this codebase and prioritize them by risk level. +``` + +**Pattern Consistency Analysis:** +```text +βœ… Architecture-Level: @workspace, Is dependency injection implemented consistently across all layers? Show examples of both correct usage and violations. + +βœ… API Design: @workspace, Do all API endpoints follow RESTful conventions? Identify inconsistencies in URL patterns, HTTP verbs, and response structures. +``` + +### πŸš€ Advanced Use Cases + +**Code Migration Planning:** +```text +@workspace, I need to migrate this solution from .NET Core 6 to .NET 9. Analyze compatibility issues and create a migration plan. +``` + +**Microservices Decomposition:** +```text +@workspace, Analyze this monolith and suggest how it could be decomposed into microservices. Identify bounded contexts and service boundaries. +``` + +**Testing Strategy:** +```text +@workspace, Evaluate the current testing strategy. What types of tests are missing? Suggest a comprehensive testing approach for this solution. +``` + +**Standards Enforcement Planning:** +```text +@workspace, Create a plan for enforcing coding standards in this solution. What automated tools, code review checklists, and team processes would be most effective? +``` + +**Legacy Code Modernization:** +```text +@workspace, This solution has evolved over several years. Identify outdated patterns, deprecated practices, and opportunities to modernize the codebase while maintaining existing functionality. +``` + +--- + +## πŸ† Advanced Scenarios for Enterprise Development + +### Team Collaboration +```text +@workspace, I'm onboarding a new developer to this project. Create a comprehensive guide that explains the codebase, development setup, and key concepts they need to understand. +``` + +### Code Review Preparation +```text +@workspace, I'm preparing for a code review. Identify potential issues, code smells, and areas that reviewers might flag in this solution. +``` \ No newline at end of file diff --git a/lessons/02-Getting-Started/c#/2.1-exploring-copilot-ask.md b/lessons/02-Getting-Started/c#/2.1-exploring-copilot-ask.md new file mode 100644 index 0000000..d1acc5c --- /dev/null +++ b/lessons/02-Getting-Started/c#/2.1-exploring-copilot-ask.md @@ -0,0 +1,448 @@ +# πŸ§ͺ Lab: Mastering GitHub Copilot Ask for Targeted Development + +--- + +## πŸ“ Overview + +**Goal:** +Master GitHub Copilot Ask for targeted, contextual assistance during active development. Learn how to get precise answers about specific code sections, debug issues in real-time, and enhance your understanding of existing code without modifying it. + +**Estimated Duration:** 15-20 minutes + +**Audience:** +Developers, QA testers, DevOps engineers, and Technical Writers working with Visual Studio 2022. + +**Prerequisites:** +- Visual Studio 2022 (version 17.14.6 or later) +- GitHub Copilot extension enabled in Visual Studio +- Access to GitHub Copilot Chat (requires a Copilot subscription) +- Access to a .NET Core Web API sample project +- Completion of the @workspace exploration lesson (recommended) + +--- + +## 🎯 What Makes Copilot Ask Unique? + +Unlike @workspace which analyzes entire projects, **Copilot Ask** excels at: + +- **Targeted Code Explanations** - Understanding specific methods, classes, or code blocks +- **Real-time Debug Assistance** - Getting help with errors and exceptions as they occur +- **Learning and Documentation** - Educational explanations without code modifications +- **Quick Context Switching** - Rapid answers while staying in your coding flow +- **Code Comprehension** - Understanding unfamiliar or complex code sections + +**Key Benefits:** +- 🎯 **Focused Analysis** - Get answers about specific code without project-wide scanning +- ⚑ **Instant Responses** - Quick help without interrupting your development flow +- πŸ“š **Educational Value** - Learn patterns and concepts through explanations +- πŸ” **Debug Support** - Real-time assistance with errors and exceptions +- πŸ’‘ **Non-Intrusive** - Get guidance without modifying your codebase + +--- + +## 🧭 Lab Steps + +### 1️⃣ **Slash Commands for Quick Code Analysis** + +**Use built-in slash commands for instant, targeted assistance:** + +1. **Open a method or class** in your editor +2. **Select the code you want to analyze** +3. **Use slash commands for specific tasks:** + +**Code Explanation:** +```text +/explain +``` +*Provides detailed explanation of selected code, including logic flow and purpose* + +**Documentation Generation:** +```text +/doc +``` +*Automatically generates XML documentation comments for the selected method or class* + +**Problem Detection and Fixes:** +```text +/fix +``` +*Identifies issues in selected code and proposes specific fixes* + +**Test Generation:** +```text +/tests +``` +*Creates comprehensive unit tests for the selected code with multiple test cases* + +**Performance Optimization:** +```text +/optimize +``` +*Analyzes selected code for performance bottlenecks and suggests improvements* + +**Help and Guidance:** +```text +/help +``` +*Provides information about available Copilot Chat features and commands* + +**Benefits:** +- ⚑ **Instant Results** - Single command for specific tasks +- 🎯 **Focused Output** - Targeted responses for selected code +- πŸ”„ **Consistent Format** - Standardized responses across different code sections +- πŸ“š **Learning Tool** - Understand patterns through explanations + +--- + +### 2️⃣ **Advanced Slash Command Workflows** + +**Combine slash commands with natural language for comprehensive analysis:** + +**Error Resolution Workflow:** +1. **Select problematic code** +2. **Use `/fix` to identify issues:** +```text +/fix +``` +3. **Follow up with `/explain` for understanding:** +```text +/explain why this error occurs and how the fix works +``` + +**Documentation and Testing Workflow:** +1. **Select a completed method** +2. **Generate documentation:** +```text +/doc +``` +3. **Create comprehensive tests:** +```text +/tests +``` +4. **Optimize if needed:** +```text +/optimize +``` + +**Learning from Legacy Code:** +1. **Select unfamiliar code** +2. **Get explanation:** +```text +/explain +``` +3. **Check for improvements:** +```text +/fix +``` +4. **Understand performance:** +```text +/optimize +``` + +**Debugging Integration:** +```text +When stopped at a breakpoint, select the current method and use: +/explain what could cause unexpected behavior at this point +``` + +--- + +### 3️⃣ **Practical Slash Command Examples** + +**Real-world scenarios using slash commands:** + +**Code Review Preparation:** +```text +Select method β†’ /doc β†’ /tests β†’ /optimize +Complete workflow: Document, test, and optimize your code before review +``` + +**Legacy Code Analysis:** +```text +Select complex method β†’ /explain β†’ /fix β†’ /optimize +Understand legacy code, identify issues, and improve performance +``` + +**Bug Hunting:** +```text +Select suspected method β†’ /fix β†’ /explain why this might fail +Identify potential issues and understand failure scenarios +``` + +**Performance Tuning:** +```text +Select slow method β†’ /optimize β†’ /explain the optimization recommendations +Get specific performance improvements with explanations +``` + +**Learning New Patterns:** +```text +Select unfamiliar pattern β†’ /explain β†’ /tests +Understand the pattern and see how to test it properly +``` + +**API Development:** +```text +Select controller action β†’ /doc β†’ /tests β†’ /optimize +Complete API endpoint development workflow +``` + +--- + +### 4️⃣ **Quick Development Assistance** + +**Get rapid help for common development tasks:** + +**Method signature help:** +```text +I need to call this method [select method]. Show me the correct way to call it with proper parameters and error handling. +``` + +**Extension and modification guidance:** +```text +I want to add a new parameter to this method [select method]. What changes would be needed and what impact would it have on callers? +``` + +**Integration questions:** +```text +How can I integrate this existing method [select method] with a new authentication requirement? +``` + +**Testing insights:** +```text +Looking at this method [select method], what unit tests should I write to ensure proper coverage? +``` + +--- + +### 5️⃣ **Code Quality and Best Practices Assessment** + +**Use Ask for targeted quality checks:** + +**Single method analysis:** +```text +Analyze this method [select method] for adherence to SOLID principles and suggest any improvements. +``` + +**Security-focused questions:** +```text +Review this authentication logic [select code] for security vulnerabilities and best practices. +``` + +**Performance analysis:** +```text +Is this database query [select LINQ query] efficient? Suggest optimizations if needed. +``` + +**Maintainability check:** +```text +Rate this code [select code block] for maintainability and readability. What would make it easier to understand and modify? +``` + +--- + +### 6️⃣ **Slash Commands Reference Guide** + +**Complete command reference with use cases:** + +| Command | Purpose | Best Used For | +|---------|---------|---------------| +| `/explain` | Detailed code explanation | Understanding complex logic, learning patterns | +| `/doc` | Generate documentation | XML comments, API documentation | +| `/fix` | Identify and fix issues | Bug hunting, code review prep | +| `/tests` | Create unit tests | Test-driven development, coverage | +| `/optimize` | Performance improvements | Bottleneck analysis, efficiency | +| `/help` | Copilot Chat guidance | Learning available features | + +**Advanced Combinations:** +```text +/fix followed by /explain - Understand both the problem and solution +/doc followed by /tests - Complete method documentation and testing +/optimize followed by /explain - Understand performance improvements +/explain followed by /tests - Learn the code then test it properly +``` + +**Quick Tips:** +- Always **select code first** before using slash commands +- **Combine commands** for comprehensive analysis +- Use `/help` when you need guidance on available features +- **Chain commands** with natural language for deeper understanding + +--- + +## 🎨 Advanced Ask Scenarios + +### πŸ” **Debug Session Integration** + +**During active debugging sessions:** + +1. **Set breakpoints** in problematic methods +2. **When execution stops**, use Ask: + +```text +Based on the current debugger state, what values should I inspect to identify the issue? +``` + +```text +The debugger shows these variable values [describe current state]. What's the most likely cause of the unexpected behavior? +``` + +### πŸ“– **Code Review Preparation** + +**Before submitting code for review:** + +```text +I'm about to submit this method [select method] for code review. What potential concerns might reviewers raise? +``` + +```text +Review this implementation [select code] as if you were a senior developer. What feedback would you provide? +``` + +### πŸ§ͺ **Test-Driven Development Support** + +**When writing tests:** + +```text +I need to test this method [select method]. What test cases should I consider, including edge cases and error conditions? +``` + +```text +This test is failing [select test method]. Based on the test logic, what might be wrong with the implementation? +``` + +--- + +## πŸ’‘ Ask-Specific Best Practices + +### 🎯 **Effective Ask Strategies** + +**Select Before Asking:** +```text +βœ… Good: [Select specific code] + "Explain this algorithm's time complexity" +❌ Vague: "How does my code work?" +``` + +**Be Context-Specific:** +```text +βœ… Good: "This error occurs when I call SaveChanges(). What's wrong?" +❌ Generic: "Why is my code broken?" +``` + +**Ask Progressive Questions:** +```text +1. First: "What does this method do?" +2. Then: "How could this method be improved?" +3. Finally: "What tests should I write for this?" +``` + +**Use Current Context:** +```text +βœ… Effective: "I'm debugging and stopped here [current line]. What should I check next?" +❌ Less useful: "How do I debug this application?" +``` + +### ⚑ **Rapid Development Workflow** + +**Quick Understanding with Slash Commands:** +```text +/explain - Explain the selected code in detail +/doc - Add documentation comment for the selected symbol +/fix - Propose a fix for problems in the selected code +/tests - Create unit tests for the selected code +/optimize - Analyze and improve running time of the selected code +/help - Get help on Copilot chat features +``` + +**Error Resolution:** +```text +1. Copy error message +2. Ask: "What causes this error and how do I fix it?" +3. Get solution +4. Continue coding +``` + +**Learning Flow:** +```text +1. Encounter unfamiliar pattern +2. Select and ask for explanation +3. Ask for best practices +4. Apply learned concepts +``` + +--- + +## 🚨 **Common Ask Scenarios for Enterprise Development** + +### 🏒 **Inherited Codebase Scenarios** + +```text +I just joined this team and I'm looking at this code [select complex method]. Explain the business logic and any gotchas I should know about. +``` + +### πŸ”’ **Security-Critical Code** + +```text +This handles user authentication [select auth code]. Are there any security vulnerabilities or areas for improvement? +``` + +### πŸš€ **Performance-Critical Paths** + +```text +This method is called frequently [select method]. Analyze its performance characteristics and suggest optimizations. +``` + +### πŸ”§ **Integration Challenges** + +```text +I need to integrate this existing method [select method] with a new microservice. What changes would be needed? +``` + +--- + +## ⚠️ **Ask vs. Workspace: When to Use Each** + +### 🎯 **Use Ask When:** +- Understanding specific code sections +- Debugging active issues +- Learning from existing implementations +- Getting quick explanations during development +- Analyzing errors and exceptions +- Understanding method or class behavior + +### 🌍 **Use @workspace When:** +- Analyzing entire project architecture +- Understanding project-wide patterns +- Generating documentation for the whole solution +- Planning major refactoring +- Assessing overall code quality +- Creating team coding standards + +--- + +## βœ… Summary + +By completing this lab, you've learned to: + +- **Use Copilot Ask for targeted code analysis** and understanding specific implementations +- **Get real-time debugging assistance** when encountering errors and exceptions +- **Enhance code comprehension** of unfamiliar or complex code sections +- **Receive quality feedback** on specific methods and classes +- **Integrate Ask into your development workflow** for continuous learning and improvement +- **Distinguish between Ask and @workspace** for optimal assistance + +Copilot Ask transforms your development experience by providing an intelligent coding companion that helps you understand, debug, and improve your code in real-time. + +--- + +## 🎯 Next Steps + +1. **Practice Ask during your daily coding** - Use it for every unfamiliar code section +2. **Integrate with debugging workflow** - Ask questions when hitting breakpoints +3. **Build code review habits** - Use Ask to self-review before submitting code +4. **Enhance learning** - Ask about patterns and practices you encounter +5. **Combine with @workspace** - Use both tools complementarily for maximum benefit + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/02-Getting-Started/c#/2.2-exploring-copilot-agent.md b/lessons/02-Getting-Started/c#/2.2-exploring-copilot-agent.md new file mode 100644 index 0000000..71397a5 --- /dev/null +++ b/lessons/02-Getting-Started/c#/2.2-exploring-copilot-agent.md @@ -0,0 +1,415 @@ +# πŸ§ͺ Lab: Building E-commerce Features with GitHub Copilot Agent + +--- + +## πŸ“ Overview + +**Goal:** +Learn how to leverage GitHub Copilot Agent to autonomously implement complex e-commerce features and architectural changes in an Orders API using Visual Studio 2022. Experience how Agent mode can accelerate development by implementing complete features across multiple files. + +**Estimated Duration:** 25-30 minutes + +**Audience:** +.NET Core Web API developers, E-commerce developers, Backend engineers. + +**Prerequisites:** + +- Visual Studio 2022 installed (17.14.6 or later) +- GitHub Copilot subscription with Agent capabilities enabled +- Access to the Orders API sample project +- Basic familiarity with ASP.NET Core Web API concepts and DDD patterns + +**Project Folder:** +[Orders API Sample Project](../../../../sample-code/Orders/README.md) + +--- + +## 🎯 What Makes Copilot Agent Powerful? + +Unlike Ask which provides guidance, and @workspace which analyzes projects, **Copilot Agent** excels at: + +- **Autonomous Implementation** - Automatically creates and modifies multiple files +- **Cross-Project Changes** - Updates controllers, services, entities, and configurations together +- **Architecture-Aware Development** - Maintains existing patterns and conventions +- **Complete Feature Development** - Implements end-to-end functionality without manual coordination +- **Intelligent File Creation** - Creates new files in appropriate locations with proper structure + +**Key Benefits:** +- πŸš€ **Rapid Feature Development** - Complete features in minutes instead of hours +- πŸ—οΈ **Architecture Consistency** - Maintains existing patterns across all changes +- πŸ“ **Multi-File Coordination** - Updates related files automatically +- πŸ”§ **Configuration Management** - Updates DI containers, routing, and settings +- πŸ§ͺ **Test Generation** - Creates comprehensive test suites alongside implementation + +--- + +## 🧭 Lab Steps + +### 1️⃣ **Setup Copilot Agent Mode** + +**Enable Agent capabilities in Visual Studio 2022:** + +1. **Open Visual Studio 2022** with the Orders.sln solution +2. **Navigate to Copilot Chat** by clicking the Copilot icon in the activity bar +3. **Access Settings** by clicking `Copilot icon > Settings > Options` +4. **Enable Agent Mode** by checking "Enable Agent mode in the chat pane" checkbox + +![Open VS settings](images/VS-copilot-options.png) +![Enable Agent Mode](images/VS-enable-agent-mode.png) + +**Verify Agent Mode:** +- Look for the Agent indicator in the chat interface +- You should see enhanced capabilities for autonomous code generation + +**πŸ“ Agent Mode Capabilities:** +Agent mode provides powerful autonomous development capabilities including: +- **Greater error iteration** - Automatically fixes issues and retries implementation +- **Tool integration** - Access to additional development tools and context +- **Automatic code application** - Directly applies changes without manual intervention +- **Multi-file coordination** - Manages changes across related files automatically + +--- + +### 2️⃣ **Implement Order Status Management Feature** + +**Use Agent to implement a complete order status tracking system:** + +```text +@workspace, I need to implement order status management for this e-commerce Orders API. Please: + +1. Add an OrderStatus enum with values: Pending, Confirmed, Processing, Shipped, Delivered, Cancelled +2. Update the Order entity to include Status property and status transition methods +3. Create an OrderStatusService with business logic for valid status transitions +4. Add new controller endpoints: PATCH /api/orders/{id}/status and GET /api/orders/status/{status} +5. Implement proper validation and error handling for invalid status transitions +6. Update the dependency injection configuration +7. Add appropriate logging for status changes + +Follow the existing DDD patterns and ensure Entity Framework integration. +``` + +**What you'll observe:** +- Agent analyzes the existing Order domain structure +- Creates the OrderStatus enum in the appropriate location +- Updates Order.cs with status property and business methods +- Creates OrderStatusService with validation logic +- Adds new controller actions with proper HTTP verbs and routing +- Updates Program.cs for dependency injection +- Implements comprehensive error handling and logging +- Maintains consistency with existing coding patterns + +**After completion:** +- Build the solution to verify compilation +- Review the generated code for pattern consistency +- Test the new endpoints using the generated .http file + +--- + +### 3️⃣ **Enhance Order Management with Customer Integration** + +**Implement customer management features:** + +```text +@workspace, Enhance the Orders API with customer management capabilities: + +1. Create a Customer entity with properties: Id, Name, Email, Address, Phone, RegistrationDate +2. Update Order entity to include CustomerId (foreign key) instead of CustomerName string +3. Create CustomerService with methods: CreateCustomer, GetCustomer, UpdateCustomer, GetCustomerOrders +4. Add CustomerController with full CRUD operations +5. Create CustomerDto and OrderDto classes for API responses +6. Implement AutoMapper configurations for entity-to-DTO mapping +7. Update OrderController to work with Customer entities +8. Add Entity Framework DbContext configuration for Customer entity +9. Create database migration scripts + +Ensure all changes follow Clean Architecture principles and existing project patterns. +``` + +**What Agent will accomplish:** +- Creates Customer entity with proper relationships +- Refactors Order entity to use Customer foreign key +- Implements CustomerService with business logic +- Creates CustomerController with RESTful endpoints +- Generates DTO classes with appropriate validation attributes +- Sets up AutoMapper profiles for clean separation +- Updates OrderController for Customer integration +- Configures Entity Framework relationships +- Creates migration scripts for database updates + +--- + +### 4️⃣ **Implement Order Analytics and Reporting** + +**Add business intelligence capabilities:** + +```text +@workspace, Implement order analytics and reporting features: + +1. Create OrderAnalyticsService with methods to calculate: + - Total revenue by date range + - Top customers by order value + - Popular products from order items + - Average order value trends + - Order status distribution + +2. Add OrderReportsController with endpoints: + - GET /api/reports/revenue?startDate={date}&endDate={date} + - GET /api/reports/top-customers?limit={number} + - GET /api/reports/order-trends?period={monthly|weekly|daily} + - GET /api/reports/status-summary + +3. Implement caching for expensive analytics queries using IMemoryCache +4. Add query optimization with proper Entity Framework includes +5. Create ReportDto classes for structured responses +6. Add OpenAPI documentation with examples for all endpoints +7. Implement proper error handling and validation + +Focus on performance and scalability for large datasets. +``` + +**Agent capabilities demonstrated:** +- Complex business logic implementation across multiple services +- Performance-optimized Entity Framework queries +- Caching strategy implementation +- Comprehensive API documentation generation +- Structured DTO design for complex data +- Error handling and validation patterns + +--- + +### 5️⃣ **Add Comprehensive Testing Suite** + +**Generate complete test coverage:** + +```text +@workspace, Create a comprehensive testing suite for the Orders API: + +1. Generate xUnit test projects for: + - Orders.Domain.Tests (unit tests for entities and services) + - Orders.Api.Tests (integration tests for controllers) + +2. Create unit tests for: + - Order entity business methods (AddOrderItem, GetTotal, status transitions) + - OrderService business logic and validation + - CustomerService CRUD operations + - OrderAnalyticsService calculation methods + +3. Create integration tests for: + - OrderController CRUD endpoints + - CustomerController operations + - OrderReportsController analytics endpoints + - Order status workflow end-to-end scenarios + +4. Use Moq for mocking dependencies (IOrderRepository, ICustomerRepository) +5. Implement TestContainers for database integration tests +6. Add test data builders/factories for consistent test setup +7. Follow AAA (Arrange-Act-Assert) pattern throughout +8. Include both positive and negative test scenarios + +Ensure high code coverage and test maintainability. +``` + +**Comprehensive testing implementation:** +- Creates separate test projects with proper structure +- Generates unit tests with thorough coverage +- Implements integration tests with realistic scenarios +- Sets up proper mocking and test data strategies +- Creates maintainable test architecture +- Includes edge cases and error scenarios + +--- + +### 6️⃣ **Implement Basic Deployment Configuration** + +**Add essential deployment setup:** + +```text +@workspace, Prepare this Orders API for basic production deployment: + +1. Create containerization setup: + - Simple Dockerfile optimized for .NET 8 + - docker-compose.yml for local development with SQL Server + +2. Implement health checks: + - Database connectivity check + - Basic application health endpoint + +3. Add configuration management: + - Environment-specific appsettings files + - Connection string configuration for different environments + - Basic logging configuration + +4. Create deployment scripts: + - Simple deployment script for IIS or self-hosted scenarios + - Database migration scripts + - Environment setup instructions + +Keep it simple and focused on single-server deployment scenarios. +``` + +**Basic deployment features:** +- Simple containerization for development +- Essential health monitoring +- Environment configuration management +- Straightforward deployment approach + +--- + +## 🎨 **Advanced Agent Techniques for E-commerce** + +### πŸ”„ **Multi-Stage Feature Development** + +**Break complex features into coordinated stages:** + +```text +@workspace, Implement order fulfillment workflow in stages: + +Stage 1: Create InventoryService to check product availability +Stage 2: Add ReservationService for temporary stock holds +Stage 3: Implement PaymentService integration interfaces +Stage 4: Create FulfillmentService for order processing workflow +Stage 5: Add notification system for order status updates + +Coordinate all services and ensure proper transaction handling. +``` + +### πŸ—οΈ **Architecture Evolution** + +**Evolve the architecture while maintaining existing functionality:** + +```text +@workspace, Refactor this Orders API to implement CQRS pattern: + +1. Separate read and write models +2. Implement command handlers for order operations +3. Create query handlers for data retrieval +4. Add event sourcing for order state changes +5. Maintain backward compatibility with existing endpoints + +Follow Clean Architecture principles throughout the refactoring. +``` + +--- + +## πŸ’‘ **Agent-Specific Best Practices for E-commerce** + +### 🎯 **Effective Agent Prompting** + +**Provide Business Context:** +```text +βœ… Effective: @workspace, Implement inventory management for an e-commerce platform where orders must check stock availability and handle backorders. + +❌ Generic: @workspace, Add inventory features. +``` + +**Specify Quality Requirements:** +```text +βœ… Comprehensive: Include proper error handling, validation, logging, and follow existing DDD patterns in the project. + +❌ Incomplete: Make it work. +``` + +**Request Complete Implementation:** +```text +βœ… Thorough: Implement the complete feature including entities, services, controllers, DTOs, validation, tests, and dependency injection configuration. + +❌ Partial: Create an order service. +``` + +### πŸš€ **E-commerce Workflow Optimization** + +**Domain-Driven Prompts:** +```text +@workspace, Following DDD principles, implement order aggregate with: +- Order root entity with business invariants +- OrderItem value objects +- Order repository interface +- Domain events for order state changes +- Application services for use cases +``` + +**Multi-Bounded Context Integration:** +```text +@workspace, Integrate Orders context with Catalog context: +- Add product validation against catalog service +- Implement inventory checking workflow +- Handle cross-context data consistency +- Add circuit breaker patterns for external calls +``` + +--- + +## 🏒 **Enterprise E-commerce Scenarios** + +### πŸ“Š **Scaling Considerations** + +```text +@workspace, Optimize this Orders API for high-volume e-commerce: +- Implement read replicas for order queries +- Add caching strategies for frequent lookups +- Create asynchronous processing for order workflows +- Implement pagination for large result sets +- Add rate limiting for API protection +``` + +### πŸ”’ **Security and Compliance** + +```text +@workspace, Implement PCI DSS compliance features: +- Add payment data encryption +- Implement audit logging for sensitive operations +- Create secure customer data handling +- Add GDPR compliance for customer data export/deletion +- Implement role-based access control +``` + +--- + +## ⚠️ **Agent vs. Other Copilot Tools** + +### πŸ€– **Use Agent When:** +- Implementing complete features across multiple files +- Creating new architectural components +- Setting up entire development workflows +- Generating comprehensive test suites +- Building production deployment configurations +- Refactoring large code sections + +### 🎯 **Use Ask When:** +- Understanding specific business logic +- Debugging individual methods +- Learning about existing patterns + +### 🌍 **Use @workspace When:** +- Analyzing overall architecture +- Understanding project-wide patterns +- Planning major architectural changes + +--- + +## βœ… Summary + +By completing this lab, you've learned to: + +- **Leverage Copilot Agent for autonomous feature development** in e-commerce applications +- **Implement complex business workflows** with coordinated multi-file changes +- **Maintain architectural consistency** while adding new capabilities +- **Generate production-ready code** with proper testing and deployment configurations +- **Apply DDD and Clean Architecture patterns** through AI-assisted development +- **Optimize for e-commerce scenarios** with performance and scalability considerations + +Copilot Agent transforms development velocity by autonomously implementing complete features while maintaining code quality and architectural integrity. + +--- + +## 🎯 Next Steps + +1. **Experiment with larger features** - Try implementing complete e-commerce workflows +2. **Practice architecture evolution** - Use Agent to refactor existing systems +3. **Integrate with team workflows** - Apply Agent in your development process +4. **Explore domain-specific patterns** - Develop prompts for your business domain +5. **Combine with other Copilot tools** - Use Agent alongside Ask and @workspace for maximum productivity + +--- + +Β© Copyright Neudeisc 2025 diff --git a/lessons/02-Getting-Started/c#/2.3-exploring-copilot-inline.md b/lessons/02-Getting-Started/c#/2.3-exploring-copilot-inline.md new file mode 100644 index 0000000..f911c1d --- /dev/null +++ b/lessons/02-Getting-Started/c#/2.3-exploring-copilot-inline.md @@ -0,0 +1,113 @@ +# πŸ§ͺ Lab: Exploring GitHub Copilot Inline + +--- + +## πŸ“ Overview + +**Goal:** +Learn how to use GitHub Copilot's inline chat and autocomplete features to make quick, incremental changes directly within the context of your code editor. You'll practice generating, modifying, and explaining code using inline prompts. + +**Estimated Duration:** 15-20 minutes + +**Audience:** +Developers looking to boost their productivity through fast, context-aware code editing within their IDE. + +**Prerequisites:** + +- Visual Studio 2022 installed (17.14.6 or later) +- GitHub Copilot extension enabled +- Access to GitHub Copilot (with inline chat feature) +- Familiarity with C# and ASP.NET Core Web API + +**Project Folder:** +[Orders API Sample Project](../../../../sample-code/Orders/README.md) + +--- + +## 🧭 Lab Steps + +### 1️⃣ **Open Target File** + +- Open the Orders.sln solution in Visual Studio 2022 +- Navigate to `Orders.Api\Controllers\OrderController.cs` file +- Open the file for editing + +--- + +### 2️⃣ **Use Code Comments to Guide Copilot** + +- In the `OrderController.cs` file, position your cursor at the end of the class (before the closing brace) +- Add a comment to guide Copilot: + + ```csharp + // Add a PUT method to update an existing order by ID with proper validation + ``` + + Press Enter and wait for Copilot suggestions. Review and accept the generated code. + +--- + +### 3️⃣ **Inline Chat with Copilot** + +To open inline chat, position your cursor and press `Ctrl + I` (Windows/Linux) or `Cmd + I` (macOS). + +- Position your cursor inside the `OrderController` class +- Open inline Copilot chat (`Ctrl + I`) +- Enter the following prompt: + + ```plaintext + Add a DELETE endpoint that removes an order by ID. Include validation to check if the order exists before deletion. + ``` + + Accept the generated code by pressing `Tab` or discard it by pressing `Escape`. + +--- + +### 4️⃣ **Enhance Order Entity** + +- Open `Orders.Domain\Order.cs` file +- Position your cursor at the end of the `Order` class +- Use inline chat (`Ctrl + I`) with this prompt: + + ```plaintext + Add a method CalculateDiscount that takes a discount percentage and applies it to the total order value +``` + +--- + +### 5️⃣ **Explain Existing Code** + +- In the `OrderController.cs` file, select the `CreateOrder` method +- Open inline chat (`Ctrl + I`) +- Enter the following prompt: + + ```plaintext + /explain this method's functionality and HTTP response codes + ``` + +--- + +## πŸ’‘ Best Practices + +- **Use Inline for Quick Edits:** Perfect for adding single methods, properties, or small code blocks +- **Leverage Code Comments:** Use comments to guide Copilot's suggestions contextually +- **Stay Focused:** Inline prompts work best for localized changes within the current file +- **Use /commands:** Try `/explain`, `/doc`, `/fix` for common tasks +- **Position Matters:** Place your cursor where you want the code to be generated + +--- + +## βœ… Summary + +By completing this lab, you've learned to: + +- Use GitHub Copilot's inline chat for quick code generation and modification +- Leverage code comments to guide contextual suggestions +- Apply inline prompts for targeted improvements without leaving your coding context +- Use built-in commands like `/explain` for code understanding + +GitHub Copilot Inline keeps you in the development flow while providing instant, context-aware assistance for rapid code iteration. + +--- + +Β© Copyright Neudeisc 2025 diff --git a/lessons/02-Getting-Started/c#/images/VS-copilot-options.png b/lessons/02-Getting-Started/c#/images/VS-copilot-options.png new file mode 100644 index 0000000..72f801f Binary files /dev/null and b/lessons/02-Getting-Started/c#/images/VS-copilot-options.png differ diff --git a/lessons/02-Getting-Started/c#/images/VS-enable-agent-mode.png b/lessons/02-Getting-Started/c#/images/VS-enable-agent-mode.png new file mode 100644 index 0000000..352369c Binary files /dev/null and b/lessons/02-Getting-Started/c#/images/VS-enable-agent-mode.png differ diff --git a/lessons/02-Getting-Started/c#/images/VS-open-edits-thread.png b/lessons/02-Getting-Started/c#/images/VS-open-edits-thread.png new file mode 100644 index 0000000..9bf8443 Binary files /dev/null and b/lessons/02-Getting-Started/c#/images/VS-open-edits-thread.png differ diff --git a/lessons/02-Getting-Started/c#/images/copilot-chat-hints.jpeg b/lessons/02-Getting-Started/c#/images/copilot-chat-hints.jpeg new file mode 100644 index 0000000..61db744 Binary files /dev/null and b/lessons/02-Getting-Started/c#/images/copilot-chat-hints.jpeg differ diff --git a/lessons/02-Getting-Started/copilot-code-review.md b/lessons/02-Getting-Started/copilot-code-review.md new file mode 100644 index 0000000..5a13f4a --- /dev/null +++ b/lessons/02-Getting-Started/copilot-code-review.md @@ -0,0 +1,633 @@ +# πŸ” Lab: GitHub Copilot for Code Review and Quality Enhancement + +--- + +## πŸ“ Overview + +**Goal:** +Master GitHub Copilot as an intelligent code review assistant to identify issues, suggest improvements, and maintain enterprise-grade code quality standards across your development projects. + +**Estimated Duration:** 20-25 minutes + +**Audience:** +Developers, Team Leads, QA Engineers, Code Reviewers, and DevOps professionals working with any programming language in enterprise environments. + +**Prerequisites:** +- GitHub Copilot installed and configured in your IDE +- Access to GitHub Copilot Chat with @workspace capability +- A sample codebase or project to review (or use our sample projects) +- Basic understanding of code review principles and quality standards + +**Sample Projects Available:** +- [Orders API](../../../sample-code/Orders/) - .NET Core API +- [SimpleFullStack](../../../sample-code/SimpleFullStack/) - React + .NET +- [eShop](../../../sample-code/eShop/) - Microservices architecture + +--- + +## 🎯 **What is Copilot-Assisted Code Review?** + +GitHub Copilot transforms traditional code review into an **intelligent, AI-assisted process** that enhances human expertise rather than replacing it. + +### **πŸ” Core Capabilities** + +**Intelligent Analysis:** +- **Pattern Recognition** - Identifies anti-patterns and code smells across your codebase +- **Security Assessment** - Detects vulnerabilities using OWASP and security best practices +- **Performance Evaluation** - Analyzes algorithmic complexity and resource usage +- **Architecture Review** - Validates design patterns and SOLID principles adherence + +**Quality Enhancement:** +- **Best Practice Enforcement** - Ensures consistency with language-specific conventions +- **Documentation Analysis** - Evaluates code documentation quality and completeness +- **Test Coverage Assessment** - Identifies testing gaps and suggests comprehensive test strategies +- **Refactoring Opportunities** - Proposes improvements for maintainability and readability + +### **πŸš€ Enterprise Benefits** + +βœ… **Accelerated Review Cycles** - Instant preliminary analysis before human review +βœ… **Consistent Quality Standards** - Uniform application of coding standards across teams +βœ… **Early Issue Detection** - Catch problems before they reach production +βœ… **Knowledge Transfer** - Educational insights for junior developers +βœ… **Compliance Assurance** - Automated checks for regulatory and security compliance +βœ… **Technical Debt Reduction** - Systematic identification and prioritization of improvements + +--- + +## 🧭 **Comprehensive Lab Steps** + +### 1️⃣ **Initialize Intelligent Code Review** + +**Set up comprehensive workspace analysis:** + +```text +@workspace I need to perform a comprehensive enterprise-grade code review of this project. Please analyze: + +1. **Overall Architecture:** Design patterns, separation of concerns, and architectural compliance +2. **Code Quality:** Maintainability, readability, and adherence to best practices +3. **Security Posture:** Vulnerabilities, authentication/authorization, and data protection +4. **Performance Characteristics:** Efficiency, scalability, and resource utilization +5. **Testing Strategy:** Coverage, quality, and testing best practices +6. **Documentation Quality:** Code comments, API documentation, and maintainability docs + +Provide a prioritized executive summary with specific examples and actionable recommendations. +``` + +**Initial Assessment Questions:** +```text +@workspace What are the top 5 most critical issues in this codebase that should be addressed immediately for production readiness? + +@workspace Rate this codebase on a scale of 1-10 for enterprise readiness and explain the rating with specific areas for improvement. +``` + +**🎯 Expected Outcomes:** +- Comprehensive quality assessment report +- Prioritized list of critical issues +- Architecture and design pattern evaluation +- Security and performance baseline analysis + +--- + +### 2️⃣ **Security and Vulnerability Assessment** + +**Conduct enterprise-level security analysis:** + +```text +@workspace Perform a comprehensive security review focusing on: + +**Critical Security Areas:** +1. **Authentication & Authorization:** JWT handling, session management, role-based access control +2. **Input Validation:** SQL injection, XSS, command injection prevention +3. **Data Protection:** Encryption at rest/transit, PII handling, GDPR compliance +4. **API Security:** Rate limiting, CORS configuration, API key management +5. **Dependency Security:** Known vulnerabilities in packages and libraries +6. **Configuration Security:** Environment variables, secrets management, secure defaults + +**OWASP Top 10 Compliance:** +- Injection flaws and prevention +- Broken authentication and session management +- Sensitive data exposure +- XML external entities (XXE) +- Broken access control +- Security misconfiguration +- Cross-site scripting (XSS) +- Insecure deserialization +- Known vulnerable components +- Insufficient logging and monitoring + +Provide specific code examples, remediation steps, and security improvement roadmap. +``` + +**Targeted Security Analysis:** +```text +@workspace Analyze the authentication and authorization implementation. Are there any security gaps that could lead to unauthorized access or privilege escalation? + +@workspace Review all API endpoints for security vulnerabilities including input validation, output encoding, and access control. + +@workspace Check this codebase for proper handling of sensitive data and compliance with data protection regulations. +``` + +**πŸ”’ Security Review Benefits:** +- OWASP Top 10 compliance verification +- Enterprise security standard adherence +- Vulnerability identification with remediation guidance +- Compliance readiness assessment (GDPR, HIPAA, SOX) + +--- + +### 3️⃣ **Performance and Scalability Analysis** + +**Comprehensive performance review:** + +```text +@workspace Conduct a detailed performance analysis covering: + +**Performance Domains:** +1. **Database Performance:** Query optimization, indexing strategies, N+1 problems +2. **Algorithm Efficiency:** Time/space complexity, optimization opportunities +3. **Memory Management:** Memory leaks, garbage collection impact, resource cleanup +4. **Concurrency & Threading:** Race conditions, deadlocks, async/await patterns +5. **Caching Strategy:** Cache hit ratios, invalidation strategies, distributed caching +6. **Network Optimization:** API response times, payload sizes, compression + +**Scalability Assessment:** +- Horizontal scaling readiness +- Stateless design verification +- Resource utilization patterns +- Bottleneck identification +- Load testing recommendations + +**Specific Areas to Examine:** +- Database queries with EXPLAIN plans +- Loops and iteration patterns +- File I/O operations +- External service integrations +- Resource-intensive operations + +Provide performance metrics, benchmarking suggestions, and optimization roadmap. +``` + +**Performance-Focused Queries:** +```text +@workspace Identify the top 3 performance bottlenecks in this application and provide specific optimization strategies with expected performance gains. + +@workspace Analyze the database interactions in this codebase. Are there any N+1 query problems or missing indexes that could impact performance? + +@workspace Review this code for memory efficiency and identify any potential memory leaks or excessive memory allocation patterns. +``` + +**πŸ“ˆ Performance Insights:** +- Algorithmic complexity analysis +- Database query optimization opportunities +- Caching strategy recommendations +- Scalability improvement suggestions + +--- + +### 4️⃣ **Architecture and Design Pattern Review** + +**Evaluate architectural quality:** + +```text +@workspace Perform an architectural review focusing on: + +**Design Principles:** +1. **SOLID Principles:** Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion +2. **Design Patterns:** Proper implementation of creational, structural, and behavioral patterns +3. **Separation of Concerns:** Layered architecture, clean architecture adherence +4. **Dependency Management:** Dependency injection, inversion of control, circular dependencies +5. **Code Organization:** Module structure, package organization, namespace conventions + +**Architecture Assessment:** +- Domain-driven design implementation +- Microservices architecture compliance (if applicable) +- API design and RESTful principles +- Event-driven architecture patterns +- Error handling and resilience patterns + +**Code Quality Metrics:** +- Cyclomatic complexity analysis +- Code duplication identification +- Coupling and cohesion measurement +- Technical debt assessment + +Provide refactoring recommendations and architectural improvement suggestions. +``` + +**Architecture-Specific Analysis:** +```text +@workspace Does this codebase follow the principles of Clean Architecture? Identify any violations and suggest improvements. + +@workspace Analyze the dependency relationships in this project. Are there any circular dependencies or architectural layer violations? + +@workspace Review the error handling strategy across this application. Is it consistent and comprehensive? +``` + +**πŸ—οΈ Architectural Benefits:** +- SOLID principles compliance verification +- Design pattern usage evaluation +- Clean architecture adherence assessment +- Technical debt identification and prioritization + +--- + +### 5️⃣ **Code Quality and Maintainability Assessment** + +**Comprehensive quality evaluation:** + +```text +@workspace Assess code quality and maintainability across these dimensions: + +**Code Quality Metrics:** +1. **Readability:** Naming conventions, code clarity, self-documenting code +2. **Maintainability:** Code complexity, modularity, changeability +3. **Consistency:** Coding standards adherence, style guide compliance +4. **Documentation:** Code comments, API documentation, README quality +5. **Error Handling:** Exception management, logging, graceful degradation + +**Language-Specific Standards:** +- C#: Microsoft coding conventions, .NET best practices +- JavaScript/TypeScript: ESLint rules, modern JS patterns +- Python: PEP 8 compliance, Pythonic patterns +- Java: Oracle coding standards, Spring framework patterns + +**Technical Debt Analysis:** +- Code smells identification +- Refactoring opportunities +- Legacy code modernization +- Dead code elimination + +**Team Standards Compliance:** +- Naming convention consistency +- Code formatting standardization +- Comment quality and appropriateness +- File and folder organization + +Provide a maintainability score and improvement roadmap. +``` + +**Quality-Focused Reviews:** +```text +@workspace Rate the readability and maintainability of this codebase. What specific improvements would make it easier for new team members to understand and modify? + +@workspace Identify code smells and anti-patterns in this project. Provide refactoring suggestions with before/after examples. + +@workspace Check this codebase for consistency with established coding standards and suggest standardization improvements. +``` + +**✨ Quality Improvements:** +- Code readability enhancement suggestions +- Maintainability score with improvement areas +- Coding standard compliance verification +- Documentation quality assessment + +--- + +### 6️⃣ **Testing Strategy and Coverage Analysis** + +**Comprehensive testing evaluation:** + +```text +@workspace Analyze the testing strategy and provide comprehensive recommendations: + +**Testing Coverage Analysis:** +1. **Unit Testing:** Test coverage percentage, critical path testing, edge case coverage +2. **Integration Testing:** API testing, database integration, external service mocking +3. **End-to-End Testing:** User journey testing, business scenario validation +4. **Performance Testing:** Load testing, stress testing, endurance testing +5. **Security Testing:** Penetration testing, vulnerability scanning + +**Test Quality Assessment:** +- Test reliability and flakiness +- Test maintenance overhead +- Mock usage and test isolation +- Test data management +- Assertion quality and coverage + +**Missing Test Scenarios:** +- Error condition testing +- Boundary value testing +- Negative test cases +- Security test cases +- Performance benchmarks + +**Testing Best Practices:** +- Test-driven development adherence +- Behavior-driven development implementation +- Continuous testing integration +- Test automation strategy + +Provide specific test case recommendations and testing improvement roadmap. +``` + +**Testing-Focused Analysis:** +```text +@workspace What critical components in this codebase lack adequate unit test coverage? Generate comprehensive test case scenarios for the most important untested areas. + +@workspace Analyze the existing tests for quality and effectiveness. Are there any flaky tests or tests that don't provide meaningful validation? + +@workspace Create a testing strategy for this application that includes unit, integration, and end-to-end testing approaches with specific recommendations. +``` + +**πŸ§ͺ Testing Enhancement:** +- Comprehensive test coverage analysis +- Critical missing test identification +- Test quality and reliability assessment +- Testing strategy recommendations + +--- + +## πŸ”§ **Advanced Code Review Techniques** + +### **🎯 File-Specific Deep Dive Analysis** + +**Targeted component review:** +```text +Select a specific file β†’ Right-click β†’ "Add to Chat Context" + +@workspace This [Controller/Service/Component] seems to have multiple responsibilities. Analyze it for Single Responsibility Principle violations and suggest refactoring approaches. + +@workspace Review this method for complexity and suggest breaking it down into smaller, more focused functions. + +@workspace Analyze this class for proper error handling and exception management patterns. +``` + +### **πŸ”„ Comparative Implementation Analysis** + +**Cross-component consistency review:** +```text +@workspace I have multiple service classes that handle similar operations. Compare their implementations and identify inconsistencies, best practices, and standardization opportunities. + +@workspace Compare the error handling approaches across different controllers in this project. Suggest a consistent error handling strategy. + +@workspace Analyze the data access patterns across this application. Are there consistent approaches being used? +``` + +### **πŸ“ˆ Legacy Code Modernization Review** + +**Legacy code improvement analysis:** +```text +@workspace This is legacy code that needs modernization. Analyze it for: +- Outdated patterns and practices +- Security vulnerabilities +- Performance improvements +- Modern framework feature adoption +- Maintainability enhancements + +Provide a step-by-step modernization plan with risk assessment. +``` + +### **πŸš€ Pre-Deployment Readiness Review** + +**Production readiness assessment:** +```text +@workspace Evaluate this codebase for production deployment readiness: +- Configuration management +- Environment-specific settings +- Security hardening +- Performance optimization +- Monitoring and logging +- Error handling and resilience +- Documentation completeness + +Provide a production readiness checklist with any blockers identified. +``` + +--- + +## πŸ’‘ **Enterprise Code Review Best Practices** + +### **🎯 Effective Review Prompting Strategies** + +**Scope-Specific Reviews:** +```text +βœ… Effective: "@workspace Review the authentication logic in UserController for security vulnerabilities and compliance with our enterprise security standards" + +❌ Ineffective: "@workspace check my code" +``` + +**Quality-Focused Analysis:** +```text +βœ… Effective: "Analyze this API controller for performance bottlenecks, security issues, and maintainability concerns. Prioritize findings by business impact" + +❌ Ineffective: "Make this code better" +``` + +**Risk-Based Prioritization:** +```text +βœ… Effective: "Identify the top 3 highest-risk issues in this codebase that could impact production stability or security" + +❌ Ineffective: "Tell me everything wrong with this code" +``` + +### **πŸš€ Enterprise Workflow Integration** + +**Pre-Commit Quality Gates:** +```text +@workspace I'm preparing to commit these changes. Perform a quality review focusing on: +- Code standards compliance +- Security vulnerability introduction +- Performance impact assessment +- Breaking change identification +- Documentation updates needed +``` + +**Pull Request Enhancement:** +```text +@workspace Analyze these code changes as if you were a senior architect reviewing a pull request. What concerns would you raise and what recommendations would you make? +``` + +**Technical Debt Management:** +```text +@workspace Help me create a technical debt reduction plan for this component. Prioritize improvements by effort vs. impact and provide implementation guidance. +``` + +--- + +## πŸ† **Enterprise-Scale Review Scenarios** + +### **πŸ”’ Compliance and Regulatory Review** + +**GDPR/Privacy Compliance:** +```text +@workspace This application processes personal data. Review the code for GDPR compliance including: +- Data minimization principles +- Consent management +- Right to erasure implementation +- Data portability features +- Privacy by design adherence + +Identify any compliance gaps and remediation strategies. +``` + +**SOX/Financial Compliance:** +```text +@workspace Review this financial application for SOX compliance requirements: +- Audit trail implementation +- Data integrity controls +- Access control mechanisms +- Change management processes +- Documentation requirements + +Provide compliance assessment and improvement recommendations. +``` + +### **πŸ‘₯ Team Standards and Governance** + +**Architecture Governance:** +```text +@workspace Our organization follows [Clean Architecture/Microservices/Domain-Driven Design]. Review this code for adherence to our architectural standards and identify any deviations that need addressing. +``` + +**Code Quality Gates:** +```text +@workspace Apply our enterprise code quality standards to this codebase: +- Minimum test coverage: 80% +- Maximum cyclomatic complexity: 10 +- No code duplication above 5% +- All public APIs must be documented +- All security-sensitive code must be reviewed + +Generate a compliance report with specific violations and remediation steps. +``` + +### **πŸ“Š DevOps and CI/CD Integration** + +**Pipeline Readiness:** +```text +@workspace Evaluate this code for CI/CD pipeline integration: +- Build automation compatibility +- Test automation readiness +- Deployment automation requirements +- Configuration management +- Monitoring and observability + +Suggest improvements for better DevOps integration. +``` + +**Infrastructure as Code Review:** +```text +@workspace Review the infrastructure and deployment configurations for: +- Security best practices +- Scalability considerations +- Cost optimization opportunities +- Monitoring and alerting setup +- Disaster recovery preparedness +``` + +--- + +## ⚠️ **Critical Considerations for Enterprise Adoption** + +### **πŸ” Human Oversight Requirements** + +**AI Limitations Awareness:** +- **Context Gaps:** AI may not understand specific business rules or domain constraints +- **False Positives:** Some suggestions may not apply to your specific architecture or requirements +- **Security Validation:** Critical security recommendations require human expert validation +- **Business Logic:** AI cannot validate business rule correctness or domain-specific requirements + +**Quality Assurance Process:** +- **Peer Review:** Combine AI analysis with human code review for comprehensive coverage +- **Testing Validation:** Always test AI-recommended changes thoroughly +- **Documentation:** Document the rationale for accepting or rejecting AI suggestions +- **Team Collaboration:** Share AI insights across the team for collective learning + +### **πŸ—οΈ Integration Best Practices** + +**Workflow Integration:** +- **Pre-Review Phase:** Use AI for initial quality assessment before human review +- **Review Enhancement:** Use AI to augment human reviewer capabilities +- **Post-Review Learning:** Analyze AI suggestions for team learning opportunities +- **Continuous Improvement:** Refine AI prompts based on team feedback and results + +**Team Training:** +- **Prompt Engineering:** Train team members on effective AI interaction +- **Result Interpretation:** Educate on understanding and validating AI recommendations +- **Best Practice Sharing:** Establish team guidelines for AI-assisted review +- **Quality Standards:** Maintain consistent quality expectations across AI and human review + +--- + +## πŸ“Š **Measuring Review Effectiveness and ROI** + +### **🎯 Key Performance Indicators** + +**Quality Metrics:** +- **Pre-Production Bug Reduction:** Measure decrease in production issues +- **Review Cycle Time:** Track time reduction in code review processes +- **Security Vulnerability Detection:** Count early vulnerability identification +- **Code Quality Scores:** Monitor improvements in maintainability and readability metrics + +**Team Productivity Metrics:** +- **Review Throughput:** Measure increase in code review capacity +- **Developer Learning:** Track improvement in code quality from junior developers +- **Knowledge Sharing:** Measure spread of best practices across the team +- **Technical Debt Reduction:** Monitor systematic technical debt elimination + +### **πŸ“ˆ Continuous Improvement Process** + +**Regular Assessment:** +```text +@workspace Based on the issues found in previous reviews, what proactive quality checks should we implement to prevent similar problems in future code? + +@workspace Analyze our recent code review findings and suggest improvements to our development process and coding standards. +``` + +**Team Retrospectives:** +- Review AI-assisted code review effectiveness monthly +- Identify patterns in issues found by AI vs. human reviewers +- Refine prompts and processes based on team feedback +- Share successful AI review patterns across the organization + +--- + +## βœ… **Comprehensive Summary** + +By mastering this comprehensive code review approach, you've learned to: + +### **🎯 Core Review Capabilities** +- **Leverage AI as an intelligent review assistant** for comprehensive quality analysis +- **Conduct multi-dimensional reviews** covering security, performance, architecture, and maintainability +- **Apply enterprise-grade standards** consistently across large codebases +- **Integrate AI review** seamlessly into existing development workflows +- **Balance AI assistance with human expertise** for optimal quality outcomes + +### **πŸš€ Advanced Review Techniques** +- **Targeted component analysis** for deep-dive quality assessment +- **Cross-component consistency** validation and standardization +- **Legacy code modernization** with systematic improvement strategies +- **Production readiness evaluation** with comprehensive deployment checklists +- **Compliance and regulatory review** for enterprise requirements + +### **πŸ’Ό Enterprise Value Creation** +- **Accelerated review cycles** while maintaining quality standards +- **Consistent quality enforcement** across development teams +- **Early issue detection** reducing production incidents +- **Knowledge transfer facilitation** for team growth and learning +- **Technical debt management** with systematic improvement planning + +--- + +## 🎯 **Next Steps: Implementation Roadmap** + +Ready to implement AI-assisted code review in your organization? Follow our comprehensive implementation roadmap that provides a structured 8-week approach for successful adoption. + +**[πŸ“‹ View Implementation Roadmap β†’](implementation-roadmap.md)** + +**What You'll Find:** +- **Phase 1: Foundation** - Team training and process definition +- **Phase 2: Integration** - Pilot projects and workflow refinement +- **Phase 3: Optimization** - Advanced techniques and scaling +- **Phase 4: Excellence** - Best practices and continuous improvement +- **Success Metrics** - Quantitative and qualitative measures +- **Risk Mitigation** - Common challenges and solutions +- **Implementation Checklist** - Week-by-week action items + +--- + +**πŸ† Code Review Mastery Achievement:** You can now leverage GitHub Copilot to conduct enterprise-grade code reviews that enhance quality, security, and maintainability while accelerating development cycles. + +--- + +Β© Copyright Neudeisc 2025 diff --git a/lessons/02-Getting-Started/github-copilot-web-features.md b/lessons/02-Getting-Started/github-copilot-web-features.md new file mode 100644 index 0000000..b09e1c9 --- /dev/null +++ b/lessons/02-Getting-Started/github-copilot-web-features.md @@ -0,0 +1,137 @@ +# 🌐 GitHub Copilot Platform Features + +--- + +## πŸ“ Overview + +Beyond the core IDE-based tools, GitHub Copilot offers powerful web-based and command-line features that enhance your development workflow across the entire software development lifecycle. These platform features integrate seamlessly with GitHub's ecosystem to provide comprehensive AI assistance. + +**Estimated Duration:** 15-20 minutes + +**Audience:** +React developers and teams using GitHub for version control and collaboration. + +--- + +## πŸ–₯️ GitHub Copilot Website + +Utilize the Copilot Web interface to explore suggestions, manage preferences, and gain insights into your coding habits and productivity. This centralized interface provides a comprehensive dashboard for reviewing usage analytics, setting global preferences, accessing educational resources, and managing your Copilot subscription. + +![GitHub Copilot Website](images/copilot-website.png 'GitHub Copilot Website') + +**How to Use:** +1. **Visit** [github.com/copilot](https://github.com/copilot) or access it through your GitHub account. +2. **Sign in** with your GitHub account that has Copilot access. +3. **Navigate through the dashboard** to explore different sections: + - **Usage Analytics**: Review your coding statistics and productivity metrics + - **Settings**: Configure global preferences and language-specific behaviors + - **Billing**: Manage subscription details and payment information + - **Resources**: Access documentation, tutorials, and community content +4. **Customize settings** to optimize Copilot's behavior for your workflow. +5. **Review usage patterns** to understand how Copilot is helping your development process. + +**Example use cases:** + +- Reviewing coding suggestions outside of your IDE +- Analyzing your coding patterns to identify improvement areas +- Managing subscription details and preferences +- Customizing Copilot's behavior across different programming languages +- Setting up global ignore patterns for sensitive or proprietary code +- Accessing learning resources and official documentation +- Reviewing historical usage statistics to optimize your development workflow +- Providing feedback to the GitHub team on Copilot's suggestions + +--- + +## πŸ’» GitHub Copilot CLI + +Streamline command-line operations by generating shell commands and automating tasks directly from your terminal. Copilot CLI transforms natural language descriptions into powerful command-line instructions, making complex operations accessible without requiring memorization of syntax or extensive documentation lookups. + +**How to Use:** +1. **Install GitHub Copilot CLI** by running `gh extension install github/gh-copilot` (requires GitHub CLI). +2. **Authenticate** with `gh auth login` if not already signed in. +3. **Use `gh copilot suggest`** followed by your natural language description of what you want to do. +4. **Use `gh copilot explain`** to understand what a complex command does. +5. **Review and execute** the suggested commands as needed. + +**Example Commands:** +- `gh copilot suggest "find all files larger than 100MB"` +- `gh copilot explain "docker run -d -p 8080:80 nginx"` + +**Example use cases:** + +- Generating complex git commands for repository management (e.g., interactive rebasing, complex merges) +- Creating automation scripts quickly through natural language prompts +- Executing system-level operations without extensive command-line expertise +- Constructing advanced grep, sed, or awk commands for text processing +- Building deployment pipelines and CI/CD scripts +- Generating database queries or migration scripts +- Creating Docker and Kubernetes management commands +- Formulating complex data transformation pipelines using tools like jq or yq + +**Labs:** +- Mastering GitHub Copilot CLI (Coming Soon) + +--- + +## πŸ” GitHub Copilot Code Review + +GitHub Copilot now extends its capabilities to the code review process, offering **AI-generated code review suggestions** directly within your workflow. This tool provides actionable, context-aware recommendationsβ€”such as identifying potential bugs, suggesting best practices, highlighting security vulnerabilities, and flagging style inconsistencies. + +With Copilot Code Review, you get tailored suggestions for improving your code quality before merging or deploying changes. The system analyzes the code in your pull requests and surfaces comments just like a human reviewer, saving you time and helping your team maintain high standards. + +**How to Use:** +When you create or view a pull request on GitHub, look for the Copilot suggestions in the "Conversation" or "Files changed" tab. You can accept, reject, or discuss Copilot's code review comments just like any other feedback. + +![GitHub Copilot Code Review](images/copilot-pr-review.png 'GitHub Copilot Code Review') + +**Labs:** +- [Code Review with Copilot](copilot-code-review.md) + +--- + +## πŸ“„ Copilot Pull Request Summaries + +Copilot can now **automatically generate a summary for your pull request**, giving reviewers a clear overview of what's changed and what to focus on. These AI-generated summaries highlight which files are impacted, the nature of the changes, and any potential areas of concern for reviewers. + +This feature accelerates the review process, ensures nothing is overlooked, and reduces manual documentation efforts. Summaries appear automatically in your pull request description, making collaboration faster and more transparent. + +**How to Use:** +When opening a pull request, Copilot may prompt you to generate a summary or insert one automatically. Review and edit as needed before publishing your PR. + +![GitHub Copilot PR Summary](images/copilot-pr-message.png 'GitHub Copilot PR Summary') + +--- + +## πŸ’¬ Generate Commit Messages + +Writing meaningful commit messages is essential for project history and collaboration. With GitHub Copilot integrated into VS Code, you can now **generate commit messages directly from the Git panel**. +Copilot analyzes your staged changes and suggests clear, descriptive commit messages, saving you time and ensuring consistency. + +This is especially helpful when making multiple or complex updatesβ€”Copilot will propose a concise summary based on what's been changed, which you can accept as-is or modify as needed. + +**How to Use:** +- In VS Code, stage your changes in the Source Control panel. +- In the commit message box, look for the Copilot icon or prompt. +- Click to have Copilot suggest a commit message for your changes. +- Edit if necessary, then commit as usual. + +![GitHub Copilot Generate Git Message](images/copilot-generate-git-message.png 'GitHub Copilot Generate Git Message') + +--- + +## βœ… Summary + +These GitHub platform features extend Copilot's capabilities beyond your IDE: + +- **Web Dashboard**: Manage settings, analytics, and preferences +- **CLI Integration**: Generate and explain terminal commands +- **Code Review**: AI-assisted pull request analysis +- **PR Summaries**: Automated pull request documentation +- **Commit Messages**: Smart commit message generation + +Together, these features create a comprehensive AI-assisted development ecosystem that spans from local development to team collaboration and project management. + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/02-Getting-Started/images/ModelSelection.png b/lessons/02-Getting-Started/images/ModelSelection.png new file mode 100644 index 0000000..719027c Binary files /dev/null and b/lessons/02-Getting-Started/images/ModelSelection.png differ diff --git a/lessons/02-Getting-Started/images/copilot-agent.png b/lessons/02-Getting-Started/images/copilot-agent.png new file mode 100644 index 0000000..469690d Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-agent.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-access-chat.png b/lessons/02-Getting-Started/images/copilot-ask-access-chat.png new file mode 100644 index 0000000..ae12b9e Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-access-chat.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-add-context.png b/lessons/02-Getting-Started/images/copilot-ask-add-context.png new file mode 100644 index 0000000..9d34349 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-add-context.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-add-file.png b/lessons/02-Getting-Started/images/copilot-ask-add-file.png new file mode 100644 index 0000000..64d567f Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-add-file.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-add-selection.png b/lessons/02-Getting-Started/images/copilot-ask-add-selection.png new file mode 100644 index 0000000..7209756 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-add-selection.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-file-context.png b/lessons/02-Getting-Started/images/copilot-ask-file-context.png new file mode 100644 index 0000000..79307d3 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-file-context.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-hashtag.png b/lessons/02-Getting-Started/images/copilot-ask-hashtag.png new file mode 100644 index 0000000..b284b6c Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-hashtag.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-highlight-section.png b/lessons/02-Getting-Started/images/copilot-ask-highlight-section.png new file mode 100644 index 0000000..1b06486 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-highlight-section.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-multiple-context.png b/lessons/02-Getting-Started/images/copilot-ask-multiple-context.png new file mode 100644 index 0000000..dd7017c Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-multiple-context.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-question.png b/lessons/02-Getting-Started/images/copilot-ask-question.png new file mode 100644 index 0000000..69ef9e2 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-question.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask-workspace.png b/lessons/02-Getting-Started/images/copilot-ask-workspace.png new file mode 100644 index 0000000..31b1dba Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask-workspace.png differ diff --git a/lessons/02-Getting-Started/images/copilot-ask.png b/lessons/02-Getting-Started/images/copilot-ask.png new file mode 100644 index 0000000..76ebd52 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-ask.png differ diff --git a/lessons/02-Getting-Started/images/copilot-edit.png b/lessons/02-Getting-Started/images/copilot-edit.png new file mode 100644 index 0000000..79863b2 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-edit.png differ diff --git a/lessons/02-Getting-Started/images/copilot-generate-git-message.png b/lessons/02-Getting-Started/images/copilot-generate-git-message.png new file mode 100644 index 0000000..9aa3b4c Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-generate-git-message.png differ diff --git a/lessons/02-Getting-Started/images/copilot-icon-bottom-right.png b/lessons/02-Getting-Started/images/copilot-icon-bottom-right.png new file mode 100644 index 0000000..1f3453a Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-icon-bottom-right.png differ diff --git a/lessons/02-Getting-Started/images/copilot-icon-upper-right.png b/lessons/02-Getting-Started/images/copilot-icon-upper-right.png new file mode 100644 index 0000000..5a0a312 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-icon-upper-right.png differ diff --git a/lessons/02-Getting-Started/images/copilot-inline-ask.png b/lessons/02-Getting-Started/images/copilot-inline-ask.png new file mode 100644 index 0000000..283a10b Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-inline-ask.png differ diff --git a/lessons/02-Getting-Started/images/copilot-inline.png b/lessons/02-Getting-Started/images/copilot-inline.png new file mode 100644 index 0000000..6291e8f Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-inline.png differ diff --git a/lessons/02-Getting-Started/images/copilot-pr-message.png b/lessons/02-Getting-Started/images/copilot-pr-message.png new file mode 100644 index 0000000..4607c8b Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-pr-message.png differ diff --git a/lessons/02-Getting-Started/images/copilot-pr-review.png b/lessons/02-Getting-Started/images/copilot-pr-review.png new file mode 100644 index 0000000..99d1d20 Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-pr-review.png differ diff --git a/lessons/02-Getting-Started/images/copilot-website.png b/lessons/02-Getting-Started/images/copilot-website.png new file mode 100644 index 0000000..1e5920b Binary files /dev/null and b/lessons/02-Getting-Started/images/copilot-website.png differ diff --git a/lessons/02-Getting-Started/implementation-roadmap.md b/lessons/02-Getting-Started/implementation-roadmap.md new file mode 100644 index 0000000..635b5a8 --- /dev/null +++ b/lessons/02-Getting-Started/implementation-roadmap.md @@ -0,0 +1,240 @@ +# 🎯 Implementation Roadmap for Teams + +--- + +## πŸ“ Overview + +This roadmap provides a structured approach for teams to implement AI-assisted code review practices using GitHub Copilot. The phased approach ensures successful adoption while minimizing disruption to existing development workflows. + +**Estimated Timeline:** 8 weeks to full implementation + +**Target Audience:** +Development teams, engineering managers, and technical leads implementing AI-assisted code review processes. + +--- + +## πŸš€ Phase 1: Foundation (Week 1-2) + +### **Team Training** +Educate developers on AI-assisted review techniques and best practices for effective collaboration with GitHub Copilot. + +**Key Activities:** +- Conduct hands-on workshops using GitHub Copilot for code review +- Train team members on effective prompt engineering for code analysis +- Establish understanding of AI capabilities and limitations +- Create internal documentation for AI review best practices + +### **Process Definition** +Establish AI review integration in existing workflows without disrupting current development practices. + +**Implementation Steps:** +- Map current code review processes and identify integration points +- Define when and how AI review should be used (pre-review, post-review, or parallel) +- Create standardized review templates and prompts +- Establish escalation procedures for AI review findings + +### **Quality Standards** +Define enterprise quality criteria and compliance requirements that AI review should support. + +**Standards to Define:** +- Security review criteria (OWASP compliance, vulnerability detection) +- Performance benchmarks and optimization targets +- Code maintainability and readability standards +- Documentation and testing requirements +- Regulatory compliance checkpoints + +### **Tool Setup** +Configure AI review prompts and templates for consistent usage across the team. + +**Setup Requirements:** +- Create standardized Copilot prompt libraries +- Configure IDE settings and extensions +- Set up shared workspace configurations +- Establish naming conventions and folder structures + +--- + +## πŸ”§ Phase 2: Integration (Week 3-4) + +### **Pilot Projects** +Apply AI review to selected components and gather feedback on effectiveness and workflow impact. + +**Pilot Selection Criteria:** +- Choose non-critical components for initial testing +- Select diverse code types (frontend, backend, API, database) +- Include both new development and refactoring tasks +- Ensure pilot covers different team skill levels + +### **Workflow Refinement** +Adjust processes based on initial experience and results from pilot projects. + +**Refinement Areas:** +- Optimize prompt templates based on real-world usage +- Adjust review timing and frequency +- Refine integration points with existing tools +- Update team communication patterns + +### **Quality Measurement** +Establish baseline metrics for review effectiveness and process improvement. + +**Key Metrics:** +- Review cycle time and efficiency gains +- Defect detection rates and severity +- Developer satisfaction and adoption rates +- Code quality improvements (maintainability, performance) +- Time savings and productivity gains + +### **Team Feedback** +Collect and incorporate developer feedback on AI review value and process improvements. + +**Feedback Collection:** +- Regular retrospectives and feedback sessions +- Anonymous surveys on tool effectiveness +- One-on-one discussions with team members +- Documentation of pain points and success stories + +--- + +## πŸ“ˆ Phase 3: Optimization (Week 5-8) + +### **Process Automation** +Integrate AI review into CI/CD pipelines where appropriate to ensure consistent quality checks. + +**Automation Opportunities:** +- Pre-commit hooks for basic quality checks +- Pull request automation with AI analysis +- Continuous quality monitoring and alerts +- Automated documentation generation and updates + +### **Advanced Techniques** +Implement specialized review patterns for different code types and development scenarios. + +**Specialized Patterns:** +- Security-focused review for authentication/authorization code +- Performance-oriented analysis for data processing components +- Accessibility review for frontend components +- API design review for service interfaces +- Database optimization review for data access layers + +### **Continuous Improvement** +Regular retrospectives and process refinement based on accumulated experience and metrics. + +**Improvement Activities:** +- Monthly process review meetings +- Quarterly assessment of quality metrics +- Regular updates to review templates and prompts +- Knowledge sharing sessions across teams + +### **Organization Scaling** +Expand successful patterns across development teams and departments. + +**Scaling Strategies:** +- Create center of excellence for AI-assisted development +- Develop training materials for new teams +- Establish mentorship programs for AI review adoption +- Share success stories and best practices organization-wide + +--- + +## πŸ† Phase 4: Excellence (Ongoing) + +### **Best Practice Documentation** +Create organizational guidelines for AI-assisted review that become part of the development culture. + +**Documentation Areas:** +- Comprehensive AI review playbooks +- Code quality standards and enforcement +- Security review protocols and procedures +- Performance optimization guidelines +- Compliance and regulatory review processes + +### **Quality Monitoring** +Continuous tracking of quality improvements and ROI from AI-assisted review implementation. + +**Monitoring Framework:** +- Real-time quality dashboards and metrics +- Regular ROI analysis and reporting +- Benchmark comparisons with industry standards +- Long-term trend analysis and insights + +### **Innovation Integration** +Adopt new AI review capabilities as they become available from GitHub and other providers. + +**Innovation Areas:** +- New Copilot features and capabilities +- Integration with emerging development tools +- Advanced AI models and specialized review agents +- Custom AI review solutions for specific needs + +### **Knowledge Sharing** +Cross-team collaboration on AI review best practices and continuous learning. + +**Sharing Mechanisms:** +- Regular community of practice meetings +- Internal conferences and knowledge sharing events +- Cross-functional collaboration on AI review innovations +- External community participation and contribution + +--- + +## πŸ“Š Success Metrics + +### **Quantitative Measures** +- **Review Efficiency:** 40-60% reduction in review cycle time +- **Defect Detection:** 25-35% increase in early defect identification +- **Code Quality:** Measurable improvements in maintainability scores +- **Developer Productivity:** 15-25% increase in feature delivery velocity + +### **Qualitative Measures** +- **Developer Satisfaction:** Improved job satisfaction and reduced review fatigue +- **Code Confidence:** Increased confidence in code quality and security +- **Learning Acceleration:** Faster onboarding and skill development +- **Innovation Culture:** Enhanced adoption of AI-assisted development practices + +--- + +## πŸ›‘οΈ Risk Mitigation + +### **Common Challenges** +- **Over-reliance on AI:** Maintain human oversight and critical thinking +- **False Positives:** Regular calibration of AI review accuracy +- **Process Resistance:** Change management and clear communication of benefits +- **Tool Integration:** Ensure seamless integration with existing development tools + +### **Mitigation Strategies** +- Regular training and awareness programs +- Continuous feedback loops and process improvements +- Clear escalation procedures for complex review scenarios +- Backup processes for AI tool unavailability + +--- + +## βœ… Implementation Checklist + +### **Week 1-2: Foundation** +- [ ] Complete team training on AI-assisted code review +- [ ] Define quality standards and compliance requirements +- [ ] Set up AI review tools and templates +- [ ] Document initial processes and procedures + +### **Week 3-4: Integration** +- [ ] Launch pilot projects with selected components +- [ ] Collect baseline metrics and initial feedback +- [ ] Refine processes based on pilot results +- [ ] Prepare for broader team rollout + +### **Week 5-8: Optimization** +- [ ] Implement process automation where appropriate +- [ ] Deploy advanced review techniques +- [ ] Establish continuous improvement processes +- [ ] Begin scaling to additional teams + +### **Ongoing: Excellence** +- [ ] Maintain and update best practice documentation +- [ ] Monitor quality metrics and ROI continuously +- [ ] Integrate new AI capabilities as available +- [ ] Foster knowledge sharing and innovation culture + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/02-Getting-Started/react/2.0-github-copilot-tools.md b/lessons/02-Getting-Started/react/2.0-github-copilot-tools.md new file mode 100644 index 0000000..94488c5 --- /dev/null +++ b/lessons/02-Getting-Started/react/2.0-github-copilot-tools.md @@ -0,0 +1,218 @@ +# πŸš€ Lesson 02 - GitHub Copilot Tools + +--- + +## πŸ“ Overview + +GitHub Copilot provides developers with powerful AI-assisted coding tools designed to streamline coding, debugging, optimization, and automation tasks. These tools integrate seamlessly into various development environments, improving productivity and efficiency across multiple stages of the software development lifecycle. + +--- + +## 🎯 Goal + +Learn about the different GitHub Copilot tools, their use cases, and how to leverage them to enhance your development workflow across various environments. + +--- + +## ⏱️ Estimated Time + +45-60 minutes + +--- + +## πŸ‘₯ Participants + +Everyone (Developers, QA testers, DevOps engineers, Technical Writers, and more) + +--- + +## πŸ› οΈ Explore Copilot Tools + +Explore the different tools available within GitHub Copilot to enhance your development workflow. Below are sections highlighting each tool, including detailed descriptions, use cases, and links to dedicated labs for hands-on learning. + +--- + +## πŸ”‘ Accessing Copilot: The Two Main Icons + +GitHub Copilot provides two main icons in your IDE to help you quickly access Copilot features and settings: + +- **Top Right Copilot Icon:** + - Located in the upper right corner of your IDE window. + - Click this icon to open the Copilot Chat panel, start a new chat, or access Copilot settings and account information. + - This is the primary entry point for interacting with Copilot's conversational features and managing your Copilot experience. + +- **Bottom Right Copilot Icon:** + - Found in the lower right corner of your IDE status bar. + - Click this icon to quickly toggle Copilot on or off, view Copilot's current status, or access inline suggestions and quick settings. + - This icon is especially useful for enabling/disabling Copilot or checking if Copilot is active in your current workspace. + +![Copilot Icon Upper Right](../images/copilot-icon-upper-right.png) +![Copilot Icon Bottom Right](../images/copilot-icon-bottom-right.png) + +--- + +## 1️⃣ GitHub Copilot Ask + +GitHub Copilot Ask provides quick, non-intrusive answers to programming questions directly in your editor. Get instant help understanding code, solving problems, or learning concepts without modifying your codebase. + +**[πŸ“„ View detailed documentation β†’](2.1-exploring-copilot-ask-(react).md)** + +**Key Benefits:** +- Context-aware responses based on your current editor state +- Non-intrusive guidance without code modifications +- Educational support for learning new concepts + +--- + +## 2️⃣ GitHub Copilot Agent + +GitHub Copilot Agent represents the most powerful mode in Copilot Chat, enabling autonomous planning and execution across your entire project. With Agent mode, you provide a high-level prompt and Copilot independently selects the right files, runs necessary tools or terminal commands, and applies code edits until the task is complete. Agent analyzes related code and identifies additional changes needed across the project to maintain consistency. + +What distinguishes Agent mode is its ability to work autonomouslyβ€”applying edits automatically rather than waiting for explicit approval at each step, while still surfacing potentially risky commands for review. This creates a continuous-edit "driver" model where you define the goal and Copilot executes updates without interruption. + +For maximum effectiveness, Agent mode works best with custom instructions that define your project structure, coding standards, and other guidelines. These instructions provide a stronger foundation for Copilot to work from, resulting in more consistent and aligned outcomes across multiple sessions. + +![GitHub Copilot Agent](../images/copilot-agent.png 'GitHub Copilot Agent') + +**How to Use:** +1. **Open Copilot Chat** by clicking the chat icon in your IDE or using the keyboard shortcut. +2. **Type `@workspace`** to enable Agent mode for your entire project. +3. **Provide a high-level description** of what you want to accomplish (e.g., "implement user authentication", "add error logging throughout the app"). +4. **Review the plan** that Copilot presents before execution. +5. **Monitor the progress** as Copilot autonomously makes changes across multiple files. +6. **Approve any risky operations** when prompted (like running terminal commands). + +**Example use cases:** + +- Building complete features from high-level descriptions +- Fixing complex bugs that require changes across multiple files +- Creating new files and scaffolding entire sections of an application +- Implementing architectural changes that affect multiple components +- Setting up new projects based on README specifications or requirements documents +- Refactoring code while maintaining consistent patterns throughout the codebase + +**Labs:** +- [Exploring GitHub Copilot Agent (React)](2.3-exploring-copilot-agent-(react).md) + +--- + +## 3️⃣ GitHub Copilot Chat Modes + +GitHub Copilot Chat operates in different modes, each optimized for specific use cases. You can switch between modes anytime in the Chat view to match your current development task. + +### **Built-in Chat Modes** + +**Ask Mode** +- **Purpose**: Optimized for answering questions about your codebase and technology concepts +- **Best for**: Understanding code, brainstorming design ideas, exploring new technologies +- **React use**: Component architecture questions, debugging help, best practices guidance + +**Agent Mode** +- **Purpose**: Autonomous edits across multiple files with intelligent decision-making +- **Best for**: Complex tasks that may require running terminal commands and tools +- **React use**: Complete feature implementation, architectural changes, setup tasks + +### **Custom Chat Modes** + +Create personalized chat modes tailored to your specific React development workflows using `.chatmode.md` files. + +**Example Custom Modes for React:** +- **"React Review"**: Code review focused on React best practices and performance +- **"Component Plan"**: Generate implementation plans for new React components +- **"Testing Focus"**: Specialized mode for creating React testing strategies + +**How to Create:** +1. Run **Chat: New Mode File** in Command Palette +2. Choose workspace or user profile location +3. Define tools, instructions, and guidelines in the `.chatmode.md` file + +Learn more: [VS Code Chat Modes Documentation](https://code.visualstudio.com/docs/copilot/chat/chat-modes) + +--- + +## 4️⃣ Workspace Context: @workspace and #codebase + +GitHub Copilot provides powerful workspace-aware context through two main approaches: `@workspace` for comprehensive project analysis and `#codebase` for flexible code search integration. + +**@workspace** searches through your entire VS Code workspace to find relevant context for your questions, including all indexable files, directory structure, GitHub's code search index (if available), symbols and definitions, and currently selected text. + +**#codebase** performs targeted codebase searches based on your prompt and adds relevant code as context. It's more flexible than `@workspace` and can be combined with other tools for editing scenarios. + +**Key Differences:** +- **@workspace**: Read-only analysis tool, works only in Ask mode, provides comprehensive project understanding +- **#codebase**: Flexible search tool, works in all chat modes (Ask, Agent), can be combined with other operations + +**How to Use:** +- Use `@workspace` followed by your question: `"@workspace how is authentication implemented?"` +- Use `#codebase` in any prompt: `"add unit tests and run them #codebase"` +- Enable `github.copilot.chat.codesearch.enabled` setting for enhanced `#codebase` effectiveness + +**Example use cases:** +- `"@workspace where is database connection string configured?"` - Find configuration patterns +- `"add a tooltip to this button, consistent with other buttons #codebase"` - Find and apply existing patterns +- `"@workspace how can I add a new API route for forgot password?"` - Get implementation guidance +- `"How do I build this #codebase?"` - Understand build processes from documentation and scripts + +**Index Management:** +Copilot uses different indexing strategies for optimal performance: +- **Remote Index**: For GitHub repositories, automatically maintained and updated +- **Local Index**: For non-GitHub projects, built automatically for projects under 750 files +- **Basic Index**: Fallback for larger projects (2500+ files) using simpler algorithms + +Learn more: [VS Code Workspace Context Documentation](https://code.visualstudio.com/docs/copilot/reference/workspace-context#_remote-index) + +--- + +## 5️⃣ Choosing the Right AI Model + +GitHub Copilot offers multiple AI models optimized for different use cases. Selecting the appropriate model can significantly impact the quality and efficiency of your development experience. + +![Model Selection Guide](../images/ModelSelection.png) + +**Model Selection Strategy:** +- **Small, scoped changes**: Use **Claude-3.5-Sonnet** for targeted modifications and quick iterations +- **Larger tasks with clear instructions**: Use **Claude-3.5-Sonnet** or **GPT-4.1** for feature development and refactoring +- **Routine or general use**: Use **Claude-3.7-Sonnet**, **Gemini-2.5-Pro**, or **GPT-4.1** for everyday development assistance +- **Very complex or ambiguous tasks**: Use **o3** for advanced reasoning and complex problem-solving + +**For React Development:** +- **Component creation and modification**: Claude-4-Sonnet or GPT-4.1 +- **Complex state management**: Claude-3.7-Sonnet or o3 for intricate logic +- **Performance optimization**: o3 for advanced analysis and recommendations +- **Quick fixes and styling**: Claude-3.5-Sonnet or Claude-3.7-Sonnet for rapid iterations + +You can switch models anytime in Copilot Chat to match your current task requirements. + +--- + +## 6️⃣ GitHub Copilot Inline + +Enhance productivity by using inline prompts directly within your editor for quick React component modifications, method generation, and incremental improvements without leaving your coding context. + +**[πŸ“„ Explore Inline with React β†’](2.4-exploring-copilot-inline-(react).md)** + +**Key Benefits:** +- **Rapid iterations** on React components and hooks +- **Context-aware suggestions** based on your current component state +- **Seamless workflow** without switching between chat and editor + +--- + +## 7️⃣ GitHub Platform Features + +GitHub Copilot extends beyond your IDE with powerful web-based and command-line features for comprehensive AI assistance across your development workflow. + +**[🌐 Explore GitHub Platform Features β†’](../github-copilot-web-features.md)** + +**Key Features:** +- **Web Dashboard**: Manage settings, analytics, and usage insights +- **CLI Integration**: Generate and explain terminal commands +- **Code Review**: AI-assisted pull request analysis +- **PR Summaries**: Automated pull request documentation +- **Commit Messages**: Smart commit message generation + + + +--- + +Β© Copyright Neudeisc 2025 diff --git a/lessons/02-Getting-Started/react/2.1-exploring-copilot-ask-(react).md b/lessons/02-Getting-Started/react/2.1-exploring-copilot-ask-(react).md new file mode 100644 index 0000000..8536131 --- /dev/null +++ b/lessons/02-Getting-Started/react/2.1-exploring-copilot-ask-(react).md @@ -0,0 +1,246 @@ +# πŸ§ͺ Lab: Exploring GitHub Copilot Ask with React + +--- + +## πŸ“ Overview + +**Goal:** +Learn how to use GitHub Copilot Ask to explore, understand, and improve your React codebase by asking natural language questions directly within Visual Studio Code. + +**Estimated Duration:** 15-20 minutes + +**Audience:** +React developers, UI/UX engineers, and front-end specialists. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Chat (requires a Copilot subscription) +- A React project or access to the SimpleFullStack React project +- Basic familiarity with React concepts + +**Project Folder:** +Any React project or [SimpleFullStack React Project](../../../../sample-code/SimpleFullStack/Web/README.md) + +--- + +## 🧭 Lab Steps + +### 1️⃣ **Launch Copilot Chat** + +- Open Visual Studio Code with your React project +- Open Copilot Chat using one of these methods: + - Click the Copilot icon in the activity bar + - Use keyboard shortcut `Ctrl+Shift+I` (Windows/Linux) or `Cmd+Shift+I` (macOS) + - Command palette (`Ctrl+Shift+P`) β†’ "Chat: Open Chat" + +--- + +### 2️⃣ **Ask About React Component Structure** + +- Open any React component file (e.g., `ProductCard.tsx`, `HomePage.tsx`, `GlobalSnackbar.tsx`) +- **Select the entire component** or specific JSX sections +- In Copilot Chat, ask: + +```text +How does this React component work and what props does it accept? +``` + +**Follow-up questions to try:** +```text +How can I improve the performance of this component? +``` + +```text +What React patterns is this component using? +``` + +**What you'll learn:** +- Component prop analysis and TypeScript interfaces +- Performance optimization suggestions (React.memo, useMemo, useCallback) +- React patterns identification (controlled components, render props, etc.) + +--- + +### 3️⃣ **Understand React Hooks Usage** + +- Find a component that uses React hooks (useState, useEffect, etc.) +- **Select the hook usage** and ask: + +```text +Explain how these React hooks work together and suggest any improvements. +``` + +```text +Are there any potential issues with this hook implementation? +``` + +**Common insights you'll get:** +- Hook dependency array analysis +- Custom hook opportunities +- State management improvements +- Effect cleanup suggestions + +--- + +### 4️⃣ **Ask About State Management** + +- Open a component with complex state or context usage +- Ask Copilot: + +```text +How is state being managed in this component? Are there better approaches? +``` + +```text +Should this component state be lifted up or moved to a context? +``` + +**Valuable for understanding:** +- State lifting patterns +- Context API usage +- When to use external state management (Redux, Zustand) + +--- + +### 5️⃣ **Component Testing Guidance** + +- Select a React component and ask: + +```text +Write unit tests for this React component using React Testing Library. +``` + +```text +What are the key behaviors I should test in this component? +``` + +**You'll receive:** +- Comprehensive test examples +- Testing best practices for React +- Mock strategies for props and hooks +- Accessibility testing suggestions + +--- + +### 6️⃣ **API Integration Questions** + +- If you have API service files or data fetching logic, ask: + +```text +How can I improve this API integration in React? Should I use a different data fetching approach? +``` + +```text +How can I add proper error handling and loading states? +``` + +**Common suggestions:** +- React Query or SWR implementation +- Error boundary usage +- Loading state patterns +- Optimistic updates + +--- + +### 7️⃣ **Performance Optimization** + +- Select components with potential performance issues and ask: + +```text +Analyze this React component for performance bottlenecks and suggest optimizations. +``` + +```text +When should I use React.memo, useMemo, and useCallback here? +``` + +**Performance insights:** +- Unnecessary re-render identification +- Memoization strategies +- Code splitting opportunities +- Bundle size optimization + +--- + +## πŸ’‘ **React-Specific Best Practices for Copilot Ask** + +### 🎯 **Effective React Questions** + +**Component Architecture:** +```text +βœ… Good: "How does this component's prop drilling affect performance and what are better alternatives?" +❌ Vague: "Is this component good?" +``` + +**Hook Usage:** +```text +βœ… Specific: "Are these useEffect dependencies correct and is there a risk of infinite loops?" +❌ Generic: "Check my hooks" +``` + +**State Management:** +```text +βœ… Focused: "Should this form state be managed locally or moved to a global store?" +❌ Broad: "How do I manage state?" +``` + +### πŸš€ **React Development Workflow** + +**Understanding Legacy Code:** +```text +I inherited this React component. Explain its purpose, dependencies, and any potential issues. +``` + +**Modernization:** +```text +This component uses class-based syntax. How can I convert it to functional components with hooks? +``` + +**Accessibility:** +```text +Review this component for accessibility issues and suggest ARIA improvements. +``` + +--- + +## πŸ”§ **Advanced React Scenarios** + +### **Context and State Management** +```text +Analyze this React Context setup. Is it efficient and are there performance concerns? +``` + +### **Custom Hooks** +```text +Can this component logic be extracted into a reusable custom hook? +``` + +### **TypeScript Integration** +```text +Improve the TypeScript types for this React component's props and state. +``` + +### **Styling and CSS-in-JS** +```text +Review this styled-components usage. Are there better patterns or performance improvements? +``` + +--- + +## βœ… Summary + +By completing this lab, you've learned to: + +- **Use Copilot Ask for React-specific guidance** on components, hooks, and patterns +- **Analyze component performance** and get optimization suggestions +- **Understand React testing strategies** with practical examples +- **Get architectural guidance** for state management and component design +- **Apply React best practices** through AI-assisted code review + +These techniques will help you leverage GitHub Copilot Ask as a React development mentor that provides instant, contextual guidance for your frontend development workflow. + +--- + +Β© Copyright Neudeisc 2025 diff --git a/lessons/02-Getting-Started/react/2.3-exploring-copilot-agent-(react).md b/lessons/02-Getting-Started/react/2.3-exploring-copilot-agent-(react).md new file mode 100644 index 0000000..1102bee --- /dev/null +++ b/lessons/02-Getting-Started/react/2.3-exploring-copilot-agent-(react).md @@ -0,0 +1,559 @@ +# πŸ€– Lab: Mastering GitHub Copilot Agent for Advanced React Development + +--- + +## πŸ“ Overview + +**Goal:** +Master GitHub Copilot Agent to autonomously implement complex, multi-component features and architectural changes across your React applications, simulating real-world enterprise development scenarios. + +**Estimated Duration:** 25-30 minutes + +**Audience:** +React developers, front-end architects, full-stack engineers, and tech leads working with complex React applications in enterprise environments. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Chat with Agent capabilities +- React project with multiple components (any React app or our sample project) +- Understanding of React patterns, hooks, and modern development practices + +**Project Setup:** +Any React project or [SimpleFullStack React Project](../../../../sample-code/SimpleFullStack/Web/README.md) + +--- + +## 🧭 **Comprehensive Lab Steps** + +### 1️⃣ **Launch and Configure Copilot Agent** + +**Initialize Agent for React Development:** +- Open Visual Studio Code with your React project +- Access Copilot Chat: `Ctrl+Shift+I` (Windows/Linux) or `Cmd+Shift+I` (macOS) +- Click the **"Agent"** tab in the chat interface +- **Agent Mode:** Enables autonomous multi-file operations and complex feature implementation + +**🎯 Agent vs. Other Tools:** +- **Agent**: Autonomous implementation across multiple files, complete features +- **Edit**: Targeted changes to specific components +- **Ask**: Guidance and explanations + +**πŸ’° Understanding Request Usage:** +- **Agent interactions count as premium requests** with advanced processing +- **Free Plan**: 50 premium requests/month (all chat interactions) +- **Paid Plans**: Monthly premium request allowances with additional requests at $0.04 USD each +- **Model Impact**: Different AI models have varying request multipliers (GPT-4.1/4o are free on paid plans) +- **Request Reset**: Premium request counters reset monthly on the 1st +- Learn more: [GitHub Copilot Request Management](https://docs.github.com/en/copilot/managing-copilot/understanding-and-managing-copilot-usage/understanding-and-managing-requests-in-copilot) + +--- + +### 2️⃣ **Enterprise Authentication System Implementation** + +**Prompt Agent with comprehensive authentication requirements:** + +```text +@agent Create a complete enterprise authentication system for this React application that includes: + +1. **Authentication Components:** + - Login form with validation and error handling + - Registration form with password strength validation + - Password reset flow with email verification + - User profile management component + +2. **Security Features:** + - JWT token management with refresh tokens + - Role-based access control (RBAC) with roles: admin, user, viewer + - Session timeout handling + - Multi-factor authentication (MFA) support + +3. **State Management:** + - Authentication context with TypeScript types + - Persistent auth state across browser sessions + - Loading states for all authentication operations + - Comprehensive error handling + +4. **Protected Routes:** + - Higher-order component for route protection + - Role-based route access + - Redirect logic for unauthorized access + - Authentication guards for sensitive components + +5. **Testing & Documentation:** + - Unit tests for all authentication components + - Integration tests for authentication flows + - API mocking for testing + - README documentation for the auth system + +Ensure TypeScript safety throughout and follow React best practices. +``` + +**πŸ” What Agent Will Deliver:** +- Complete auth folder structure with 15+ files +- Type-safe authentication context and hooks +- Protected route components with role checking +- Comprehensive form validation and error handling +- JWT token management with automatic refresh +- Complete test suite with 90%+ coverage +- Integration with localStorage for persistence + +--- + +### 3️⃣ **Advanced Data Visualization Dashboard** + +**Challenge Agent with complex data requirements:** + +```text +@agent Build a comprehensive data visualization dashboard for this React application: + +1. **Dashboard Architecture:** + - Modular dashboard with drag-and-drop widget layout + - Real-time data updates using WebSocket connections + - Responsive grid system that adapts to screen sizes + - Widget customization and configuration options + +2. **Chart Components:** + - Line charts for time-series data with zoom and pan + - Bar charts with interactive tooltips and filtering + - Pie charts with drill-down capabilities + - Heatmap visualization for correlation data + - Custom metric cards with trend indicators + +3. **Data Management:** + - Efficient data fetching with React Query + - Data normalization and transformation + - Real-time data streaming integration + - Export functionality (PDF, Excel, CSV) + - Data filtering and search capabilities + +4. **Performance Optimization:** + - Virtual scrolling for large datasets + - Memoization for expensive chart calculations + - Lazy loading for chart libraries + - Debounced updates for real-time data + +5. **User Experience:** + - Dashboard templates and presets + - User preferences persistence + - Dark/light theme support + - Accessibility compliance (WCAG 2.1 AA) + - Mobile-responsive design + +Include comprehensive error boundaries, loading states, and TypeScript definitions. +``` + +**πŸš€ Expected Implementation:** +- 20+ component files for dashboard ecosystem +- Chart.js or D3 integration with React wrappers +- WebSocket service for real-time updates +- Custom hooks for data management +- Responsive grid system with drag-and-drop +- Performance-optimized rendering +- Complete test coverage for all components + +--- + +### 4️⃣ **Comprehensive E-commerce Product Catalog** + +**Request full e-commerce feature implementation:** + +```text +@agent Implement a full-featured e-commerce product catalog system: + +1. **Product Display Components:** + - Product grid with infinite scrolling + - Product detail pages with image galleries + - Product comparison functionality + - Recently viewed products tracking + - Product recommendations engine + +2. **Search and Filtering:** + - Advanced search with autocomplete + - Multi-faceted filtering (price, category, brand, ratings) + - Sort options (price, popularity, rating, date) + - Search result highlighting + - Saved searches and filters + +3. **Shopping Cart Integration:** + - Add to cart with quantity selection + - Cart persistence across sessions + - Cart item management (update, remove) + - Price calculations with taxes and discounts + - Checkout flow integration + +4. **Performance Features:** + - Image lazy loading and optimization + - Virtual scrolling for large product lists + - Search debouncing and caching + - Optimistic UI updates + - Progressive loading strategies + +5. **Advanced Features:** + - Product reviews and ratings system + - Wishlist functionality + - Product availability notifications + - Price drop alerts + - Social sharing integration + +6. **Testing and Quality:** + - E2E tests for complete user flows + - Component testing with React Testing Library + - Performance testing and monitoring + - Accessibility testing + - Error boundary implementation + +Use modern React patterns, TypeScript, and ensure mobile-first responsive design. +``` + +**πŸ“¦ Comprehensive Delivery:** +- 30+ component architecture +- Advanced search with Elasticsearch-style filtering +- Shopping cart with complex state management +- Image optimization and lazy loading +- Complete user flow testing +- Performance monitoring integration +- SEO-optimized product pages + +--- + +### 5️⃣ **Enterprise-Grade Form Builder System** + +**Prompt for complex form architecture:** + +```text +@agent Create an enterprise form builder system for dynamic form creation: + +1. **Form Builder Interface:** + - Drag-and-drop form designer with live preview + - Field type library (text, email, select, radio, checkbox, file upload) + - Advanced field configurations (validation rules, conditional logic) + - Form templates and presets + - Form versioning and revision history + +2. **Dynamic Form Rendering:** + - JSON schema-driven form rendering + - Conditional field display based on user inputs + - Multi-step form wizard support + - Form progress tracking and saving + - Field dependency management + +3. **Validation and Submission:** + - Real-time field validation with custom rules + - Cross-field validation dependencies + - File upload with size and type restrictions + - Form submission with success/error handling + - Draft saving and auto-recovery + +4. **Data Management:** + - Form response collection and storage + - Export responses to various formats + - Response analytics and reporting + - Data encryption for sensitive fields + - GDPR compliance features + +5. **Integration Features:** + - API integration for external data sources + - Webhook support for form submissions + - Email notifications and confirmations + - Third-party service integrations + - Custom action triggers + +6. **Enterprise Requirements:** + - Role-based form access control + - Form sharing and collaboration + - White-label customization options + - Performance optimization for large forms + - Comprehensive audit logging + +Include TypeScript definitions, comprehensive testing, and documentation. +``` + +**πŸ—οΈ System Architecture:** +- Form builder with visual designer interface +- JSON schema engine for form definitions +- Dynamic validation system with custom rules +- Multi-tenant architecture support +- Complete admin dashboard +- Analytics and reporting components +- Integration APIs and webhooks + +--- + +### 6️⃣ **Real-time Collaboration Platform** + +**Challenge Agent with real-time features:** + +```text +@agent Build a real-time collaboration platform within this React application: + +1. **Real-time Communication:** + - WebSocket-based messaging system + - Live document editing with operational transformation + - Video/audio calling integration + - Screen sharing capabilities + - Presence indicators and user activity + +2. **Collaboration Features:** + - Shared workspaces with real-time updates + - Comment and annotation system + - Version control with conflict resolution + - Live cursor tracking for multiple users + - Collaborative drawing/whiteboard functionality + +3. **Notification System:** + - Real-time push notifications + - In-app notification center + - Email notification preferences + - Mobile push notification support + - Notification batching and filtering + +4. **User Management:** + - Team creation and management + - Permission levels and access control + - User invitation and onboarding + - Activity tracking and analytics + - Integration with external identity providers + +5. **Performance and Scalability:** + - Connection state management + - Automatic reconnection handling + - Message queuing and buffering + - Efficient data synchronization + - Offline mode with sync on reconnect + +6. **Security and Privacy:** + - End-to-end encryption for sensitive communications + - Message retention policies + - User data privacy controls + - Audit logging for compliance + - Rate limiting and abuse prevention + +Implement with TypeScript, comprehensive error handling, and full test coverage. +``` + +**🌐 Real-time Architecture:** +- WebSocket service layer with reconnection logic +- Operational transformation for conflict resolution +- Event sourcing for real-time updates +- Presence management system +- Notification pipeline with multiple channels +- Security layer with encryption +- Performance optimization for large teams + +--- + +### 7️⃣ **Advanced Performance Monitoring Dashboard** + +**Request comprehensive performance system:** + +```text +@agent Create an advanced performance monitoring and analytics system: + +1. **Performance Metrics Collection:** + - Core Web Vitals monitoring (LCP, FID, CLS) + - Custom performance markers and measurements + - User interaction tracking + - Resource loading performance + - Error tracking and reporting + +2. **Analytics Dashboard:** + - Real-time performance visualizations + - Historical trend analysis + - User journey and funnel analysis + - A/B testing results dashboard + - Custom metric definitions and tracking + +3. **Monitoring Infrastructure:** + - Automatic performance data collection + - Background data aggregation + - Alert system for performance degradation + - Integration with external monitoring services + - Performance budget tracking + +4. **User Experience Insights:** + - Session replay functionality + - Heatmap generation for user interactions + - Conversion tracking and analysis + - User feedback collection system + - Performance impact on business metrics + +5. **Developer Tools:** + - Performance profiling tools + - Bundle analysis and optimization suggestions + - Code splitting effectiveness tracking + - Third-party script impact analysis + - Performance regression detection + +6. **Reporting and Alerts:** + - Automated performance reports + - Slack/email alert integration + - Custom dashboard creation + - Performance SLA monitoring + - Executive summary reports + +Include integration with popular analytics services and comprehensive documentation. +``` + +**πŸ“Š Monitoring Ecosystem:** +- Performance data collection SDK +- Real-time analytics engine +- Custom visualization components +- Alert management system +- Report generation pipeline +- Integration with monitoring services +- Performance optimization recommendations + +--- + +## πŸ’‘ **Advanced Agent Strategies for React** + +### 🎯 **Maximizing Agent Effectiveness** + +**Architecture-Level Prompts:** +```text +@agent Refactor this React application to use micro-frontend architecture with module federation, ensuring proper isolation and communication between modules. +``` + +**Full-Stack Integration:** +```text +@agent Create a complete React application with Node.js backend integration, including API design, authentication, database schema, and deployment configuration. +``` + +**Performance-Focused Requests:** +```text +@agent Optimize this React application for maximum performance, implementing all relevant optimizations including code splitting, lazy loading, memoization, and bundle optimization. +``` + +### πŸš€ **Enterprise-Grade Prompts** + +**Security Implementation:** +```text +@agent Implement comprehensive security measures for this React application including CSP headers, XSS protection, secure authentication, and security audit capabilities. +``` + +**Accessibility Compliance:** +```text +@agent Ensure this React application meets WCAG 2.1 AA standards by implementing comprehensive accessibility features, keyboard navigation, and screen reader support. +``` + +**Testing Strategy:** +```text +@agent Create a complete testing strategy for this React application including unit tests, integration tests, E2E tests, visual regression tests, and performance tests. +``` + +--- + +## πŸ”§ **Agent vs. Other Copilot Tools in React Development** + +### πŸ€– **Use Agent For:** +- **Complete feature implementation** across multiple components +- **Architectural changes** that affect many files +- **New system creation** (auth, forms, dashboards) +- **Complex integrations** with external services +- **Performance optimization** across the entire application +- **Testing strategy implementation** with comprehensive coverage +- **Full-stack feature development** with backend integration + +### ✏️ **Use Edit For:** +- **Targeted component modifications** within a single file +- **Specific bug fixes** or small enhancements +- **Code refactoring** of individual components +- **Style updates** or minor UI changes +- **Performance tweaks** to specific components + +### πŸ€” **Use Ask For:** +- **Understanding** complex React patterns +- **Guidance** on architectural decisions +- **Debugging help** for specific issues +- **Learning** about new React features +- **Code review** and best practice recommendations + +--- + +## ⚠️ **Enterprise Considerations for Agent Usage** + +### πŸ’³ **Request Management and Budgeting** +- **Monitor premium request usage** through GitHub's usage dashboard +- **Set spending limits** for additional premium requests beyond plan allowances +- **Choose appropriate AI models** - GPT-4.1/4o are included in paid plans, advanced models consume premium requests +- **Plan complex Agent tasks** to maximize value from premium requests +- **Track team usage** to optimize request allocation across developers + +### πŸ”’ **Security and Compliance** +- **Review all generated code** for security vulnerabilities +- **Validate API integrations** and authentication flows +- **Ensure data privacy** compliance in generated components +- **Test generated security measures** thoroughly +- **Document security decisions** made by Agent + +### πŸ—οΈ **Architecture and Maintainability** +- **Review architectural decisions** proposed by Agent +- **Ensure code quality standards** are maintained +- **Validate testing coverage** and quality +- **Check performance implications** of generated code +- **Document complex implementations** for team understanding + +### πŸ‘₯ **Team Integration** +- **Establish Agent usage guidelines** for consistent results +- **Create prompt templates** for common scenarios +- **Review and approve** Agent-generated code in team workflows +- **Share successful patterns** across development teams +- **Train team members** on effective Agent interaction + +--- + +## βœ… **Comprehensive Summary** + +By completing this advanced lab, you've mastered: + +### 🎯 **Core Agent Capabilities** +- **Autonomous feature implementation** across multiple files and components +- **Complex system architecture** creation with proper separation of concerns +- **Enterprise-grade feature development** with security and performance considerations +- **Integration capabilities** with external services and APIs +- **Comprehensive testing strategy** implementation + +### πŸš€ **Advanced React Patterns** +- **Authentication and authorization** systems with role-based access +- **Real-time applications** with WebSocket integration +- **Performance monitoring** and optimization strategies +- **Data visualization** and dashboard development +- **Form builders** and dynamic content systems +- **Collaboration platforms** with real-time features + +### πŸ—οΈ **Enterprise Development Skills** +- **Large-scale application architecture** planning and implementation +- **Security-first development** approach with Agent assistance +- **Performance optimization** at scale +- **Testing strategy** for complex applications +- **Team collaboration** and code review processes + +### πŸ’Ό **Business Value Creation** +- **Accelerated development cycles** through Agent automation +- **Consistent code quality** across large teams +- **Reduced technical debt** through proper architectural patterns +- **Enhanced security posture** through systematic implementation +- **Improved maintainability** of complex React applications + +--- + +## 🎯 **Next Steps for Enterprise Teams** + +1. **Establish Agent Guidelines** - Create team standards for Agent usage +2. **Build Prompt Libraries** - Develop reusable prompts for common patterns +3. **Integration Workflows** - Incorporate Agent into CI/CD pipelines +4. **Training Programs** - Educate teams on advanced Agent techniques +5. **Quality Assurance** - Implement review processes for Agent-generated code +6. **Performance Monitoring** - Track the impact of Agent-assisted development +7. **Security Reviews** - Regular audits of Agent-generated security implementations +8. **Knowledge Sharing** - Document successful Agent patterns and outcomes + +--- + +**πŸ† Agent Mastery Achievement:** You can now leverage GitHub Copilot Agent to autonomously implement enterprise-grade React applications with complex features, robust architecture, and comprehensive testing strategies. + +--- + +Β© Copyright Neudeisc 2025 diff --git a/lessons/02-Getting-Started/react/2.4-exploring-copilot-inline-(react).md b/lessons/02-Getting-Started/react/2.4-exploring-copilot-inline-(react).md new file mode 100644 index 0000000..93e225a --- /dev/null +++ b/lessons/02-Getting-Started/react/2.4-exploring-copilot-inline-(react).md @@ -0,0 +1,285 @@ +# πŸ§ͺ Lab: Exploring GitHub Copilot Inline with React + +--- + +## πŸ“ Overview + +**Goal:** +Master GitHub Copilot's inline features for rapid React development. Learn to use inline chat, autocomplete, and ghost text to make quick, context-aware modifications to React components, hooks, and utilities without breaking your development flow. + +**Estimated Duration:** 20-25 minutes + +**Audience:** +React developers looking to boost productivity through fast, iterative coding and seamless inline assistance. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot (with inline chat feature) +- Familiarity with React, TypeScript, and modern React patterns + +**Project Folder:** +[SimpleFullStack React Project](../../../../sample-code/SimpleFullStack/Web/README.md) + +--- + +## 🎯 What Makes Copilot Inline Powerful for React? + +**Inline Features Overview:** +- **Inline Chat (`Ctrl+I` / `Cmd+I`)**: Natural language prompts for targeted code generation +- **Ghost Text**: Predictive code suggestions that appear as you type +- **Tab Completion**: Accept suggestions with Tab key for rapid development +- **Context Awareness**: Understands your React component structure and TypeScript types + +**Key Benefits for React Development:** +- ⚑ **Rapid Iterations** - Make quick changes without context switching +- 🎯 **Component-Aware** - Understands React patterns, hooks, and JSX structure +- πŸ”„ **Seamless Workflow** - No interruption to your coding flow +- πŸ“ **TypeScript Integration** - Leverages type information for better suggestions + +--- + +## πŸ› οΈ How to Use Copilot Inline with React + +### **Inline Chat (`Ctrl+I` / `Cmd+I`)** +1. **Position your cursor** where you want to generate or modify code +2. **Press the keyboard shortcut** to open inline chat +3. **Type your instruction** in natural language +4. **Review and accept** the generated code + +### **Ghost Text Suggestions** +1. **Start typing** React code patterns +2. **Watch for gray text** suggestions that appear +3. **Press Tab** to accept, or continue typing to ignore +4. **Use arrow keys** to navigate through multiple suggestions + +### **React-Specific Inline Examples** + +**Component Generation:** +```text +Cursor position: Empty file +Inline prompt: "Create a React functional component with TypeScript" +``` + +**Hook Implementation:** +```text +Cursor position: Inside component +Inline prompt: "Add useState for managing form data" +``` + +**JSX Enhancement:** +```text +Cursor position: Inside return statement +Inline prompt: "Add loading spinner with conditional rendering" +``` + +**Event Handler Creation:** +```text +Cursor position: After other methods +Inline prompt: "Add click handler that validates and submits form" +``` + +**Styling and Props:** +```text +Cursor position: Component props +Inline prompt: "Add TypeScript interface for component props" +``` + +--- + +## 🧭 Lab Steps + +### 1️⃣ Open Target File + +- Navigate to the `sample-code/SimpleFullStack/Web/src/ui/components/global_snackbar` folder in Visual Studio Code. +- Open the `GlobalSnackbar.tsx` file for editing. + +--- + +### 2️⃣ Rename Component Method + +- Locate the existing `handleClose` method in the `GlobalSnackbar` component. +- Place your cursor on `handleClose`. +- Press `Ctrl+I` (Windows/Linux) or `Cmd+I` (Mac) to open an inline Copilot prompt. +- Enter the following prompt: + + ```plaintext + Rename symbol `handleClose` to `dismissSnackbar` + ``` + + Press Enter. Review the rename suggestions and apply them. + +--- + +### 3️⃣ Add a Custom Animation Method + +- With the same file still open, place your cursor after the `TransitionUp` function and before the `GlobalSnackbar` component. +- Open a new inline Copilot prompt. +- Enter the following prompt: + + ```plaintext + Add a new transition function for sliding from the left + ``` + + Accept the generated code suggestion, which should look similar to this: + + ```typescript + function TransitionLeft(props: SlideProps) { + return ; + } + ``` + +--- + +### 4️⃣ Create a Duration Helper + +- Place your cursor inside the `GlobalSnackbar` component, after the destructured hook variables and before the `dismissSnackbar` method. +- In a new inline prompt, enter: + + ```plaintext + Add a function to determine duration based on severity + ``` + + - Accept the method stub and close the prompt. + - Delete the return statement if you want to see Copilot's ghost text suggestions. + - Observe the "ghost text" Copilot suggests in gray text. + - Press `Tab` to accept the suggestion, or arrow keys to navigate through the suggestion. + +--- + +### 5️⃣ Enhance the Snackbar Styling + +- Locate the `` component in the return statement. +- Place your cursor inside the `sx` prop of the Alert component. +- Open a new inline Copilot prompt (`Ctrl+I`) and enter: + + ```plaintext + Enhance styling with dynamic colors based on severity + ``` + + Accept the Copilot suggestion to improve the styling. + +--- + +### 6️⃣ Add Accessibility Features + +- Locate the `` component in the return statement. +- Place your cursor at the end of the props, before the closing `>`. +- Open an inline prompt and enter: + + ```plaintext + Add accessibility attributes + ``` + + Review and accept Copilot's suggested accessibility enhancements. + +--- + +### 7️⃣ Generate a New Hook + +- Open a new file called `useSnackbarTimer.ts` in the same directory. +- With your cursor at the beginning of the empty file, open an inline prompt and enter: + + ```plaintext + Create a custom hook that manages snackbar display timing + ``` + + Accept the hook implementation that Copilot suggests, which should include imports, type definitions, and the hook logic. + +--- + +### 8️⃣ Integrate the Hook + +- Return to `GlobalSnackbar.tsx`. +- Add an import for the new hook at the top of the file. +- Place your cursor inside the component, after the useGlobalSnackbar hook. +- Open an inline prompt and enter: + + ```plaintext + Use the useSnackbarTimer hook + ``` + + Accept the suggestion to integrate the hook into the component. + +--- + +## πŸ’‘ Advanced Inline Techniques for React + +### **Progressive Component Building** +```text +1. Start with basic component structure: "Create functional component" +2. Add state management: "Add useState for user data" +3. Implement handlers: "Add form submission handler" +4. Enhance UI: "Add loading states and error handling" +5. Optimize: "Add useMemo for expensive calculations" +``` + +### **Hook Development Workflow** +```text +1. Define hook signature: "Create custom hook for API data fetching" +2. Add state logic: "Add loading, data, and error states" +3. Implement effects: "Add useEffect for data fetching" +4. Add utilities: "Add refresh and cancel functions" +``` + +### **TypeScript Integration** +```text +- Use existing types: "Add props using the User interface" +- Generate new types: "Create TypeScript interface for this component" +- Enhance type safety: "Add proper typing for this event handler" +``` + +--- + +## πŸ“š Best Practices for React Development + +### **Inline Chat Strategies** +- **Be Specific**: "Add error boundary with fallback UI" vs "Add error handling" +- **Reference Context**: "Add loading state consistent with other components" +- **Specify Patterns**: "Use React Query for this API call" +- **Include Constraints**: "Add validation using Yup schema" + +### **Component Enhancement Tips** +- **Position Strategically**: Place cursor exactly where code should be inserted +- **Leverage TypeScript**: Maintain type definitions for better suggestions +- **Chain Small Changes**: Build complexity through incremental improvements +- **Review Integration**: Verify new code works with existing component logic + +### **Performance Considerations** +- **Use Inline for**: Small functions, JSX elements, prop additions, simple hooks +- **Avoid for**: Large refactoring, complex state logic, architectural changes +- **Optimize Flow**: Accept ghost text quickly, use inline chat for specific needs + +--- + +## βœ… Summary + +By completing this lab, you've mastered: + +### **Core Inline Features** +- **Inline Chat (`Ctrl+I`)** for targeted React component modifications +- **Ghost Text Acceptance** for rapid code completion +- **Context-Aware Suggestions** that understand React patterns and TypeScript + +### **React Development Skills** +- **Progressive Component Building** through incremental inline improvements +- **Custom Hook Development** using step-by-step inline prompts +- **TypeScript Integration** for type-safe component development +- **Accessibility Enhancement** with minimal inline effort + +### **Workflow Optimization** +- **Strategic Cursor Positioning** for accurate code insertion +- **Efficient Prompt Techniques** for specific React outcomes +- **Seamless Development Flow** without context switching + +### **Best Practices Mastery** +- **When to Use Inline** vs other Copilot tools +- **React-Specific Prompting** strategies for components, hooks, and JSX +- **Performance Considerations** for optimal development speed + +GitHub Copilot Inline transforms your React development by providing instant, context-aware assistance directly within your coding flow, enabling rapid iteration and enhancement of components, hooks, and utilities. + +--- + +Β© Copyright Neudeisc 2025 diff --git a/lessons/03-Context-and-Prompt/3.1-managing-context-effectively.md b/lessons/03-Context-and-Prompt/3.1-managing-context-effectively.md new file mode 100644 index 0000000..eab7211 --- /dev/null +++ b/lessons/03-Context-and-Prompt/3.1-managing-context-effectively.md @@ -0,0 +1,306 @@ +# 🎯 Lesson 3.1: Managing Context Effectively with GitHub Copilot + +## Overview + +**Goal:** +Master the art of providing effective context to GitHub Copilot to get more accurate, relevant, and useful AI-generated code suggestions and responses. + +**Estimated Duration:** 20-25 minutes + +**Audience:** +All developers using GitHub Copilot who want to maximize AI assistance quality + +**Prerequisites:** +- GitHub Copilot installed and activated +- VS Code with latest Copilot extensions +- Basic familiarity with GitHub Copilot Chat + +--- + +## 🧠 Understanding Context in GitHub Copilot + +GitHub Copilot uses **context** - the surrounding information about your code, project, and intent - to generate relevant suggestions. The more precise and relevant context you provide, the better the AI responses become. + +### Types of Context + +1. **Implicit Context**: Automatically gathered by Copilot + - Currently open files and active editor content + - File structure and project organization + - Git history and recent changes + - Language and framework detection + +2. **Explicit Context**: Manually provided by you + - File and folder references using `#` symbols + - Codebase search with `#codebase` + - Web content with `#fetch` and `#githubRepo` + - Symbol references and selections + +--- + +## πŸ“ Adding Files and Folders as Context + +### Using #-mentions + +The most direct way to provide context is by using `#` followed by file, folder, or symbol names: + +```text +# Reference specific files +"Update the user authentication logic in #src/auth/login.js" + +# Reference folders +"Add error handling consistent with patterns in #utils/" + +# Reference symbols (functions, classes, interfaces) +"Refactor the #UserRepository class to use async/await" +``` + +### Drag and Drop Context + +You can also drag and drop files or folders from: +- **Explorer view** into the chat +- **Search results** into the chat +- **Editor tabs** into the chat + +### Add Context Button + +Use the **Add Context** button in Chat view: +1. Click **Add Context** (+) button +2. Select **Files & Folders** or **Symbols** +3. Browse and select relevant items + +--- + +## πŸ” Codebase Search with #codebase + +Instead of manually adding files, let VS Code automatically find relevant code: + +```text +# Let Copilot search your entire codebase +"Explain how authentication works in #codebase" + +# Find configuration patterns +"Where is the database connection string configured? #codebase" + +# Discover implementation patterns +"Add a new API route for updating user profiles #codebase" +``` + +**πŸ’‘ Pro Tip:** Enable the `github.copilot.chat.codesearch.enabled` setting for enhanced codebase search results. + +--- + +## 🌐 Referencing External Content + +### Web Content with #fetch + +Reference specific web pages for up-to-date documentation: + +```text +# Get latest framework documentation +"Update my React app to use React 19 features #fetch https://react.dev/blog/2024/12/05/react-19" + +# Reference specific API documentation +"Implement OAuth 2.0 flow following #fetch https://auth0.com/docs/get-started/authentication-and-authorization-flow" + +# Get latest release notes +"What are the new features in VS Code 1.100? #fetch https://code.visualstudio.com/updates/v1_100" +``` + +### GitHub Repository References + +Reference code patterns from other repositories: + +```text +# Learn from established patterns +"How does routing work in Next.js #githubRepo vercel/next.js" + +# Follow testing patterns +"Add unit tests using the same setup as #githubRepo microsoft/typescript" + +# Implement consistent code style +"Review my component structure against #githubRepo react/react" +``` + +--- + +## πŸ› οΈ Using Tools and MCP Servers + +Reference tools directly in your prompts: + +```text +# Use GitHub MCP server +"What are my open issues #github-mcp" +"Implement a fix for issue #123 #github-mcp" + +# Use database tools +"Generate an API endpoint for weather data from #postgres" +"What's the schema for the users table #postgres" +``` + +--- + +## πŸ‘₯ Chat Participants with @-mentions + +Use domain experts for specialized help: + +```text +# Visual studio help +" @vs how do i setup my azure authentication" + +# VS Code expertise +"@vscode how to configure custom keybindings for Copilot" + +# Terminal commands +"@terminal show me the largest files in the current directory" + +# Workspace operations +"@workspace explain the project structure and main dependencies" +``` + +--- + +## 🎯 Best Practices for Context Management + +### 1. Be Specific and Intentional + +**❌ Poor Context:** +```text +"Fix this function" +``` + +**βœ… Good Context:** +```text +"Fix the #getUserById function in #src/services/userService.js to handle null responses and add proper TypeScript types" +``` + +### 2. Layer Your Context + +Combine multiple context types for comprehensive understanding: + +```text +"Create a new payment processing component following the patterns in #components/checkout/ and #utils/api.js, using the Stripe integration documented at #fetch https://docs.stripe.com/payments/quickstart" +``` + +### 3. Use Progressive Context + +Start with broad context, then narrow down: + +```text +# Initial prompt +"Review the authentication system in #codebase" + +# Follow-up with specific context +"Now focus on the JWT token validation in #src/auth/middleware.js and suggest security improvements" +``` + +### 4. Clean Up Context When Switching Topics + +Start a new chat session (βŒƒL / Ctrl+L) when moving to unrelated topics to avoid context pollution. + +--- + +## πŸ§ͺ Lab Exercise: Context Mastery + +### Exercise 1: File Reference Practice + +1. Open your project in VS Code +2. Start a new chat session +3. Try these context patterns: + +```text +# Pattern 1: Specific file reference +"Explain the main function in #src/app.js" + +# Pattern 2: Multiple file reference +"Compare the user models in #src/models/user.js and #src/types/user.ts" + +# Pattern 3: Folder-level context +"Review all components in #src/components/ for accessibility issues" +``` + +### Exercise 2: Codebase Search + +```text +# Search for patterns +"Find all database queries in #codebase and check for SQL injection vulnerabilities" + +# Discover architecture +"Show me how error handling is implemented across #codebase" +``` + +### Exercise 3: External References + +```text +# Get latest best practices +"Update my React component props validation using #fetch https://react.dev/reference/react/Component#static-proptypes" + +# Learn from other projects +"Improve my Express.js error handling following patterns from #githubRepo expressjs/express" +``` + +--- + +## πŸ“Š Context Quality Indicators + +Watch for these signs of effective context: + +**βœ… Good Context Indicators:** +- Copilot references specific files/functions you mentioned +- Generated code follows your project's patterns and conventions +- Suggestions are immediately relevant to your current task +- Code uses the correct imports and dependencies + +**❌ Poor Context Indicators:** +- Generic code that doesn't match your project structure +- Incorrect or outdated API usage +- Code that doesn't compile with your dependencies +- Suggestions that ignore your established patterns + +--- + +## πŸš€ Advanced Context Techniques + +### Workspace Context Management + +```text +# Set project-wide context +"@workspace I'm working on an e-commerce platform using Node.js, Express, PostgreSQL, and React. Keep this context for all suggestions." + +# Reference workspace configuration +"@workspace what testing framework is configured in this project?" +``` + +### Context Layering for Complex Tasks + +```text +# Layer 1: Project context +"I'm building a real-time chat application #codebase" + +# Layer 2: Specific technology context +"Using Socket.io patterns from #githubRepo socketio/socket.io" + +# Layer 3: Implementation context +"Focus on the message handling in #src/chat/messageHandler.js" +``` + +--- + +## 🎯 Summary + +Effective context management is the key to unlocking GitHub Copilot's full potential. By mastering: + +- **File and symbol references** with `#` syntax +- **Codebase search** with `#codebase` +- **External content** with `#fetch` and `#githubRepo` +- **Specialized participants** with `@` mentions +- **Progressive context building** for complex tasks + +You'll transform GitHub Copilot from a simple code completion tool into a powerful AI pair programmer that understands your project, follows your patterns, and delivers precisely what you need. + +--- + +**Next:** [3.2 Custom Instructions and Workspace Configuration β†’](3.2-custom-instructions-workspace.md) + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/03-Context-and-Prompt/3.2-custom-instructions-workspace.md b/lessons/03-Context-and-Prompt/3.2-custom-instructions-workspace.md new file mode 100644 index 0000000..675f6e6 --- /dev/null +++ b/lessons/03-Context-and-Prompt/3.2-custom-instructions-workspace.md @@ -0,0 +1,609 @@ +# πŸ› οΈ Lesson 3.2: Custom Instructions and Workspace Configuration + +## Overview + +**Goal:** +Learn how to configure GitHub Copilot with custom instructions and workspace settings to align AI assistance with your coding style, team standards, and project requirements. + +**Estimated Duration:** 25-30 minutes + +**Audience:** +Developers, team leads, and engineering managers who want to standardize AI assistance across their development environments + +**Prerequisites:** +- GitHub Copilot installed and activated +- VS Code with latest Copilot extensions +- Understanding of your team's coding standards + +--- + +## 🎯 Understanding Custom Instructions + +Custom instructions are persistent guidelines that automatically influence every GitHub Copilot interaction, helping you: + +- **Maintain consistency** across all AI-generated code +- **Enforce team coding standards** automatically +- **Reduce repetitive context** in chat prompts +- **Align AI behavior** with your project architecture +- **Save time** by avoiding repeated explanations + +--- + +## πŸ“ Types of Custom Instructions + +### 1. **Instruction Files (.copilot-instructions.md)** +- **Workspace-specific** - stored in your project repository +- **Version controlled** - shared with your team +- **Markdown format** - human-readable and maintainable +- **Automatically applied** to all Copilot interactions + +### 2. **VS Code Settings** +- **User-level** - apply globally across all projects +- **Workspace-level** - specific to current project +- **JSON format** - programmatic configuration +- **IDE integration** - leverages VS Code features + +--- + +## πŸ“„ Creating Instruction Files + +### Basic Instruction File Setup + +Create `.copilot-instructions.md` in your project root: + +```markdown +# GitHub Copilot Instructions + +## Project Overview +This is a modern e-commerce platform built with: +- **Frontend**: React 18 with TypeScript +- **Backend**: Node.js with Express and PostgreSQL +- **Architecture**: Clean Architecture with Domain-Driven Design +- **Testing**: Jest for unit tests, Cypress for e2e tests + +## Coding Standards + +### TypeScript Guidelines +- Use strict TypeScript types for all functions and variables +- Prefer interfaces over type aliases for object shapes +- Use meaningful names that clearly indicate purpose +- Include comprehensive JSDoc comments for public APIs + +### React Best Practices +- Use functional components with hooks exclusively +- Implement proper error boundaries for component trees +- Use React.memo for expensive components +- Follow the compound component pattern for complex UI + +### API Development +- Follow RESTful conventions with proper HTTP status codes +- Implement comprehensive input validation and sanitization +- Use consistent error response formats with correlation IDs +- Include proper authentication and authorization checks + +## Security Requirements + +### Never Include +- Hardcoded secrets, API keys, or passwords +- Sensitive data in logs or error messages +- Direct database queries without parameterization +- User input without proper validation + +### Always Include +- Environment variable configuration +- Input validation and sanitization +- Proper error handling with correlation IDs +- Security headers and CORS configuration + +## Testing Standards +- Write unit tests for all business logic (minimum 80% coverage) +- Include both positive and negative test cases +- Mock external dependencies appropriately +- Use descriptive test names that explain the scenario +``` + +### Advanced Language-Specific Instructions + +Create specialized instruction files for different contexts: + +**`.copilot-instructions-react.md`:** +```markdown +# React Component Guidelines + +## Component Structure +```typescript +interface ComponentProps { + readonly data: DataType; + readonly onAction?: (item: DataType) => void; + readonly className?: string; +} + +export const ComponentName: React.FC = ({ + data, + onAction, + className +}) => { + // Implementation +}; +``` + +## State Management +- Use useState for local component state +- Use useReducer for complex state logic +- Implement custom hooks for reusable stateful logic +- Use React Query for server state management + +## Performance +- Wrap expensive calculations in useMemo +- Use useCallback for functions passed to child components +- Implement lazy loading for heavy components +- Use React.Suspense for code splitting +``` + +--- + +## βš™οΈ VS Code Settings Configuration + +### User Settings (Global) + +Open VS Code settings (`Ctrl/Cmd + ,`) and configure: + +```json +{ + "github.copilot.enable": true, + "github.copilot.chat.useInstructionFiles": true, + "github.copilot.chat.codesearch.enabled": true, + "github.copilot.editor.enableAutoCompletions": true, + "github.copilot.chat.followups": "on" +} +``` + +### Task-Specific Instructions (User-Level) + +Configure custom instructions for specific GitHub Copilot tasks: + +```json +{ + "github.copilot.chat.codeGeneration.instructions": "Follow our TypeScript coding standards: use strict types, meaningful variable names, and include comprehensive JSDoc comments for all public functions. Implement proper error handling with try-catch blocks and correlation IDs.", + + "github.copilot.chat.testGeneration.instructions": "Generate Jest unit tests with descriptive test names that explain the scenario. Include both positive and negative test cases. Use the AAA pattern (Arrange-Act-Assert). Mock external dependencies with proper type safety.", + + "github.copilot.chat.reviewSelection.instructions": "Focus on security vulnerabilities, performance issues, maintainability concerns, and adherence to our team's coding standards. Check for proper error handling, input validation, and TypeScript type safety.", + + "github.copilot.chat.commitMessageGeneration.instructions": "Use conventional commit format: type(scope): description. Types: feat, fix, docs, style, refactor, test, chore. Keep messages under 72 characters. Include ticket numbers when applicable.", + + "github.copilot.chat.pullRequestDescriptionGeneration.instructions": "Include: What changed, Why it changed, How to test, Any breaking changes, Screenshots for UI changes. Use our PR template format with checkboxes for review criteria." +} +``` + +### Workspace Settings (Project-Specific) + +Create `.vscode/settings.json` in your project: + +```json +{ + "github.copilot.chat.useInstructionFiles": true, + "github.copilot.enable": { + "*": true, + "yaml": false, + "plaintext": false + }, + + // Project-specific task instructions + "github.copilot.chat.codeGeneration.instructions": "This is an e-commerce platform using React 18, TypeScript, Node.js, and PostgreSQL. Follow Clean Architecture principles. Use our custom hooks for API calls and state management patterns from /src/hooks/.", + + "github.copilot.chat.testGeneration.instructions": "Use React Testing Library for component tests. Test user interactions, not implementation details. Mock API calls using MSW. Follow the test patterns in /src/__tests__/utils/testHelpers.js", + + "github.copilot.chat.reviewSelection.instructions": "Check for e-commerce specific security: payment data handling, user authentication, input sanitization. Ensure GDPR compliance for user data. Verify proper error boundaries for React components." +} +``` + +--- + +## πŸš€ Demo-Ready Instruction Examples + +### Enterprise E-commerce Platform + +```markdown +# E-commerce Platform Instructions + +## Architecture Context +- Microservices architecture with API Gateway +- Event-driven communication using message queues +- CQRS pattern for read/write operations +- Clean Architecture with dependency injection + +## Technology Stack +- **API**: Node.js + Express + TypeScript +- **Database**: PostgreSQL with Prisma ORM +- **Frontend**: React 18 + Next.js + TailwindCSS +- **Testing**: Jest + Testing Library + Cypress +- **Infrastructure**: Docker + Kubernetes + AWS + +## Code Generation Preferences + +### API Endpoints +```typescript +// Always include proper error handling +app.post('/api/users', async (req, res) => { + try { + const validatedData = userSchema.parse(req.body); + const user = await userService.createUser(validatedData); + res.status(201).json({ data: user, success: true }); + } catch (error) { + logger.error('Failed to create user', { error, correlationId: req.id }); + res.status(400).json({ + error: 'Invalid user data', + correlationId: req.id + }); + } +}); +``` + +### React Components +```typescript +// Use this exact pattern for all components +interface Props { + readonly data: DataType; + readonly onUpdate?: (data: DataType) => Promise; + readonly isLoading?: boolean; +} + +export const ComponentName: React.FC = ({ + data, + onUpdate, + isLoading = false +}) => { + // Implementation with proper error boundaries +}; +``` + +## Performance Requirements +- All API responses must be under 200ms +- Implement caching for frequently accessed data +- Use database indexing for common query patterns +- Include monitoring and alerting for critical paths +``` + +### Banking Application Security-First + +```markdown +# Banking Application Security Instructions + +## Security-First Development +Every generated code must include: +- Input validation using Joi or Zod schemas +- SQL injection prevention with parameterized queries +- Rate limiting on all public endpoints +- Comprehensive audit logging with correlation IDs + +## Compliance Requirements +- PCI DSS compliance for payment processing +- SOX compliance for financial reporting +- GDPR compliance for user data handling +- Never log sensitive financial information + +## Error Handling Standards +```typescript +// Standard error response format +interface ApiError { + readonly error: string; + readonly code: string; + readonly correlationId: string; + readonly timestamp: string; +} + +// Never expose internal system details +catch (error) { + logger.error('Operation failed', { + error: error.message, + correlationId, + userId: req.user.id + }); + + return res.status(500).json({ + error: 'An internal error occurred', + code: 'INTERNAL_ERROR', + correlationId, + timestamp: new Date().toISOString() + }); +} +``` +``` + +--- + +## 🎯 Task-Specific Instructions in Action + +### Code Generation Instructions + +When you generate code, Copilot will automatically apply your `codeGeneration.instructions`: + +```text +# User prompt +"Create a user authentication service" + +# With instructions applied, Copilot generates: +``` +```typescript +/** + * User authentication service with JWT token management + * @module UserAuthService + */ +export class UserAuthService { + /** + * Authenticates user credentials and returns JWT token + * @param email - User email address + * @param password - User password + * @returns Promise resolving to authentication result + * @throws {AuthenticationError} When credentials are invalid + */ + async authenticateUser( + email: string, + password: string + ): Promise { + const correlationId = generateCorrelationId(); + + try { + // Implementation follows your standards automatically + const user = await this.validateCredentials(email, password); + const token = this.generateJWTToken(user); + + logger.info('User authenticated successfully', { + userId: user.id, + correlationId + }); + + return { user, token, correlationId }; + } catch (error) { + logger.error('Authentication failed', { + email, + error: error.message, + correlationId + }); + throw new AuthenticationError('Invalid credentials', correlationId); + } + } +} +``` + +### Test Generation Instructions + +When generating tests, your `testGeneration.instructions` ensure consistent patterns: + +```text +# User prompt +"Generate tests for the UserAuthService" + +# Copilot generates following your test standards: +``` +```typescript +describe('UserAuthService', () => { + describe('authenticateUser', () => { + // Arrange + let authService: UserAuthService; + let mockUserRepository: jest.Mocked; + + beforeEach(() => { + mockUserRepository = createMockUserRepository(); + authService = new UserAuthService(mockUserRepository); + }); + + it('should successfully authenticate valid user credentials', async () => { + // Arrange + const email = 'user@example.com'; + const password = 'validPassword123'; + const mockUser = createMockUser({ email }); + mockUserRepository.findByEmail.mockResolvedValue(mockUser); + + // Act + const result = await authService.authenticateUser(email, password); + + // Assert + expect(result.user).toEqual(mockUser); + expect(result.token).toBeDefined(); + expect(result.correlationId).toBeDefined(); + }); + + it('should throw AuthenticationError for invalid credentials', async () => { + // Arrange + const email = 'user@example.com'; + const password = 'invalidPassword'; + mockUserRepository.findByEmail.mockResolvedValue(null); + + // Act & Assert + await expect( + authService.authenticateUser(email, password) + ).rejects.toThrow(AuthenticationError); + }); + }); +}); +``` + +### Commit Message Generation + +Your `commitMessageGeneration.instructions` ensure consistent commit formatting: + +```text +# Before (generic): +"Updated user service" + +# After (with instructions): +"feat(auth): add JWT token rotation for user authentication + +- Implement refresh token mechanism +- Add token expiration handling +- Include correlation ID logging + +Fixes: AUTH-123" +``` + +--- + +## πŸ§ͺ Practical Lab Exercises + +### Exercise 1: Task-Specific Configuration + +1. **Configure task-specific instructions** in your VS Code settings: + +```json +{ + "github.copilot.chat.codeGeneration.instructions": "Use camelCase for variables (userProfile, orderHistory), camelCase with verb prefix for functions (getUserById, validateEmail), UPPER_SNAKE_CASE for constants (API_BASE_URL), and PascalCase for components (UserProfile). Include JSDoc for all functions.", + + "github.copilot.chat.testGeneration.instructions": "Generate tests with descriptive names that explain the scenario. Use Jest with React Testing Library. Mock external dependencies. Include both success and error cases.", + + "github.copilot.chat.commitMessageGeneration.instructions": "Use conventional commits: type(scope): description. Keep under 72 characters. Include ticket numbers like PROJ-123." +} +``` + +2. **Test the instructions** with different tasks: + +**Code Generation:** +```text +"Create a user service function that fetches user profile data from an API" +``` + +**Test Generation:** +```text +"Generate tests for the getUserProfile function" +``` + +**Commit Message:** +```text +Select your code changes and ask Copilot to generate a commit message +``` + +Observe how each task follows the specific instructions you configured. + +### Exercise 2: Technology Stack Alignment + +1. **Configure stack-specific instructions**: + +```markdown +# React + TypeScript + Material-UI Stack + +## Component Patterns +- Use Material-UI components exclusively +- Implement proper TypeScript prop interfaces +- Include error boundaries for all page-level components +- Use React Query for all API operations + +## State Management +- Local state: useState hook +- Complex state: useReducer hook +- Server state: React Query +- Global state: Zustand store +``` + +2. **Test with component generation**: +```text +"Create a user profile form component with validation" +``` + +### Exercise 3: Multi-Language Project + +For projects with multiple languages, create specific instruction files: + +- `.copilot-instructions-typescript.md` +- `.copilot-instructions-python.md` +- `.copilot-instructions-sql.md` + +Each file should contain language-specific standards and patterns. + +--- + +## πŸ“Š Monitoring Instruction Effectiveness + +### Quality Indicators + +**βœ… Instructions Working Well:** +- Generated code follows your naming conventions +- Proper error handling is automatically included +- Code structure matches your architectural patterns +- Dependencies and imports are correctly used + +**❌ Instructions Need Improvement:** +- Generated code ignores your standards +- Security patterns are inconsistent +- Code doesn't compile with your dependencies +- Architectural patterns are not followed + +### Iterative Improvement + +1. **Monitor** generated code quality over time +2. **Identify** common issues or deviations +3. **Update** instruction files to address gaps +4. **Test** changes with representative prompts +5. **Share** improvements with your team + +--- + +## πŸ”§ Advanced Configuration Techniques + +### Conditional Instructions + +Use conditional logic in instruction files: + +```markdown +## Environment-Specific Guidelines + +### Development Environment +- Include detailed logging and debug information +- Use development database connections +- Enable verbose error messages + +### Production Environment +- Minimize logging for performance +- Use connection pooling +- Return generic error messages +``` + +### Integration with CI/CD + +Include instruction files in your CI/CD pipeline: + +```yaml +# .github/workflows/copilot-standards.yml +name: Copilot Standards Check +on: [pull_request] + +jobs: + validate-instructions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Validate Copilot Instructions + run: | + # Check that instruction files are present + test -f .copilot-instructions.md + # Validate markdown format + markdownlint .copilot-instructions*.md +``` + +--- + +## 🎯 Summary + +Custom instructions transform GitHub Copilot from a general-purpose AI assistant into a team-aware, project-specific developer that understands your standards, follows your patterns, and generates code that fits seamlessly into your codebase. + +**Key Benefits:** +- **Consistency** across all AI-generated code +- **Team alignment** through shared instruction files +- **Reduced onboarding** time for new developers +- **Improved code quality** through enforced standards +- **Enhanced productivity** by eliminating repetitive context + +**Best Practices:** +- Start simple and iterate based on results +- Include concrete examples in your instructions +- Version control your instruction files +- Regularly review and update standards +- Share successful patterns with your team + +--- + +**Next:** [3.3 Advanced Prompt Engineering Techniques β†’](3.3-prompt-engineering-advanced.md) + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/03-Context-and-Prompt/3.3-prompt-engineering-advanced.md b/lessons/03-Context-and-Prompt/3.3-prompt-engineering-advanced.md new file mode 100644 index 0000000..a2e1b3a --- /dev/null +++ b/lessons/03-Context-and-Prompt/3.3-prompt-engineering-advanced.md @@ -0,0 +1,590 @@ +# 🎯 Lesson 3.3: Advanced Prompt Engineering for GitHub Copilot + +## Overview + +**Goal:** +Master prompt engineering techniques to maximize GitHub Copilot's effectiveness across development scenarios, from basic code completion to complex problem-solving using the latest official best practices. + +**Estimated Duration:** 30-35 minutes + +**Audience:** +Developers at all levels who want to write effective prompts for GitHub Copilot code completion and Copilot Chat + +**Prerequisites:** +- GitHub Copilot installed and activated +- Basic understanding of GitHub Copilot functionality +- Familiarity with your development environment + +**Official Resources:** +- [GitHub Copilot Documentation](https://docs.github.com/en/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot) +- [How to write better prompts for GitHub Copilot](https://github.blog/developer-skills/github/how-to-write-better-prompts-for-github-copilot/) + +--- + +## 🧠 Strategies in Prompt Engineering + +Effective prompt engineering follows established patterns that guide AI models to produce better results: + +![Strategies in Prompt Engineering](images/prompt.png) + +### **Zero Shot** +- AI Model asked to perform task without examples +- **When to use**: Simple, well-defined tasks that don't require specific formatting +- **Example**: "Create a function that calculates the area of a circle" + +### **One-shot** +- AI Model is given an example before it performs task +- **When to use**: When you want to establish a specific pattern or format +- **Example**: "Here's how to format a commit message: `feat(auth): add login validation`. Now write one for adding a new user profile feature." + +### **Few-shot** +- The model is given several examples to establish pattern +- **When to use**: Complex tasks requiring consistent structure or multiple variations +- **Example**: Provide 3-4 examples of API endpoint documentation before asking for a new one + +### **Chain of Thought** +- Prompts that encourage model to generate detailed steps and explain the reason for each step +- **When to use**: Complex problem-solving, debugging, or when you need to understand the reasoning +- **Example**: "Debug this authentication issue step by step. First, check if the JWT token is valid, then verify the user permissions, then..." + +--- + +## 🎯 Understanding Prompts and Context + +### What is a Prompt? + +According to the [official GitHub documentation](https://docs.github.com/en/copilot/using-github-copilot/prompt-engineering-for-github-copilot), a prompt is: + +> **A request that you make to GitHub Copilot** - such as a question in Copilot Chat, or a code snippet that you ask Copilot to complete. + +### Two Types of Prompting + +| **Code Completion Prompts** | **Copilot Chat Prompts** | +|----------------------------|---------------------------| +| **Comments and code context** in your editor | **Natural language questions** in chat interface | +| Triggers automatic suggestions | Requires explicit user input | +| Uses surrounding code for context | Uses chat history and referenced files | +| Best for: Implementation details | Best for: Planning, debugging, explanations | + +### How Context Works + +GitHub Copilot uses multiple context sources: +- **Open files** in your IDE (neighboring tabs) +- **Current file** content and cursor position +- **Chat history** (for Copilot Chat) +- **Custom instructions** from `.copilot-instructions.md` +- **Referenced code** using `@` mentions and file selections + +--- + +## πŸ“š Core Prompt Engineering Principles + +Based on the [official GitHub Copilot best practices](https://github.blog/developer-skills/github/how-to-write-better-prompts-for-github-copilot/), here are the foundational techniques: + +### 1. **Start General, Then Get Specific** + +Begin with a broad description, then add specific requirements: + +```text +// ❌ Too vague +// Write some code for user authentication + +// βœ… Good structure +// Create a user authentication system for a Node.js app +// - Use Express and JWT tokens +// - Include password hashing with bcrypt +// - Add rate limiting for login attempts +// - Return proper error messages for invalid credentials +``` + +### 2. **Provide Clear Examples** + +Examples help Copilot understand exactly what you want: + +```javascript +// βœ… Excellent prompt with examples +// Write a function that finds all dates in a string and returns them in an array +// Dates can be formatted like: +// - 05/02/24, 05/02/2024, 5/2/24, 5/2/2024 +// - 05-02-24, 05-02-2024, 5-2-24, 5-2-2024 +// +// Example: +// findDates("I have a dentist appointment on 11/14/2023 and book club on 12-1-23") +// Returns: ["11/14/2023", "12-1-23"] + +function findDates(text) { + // Copilot will generate accurate regex and logic based on examples +} +``` + +### 3. **Break Complex Tasks into Simple Steps** + +Instead of asking for everything at once, decompose the problem: + +```text +// ❌ Too complex +"Create a complete word search puzzle generator with solutions" + +// βœ… Step-by-step approach +// Step 1: Write a function to generate a 10x10 grid of random letters +// Step 2: Write a function to place words horizontally in the grid +// Step 3: Write a function to place words vertically in the grid +// Step 4: Write a function to find all words in a completed grid +// Step 5: Combine everything into a puzzle generator +``` + +### 4. **Use Good Coding Practices** + +Following best practices improves AI suggestions: + +```javascript +// βœ… Descriptive naming and clear patterns +function authenticateUser(username, password) { + // Copilot generates relevant, well-structured code +} + +// ❌ Poor naming confuses AI +function rndpwd(l) { + // Copilot responds with: "Code goes here" +} +``` + +--- + +## πŸ—£οΈ Copilot Chat Advanced Techniques + +### Avoid Ambiguity + +Be specific about what you're referencing: + +```text +// ❌ Ambiguous +"What does this do?" + +// βœ… Specific +"What does the createUser function do?" +"What does the code in your last response do?" +"Explain the authentication logic in the selected code" +``` + +### Reference Relevant Code + +Use IDE features to provide context: + +```text +// In VS Code: Use @workspace, #file references +@workspace How can I optimize the database queries in this project? + +// In JetBrains: Use @project +@project What design patterns are used in the authentication module? + +// Reference specific files +How can I improve the error handling in #src/utils/api.js? +``` + +### Use Chat Participants and Context + +GitHub Copilot Chat supports various participants: + +| Participant | Purpose | Example Usage | +|-------------|---------|---------------| +| `@workspace` | Analyze entire workspace | `@workspace Find all TODO comments` | +| `@vscode` | VS Code specific tasks | `@vscode How do I set up a debug configuration?` | +| `@terminal` | Terminal and CLI help | `@terminal Help me with git rebase commands` | + +### Keep Chat History Relevant + +Manage your conversation context: + +```text +// βœ… Good practices +- Start new threads for different tasks +- Delete irrelevant or failed responses +- Reference previous responses when building on them +- Clear history when switching contexts + +// Example of building on previous responses +"Enhance the authentication function from your last response to include 2FA" +``` + +--- + +## πŸ› οΈ Code Completion Best Practices + +### Optimize Your Development Environment + +Based on [official documentation](https://docs.github.com/en/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot), here's how to get better suggestions: + +#### File Management + +```text +βœ… Do: +- Keep 1-2 relevant files open (neighboring tabs) +- Close unrelated files that might confuse context +- Open files that contain patterns you want to follow + +❌ Avoid: +- Having too many unrelated files open +- Working in files without relevant context +- Keeping outdated implementations open +``` + +#### Comment-Driven Development + +Use descriptive comments to guide AI suggestions: + +```javascript +// βœ… Excellent comment-driven approach +/* +Create a React component for user profile editing with: +- Form validation using Yup schema +- Auto-save on field changes +- Loading states during API calls +- Error handling with toast notifications +- Responsive design for mobile devices +*/ + +function UserProfileEditor() { + // Copilot generates structured component with all requirements +} +``` + +#### Progressive Implementation + +Let AI build complexity gradually: + +```javascript +// Step 1: Basic structure +function calculateShippingCost(weight, distance) { + // Base calculation +} + +// Step 2: Add validation +function calculateShippingCost(weight, distance) { + if (weight <= 0 || distance <= 0) { + throw new Error('Invalid input parameters'); + } + // Copilot continues with validated logic +} + +// Step 3: Add premium features +function calculateShippingCost(weight, distance, options = {}) { + // Previous validation code + + // Add express shipping, insurance, etc. + const { express = false, insurance = false } = options; + // Copilot builds on existing pattern +} +``` + +--- + +## πŸ’‘ Practical Prompt Examples + +### Real-World Code Completion Scenarios + +Based on the [GitHub blog examples](https://github.blog/developer-skills/github/how-to-write-better-prompts-for-github-copilot/), here are proven patterns: + +#### Building a Markdown Editor + +```javascript +/* +Create a basic markdown editor in Next.js with the following features: +- Use react hooks +- Create state for markdown with default text "type markdown here" +- A text area where users can write markdown +- Show a live preview of the markdown text as I type +- Support for basic markdown syntax like headers, bold, italics +- Use React markdown npm package +- The markdown text and resulting HTML should be saved in the component's state and updated in real time +*/ + +export default function MarkdownEditor() { + // Copilot generates complete functional component +} +``` + +#### Step-by-Step Text Processing + +```javascript +// Reverse a sentence step by step (proven example from GitHub blog) + +// Step 1: Make the first letter lowercase if it's not an 'I' +function processFirstLetter(sentence) { + // Copilot suggestion +} + +// Step 2: Split the sentence into an array of words +function splitIntoWords(sentence) { + // Copilot suggestion +} + +// Step 3: Remove punctuation marks +function removePunctuation(words) { + // Copilot suggestion +} + +// Step 4: Reverse the word order +function reverseWords(words) { + // Copilot suggestion +} + +// Step 5: Capitalize first letter and add punctuation +function finalizeReversedSentence(words) { + // Copilot suggestion +} +``` + +#### Array Processing with Examples + +```javascript +// Extract names from nested arrays (proven GitHub blog example) +// Example: Extract names from the data array +// Desired outcome: ['John', 'Jane', 'Bob'] +const data = [ + [{ name: 'John', age: 25 }, { name: 'Jane', age: 30 }], + [{ name: 'Bob', age: 40 }] +]; + +// Copilot will generate: data.flatMap(sublist => sublist.map(person => person.name)) +``` + +--- + +## 🎯 Advanced Chat Prompting Techniques + +### Iterative Prompt Refinement + +Experiment and iterate when results aren't perfect: + +```text +// ❌ First attempt (too vague) +# Write some code for grades.py + +// ⚠️ Second attempt (better but unclear) +# Implement a function in grades.py to calculate the average grade + +// βœ… Final attempt (specific and clear) +# Implement the function calculate_average_grade in grades.py that takes a list of grades as input and returns the average grade as a floating-point number +``` + +### Using Unit Tests as Examples + +Write tests first to guide implementation: + +```javascript +// First, describe what you want with tests +describe('isPrime function', () => { + test('should return true for prime numbers', () => { + expect(isPrime(2)).toBe(true); + expect(isPrime(3)).toBe(true); + expect(isPrime(17)).toBe(true); + }); + + test('should return false for non-prime numbers', () => { + expect(isPrime(1)).toBe(false); + expect(isPrime(4)).toBe(false); + expect(isPrime(15)).toBe(false); + }); + + test('should throw error for invalid input', () => { + expect(() => isPrime(-1)).toThrow(); + expect(() => isPrime(1.5)).toThrow(); + }); +}); + +// Then ask Copilot to implement the function based on these tests +function isPrime(num) { + // Copilot generates implementation that passes all tests +} +``` + +### Complex Problem Decomposition + +Break large tasks into manageable pieces: + +```text +// Instead of: "Create a complete e-commerce shopping cart" +// Use this step-by-step approach: + +// Step 1: Basic cart structure +"Create a shopping cart object that can store items with id, name, price, and quantity" + +// Step 2: Add cart operations +"Add methods to add items, remove items, and update quantities in the cart" + +// Step 3: Calculations +"Add methods to calculate item totals, tax, and final cart total" + +// Step 4: Persistence +"Add localStorage persistence to save and restore cart state" + +// Step 5: Integration +"Add React hooks to integrate the cart with a React component" +``` + +--- + +## πŸ§ͺ Hands-On Practice Exercises + +### Exercise 1: Progressive Prompting + +Practice the step-by-step approach with a real example: + +**Task:** Build a password strength validator + +```javascript +// Step 1: Basic structure +// Create a function that checks if a password meets basic requirements: +// - At least 8 characters long +// - Contains at least one uppercase letter +// - Contains at least one lowercase letter +// - Contains at least one number + +function validatePassword(password) { + // Let Copilot implement basic validation +} + +// Step 2: Add advanced requirements +// Enhance the function to also check for: +// - At least one special character (!@#$%^&*) +// - No common passwords (password, 123456, etc.) +// - No repeated characters more than 3 times + +// Step 3: Return detailed feedback +// Return an object with: +// - isValid: boolean +// - score: 1-100 +// - suggestions: array of improvement tips +// - estimatedCrackTime: string +``` + +### Exercise 2: Example-Driven Development + +Use the proven examples approach: + +```javascript +// Create a function that formats phone numbers +// Examples of inputs and expected outputs: +// formatPhoneNumber("1234567890") β†’ "(123) 456-7890" +// formatPhoneNumber("123-456-7890") β†’ "(123) 456-7890" +// formatPhoneNumber("+1 123 456 7890") β†’ "(123) 456-7890" +// formatPhoneNumber("123.456.7890") β†’ "(123) 456-7890" +// +// Should handle invalid inputs by returning null: +// formatPhoneNumber("123") β†’ null +// formatPhoneNumber("abc") β†’ null + +function formatPhoneNumber(input) { + // Copilot will generate robust formatting logic +} +``` + +### Exercise 3: Chat-Based Problem Solving + +Practice using Copilot Chat for complex scenarios: + +```text +@workspace I need to optimize the performance of our React app. + +Current issues: +- Initial page load takes 8 seconds +- Bundle size is 2.5MB +- Lighthouse performance score is 23/100 + +Analyze the codebase and suggest specific optimizations: +1. Code splitting opportunities +2. Unused dependency removal +3. Image optimization needs +4. Bundle analysis insights + +Provide actionable steps with expected impact estimates. +``` + +--- + +## ⚑ Quick Reference Guide + +### Do's and Don'ts Summary + +| βœ… **DO** | ❌ **DON'T** | +|-----------|--------------| +| Start general, then get specific | Use vague, ambiguous language | +| Provide clear examples | Assume Copilot knows your context | +| Break complex tasks into steps | Ask for everything at once | +| Use descriptive naming | Use poor coding practices | +| Keep relevant files open | Have too many unrelated files open | +| Iterate and refine prompts | Give up after first attempt | +| Reference specific files/code | Use unclear references | +| Experiment with different approaches | Stick to one prompt style | + +### Essential Prompt Starters + +```text +Code Completion: +// Create a function that [specific goal] +// Requirements: [list specific needs] +// Example usage: [show expected input/output] + +Copilot Chat: +@workspace How can I [specific task] in this project? +Explain the [specific code/concept] in the selected code +Generate unit tests for the [specific function/component] +What are the security implications of [specific code pattern]? +Optimize this code for [specific performance criteria] +``` + +--- + +--- + +## 🎯 Key Takeaways + +Effective prompt engineering for GitHub Copilot is based on proven techniques from [official GitHub resources](https://docs.github.com/en/copilot/using-github-copilot/prompt-engineering-for-github-copilot): + +### **The Foundation:** +- **Start general, then get specific** - provide context before details +- **Use clear examples** - show exactly what you want +- **Break complex tasks down** - one step at a time +- **Follow good coding practices** - descriptive names and clear patterns + +### **For Code Completion:** +- Use descriptive comments to guide suggestions +- Keep relevant files open (1-2 neighboring tabs) +- Let AI build complexity progressively +- Iterate on naming and structure + +### **For Copilot Chat:** +- Avoid ambiguous references ("this", "that") +- Use `@workspace`, `@vscode`, and file references +- Keep chat history relevant and focused +- Experiment and refine your prompts + +### **Quality Indicators:** +- βœ… Generated code compiles and follows your patterns +- βœ… Includes proper error handling and edge cases +- βœ… Integrates well with existing codebase +- ❌ Generic code that ignores your context +- ❌ Missing security or performance considerations + +### **Remember:** +Just like pair programming with a human, **communication is key**. The more clearly you express your intent and provide relevant context, the better GitHub Copilot can assist you in writing production-ready code. + +**Resources for Continued Learning:** + +**GitHub Copilot Specific:** +- [GitHub Copilot Chat Cookbook](https://docs.github.com/en/copilot/copilot-chat-cookbook) - Specific prompt examples +- [Using GitHub Copilot in your IDE](https://github.blog/developer-skills/github/using-github-copilot-in-your-ide-tips-tricks-and-best-practices/) - Practical tips and workflows + +**Advanced Prompt Engineering (Multi-Model):** +- [Anthropic Claude Prompt Engineering Guide](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview) - Comprehensive guide covering chain-of-thought, XML tags, and advanced techniques +- [OpenAI GPT-4.1 Prompting Guide](https://cookbook.openai.com/examples/gpt4-1_prompting_guide) - Latest best practices for agentic workflows, tool calling, and instruction following +- [Google Gemini Workspace Prompting Guide](https://services.google.com/fh/files/misc/gemini-for-google-workspace-prompting-guide-101.pdf) - Enterprise-focused prompting strategies for productivity tools + +--- + +**Next:** Continue exploring advanced GitHub Copilot features and enterprise integration strategies + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/03-Context-and-Prompt/3.4-model-selection-strategy.md b/lessons/03-Context-and-Prompt/3.4-model-selection-strategy.md new file mode 100644 index 0000000..f41a5b0 --- /dev/null +++ b/lessons/03-Context-and-Prompt/3.4-model-selection-strategy.md @@ -0,0 +1,122 @@ +# 🎯 Lesson 3.4 - AI Model Selection Strategy + +--- + +## πŸ“ Overview + +GitHub Copilot offers multiple AI models optimized for different use cases. Selecting the appropriate model can significantly impact the quality and efficiency of your development experience across various programming languages and frameworks. + +--- + +## 🎯 Goal + +Learn how to choose the right AI model for different development tasks to maximize productivity and code quality. + +--- + +## ⏱️ Estimated Time + +15-20 minutes + +--- + +## πŸ‘₯ Participants + +All developers working with GitHub Copilot across different languages and frameworks + +--- + +## πŸ€– Choosing the Right AI Model + +GitHub Copilot offers multiple AI models optimized for different use cases. Selecting the appropriate model can significantly impact the quality and efficiency of your development experience. + +![Model Selection Guide](../02-Getting-Started/images/ModelSelection.png) + +**Model Selection Strategy:** +- **Small, scoped changes**: Use **Claude-3.5-Sonnet** for targeted modifications and quick iterations +- **Larger tasks with clear instructions**: Use **Claude-3.5-Sonnet** or **GPT-4.1** for feature development and refactoring +- **Routine or general use**: Use **Claude-3.7-Sonnet**, **Gemini-2.5-Pro**, or **GPT-4.1** for everyday development assistance +- **Very complex or ambiguous tasks**: Use **o3** for advanced reasoning and complex problem-solving + +--- + +## πŸ› οΈ Framework-Specific Recommendations + +### **For React Development:** +- **Component creation and modification**: Claude-3.5-Sonnet or GPT-4.1 +- **Complex state management**: Claude-3.7-Sonnet or o3 for intricate logic +- **Performance optimization**: o3 for advanced analysis and recommendations +- **Quick fixes and styling**: Claude-3.5-Sonnet or Claude-3.7-Sonnet for rapid iterations + +### **For .NET/C# Development:** +- **API development**: Claude-3.5-Sonnet or GPT-4.1 for controller and service implementation +- **Entity Framework operations**: Claude-3.7-Sonnet for complex queries and migrations +- **Architecture decisions**: o3 for design patterns and architectural guidance +- **Unit testing**: Claude-3.5-Sonnet for test generation and coverage + +### **For Python Development:** +- **Data analysis and ML**: o3 for complex algorithms and model optimization +- **Web frameworks (Django/Flask)**: Claude-3.5-Sonnet or GPT-4.1 +- **Script automation**: Claude-3.7-Sonnet for general scripting tasks +- **API integration**: Claude-3.5-Sonnet for quick API implementations + +### **For JavaScript/TypeScript:** +- **Node.js backend**: Claude-3.5-Sonnet or GPT-4.1 +- **Complex type definitions**: o3 for advanced TypeScript patterns +- **Build tooling**: Claude-3.7-Sonnet for configuration and setup +- **Testing frameworks**: Claude-3.5-Sonnet for test implementation + +--- + +## πŸ”„ When to Switch Models + +**During Development:** +- Start with **Claude-3.5-Sonnet** for most tasks +- Switch to **o3** when encountering complex logic or architectural decisions +- Use **GPT-4.1** for tasks requiring extensive context understanding +- Try **Claude-3.7-Sonnet** or **Gemini-2.5-Pro** for routine code generation + +**Based on Task Complexity:** +- **Simple tasks**: Claude-3.5-Sonnet, Claude-3.7-Sonnet +- **Medium complexity**: GPT-4.1, Gemini-2.5-Pro +- **High complexity**: o3 + +**Based on Response Quality:** +- If responses are too generic, try **o3** for more sophisticated analysis +- If responses are too complex, switch to **Claude-3.5-Sonnet** for simpler solutions +- If context understanding is poor, try **GPT-4.1** + +--- + +## πŸ’‘ Best Practices + +### **Model Selection Tips:** +1. **Start with the fastest model** (Claude-3.5-Sonnet) for initial iterations +2. **Escalate to more powerful models** (o3) for complex problems +3. **Consider response time** vs. quality trade-offs for your workflow +4. **Experiment with different models** for the same task to find your preference + +### **Task-Model Matching:** +- **Code review and analysis**: o3 for comprehensive insights +- **Quick fixes and small changes**: Claude-3.5-Sonnet for speed +- **Documentation writing**: GPT-4.1 for natural language clarity +- **Debugging complex issues**: o3 for deep problem analysis + +### **Performance Considerations:** +- **Faster models** for iterative development cycles +- **More powerful models** for critical code that needs high quality +- **Balance speed vs. accuracy** based on project deadlines + +--- + +## 🎯 Key Takeaways + +- **No single model is best for everything** - match the model to the task +- **You can switch models anytime** in Copilot Chat to match your current needs +- **Experiment with different models** to understand their strengths +- **Consider both speed and quality** when selecting models for your workflow +- **Use more powerful models for complex decisions** and simpler models for routine tasks + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/03-Context-and-Prompt/3.5-reusable-prompts-and-instructions.md b/lessons/03-Context-and-Prompt/3.5-reusable-prompts-and-instructions.md new file mode 100644 index 0000000..21a73e8 --- /dev/null +++ b/lessons/03-Context-and-Prompt/3.5-reusable-prompts-and-instructions.md @@ -0,0 +1,351 @@ +# 🎯 Lesson 3.5 - Reusable Prompts and Instructions + +--- + +## πŸ“ Overview + +Learn how to create and use reusable prompt files and instruction files to standardize your Copilot interactions, maintain consistency across projects, and improve development workflows through customizable templates. + +--- + +## 🎯 Goal + +Master the creation and usage of `.prompt.md` files, instruction files, and custom Copilot instructions to streamline repetitive tasks and enforce coding standards. + +--- + +## ⏱️ Estimated Time + +25-30 minutes + +--- + +## πŸ‘₯ Participants + +All developers working with GitHub Copilot who want to create reusable templates and standardize AI interactions + +--- + +## πŸ“ Reusable Prompt Files (Experimental) + +Prompt files allow you to create reusable templates for common development tasks. These `.prompt.md` files can be shared across teams and projects to maintain consistency. + +### **Key Benefits:** +- **Standardize workflows** across team members +- **Reduce repetitive prompt writing** for common tasks +- **Maintain consistency** in code generation and review processes +- **Share best practices** through reusable templates + +### **Prompt File Structure:** + +```markdown +--- +mode: 'ask' # ask, edit, or agent +model: Claude Sonnet 4 +description: 'Brief description of the prompt' +tools: ['terminal', 'browser'] # For agent mode +--- + +Your prompt content here with Markdown formatting. +Reference files: [config](../config/settings.json) +Use variables: ${workspaceFolder}, ${selection}, ${input:formName} +``` + +### **Variable References:** +- **Workspace**: `${workspaceFolder}`, `${workspaceFolderBasename}` +- **Selection**: `${selection}`, `${selectedText}` +- **File context**: `${file}`, `${fileBasename}`, `${fileDirname}` +- **Input variables**: `${input:variableName}`, `${input:variableName:placeholder}` + +--- + +## πŸ“‹ Custom Instructions + +Custom instructions provide context and guidelines that are automatically included in Copilot requests, ensuring consistent behavior across all interactions. + +### **Instruction File Structure:** + +```yaml +--- +applyTo: + - codeGeneration + - testGeneration + - reviewSelection + - commitMessageGeneration + - pullRequestDescriptionGeneration +filePatterns: + - "**/*.tsx" + - "**/*.ts" + - "**/*.js" +keywords: + - react + - component + - security +--- + +# Your instruction content here +``` + +### **ApplyTo Types:** + +**`codeGeneration`** - Applied when generating new code: +- Coding standards, patterns, and conventions +- Framework preferences and architectural guidelines +- Security requirements and best practices + +**`testGeneration`** - Applied when generating tests: +- Testing frameworks, patterns, and coverage requirements +- Mock strategies and test organization +- Accessibility testing requirements + +**`reviewSelection`** - Applied when reviewing selected code: +- Review criteria, security standards, and best practices +- What to look for in code quality assessments +- Performance and accessibility considerations + +**`commitMessageGeneration`** - Applied when generating commit messages: +- Commit message format and conventions +- Semantic versioning and changelog requirements +- Issue reference patterns + +**`pullRequestDescriptionGeneration`** - Applied when generating PR descriptions: +- PR template structure and required sections +- Change description standards +- Review checklist requirements + +### **Targeting Options:** + +**File Patterns** - Limit instructions to specific file types: +```yaml +filePatterns: + - "**/*.tsx" # React TypeScript components + - "**/*.test.ts" # Test files only + - "**/api/**" # API directory only +``` + +**Keywords** - Apply when specific terms are detected: +```yaml +keywords: + - react + - security + - performance + - accessibility +``` + +--- + +## πŸ—‚οΈ File Locations and Organization + +### **Default Locations:** +- **Prompt files**: `.github/prompts/` folder +- **Instruction files**: `.github/instructions/` folder +- **Copilot instructions**: `.github/copilot-instructions.md` + +### **Custom Locations:** +Configure custom locations in VS Code settings: + +```json +{ + "chat.promptFilesLocations": { + ".github/prompts": true, + "templates/prompts": true, + "team-prompts": false + }, + "chat.instructionsFilesLocations": { + "src/frontend/instructions": true, + "src/backend/instructions": true + } +} +``` + +--- + +## πŸš€ Using Prompt Files + +### **Method 1: Command Palette** +1. Run `Chat: Run Prompt` command +2. Select prompt file from Quick Pick +3. Provide any required input variables + +### **Method 2: Chat Input** +``` +/create-react-form +/api-security-review +/create-react-form: formName=UserRegistration +``` + +### **Method 3: Editor Play Button** +1. Open `.prompt.md` file in editor +2. Click play button in title area +3. Choose current or new chat session + +--- + +## πŸ”„ Syncing Across Devices + +Enable Settings Sync to share user prompt files across multiple devices: + +1. Enable Settings Sync in VS Code +2. Run `Settings Sync: Configure` +3. Select **Prompts and Instructions** from sync options +4. User prompt files will sync automatically + +--- + +## βš™οΈ Configuration Settings + +### **Enable Experimental Features:** +```json +{ + "chat.promptFiles": true, + "github.copilot.chat.codeGeneration.useInstructionFiles": true +} +``` + +### **Custom Instructions (JSON Settings):** +```json +{ + "github.copilot.chat.codeGeneration.instructions": [ + "Use TypeScript strict mode", + "Prefer functional components with hooks", + "Include proper error handling", + "Add JSDoc comments for public APIs" + ], + "github.copilot.chat.testGeneration.instructions": [ + "Use React Testing Library for component tests", + "Mock external dependencies", + "Include accessibility tests", + "Aim for 90% code coverage" + ] +} +``` + +--- + +## πŸ’‘ Best Practices + +### **Prompt File Design:** +- **Use descriptive names** that clearly indicate the prompt's purpose +- **Include comprehensive descriptions** in the front matter +- **Reference relevant files** using Markdown links +- **Make prompts modular** and composable with other prompts + +### **Instruction Management:** +- **Keep instructions specific** to avoid conflicting guidance +- **Use team-shared instruction files** for consistency +- **Version control** all instruction and prompt files +- **Review and update regularly** as standards evolve + +### **Organization Strategies:** +- **Group by domain**: frontend, backend, testing, documentation +- **Use clear naming conventions**: `create-*`, `review-*`, `test-*` +- **Create prompt hierarchies** by referencing other prompt files +- **Document dependencies** and usage examples + +--- + +## 🎯 Common Use Cases + +### **Development Workflows:** +- API endpoint creation templates +- Component scaffolding prompts +- Database migration generators +- Documentation templates + +### **Code Quality:** +- Security review checklists +- Performance optimization guides +- Accessibility audit prompts +- Code style enforcement + +### **Testing:** +- Unit test generation templates +- Integration test scaffolding +- E2E test scenario creation +- Test data generation + +--- + +## πŸ”— Example Templates + +### **React Component Creation:** +```markdown +--- +mode: 'agent' +model: Claude Sonnet 4 +description: 'Create a new React component with TypeScript' +--- + +Create a new React component named ${input:componentName} with the following requirements: +- Use TypeScript with strict typing +- Include proper prop validation +- Add JSDoc documentation +- Include basic styling with CSS modules +- Add unit tests with React Testing Library + +Component should be created in: src/components/${input:componentName}/ +``` + +### **API Security Review:** +```markdown +--- +mode: 'ask' +model: Claude Sonnet 4 +description: 'Perform REST API security review' +--- + +Review the selected API code for security vulnerabilities: +- Authentication and authorization checks +- Input validation and sanitization +- Rate limiting implementation +- Error handling and information disclosure +- CORS configuration +- SQL injection prevention + +Provide a prioritized TODO list with remediation steps. +``` + +--- + +## πŸ” Key Differences: Instructions vs Prompts + +| Aspect | Instruction Files | Prompt Files | +|--------|------------------|--------------| +| **Purpose** | Background guidance | Interactive templates | +| **Activation** | Automatic (via `applyTo`) | Manual (user-triggered) | +| **Structure** | YAML frontmatter + content | YAML frontmatter + variables | +| **Targeting** | `applyTo`, `filePatterns`, `keywords` | User selection | +| **Variables** | Not supported | `${input:name:placeholder}` | +| **Scope** | Specific Copilot operations | Any chat interaction | + +### **When Instructions Apply:** +```yaml +# This instruction applies automatically when: +applyTo: + - codeGeneration # User asks Copilot to generate code + - reviewSelection # User asks for code review +filePatterns: + - "**/*.tsx" # Only for TypeScript React files +keywords: + - react # When "react" appears in the request +``` + +### **When Prompts Execute:** +```bash +# User manually triggers: +/create-react-component # Runs the prompt template +/api-security-review # Executes security review prompt +``` + +## 🎯 Key Takeaways + +- **Instruction files provide automatic context** through `applyTo` targeting +- **Prompt files offer reusable templates** for specific workflows +- **File patterns and keywords** make instructions contextually relevant +- **Variable substitution** makes prompts flexible and interactive +- **Both approaches complement each other** for comprehensive AI assistance +- **Proper targeting ensures** instructions apply at the right time + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/03-Context-and-Prompt/images/prompt.png b/lessons/03-Context-and-Prompt/images/prompt.png new file mode 100644 index 0000000..f601c3b Binary files /dev/null and b/lessons/03-Context-and-Prompt/images/prompt.png differ diff --git a/lessons/04-Advanced-Tools/4.1-understanding-mcp.md b/lessons/04-Advanced-Tools/4.1-understanding-mcp.md new file mode 100644 index 0000000..da4cd16 --- /dev/null +++ b/lessons/04-Advanced-Tools/4.1-understanding-mcp.md @@ -0,0 +1,519 @@ +# 🧠 Lesson 4.1: Understanding Model Context Protocol (MCP) in VS Code + +--- + +## πŸ“ Overview + +**Goal:** +Master Model Context Protocol (MCP) integration with VS Code and GitHub Copilot to extend AI capabilities with external tools, resources, and custom workflows. + +**Estimated Duration:** 20-25 minutes + +**Audience:** +Developers using VS Code with GitHub Copilot who want to extend AI capabilities with external services and custom tools. + +**Prerequisites:** +- VS Code with GitHub Copilot installed +- Basic understanding of GitHub Copilot Agent mode +- Familiarity with JSON configuration + +--- + +## 🎯 Learning Objectives + +By the end of this lesson, you will: + +1. **Understand MCP architecture** and its role in AI tool integration +2. **Configure MCP servers** in VS Code for GitHub Copilot +3. **Use MCP tools, resources, and prompts** in Agent mode +4. **Manage and troubleshoot** MCP server configurations +5. **Recognize enterprise benefits** of standardized AI tool integration + +--- + +## 🌐 What is Model Context Protocol (MCP)? + +### **The Official Definition** +> *"The Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you're building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need."* + +### **In Simple Terms** +MCP is like **USB for AI tools** - a universal standard that allows any AI application to connect to any data source or service without custom integration code. + +--- + +## πŸ—οΈ MCP Architecture in VS Code + +```mermaid +graph TD + subgraph "VS Code Environment" + A[GitHub Copilot Agent] + A --> B[MCP Client Manager] + B --> C1[MCP Client 1] + B --> C2[MCP Client 2] + B --> C3[MCP Client 3] + end + + subgraph "MCP Server Processes" + C1 -.-> D1[GitHub MCP Server
πŸ”§ PR management
πŸ“„ Repository data
πŸ’¬ Code review prompts] + C2 -.-> D2[Database MCP Server
πŸ”§ SQL queries
πŸ“„ Schema info
πŸ’¬ Data analysis] + C3 -.-> D3[Custom API Server
πŸ”§ Business logic
πŸ“„ Company data
πŸ’¬ Domain-specific tasks] + end + + style A fill:#007ACC + style B fill:#E3F2FD + style D1 fill:#FFF3E0 + style D2 fill:#FFF3E0 + style D3 fill:#FFF3E0 +``` + +### **Communication Flow** +1. **VS Code launches MCP servers** as separate processes +2. **JSON-RPC over stdio/HTTP** handles communication +3. **GitHub Copilot Agent** orchestrates tool usage +4. **User confirmation** required for potentially dangerous operations + +--- + +## βš™οΈ Configuring MCP in VS Code + +### **Prerequisites Setup** +Enable MCP support in VS Code settings: + +```json +{ + "github.copilot.chat.experimental.mcpServers": true +} +``` + +### **Configuration File Location** +Create `.vscode/mcp.json` in your workspace or user profile: + +**Workspace-specific:** `.vscode/mcp.json` +**User-global:** `~/.vscode/mcp.json` (macOS/Linux) or `%APPDATA%/Code/User/mcp.json` (Windows) + +### **Basic Configuration Format** +```json +{ + "servers": { + "github": { + "type": "stdio", + "command": "uvx", + "args": ["mcp-server-github"], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "your_token_here" + } + }, + "fetch": { + "type": "stdio", + "command": "uvx", + "args": ["mcp-server-fetch"] + }, + "database": { + "type": "sse", + "url": "http://localhost:3001/sse", + "headers": { + "Authorization": "Bearer your_token" + } + } + } +} +``` + +### **Server Types** +- **`stdio`:** Local process communication via standard input/output +- **`sse`:** Remote server communication via Server-Sent Events +- **`docker`:** Containerized MCP servers (if Docker available) + +--- + +## πŸ› οΈ Using MCP Tools in Agent Mode + +### **Activating MCP Tools** + +1. **Open Chat view** (βŒƒβŒ˜I / Ctrl+Alt+I) +2. **Select Agent mode** from the dropdown +3. **Click Tools button** to see available MCP tools +4. **Toggle specific tools** on/off as needed + +### **Direct Tool Reference** +Reference tools directly in prompts using `#` syntax: +```text +"Deploy the authentication service using #aws-deploy and create a PR with #github" +``` + +### **Tool Confirmation Flow** +When MCP tools are invoked: +1. **Tool execution prompt** appears +2. **Review tool parameters** before execution +3. **Choose confirmation level:** + - **Continue Once:** Execute this time only + - **Continue for Session:** Auto-approve for current session + - **Continue for Workspace:** Auto-approve for this workspace + - **Continue Always:** Auto-approve globally + +### **Example Tool Usage** +```text +User Prompt: "Create a pull request with the latest authentication improvements" + +Agent Response: +πŸ”§ Using GitHub MCP tool: create_pull_request +Parameters: +- title: "feat(auth): improve JWT token validation" +- body: "Enhanced security with token rotation and expiration handling" +- base: "main" +- head: "feature/auth-improvements" + +[Continue] [Edit Parameters] [Cancel] +``` + +--- + +## πŸ“„ MCP Resources and Context + +### **Adding MCP Resources** +1. **Select Add Context** > **MCP Resources** +2. **Choose resource type** from available servers +3. **Provide parameters** if required +4. **Resource content** is added to chat context + +### **Example Resources** +```text +# Database schema information +Add Context > MCP Resources > Database Schema > "users table" + +# GitHub repository information +Add Context > MCP Resources > GitHub Repository > "microsoft/vscode" + +# File system access +Add Context > MCP Resources > File System > "/path/to/configs" +``` + +### **Resource Usage in Prompts** +```text +"Analyze the database performance issues in the users table and suggest optimizations based on the current schema" + +AI Response: +πŸ“„ Using Database MCP resource: users_table_schema +πŸ“Š Schema shows indexes on: id, email, created_at +πŸ” Missing indexes detected on: last_login, account_status + +Recommendations: +1. Add composite index on (account_status, last_login) +2. Consider partitioning by created_at for large datasets +3. Implement soft delete pattern instead of hard deletes +``` + +--- + +## πŸ’¬ MCP Prompts and Templates + +### **Invoking MCP Prompts** +Use `/` followed by the prompt name: +```text +/mcp.github.code-review +/mcp.database.performance-analysis +/mcp.aws.deployment-checklist +``` + +### **Prompt with Parameters** +```text +/mcp.github.create-release +> Release version: 2.1.0 +> Release notes: Include authentication improvements and bug fixes +``` + +### **Custom MCP Prompts** +MCP servers can provide domain-specific prompts: +```json +{ + "prompts": [ + { + "name": "security-audit", + "description": "Perform security audit on codebase", + "arguments": [ + { + "name": "scope", + "description": "Audit scope (full, auth, api)", + "required": true + } + ] + } + ] +} +``` + +--- + +## 🎯 Tool Sets for Organization + +### **Creating Tool Sets** +Group related MCP tools into sets for easier management: + +```json +{ + "toolSets": { + "development": { + "tools": [ + "github.create_pr", + "github.create_issue", + "database.execute_query", + "aws.deploy_service" + ] + }, + "security": { + "tools": [ + "security.scan_vulnerabilities", + "github.security_audit", + "aws.check_permissions" + ] + } + } +} +``` + +### **Using Tool Sets** +```text +"Using the #development tool set, deploy the user authentication service and create a PR for review" +``` + +--- + +## πŸ”§ Managing MCP Servers + +### **MCP Server Management Commands** +Use Command Palette (βŒƒβ‡§P / Ctrl+Shift+P): + +- **`MCP: List Servers`** - View all configured servers +- **`MCP: Browse Resources`** - Explore available resources +- **`MCP: Restart Server`** - Restart a specific server +- **`MCP: Show Output`** - View server logs for debugging + +### **Server Status Monitoring** +```text +Command: MCP: List Servers + +Results: +βœ… github - Running (3 tools, 5 resources available) +❌ database - Error (Connection timeout) +⏸️ aws - Stopped (Start manually) +πŸ”„ custom-api - Restarting +``` + +### **Configuration Management** +VS Code provides editor support for `.vscode/mcp.json`: +- **Syntax highlighting** for JSON configuration +- **Command lenses** to start/stop/restart servers +- **Error validation** for configuration issues +- **IntelliSense** for known server types + +--- + +## 🚨 Troubleshooting MCP Issues + +### **Common Issues and Solutions** + +**❌ MCP Server Error Indicator in Chat** +```text +Solution: +1. Click error notification in Chat view +2. Select "Show Output" to view logs +3. Check server configuration in mcp.json +4. Verify environment variables and permissions +``` + +**❌ Server Not Starting** +```text +Checklist: +β–‘ Command path is correct (uvx, node, python) +β–‘ Arguments are properly formatted +β–‘ Environment variables are set +β–‘ Network connectivity (for remote servers) +β–‘ Permissions for file/directory access +``` + +**❌ Tools Not Appearing** +```text +Verification steps: +1. Ensure MCP experimental flag is enabled +2. Check server is running in MCP: List Servers +3. Verify tools are enabled in Agent mode +4. Restart VS Code if configuration changed +``` + +### **Development Mode for Custom Servers** +```json +{ + "servers": { + "my-custom-server": { + "command": "node", + "args": ["build/index.js"], + "dev": { + "watch": "build/**/*.js", + "debug": { "type": "node" } + } + } + } +} +``` + +--- + +## 🏒 Enterprise Benefits in VS Code + +### **πŸš€ Developer Productivity** +- **Unified AI interface** for all development tools +- **Context-aware assistance** with real project data +- **Automated workflows** through natural language +- **Reduced context switching** between tools + +### **πŸ”§ Standardization** +- **Consistent tool integration** across teams +- **Shared MCP configurations** in repositories +- **Standardized AI workflows** for common tasks +- **Centralized tool management** and updates + +### **πŸ”’ Security & Control** +- **Tool-level permissions** and confirmation prompts +- **Audit trails** for all AI-initiated actions +- **Isolated server processes** prevent cross-contamination +- **Enterprise authentication** integration + +### **πŸ“ˆ Scalability** +- **Easy addition** of new capabilities +- **Team-wide tool sets** for consistency +- **Custom MCP servers** for internal APIs +- **Future-proof integration** architecture + +--- + +## 🎯 Real-World VS Code Scenarios + +### **Full-Stack Development Workflow** +```text +Prompt: "I need to add user profile editing to our app. Set up the full workflow." + +Agent with MCP tools: +1. πŸ”§ database β†’ Create user_profiles table with schema +2. πŸ”§ github β†’ Create feature branch "user-profile-editing" +3. πŸ“„ codebase β†’ Analyze existing user management patterns +4. πŸ”§ github β†’ Generate API endpoints following project conventions +5. πŸ”§ github β†’ Create React components with TypeScript +6. πŸ”§ testing β†’ Generate unit and integration tests +7. πŸ”§ github β†’ Create PR with comprehensive description +``` + +### **DevOps and Deployment** +```text +Prompt: "Deploy the authentication service to staging and monitor for issues" + +Agent workflow: +1. πŸ”§ aws β†’ Deploy to staging environment +2. πŸ”§ monitoring β†’ Set up health checks and alerts +3. πŸ”§ github β†’ Update deployment documentation +4. πŸ”§ slack β†’ Notify team of deployment +5. πŸ“„ logs β†’ Monitor for first 10 minutes +6. πŸ”§ github β†’ Create deployment success issue if all good +``` + +### **Security and Compliance** +```text +Prompt: "Audit our authentication system for security vulnerabilities" + +Agent analysis: +1. πŸ“„ codebase β†’ Scan authentication-related files +2. πŸ”§ security-scanner β†’ Run vulnerability analysis +3. πŸ“„ dependencies β†’ Check for known CVEs +4. πŸ”§ github β†’ Review recent security-related PRs +5. πŸ”§ compliance β†’ Generate security audit report +6. πŸ”§ jira β†’ Create tickets for remediation items +``` + +--- + +## 🌟 Available MCP Servers for VS Code + +### **Official Reference Servers** +Based on the [Model Context Protocol servers repository](https://github.com/modelcontextprotocol/servers), these are the officially maintained reference implementations: + +**Development & DevOps:** +- **GitHub** - Repository management, PR automation, issue tracking +- **Git** - Local Git repository operations and history +- **Filesystem** - Secure file and directory operations +- **Docker** - Container management and deployment + +**Databases & Data:** +- **PostgreSQL** - Database queries and schema management +- **SQLite** - Lightweight database operations +- **Memory** - Persistent key-value storage across sessions + +**External Services:** +- **Fetch** - Web content retrieval for documentation and APIs +- **Brave Search** - Web search capabilities for real-time information +- **Google Drive** - Document access and collaboration +- **Slack** - Team communication and workflow integration + +**Development Tools:** +- **Puppeteer** - Web automation and testing +- **Sentry** - Error monitoring and performance tracking +- **Everything** - Fast file search on Windows systems + +### **VS Code Supported Servers** +For the complete VS Code MCP integration guide and supported servers, visit the [official VS Code MCP documentation](https://code.visualstudio.com/mcp). + +### **Community Servers (700+ Available)** +The MCP ecosystem includes hundreds of community-built servers for: +- **Cloud Platforms:** AWS, Azure, GCP, DigitalOcean +- **Databases:** MySQL, MongoDB, Redis, ClickHouse +- **Business Apps:** Salesforce, HubSpot, Notion, Jira +- **AI/ML Platforms:** OpenAI, Hugging Face, Anthropic +- **Development Tools:** Kubernetes, Jenkins, Terraform + +### **Enterprise Custom Servers** +- **Internal APIs** - Company-specific business logic +- **Legacy systems** - Bridge to older systems +- **Custom databases** - Proprietary data sources +- **Business workflows** - Domain-specific operations + +--- + +## πŸŽ“ Key Takeaways + +### **MCP in VS Code Transforms Development by:** +1. **Extending GitHub Copilot** with unlimited external capabilities +2. **Standardizing tool integration** through a universal protocol +3. **Enabling complex workflows** through natural language +4. **Maintaining security** with confirmation and isolation +5. **Supporting custom tools** for enterprise needs + +### **Best Practices:** +- **Start with official servers** (GitHub, Fetch) for reliability +- **Use workspace-specific configurations** for team consistency +- **Implement tool sets** for common workflow groupings +- **Monitor server logs** for troubleshooting and optimization +- **Train team members** on MCP prompt patterns and tool usage + +### **When MCP Adds Value:** +βœ… **Multi-tool workflows** requiring coordination +βœ… **Custom enterprise integrations** with internal systems +βœ… **Development automation** through AI assistance +βœ… **Complex data analysis** requiring multiple sources +βœ… **Deployment and operations** workflows + +--- + +## πŸƒβ€β™€οΈ Ready for Hands-On Practice? + +Now that you understand MCP in VS Code, let's put it into practice. In the next lesson, we'll: + +1. **Configure GitHub MCP server** in VS Code +2. **Use Agent mode** with MCP tools for repository management +3. **Automate pull request workflows** with natural language +4. **Experience the power** of AI-driven development workflows + +--- + +**Next:** [4.2 Hands-On Lab: GitHub MCP Integration β†’](./4.2-github-mcp-lab.md) + +--- + +Β© Copyright Neudeisc 2025 \ No newline at end of file diff --git a/lessons/04-Advanced-Tools/4.2-github-mcp-lab.md b/lessons/04-Advanced-Tools/4.2-github-mcp-lab.md new file mode 100644 index 0000000..9c03a64 --- /dev/null +++ b/lessons/04-Advanced-Tools/4.2-github-mcp-lab.md @@ -0,0 +1,534 @@ +# πŸ› οΈ Lab 4.2: Hands-On with GitHub MCP Server + +--- + +## πŸ“ Overview + +**Goal:** +Experience the power of Model Context Protocol firsthand by setting up GitHub's official MCP server and using it to automate pull request workflows, issue management, and repository operations through natural language. + +**Estimated Duration:** 25-30 minutes + +**Audience:** +Developers, DevOps engineers, and technical leads who want to experience the practical benefits of MCP in real GitHub workflows. + +**Prerequisites:** +- GitHub account with repository access +- Claude Desktop, Cursor, or another MCP-compatible AI application +- Node.js 18+ installed (for MCP server) +- Basic familiarity with Git and GitHub workflows + +--- + +## 🎯 Lab Objectives + +By the end of this lab, you will: + +1. **Install and configure** GitHub's official MCP server +2. **Connect the server** to your AI application (Claude Desktop) +3. **Automate pull request operations** using natural language +4. **Manage issues and repositories** through AI assistance +5. **Understand the practical benefits** of MCP over traditional integrations + +--- + +## πŸš€ Step 1: Install GitHub MCP Server + +### **1.1 Install via NPM (Recommended)** + +```bash +# Install GitHub MCP server globally +npm install -g @modelcontextprotocol/server-github + +# Verify installation +mcp-server-github --version +``` + +### **1.2 Alternative: Clone and Build from Source** + +```bash +# Clone the official MCP servers repository +git clone https://github.com/modelcontextprotocol/servers.git +cd servers/src/github + +# Install dependencies +npm install + +# Build the server +npm run build +``` + +### **What You'll Observe:** +- βœ… GitHub MCP server successfully installed +- βœ… Command-line tool available globally +- βœ… Ready for configuration with AI applications + +--- + +## πŸ” Step 2: Configure GitHub Authentication + +### **2.1 Create GitHub Personal Access Token** + +1. **Navigate to GitHub Settings:** + - Go to [GitHub Settings β†’ Developer settings β†’ Personal access tokens](https://github.com/settings/tokens) + - Click "Generate new token (classic)" + +2. **Configure Token Permissions:** + ``` + Token Name: MCP GitHub Server + Expiration: 90 days (or as per your organization's policy) + + Scopes to select: + βœ… repo (Full control of private repositories) + βœ… read:org (Read org and team membership) + βœ… read:user (Read user profile data) + βœ… user:email (Access user email addresses) + βœ… read:project (Read access to projects) + ``` + +3. **Copy the token** - you'll need it for configuration + +### **2.2 Set Environment Variable** + +```bash +# Add to your shell profile (.bashrc, .zshrc, etc.) +export GITHUB_PERSONAL_ACCESS_TOKEN="your_token_here" + +# Or create a .env file in your project directory +echo "GITHUB_PERSONAL_ACCESS_TOKEN=your_token_here" > ~/.mcp-github.env +``` + +### **Security Best Practices:** +- βœ… Use tokens with minimal required permissions +- βœ… Set appropriate expiration dates +- βœ… Store tokens securely (never commit to repos) +- βœ… Rotate tokens regularly per enterprise policy + +--- + +## πŸ”— Step 3: Connect to Claude Desktop + +### **3.1 Configure Claude Desktop** + +Edit your Claude Desktop configuration file: + +**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` +**Windows:** `%APPDATA%\Claude\claude_desktop_config.json` + +```json +{ + "mcpServers": { + "github": { + "command": "mcp-server-github", + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "your_token_here" + } + } + } +} +``` + +### **3.2 Alternative: Using Environment File** + +```json +{ + "mcpServers": { + "github": { + "command": "mcp-server-github", + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "your_token_here" + }, + "args": ["--env-file", "/path/to/.mcp-github.env"] + } + } +} +``` + +### **3.3 Restart Claude Desktop** + +1. **Completely quit** Claude Desktop +2. **Restart the application** +3. **Look for the MCP connection indicator** (πŸ”§ icon in the interface) + +### **What You'll Observe:** +- βœ… Claude Desktop shows MCP server connection +- βœ… GitHub tools become available to the AI +- βœ… Ready to execute GitHub operations via natural language + +--- + +## πŸ“‹ Step 4: Explore Available GitHub Tools + +### **4.1 Discover GitHub Capabilities** + +Ask Claude: *"What GitHub operations can you perform for me?"* + +**Expected Response:** +``` +I can help you with comprehensive GitHub operations including: + +πŸ”§ Repository Management: +- Create and search repositories +- Get repository information and file contents +- Fork repositories + +πŸ“„ Issue Management: +- List, create, and update issues +- Search issues across repositories +- Add comments to issues + +πŸš€ Pull Request Operations: +- List and create pull requests +- Get PR details and review status +- Add comments and request reviews + +πŸ‘₯ Collaboration: +- Search users and get user information +- List repository collaborators +- Manage organization repositories + +πŸ“Š Project Insights: +- Get repository statistics +- List branches and tags +- Access commit history +``` + +### **4.2 Test Basic Connectivity** + +Ask Claude: *"List my repositories and show me the most recently active ones"* + +**What You'll Observe:** +- βœ… Claude connects to GitHub via MCP +- βœ… Retrieves your repository list +- βœ… Provides formatted, readable output +- βœ… No custom API code required + +--- + +## 🎯 Step 5: Automate Pull Request Workflows + +### **5.1 Create a Pull Request** + +**Natural Language Command:** +> *"Create a pull request in my [repository-name] repository to merge the 'feature/user-authentication' branch into 'main'. Title it 'Add user authentication system' and include a description about implementing JWT-based auth with password reset functionality."* + +**What Happens Behind the Scenes:** +1. **MCP Discovery:** Claude asks GitHub server what tools are available +2. **Tool Selection:** Chooses `create_pull_request` tool +3. **Parameter Extraction:** Extracts repository, branches, title, description from your request +4. **API Call:** GitHub MCP server makes authenticated GitHub API call +5. **Response:** Formatted result returned to Claude + +**Expected Output:** +``` +βœ… Pull Request Created Successfully! + +πŸ“ Details: +- Repository: your-username/repository-name +- PR Number: #42 +- Title: Add user authentication system +- Status: Open +- Source: feature/user-authentication β†’ main +- URL: https://github.com/your-username/repository-name/pull/42 + +The pull request has been created and is ready for review. +``` + +### **5.2 Review Open Pull Requests** + +**Natural Language Command:** +> *"Show me all open pull requests in my repositories that need my review, especially anything related to security or authentication"* + +**Expected Output:** +``` +πŸ” Found 3 Pull Requests Requiring Your Review: + +1. πŸ” Repository: project-alpha + PR #15: "Implement OAuth2 integration" + - 🏷️ Labels: security, authentication + - πŸ‘€ Author: team-member-1 + - πŸ“… Created: 2 days ago + - βœ… CI Status: Passing + - πŸ“Š Changes: +156 -23 (5 files) + +2. πŸ”’ Repository: project-beta + PR #8: "Fix JWT token validation" + - 🏷️ Labels: security, bugfix + - πŸ‘€ Author: team-member-2 + - πŸ“… Created: 1 day ago + - ⚠️ CI Status: Failing + - πŸ“Š Changes: +45 -12 (2 files) + +3. πŸ›‘οΈ Repository: project-gamma + PR #23: "Update security dependencies" + - 🏷️ Labels: dependencies, security + - πŸ‘€ Author: dependabot[bot] + - πŸ“… Created: 3 hours ago + - βœ… CI Status: Passing + - πŸ“Š Changes: +2 -2 (1 file) +``` + +### **5.3 Add Review Comments** + +**Natural Language Command:** +> *"Add a comment to PR #15 in project-alpha asking about the OAuth2 token expiration handling and suggesting to add rate limiting for the auth endpoints"* + +**Expected Output:** +``` +βœ… Comment Added to Pull Request + +πŸ“ Repository: project-alpha +πŸ”— PR #15: "Implement OAuth2 integration" +πŸ’¬ Comment added successfully + +Your comment has been posted asking about OAuth2 token expiration handling and suggesting rate limiting for auth endpoints. +``` + +--- + +## 🎯 Step 6: Manage Issues and Project Tasks + +### **6.1 Create Issues from Natural Language** + +**Natural Language Command:** +> *"Create an issue in my project-alpha repository for implementing two-factor authentication. Label it as 'enhancement' and 'security', assign it to myself, and include a detailed description with requirements for SMS and authenticator app support."* + +**Expected Output:** +``` +βœ… Issue Created Successfully! + +πŸ“‹ Issue Details: +- Repository: project-alpha +- Issue Number: #47 +- Title: Implement Two-Factor Authentication +- Labels: enhancement, security +- Assignee: your-username +- Status: Open + +πŸ“ Description includes: +- SMS verification support +- Authenticator app integration (TOTP) +- Backup recovery codes +- User settings management +- Security audit trail + +πŸ”— URL: https://github.com/your-username/project-alpha/issues/47 +``` + +### **6.2 Search and Analyze Issues** + +**Natural Language Command:** +> *"Find all open security-related issues across my repositories and prioritize them by creation date and label urgency"* + +**Expected Output:** +``` +πŸ” Security Issues Analysis (5 issues found) + +🚨 High Priority: +1. project-alpha #32: "SQL Injection vulnerability in user search" + πŸ“… Created: 5 days ago | 🏷️ security, critical + +2. project-beta #18: "XSS vulnerability in comment system" + πŸ“… Created: 3 days ago | 🏷️ security, high + +⚠️ Medium Priority: +3. project-alpha #47: "Implement Two-Factor Authentication" + πŸ“… Created: Today | 🏷️ security, enhancement + +4. project-gamma #12: "Update HTTPS configuration" + πŸ“… Created: 1 week ago | 🏷️ security, configuration + +πŸ“‹ Standard Priority: +5. project-beta #25: "Security audit documentation" + πŸ“… Created: 2 weeks ago | 🏷️ security, documentation +``` + +--- + +## 🎯 Step 7: Advanced Repository Operations + +### **7.1 Repository Insights and Analytics** + +**Natural Language Command:** +> *"Analyze the activity in my top 3 repositories - show me recent commits, contributor activity, and any repositories that might need attention"* + +**Expected Output:** +``` +πŸ“Š Repository Activity Analysis + +πŸ† Most Active: project-alpha +- πŸ“ˆ 15 commits in last 7 days +- πŸ‘₯ 4 active contributors +- πŸ”„ 3 open PRs, 2 pending reviews +- ⚠️ 5 open issues (2 security-related) +- πŸ“… Last activity: 2 hours ago + +πŸ₯ˆ Moderately Active: project-beta +- πŸ“ˆ 8 commits in last 7 days +- πŸ‘₯ 2 active contributors +- πŸ”„ 1 open PR, ready to merge +- ⚠️ 3 open issues +- πŸ“… Last activity: 1 day ago + +πŸ₯‰ Needs Attention: project-gamma +- πŸ“ˆ 2 commits in last 7 days +- πŸ‘₯ 1 contributor +- πŸ”„ 0 open PRs +- ⚠️ 8 open issues (3 stale) +- πŸ“… Last activity: 4 days ago +- 🚨 Recommendation: Review stale issues and update project status +``` + +### **7.2 File and Code Search** + +**Natural Language Command:** +> *"Search for any files in my repositories that contain hardcoded API keys or passwords - focus on config files and environment files"* + +**Expected Output:** +``` +πŸ” Security Scan Results + +⚠️ Potential Security Issues Found: + +1. πŸ“ project-alpha/config/database.js + - Line 15: Potential database password hardcoded + - Last modified: 3 weeks ago + - 🚨 Recommendation: Move to environment variables + +2. πŸ“ project-beta/src/config.py + - Line 8: API key pattern detected + - Last modified: 1 week ago + - 🚨 Recommendation: Use secure configuration management + +βœ… Clean Files: +- project-gamma: No hardcoded secrets detected +- All .env.example files properly configured + +πŸ›‘οΈ Security Recommendations: +1. Implement secret scanning in CI/CD +2. Use secure secret management (AWS Secrets Manager, Azure Key Vault) +3. Add .env files to .gitignore +4. Conduct regular security audits +``` + +--- + +## 🎯 Step 8: Collaborative Workflows + +### **8.1 Team Coordination** + +**Natural Language Command:** +> *"List all repositories where I'm a collaborator, show recent activity, and identify any PRs waiting for my review or approval"* + +### **8.2 Cross-Repository Operations** + +**Natural Language Command:** +> *"Create a summary report of all security-related work across my repositories in the last month - include issues created, PRs merged, and outstanding security tasks"* + +--- + +## πŸ”„ Step 9: Compare with Traditional Approach + +### **Traditional GitHub API Integration** +```javascript +// Required custom code for basic PR creation +const { Octokit } = require("@octokit/rest"); + +const octokit = new Octokit({ + auth: process.env.GITHUB_TOKEN, +}); + +async function createPR(owner, repo, title, body, head, base) { + try { + const response = await octokit.rest.pulls.create({ + owner, + repo, + title, + body, + head, + base, + }); + + // Custom formatting needed + return formatPRResponse(response.data); + } catch (error) { + // Custom error handling needed + handleGitHubAPIError(error); + } +} + +// Additional functions needed for: +// - Error handling +// - Response formatting +// - Rate limiting +// - Authentication management +// - API versioning +``` + +### **MCP Approach** +```bash +# No code needed - just natural language! +User: "Create a pull request to merge feature/auth into main with title 'Add authentication' and description about JWT implementation" + +# AI handles: +# βœ… API authentication +# βœ… Parameter extraction +# βœ… Error handling +# βœ… Response formatting +# βœ… User-friendly output +``` + +--- + +## πŸŽ“ Key Takeaways + +### **What You Experienced** +1. **Zero Custom Code:** No GitHub API wrappers or authentication logic needed +2. **Natural Language Interface:** Complex operations described in plain English +3. **Intelligent Context:** AI understands relationships between repos, PRs, and issues +4. **Consistent Experience:** Same patterns work for other MCP servers (AWS, databases, etc.) +5. **Enterprise Ready:** Built-in security, error handling, and audit trails + +### **Productivity Benefits** +- ⚑ **10x faster** setup compared to custom GitHub integrations +- 🧠 **AI-powered insights** across repositories and issues +- πŸ”’ **Security-first** approach with proper token management +- 🀝 **Team collaboration** enhanced through natural language workflows +- πŸ“ˆ **Scalable** to hundreds of repositories and team members + +### **Enterprise Impact** +- **Reduced development time** for GitHub integrations +- **Standardized workflows** across development teams +- **Enhanced security** through centralized authentication +- **Better visibility** into project status and security posture +- **Future-proof architecture** as GitHub adds new features + +--- + +## πŸš€ Next Steps + +### **Immediate Actions** +1. **Set up GitHub MCP** in your development environment +2. **Train your team** on natural language GitHub operations +3. **Identify workflow automation** opportunities in your projects +4. **Establish security policies** for MCP server access + +### **Advanced Exploration** +1. **Combine with other MCP servers** (AWS, databases, Slack) +2. **Create custom MCP servers** for internal tools +3. **Implement CI/CD integration** with MCP-powered workflows +4. **Develop team-specific prompts** and automation templates + +--- + +## 🌟 Congratulations! + +You've successfully experienced the power of Model Context Protocol with GitHub! You've seen how MCP transforms complex API integrations into simple, natural language interactions while maintaining enterprise-grade security and functionality. + +This is just the beginning. With 700+ community MCP servers available and a growing ecosystem, you can now connect AI to virtually any service or data source with the same ease you just experienced with GitHub. + +**The future of AI-powered development workflows is here, and it speaks your language!** + +--- + +**Next:** [4.3 Playwright MCP Integration β†’](./4.3-playwright-mcp.md) \ No newline at end of file diff --git a/lessons/04-Advanced-Tools/4.3-playwright-mcp.md b/lessons/04-Advanced-Tools/4.3-playwright-mcp.md new file mode 100644 index 0000000..0e32f11 --- /dev/null +++ b/lessons/04-Advanced-Tools/4.3-playwright-mcp.md @@ -0,0 +1,12 @@ +using the playwright MCP server + +Use Playwright tools to generate test for scenario: + +## GitHub PR Checks Navigation Checklist + +1. Open the [Microsoft Playwright GitHub repository](https://github.com/microsoft/playwright). +2. Click on the **Pull requests** tab. +3. Find and open the pull request titled **"chore: make noWaitAfter a default"**. +4. Switch to the **Checks** tab for that pull request. +5. Expand the **infra** check suite to view its jobs. +6. Click on the **docs & lint** job to view its details. \ No newline at end of file diff --git a/lessons/README.md b/lessons/README.md new file mode 100644 index 0000000..02a45f5 --- /dev/null +++ b/lessons/README.md @@ -0,0 +1,68 @@ +# GitHub Copilot Training Workshop + +Welcome to the GitHub Copilot Training Workshop! This repository contains all the materials for our hands-on training sessions, designed to help developers master GitHub Copilot for maximum productivity and code quality. + +## πŸ“š Table of Contents + +### **Module 1: Installation and Setup** + +- **[1.1 Installing GitHub Copilot](./01-Installation-and-Setup/01-installing-copilot.md)**: A complete guide to installing GitHub Copilot in VS Code and Visual Studio. + +--- + +### **Module 2: Getting Started with Copilot** + +This module provides language-specific tracks for C# and React developers, along with language-agnostic resources for enterprise-level skills. + +#### **C# Developer Track** + +- **[2.0 Exploring the Workspace (`@workspace`)](./02-Getting-Started/c%23/2.0-exploring-workspace.md)**: Master the `@workspace` command for codebase-wide questions. +- **[2.1 Mastering Copilot Chat (`/ask`)](./02-Getting-Started/c%23/2.1-exploring-copilot-ask.md)**: Use the `/ask` command for targeted questions and answers. +- **[2.2 Using Copilot Agent Mode](./02-Getting-Started/c%23/2.2-exploring-copilot-agent.md)**: Leverage autonomous agent capabilities for complex tasks. +- **[2.3 Inline Chat and Suggestions](./02-Getting-Started/c%23/2.3-exploring-copilot-inline.md)**: Efficiently modify and generate code with inline suggestions. + +#### **React Developer Track** + +- **[2.0 GitHub Copilot Tools Overview](./02-Getting-Started/react/2.0-github-copilot-tools.md)**: A comprehensive overview of all major Copilot tools. +- **[2.1 Exploring Copilot Chat (`/ask`)](./02-Getting-Started/react/2.1-exploring-copilot-ask-(react).md)**: Hands-on lab for React-specific chat scenarios. +- **[2.2 Editing Code (`/edit`)](./02-Getting-Started/react/2.2-exploring-copilot-edit-(react).md)**: Practical workflows for editing React components. +- **[2.3 Using Copilot Agent Mode](./02-Getting-Started/react/2.3-exploring-copilot-agent-(react).md)**: Implement complex React features autonomously. +- **[2.4 Inline Chat and Suggestions](./02-Getting-Started/react/2.4-exploring-copilot-inline-(react).md)**: Apply inline development patterns for React. + +#### **Language-Agnostic Enterprise Skills** + +- **[Enterprise Code Review with Copilot](./02-Getting-Started/copilot-code-review.md)**: Enhance code quality with AI-assisted code reviews. +- **[GitHub Copilot Web Features](./02-Getting-Started/github-copilot-web-features.md)**: Explore platform features beyond the IDE. +- **[Team Adoption Strategy](./02-Getting-Started/implementation-roadmap.md)**: An 8-week roadmap for successful team adoption. + +--- + +### **Module 3: Context and Prompt Engineering** + +- **[3.1 Managing Context Effectively](./03-Context-and-Prompt/3.1-managing-context-effectively.md)**: Master context with #-mentions, @-participants, and external references. +- **[3.2 Custom Instructions & Workspace Configuration](./03-Context-and-Prompt/3.2-custom-instructions-workspace.md)**: Align Copilot with team standards and project requirements. +- **[3.3 Advanced Prompt Engineering](./03-Context-and-Prompt/3.3-prompt-engineering-advanced.md)**: Apply advanced prompting techniques for complex scenarios. + +--- + +### **Module 4: Advanced Tools (MCP)** + +- **[4.1 Understanding Model Context Protocol (MCP)](./04-Advanced-Tools/4.1-understanding-mcp.md)**: Learn the fundamentals of extending Copilot with custom tools. +- **[4.2 Lab: GitHub MCP Integration](./04-Advanced-Tools/4.2-github-mcp-lab.md)**: Hands-on lab for integrating the official GitHub MCP server. +- **[4.3 Lab: Playwright MCP Setup](./04-Advanced-Tools/4.3-playwright-mcp.md)**: Set up and use the Playwright MCP server for automated testing. + +## πŸš€ How to Get Started + +1. **Installation**: Begin with **[1.1 Installing GitHub Copilot](./01-Installation-and-Setup/01-installing-copilot.md)**. +2. **Choose Your Track**: + - **C# Developers**: Start with **[2.0 Exploring the Workspace (`@workspace`)](./02-Getting-Started/c%23/2.0-exploring-workspace.md)**. + - **React Developers**: Start with **[2.0 GitHub Copilot Tools Overview](./02-Getting-Started/react/2.0-github-copilot-tools.md)**. +3. **Advance Your Skills**: Progress through the modules on Context, Prompting, and Advanced Tools to become a Copilot expert. + +## πŸ’¬ Feedback + +Your feedback is valuable! Please open an issue or submit a pull request if you have suggestions for improving the workshop materials. + +--- + +Β© Copyright Neudeisc 2025 diff --git a/samples/Orders/Orders.Api/Controllers/OrderController.cs b/sample-code/Orders/Orders.Api/Controllers/OrderController.cs similarity index 100% rename from samples/Orders/Orders.Api/Controllers/OrderController.cs rename to sample-code/Orders/Orders.Api/Controllers/OrderController.cs diff --git a/samples/Orders/Orders.Api/Orders.Api.csproj b/sample-code/Orders/Orders.Api/Orders.Api.csproj similarity index 100% rename from samples/Orders/Orders.Api/Orders.Api.csproj rename to sample-code/Orders/Orders.Api/Orders.Api.csproj diff --git a/samples/Orders/Orders.Api/Orders.Api.http b/sample-code/Orders/Orders.Api/Orders.Api.http similarity index 100% rename from samples/Orders/Orders.Api/Orders.Api.http rename to sample-code/Orders/Orders.Api/Orders.Api.http diff --git a/samples/Orders/Orders.Api/Program.cs b/sample-code/Orders/Orders.Api/Program.cs similarity index 100% rename from samples/Orders/Orders.Api/Program.cs rename to sample-code/Orders/Orders.Api/Program.cs diff --git a/samples/Orders/Orders.Api/Properties/launchSettings.json b/sample-code/Orders/Orders.Api/Properties/launchSettings.json similarity index 100% rename from samples/Orders/Orders.Api/Properties/launchSettings.json rename to sample-code/Orders/Orders.Api/Properties/launchSettings.json diff --git a/samples/Orders/Orders.Api/Repositories/OrderRepository.cs b/sample-code/Orders/Orders.Api/Repositories/OrderRepository.cs similarity index 100% rename from samples/Orders/Orders.Api/Repositories/OrderRepository.cs rename to sample-code/Orders/Orders.Api/Repositories/OrderRepository.cs diff --git a/samples/Orders/Orders.Api/appsettings.Development.json b/sample-code/Orders/Orders.Api/appsettings.Development.json similarity index 100% rename from samples/Orders/Orders.Api/appsettings.Development.json rename to sample-code/Orders/Orders.Api/appsettings.Development.json diff --git a/samples/Orders/Orders.Api/appsettings.json b/sample-code/Orders/Orders.Api/appsettings.json similarity index 100% rename from samples/Orders/Orders.Api/appsettings.json rename to sample-code/Orders/Orders.Api/appsettings.json diff --git a/samples/Orders/Orders.Domain/Order.cs b/sample-code/Orders/Orders.Domain/Order.cs similarity index 100% rename from samples/Orders/Orders.Domain/Order.cs rename to sample-code/Orders/Orders.Domain/Order.cs diff --git a/samples/Orders/Orders.Domain/OrderService.cs b/sample-code/Orders/Orders.Domain/OrderService.cs similarity index 100% rename from samples/Orders/Orders.Domain/OrderService.cs rename to sample-code/Orders/Orders.Domain/OrderService.cs diff --git a/samples/Orders/Orders.Domain/Orders.Domain.csproj b/sample-code/Orders/Orders.Domain/Orders.Domain.csproj similarity index 100% rename from samples/Orders/Orders.Domain/Orders.Domain.csproj rename to sample-code/Orders/Orders.Domain/Orders.Domain.csproj diff --git a/samples/Orders/Orders.sln.sln b/sample-code/Orders/Orders.sln.sln similarity index 100% rename from samples/Orders/Orders.sln.sln rename to sample-code/Orders/Orders.sln.sln diff --git a/samples/Orders/README.md b/sample-code/Orders/README.md similarity index 100% rename from samples/Orders/README.md rename to sample-code/Orders/README.md diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/Readme.md b/sample-code/SimpleFullStack/DotnetCoreApi/Readme.md new file mode 100644 index 0000000..1b226aa --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/Readme.md @@ -0,0 +1,131 @@ +# Copilot Sample .NET 8 Full Stack API + +## 1. What is this project about? +This repository is a Web API solution demonstrating a modern .NET 8 backend API for inventory and product management. It includes: +- **copilot-sample.Api**: ASP.NET Core Web API for managing products, categories, inventory, pricing, and reviews. +- **copilot-sample.DataAccess**: Data access layer using Entity Framework Core (EF Core) with SQLite (default) or SQL Server. +- **copilot-sample.Test**: Unit tests for API and services using xUnit, Moq, and FluentAssertions. + +The project is designed for learning, prototyping, and as a reference for building scalable .NET 8 APIs with best practices. + +**πŸš€ Quick Start**: After running the API, visit [http://localhost:5111/swagger](http://localhost:5111/swagger) to explore and test all endpoints! + +## 2. Database Schema & Entities + +This project implements a comprehensive inventory management system with the following entity relationships: + +![Database Schema](copilot-sample.Api/inventory.db.png) + +### Core Entities + +#### πŸ“¦ **Categories** +- Hierarchical category structure with parent-child relationships +- Supports unlimited nesting levels (e.g., Electronics β†’ Laptops β†’ Gaming Laptops) +- Fields: `CategoryID`, `Name`, `Description`, `ParentCategoryID` + +#### πŸ›οΈ **Products** +- Core product information with category association +- Includes SKU management, branding, and lifecycle tracking +- Fields: `ProductID`, `Name`, `Description`, `SKU`, `CategoryID`, `Brand`, `CreatedAt`, `UpdatedAt`, `IsActive` + +#### πŸ’° **ProductPrices** +- Time-based pricing with currency support +- Enables price history and future pricing schedules +- Fields: `PriceID`, `ProductID`, `Price`, `CurrencyCode`, `EffectiveFrom`, `EffectiveTill` + +#### πŸ“Š **Inventory** +- Real-time stock quantity tracking +- Last updated timestamps for inventory management +- Fields: `InventoryID`, `ProductID`, `Quantity`, `LastUpdated` + +#### 🏷️ **ProductAttributes** +- Flexible key-value product specifications +- Supports dynamic product properties (Color, Size, Weight, etc.) +- Fields: `AttributeID`, `ProductID`, `AttributeName`, `AttributeValue` + +#### ⭐ **ProductReviews** +- Customer review and rating system +- Rating validation (1-5 stars) with comments +- Fields: `ReviewID`, `ProductID`, `ReviewerName`, `Rating`, `Comment`, `ReviewDate` + +### Entity Relationships + +- **Categories**: Self-referencing hierarchy (Parent β†’ Children) +- **Products**: Belongs to Category, has multiple Prices, Attributes, Reviews, and one Inventory record +- **Pricing**: Multiple price records per product for historical tracking +- **Inventory**: One-to-one with Products for current stock levels +- **Attributes**: Multiple dynamic properties per product +- **Reviews**: Multiple customer reviews per product + +### Sample Data + +The system comes pre-loaded with sample data including: +- **4 Categories**: Electronics (parent) with Laptops, Smartphones, Accessories (children) +- **7 Products**: Various electronics with different brands and specifications +- **Pricing**: Current prices in USD for all products +- **Inventory**: Stock quantities for each product +- **Attributes**: Technical specifications and features +- **Reviews**: Sample customer feedback with ratings + +## 3. How is it setup? +- **Database**: Uses SQLite by default (see `copilot-sample.Api/DBSetup.md` for details). Can be switched to SQL Server. +- **Entity Framework Core**: Handles data access and migrations. +- **Dependency Injection**: All services are registered and injected using .NET's built-in DI. +- **Swagger**: API documentation and testing UI available at `/swagger` when running the API. +- **Unit Testing**: xUnit, Moq, and FluentAssertions for robust test coverage. + +## 4. Dependencies +- [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) +- ASP.NET Core 8 +- Entity Framework Core 8 (with SQLite and InMemory providers) +- Swashbuckle.AspNetCore (Swagger) +- xUnit, Moq, FluentAssertions (for testing) + +See each project's `.csproj` for full dependency details. + +## 5. How to run it + +### Build and Run the API + +```bash +# Restore dependencies and build all projects +dotnet build + +# Run the API (from the Api project directory) +cd copilot-sample.Api +dotnet run +``` + +The API will be available at `https://localhost:7111` or `http://localhost:5111` by default. + +**πŸš€ Quick Start**: After running the API, visit [http://localhost:5111/swagger](http://localhost:5111/swagger) to explore and test all endpoints! + +### Access Swagger UI + +- **Swagger Documentation**: Available at [http://localhost:5111/swagger](http://localhost:5111/swagger) when running the API +- **HTTPS Swagger**: Available at [https://localhost:7111/swagger](https://localhost:7111/swagger) when using HTTPS + +### Quick Start with Swagger + +Once the API is running, you can: + +1. Open [http://localhost:5111/swagger](http://localhost:5111/swagger) in your browser +2. Explore all available endpoints and their documentation +3. Test API endpoints directly from the Swagger UI +4. View request/response models and schemas + +### Database Setup + +- By default, the API will create a `inventory.db` SQLite file on first run. +- For advanced setup, migrations, or switching to SQL Server, see [`copilot-sample.Api/DBSetup.md`](copilot-sample.Api/DBSetup.md). + +### Run Unit Tests + +```bash +# From the solution root or Test project directory +dotnet test +``` + +--- + +For more details, see inline code comments, Swagger UI, and the `copilot-instructions.md` file for Copilot usage tips. diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/appsettings.json b/sample-code/SimpleFullStack/DotnetCoreApi/appsettings.json new file mode 100644 index 0000000..1142993 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=YourDatabaseName;Trusted_Connection=True;TrustServerCertificate=True;" + }, + "AllowedHosts": "*" +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-instructions.md b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-instructions.md new file mode 100644 index 0000000..93a8c06 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-instructions.md @@ -0,0 +1,210 @@ +# Copilot Instructions for .NET 8 Full Stack Inventory API + +## Project Overview +This is a .NET 8 Web API solution for inventory and product management with a comprehensive 3-layer architecture. The solution demonstrates modern .NET development practices with Entity Framework Core, SQLite database, clean architecture, and comprehensive testing. + +## Project Structure + +### Solution Architecture +``` +copilot-sample.sln (Solution file) +β”œβ”€β”€ copilot-sample.Api/ (Web API Layer) +β”œβ”€β”€ copilot-sample.DataAccess/ (Data Access Layer) +β”œβ”€β”€ copilot-sample.Test/ (Unit Testing) +└── lessons/ (Learning materials) +``` + +### 1. **copilot-sample.Api** - Web API Layer +- **Framework**: ASP.NET Core 8.0 Web API (`Microsoft.NET.Sdk.Web`) +- **Purpose**: Handles HTTP requests, API controllers, and business logic services +- **Key Dependencies**: + - `Microsoft.EntityFrameworkCore.Design` (9.0.5) - For EF migrations + - `Swashbuckle.AspNetCore` (6.6.2) - For Swagger/OpenAPI documentation +- **Project Reference**: `copilot-sample.DataAccess` +- **Structure**: + - `Controllers/` - API controllers (ProductController, CategoryController, ProductAttributeController) + - `Services/` - Business logic services (ProductService, CategoryService, ProductAttributeService) + - `Contracts/` - Service interfaces (IProductService, ICategoryService) + - `Models/Dtos/` - Data Transfer Objects for API responses + - `Program.cs` - Application configuration and dependency injection + - `appsettings.json` - Configuration including connection strings + +### 2. **copilot-sample.DataAccess** - Data Access Layer +- **Framework**: .NET 8 Class Library (`Microsoft.NET.Sdk`) +- **Purpose**: Entity Framework Core data layer with entities, configurations, and migrations +- **Key Dependencies**: + - `Microsoft.EntityFrameworkCore` (9.0.5) - Core EF functionality + - `Microsoft.EntityFrameworkCore.Sqlite` (9.0.5) - SQLite provider + - `Microsoft.EntityFrameworkCore.Tools` (9.0.5) - Migration tools +- **Structure**: + - `Entities/` - Database entity models (Product, Category, Inventory, ProductPrice, ProductAttribute, ProductReview) + - `EntityConfiguration/` - EF Core fluent configurations for each entity + - `Extensions/` - Extension methods for database setup + - `Migrations/` - EF Core migration files + - `SeedData/` - Database seed data classes + - `AppDbContext.cs` - Main Entity Framework DbContext + +### 3. **copilot-sample.Test** - Unit Testing Layer +- **Framework**: .NET 8 Test Project (`Microsoft.NET.Sdk`) +- **Purpose**: Unit tests for API controllers and services +- **Key Dependencies**: + - `Microsoft.NET.Test.Sdk` (17.8.0) - Test framework + - `xunit` (2.9.3) - Testing framework + - `xunit.runner.visualstudio` (2.5.3) - Visual Studio test runner + - `Moq` (4.20.72) - Mocking framework + - `FluentAssertions` (8.2.0) - Assertion library + - `Microsoft.EntityFrameworkCore.InMemory` (9.0.5) - In-memory database for testing + - `coverlet.collector` (6.0.0) - Code coverage +- **Project Reference**: `copilot-sample.Api` + +## Database Schema & Entities + +### Core Entities +1. **Categories** - Hierarchical product categories with parent-child relationships +2. **Products** - Core product information with SKU, brand, and lifecycle tracking +3. **ProductPrices** - Time-based pricing with currency support and price history +4. **Inventory** - Real-time stock quantity tracking +5. **ProductAttributes** - Flexible key-value product specifications +6. **ProductReviews** - Customer review and rating system (1-5 stars) + +### Entity Relationships +- **Categories**: Self-referencing hierarchy (Parent β†’ Children) +- **Products**: Belongs to Category, has multiple Prices/Attributes/Reviews, and one Inventory +- **One-to-One**: Product ↔ Inventory +- **One-to-Many**: Category β†’ Products, Product β†’ (Prices, Attributes, Reviews) + +### Database Configuration +- **Primary Database**: SQLite (`inventory.db`) +- **Connection String**: Configured in `appsettings.json` +- **ORM**: Entity Framework Core 9.0.5 +- **Migration Support**: Full EF Core migrations with seed data +- **Alternative**: SQL Server support available (see `dbschema.sql`) + +## Development Guidelines + +### 1. **API Development** +- Follow RESTful API conventions +- Use proper HTTP status codes (200, 201, 400, 404, etc.) +- Implement comprehensive error handling with descriptive messages +- Use DTOs for API contracts (separate from entities) +- Apply dependency injection for services and repositories +- Document APIs with Swagger/OpenAPI annotations + +### 2. **Entity Framework Patterns** +- Use Entity Configurations (IEntityTypeConfiguration) for database mapping +- Implement proper navigation properties for relationships +- Use async/await for all database operations +- Apply appropriate EF Core conventions (HasKey, HasIndex, HasMaxLength) +- Leverage fluent API for complex configurations +- Use centralized seed data in `SeedData/` folder + +### 3. **Service Layer Design** +- Implement service interfaces in `Contracts/` folder +- Register services with appropriate lifetime (Scoped for database services) +- Use repository pattern through EF Core DbContext +- Implement proper exception handling and logging +- Return DTOs from services, not entities +- Apply business logic in service layer, not controllers + +### 4. **Testing Standards** +- Write unit tests for all controllers and services +- Use in-memory database for integration tests +- Mock external dependencies with Moq +- Use FluentAssertions for readable test assertions +- Follow AAA pattern (Arrange, Act, Assert) +- Test both success and failure scenarios + +### 5. **Code Organization** +- Use namespace conventions: `copilot_sample.{Layer}.{Feature}` +- Follow C# naming conventions (PascalCase for public members) +- Use nullable reference types (`string?` for optional properties) +- Implement proper using statements and imports +- Use record types for DTOs when appropriate + +## Configuration & Deployment + +### Environment Setup +- **Target Framework**: .NET 8.0 +- **Nullable**: Enabled +- **Implicit Usings**: Enabled +- **Default Ports**: HTTPS (7111), HTTP (5111) + +### Key Configuration Files +- `appsettings.json` - Connection strings and app settings +- `appsettings.Development.json` - Development-specific settings +- `launchSettings.json` - Development server configuration + +### Database Commands +```bash +# Add migration +dotnet ef migrations add MigrationName --startup-project ../copilot-sample.Api + +# Update database +dotnet ef database update --startup-project ../copilot-sample.Api + +# Remove last migration +dotnet ef migrations remove --startup-project ../copilot-sample.Api +``` + +### Running the Application +```bash +# Build solution +dotnet build + +# Run API (from Api directory) +cd copilot-sample.Api +dotnet run + +# Run tests +dotnet test +``` + +## API Endpoints Overview + +### Products API (`/api/Product`) +- `GET /api/Product` - Get all products +- `GET /api/Product/{id}` - Get product by ID +- `POST /api/Product` - Create new product +- `PUT /api/Product/{id}` - Update existing product +- `DELETE /api/Product/{id}` - Delete product + +### Categories API (`/api/Category`) +- `GET /api/Category` - Get all categories +- `GET /api/Category/{id}` - Get category by ID +- `POST /api/Category` - Create new category +- `PUT /api/Category/{id}` - Update existing category +- `DELETE /api/Category/{id}` - Delete category + +### Product Attributes API (`/api/ProductAttribute`) +- `GET /api/ProductAttribute/product/{productId}` - Get attributes for product +- `POST /api/ProductAttribute` - Add product attribute +- `PUT /api/ProductAttribute/{id}` - Update product attribute +- `DELETE /api/ProductAttribute/{id}` - Delete product attribute + +## Best Practices for GitHub Copilot + +### 1. **Context Awareness** +- Always reference the correct project layer when suggesting code +- Consider the existing entity relationships and constraints +- Respect the established naming conventions and patterns +- Use the correct namespace based on the file location + +### 2. **Entity Framework Guidelines** +- Suggest proper async/await patterns for database operations +- Use Include() for loading related data when needed +- Recommend appropriate EF Core methods (Find, First, Single, etc.) +- Consider performance implications of queries + +### 3. **API Design** +- Follow existing controller patterns and response formats +- Suggest proper validation and error handling +- Use consistent DTO mapping patterns +- Apply appropriate HTTP methods and status codes + +### 4. **Testing Recommendations** +- Create comprehensive test coverage for new features +- Use the established testing patterns and dependencies +- Mock database context appropriately for unit tests +- Test edge cases and error scenarios + +This project serves as a comprehensive example of modern .NET 8 API development with clean architecture, proper separation of concerns, and industry best practices for enterprise applications. diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/ICategoryService.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/ICategoryService.cs new file mode 100644 index 0000000..941bb8a --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/ICategoryService.cs @@ -0,0 +1,17 @@ +using copilot_sample.Api.Models.Dtos; + +namespace copilot_sample.Api.Services +{ + /// + /// Interface for managing categories in the application. + /// Provides methods for CRUD operations on categories. + /// + public interface ICategoryService + { + Task> GetCategoriesAsync(); + Task GetCategoryByIdAsync(int id); + Task AddCategoryAsync(AddCategoryDto addCategoryDto); + Task UpdateCategoryDescriptionAsync(int id, UpdateCategoryDescriptionDto updateDto); + Task DeleteCategoryAsync(int id); + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/IProductService.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/IProductService.cs new file mode 100644 index 0000000..f06ae12 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/IProductService.cs @@ -0,0 +1,17 @@ +using copilot_sample.Api.Models.Dtos; + +namespace copilot_sample.Api.Services +{ + /// + /// Interface for managing products in the application. + /// Provides methods for CRUD operations on products. + /// + public interface IProductService + { + Task> GetProductsAsync(); + Task GetProductByIdAsync(int id); + Task AddProductAsync(AddProductDto addProductDto); + Task UpdateProductAsync(int id, UpdateProductDto updateProductDto); + Task DeleteProductAsync(int id); + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/CategoryController.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/CategoryController.cs new file mode 100644 index 0000000..9f05ef5 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/CategoryController.cs @@ -0,0 +1,81 @@ +ο»Ώusing copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace copilot_sample.Api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class CategoryController : ControllerBase + { + private readonly ICategoryService _categoryService; + + public CategoryController(ICategoryService categoryService) + { + _categoryService = categoryService; + } + + [HttpGet] + public async Task GetCategories() + { + var categories = await _categoryService.GetCategoriesAsync(); + return Ok(categories); + } + + [HttpGet("{id}")] + public async Task GetCategoryById(int id) + { + var category = await _categoryService.GetCategoryByIdAsync(id); + if (category == null) + { + return NotFound(new { Message = $"Category with ID {id} not found." }); + } + return Ok(category); + } + + [HttpPost] + public async Task AddCategory([FromBody] AddCategoryDto addCategoryDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var category = await _categoryService.AddCategoryAsync(addCategoryDto); + if (category == null) + { + return Conflict(new { Message = $"A category with {addCategoryDto.Name} already exists." }); + } + return CreatedAtAction(nameof(GetCategoryById), new { id = category.CategoryID }, category); + } + + [HttpPatch("{id}/description")] + public async Task UpdateCategoryDescription(int id, [FromBody] UpdateCategoryDescriptionDto updateDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var success = await _categoryService.UpdateCategoryDescriptionAsync(id, updateDto); + if (!success) + { + return NotFound(new { Message = $"Category with ID {id} not found." }); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteCategory(int id) + { + var success = await _categoryService.DeleteCategoryAsync(id); + if (!success) + { + return NotFound(new { Message = $"Category with ID {id} not found." }); + } + + return NoContent(); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductAttributeController.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductAttributeController.cs new file mode 100644 index 0000000..03cbf00 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductAttributeController.cs @@ -0,0 +1,74 @@ +using copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace copilot_sample.Api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class ProductAttributeController : ControllerBase + { + private readonly ProductAttributeService _productAttributeService; + + public ProductAttributeController(ProductAttributeService productAttributeService) + { + _productAttributeService = productAttributeService; + } + + [HttpGet] + public async Task GetProductAttributes() + { + var attributes = await _productAttributeService.GetProductAttributesAsync(); + return Ok(attributes); + } + + [HttpGet("{id}")] + public async Task GetProductAttributeById(int id) + { + var attribute = await _productAttributeService.GetProductAttributeByIdAsync(id); + if (attribute == null) + { + return NotFound(new { Message = $"Product Attribute with ID {id} not found." }); + } + return Ok(attribute); + } + + [HttpPost] + public async Task AddProductAttribute([FromBody] AddProductAttributeDto addDto) + { + try + { + var attribute = await _productAttributeService.AddProductAttributeAsync(addDto); + return CreatedAtAction(nameof(GetProductAttributeById), new { id = attribute.AttributeID }, attribute); + } + catch (ArgumentException ex) + { + return BadRequest(new { Message = ex.Message }); + } + } + + [HttpPut("{id}")] + public async Task UpdateProductAttribute(int id, [FromBody] UpdateProductAttributeDto updateDto) + { + var success = await _productAttributeService.UpdateProductAttributeAsync(id, updateDto); + if (!success) + { + return NotFound(new { Message = $"Product Attribute with ID {id} not found." }); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteProductAttribute(int id) + { + var success = await _productAttributeService.DeleteProductAttributeAsync(id); + if (!success) + { + return NotFound(new { Message = $"Product Attribute with ID {id} not found." }); + } + + return NoContent(); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductController.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductController.cs new file mode 100644 index 0000000..f379a41 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductController.cs @@ -0,0 +1,74 @@ +using copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace copilot_sample.Api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class ProductController : ControllerBase + { + private readonly IProductService _productService; + + public ProductController(IProductService productService) + { + _productService = productService; + } + + [HttpGet] + public async Task GetProducts() + { + var products = await _productService.GetProductsAsync(); + return Ok(products); + } + + [HttpGet("{id}")] + public async Task GetProductById(int id) + { + var product = await _productService.GetProductByIdAsync(id); + if (product == null) + { + return NotFound(new { Message = $"Product with ID {id} not found." }); + } + return Ok(product); + } + + [HttpPost] + public async Task AddProduct([FromBody] AddProductDto addProductDto) + { + try + { + var product = await _productService.AddProductAsync(addProductDto); + return CreatedAtAction(nameof(GetProductById), new { id = product.ProductID }, product); + } + catch (ArgumentException ex) + { + return BadRequest(new { Message = "Invalid request.", Details = ex.Message }); + } + } + + [HttpPut("{id}")] + public async Task UpdateProduct(int id, [FromBody] UpdateProductDto updateProductDto) + { + var success = await _productService.UpdateProductAsync(id, updateProductDto); + if (!success) + { + return NotFound(new { Message = $"Product with ID {id} not found." }); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteProduct(int id) + { + var success = await _productService.DeleteProductAsync(id); + if (!success) + { + return NotFound(new { Message = $"Product with ID {id} not found." }); + } + + return NoContent(); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/DBSetup.md b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/DBSetup.md new file mode 100644 index 0000000..d22ef4b --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/DBSetup.md @@ -0,0 +1,196 @@ +# Database Setup + +## SQLite Setup (Current Configuration) + +1. The project is configured to use SQLite database with Entity Framework Core. +2. The database file `inventory.db` will be created automatically in the project root when you run the application. +3. You can use Entity Framework migrations to create and seed the database: + +```bash +# Navigate to the DataAccess project +cd copilot-sample.DataAccess + +# Add initial migration +dotnet ef migrations add InitialCreate --startup-project ../copilot-sample.Api + +# Update database (creates SQLite file and tables) +dotnet ef database update --startup-project ../copilot-sample.Api +``` + +4. Alternatively, you can manually create the database using the provided SQLite schema file: + +```bash +# From the Api project directory +sqlite3 inventory.db < sqlite-schema.sql +``` + +## Original SQL Server Setup (For Reference) + +1. Install MS SQL Server (Developer or Express) on localhost. + Download: +2. Create a database named **Inventory**. +3. Run the following DDL statements to create the necessary tables: + +```sql +-- Inventory Database schema + +-- Categories Table +CREATE TABLE Categories ( + CategoryID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(100) NOT NULL, + Description NVARCHAR(500), + ParentCategoryID INT NULL, + FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID) +); + +-- Products Table +CREATE TABLE Products ( + ProductID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(200) NOT NULL, + Description NVARCHAR(MAX), + SKU NVARCHAR(100) UNIQUE NOT NULL, + CategoryID INT NOT NULL, + Brand NVARCHAR(100), + CreatedAt DATETIME DEFAULT GETDATE(), + UpdatedAt DATETIME DEFAULT GETDATE(), + IsActive BIT DEFAULT 1, + FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID) +); + +-- Pricing Table +CREATE TABLE ProductPrices ( + PriceID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Price DECIMAL(18, 2) NOT NULL, + CurrencyCode CHAR(3) DEFAULT 'USD', + EffectiveFrom DATETIME DEFAULT GETDATE(), + EffectiveTill DATETIME NULL, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +-- Inventory Table +CREATE TABLE Inventory ( + InventoryID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Quantity INT NOT NULL, + LastUpdated DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +-- Product Attributes +CREATE TABLE ProductAttributes ( + AttributeID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + AttributeName NVARCHAR(100), + AttributeValue NVARCHAR(255), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +-- Product Reviews +CREATE TABLE ProductReviews ( + ReviewID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + ReviewerName NVARCHAR(100), + Rating INT CHECK (Rating BETWEEN 1 AND 5), + Comment NVARCHAR(MAX), + ReviewDate DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); +``` + +4. Run the following DML statements to insert sample data: + +```sql +-- Insert Categories +INSERT INTO Categories (Name, Description, ParentCategoryID) VALUES +('Electronics', 'Electronic gadgets and devices', NULL), +('Laptops', 'Portable computers', 1), +('Smartphones', 'Mobile phones and accessories', 1), +('Accessories', 'Electronics accessories', 1); + +-- Insert Products +INSERT INTO Products (Name, Description, SKU, CategoryID, Brand) VALUES +('UltraBook X1', 'Lightweight business laptop with high performance', 'SKU-UBX1', 2, 'TechBrand'), +('Gaming Beast Z9', 'High-end gaming laptop with RGB lighting', 'SKU-GBZ9', 2, 'GamePro'), +('Galaxy X10', 'Latest smartphone with AI-powered camera', 'SKU-GX10', 3, 'SmartTech'), +('EarPods Pro', 'Wireless earphones with noise cancellation', 'SKU-EPP', 4, 'SoundWave'); + +-- Insert Product Prices +INSERT INTO ProductPrices (ProductID, Price, CurrencyCode, EffectiveFrom, EffectiveTill) VALUES +(1, 1299.99, 'USD', '2025-01-01', NULL), +(1, 1199.99, 'USD', '2024-10-01', '2024-12-31'), +(2, 1999.00, 'USD', '2025-01-15', NULL), +(3, 899.50, 'USD', '2025-03-15', NULL), +(4, 199.99, 'USD', '2025-02-01', NULL); + +-- Insert Inventory +INSERT INTO Inventory (ProductID, Quantity) VALUES +(1, 25), +(2, 10), +(3, 50), +(4, 100); + +-- Insert Product Attributes +INSERT INTO ProductAttributes (ProductID, AttributeName, AttributeValue) VALUES +(1, 'Processor', 'Intel Core i7'), +(1, 'RAM', '16GB'), +(1, 'Storage', '512GB SSD'), +(2, 'Processor', 'AMD Ryzen 9'), +(2, 'RAM', '32GB'), +(2, 'Graphics Card', 'NVIDIA RTX 4080'), +(3, 'Display', '6.5-inch OLED'), +(3, 'Battery', '4000mAh'), +(3, 'Camera', '108MP'), +(4, 'Connectivity', 'Bluetooth 5.2'), +(4, 'Noise Cancellation', 'Active'), +(4, 'Battery Life', '8 hours'); + +-- Insert Product Reviews +INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES +(1, 'Alice Johnson', 5, 'Absolutely love this laptop! Fast and sleek.'), +(1, 'Bob Smith', 4, 'Good performance but gets a bit warm.'), +(2, 'Tommy Lee', 5, 'A beast for gaming. Smooth experience!'), +(3, 'Carlos Vega', 5, 'Amazing camera and display! Worth every penny.'), +(3, 'Diana Lee', 3, 'Battery life could be better.'), +(4, 'Emma Stone', 4, 'Very comfortable fit and great sound.'), +(4, 'John Doe', 2, 'Connection drops sometimes.'); + +-- More Products +INSERT INTO Products (Name, Description, SKU, CategoryID, Brand) VALUES +('ThinkMate Pro 14', 'Durable business laptop with excellent battery life', 'SKU-TMP14', 2, 'ThinkCorp'), +('PixelCam A2', 'Compact smartphone with high-resolution camera', 'SKU-PXA2', 3, 'PixelTech'), +('PowerCharge 10000', 'Portable power bank with fast charging support', 'SKU-PC10000', 4, 'ChargeX'); + +-- More Product Prices +INSERT INTO ProductPrices (ProductID, Price, CurrencyCode, EffectiveFrom, EffectiveTill) VALUES +(5, 999.99, 'USD', '2025-04-01', NULL), +(6, 649.00, 'USD', '2025-04-15', NULL), +(7, 49.99, 'USD', '2025-03-01', NULL); + +-- More Inventory +INSERT INTO Inventory (ProductID, Quantity) VALUES +(5, 30), +(6, 45), +(7, 200); + +-- More Product Attributes +INSERT INTO ProductAttributes (ProductID, AttributeName, AttributeValue) VALUES +(5, 'Processor', 'Intel i5'), +(5, 'RAM', '8GB'), +(5, 'Weight', '1.3kg'), +(6, 'Camera', '64MP'), +(6, 'Storage', '128GB'), +(6, 'Display', '6.1-inch AMOLED'), +(7, 'Capacity', '10000mAh'), +(7, 'USB Ports', '2'), +(7, 'Fast Charging', 'Yes'); + +-- More Product Reviews +INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES +(5, 'Harvey Dent', 4, 'Reliable laptop, solid build and decent performance.'), +(5, 'Rachel Green', 3, 'Good for light work, but a bit slow for multitasking.'), +(6, 'Bruce Banner', 5, 'Compact yet powerful. Great value for the price.'), +(6, 'Natasha Romanoff', 4, 'Excellent camera quality and battery.'), +(7, 'Steve Rogers', 5, 'Lasts all day. Perfect for travel.'), +(7, 'Tony Stark', 3, 'Charges fast, but gets warm during use.'); +``` diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/CategoryMappings.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/CategoryMappings.cs new file mode 100644 index 0000000..84197f7 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/CategoryMappings.cs @@ -0,0 +1,52 @@ +using System.Linq.Expressions; +using copilot_sample.DataAccess.Entities; + +namespace copilot_sample.Api.Models.Dtos +{ + public static class CategoryMappings + { + private static readonly Func _compiledMapper = null!; + + static CategoryMappings() + { + _compiledMapper = MapCategory; + } + + private static CategoryDto MapCategory(Category category) + { + return new CategoryDto + { + CategoryID = category.CategoryID, + Name = category.Name, + Description = category.Description, + ParentCategoryID = category.ParentCategoryID, + SubCategories = category.SubCategories?.Select(sc => MapCategory(sc)).ToList() + }; + } + + /// + /// Expression to map a Category entity to a CategoryDto without subcategories. + /// + private static readonly Expression> ToCategoryDto = category => new CategoryDto + { + CategoryID = category.CategoryID, + Name = category.Name, + Description = category.Description, + ParentCategoryID = category.ParentCategoryID, + SubCategories = null // Subcategories will be mapped separately + }; + + /// + /// Method to map subcategories after the main query is executed. + /// + public static CategoryDto MapWithSubcategories(Category category) + { + var dto = _compiledMapper(category); + if (category.SubCategories != null) + { + dto.SubCategories = category.SubCategories.Select(MapWithSubcategories).ToList(); + } + return dto; + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/AddCategoryDto.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/AddCategoryDto.cs new file mode 100644 index 0000000..ad57b8a --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/AddCategoryDto.cs @@ -0,0 +1,13 @@ +namespace copilot_sample.Api.Models.Dtos +{ + public class AddCategoryDto + { + public string Name { get; set; } = null!; + public string? Description { get; set; } + public int? ParentCategoryID { get; set; } + } + public class UpdateCategoryDescriptionDto + { + public string? Description { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/CategoryDto.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/CategoryDto.cs new file mode 100644 index 0000000..493be18 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/CategoryDto.cs @@ -0,0 +1,11 @@ +namespace copilot_sample.Api.Models.Dtos +{ + public class CategoryDto + { + public int CategoryID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public int? ParentCategoryID { get; set; } + public List? SubCategories { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductAttributeDto.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductAttributeDto.cs new file mode 100644 index 0000000..6a396b0 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductAttributeDto.cs @@ -0,0 +1,24 @@ +namespace copilot_sample.Api.Models.Dtos +{ + public class ProductAttributeDto + { + public int AttributeID { get; set; } + public int ProductID { get; set; } + public string AttributeName { get; set; } = null!; + public string AttributeValue { get; set; } = null!; + } + + public class AddProductAttributeDto + { + public int ProductID { get; set; } + public string AttributeName { get; set; } = null!; + public string AttributeValue { get; set; } = null!; + } + + public class UpdateProductAttributeDto + { + public required string AttributeName { get; set; } + public required string AttributeValue { get; set; } + } + +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductDto.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductDto.cs new file mode 100644 index 0000000..820705b --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductDto.cs @@ -0,0 +1,37 @@ +namespace copilot_sample.Api.Models.Dtos +{ + public class ProductDto + { + public int ProductID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string SKU { get; set; } = null!; + public int CategoryID { get; set; } + public string? Brand { get; set; } + public bool IsActive { get; set; } + + public CategoryDto Category { get; set; } + } + + public class AddProductDto + { + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string SKU { get; set; } = null!; + public int CategoryID { get; set; } + public string? Brand { get; set; } + public bool IsActive { get; set; } + } + + public class UpdateProductDto + { + public string? Name { get; set; } + public string? Description { get; set; } + public string? SKU { get; set; } + public int? CategoryID { get; set; } + public string? Brand { get; set; } + public bool? IsActive { get; set; } + } + + +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Program.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Program.cs new file mode 100644 index 0000000..574b500 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Program.cs @@ -0,0 +1,55 @@ +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Extensions; +using copilot_sample.Api.Services; +using Microsoft.EntityFrameworkCore; +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddControllers().AddJsonOptions(options => +{ + options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; +}); + +// Register CategoryService +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +// Configure Entity Framework Core with SQLite +builder.Services.AddDbContext(options => + options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"))); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +// Initialize database using the extension methods from DataAccess layer +try +{ + await app.Services.SetupDatabaseAsync(); + app.Logger.LogInformation("Database initialized successfully."); +} +catch (Exception ex) +{ + app.Logger.LogError(ex, "Failed to initialize database. Application will continue but may not function properly."); +} + +app.Run(); diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Properties/launchSettings.json b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Properties/launchSettings.json new file mode 100644 index 0000000..b084176 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Properties/launchSettings.json @@ -0,0 +1,40 @@ +ο»Ώ{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:43099", + "sslPort": 44395 + } + }, "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5111", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7111;http://localhost:5111", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/CategoryService.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/CategoryService.cs new file mode 100644 index 0000000..0820586 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/CategoryService.cs @@ -0,0 +1,84 @@ +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using copilot_sample.Api.Models.Dtos; +using Microsoft.EntityFrameworkCore; + +namespace copilot_sample.Api.Services +{ + /// + /// Service for managing categories in the application. + /// Provides methods for CRUD operations on categories. + /// + public class CategoryService : ICategoryService + { + private readonly AppDbContext _dbContext; + + /// + /// Initializes a new instance of the class. + /// + /// The database context to interact with the database. + public CategoryService(AppDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetCategoriesAsync() + { + return await _dbContext.Categories + .Include(c => c.SubCategories) + .Select(i => CategoryMappings.MapWithSubcategories(i)) + .ToListAsync(); + } + + public async Task GetCategoryByIdAsync(int id) + { + var category = await _dbContext.Categories + .Include(c => c.SubCategories) + .FirstOrDefaultAsync(c => c.CategoryID == id); + + if (category == null) + { + return null; + } + + // Use the mapping expression to map the category to a CategoryDto + return CategoryMappings.MapWithSubcategories(category); + } + + public async Task AddCategoryAsync(AddCategoryDto addCategoryDto) + { + var category = new Category + { + Name = addCategoryDto.Name, + Description = addCategoryDto.Description, + ParentCategoryID = addCategoryDto.ParentCategoryID + }; + + _dbContext.Categories.Add(category); + await _dbContext.SaveChangesAsync(); + + // Use the mapping expression to map the newly created category to a CategoryDto + return CategoryMappings.MapWithSubcategories(category); + } + + public async Task UpdateCategoryDescriptionAsync(int id, UpdateCategoryDescriptionDto updateDto) + { + var category = await _dbContext.Categories.FindAsync(id); + if (category == null) + { + return false; + } + + category.Description = updateDto.Description; + _dbContext.Categories.Update(category); + await _dbContext.SaveChangesAsync(); + + return true; + } + + public async Task DeleteCategoryAsync(int id) + { + return true; + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductAttributeService.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductAttributeService.cs new file mode 100644 index 0000000..82a1bb8 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductAttributeService.cs @@ -0,0 +1,89 @@ +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using copilot_sample.Api.Models.Dtos; +using Microsoft.EntityFrameworkCore; + +namespace copilot_sample.Api.Services +{ + public class ProductAttributeService + { + private readonly AppDbContext _dbContext; + + public ProductAttributeService(AppDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetProductAttributesAsync() + { + var attributes = await _dbContext.ProductAttributes.ToListAsync(); + return attributes.Select(a => new ProductAttributeDto + { + AttributeID = a.AttributeID, + ProductID = a.ProductID, + AttributeName = a.AttributeName, + AttributeValue = a.AttributeValue + }).ToList(); + } + + public async Task GetProductAttributeByIdAsync(int id) + { + var attribute = await _dbContext.ProductAttributes.FindAsync(id); + if (attribute == null) return null; + + return new ProductAttributeDto + { + AttributeID = attribute.AttributeID, + ProductID = attribute.ProductID, + AttributeName = attribute.AttributeName, + AttributeValue = attribute.AttributeValue + }; + } + + public async Task AddProductAttributeAsync(AddProductAttributeDto addDto) + { + var product = await _dbContext.Products.FindAsync(addDto.ProductID); + if (product == null) + { + throw new ArgumentException($"Product with ID {addDto.ProductID} does not exist."); + } + + var attribute = new ProductAttribute + { + ProductID = addDto.ProductID, + AttributeName = addDto.AttributeName, + AttributeValue = addDto.AttributeValue + }; + + _dbContext.ProductAttributes.Add(attribute); + await _dbContext.SaveChangesAsync(); + + return attribute; + } + + public async Task UpdateProductAttributeAsync(int id, UpdateProductAttributeDto updateDto) + { + var attribute = await _dbContext.ProductAttributes.FindAsync(id); + if (attribute == null) return false; + + attribute.AttributeName = updateDto.AttributeName; + attribute.AttributeValue = updateDto.AttributeValue; + + _dbContext.ProductAttributes.Update(attribute); + await _dbContext.SaveChangesAsync(); + + return true; + } + + public async Task DeleteProductAttributeAsync(int id) + { + var attribute = await _dbContext.ProductAttributes.FindAsync(id); + if (attribute == null) return false; + + _dbContext.ProductAttributes.Remove(attribute); + await _dbContext.SaveChangesAsync(); + + return true; + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductService.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductService.cs new file mode 100644 index 0000000..c6bf584 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductService.cs @@ -0,0 +1,121 @@ +using copilot_sample.Api.Models.Dtos; +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using Microsoft.EntityFrameworkCore; + +namespace copilot_sample.Api.Services +{ + /// + /// Service for managing products in the application. + /// Provides methods for CRUD operations on products. + /// + public class ProductService : IProductService + { + private readonly AppDbContext _dbContext; + + /// + /// Initializes a new instance of the class. + /// + /// The database context to interact with the database. + public ProductService(AppDbContext dbContext) + { + _dbContext = dbContext; + } public async Task> GetProductsAsync() + { + var products = await _dbContext.Products.ToListAsync(); + return products.Select(p => new ProductDto + { + ProductID = p.ProductID, + Name = p.Name, + Description = p.Description, + SKU = p.SKU, + CategoryID = p.CategoryID, + Brand = p.Brand, + IsActive = p.IsActive, + Category = new CategoryDto + { + CategoryID = p.Category.CategoryID, + Name = p.Category.Name, + Description = p.Category.Description, + + } + }).ToList(); + } + + public async Task GetProductByIdAsync(int id) + { + var product = await _dbContext.Products.FindAsync(id); + if (product == null) return null; + + return new ProductDto + { + ProductID = product.ProductID, + Name = product.Name, + Description = product.Description, + SKU = product.SKU, + CategoryID = product.CategoryID, + Brand = product.Brand, + IsActive = product.IsActive + }; + } + + public async Task AddProductAsync(AddProductDto addProductDto) + { + var product = new Product + { + Name = addProductDto.Name, + Description = addProductDto.Description, + SKU = addProductDto.SKU, + CategoryID = addProductDto.CategoryID, + Brand = addProductDto.Brand, + IsActive = addProductDto.IsActive, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + _dbContext.Products.Add(product); + await _dbContext.SaveChangesAsync(); + + return new ProductDto + { + ProductID = product.ProductID, + Name = product.Name, + Description = product.Description, + SKU = product.SKU, + CategoryID = product.CategoryID, + Brand = product.Brand, + IsActive = product.IsActive + }; + } + + public async Task UpdateProductAsync(int id, UpdateProductDto updateProductDto) + { + var product = await _dbContext.Products.FindAsync(id); + if (product == null) return false; + + product.Name = updateProductDto.Name ?? product.Name; + product.Description = updateProductDto.Description ?? product.Description; + product.SKU = updateProductDto.SKU ?? product.SKU; + product.CategoryID = updateProductDto.CategoryID ?? product.CategoryID; + product.Brand = updateProductDto.Brand ?? product.Brand; + product.IsActive = updateProductDto.IsActive ?? product.IsActive; + product.UpdatedAt = DateTime.UtcNow; + + _dbContext.Products.Update(product); + await _dbContext.SaveChangesAsync(); + + return true; + } + + public async Task DeleteProductAsync(int id) + { + var product = await _dbContext.Products.FindAsync(id); + if (product == null) return false; + + _dbContext.Products.Remove(product); + await _dbContext.SaveChangesAsync(); + + return true; + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.Development.json b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.Development.json new file mode 100644 index 0000000..cfbe1d4 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=inventory.db" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.json b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.json new file mode 100644 index 0000000..2654ac8 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.json @@ -0,0 +1,12 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=inventory.db" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj new file mode 100644 index 0000000..fd0cc68 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj @@ -0,0 +1,33 @@ +ο»Ώ + + + net8.0 + enable + enable + copilot_sample + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.http b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.http new file mode 100644 index 0000000..e7882e3 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.http @@ -0,0 +1,19 @@ +@copilot_sample_HostAddress = http://localhost:5111 + +# Test Categories API +GET {{copilot_sample_HostAddress}}/api/Category +Accept: application/json + +### + +# Test Products API +GET {{copilot_sample_HostAddress}}/api/Product +Accept: application/json + +### + +# Test Swagger endpoint +GET {{copilot_sample_HostAddress}}/swagger +Accept: text/html + +### diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/dbschema.sql b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/dbschema.sql new file mode 100644 index 0000000..fceb5b0 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/dbschema.sql @@ -0,0 +1,62 @@ +ο»Ώ--Categories Table +CREATE TABLE Categories ( + CategoryID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(100) NOT NULL, + Description NVARCHAR(500), + ParentCategoryID INT NULL, + FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID) +); + +--Products Table +CREATE TABLE Products ( + ProductID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(200) NOT NULL, + Description NVARCHAR(MAX), + SKU NVARCHAR(100) UNIQUE NOT NULL, + CategoryID INT NOT NULL, + Brand NVARCHAR(100), + CreatedAt DATETIME DEFAULT GETDATE(), + UpdatedAt DATETIME DEFAULT GETDATE(), + IsActive BIT DEFAULT 1, + FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID) +); + +--Pricing Table +CREATE TABLE ProductPrices ( + PriceID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Price DECIMAL(18, 2) NOT NULL, + CurrencyCode CHAR(3) DEFAULT 'USD', + EffectiveFrom DATETIME DEFAULT GETDATE(), + EffectiveTill DATETIME NULL, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Inventory Table +CREATE TABLE Inventory ( + InventoryID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Quantity INT NOT NULL, + LastUpdated DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Product Attributes +CREATE TABLE ProductAttributes ( + AttributeID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + AttributeName NVARCHAR(100), + AttributeValue NVARCHAR(255), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Product Reviews +CREATE TABLE ProductReviews ( + ReviewID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + ReviewerName NVARCHAR(100), + Rating INT CHECK (Rating BETWEEN 1 AND 5), + Comment NVARCHAR(MAX), + ReviewDate DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db new file mode 100644 index 0000000..1869a2e Binary files /dev/null and b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db differ diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db-shm b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db-shm new file mode 100644 index 0000000..091e917 Binary files /dev/null and b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db-shm differ diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db-wal b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db-wal new file mode 100644 index 0000000..adccd94 Binary files /dev/null and b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db-wal differ diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db.png b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db.png new file mode 100644 index 0000000..ae62202 Binary files /dev/null and b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/inventory.db.png differ diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/sqlite-schema.sql b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/sqlite-schema.sql new file mode 100644 index 0000000..b2027c6 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/sqlite-schema.sql @@ -0,0 +1,159 @@ +-- SQLite Database Schema for Inventory System +-- Note: SQLite uses AUTOINCREMENT instead of IDENTITY and different data types + +-- Categories Table +CREATE TABLE Categories ( + CategoryID INTEGER PRIMARY KEY AUTOINCREMENT, + Name TEXT NOT NULL, + Description TEXT, + ParentCategoryID INTEGER, + FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID) +); + +-- Products Table +CREATE TABLE Products ( + ProductID INTEGER PRIMARY KEY AUTOINCREMENT, + Name TEXT NOT NULL, + Description TEXT, + SKU TEXT UNIQUE NOT NULL, + CategoryID INTEGER NOT NULL, + Brand TEXT, + CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP, + UpdatedAt DATETIME DEFAULT CURRENT_TIMESTAMP, + IsActive INTEGER DEFAULT 1, + FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID) +); + +-- Pricing Table +CREATE TABLE ProductPrices ( + PriceID INTEGER PRIMARY KEY AUTOINCREMENT, + ProductID INTEGER NOT NULL, + Price DECIMAL(18, 2) NOT NULL, + CurrencyCode TEXT DEFAULT 'USD', + EffectiveFrom DATETIME DEFAULT CURRENT_TIMESTAMP, + EffectiveTill DATETIME, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +-- Inventory Table +CREATE TABLE Inventory ( + InventoryID INTEGER PRIMARY KEY AUTOINCREMENT, + ProductID INTEGER NOT NULL, + Quantity INTEGER NOT NULL, + LastUpdated DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +-- Product Attributes +CREATE TABLE ProductAttributes ( + AttributeID INTEGER PRIMARY KEY AUTOINCREMENT, + ProductID INTEGER NOT NULL, + AttributeName TEXT, + AttributeValue TEXT, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +-- Product Reviews +CREATE TABLE ProductReviews ( + ReviewID INTEGER PRIMARY KEY AUTOINCREMENT, + ProductID INTEGER NOT NULL, + ReviewerName TEXT, + Rating INTEGER CHECK (Rating BETWEEN 1 AND 5), + Comment TEXT, + ReviewDate DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +-- Insert Sample Data +-- Insert Categories +INSERT INTO Categories (Name, Description, ParentCategoryID) VALUES +('Electronics', 'Electronic gadgets and devices', NULL), +('Laptops', 'Portable computers', 1), +('Smartphones', 'Mobile phones and accessories', 1), +('Accessories', 'Electronics accessories', 1); + +-- Insert Products +INSERT INTO Products (Name, Description, SKU, CategoryID, Brand) VALUES +('UltraBook X1', 'Lightweight business laptop with high performance', 'SKU-UBX1', 2, 'TechBrand'), +('Gaming Beast Z9', 'High-end gaming laptop with RGB lighting', 'SKU-GBZ9', 2, 'GamePro'), +('Galaxy X10', 'Latest smartphone with AI-powered camera', 'SKU-GX10', 3, 'SmartTech'), +('EarPods Pro', 'Wireless earphones with noise cancellation', 'SKU-EPP', 4, 'SoundWave'); + +-- Insert Product Prices +INSERT INTO ProductPrices (ProductID, Price, CurrencyCode, EffectiveFrom, EffectiveTill) VALUES +(1, 1299.99, 'USD', '2025-01-01', NULL), +(1, 1199.99, 'USD', '2024-10-01', '2024-12-31'), +(2, 1999.00, 'USD', '2025-01-15', NULL), +(3, 899.50, 'USD', '2025-03-15', NULL), +(4, 199.99, 'USD', '2025-02-01', NULL); + +-- Insert Inventory +INSERT INTO Inventory (ProductID, Quantity) VALUES +(1, 25), +(2, 10), +(3, 50), +(4, 100); + +-- Insert Product Attributes +INSERT INTO ProductAttributes (ProductID, AttributeName, AttributeValue) VALUES +(1, 'Processor', 'Intel Core i7'), +(1, 'RAM', '16GB'), +(1, 'Storage', '512GB SSD'), +(2, 'Processor', 'AMD Ryzen 9'), +(2, 'RAM', '32GB'), +(2, 'Graphics Card', 'NVIDIA RTX 4080'), +(3, 'Display', '6.5-inch OLED'), +(3, 'Battery', '4000mAh'), +(3, 'Camera', '108MP'), +(4, 'Connectivity', 'Bluetooth 5.2'), +(4, 'Noise Cancellation', 'Active'), +(4, 'Battery Life', '8 hours'); + +-- Insert Product Reviews +INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES +(1, 'Alice Johnson', 5, 'Absolutely love this laptop! Fast and sleek.'), +(1, 'Bob Smith', 4, 'Good performance but gets a bit warm.'), +(2, 'Tommy Lee', 5, 'A beast for gaming. Smooth experience!'), +(3, 'Carlos Vega', 5, 'Amazing camera and display! Worth every penny.'), +(3, 'Diana Lee', 3, 'Battery life could be better.'), +(4, 'Emma Stone', 4, 'Very comfortable fit and great sound.'), +(4, 'John Doe', 2, 'Connection drops sometimes.'); + +-- More Products +INSERT INTO Products (Name, Description, SKU, CategoryID, Brand) VALUES +('ThinkMate Pro 14', 'Durable business laptop with excellent battery life', 'SKU-TMP14', 2, 'ThinkCorp'), +('PixelCam A2', 'Compact smartphone with high-resolution camera', 'SKU-PXA2', 3, 'PixelTech'), +('PowerCharge 10000', 'Portable power bank with fast charging support', 'SKU-PC10000', 4, 'ChargeX'); + +-- More Product Prices +INSERT INTO ProductPrices (ProductID, Price, CurrencyCode, EffectiveFrom, EffectiveTill) VALUES +(5, 999.99, 'USD', '2025-04-01', NULL), +(6, 649.00, 'USD', '2025-04-15', NULL), +(7, 49.99, 'USD', '2025-03-01', NULL); + +-- More Inventory +INSERT INTO Inventory (ProductID, Quantity) VALUES +(5, 30), +(6, 45), +(7, 200); + +-- More Product Attributes +INSERT INTO ProductAttributes (ProductID, AttributeName, AttributeValue) VALUES +(5, 'Processor', 'Intel i5'), +(5, 'RAM', '8GB'), +(5, 'Weight', '1.3kg'), +(6, 'Camera', '64MP'), +(6, 'Storage', '128GB'), +(6, 'Display', '6.1-inch AMOLED'), +(7, 'Capacity', '10000mAh'), +(7, 'USB Ports', '2'), +(7, 'Fast Charging', 'Yes'); + +-- More Product Reviews +INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES +(5, 'Harvey Dent', 4, 'Reliable laptop, solid build and decent performance.'), +(5, 'Rachel Green', 3, 'Good for light work, but a bit slow for multitasking.'), +(6, 'Bruce Banner', 5, 'Compact yet powerful. Great value for the price.'), +(6, 'Natasha Romanoff', 4, 'Excellent camera quality and battery.'), +(7, 'Steve Rogers', 5, 'Lasts all day. Perfect for travel.'), +(7, 'Tony Stark', 3, 'Charges fast, but gets warm during use.'); diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/AppDbContext.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/AppDbContext.cs new file mode 100644 index 0000000..3675189 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/AppDbContext.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using copilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.EntityConfiguration; +using copilot_sample.DataAccess.SeedData; + +namespace copilot_sample.DataAccess +{ + public class AppDbContext : DbContext + { + public AppDbContext(DbContextOptions options) : base(options) + { + } + + // Define DbSet properties for your entities + public DbSet Categories { get; set; } + public DbSet Products { get; set; } + public DbSet ProductPrices { get; set; } + public DbSet Inventory { get; set; } + public DbSet ProductAttributes { get; set; } + public DbSet ProductReviews { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // Apply entity configurations + modelBuilder.ApplyConfiguration(new CategoryConfiguration()); + modelBuilder.ApplyConfiguration(new ProductConfiguration()); + modelBuilder.ApplyConfiguration(new ProductPriceConfiguration()); + modelBuilder.ApplyConfiguration(new InventoryConfiguration()); + modelBuilder.ApplyConfiguration(new ProductAttributeConfiguration()); + modelBuilder.ApplyConfiguration(new ProductReviewConfiguration()); + + // Seed data + SeedData(modelBuilder); + } + + private void SeedData(ModelBuilder modelBuilder) + { + // Use centralized seed data from the SeedData folder + InventorySeedData.ApplyToModelBuilder(modelBuilder); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/DatabaseInitializer.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/DatabaseInitializer.cs new file mode 100644 index 0000000..0b7cc1d --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/DatabaseInitializer.cs @@ -0,0 +1,164 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using copilot_sample.DataAccess.SeedData; + +namespace copilot_sample.DataAccess +{ + /// + /// Provides database initialization and seeding functionality. + /// Handles database creation, migration application, and optional runtime seeding. + /// + public static class DatabaseInitializer + { + /// + /// Initializes the database by ensuring it exists and applying pending migrations. + /// This method should be called during application startup. + /// + /// The service provider to resolve dependencies + /// Optional logger for tracking initialization progress + public static async Task InitializeAsync(IServiceProvider serviceProvider, ILogger? logger = null) + { + try + { + using var scope = serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + logger?.LogInformation("Starting database initialization..."); + + // Ensure database is created and apply pending migrations + await context.Database.MigrateAsync(); + + logger?.LogInformation("Database initialization completed successfully. All migrations have been applied."); + + // Check if data exists (seed data is handled by migrations via AppDbContext.SeedData) + var hasData = await context.Categories.AnyAsync(); + if (hasData) + { + logger?.LogInformation("Database contains seed data from migrations."); + } + else + { + logger?.LogWarning("Database appears to be empty. Seed data should be applied via migrations."); + } + } + catch (Exception ex) + { + logger?.LogError(ex, "An error occurred while initializing the database."); + throw; + } + } + /// + /// Alternative method for runtime seeding if needed (not recommended for production). + /// Use this only if you need to add data that's not part of migrations. + /// + /// The service provider to resolve dependencies + /// Optional logger for tracking seeding progress + /// If true, will clear existing data and reseed + public static async Task SeedDataAsync(IServiceProvider serviceProvider, ILogger? logger = null, bool forceReseed = false) + { + try + { + using var scope = serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + // Check if seeding is needed + var hasData = await context.Categories.AnyAsync(); + if (hasData && !forceReseed) + { + logger?.LogInformation("Database already contains data. Skipping runtime seeding."); + return; + } + + if (forceReseed && hasData) + { + logger?.LogInformation("Force reseed requested. Clearing existing data..."); + await ClearExistingDataAsync(context, logger); + } + + logger?.LogInformation("Starting runtime database seeding..."); + + // Apply seed data using the centralized seed data class + await InventorySeedData.ApplyToContextAsync(context); + + // Log seeding results + var counts = InventorySeedData.GetSeedDataCounts(); + logger?.LogInformation($"Runtime seeding completed successfully. Added: {counts.Categories} categories, {counts.Products} products, {counts.Prices} prices, {counts.Inventory} inventory items, {counts.Attributes} attributes, {counts.Reviews} reviews."); + } + catch (Exception ex) + { + logger?.LogError(ex, "An error occurred while performing runtime database seeding."); + throw; + } + } + + /// + /// Clears all existing data from the database (in reverse dependency order). + /// WARNING: This will delete all data in the database! + /// + /// The database context + /// Optional logger + private static async Task ClearExistingDataAsync(AppDbContext context, ILogger? logger = null) + { + logger?.LogWarning("Clearing all existing data from database..."); + + // Delete in reverse dependency order to avoid foreign key constraints + context.ProductReviews.RemoveRange(context.ProductReviews); + context.ProductAttributes.RemoveRange(context.ProductAttributes); + context.ProductPrices.RemoveRange(context.ProductPrices); + context.Inventory.RemoveRange(context.Inventory); + context.Products.RemoveRange(context.Products); + context.Categories.RemoveRange(context.Categories); + + await context.SaveChangesAsync(); + logger?.LogInformation("All existing data cleared from database."); + } + + /// + /// Validates that the database schema and seed data are properly configured. + /// + /// The service provider to resolve dependencies + /// Optional logger for validation messages + /// True if validation passes, false otherwise + public static async Task ValidateDatabaseAsync(IServiceProvider serviceProvider, ILogger? logger = null) + { + try + { + using var scope = serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + // Check if database can be accessed + var canConnect = await context.Database.CanConnectAsync(); + if (!canConnect) + { + logger?.LogError("Cannot connect to the database."); + return false; + } + + // Check if required tables exist with data + var categoriesCount = await context.Categories.CountAsync(); + var productsCount = await context.Products.CountAsync(); + var inventoryCount = await context.Inventory.CountAsync(); + + logger?.LogInformation($"Database validation: Categories={categoriesCount}, Products={productsCount}, Inventory={inventoryCount}"); + + // Basic validation - should have seed data + if (categoriesCount >= 4 && productsCount >= 7 && inventoryCount >= 7) + { + logger?.LogInformation("Database validation passed."); + return true; + } + else + { + logger?.LogWarning("Database validation failed. Expected seed data not found."); + return false; + } + } + catch (Exception ex) + { + logger?.LogError(ex, "An error occurred during database validation."); + return false; + } + } + } +} \ No newline at end of file diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Category.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Category.cs new file mode 100644 index 0000000..13d354d --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Category.cs @@ -0,0 +1,15 @@ +namespace copilot_sample.DataAccess.Entities +{ + public class Category + { + public int CategoryID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public int? ParentCategoryID { get; set; } + + // Navigation properties + public Category? ParentCategory { get; set; } + public ICollection? SubCategories { get; set; } + public ICollection? Products { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Inventory.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Inventory.cs new file mode 100644 index 0000000..42af7a4 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Inventory.cs @@ -0,0 +1,13 @@ +namespace copilot_sample.DataAccess.Entities +{ + public class Inventory + { + public int InventoryID { get; set; } + public int ProductID { get; set; } + public int Quantity { get; set; } + public DateTime LastUpdated { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Product.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Product.cs new file mode 100644 index 0000000..afe0daa --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Product.cs @@ -0,0 +1,22 @@ +namespace copilot_sample.DataAccess.Entities +{ + public class Product + { + public int ProductID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string SKU { get; set; } = null!; + public int CategoryID { get; set; } + public string? Brand { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + public bool IsActive { get; set; } + + // Navigation properties + public Category? Category { get; set; } + public ICollection? ProductPrices { get; set; } + public Inventory? Inventory { get; set; } + public ICollection? ProductAttributes { get; set; } + public ICollection? ProductReviews { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductAttribute.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductAttribute.cs new file mode 100644 index 0000000..3e77407 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductAttribute.cs @@ -0,0 +1,13 @@ +namespace copilot_sample.DataAccess.Entities +{ + public class ProductAttribute + { + public int AttributeID { get; set; } + public int ProductID { get; set; } + public required string AttributeName { get; set; } + public required string AttributeValue { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductPrice.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductPrice.cs new file mode 100644 index 0000000..167b2c3 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductPrice.cs @@ -0,0 +1,15 @@ +namespace copilot_sample.DataAccess.Entities +{ + public class ProductPrice + { + public int PriceID { get; set; } + public int ProductID { get; set; } + public decimal Price { get; set; } + public string CurrencyCode { get; set; } = "USD"; + public DateTime EffectiveFrom { get; set; } + public DateTime? EffectiveTill { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductReview.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductReview.cs new file mode 100644 index 0000000..ac0a008 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductReview.cs @@ -0,0 +1,15 @@ +namespace copilot_sample.DataAccess.Entities +{ + public class ProductReview + { + public int ReviewID { get; set; } + public int ProductID { get; set; } + public string? ReviewerName { get; set; } + public int Rating { get; set; } + public string? Comment { get; set; } + public DateTime ReviewDate { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/CategoryConfiguration.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/CategoryConfiguration.cs new file mode 100644 index 0000000..ed3a6d3 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/CategoryConfiguration.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copilot_sample.DataAccess.Entities; + +namespace copilot_sample.DataAccess.EntityConfiguration +{ + public class CategoryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("Categories"); + + // Primary key + builder.HasKey(c => c.CategoryID); // Column mappings + builder.Property(c => c.CategoryID) + .HasColumnName("CategoryID") + .ValueGeneratedOnAdd(); + + builder.Property(c => c.Name) + .HasColumnName("Name") + .HasMaxLength(100) + .IsRequired(); + + builder.Property(c => c.Description) + .HasColumnName("Description") + .HasMaxLength(500); + + builder.Property(c => c.ParentCategoryID) + .HasColumnName("ParentCategoryID"); + + // Relationships + builder.HasOne(c => c.ParentCategory) + .WithMany(c => c.SubCategories) + .HasForeignKey(c => c.ParentCategoryID) + .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/InventoryConfiguration.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/InventoryConfiguration.cs new file mode 100644 index 0000000..6ec8f49 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/InventoryConfiguration.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copilot_sample.DataAccess.Entities; + +namespace copilot_sample.DataAccess.EntityConfiguration +{ + public class InventoryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("Inventory"); // Primary key + builder.HasKey(i => i.InventoryID); + + // Column mappings + builder.Property(i => i.InventoryID) + .HasColumnName("InventoryID") + .ValueGeneratedOnAdd(); + + builder.Property(i => i.ProductID) + .HasColumnName("ProductID") + .IsRequired(); + + builder.Property(i => i.Quantity) + .HasColumnName("Quantity") + .IsRequired(); builder.Property(i => i.LastUpdated) + .HasColumnName("LastUpdated") + .HasDefaultValueSql("datetime('now')"); + + // Relationships + builder.HasOne(i => i.Product) + .WithOne(p => p.Inventory) + .HasForeignKey(i => i.ProductID); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs new file mode 100644 index 0000000..4672b9d --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copilot_sample.DataAccess.Entities; + +namespace copilot_sample.DataAccess.EntityConfiguration +{ + public class ProductAttributeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("ProductAttributes"); + + // Primary key + builder.HasKey(pa => pa.AttributeID); // Column mappings + builder.Property(pa => pa.AttributeID) + .HasColumnName("AttributeID") + .ValueGeneratedOnAdd(); + + builder.Property(pa => pa.ProductID) + .HasColumnName("ProductID") + .IsRequired(); + + builder.Property(pa => pa.AttributeName) + .HasColumnName("AttributeName") + .HasMaxLength(100); + + builder.Property(pa => pa.AttributeValue) + .HasColumnName("AttributeValue") + .HasMaxLength(255); + + // Relationships + builder.HasOne(pa => pa.Product) + .WithMany(p => p.ProductAttributes) + .HasForeignKey(pa => pa.ProductID); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductConfiguration.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductConfiguration.cs new file mode 100644 index 0000000..0be24fc --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductConfiguration.cs @@ -0,0 +1,74 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copilot_sample.DataAccess.Entities; + +namespace copilot_sample.DataAccess.EntityConfiguration +{ + public class ProductConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("Products"); // Primary key + builder.HasKey(p => p.ProductID); + + // Column mappings + builder.Property(p => p.ProductID) + .HasColumnName("ProductID") + .ValueGeneratedOnAdd(); + + builder.Property(p => p.Name) + .HasColumnName("Name") + .HasMaxLength(200) + .IsRequired(); builder.Property(p => p.Description) + .HasColumnName("Description"); + + builder.Property(p => p.SKU) + .HasColumnName("SKU") + .HasMaxLength(100) + .IsRequired(); + + builder.HasIndex(p => p.SKU) + .IsUnique(); + + builder.Property(p => p.CategoryID) + .HasColumnName("CategoryID") + .IsRequired(); + + builder.Property(p => p.Brand) + .HasColumnName("Brand") + .HasMaxLength(100); builder.Property(p => p.CreatedAt) + .HasColumnName("CreatedAt") + .HasDefaultValueSql("datetime('now')"); + + builder.Property(p => p.UpdatedAt) + .HasColumnName("UpdatedAt") + .HasDefaultValueSql("datetime('now')"); + + builder.Property(p => p.IsActive) + .HasColumnName("IsActive") + .HasDefaultValue(true); + + // Relationships + builder.HasOne(p => p.Category) + .WithMany(c => c.Products) + .HasForeignKey(p => p.CategoryID); + + builder.HasMany(p => p.ProductPrices) + .WithOne(pp => pp.Product) + .HasForeignKey(pp => pp.ProductID); + + builder.HasOne(p => p.Inventory) + .WithOne(i => i.Product) + .HasForeignKey(i => i.ProductID); + + builder.HasMany(p => p.ProductAttributes) + .WithOne(pa => pa.Product) + .HasForeignKey(pa => pa.ProductID); + + builder.HasMany(p => p.ProductReviews) + .WithOne(pr => pr.Product) + .HasForeignKey(pr => pr.ProductID); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductPriceConfiguration.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductPriceConfiguration.cs new file mode 100644 index 0000000..3b76af9 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductPriceConfiguration.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copilot_sample.DataAccess.Entities; + +namespace copilot_sample.DataAccess.EntityConfiguration +{ + public class ProductPriceConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("ProductPrices"); // Primary key + builder.HasKey(pp => pp.PriceID); + + // Column mappings + builder.Property(pp => pp.PriceID) + .HasColumnName("PriceID") + .ValueGeneratedOnAdd(); + + builder.Property(pp => pp.ProductID) + .HasColumnName("ProductID") + .IsRequired(); builder.Property(pp => pp.Price) + .HasColumnName("Price") + .HasPrecision(18, 2) + .IsRequired(); + + builder.Property(pp => pp.CurrencyCode) + .HasColumnName("CurrencyCode") + .HasMaxLength(3) + .HasDefaultValue("USD"); builder.Property(pp => pp.EffectiveFrom) + .HasColumnName("EffectiveFrom") + .HasDefaultValueSql("datetime('now')"); + + builder.Property(pp => pp.EffectiveTill) + .HasColumnName("EffectiveTill"); + + // Relationships + builder.HasOne(pp => pp.Product) + .WithMany(p => p.ProductPrices) + .HasForeignKey(pp => pp.ProductID); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductReviewConfiguration.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductReviewConfiguration.cs new file mode 100644 index 0000000..eadc35f --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductReviewConfiguration.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copilot_sample.DataAccess.Entities; + +namespace copilot_sample.DataAccess.EntityConfiguration +{ + public class ProductReviewConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { // Table mapping with check constraint + builder.ToTable("ProductReviews", t => t.HasCheckConstraint("CK_ProductReviews_Rating", "Rating BETWEEN 1 AND 5")); + + // Primary key + builder.HasKey(pr => pr.ReviewID); + + // Column mappings + builder.Property(pr => pr.ReviewID) + .HasColumnName("ReviewID") + .ValueGeneratedOnAdd(); + + builder.Property(pr => pr.ProductID) + .HasColumnName("ProductID") + .IsRequired(); + + builder.Property(pr => pr.ReviewerName) + .HasColumnName("ReviewerName") + .HasMaxLength(100); builder.Property(pr => pr.Rating) + .HasColumnName("Rating") + .IsRequired(); builder.Property(pr => pr.Comment) + .HasColumnName("Comment"); + + builder.Property(pr => pr.ReviewDate) + .HasColumnName("ReviewDate") + .HasDefaultValueSql("datetime('now')"); + + // Relationships + builder.HasOne(pr => pr.Product) + .WithMany(p => p.ProductReviews) + .HasForeignKey(pr => pr.ProductID); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Extensions/ServiceProviderExtensions.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Extensions/ServiceProviderExtensions.cs new file mode 100644 index 0000000..8350a9e --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Extensions/ServiceProviderExtensions.cs @@ -0,0 +1,106 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace copilot_sample.DataAccess.Extensions +{ + /// + /// Extension methods for IServiceProvider to facilitate database operations. + /// These extensions make it easier to initialize and seed the database from the API layer. + /// + public static class ServiceProviderExtensions + { + /// + /// Initializes the database with migrations and validates the setup. + /// This is the recommended method for production environments. + /// + /// The service provider + /// Optional logger + /// True if initialization and validation succeed, false otherwise + public static async Task InitializeDatabaseAsync(this IServiceProvider serviceProvider, ILogger? logger = null) + { + try + { + // Initialize database (apply migrations) + await DatabaseInitializer.InitializeAsync(serviceProvider, logger); + + // Validate database setup + var isValid = await DatabaseInitializer.ValidateDatabaseAsync(serviceProvider, logger); + if (!isValid) + { + logger?.LogWarning("Database validation failed after initialization."); + return false; + } + + logger?.LogInformation("Database initialization and validation completed successfully."); + return true; + } + catch (Exception ex) + { + logger?.LogError(ex, "Failed to initialize and validate database."); + return false; + } + } + + /// + /// Seeds the database with sample data if needed. + /// This method should only be used for development/testing environments. + /// + /// The service provider + /// Optional logger + /// If true, clears existing data and reseeds + /// True if seeding succeeds or is skipped, false otherwise + public static async Task SeedDatabaseAsync(this IServiceProvider serviceProvider, ILogger? logger = null, bool forceReseed = false) + { + try + { + await DatabaseInitializer.SeedDataAsync(serviceProvider, logger, forceReseed); + return true; + } + catch (Exception ex) + { + logger?.LogError(ex, "Failed to seed database."); + return false; + } + } + + /// + /// Performs a complete database setup: initialization, validation, and optional seeding. + /// This is a convenience method that combines multiple operations. + /// + /// The service provider + /// Optional logger + /// If true, seeds data in development environment + /// If true and seeding is enabled, clears existing data first + /// True if all operations succeed, false otherwise + public static async Task SetupDatabaseAsync(this IServiceProvider serviceProvider, ILogger? logger = null, bool seedInDevelopment = true, bool forceReseed = false) + { + try + { + // Step 1: Initialize database + var initSuccess = await serviceProvider.InitializeDatabaseAsync(logger); + if (!initSuccess) + { + return false; + } + + // Step 2: Seed data if in development and requested + if (seedInDevelopment) + { + var seedSuccess = await serviceProvider.SeedDatabaseAsync(logger, forceReseed); + if (!seedSuccess) + { + logger?.LogWarning("Database seeding failed, but initialization was successful."); + return true; // Don't fail entirely if seeding fails + } + } + + return true; + } + catch (Exception ex) + { + logger?.LogError(ex, "Failed to setup database."); + return false; + } + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Migrations/AppDbContextModelSnapshot.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..78cd93b --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,835 @@ +ο»Ώ// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using copilot_sample.DataAccess; + +#nullable disable + +namespace copilotsample.DataAccess.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.5"); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Category", b => + { + b.Property("CategoryID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("CategoryID"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("TEXT") + .HasColumnName("Description"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT") + .HasColumnName("Name"); + + b.Property("ParentCategoryID") + .HasColumnType("INTEGER") + .HasColumnName("ParentCategoryID"); + + b.HasKey("CategoryID"); + + b.HasIndex("ParentCategoryID"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryID = 1, + Description = "Electronic gadgets and devices", + Name = "Electronics" + }, + new + { + CategoryID = 2, + Description = "Portable computers", + Name = "Laptops", + ParentCategoryID = 1 + }, + new + { + CategoryID = 3, + Description = "Mobile phones and accessories", + Name = "Smartphones", + ParentCategoryID = 1 + }, + new + { + CategoryID = 4, + Description = "Electronics accessories", + Name = "Accessories", + ParentCategoryID = 1 + }); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Inventory", b => + { + b.Property("InventoryID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("InventoryID"); + + b.Property("LastUpdated") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("LastUpdated") + .HasDefaultValueSql("datetime('now')"); + + b.Property("ProductID") + .HasColumnType("INTEGER") + .HasColumnName("ProductID"); + + b.Property("Quantity") + .HasColumnType("INTEGER") + .HasColumnName("Quantity"); + + b.HasKey("InventoryID"); + + b.HasIndex("ProductID") + .IsUnique(); + + b.ToTable("Inventory", (string)null); + + b.HasData( + new + { + InventoryID = 1, + LastUpdated = new DateTime(2024, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + ProductID = 1, + Quantity = 25 + }, + new + { + InventoryID = 2, + LastUpdated = new DateTime(2024, 1, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + ProductID = 2, + Quantity = 10 + }, + new + { + InventoryID = 3, + LastUpdated = new DateTime(2024, 3, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + ProductID = 3, + Quantity = 50 + }, + new + { + InventoryID = 4, + LastUpdated = new DateTime(2024, 2, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + ProductID = 4, + Quantity = 100 + }, + new + { + InventoryID = 5, + LastUpdated = new DateTime(2024, 4, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + ProductID = 5, + Quantity = 30 + }, + new + { + InventoryID = 6, + LastUpdated = new DateTime(2024, 4, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + ProductID = 6, + Quantity = 45 + }, + new + { + InventoryID = 7, + LastUpdated = new DateTime(2024, 3, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + ProductID = 7, + Quantity = 200 + }); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Product", b => + { + b.Property("ProductID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ProductID"); + + b.Property("Brand") + .HasMaxLength(100) + .HasColumnType("TEXT") + .HasColumnName("Brand"); + + b.Property("CategoryID") + .HasColumnType("INTEGER") + .HasColumnName("CategoryID"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("CreatedAt") + .HasDefaultValueSql("datetime('now')"); + + b.Property("Description") + .HasColumnType("TEXT") + .HasColumnName("Description"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true) + .HasColumnName("IsActive"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT") + .HasColumnName("Name"); + + b.Property("SKU") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT") + .HasColumnName("SKU"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("UpdatedAt") + .HasDefaultValueSql("datetime('now')"); + + b.HasKey("ProductID"); + + b.HasIndex("CategoryID"); + + b.HasIndex("SKU") + .IsUnique(); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductID = 1, + Brand = "TechBrand", + CategoryID = 2, + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Lightweight business laptop with high performance", + IsActive = true, + Name = "UltraBook X1", + SKU = "SKU-UBX1", + UpdatedAt = new DateTime(2024, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + ProductID = 2, + Brand = "GamePro", + CategoryID = 2, + CreatedAt = new DateTime(2024, 1, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "High-end gaming laptop with RGB lighting", + IsActive = true, + Name = "Gaming Beast Z9", + SKU = "SKU-GBZ9", + UpdatedAt = new DateTime(2024, 1, 15, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + ProductID = 3, + Brand = "SmartTech", + CategoryID = 3, + CreatedAt = new DateTime(2024, 3, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Latest smartphone with AI-powered camera", + IsActive = true, + Name = "Galaxy X10", + SKU = "SKU-GX10", + UpdatedAt = new DateTime(2024, 3, 15, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + ProductID = 4, + Brand = "SoundWave", + CategoryID = 4, + CreatedAt = new DateTime(2024, 2, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Wireless earphones with noise cancellation", + IsActive = true, + Name = "EarPods Pro", + SKU = "SKU-EPP", + UpdatedAt = new DateTime(2024, 2, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + ProductID = 5, + Brand = "ThinkCorp", + CategoryID = 2, + CreatedAt = new DateTime(2024, 4, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Durable business laptop with excellent battery life", + IsActive = true, + Name = "ThinkMate Pro 14", + SKU = "SKU-TMP14", + UpdatedAt = new DateTime(2024, 4, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + ProductID = 6, + Brand = "PixelTech", + CategoryID = 3, + CreatedAt = new DateTime(2024, 4, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Compact smartphone with high-resolution camera", + IsActive = true, + Name = "PixelCam A2", + SKU = "SKU-PXA2", + UpdatedAt = new DateTime(2024, 4, 15, 0, 0, 0, 0, DateTimeKind.Unspecified) + }, + new + { + ProductID = 7, + Brand = "ChargeX", + CategoryID = 4, + CreatedAt = new DateTime(2024, 3, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Portable power bank with fast charging support", + IsActive = true, + Name = "PowerCharge 10000", + SKU = "SKU-PC10000", + UpdatedAt = new DateTime(2024, 3, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) + }); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.ProductAttribute", b => + { + b.Property("AttributeID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("AttributeID"); + + b.Property("AttributeName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT") + .HasColumnName("AttributeName"); + + b.Property("AttributeValue") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT") + .HasColumnName("AttributeValue"); + + b.Property("ProductID") + .HasColumnType("INTEGER") + .HasColumnName("ProductID"); + + b.HasKey("AttributeID"); + + b.HasIndex("ProductID"); + + b.ToTable("ProductAttributes", (string)null); + + b.HasData( + new + { + AttributeID = 1, + AttributeName = "Processor", + AttributeValue = "Intel Core i7", + ProductID = 1 + }, + new + { + AttributeID = 2, + AttributeName = "RAM", + AttributeValue = "16GB", + ProductID = 1 + }, + new + { + AttributeID = 3, + AttributeName = "Storage", + AttributeValue = "512GB SSD", + ProductID = 1 + }, + new + { + AttributeID = 4, + AttributeName = "Processor", + AttributeValue = "AMD Ryzen 9", + ProductID = 2 + }, + new + { + AttributeID = 5, + AttributeName = "RAM", + AttributeValue = "32GB", + ProductID = 2 + }, + new + { + AttributeID = 6, + AttributeName = "Graphics Card", + AttributeValue = "NVIDIA RTX 4080", + ProductID = 2 + }, + new + { + AttributeID = 7, + AttributeName = "Display", + AttributeValue = "6.5-inch OLED", + ProductID = 3 + }, + new + { + AttributeID = 8, + AttributeName = "Battery", + AttributeValue = "4000mAh", + ProductID = 3 + }, + new + { + AttributeID = 9, + AttributeName = "Camera", + AttributeValue = "108MP", + ProductID = 3 + }, + new + { + AttributeID = 10, + AttributeName = "Connectivity", + AttributeValue = "Bluetooth 5.2", + ProductID = 4 + }, + new + { + AttributeID = 11, + AttributeName = "Noise Cancellation", + AttributeValue = "Active", + ProductID = 4 + }, + new + { + AttributeID = 12, + AttributeName = "Battery Life", + AttributeValue = "8 hours", + ProductID = 4 + }, + new + { + AttributeID = 13, + AttributeName = "Processor", + AttributeValue = "Intel i5", + ProductID = 5 + }, + new + { + AttributeID = 14, + AttributeName = "RAM", + AttributeValue = "8GB", + ProductID = 5 + }, + new + { + AttributeID = 15, + AttributeName = "Weight", + AttributeValue = "1.3kg", + ProductID = 5 + }, + new + { + AttributeID = 16, + AttributeName = "Camera", + AttributeValue = "64MP", + ProductID = 6 + }, + new + { + AttributeID = 17, + AttributeName = "Storage", + AttributeValue = "128GB", + ProductID = 6 + }, + new + { + AttributeID = 18, + AttributeName = "Display", + AttributeValue = "6.1-inch AMOLED", + ProductID = 6 + }, + new + { + AttributeID = 19, + AttributeName = "Capacity", + AttributeValue = "10000mAh", + ProductID = 7 + }, + new + { + AttributeID = 20, + AttributeName = "USB Ports", + AttributeValue = "2", + ProductID = 7 + }, + new + { + AttributeID = 21, + AttributeName = "Fast Charging", + AttributeValue = "Yes", + ProductID = 7 + }); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.ProductPrice", b => + { + b.Property("PriceID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("PriceID"); + + b.Property("CurrencyCode") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("TEXT") + .HasDefaultValue("USD") + .HasColumnName("CurrencyCode"); + + b.Property("EffectiveFrom") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("EffectiveFrom") + .HasDefaultValueSql("datetime('now')"); + + b.Property("EffectiveTill") + .HasColumnType("TEXT") + .HasColumnName("EffectiveTill"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("TEXT") + .HasColumnName("Price"); + + b.Property("ProductID") + .HasColumnType("INTEGER") + .HasColumnName("ProductID"); + + b.HasKey("PriceID"); + + b.HasIndex("ProductID"); + + b.ToTable("ProductPrices", (string)null); + + b.HasData( + new + { + PriceID = 1, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 1299.99m, + ProductID = 1 + }, + new + { + PriceID = 2, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2024, 10, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + EffectiveTill = new DateTime(2024, 12, 31, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 1199.99m, + ProductID = 1 + }, + new + { + PriceID = 3, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2025, 1, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 1999.00m, + ProductID = 2 + }, + new + { + PriceID = 4, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2025, 3, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 899.50m, + ProductID = 3 + }, + new + { + PriceID = 5, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2025, 2, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 199.99m, + ProductID = 4 + }, + new + { + PriceID = 6, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2025, 4, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 999.99m, + ProductID = 5 + }, + new + { + PriceID = 7, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2025, 4, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 649.00m, + ProductID = 6 + }, + new + { + PriceID = 8, + CurrencyCode = "USD", + EffectiveFrom = new DateTime(2025, 3, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Price = 49.99m, + ProductID = 7 + }); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.ProductReview", b => + { + b.Property("ReviewID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ReviewID"); + + b.Property("Comment") + .HasColumnType("TEXT") + .HasColumnName("Comment"); + + b.Property("ProductID") + .HasColumnType("INTEGER") + .HasColumnName("ProductID"); + + b.Property("Rating") + .HasColumnType("INTEGER") + .HasColumnName("Rating"); + + b.Property("ReviewDate") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("ReviewDate") + .HasDefaultValueSql("datetime('now')"); + + b.Property("ReviewerName") + .HasMaxLength(100) + .HasColumnType("TEXT") + .HasColumnName("ReviewerName"); + + b.HasKey("ReviewID"); + + b.HasIndex("ProductID"); + + b.ToTable("ProductReviews", null, t => + { + t.HasCheckConstraint("CK_ProductReviews_Rating", "Rating BETWEEN 1 AND 5"); + }); + + b.HasData( + new + { + ReviewID = 1, + Comment = "Absolutely love this laptop! Fast and sleek.", + ProductID = 1, + Rating = 5, + ReviewDate = new DateTime(2024, 1, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Alice Johnson" + }, + new + { + ReviewID = 2, + Comment = "Good performance but gets a bit warm.", + ProductID = 1, + Rating = 4, + ReviewDate = new DateTime(2024, 1, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Bob Smith" + }, + new + { + ReviewID = 3, + Comment = "A beast for gaming. Smooth experience!", + ProductID = 2, + Rating = 5, + ReviewDate = new DateTime(2024, 2, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Tommy Lee" + }, + new + { + ReviewID = 4, + Comment = "Amazing camera and display! Worth every penny.", + ProductID = 3, + Rating = 5, + ReviewDate = new DateTime(2024, 3, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Carlos Vega" + }, + new + { + ReviewID = 5, + Comment = "Battery life could be better.", + ProductID = 3, + Rating = 3, + ReviewDate = new DateTime(2024, 3, 25, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Diana Lee" + }, + new + { + ReviewID = 6, + Comment = "Very comfortable fit and great sound.", + ProductID = 4, + Rating = 4, + ReviewDate = new DateTime(2024, 2, 10, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Emma Stone" + }, + new + { + ReviewID = 7, + Comment = "Connection drops sometimes.", + ProductID = 4, + Rating = 2, + ReviewDate = new DateTime(2024, 2, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "John Doe" + }, + new + { + ReviewID = 8, + Comment = "Reliable laptop, solid build and decent performance.", + ProductID = 5, + Rating = 4, + ReviewDate = new DateTime(2024, 4, 10, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Harvey Dent" + }, + new + { + ReviewID = 9, + Comment = "Good for light work, but a bit slow for multitasking.", + ProductID = 5, + Rating = 3, + ReviewDate = new DateTime(2024, 4, 12, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Rachel Green" + }, + new + { + ReviewID = 10, + Comment = "Compact yet powerful. Great value for the price.", + ProductID = 6, + Rating = 5, + ReviewDate = new DateTime(2024, 4, 20, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Bruce Banner" + }, + new + { + ReviewID = 11, + Comment = "Excellent camera quality and battery.", + ProductID = 6, + Rating = 4, + ReviewDate = new DateTime(2024, 4, 22, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Natasha Romanoff" + }, + new + { + ReviewID = 12, + Comment = "Lasts all day. Perfect for travel.", + ProductID = 7, + Rating = 5, + ReviewDate = new DateTime(2024, 3, 10, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Steve Rogers" + }, + new + { + ReviewID = 13, + Comment = "Charges fast, but gets warm during use.", + ProductID = 7, + Rating = 3, + ReviewDate = new DateTime(2024, 3, 15, 0, 0, 0, 0, DateTimeKind.Unspecified), + ReviewerName = "Tony Stark" + }); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Category", b => + { + b.HasOne("copilot_sample.DataAccess.Entities.Category", "ParentCategory") + .WithMany("SubCategories") + .HasForeignKey("ParentCategoryID") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentCategory"); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Inventory", b => + { + b.HasOne("copilot_sample.DataAccess.Entities.Product", "Product") + .WithOne("Inventory") + .HasForeignKey("copilot_sample.DataAccess.Entities.Inventory", "ProductID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Product", b => + { + b.HasOne("copilot_sample.DataAccess.Entities.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.ProductAttribute", b => + { + b.HasOne("copilot_sample.DataAccess.Entities.Product", "Product") + .WithMany("ProductAttributes") + .HasForeignKey("ProductID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.ProductPrice", b => + { + b.HasOne("copilot_sample.DataAccess.Entities.Product", "Product") + .WithMany("ProductPrices") + .HasForeignKey("ProductID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.ProductReview", b => + { + b.HasOne("copilot_sample.DataAccess.Entities.Product", "Product") + .WithMany("ProductReviews") + .HasForeignKey("ProductID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Category", b => + { + b.Navigation("Products"); + + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("copilot_sample.DataAccess.Entities.Product", b => + { + b.Navigation("Inventory"); + + b.Navigation("ProductAttributes"); + + b.Navigation("ProductPrices"); + + b.Navigation("ProductReviews"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/SeedData/InventorySeedData.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/SeedData/InventorySeedData.cs new file mode 100644 index 0000000..657a5b7 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/SeedData/InventorySeedData.cs @@ -0,0 +1,297 @@ +using copilot_sample.DataAccess.Entities; +using Microsoft.EntityFrameworkCore; + +namespace copilot_sample.DataAccess.SeedData +{ + /// + /// Contains all seed data definitions for the inventory system. + /// This class centralizes seed data management and can be used by both migrations and runtime seeding. + /// + public static class InventorySeedData + { + /// + /// Applies all seed data to the given ModelBuilder. + /// Used during migration generation via AppDbContext.OnModelCreating. + /// + /// The ModelBuilder to configure + public static void ApplyToModelBuilder(ModelBuilder modelBuilder) + { + // Seed Categories + modelBuilder.Entity().HasData(GetCategories()); + + // Seed Products + modelBuilder.Entity().HasData(GetProducts()); + + // Seed Product Prices + modelBuilder.Entity().HasData(GetProductPrices()); + + // Seed Inventory + modelBuilder.Entity().HasData(GetInventoryItems()); + + // Seed Product Attributes + modelBuilder.Entity().HasData(GetProductAttributes()); + + // Seed Product Reviews + modelBuilder.Entity().HasData(GetProductReviews()); + } + + /// + /// Applies seed data directly to a database context. + /// Used for runtime seeding when migrations are not available. + /// + /// The database context + public static async Task ApplyToContextAsync(AppDbContext context) + { + // Check if data already exists + if (await context.Categories.AnyAsync()) + { + return; // Data already exists, skip seeding + } + + // Add seed data + await context.Categories.AddRangeAsync(GetCategories()); + await context.SaveChangesAsync(); + + await context.Products.AddRangeAsync(GetProducts()); + await context.SaveChangesAsync(); + + await context.ProductPrices.AddRangeAsync(GetProductPrices()); + await context.SaveChangesAsync(); + + await context.Inventory.AddRangeAsync(GetInventoryItems()); + await context.SaveChangesAsync(); + + await context.ProductAttributes.AddRangeAsync(GetProductAttributes()); + await context.SaveChangesAsync(); + + await context.ProductReviews.AddRangeAsync(GetProductReviews()); + await context.SaveChangesAsync(); + } + + /// + /// Gets the seed data for categories + /// + public static Category[] GetCategories() + { + return new[] + { + new Category { CategoryID = 1, Name = "Electronics", Description = "Electronic gadgets and devices", ParentCategoryID = null }, + new Category { CategoryID = 2, Name = "Laptops", Description = "Portable computers", ParentCategoryID = 1 }, + new Category { CategoryID = 3, Name = "Smartphones", Description = "Mobile phones and accessories", ParentCategoryID = 1 }, + new Category { CategoryID = 4, Name = "Accessories", Description = "Electronics accessories", ParentCategoryID = 1 } + }; + } + + /// + /// Gets the seed data for products + /// + public static Product[] GetProducts() + { + return new[] + { + new Product + { + ProductID = 1, + Name = "UltraBook X1", + Description = "Lightweight business laptop with high performance", + SKU = "SKU-UBX1", + CategoryID = 2, + Brand = "TechBrand", + CreatedAt = new DateTime(2024, 1, 1), + UpdatedAt = new DateTime(2024, 1, 1), + IsActive = true + }, + new Product + { + ProductID = 2, + Name = "Gaming Beast Z9", + Description = "High-end gaming laptop with RGB lighting", + SKU = "SKU-GBZ9", + CategoryID = 2, + Brand = "GamePro", + CreatedAt = new DateTime(2024, 1, 15), + UpdatedAt = new DateTime(2024, 1, 15), + IsActive = true + }, + new Product + { + ProductID = 3, + Name = "Galaxy X10", + Description = "Latest smartphone with AI-powered camera", + SKU = "SKU-GX10", + CategoryID = 3, + Brand = "SmartTech", + CreatedAt = new DateTime(2024, 3, 15), + UpdatedAt = new DateTime(2024, 3, 15), + IsActive = true + }, + new Product + { + ProductID = 4, + Name = "EarPods Pro", + Description = "Wireless earphones with noise cancellation", + SKU = "SKU-EPP", + CategoryID = 4, + Brand = "SoundWave", + CreatedAt = new DateTime(2024, 2, 1), + UpdatedAt = new DateTime(2024, 2, 1), + IsActive = true + }, + new Product + { + ProductID = 5, + Name = "ThinkMate Pro 14", + Description = "Durable business laptop with excellent battery life", + SKU = "SKU-TMP14", + CategoryID = 2, + Brand = "ThinkCorp", + CreatedAt = new DateTime(2024, 4, 1), + UpdatedAt = new DateTime(2024, 4, 1), + IsActive = true + }, + new Product + { + ProductID = 6, + Name = "PixelCam A2", + Description = "Compact smartphone with high-resolution camera", + SKU = "SKU-PXA2", + CategoryID = 3, + Brand = "PixelTech", + CreatedAt = new DateTime(2024, 4, 15), + UpdatedAt = new DateTime(2024, 4, 15), + IsActive = true + }, + new Product + { + ProductID = 7, + Name = "PowerCharge 10000", + Description = "Portable power bank with fast charging support", + SKU = "SKU-PC10000", + CategoryID = 4, + Brand = "ChargeX", + CreatedAt = new DateTime(2024, 3, 1), + UpdatedAt = new DateTime(2024, 3, 1), + IsActive = true + } + }; + } + + /// + /// Gets the seed data for product prices + /// + public static ProductPrice[] GetProductPrices() + { + return new[] + { + new ProductPrice { PriceID = 1, ProductID = 1, Price = 1299.99m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2025, 1, 1), EffectiveTill = null }, + new ProductPrice { PriceID = 2, ProductID = 1, Price = 1199.99m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2024, 10, 1), EffectiveTill = new DateTime(2024, 12, 31) }, + new ProductPrice { PriceID = 3, ProductID = 2, Price = 1999.00m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2025, 1, 15), EffectiveTill = null }, + new ProductPrice { PriceID = 4, ProductID = 3, Price = 899.50m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2025, 3, 15), EffectiveTill = null }, + new ProductPrice { PriceID = 5, ProductID = 4, Price = 199.99m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2025, 2, 1), EffectiveTill = null }, + new ProductPrice { PriceID = 6, ProductID = 5, Price = 999.99m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2025, 4, 1), EffectiveTill = null }, + new ProductPrice { PriceID = 7, ProductID = 6, Price = 649.00m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2025, 4, 15), EffectiveTill = null }, + new ProductPrice { PriceID = 8, ProductID = 7, Price = 49.99m, CurrencyCode = "USD", EffectiveFrom = new DateTime(2025, 3, 1), EffectiveTill = null } + }; + } + + /// + /// Gets the seed data for inventory items + /// + public static Inventory[] GetInventoryItems() + { + return new[] + { + new Inventory { InventoryID = 1, ProductID = 1, Quantity = 25, LastUpdated = new DateTime(2024, 1, 1) }, + new Inventory { InventoryID = 2, ProductID = 2, Quantity = 10, LastUpdated = new DateTime(2024, 1, 15) }, + new Inventory { InventoryID = 3, ProductID = 3, Quantity = 50, LastUpdated = new DateTime(2024, 3, 15) }, + new Inventory { InventoryID = 4, ProductID = 4, Quantity = 100, LastUpdated = new DateTime(2024, 2, 1) }, + new Inventory { InventoryID = 5, ProductID = 5, Quantity = 30, LastUpdated = new DateTime(2024, 4, 1) }, + new Inventory { InventoryID = 6, ProductID = 6, Quantity = 45, LastUpdated = new DateTime(2024, 4, 15) }, + new Inventory { InventoryID = 7, ProductID = 7, Quantity = 200, LastUpdated = new DateTime(2024, 3, 1) } + }; + } + + /// + /// Gets the seed data for product attributes + /// + public static ProductAttribute[] GetProductAttributes() + { + return new[] + { + // UltraBook X1 attributes + new ProductAttribute { AttributeID = 1, ProductID = 1, AttributeName = "Processor", AttributeValue = "Intel Core i7" }, + new ProductAttribute { AttributeID = 2, ProductID = 1, AttributeName = "RAM", AttributeValue = "16GB" }, + new ProductAttribute { AttributeID = 3, ProductID = 1, AttributeName = "Storage", AttributeValue = "512GB SSD" }, + // Gaming Beast Z9 attributes + new ProductAttribute { AttributeID = 4, ProductID = 2, AttributeName = "Processor", AttributeValue = "AMD Ryzen 9" }, + new ProductAttribute { AttributeID = 5, ProductID = 2, AttributeName = "RAM", AttributeValue = "32GB" }, + new ProductAttribute { AttributeID = 6, ProductID = 2, AttributeName = "Graphics Card", AttributeValue = "NVIDIA RTX 4080" }, + // Galaxy X10 attributes + new ProductAttribute { AttributeID = 7, ProductID = 3, AttributeName = "Display", AttributeValue = "6.5-inch OLED" }, + new ProductAttribute { AttributeID = 8, ProductID = 3, AttributeName = "Battery", AttributeValue = "4000mAh" }, + new ProductAttribute { AttributeID = 9, ProductID = 3, AttributeName = "Camera", AttributeValue = "108MP" }, + // EarPods Pro attributes + new ProductAttribute { AttributeID = 10, ProductID = 4, AttributeName = "Connectivity", AttributeValue = "Bluetooth 5.2" }, + new ProductAttribute { AttributeID = 11, ProductID = 4, AttributeName = "Noise Cancellation", AttributeValue = "Active" }, + new ProductAttribute { AttributeID = 12, ProductID = 4, AttributeName = "Battery Life", AttributeValue = "8 hours" }, + // ThinkMate Pro 14 attributes + new ProductAttribute { AttributeID = 13, ProductID = 5, AttributeName = "Processor", AttributeValue = "Intel i5" }, + new ProductAttribute { AttributeID = 14, ProductID = 5, AttributeName = "RAM", AttributeValue = "8GB" }, + new ProductAttribute { AttributeID = 15, ProductID = 5, AttributeName = "Weight", AttributeValue = "1.3kg" }, + // PixelCam A2 attributes + new ProductAttribute { AttributeID = 16, ProductID = 6, AttributeName = "Camera", AttributeValue = "64MP" }, + new ProductAttribute { AttributeID = 17, ProductID = 6, AttributeName = "Storage", AttributeValue = "128GB" }, + new ProductAttribute { AttributeID = 18, ProductID = 6, AttributeName = "Display", AttributeValue = "6.1-inch AMOLED" }, + // PowerCharge 10000 attributes + new ProductAttribute { AttributeID = 19, ProductID = 7, AttributeName = "Capacity", AttributeValue = "10000mAh" }, + new ProductAttribute { AttributeID = 20, ProductID = 7, AttributeName = "USB Ports", AttributeValue = "2" }, + new ProductAttribute { AttributeID = 21, ProductID = 7, AttributeName = "Fast Charging", AttributeValue = "Yes" } + }; + } + + /// + /// Gets the seed data for product reviews + /// + public static ProductReview[] GetProductReviews() + { + return new[] + { + // UltraBook X1 reviews + new ProductReview { ReviewID = 1, ProductID = 1, ReviewerName = "Alice Johnson", Rating = 5, Comment = "Absolutely love this laptop! Fast and sleek.", ReviewDate = new DateTime(2024, 1, 15) }, + new ProductReview { ReviewID = 2, ProductID = 1, ReviewerName = "Bob Smith", Rating = 4, Comment = "Good performance but gets a bit warm.", ReviewDate = new DateTime(2024, 1, 20) }, + // Gaming Beast Z9 reviews + new ProductReview { ReviewID = 3, ProductID = 2, ReviewerName = "Tommy Lee", Rating = 5, Comment = "A beast for gaming. Smooth experience!", ReviewDate = new DateTime(2024, 2, 1) }, + // Galaxy X10 reviews + new ProductReview { ReviewID = 4, ProductID = 3, ReviewerName = "Carlos Vega", Rating = 5, Comment = "Amazing camera and display! Worth every penny.", ReviewDate = new DateTime(2024, 3, 20) }, + new ProductReview { ReviewID = 5, ProductID = 3, ReviewerName = "Diana Lee", Rating = 3, Comment = "Battery life could be better.", ReviewDate = new DateTime(2024, 3, 25) }, + // EarPods Pro reviews + new ProductReview { ReviewID = 6, ProductID = 4, ReviewerName = "Emma Stone", Rating = 4, Comment = "Very comfortable fit and great sound.", ReviewDate = new DateTime(2024, 2, 10) }, + new ProductReview { ReviewID = 7, ProductID = 4, ReviewerName = "John Doe", Rating = 2, Comment = "Connection drops sometimes.", ReviewDate = new DateTime(2024, 2, 15) }, + // ThinkMate Pro 14 reviews + new ProductReview { ReviewID = 8, ProductID = 5, ReviewerName = "Harvey Dent", Rating = 4, Comment = "Reliable laptop, solid build and decent performance.", ReviewDate = new DateTime(2024, 4, 10) }, + new ProductReview { ReviewID = 9, ProductID = 5, ReviewerName = "Rachel Green", Rating = 3, Comment = "Good for light work, but a bit slow for multitasking.", ReviewDate = new DateTime(2024, 4, 12) }, + // PixelCam A2 reviews + new ProductReview { ReviewID = 10, ProductID = 6, ReviewerName = "Bruce Banner", Rating = 5, Comment = "Compact yet powerful. Great value for the price.", ReviewDate = new DateTime(2024, 4, 20) }, + new ProductReview { ReviewID = 11, ProductID = 6, ReviewerName = "Natasha Romanoff", Rating = 4, Comment = "Excellent camera quality and battery.", ReviewDate = new DateTime(2024, 4, 22) }, + // PowerCharge 10000 reviews + new ProductReview { ReviewID = 12, ProductID = 7, ReviewerName = "Steve Rogers", Rating = 5, Comment = "Lasts all day. Perfect for travel.", ReviewDate = new DateTime(2024, 3, 10) }, + new ProductReview { ReviewID = 13, ProductID = 7, ReviewerName = "Tony Stark", Rating = 3, Comment = "Charges fast, but gets warm during use.", ReviewDate = new DateTime(2024, 3, 15) } + }; + } + + /// + /// Gets summary information about the seed data + /// + public static (int Categories, int Products, int Prices, int Inventory, int Attributes, int Reviews) GetSeedDataCounts() + { + return ( + Categories: GetCategories().Length, + Products: GetProducts().Length, + Prices: GetProductPrices().Length, + Inventory: GetInventoryItems().Length, + Attributes: GetProductAttributes().Length, + Reviews: GetProductReviews().Length + ); + } + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/copilot-sample.DataAccess.csproj b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/copilot-sample.DataAccess.csproj new file mode 100644 index 0000000..c6fc9e8 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/copilot-sample.DataAccess.csproj @@ -0,0 +1,17 @@ +ο»Ώ + + + net8.0 + enable + enable + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/CategoryServiceTests.cs b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/CategoryServiceTests.cs new file mode 100644 index 0000000..69e260a --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/CategoryServiceTests.cs @@ -0,0 +1,195 @@ +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; + +public class CategoryServiceTests +{ + private AppDbContext GetInMemoryDbContext() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "TestDatabase") + .Options; + + return new AppDbContext(options); + } + + [Fact] + public async Task GetCategoriesAsync_ShouldReturnAllCategories() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.AddRange(new List + { + new Category { CategoryID = 100, Name = "Electronics", Description = "Electronic items" }, + new Category { CategoryID = 200, Name = "Books", Description = "Books and novels" } + }); + await dbContext.SaveChangesAsync(); + var total = dbContext.Categories.Count(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.GetCategoriesAsync(); + + // Assert + result.Should().HaveCount(total); + result.Should().Contain(c => c.Name == "Electronics"); + result.Should().Contain(c => c.Name == "Books"); + } + + [Fact] + public async Task GetCategoryByIdAsync_ShouldReturnCategory_WhenCategoryExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 1, Name = "Electronics", Description = "Electronic items" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.GetCategoryByIdAsync(1); + + // Assert + result.Should().NotBeNull(); + result!.Name.Should().Be("Electronics"); + } + + [Fact] + public async Task GetCategoryByIdAsync_ShouldReturnNull_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.GetCategoryByIdAsync(99); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public async Task AddCategoryAsync_ShouldAddCategory_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + var categoryService = new CategoryService(dbContext); + + var newCategory = new AddCategoryDto + { + Name = "Electronics Apps", + Description = "Electronic items" + }; + var totalCount = dbContext.Categories.Count(); + + // Act + var result = await categoryService.AddCategoryAsync(newCategory); + + // Assert + result.Should().NotBeNull(); + result.Name.Should().Be("Electronics Apps"); + (await dbContext.Categories.CountAsync()).Should().Be(totalCount +1); + } + + [Fact] + public async Task AddCategoryAsync_ShouldReturnNull_WhenCategoryWithSameNameExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 101, Name = "Electronics101", Description = "Electronic items" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + var newCategory = new AddCategoryDto + { + Name = "Electronics101", + Description = "Duplicate category" + }; + + // Act + var result = await categoryService.AddCategoryAsync(newCategory); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public async Task UpdateCategoryDescriptionAsync_ShouldUpdateDescription_WhenCategoryExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 12, Name = "Electronics12", Description = "Old description" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + var updateDto = new UpdateCategoryDescriptionDto + { + Description = "Updated description" + }; + + // Act + var result = await categoryService.UpdateCategoryDescriptionAsync(12, updateDto); + + // Assert + result.Should().BeTrue(); + var updatedCategory = await dbContext.Categories.FindAsync(12); + updatedCategory!.Description.Should().Be("Updated description"); + } + + [Fact] + public async Task UpdateCategoryDescriptionAsync_ShouldReturnFalse_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + var categoryService = new CategoryService(dbContext); + + var updateDto = new UpdateCategoryDescriptionDto + { + Description = "Updated description" + }; + + // Act + var result = await categoryService.UpdateCategoryDescriptionAsync(13, updateDto); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public async Task DeleteCategoryAsync_ShouldDeleteCategory_WhenCategoryExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 11, Name = "Electronics11", Description = "Electronic items" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.DeleteCategoryAsync(11); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public async Task DeleteCategoryAsync_ShouldReturnFalse_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.DeleteCategoryAsync(112); + + // Assert + result.Should().BeTrue(); + } +} diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/copilot-sample.Test.csproj b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/copilot-sample.Test.csproj new file mode 100644 index 0000000..76f3407 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/copilot-sample.Test.csproj @@ -0,0 +1,31 @@ +ο»Ώ + + + net8.0 + copilot_sample.Test + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.sln b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.sln new file mode 100644 index 0000000..c6181c6 --- /dev/null +++ b/sample-code/SimpleFullStack/DotnetCoreApi/copilot-sample.sln @@ -0,0 +1,64 @@ +ο»Ώ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35828.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.Api", "copilot-sample.Api\copilot-sample.Api.csproj", "{8E754013-64CD-402F-A9D1-10D3CFE753D3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.Test", "copilot-sample.Test\copilot-sample.Test.csproj", "{0AE92036-1C6A-41C7-A84D-FE4E1A579351}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.DataAccess", "copilot-sample.DataAccess\copilot-sample.DataAccess.csproj", "{8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lessons", "lessons", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + lessons\01-VS-copilot-setup.md = lessons\01-VS-copilot-setup.md + Readme.md = Readme.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{CBAF67E1-E469-4CBF-AD83-7646C6F7CBE0}" + ProjectSection(SolutionItems) = preProject + lessons\Images\gh-cp-header.png = lessons\Images\gh-cp-header.png + lessons\Images\open-gh-cp-chat.png = lessons\Images\open-gh-cp-chat.png + lessons\Images\VS-copilot-options.png = lessons\Images\VS-copilot-options.png + lessons\Images\VS-enable-agent-mode.png = lessons\Images\VS-enable-agent-mode.png + lessons\Images\VS-open-edits-thread.png = lessons\Images\VS-open-edits-thread.png + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-github-copilot-tools", "02-github-copilot-tools", "{A6DB6DFA-6B61-4026-931D-D97702AC08F6}" + ProjectSection(SolutionItems) = preProject + lessons\01-github-copilot-tools\01a-exploring-copilot-ask.md = lessons\01-github-copilot-tools\01a-exploring-copilot-ask.md + lessons\01-github-copilot-tools\01b-exploring-copilot-edit.md = lessons\01-github-copilot-tools\01b-exploring-copilot-edit.md + lessons\01-github-copilot-tools\01c-exploring-copilot-agent.md = lessons\01-github-copilot-tools\01c-exploring-copilot-agent.md + lessons\01-github-copilot-tools\01d-exploring-copilot-inline.md = lessons\01-github-copilot-tools\01d-exploring-copilot-inline.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Release|Any CPU.Build.0 = Release|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Release|Any CPU.Build.0 = Release|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CBAF67E1-E469-4CBF-AD83-7646C6F7CBE0} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {A6DB6DFA-6B61-4026-931D-D97702AC08F6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B78DD698-ADCA-4195-AD2C-9DFDB1A3F59E} + EndGlobalSection +EndGlobal diff --git a/sample-code/SimpleFullStack/Web/.example.env b/sample-code/SimpleFullStack/Web/.example.env new file mode 100644 index 0000000..bb9149c --- /dev/null +++ b/sample-code/SimpleFullStack/Web/.example.env @@ -0,0 +1 @@ +VITE_BASE_URL=http://localhost:7071/api diff --git a/sample-code/SimpleFullStack/Web/.gitignore b/sample-code/SimpleFullStack/Web/.gitignore new file mode 100644 index 0000000..01774db --- /dev/null +++ b/sample-code/SimpleFullStack/Web/.gitignore @@ -0,0 +1,99 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TypeScript output +dist +out + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json + +# Azurite artifacts +__blobstorage__ +__queuestorage__ +__azurite_db*__.json \ No newline at end of file diff --git a/sample-code/SimpleFullStack/Web/README.md b/sample-code/SimpleFullStack/Web/README.md new file mode 100644 index 0000000..27eccaa --- /dev/null +++ b/sample-code/SimpleFullStack/Web/README.md @@ -0,0 +1,80 @@ +# Copilot Labs React Demo App + +This is a demo React application created for exploring and showcasing GitHub Copilot Labs features. It is designed as a modern, responsive personal website and serves as a hands-on playground for Copilot Labs users to experiment with code generation, editing, and best practices in a real-world React project. + +## Demo Purpose + +- **Showcase Copilot Labs capabilities in a real React project** +- **Provide a safe environment for learning and experimenting with Copilot** +- **Demonstrate modern React, TypeScript, and Vite project structure** + +## Tech Stack + +- **React** (with Vite) +- **TypeScript** +- **Material UI (MUI)** +- **Zustand** (state management) +- **Axios** (HTTP requests) +- **React Hook Form** (form management) +- **React Router DOM** (routing) +- **React Query** (data fetching) + +## Project Structure + +``` +Web/ +β”œβ”€β”€ public/ # Static assets +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ App.tsx # Main app component +β”‚ β”œβ”€β”€ main.tsx # Entry point +β”‚ β”œβ”€β”€ assets/ # Images and icons +β”‚ β”œβ”€β”€ config/ # App configuration +β”‚ β”‚ └── json/ # JSON data for skills, projects, experience +β”‚ β”œβ”€β”€ services/ # API and data services +β”‚ β”œβ”€β”€ store/ # Zustand state management +β”‚ └── ui/ +β”‚ β”œβ”€β”€ components/ # Reusable UI components +β”‚ β”‚ β”œβ”€β”€ icon_service/ # Centralized icon mapping and service +β”‚ β”‚ β”œβ”€β”€ infinite_icon_carousel/ # Animated skill carousel +β”‚ β”‚ β”œβ”€β”€ tech/ # TechChip and tech icon mapping +β”‚ β”œβ”€β”€ forms/ # Form components +β”‚ β”œβ”€β”€ nav/ # Navigation components +β”‚ └── pages/ # Route pages (blog, home, projects, etc.) +β”œβ”€β”€ package.json # Project dependencies +β”œβ”€β”€ tsconfig.json # TypeScript configuration +β”œβ”€β”€ vite.config.ts # Vite configuration +└── README.md # Project overview and setup (this file) +``` + +## Getting Started + +1. **Install dependencies:** + ```powershell + npm install + ``` +2. **Start the development server:** + ```powershell + npm run dev + ``` + +The app will be available at the local address shown in your terminal (typically http://localhost:5173). + +--- + +## Data-Driven Features + +- **Skills Carousel:** Powered by JSON for easy updates +- **Featured Projects:** JSON-driven, type-safe project data +- **Centralized Icon Service:** Consistent icon rendering across the app + +## Planned Features + +- Home, Projects, Blog, and Contact pages +- Responsive, accessible Material UI design +- State management with Zustand +- Async data with React Query +- Easy content updates via JSON + +--- + +This project is intended for demo and educational purposes as part of Copilot Labs. Feel free to experiment, break things, and learn! diff --git a/sample-code/SimpleFullStack/Web/eslint.config.js b/sample-code/SimpleFullStack/Web/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/sample-code/SimpleFullStack/Web/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/sample-code/SimpleFullStack/Web/index.html b/sample-code/SimpleFullStack/Web/index.html new file mode 100644 index 0000000..4faf546 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/index.html @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + GitHub Copilot Example Project + + +
+ + + diff --git a/sample-code/SimpleFullStack/Web/package-lock.json b/sample-code/SimpleFullStack/Web/package-lock.json new file mode 100644 index 0000000..a8e4992 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/package-lock.json @@ -0,0 +1,6306 @@ +{ + "name": "web", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@hookform/resolvers": "^5.0.1", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@tanstack/react-query": "^5.76.1", + "axios": "^1.9.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hook-form": "^7.56.4", + "react-icons": "^5.5.0", + "react-router-dom": "^7.6.0", + "react-vertical-timeline-component": "^3.5.3", + "zod": "^3.25.27", + "zustand": "^5.0.4" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/node": "^22.15.21", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@types/react-vertical-timeline-component": "^3.3.6", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.30.1", + "vite": "^6.3.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-beta.53.tgz", + "integrity": "sha512-HVsEm3wjSe3BCXWxnyqrTWWQAxvtHR35F4q84jS68aS8R3WfbOnFEwlqsrWX5quZL0ArR68REOWRDCyG+JBSlQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-call-delegate": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.0.0-beta.53.tgz", + "integrity": "sha512-kOdnk7nDkQsAM+fxhiN6sy0jNep5VN6jv7H8pbu2trW5ziopw+cwNxTkihLUAEC+gJU45WngJTZtjUMR/2Kckg==", + "license": "MIT", + "dependencies": { + "@babel/helper-hoist-variables": "7.0.0-beta.53", + "@babel/traverse": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/generator": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.53.tgz", + "integrity": "sha512-XnfdZ6oFVC4cE4+7jbEa1MLFSXrGY/SfSE6onUyyPSrRbjYs9sdrYKi/JgKGSJX65A8GFswHwWcBPCynfVEr5g==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53", + "jsesc": "^2.5.1", + "lodash": "^4.17.5", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/traverse": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.53.tgz", + "integrity": "sha512-JZh3vX/9ox9aoub2gLlpPRm8LM0yJuqzmp5MrbwD57SPh1dHMDWjGen9exbaITAe03t9MJV5PAacv0K2UJBffg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/generator": "7.0.0-beta.53", + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "debug": "^3.1.0", + "globals": "^11.1.0", + "invariant": "^2.2.0", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-call-delegate/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-call-delegate/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-define-map": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.0.0-beta.53.tgz", + "integrity": "sha512-rqDAadUz9cLul+epYez/X6PPwS85j/xL2q61JT14MFJaaCFKmQ8QhmZmktcSsYC8XhsDlaLAdwxSIw1a8oih9g==", + "license": "MIT", + "dependencies": { + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-define-map/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.53.tgz", + "integrity": "sha512-vmdaNg17OWa0lFVJqZLQcvc59KIOcJDpyvqr3EJT9BYsjh/JxDlYq/JpBzLpWv9AkXeBdY4NevZXD37gdsLu0Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-get-function-arity": "7.0.0-beta.53", + "@babel/template": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.53.tgz", + "integrity": "sha512-MCZLPfGfNBHdE5wNfY5eK1hpY3fyq8zq+NfbfFCUtIzHl7SfUzHzH8rKPBXSB2Ypetq2sBHdDyslSSgnG0Watg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-function-name/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-function-name/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-function-name/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-function-name/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-function-name/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-function-name/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.53.tgz", + "integrity": "sha512-jLbME3MfCVT88GLuUDJ1X+ErDeWi59aeBb/O6pyhp5C+eVRRiLxzptRmpvJqG+Va6aOBWSoJ8uBNKJ1ghT/ONg==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-get-function-arity/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0-beta.53.tgz", + "integrity": "sha512-ktLEBpVZkvPUjNn8JK11m/74cWw9H9U3QizAiJUPdnvkvz/F0ucMyIOpMa7vuhmpHmlRzgAraIlTrmQfxv6BGg==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0-beta.53.tgz", + "integrity": "sha512-/rJvy0+ipYwZh5pXSrifbUo7Ct+Dfm85AQqSYphbX67qEOEk92phxE95Tpw1wtLgWEbWBQ3WRHfTyEadqlPocg==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-member-expression-to-functions/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0-beta.53.tgz", + "integrity": "sha512-B6DMEnC9slZtBDRRjLi7OTcfmsXPPZsRLldqQ0TZjWj4QuZWFSDlonVWIYI+2Fb9DiA/dZMXMv9JDgVGGibMkw==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-optimise-call-expression/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-regex": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.0.0-beta.53.tgz", + "integrity": "sha512-Wh9ORGs15i37YovmEcS2W8PQDMR9T5UxAL1EtHW/uAfqH4T703dRK/7rsREYjA2lQ8pPHJWN/y0tMuCoOvl3jQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.0.0-beta.53.tgz", + "integrity": "sha512-/KG2wmojlGgtuco35Aq5RYftukhXiql4dG7ux+oAnpi6wALb6BjPmUWJe5m57lNNQgLtCfQ8596j0h5GZu65QA==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "7.0.0-beta.53", + "@babel/helper-optimise-call-expression": "7.0.0-beta.53", + "@babel/traverse": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/generator": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.53.tgz", + "integrity": "sha512-XnfdZ6oFVC4cE4+7jbEa1MLFSXrGY/SfSE6onUyyPSrRbjYs9sdrYKi/JgKGSJX65A8GFswHwWcBPCynfVEr5g==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53", + "jsesc": "^2.5.1", + "lodash": "^4.17.5", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/traverse": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.53.tgz", + "integrity": "sha512-JZh3vX/9ox9aoub2gLlpPRm8LM0yJuqzmp5MrbwD57SPh1dHMDWjGen9exbaITAe03t9MJV5PAacv0K2UJBffg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/generator": "7.0.0-beta.53", + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "debug": "^3.1.0", + "globals": "^11.1.0", + "invariant": "^2.2.0", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-replace-supers/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-replace-supers/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.0.0-beta.53.tgz", + "integrity": "sha512-mq4csKX0vucrhZKgTG/ogNCuq6KiLEVXRDG5sRWggpuN4N6f/z+CyGNi83tqLRv9VLjV7IEQu/6UyI2wAUxFOg==", + "license": "MIT", + "dependencies": { + "@babel/template": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/template": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.53.tgz", + "integrity": "sha512-MCZLPfGfNBHdE5wNfY5eK1hpY3fyq8zq+NfbfFCUtIzHl7SfUzHzH8rKPBXSB2Ypetq2sBHdDyslSSgnG0Watg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-simple-access/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-simple-access/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.53.tgz", + "integrity": "sha512-7twjNOXZFIuiGpfkaf2j1WuGFbfrmHS5ES9GXXXT0xbQ5UmyX9nvaTJHMt11t6pvIjv1xvtBVuDyMCrvyd+E/w==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.56.tgz", + "integrity": "sha512-q4TfI+jJISul6vVpZJktzH4tupwRiVk6KXRhB8PHqJ7erl966I6ePDXl9mAbE8jMM7YswhnnB0j1SYP7LBVyhg==", + "license": "MIT", + "peer": true, + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT", + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT", + "peer": true + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz", + "integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.0.tgz", + "integrity": "sha512-E0OqhZv548Qdc0PwWhLVA2zmjJZSTvaL4ZhoswmI8NJEC1tpW2js6LLP827jrW9MEiXYdz3QS6+hask83w74yQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.0.tgz", + "integrity": "sha512-1mUPMAZ+Qk3jfgL5ftRR06ATH/Esi0izHl1z56H+df6cwIlCWG66RXciUqeJCttbOXOQ5y2DCjLZI/4t3Yg3LA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.0.tgz", + "integrity": "sha512-ahUJdrhEv+mCp4XHW+tHIEYzZMSRLg8z4AjUOsj44QpD1ZaMxQoVOG2xiHvLFdcsIPbgSRx1bg1eQSheHBgvtg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/core-downloads-tracker": "^7.1.0", + "@mui/system": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.1.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.0.tgz", + "integrity": "sha512-4Kck4jxhqF6YxNwJdSae1WgDfXVg0lIH6JVJ7gtuFfuKcQCgomJxPvUEOySTFRPz1IZzwz5OAcToskRdffElDA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/utils": "^7.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.0.tgz", + "integrity": "sha512-m0mJ0c6iRC+f9hMeRe0W7zZX1wme3oUX0+XTVHjPG7DJz6OdQ6K/ggEOq7ZdwilcpdsDUwwMfOmvO71qDkYd2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.0.tgz", + "integrity": "sha512-iedAWgRJMCxeMHvkEhsDlbvkK+qKf9me6ofsf7twk/jfT4P1ImVf7Rwb5VubEA0sikrVL+1SkoZM41M4+LNAVA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/private-theming": "^7.1.0", + "@mui/styled-engine": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz", + "integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz", + "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/types": "^7.4.2", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz", + "integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz", + "integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz", + "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz", + "integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz", + "integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz", + "integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz", + "integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz", + "integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz", + "integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz", + "integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz", + "integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz", + "integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz", + "integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz", + "integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz", + "integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz", + "integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz", + "integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz", + "integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz", + "integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz", + "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tanstack/query-core": { + "version": "5.76.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.76.0.tgz", + "integrity": "sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.76.1.tgz", + "integrity": "sha512-YxdLZVGN4QkT5YT1HKZQWiIlcgauIXEIsMOTSjvyD5wLYK8YVvKZUPAysMqossFJJfDpJW3pFn7WNZuPOqq+fw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.76.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", + "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", + "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-vertical-timeline-component": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/react-vertical-timeline-component/-/react-vertical-timeline-component-3.3.6.tgz", + "integrity": "sha512-OUvyPXRjXvUD/SNLO0CW0GbIxVF32Ios5qHecMSfw6kxnK1cPULD9NV80EuqZ3WmS/s6BgbcwmN8k4ISb3akhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", + "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.155", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", + "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "dev": true, + "license": "ISC" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-hook-form": { + "version": "7.56.4", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.4.tgz", + "integrity": "sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", + "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.0.tgz", + "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", + "license": "MIT", + "dependencies": { + "react-router": "7.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-vertical-timeline-component": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/react-vertical-timeline-component/-/react-vertical-timeline-component-3.5.3.tgz", + "integrity": "sha512-GS3kcppKpxbgOJDbPWsO3B6/MktVuo/YzebrOHcf5IF9VfOGeKysJuMyuKkvp6XDWAm0n1wlIYyYK1GxiGXSqQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@babel/preset-es2015": "^7.0.0-beta.53", + "classnames": "^2.2.6", + "react-intersection-observer": "^8.26.2" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.56.tgz", + "integrity": "sha512-OBeGs8UXWpKl0oK2T5nUXNl2yu8RKxqL/7aUnMtKDXCU6VUrNP3npdrPivBA11HPB15TVI49nWf2lntTzoUuAg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/highlight": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/core": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.0.0-beta.56.tgz", + "integrity": "sha512-IsytpdHZqo5pgJj4FTcpEMKmfXK9TdvThLZo4yUOjbuVZCy8NAwoeBnojvKCNf+139L7xNIIosp3RVA0cMkbOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.56", + "@babel/generator": "7.0.0-beta.56", + "@babel/helpers": "7.0.0-beta.56", + "@babel/parser": "7.0.0-beta.56", + "@babel/template": "7.0.0-beta.56", + "@babel/traverse": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56", + "convert-source-map": "^1.1.0", + "debug": "^3.1.0", + "json5": "^0.5.0", + "lodash": "^4.17.10", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/generator": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.56.tgz", + "integrity": "sha512-d+Ls/Vr5OU5FBDYQToXSqAluI3r2UaSoNZ41zD3sxdoVoaT8K5Bdh4So4eG4o//INGM7actValXGfb+5J1+r8w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "7.0.0-beta.56", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-imports": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.53.tgz", + "integrity": "sha512-nyyERQH7kRCy0OR2Ek0+sD+wxZEhCmaLAVE7SylPYmCce1Dq8XGmibT1eQVekRkr78utXnDKMe4A269SBVlIRA==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-imports/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0-beta.53.tgz", + "integrity": "sha512-FQrR3poCdkIxIl+QGkw9Fq3fYcEmcFloO/CSX26FYZuXcHZ5FbPLZajtdcQmPNWWqIicHzCXd0h+gkcRSP9siQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "7.0.0-beta.53", + "@babel/helper-simple-access": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "@babel/template": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.53.tgz", + "integrity": "sha512-MCZLPfGfNBHdE5wNfY5eK1hpY3fyq8zq+NfbfFCUtIzHl7SfUzHzH8rKPBXSB2Ypetq2sBHdDyslSSgnG0Watg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-plugin-utils": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0-beta.53.tgz", + "integrity": "sha512-ziTIzKm3Hj8LvmV6HwyPC2t2NgSNg2T72Cifqaw3zo44ATRUeNI/nH7NoQZChNNwye97pbzs+UAHq6fCTt3uFg==", + "license": "MIT" + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helpers": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.0.0-beta.56.tgz", + "integrity": "sha512-KaNBuVlAGW6sFCEWjliN29dL8K4L/ff8ZUaR/D5ou/JsqOuxLRy34Rf8WXMru3Et2g4Czly6vJeSmaYyf3s0lA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "7.0.0-beta.56", + "@babel/traverse": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/parser": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.56.tgz", + "integrity": "sha512-JM0ughhbo+sPXw2Z+SUyowfYrAOhjanzjMshcLswBdXVelJCOeEKe/FqMqPWGVPQr7wByongXIn+MKdCpY7DBw==", + "license": "MIT", + "peer": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0-beta.53.tgz", + "integrity": "sha512-CxYIFD+eutA6WzT16fsxGn8Y1A50iG3JVOeL8MGn51h42E2ea5xvfWnT/aAthEuKqbHR+FDyxBZ7o2lXRMwAag==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0-beta.53.tgz", + "integrity": "sha512-bBCniYwYf4HI9jaVT1sf272aijJBDg17hAAAa1XduB/humwRD+XE5nZXYfh5ktMTjxHyWxB55LK6Y1W+F4RSDg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0-beta.53.tgz", + "integrity": "sha512-lBDSm853wqigbxRR9m71Ow91whK/gjOJzFo2kFwJulUZJaV/pdQGSXopANPRjITplKbDufIbClPdIK1V6dnN+w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "lodash": "^4.17.5" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-classes": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.0.0-beta.53.tgz", + "integrity": "sha512-Mv/3NVDJ40aawBuwg+Yy776Qynmo8FRM4RLtGk+TyIH9PKw83b1jL0Gxa1OvzXjBiizq6oQLOhUvWnmh1uSL5A==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "7.0.0-beta.53", + "@babel/helper-define-map": "7.0.0-beta.53", + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-optimise-call-expression": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-replace-supers": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "globals": "^11.1.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0-beta.53.tgz", + "integrity": "sha512-5q7Epq0AIj3Pakj1w4WeJvcGZbPpIGymjXjinh7s/U8nCuXsxxjBiBXc0Hk0IaY95C51FxWmmbp3t1v7QqifNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-destructuring": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.0.0-beta.53.tgz", + "integrity": "sha512-V7qWHCE5f2/hb4gd7rFe1MIkVoC0ZIo/XFR9YAHbRdUQtOI57FUjJL+Fz2BT5MaZYglfWFZy+YLnVzQhInJFJA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0-beta.53.tgz", + "integrity": "sha512-Laqs9gb/pkNVCZBugOYRhz8qaxz+XMz9CjKpF9eMrET1FToh0I2lp4yRKUHc4VPHd+oolkWvN55Hi28Ia3QOHQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-for-of": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0-beta.53.tgz", + "integrity": "sha512-7DD7gd/ywy3fTBZvQ8CiulD3SsUZLNrw22zD4nmH7LS8mTFcUnAsenbzHMDKmFs02ZwkLaAOS0lAQZFuSA/bNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-function-name": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.0.0-beta.53.tgz", + "integrity": "sha512-mvkWR0ay5U8IZQiCRV02jnhk0uY+DHZCbmBVQ9KAYq0mvunmtxHsk3NtEzSTHMSyXr69BUkQlVnYlWtYN0HP6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-instanceof": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-instanceof/-/plugin-transform-instanceof-7.0.0-beta.53.tgz", + "integrity": "sha512-/WX7rfrMy2nYfSGWdST3RZTPREDpNUNzl5YGx3O/M33qqepbeWBCwSggTSkdQ7iBAPi/CBjY7fGSVGCcCPR7/w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-literals": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0-beta.53.tgz", + "integrity": "sha512-1bWy5iRSQngH9klvojOdMotFH9PWY6aRDWSiHddIsc54VQrKz9NH6bBAwhf+2Jt+SJfCUbAaGuleeZkfMPZ7fg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.0.0-beta.53.tgz", + "integrity": "sha512-DldT9fmUfjr+pY1/fLidnMq4wk2GiN4114oWshYHSd5Eachi5BkfM6Ao2CsTbDL5PyTy3rdIRB6K9nL+7ze0YQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.53.tgz", + "integrity": "sha512-PP1obXrhqknxHDtJ90DoB+SwjwLzC0bGCeYBAx1T0rgLjmWYMGB103wNpfdJ1jqUH5mmC6PTNKBlasTGiEc9Cw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-simple-access": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0-beta.53.tgz", + "integrity": "sha512-ZCJZ18qEd7zf9F7Caph/z4TUARMlZiC5aI67wzOSeNpLhqSOrno5qxN7uukOuYvpsk3ji04S4tJOqHSuG+NUTA==", + "license": "MIT", + "dependencies": { + "@babel/helper-hoist-variables": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.0.0-beta.53.tgz", + "integrity": "sha512-TXX2R9rZQJbxyJ2leae4N+pT73t6Niolosa0WyZpLTGBmjKrFRHm/vpCm3v+tqb0GZMXbMQ/vzCRlMcQD8tPTQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-object-super": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.0.0-beta.53.tgz", + "integrity": "sha512-ko8FH0QnK76kZoNmP0KuZPFRlp2D07oLEXFylwpfOw6v2xmwqxajGiL51qrf0fhS5CT7zFbkHDmZljPfOo6Tzw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-replace-supers": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-parameters": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.0.0-beta.53.tgz", + "integrity": "sha512-X9zQrth/hIJ8EQFIF4lc+I16MmgEBoiog0izTC37wwXHEMtC9UWtspfDNVOIwuJ5vqKbQn8arqCUCh63SSG90g==", + "license": "MIT", + "dependencies": { + "@babel/helper-call-delegate": "7.0.0-beta.53", + "@babel/helper-get-function-arity": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-regenerator": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0-beta.53.tgz", + "integrity": "sha512-mGWykD0r9/7isJjTMG45kgUb63zWp9Rx1Mrd0tt8928IlNx4V2/1zjB1RqObiBE+ylkmhz6G3ywNmXuXSy9haQ==", + "license": "MIT", + "dependencies": { + "regenerator-transform": "^0.13.3" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0-beta.53.tgz", + "integrity": "sha512-kNjYrpDKi+ZYjWM1qQwD10ERplviaDb37/9RoLpeIRezf+DXPm5PMdIMYJH2gD552fbtkYwESp44hm2Izpi3rg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-spread": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0-beta.53.tgz", + "integrity": "sha512-pkK2dpGXiblw+OojZnyMdJAD/qUs0bDnhqqmN4WLdbvsbQW0wC/nWD7YmGUwl9B8kJ4cFmrvT1l5idh4d9Az3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0-beta.53.tgz", + "integrity": "sha512-axZvAsF66i0/hBqtlAeVWB66OGx+EU/5cY4DEQtqd217LdbtrTpV3oynG0cZylNPeY2TCU848ojlBCA1L3PvTQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-regex": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-template-literals": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0-beta.53.tgz", + "integrity": "sha512-0q5OZuPVBAB/rsqulVLWT/bEoT1dEcKiVkyUagKgaVer2rXy1eB6eSFV3cJ/gpnlXDB2L0dCgeakgGJz/a1q4g==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0-beta.53.tgz", + "integrity": "sha512-xm9X4m0x+HrZ9r8GqNFjnFlip0nh7zUjGzGFOFD1l07gepng3tG1HYrO+LJ9WwOqnE1pqc8d0cCz7nvxlEqHLQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0-beta.53.tgz", + "integrity": "sha512-6DlspW3xuGi9JKzof3cqdel69TF/bE0tn7wC3tl1+VZ+BnUauEcZjlaN8azTD5YfgEUuiqWl9+Oz0WOyBf+0Yw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-regex": "7.0.0-beta.53", + "regexpu-core": "^4.1.3" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/preset-es2015": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/preset-es2015/-/preset-es2015-7.0.0-beta.53.tgz", + "integrity": "sha512-rcLuTFjJ4jlJdjkFeyX/BUyht3tGmfa3fgtAlPafNLLsAZ6nriJFhFNSXdB6Sl+seTcKVYvZoEFWNuVvqDXrnQ==", + "deprecated": "πŸ‘‹ We've deprecated any official yearly presets in 6.x in favor or babel-preset-env. For 7.x it would be @babel/preset-env.", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/plugin-transform-arrow-functions": "7.0.0-beta.53", + "@babel/plugin-transform-block-scoped-functions": "7.0.0-beta.53", + "@babel/plugin-transform-block-scoping": "7.0.0-beta.53", + "@babel/plugin-transform-classes": "7.0.0-beta.53", + "@babel/plugin-transform-computed-properties": "7.0.0-beta.53", + "@babel/plugin-transform-destructuring": "7.0.0-beta.53", + "@babel/plugin-transform-duplicate-keys": "7.0.0-beta.53", + "@babel/plugin-transform-for-of": "7.0.0-beta.53", + "@babel/plugin-transform-function-name": "7.0.0-beta.53", + "@babel/plugin-transform-instanceof": "7.0.0-beta.53", + "@babel/plugin-transform-literals": "7.0.0-beta.53", + "@babel/plugin-transform-modules-amd": "7.0.0-beta.53", + "@babel/plugin-transform-modules-commonjs": "7.0.0-beta.53", + "@babel/plugin-transform-modules-systemjs": "7.0.0-beta.53", + "@babel/plugin-transform-modules-umd": "7.0.0-beta.53", + "@babel/plugin-transform-object-super": "7.0.0-beta.53", + "@babel/plugin-transform-parameters": "7.0.0-beta.53", + "@babel/plugin-transform-regenerator": "7.0.0-beta.53", + "@babel/plugin-transform-shorthand-properties": "7.0.0-beta.53", + "@babel/plugin-transform-spread": "7.0.0-beta.53", + "@babel/plugin-transform-sticky-regex": "7.0.0-beta.53", + "@babel/plugin-transform-template-literals": "7.0.0-beta.53", + "@babel/plugin-transform-typeof-symbol": "7.0.0-beta.53", + "@babel/plugin-transform-unicode-regex": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/template": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.56.tgz", + "integrity": "sha512-rsR9K18h0oiJTUmS/ICYREbV8qhPTic4SIqDSkzv9xOxupt7dKj8hWmZQLGPySO5x6cdn8py039o1wPQnsEGHg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.56", + "@babel/parser": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56", + "lodash": "^4.17.10" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.56.tgz", + "integrity": "sha512-9WTqtKP2Ll+jG68r+JEecXAbdH/kk5inN1VDSDaTgdYtZ82BYUS9XRWMVpc5HB9LJsu2ZEyUA1cGybID7eeOXA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.56", + "@babel/generator": "7.0.0-beta.56", + "@babel/helper-function-name": "7.0.0-beta.56", + "@babel/helper-split-export-declaration": "7.0.0-beta.56", + "@babel/parser": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56", + "debug": "^3.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse/node_modules/@babel/helper-function-name": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.56.tgz", + "integrity": "sha512-Lq4nPOt1j3sUq+1GVrw57dKq6wBKAHplGjYzEG8dkytqo93i6uSKKKg3smYXx2qohEVD5ciAyJjgRJq7RQu4Lg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-get-function-arity": "7.0.0-beta.56", + "@babel/template": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse/node_modules/@babel/helper-get-function-arity": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.56.tgz", + "integrity": "sha512-QU9EVlnDGTzBasgrdo/I4+RzZS7oqzz9YcetpYko3bp+VsRGokqsAQl3gIvxWTtxwibwboDEdBx+fGArtb2fhw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.56.tgz", + "integrity": "sha512-j886mQJQg5HDF7X0qK/AfNdrpIYUcJHxRKwBJ9dUvhpO3eFqsTLbJJpitgLaJQjh9D7Db5Aiq8MRghj3+MH57g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/types": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.56.tgz", + "integrity": "sha512-fRIBeHtKxAD3D1E7hYSpG4MnLt0AfzHHs5gfVclOB0NlfLu3qiWU/IqdbK2ixTK61424iEkV1P/VAzndx6ungA==", + "license": "MIT", + "peer": true, + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/react-vertical-timeline-component/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/react-vertical-timeline-component/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/react-intersection-observer": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.34.0.tgz", + "integrity": "sha512-TYKh52Zc0Uptp5/b4N91XydfSGKubEhgZRtcg1rhTKABXijc4Sdr1uTp5lJ8TN27jwUsdXxjHXtHa0kPj704sw==", + "license": "MIT", + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", + "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-transform": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz", + "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==", + "license": "MIT", + "dependencies": { + "private": "^0.1.6" + } + }, + "node_modules/regexpu-core": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", + "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^9.0.0", + "regjsgen": "^0.5.2", + "regjsparser": "^0.7.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", + "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", + "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.0", + "@rollup/rollup-android-arm64": "4.41.0", + "@rollup/rollup-darwin-arm64": "4.41.0", + "@rollup/rollup-darwin-x64": "4.41.0", + "@rollup/rollup-freebsd-arm64": "4.41.0", + "@rollup/rollup-freebsd-x64": "4.41.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", + "@rollup/rollup-linux-arm-musleabihf": "4.41.0", + "@rollup/rollup-linux-arm64-gnu": "4.41.0", + "@rollup/rollup-linux-arm64-musl": "4.41.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-musl": "4.41.0", + "@rollup/rollup-linux-s390x-gnu": "4.41.0", + "@rollup/rollup-linux-x64-gnu": "4.41.0", + "@rollup/rollup-linux-x64-musl": "4.41.0", + "@rollup/rollup-win32-arm64-msvc": "4.41.0", + "@rollup/rollup-win32-ia32-msvc": "4.41.0", + "@rollup/rollup-win32-x64-msvc": "4.41.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.27", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.27.tgz", + "integrity": "sha512-xkYsE+ztNLzBeoAG8Ipd2ICr86gyMpovQlB+Vid1LT7V16/Dj0z+Up1u1qxNX58cmJ/AtG2mvGw/7+jK48xEYw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.4.tgz", + "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/sample-code/SimpleFullStack/Web/package.json b/sample-code/SimpleFullStack/Web/package.json new file mode 100644 index 0000000..cdefe39 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/package.json @@ -0,0 +1,44 @@ +{ + "name": "web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@hookform/resolvers": "^5.0.1", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@tanstack/react-query": "^5.76.1", + "axios": "^1.9.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hook-form": "^7.56.4", + "react-icons": "^5.5.0", + "react-router-dom": "^7.6.0", + "react-vertical-timeline-component": "^3.5.3", + "zod": "^3.25.27", + "zustand": "^5.0.4" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/node": "^22.15.21", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@types/react-vertical-timeline-component": "^3.3.6", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.30.1", + "vite": "^6.3.5" + } +} \ No newline at end of file diff --git a/sample-code/SimpleFullStack/Web/public/github-copilot-logo.svg b/sample-code/SimpleFullStack/Web/public/github-copilot-logo.svg new file mode 100644 index 0000000..6b60e46 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/public/github-copilot-logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/sample-code/SimpleFullStack/Web/public/routes.json b/sample-code/SimpleFullStack/Web/public/routes.json new file mode 100644 index 0000000..f1bc58d --- /dev/null +++ b/sample-code/SimpleFullStack/Web/public/routes.json @@ -0,0 +1,9 @@ +{ + "routes": [ + { + "route": "/*", + "serve": "/index.html", + "statusCode": 200 + } + ] +} \ No newline at end of file diff --git a/sample-code/SimpleFullStack/Web/routes.json b/sample-code/SimpleFullStack/Web/routes.json new file mode 100644 index 0000000..f1bc58d --- /dev/null +++ b/sample-code/SimpleFullStack/Web/routes.json @@ -0,0 +1,9 @@ +{ + "routes": [ + { + "route": "/*", + "serve": "/index.html", + "statusCode": 200 + } + ] +} \ No newline at end of file diff --git a/sample-code/SimpleFullStack/Web/src/App.tsx b/sample-code/SimpleFullStack/Web/src/App.tsx new file mode 100644 index 0000000..314f281 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/App.tsx @@ -0,0 +1,33 @@ +import React, { Suspense } from 'react'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import { ThemeProvider, CssBaseline } from '@mui/material'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import myRoutes from 'config/routes.tsx'; +import GlobalModal from 'ui/components/global_modal.tsx/GlobalModal'; +import GlobalSnackbar from 'ui/components/global_snackbar/GlobalSnackbar'; +import ErrorBoundary from 'ui/components/boundaries/ErrorBoundary'; +import LoadingPage from 'ui/components/boundaries/LoadingPage'; +import { lightTheme } from 'config/theme/lightTheme'; + + +const queryClient = new QueryClient(); +const router = createBrowserRouter(myRoutes); + +const App: React.FC = () => { + return ( + + + + + + + }> + + + + + + ); +}; + +export default App; diff --git a/sample-code/SimpleFullStack/Web/src/config/constants.ts b/sample-code/SimpleFullStack/Web/src/config/constants.ts new file mode 100644 index 0000000..52695b3 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/config/constants.ts @@ -0,0 +1,25 @@ +import PersonIcon from '@mui/icons-material/Person'; +import ShoppingBasketIcon from '@mui/icons-material/ShoppingBasket'; + +export const APPLICATION = { + Home: { + id: 'home', + label: 'Home', + route: '/', + icon: PersonIcon, + }, + Products: { + id: 'products', + label: 'Products', + route: '/products', + icon: ShoppingBasketIcon, + }, +}; + +//Make Applicattion an array +export const APPLICATION_ARRAY = Object.values(APPLICATION); + +export type ApplicationType = (typeof APPLICATION)[keyof typeof APPLICATION]; + +export const USER_NAME = 'Neudesic'; +export const BASE_URL = import.meta.env.VITE_BASE_URL || ''; diff --git a/sample-code/SimpleFullStack/Web/src/config/routes.tsx b/sample-code/SimpleFullStack/Web/src/config/routes.tsx new file mode 100644 index 0000000..c210c3f --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/config/routes.tsx @@ -0,0 +1,38 @@ +import type { RouteObject } from 'react-router-dom'; +import MainLayout from 'ui/components/layouts/MainLayout'; +import { APPLICATION } from './constants'; +import NotFoundPage from 'ui/components/boundaries/NotFoundPage'; + +const myRoutes: RouteObject[] = [ + { + path: APPLICATION.Home.route, + element: , + children: [{ + path: APPLICATION.Home.route, + lazy: async () => { + const Component = await import('ui/pages/home/HomePage'); + return { Component: Component.default }; + }, + }, { + path: APPLICATION.Products.route, + lazy: async () => { + const Component = await import('ui/pages/product/ProductsPage'); + return { Component: Component.default }; + }, + }, + { + path: `${APPLICATION.Products.route}/:productId`, + lazy: async () => { + const Component = await import('ui/pages/product/ProductDetailPage'); + return { Component: Component.default }; + }, + }, + { + path: '*', + element: , + }, + ], + }, +]; + +export default myRoutes; diff --git a/sample-code/SimpleFullStack/Web/src/config/theme/lightTheme.ts b/sample-code/SimpleFullStack/Web/src/config/theme/lightTheme.ts new file mode 100644 index 0000000..4c1b178 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/config/theme/lightTheme.ts @@ -0,0 +1,30 @@ +import { createTheme } from '@mui/material/styles'; + +export const lightTheme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#e06a00', // Slightly darker orange for button color + contrastText: '#FFFFFF', + }, + secondary: { + main: '#393E46', // Selected nav link color (orange) + contrastText: '#FFFFFF', + }, + background: { + default: '#E5E5E5', + paper: '#F2F2F2', + }, + text: { + primary: '#212121', + secondary: '#616161', + }, + error: { main: '#D32F2F', contrastText: '#FFFFFF' }, + warning: { main: '#F4802B', contrastText: '#FFFFFF' }, + success: { main: '#388E3C', contrastText: '#FFFFFF' }, + divider: '#E0E0E0', + }, + typography: { + fontFamily: 'Roboto, Arial, sans-serif', + }, +}); diff --git a/sample-code/SimpleFullStack/Web/src/json/categories.json b/sample-code/SimpleFullStack/Web/src/json/categories.json new file mode 100644 index 0000000..22ecdf5 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/json/categories.json @@ -0,0 +1,75 @@ +[ + { + "categoryID": 1, + "name": "Electronics", + "description": "Electronic devices and accessories", + "parentCategoryID": null, + "subCategories": [ + { + "categoryID": 2, + "name": "Computers", + "description": "Desktop and laptop computers", + "parentCategoryID": 1, + "subCategories": [ + { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + }, + { + "categoryID": 6, + "name": "Desktops", + "description": "Stationary computers", + "parentCategoryID": 2, + "subCategories": [] + } + ] + }, + { + "categoryID": 3, + "name": "Smartphones", + "description": "Mobile phones and accessories", + "parentCategoryID": 1, + "subCategories": [] + }, + { + "categoryID": 4, + "name": "Audio", + "description": "Headphones, speakers, and audio equipment", + "parentCategoryID": 1, + "subCategories": [] + } + ] + }, + { + "categoryID": 7, + "name": "Clothing", + "description": "Apparel and fashion items", + "parentCategoryID": null, + "subCategories": [ + { + "categoryID": 8, + "name": "Men's", + "description": "Men's clothing", + "parentCategoryID": 7, + "subCategories": [] + }, + { + "categoryID": 9, + "name": "Women's", + "description": "Women's clothing", + "parentCategoryID": 7, + "subCategories": [] + } + ] + }, + { + "categoryID": 10, + "name": "Home & Garden", + "description": "Items for home and outdoor spaces", + "parentCategoryID": null, + "subCategories": [] + } +] \ No newline at end of file diff --git a/sample-code/SimpleFullStack/Web/src/json/productAttributes.json b/sample-code/SimpleFullStack/Web/src/json/productAttributes.json new file mode 100644 index 0000000..2592693 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/json/productAttributes.json @@ -0,0 +1,230 @@ +[ + { + "attributeID": 1, + "productID": 1, + "attributeName": "Processor", + "attributeValue": "Apple M2 Pro" + }, + { + "attributeID": 2, + "productID": 1, + "attributeName": "RAM", + "attributeValue": "16GB" + }, + { + "attributeID": 3, + "productID": 1, + "attributeName": "Storage", + "attributeValue": "512GB SSD" + }, + { + "attributeID": 4, + "productID": 1, + "attributeName": "Screen Size", + "attributeValue": "16 inches" + }, + { + "attributeID": 5, + "productID": 1, + "attributeName": "Color", + "attributeValue": "Space Gray" + }, + { + "attributeID": 6, + "productID": 2, + "attributeName": "Processor", + "attributeValue": "Intel Core i7-13700H" + }, + { + "attributeID": 7, + "productID": 2, + "attributeName": "RAM", + "attributeValue": "16GB" + }, + { + "attributeID": 8, + "productID": 2, + "attributeName": "Storage", + "attributeValue": "1TB SSD" + }, + { + "attributeID": 9, + "productID": 2, + "attributeName": "Screen Size", + "attributeValue": "15.6 inches" + }, + { + "attributeID": 10, + "productID": 2, + "attributeName": "Color", + "attributeValue": "Silver" + }, + { + "attributeID": 11, + "productID": 3, + "attributeName": "Storage", + "attributeValue": "256GB" + }, + { + "attributeID": 12, + "productID": 3, + "attributeName": "Color", + "attributeValue": "Titanium" + }, + { + "attributeID": 13, + "productID": 3, + "attributeName": "Screen Size", + "attributeValue": "6.1 inches" + }, + { + "attributeID": 14, + "productID": 3, + "attributeName": "Camera", + "attributeValue": "48MP Triple Camera" + }, + { + "attributeID": 15, + "productID": 4, + "attributeName": "Storage", + "attributeValue": "512GB" + }, + { + "attributeID": 16, + "productID": 4, + "attributeName": "Color", + "attributeValue": "Phantom Black" + }, + { + "attributeID": 17, + "productID": 4, + "attributeName": "Screen Size", + "attributeValue": "6.8 inches" + }, + { + "attributeID": 18, + "productID": 4, + "attributeName": "Camera", + "attributeValue": "200MP Quad Camera" + }, + { + "attributeID": 19, + "productID": 5, + "attributeName": "Color", + "attributeValue": "Black" + }, + { + "attributeID": 20, + "productID": 5, + "attributeName": "Battery Life", + "attributeValue": "30 hours" + }, + { + "attributeID": 21, + "productID": 5, + "attributeName": "Noise Cancellation", + "attributeValue": "Active" + }, + { + "attributeID": 22, + "productID": 6, + "attributeName": "Processor", + "attributeValue": "Intel Core i5-13400" + }, + { + "attributeID": 23, + "productID": 6, + "attributeName": "RAM", + "attributeValue": "8GB" + }, + { + "attributeID": 24, + "productID": 6, + "attributeName": "Storage", + "attributeValue": "512GB SSD" + }, + { + "attributeID": 25, + "productID": 7, + "attributeName": "Size", + "attributeValue": "Large" + }, + { + "attributeID": 26, + "productID": 7, + "attributeName": "Material", + "attributeValue": "100% Cotton" + }, + { + "attributeID": 27, + "productID": 7, + "attributeName": "Color", + "attributeValue": "Navy Blue" + }, + { + "attributeID": 28, + "productID": 8, + "attributeName": "Size", + "attributeValue": "Medium" + }, + { + "attributeID": 29, + "productID": 8, + "attributeName": "Material", + "attributeValue": "Cotton Blend" + }, + { + "attributeID": 30, + "productID": 8, + "attributeName": "Color", + "attributeValue": "Floral Print" + }, + { + "attributeID": 31, + "productID": 9, + "attributeName": "Plants Included", + "attributeValue": "Snake Plant, Pothos, ZZ Plant" + }, + { + "attributeID": 32, + "productID": 9, + "attributeName": "Pot Material", + "attributeValue": "Ceramic" + }, + { + "attributeID": 33, + "productID": 9, + "attributeName": "Care Level", + "attributeValue": "Easy" + }, + { + "attributeID": 34, + "productID": 10, + "attributeName": "Processor", + "attributeValue": "Intel Core i9-13900H" + }, + { + "attributeID": 35, + "productID": 10, + "attributeName": "Graphics", + "attributeValue": "NVIDIA RTX 4070 8GB" + }, + { + "attributeID": 36, + "productID": 10, + "attributeName": "RAM", + "attributeValue": "32GB DDR5" + }, + { + "attributeID": 37, + "productID": 10, + "attributeName": "Storage", + "attributeValue": "1TB NVMe SSD" + }, + { + "attributeID": 38, + "productID": 10, + "attributeName": "Screen", + "attributeValue": "17.3 inch 240Hz QHD" + } +] \ No newline at end of file diff --git a/sample-code/SimpleFullStack/Web/src/json/products.json b/sample-code/SimpleFullStack/Web/src/json/products.json new file mode 100644 index 0000000..11f8b09 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/json/products.json @@ -0,0 +1,162 @@ +[ + { + "productID": 1, + "name": "MacBook Pro 16-inch", + "description": "Apple MacBook Pro with M2 Pro chip, 16GB RAM, 512GB SSD", + "sku": "APP-MBP16-M2P", + "categoryID": 5, + "brand": "Apple", + "isActive": true, + "category": { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + } + }, + { + "productID": 2, + "name": "Dell XPS 15", + "description": "Dell XPS 15 with Intel i7, 16GB RAM, 1TB SSD", + "sku": "DEL-XPS15-I7", + "categoryID": 5, + "brand": "Dell", + "isActive": true, + "category": { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + } + }, + { + "productID": 3, + "name": "iPhone 15 Pro", + "description": "Apple iPhone 15 Pro, 256GB", + "sku": "APP-IP15P-256", + "categoryID": 3, + "brand": "Apple", + "isActive": true, + "category": { + "categoryID": 3, + "name": "Smartphones", + "description": "Mobile phones and accessories", + "parentCategoryID": 1, + "subCategories": [] + } + }, + { + "productID": 4, + "name": "Samsung Galaxy S24 Ultra", + "description": "Samsung Galaxy S24 Ultra, 512GB", + "sku": "SAM-GS24U-512", + "categoryID": 3, + "brand": "Samsung", + "isActive": true, + "category": { + "categoryID": 3, + "name": "Smartphones", + "description": "Mobile phones and accessories", + "parentCategoryID": 1, + "subCategories": [] + } + }, + { + "productID": 5, + "name": "Sony WH-1000XM5", + "description": "Sony Wireless Noise Cancelling Headphones", + "sku": "SON-WH1000XM5", + "categoryID": 4, + "brand": "Sony", + "isActive": true, + "category": { + "categoryID": 4, + "name": "Audio", + "description": "Headphones, speakers, and audio equipment", + "parentCategoryID": 1, + "subCategories": [] + } + }, + { + "productID": 6, + "name": "HP Pavilion Desktop", + "description": "HP Pavilion Desktop PC with Intel i5, 8GB RAM, 512GB SSD", + "sku": "HP-PVDTOP-I5", + "categoryID": 6, + "brand": "HP", + "isActive": true, + "category": { + "categoryID": 6, + "name": "Desktops", + "description": "Stationary computers", + "parentCategoryID": 2, + "subCategories": [] + } + }, + { + "productID": 7, + "name": "Men's Cotton T-Shirt", + "description": "Classic fit cotton t-shirt, various colors", + "sku": "CLO-MCTS-LG", + "categoryID": 8, + "brand": "Generic", + "isActive": true, + "category": { + "categoryID": 8, + "name": "Men's", + "description": "Men's clothing", + "parentCategoryID": 7, + "subCategories": [] + } + }, + { + "productID": 8, + "name": "Women's Casual Dress", + "description": "Summer casual dress, knee length", + "sku": "CLO-WCDR-MD", + "categoryID": 9, + "brand": "Generic", + "isActive": true, + "category": { + "categoryID": 9, + "name": "Women's", + "description": "Women's clothing", + "parentCategoryID": 7, + "subCategories": [] + } + }, + { + "productID": 9, + "name": "Indoor Plant Set", + "description": "Set of 3 small indoor plants with pots", + "sku": "HG-IPLTS-3PK", + "categoryID": 10, + "brand": "GreenThumb", + "isActive": true, + "category": { + "categoryID": 10, + "name": "Home & Garden", + "description": "Items for home and outdoor spaces", + "parentCategoryID": null, + "subCategories": [] + } + }, + { + "productID": 10, + "name": "Gaming Laptop", + "description": "High-performance gaming laptop with RTX 4070", + "sku": "MSI-GMLPT-4070", + "categoryID": 5, + "brand": "MSI", + "isActive": false, + "category": { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + } + } +] \ No newline at end of file diff --git a/sample-code/SimpleFullStack/Web/src/main.tsx b/sample-code/SimpleFullStack/Web/src/main.tsx new file mode 100644 index 0000000..4aff025 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/sample-code/SimpleFullStack/Web/src/services/axiosClient.ts b/sample-code/SimpleFullStack/Web/src/services/axiosClient.ts new file mode 100644 index 0000000..c2e0896 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/services/axiosClient.ts @@ -0,0 +1,13 @@ +import axios from 'axios'; +import { BASE_URL } from 'config/constants'; + +// Create a pre-configured Axios instance +const axiosClient = axios.create({ + baseURL: BASE_URL, // Set your API base URL here + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + }, +}); + +export default axiosClient; diff --git a/sample-code/SimpleFullStack/Web/src/store/useAppConfig.ts b/sample-code/SimpleFullStack/Web/src/store/useAppConfig.ts new file mode 100644 index 0000000..86b84e0 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/store/useAppConfig.ts @@ -0,0 +1,15 @@ +import { create } from 'zustand'; + +export type ThemeMode = 'light' | 'dark'; + +interface AppConfigStore { + theme: ThemeMode; + setTheme: (theme: ThemeMode) => void; +} + +const useAppConfig = create(set => ({ + theme: 'light', + setTheme: theme => set({ theme }), +})); + +export default useAppConfig; diff --git a/sample-code/SimpleFullStack/Web/src/types/Category.ts b/sample-code/SimpleFullStack/Web/src/types/Category.ts new file mode 100644 index 0000000..adc9f5b --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/types/Category.ts @@ -0,0 +1,17 @@ +export interface Category { + categoryID: number; + name: string; + description?: string; + parentCategoryID?: number; + subCategories?: Category[]; +} + +export interface AddCategory { + name: string; + description?: string; + parentCategoryID?: number; +} + +export interface UpdateCategoryDescription { + description?: string; +} diff --git a/sample-code/SimpleFullStack/Web/src/types/Product.ts b/sample-code/SimpleFullStack/Web/src/types/Product.ts new file mode 100644 index 0000000..525f165 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/types/Product.ts @@ -0,0 +1,30 @@ +import type { Category } from './Category'; + +export interface Product { + productID: number; + name: string; + description?: string; + sku: string; + categoryID: number; + brand?: string; + isActive: boolean; + category: Category; +} + +export interface AddProduct { + name: string; + description?: string; + sku: string; + categoryID: number; + brand?: string; + isActive: boolean; +} + +export interface UpdateProduct { + name?: string; + description?: string; + sku?: string; + categoryID?: number; + brand?: string; + isActive?: boolean; +} diff --git a/sample-code/SimpleFullStack/Web/src/types/ProductAttribute.ts b/sample-code/SimpleFullStack/Web/src/types/ProductAttribute.ts new file mode 100644 index 0000000..d9f8163 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/types/ProductAttribute.ts @@ -0,0 +1,17 @@ +export interface ProductAttribute { + attributeID: number; + productID: number; + attributeName: string; + attributeValue: string; +} + +export interface AddProductAttribute { + productID: number; + attributeName: string; + attributeValue: string; +} + +export interface UpdateProductAttribute { + attributeName: string; + attributeValue: string; +} diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/ErrorBoundary.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/ErrorBoundary.tsx new file mode 100644 index 0000000..08f55c2 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/ErrorBoundary.tsx @@ -0,0 +1,51 @@ +import React, { Component, type ReactNode } from 'react'; +import { Box, Typography, Button } from '@mui/material'; + +interface ErrorBoundaryProps { + children: ReactNode; +} + +interface ErrorBoundaryState { + hasError: boolean; + error?: Error; +} + +class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error) { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error('ErrorBoundary caught an error:', error, errorInfo); + } + + handleReload = () => { + window.location.reload(); + }; + + render() { + if (this.state.hasError) { + return ( + + + Something went wrong. + + + {this.state.error?.message || 'An unexpected error occurred.'} + + + + ); + } + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/LoadingPage.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/LoadingPage.tsx new file mode 100644 index 0000000..e528de1 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/LoadingPage.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Box, Typography, CircularProgress } from '@mui/material'; + +const LoadingPage: React.FC = () => ( + + + + Loading... + + +); + +export default LoadingPage; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/NotFoundPage.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/NotFoundPage.tsx new file mode 100644 index 0000000..21cbc90 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/boundaries/NotFoundPage.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Box, Typography } from '@mui/material'; + +const NotFoundPage: React.FC = () => ( + + + 404 + + + Page Not Found + + + Sorry, the page you are looking for does not exist. + + +); + +export default NotFoundPage; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/bread_crumb/BreadCrumb.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/bread_crumb/BreadCrumb.tsx new file mode 100644 index 0000000..d2634f0 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/bread_crumb/BreadCrumb.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Breadcrumbs, capitalize, Link, Typography } from '@mui/material'; +import { useLocation, Link as RouterLink } from 'react-router-dom'; + +const BreadCrumb: React.FC = () => { + const location = useLocation(); + const pathnames = location.pathname.split('/').filter((x) => x); + if (pathnames.length === 0) return null; + return ( + + + Home + + {pathnames.map((value, index) => { + const to = `/${pathnames.slice(0, index + 1).join('/')}`; + const isLast = index === pathnames.length - 1; + return isLast ? ( + + {capitalize(decodeURIComponent(value))} + + ) : ( + + {capitalize(decodeURIComponent(value))} + + ); + })} + + ); +}; + +export default BreadCrumb; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx new file mode 100644 index 0000000..bf31798 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Box } from '@mui/material'; + + +const Footer: React.FC = () => { + return ( + + + ); +}; + +export default Footer; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/global_modal.tsx/GlobalModal.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/global_modal.tsx/GlobalModal.tsx new file mode 100644 index 0000000..1fc3aa6 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/global_modal.tsx/GlobalModal.tsx @@ -0,0 +1,64 @@ +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import IconButton from '@mui/material/IconButton'; +import CloseIcon from '@mui/icons-material/Close'; +import useGlobalModal from './useGlobalModal'; + +const GlobalModal = () => { + const { isOpen, content, close, dialogProps, title, titleColor, primaryAction, primaryActionText, closeAction, formName } = useGlobalModal(); + + const handleClose = () => { + if (closeAction) { + closeAction(); + } else { + close(); + } + }; + + return ( + + {title && ( + + {title} + + + + + )} + {content} + + + + {formName && ( + + )} + {primaryAction && ( + + )} + + + + ); +}; + +export default GlobalModal; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/global_modal.tsx/useGlobalModal.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/global_modal.tsx/useGlobalModal.tsx new file mode 100644 index 0000000..a463857 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/global_modal.tsx/useGlobalModal.tsx @@ -0,0 +1,64 @@ +import { create } from 'zustand'; +import type { DialogProps } from '@mui/material/Dialog'; +import type { ReactNode } from 'react'; + +export type GlobalModalStore = { + isOpen: boolean; + content: ReactNode | null; + dialogProps?: Omit; + title?: ReactNode; + titleColor?: string; + primaryAction?: () => void; + primaryActionText?: string; + closeAction?: () => void; + formName?: string; + open: ( + content: ReactNode, + options?: { + dialogProps?: GlobalModalStore['dialogProps']; + title?: ReactNode; + titleColor?: string; + primaryAction?: () => void; + primaryActionText?: string; + closeAction?: () => void; + formName?: string; + } + ) => void; + close: () => void; +}; + +const useGlobalModal = create(set => ({ + isOpen: false, + content: null, + dialogProps: undefined, + title: undefined, + titleColor: undefined, + primaryAction: undefined, + primaryActionText: undefined, + closeAction: undefined, + formName: undefined, + open: (content, options) => set({ + isOpen: true, + content, + dialogProps: options?.dialogProps, + title: options?.title, + titleColor: options?.titleColor, + primaryAction: options?.primaryAction, + primaryActionText: options?.primaryActionText, + closeAction: options?.closeAction, + formName: options?.formName, + }), + close: () => set({ + isOpen: false, + content: null, + dialogProps: undefined, + title: undefined, + titleColor: undefined, + primaryAction: undefined, + primaryActionText: undefined, + closeAction: undefined, + formName: undefined, + }), +})); + +export default useGlobalModal; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/global_snackbar/GlobalSnackbar.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/global_snackbar/GlobalSnackbar.tsx new file mode 100644 index 0000000..f066073 --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/global_snackbar/GlobalSnackbar.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; +import Slide from '@mui/material/Slide'; +import type { SlideProps } from '@mui/material/Slide'; +import useGlobalSnackbar from './useGlobalSnackbar'; + +function TransitionUp(props: SlideProps) { + return ; +} + +export default function GlobalSnackbar() { + const { isOpen, message, severity, close, snackbarProps, alertProps } = useGlobalSnackbar(); + + const handleClose = (_event?: React.SyntheticEvent | Event, reason?: string) => { + if (reason === 'clickaway') return; + close(); + }; + + return ( + + + {message} + + + ); +} diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/global_snackbar/useGlobalSnackbar.ts b/sample-code/SimpleFullStack/Web/src/ui/components/global_snackbar/useGlobalSnackbar.ts new file mode 100644 index 0000000..6330ccb --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/global_snackbar/useGlobalSnackbar.ts @@ -0,0 +1,41 @@ +import { create } from 'zustand'; +import type { SnackbarProps } from '@mui/material/Snackbar'; +import type { AlertColor, AlertProps } from '@mui/material/Alert'; + +export type GlobalSnackbarStore = { + isOpen: boolean; + message: string; + severity: AlertColor; + snackbarProps?: Omit< + SnackbarProps, + 'open' | 'onClose' | 'message' | 'children' + >; + alertProps?: Omit; + open: ( + message: string, + severity: AlertColor, + snackbarProps?: GlobalSnackbarStore['snackbarProps'], + alertProps?: GlobalSnackbarStore['alertProps'] + ) => void; + close: () => void; +}; + +const useGlobalSnackbar = create(set => ({ + isOpen: false, + message: '', + severity: 'success', + snackbarProps: undefined, + alertProps: undefined, + open: (message, severity, snackbarProps, alertProps) => + set({ isOpen: true, message, severity, snackbarProps, alertProps }), + close: () => + set({ + isOpen: false, + message: '', + severity: 'success', + snackbarProps: undefined, + alertProps: undefined, + }), +})); + +export default useGlobalSnackbar; diff --git a/sample-code/SimpleFullStack/Web/src/ui/components/layouts/MainLayout.tsx b/sample-code/SimpleFullStack/Web/src/ui/components/layouts/MainLayout.tsx new file mode 100644 index 0000000..272a08f --- /dev/null +++ b/sample-code/SimpleFullStack/Web/src/ui/components/layouts/MainLayout.tsx @@ -0,0 +1,28 @@ +import Box from '@mui/material/Box'; +import Footer from 'ui/components/footer/Footer'; +import { Outlet } from 'react-router-dom'; +import NavBar from '../nav/NavBar'; + + +const MainLayout = () => { + return ( + + + + + +