codenuk_backend_mine/services/architecture-designer/designers/frontend/angular_designer_18.py

738 lines
32 KiB
Python

# ANGULAR 18 FRONTEND DESIGNER SPECIALIST
# Expert-level Angular 18 architecture design with TypeScript, standalone components, and modern patterns
import json
from typing import Dict, Any, List
from loguru import logger
try:
import anthropic
CLAUDE_AVAILABLE = True
except ImportError:
CLAUDE_AVAILABLE = False
class Angular18Designer:
"""Expert Angular 18 Frontend Designer - Processes tagged rules from requirement-processor"""
def __init__(self):
self.framework = "Angular 18"
self.language = "TypeScript"
self.claude_client = None
if CLAUDE_AVAILABLE:
try:
self.claude_client = anthropic.Anthropic()
logger.info(f"{self.framework} Designer initialized with Claude AI")
except Exception as e:
logger.warning(f"⚠️ Claude AI not available for {self.framework}: {e}")
else:
logger.warning(f"⚠️ Claude AI not available for {self.framework}")
async def design_frontend_architecture(
self,
functional_requirements: Dict[str, Any],
business_context: Dict[str, Any],
tech_stack: Any
) -> Dict[str, Any]:
"""Design comprehensive Angular 18 frontend architecture from tagged rules"""
logger.info(f"🎨 Designing {self.framework} frontend architecture...")
try:
# Extract all tagged rules from requirement-processor
tagged_rules = self._extract_tagged_rules(functional_requirements)
if not tagged_rules:
logger.warning("⚠️ No tagged rules found, using basic architecture")
return self._generate_basic_architecture(functional_requirements)
logger.info(f"📋 Processing {len(tagged_rules)} tagged rules for Angular 18 design")
if self.claude_client:
return await self._generate_ai_architecture(
tagged_rules, functional_requirements, business_context, tech_stack
)
else:
return self._generate_rule_based_architecture(
tagged_rules, functional_requirements, business_context, tech_stack
)
except Exception as e:
logger.error(f"{self.framework} AI generation failed: {e}")
return self._generate_rule_based_architecture(
tagged_rules, functional_requirements, business_context, tech_stack
)
def _extract_tagged_rules(self, functional_requirements: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Extract all tagged rules from requirement-processor output"""
all_rules = []
# Extract from detailed_requirements with tagged rules
detailed_requirements = functional_requirements.get('detailed_requirements', [])
for req in detailed_requirements:
requirement_name = req.get('requirement_name', 'Unknown')
feature_name = req.get('feature_name', 'Unknown')
rules = req.get('rules', [])
for rule in rules:
all_rules.append({
"rule_text": rule,
"requirement_name": requirement_name,
"feature_name": feature_name,
"source": "detailed_requirements"
})
# Extract from tagged_rules array (fallback)
tagged_rules = functional_requirements.get('tagged_rules', [])
for tagged_rule in tagged_rules:
all_rules.append({
"rule_text": tagged_rule.get('rule_text', ''),
"requirement_name": tagged_rule.get('requirement_name', 'Unknown'),
"feature_name": tagged_rule.get('feature_name', 'Unknown'),
"rule_id": tagged_rule.get('rule_id', ''),
"source": "tagged_rules"
})
# Extract from business_logic_rules (final fallback)
business_rules = functional_requirements.get('business_logic_rules', [])
for rule in business_rules:
all_rules.append({
"rule_text": rule,
"requirement_name": "General",
"feature_name": functional_requirements.get('feature_name', 'General'),
"source": "business_logic_rules"
})
logger.info(f"✅ Extracted {len(all_rules)} tagged rules for Angular 18 processing")
return all_rules
async def _generate_ai_architecture(
self,
tagged_rules: List[Dict[str, Any]],
functional_requirements: Dict[str, Any],
business_context: Dict[str, Any],
tech_stack: Any
) -> Dict[str, Any]:
"""Generate AI-powered Angular 18 architecture based on tagged rules"""
# Build comprehensive prompt with all tagged rules
rules_text = ""
for rule in tagged_rules:
rules_text += f"- {rule['feature_name']}{rule['requirement_name']}: {rule['rule_text']}\n"
feature_name = functional_requirements.get('feature_name', 'Angular Application')
complexity = functional_requirements.get('complexity_level', 'medium')
prompt = f"""You are a senior Angular 18 architect. Design a complete, production-ready frontend architecture based on these specific tagged business rules.
PROJECT CONTEXT:
- Application: {feature_name}
- Complexity: {complexity}
- Framework: Angular 18 with TypeScript
- Backend: ASP.NET Core Web API 8
- Database: MS SQL Server 2022
TAGGED BUSINESS RULES TO IMPLEMENT:
{rules_text}
Design a comprehensive Angular 18 architecture that implements ALL these tagged rules with:
1. **PROJECT STRUCTURE** (Angular 18 specific)
- Standalone components architecture
- Feature-based module organization
- Lazy loading strategy
- Signal-based state management
2. **COMPONENTS FOR EACH RULE**
- Analyze each tagged rule and determine what Angular components are needed
- Use Angular 18 standalone components
- Implement new control flow syntax (@if, @for, @switch)
- Component communication patterns
3. **SERVICES & DATA MANAGEMENT**
- HTTP services for ASP.NET Core Web API integration
- State management with Signals or NgRx (based on complexity)
- Data models and interfaces matching backend DTOs
- Error handling and loading states
4. **ROUTING & NAVIGATION**
- Route configuration for each feature/requirement
- Route guards for authentication/authorization
- Lazy loading modules
- Navigation workflows based on business rules
5. **FORMS & VALIDATION**
- Reactive forms for data entry requirements
- Custom validators based on business rules
- Form state management
- Dynamic form generation if needed
6. **UI/UX IMPLEMENTATION**
- Angular Material 3 components
- Responsive design with Angular CDK
- Theme configuration
- Accessibility compliance
7. **TESTING STRATEGY**
- Unit tests with Jest/Jasmine
- Component testing
- Integration tests
- E2E tests with Cypress
Return detailed JSON with specific Angular 18 components, services, modules, and implementation details that cover ALL tagged rules.
CRITICAL:
- Each tagged rule should map to specific Angular components/services
- Use Angular 18 features (standalone components, signals, new control flow)
- Include exact file structure and component specifications
- Ensure 100% coverage of all tagged business rules
JSON Format:
{{
"framework_info": {{"name": "Angular 18", "version": "18.x", ...}},
"project_structure": {{"src/app/": {{"features/": "...", "shared/": "..."}}}},
"components": [{{
"name": "ComponentName",
"path": "src/app/features/...",
"purpose": "Implements rule: [specific rule text]",
"type": "standalone",
"dependencies": [...],
"inputs": [...],
"outputs": [...]
}}],
"services": [...],
"routing": {...},
"forms": [...],
"state_management": {...},
"ui_framework": {...},
"testing": {...},
"implementation_ready": true
}}"""
try:
message = self.claude_client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=8000,
temperature=0.1,
messages=[{"role": "user", "content": prompt}]
)
claude_response = message.content[0].text.strip()
try:
architecture = json.loads(claude_response)
logger.info(f"{self.framework} AI architecture generated successfully")
# Add tagged rules coverage analysis
architecture["tagged_rules_coverage"] = self._analyze_rules_coverage(tagged_rules, architecture)
return architecture
except json.JSONDecodeError:
logger.warning(f"⚠️ {self.framework} AI response wasn't valid JSON, using fallback")
return self._generate_rule_based_architecture(tagged_rules, functional_requirements, business_context, tech_stack)
except Exception as e:
logger.error(f"{self.framework} Claude API error: {e}")
raise e
def _generate_rule_based_architecture(
self,
tagged_rules: List[Dict[str, Any]],
functional_requirements: Dict[str, Any],
business_context: Dict[str, Any],
tech_stack: Any
) -> Dict[str, Any]:
"""Generate Angular 18 architecture based on tagged rules analysis (fallback without AI)"""
feature_name = functional_requirements.get('feature_name', 'Angular Application')
# Analyze tagged rules to generate components and services
components = self._generate_components_from_rules(tagged_rules)
services = self._generate_services_from_rules(tagged_rules)
routes = self._generate_routes_from_rules(tagged_rules)
forms = self._generate_forms_from_rules(tagged_rules)
return {
"framework_info": {
"name": "Angular 18",
"version": "18.x",
"language": "TypeScript",
"cli_version": "18.x",
"node_version": "18+ or 20+",
"standalone_components": True
},
"project_structure": {
"src/app/": {
"core/": "Singleton services, guards, interceptors",
"shared/": "Shared standalone components, pipes, directives",
"features/": "Feature-based standalone components with lazy loading",
"models/": "TypeScript interfaces and DTOs matching backend",
"services/": "HTTP services for API communication",
"guards/": "Route guards for authentication/authorization",
"interceptors/": "HTTP interceptors for auth/error handling",
"pipes/": "Custom pipes for data transformation",
"directives/": "Custom directives for DOM manipulation"
}
},
"components": components,
"services": services,
"routing": {
"strategy": "Lazy loading with standalone components",
"routes": routes,
"guards": ["AuthGuard", "RoleGuard", "CanDeactivateGuard"],
"resolvers": ["DataResolver for pre-loading data"]
},
"forms": forms,
"state_management": {
"approach": "Angular 18 Signals for reactive state",
"complex_state": "NgRx Store for complex business logic",
"http_state": "HTTP services with signal-based caching"
},
"http_communication": {
"base_service": "ApiService with HttpClient",
"interceptors": ["AuthInterceptor", "ErrorInterceptor", "LoadingInterceptor"],
"error_handling": "Global error handling with user notifications"
},
"ui_framework": {
"library": "Angular Material 3",
"theming": "Material 3 design tokens",
"responsive": "Angular CDK Layout for responsive design",
"accessibility": "CDK a11y for accessibility compliance"
},
"testing": {
"unit": "Jest or Jasmine/Karma with TestBed",
"integration": "Component integration tests",
"e2e": "Cypress or Playwright for end-to-end testing",
"coverage": "Istanbul for code coverage reporting"
},
"build_optimization": {
"standalone_components": "Tree-shakeable standalone architecture",
"lazy_loading": "Route-based code splitting",
"bundle_optimization": "Angular CLI build optimizations",
"pwa": "Service Worker for progressive web app features"
},
"tagged_rules_coverage": self._analyze_rules_coverage(tagged_rules, {}),
"implementation_ready": True,
"expert_level": True,
"angular_18_features": [
"Standalone components architecture",
"New control flow syntax (@if, @for, @switch)",
"Signals for reactive programming",
"Improved hydration for SSR",
"Material 3 design system integration"
]
}
def _generate_components_from_rules(self, tagged_rules: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Generate Angular 18 components based on tagged business rules"""
components = [
{
"name": "AppComponent",
"path": "src/app/app.component.ts",
"type": "standalone",
"purpose": "Root application component with navigation shell",
"implements_rules": [],
"dependencies": ["CommonModule", "RouterOutlet", "MaterialModule"],
"template_features": ["Navigation", "Header", "Footer", "Router Outlet"]
}
]
# Analyze each tagged rule to generate specific components
for rule in tagged_rules:
rule_text = rule['rule_text'].lower()
feature_name = rule['feature_name']
requirement_name = rule['requirement_name']
# Generate components based on rule content
if any(word in rule_text for word in ['display', 'show', 'list', 'view']):
components.append({
"name": f"{feature_name.replace(' ', '')}ListComponent",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/{requirement_name.lower().replace(' ', '-')}-list.component.ts",
"type": "standalone",
"purpose": f"Display list view for: {rule['rule_text']}",
"implements_rules": [rule['rule_text']],
"dependencies": ["CommonModule", "MaterialModule", "RouterModule"],
"inputs": ["data", "loading", "error"],
"outputs": ["itemSelected", "actionTriggered"],
"template_features": ["Data table", "Filtering", "Pagination", "Search"]
})
if any(word in rule_text for word in ['create', 'add', 'new', 'form', 'input']):
components.append({
"name": f"{feature_name.replace(' ', '')}FormComponent",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/{requirement_name.lower().replace(' ', '-')}-form.component.ts",
"type": "standalone",
"purpose": f"Form component for: {rule['rule_text']}",
"implements_rules": [rule['rule_text']],
"dependencies": ["CommonModule", "ReactiveFormsModule", "MaterialModule"],
"inputs": ["initialData", "editMode"],
"outputs": ["formSubmit", "formCancel"],
"template_features": ["Reactive forms", "Validation", "Material form fields"]
})
if any(word in rule_text for word in ['edit', 'update', 'modify']):
components.append({
"name": f"{feature_name.replace(' ', '')}EditComponent",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/{requirement_name.lower().replace(' ', '-')}-edit.component.ts",
"type": "standalone",
"purpose": f"Edit component for: {rule['rule_text']}",
"implements_rules": [rule['rule_text']],
"dependencies": ["CommonModule", "ReactiveFormsModule", "MaterialModule"],
"inputs": ["itemId", "item"],
"outputs": ["updateComplete", "editCancel"],
"template_features": ["Pre-populated forms", "Validation", "Save/Cancel actions"]
})
if any(word in rule_text for word in ['approve', 'workflow', 'status', 'process']):
components.append({
"name": f"{feature_name.replace(' ', '')}WorkflowComponent",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/{requirement_name.lower().replace(' ', '-')}-workflow.component.ts",
"type": "standalone",
"purpose": f"Workflow management for: {rule['rule_text']}",
"implements_rules": [rule['rule_text']],
"dependencies": ["CommonModule", "MaterialModule", "CdkStepperModule"],
"inputs": ["workflowData", "currentStep"],
"outputs": ["stepComplete", "workflowFinish"],
"template_features": ["Stepper", "Status indicators", "Action buttons"]
})
if any(word in rule_text for word in ['calculate', 'total', 'amount', 'compute']):
components.append({
"name": f"{feature_name.replace(' ', '')}CalculatorComponent",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/{requirement_name.lower().replace(' ', '-')}-calculator.component.ts",
"type": "standalone",
"purpose": f"Calculation component for: {rule['rule_text']}",
"implements_rules": [rule['rule_text']],
"dependencies": ["CommonModule", "ReactiveFormsModule", "MaterialModule"],
"inputs": ["calculationInputs"],
"outputs": ["calculationResult", "calculationError"],
"template_features": ["Calculation inputs", "Real-time results", "Formula display"]
})
return components
def _generate_services_from_rules(self, tagged_rules: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Generate Angular 18 services based on tagged business rules"""
services = [
{
"name": "ApiService",
"path": "src/app/core/services/api.service.ts",
"purpose": "Base HTTP service for ASP.NET Core Web API communication",
"injectable": "root",
"dependencies": ["HttpClient"],
"methods": ["get", "post", "put", "delete", "patch"],
"implements_rules": []
},
{
"name": "AuthService",
"path": "src/app/core/services/auth.service.ts",
"purpose": "Authentication and authorization service",
"injectable": "root",
"dependencies": ["HttpClient", "Router"],
"methods": ["login", "logout", "isAuthenticated", "getToken", "refreshToken"],
"implements_rules": []
}
]
# Generate services based on tagged rules
processed_features = set()
for rule in tagged_rules:
rule_text = rule['rule_text'].lower()
feature_name = rule['feature_name']
requirement_name = rule['requirement_name']
# Avoid duplicate services for same feature
service_key = f"{feature_name}_{requirement_name}"
if service_key in processed_features:
continue
processed_features.add(service_key)
# Generate data service for each feature/requirement
services.append({
"name": f"{feature_name.replace(' ', '')}DataService",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/services/{requirement_name.lower().replace(' ', '-')}.service.ts",
"purpose": f"Data service for {feature_name} - {requirement_name}",
"injectable": "root",
"dependencies": ["ApiService"],
"methods": self._generate_service_methods_from_rule(rule),
"implements_rules": [rule['rule_text']],
"api_endpoints": self._generate_api_endpoints_from_rule(rule, feature_name, requirement_name)
})
# Generate specific services based on rule content
if any(word in rule_text for word in ['validate', 'check', 'verify']):
services.append({
"name": f"{feature_name.replace(' ', '')}ValidationService",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/services/{requirement_name.lower().replace(' ', '-')}-validation.service.ts",
"purpose": f"Validation service for: {rule['rule_text']}",
"injectable": "root",
"dependencies": [],
"methods": ["validate", "validateField", "getValidationErrors"],
"implements_rules": [rule['rule_text']]
})
if any(word in rule_text for word in ['calculate', 'compute', 'total']):
services.append({
"name": f"{feature_name.replace(' ', '')}CalculationService",
"path": f"src/app/features/{feature_name.lower().replace(' ', '-')}/services/{requirement_name.lower().replace(' ', '-')}-calculation.service.ts",
"purpose": f"Calculation service for: {rule['rule_text']}",
"injectable": "root",
"dependencies": [],
"methods": ["calculate", "validateInputs", "formatResult"],
"implements_rules": [rule['rule_text']]
})
return services
def _generate_routes_from_rules(self, tagged_rules: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Generate Angular routing configuration based on tagged rules"""
routes = [
{
"path": "",
"redirectTo": "/dashboard",
"pathMatch": "full"
},
{
"path": "dashboard",
"component": "DashboardComponent",
"title": "Dashboard"
}
]
# Generate routes for each feature/requirement
processed_routes = set()
for rule in tagged_rules:
feature_name = rule['feature_name']
requirement_name = rule['requirement_name']
# Create unique route path
route_path = f"{feature_name.lower().replace(' ', '-')}/{requirement_name.lower().replace(' ', '-')}"
if route_path in processed_routes:
continue
processed_routes.add(route_path)
routes.append({
"path": route_path,
"loadComponent": f"() => import('./features/{feature_name.lower().replace(' ', '-')}/{requirement_name.lower().replace(' ', '-')}.component').then(m => m.{feature_name.replace(' ', '')}{requirement_name.replace(' ', '')}Component)",
"title": f"{feature_name} - {requirement_name}",
"data": {
"breadcrumb": f"{feature_name} > {requirement_name}",
"implemented_rules": [rule['rule_text']]
}
})
return routes
def _generate_forms_from_rules(self, tagged_rules: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Generate Angular reactive forms based on tagged rules"""
forms = []
for rule in tagged_rules:
rule_text = rule['rule_text'].lower()
feature_name = rule['feature_name']
requirement_name = rule['requirement_name']
if any(word in rule_text for word in ['create', 'add', 'input', 'form', 'enter']):
forms.append({
"name": f"{feature_name.replace(' ', '')}{requirement_name.replace(' ', '')}Form",
"purpose": f"Form for: {rule['rule_text']}",
"type": "reactive",
"fields": self._extract_form_fields_from_rule(rule),
"validators": self._extract_validators_from_rule(rule),
"implements_rules": [rule['rule_text']]
})
return forms
def _generate_service_methods_from_rule(self, rule: Dict[str, Any]) -> List[str]:
"""Generate service methods based on rule content"""
methods = []
rule_text = rule['rule_text'].lower()
if any(word in rule_text for word in ['get', 'retrieve', 'fetch', 'load']):
methods.extend(["getAll", "getById", "search"])
if any(word in rule_text for word in ['create', 'add', 'new']):
methods.append("create")
if any(word in rule_text for word in ['update', 'modify', 'edit']):
methods.append("update")
if any(word in rule_text for word in ['delete', 'remove']):
methods.append("delete")
if any(word in rule_text for word in ['validate', 'check']):
methods.append("validate")
return methods if methods else ["getAll", "getById", "create", "update", "delete"]
def _generate_api_endpoints_from_rule(self, rule: Dict[str, Any], feature_name: str, requirement_name: str) -> List[str]:
"""Generate API endpoint paths based on rule"""
base_path = f"/api/{feature_name.lower().replace(' ', '-')}"
endpoints = []
rule_text = rule['rule_text'].lower()
if any(word in rule_text for word in ['get', 'list', 'retrieve']):
endpoints.extend([f"GET {base_path}", f"GET {base_path}/{{id}}"])
if any(word in rule_text for word in ['create', 'add']):
endpoints.append(f"POST {base_path}")
if any(word in rule_text for word in ['update', 'edit']):
endpoints.append(f"PUT {base_path}/{{id}}")
if any(word in rule_text for word in ['delete', 'remove']):
endpoints.append(f"DELETE {base_path}/{{id}}")
return endpoints
def _extract_form_fields_from_rule(self, rule: Dict[str, Any]) -> List[Dict[str, str]]:
"""Extract form fields from rule content"""
fields = []
rule_text = rule['rule_text'].lower()
# Common fields based on rule content
if 'name' in rule_text:
fields.append({"name": "name", "type": "text", "required": True})
if 'description' in rule_text:
fields.append({"name": "description", "type": "textarea", "required": False})
if 'email' in rule_text:
fields.append({"name": "email", "type": "email", "required": True})
if 'amount' in rule_text or 'price' in rule_text:
fields.append({"name": "amount", "type": "number", "required": True})
if 'date' in rule_text:
fields.append({"name": "date", "type": "date", "required": True})
if 'status' in rule_text:
fields.append({"name": "status", "type": "select", "required": True})
return fields if fields else [{"name": "name", "type": "text", "required": True}]
def _extract_validators_from_rule(self, rule: Dict[str, Any]) -> List[str]:
"""Extract validation requirements from rule content"""
validators = []
rule_text = rule['rule_text'].lower()
if 'required' in rule_text or 'must' in rule_text:
validators.append("Validators.required")
if 'email' in rule_text:
validators.append("Validators.email")
if 'minimum' in rule_text or 'max' in rule_text:
validators.append("Validators.min")
if 'unique' in rule_text:
validators.append("CustomValidators.unique")
return validators
def _analyze_rules_coverage(self, tagged_rules: List[Dict[str, Any]], architecture: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Analyze how well the architecture covers the tagged rules"""
coverage_analysis = []
for rule in tagged_rules:
rule_text = rule['rule_text']
coverage = {
"rule_text": rule_text,
"feature_name": rule['feature_name'],
"requirement_name": rule['requirement_name'],
"covered_by_components": [],
"covered_by_services": [],
"covered_by_routes": [],
"coverage_complete": False
}
# Check component coverage
components = architecture.get("components", [])
for component in components:
if rule_text in component.get("implements_rules", []):
coverage["covered_by_components"].append(component["name"])
# Check service coverage
services = architecture.get("services", [])
for service in services:
if rule_text in service.get("implements_rules", []):
coverage["covered_by_services"].append(service["name"])
# Determine if coverage is complete
coverage["coverage_complete"] = (
len(coverage["covered_by_components"]) > 0 or
len(coverage["covered_by_services"]) > 0
)
coverage_analysis.append(coverage)
return coverage_analysis
def _generate_basic_architecture(self, functional_requirements: Dict[str, Any]) -> Dict[str, Any]:
"""Generate basic Angular 18 architecture when no tagged rules are available"""
feature_name = functional_requirements.get('feature_name', 'Angular Application')
return {
"framework_info": {
"name": "Angular 18",
"version": "18.x",
"language": "TypeScript",
"status": "basic_architecture_no_tagged_rules"
},
"components": [
{
"name": "AppComponent",
"path": "src/app/app.component.ts",
"type": "standalone",
"purpose": "Root application component"
},
{
"name": "DashboardComponent",
"path": "src/app/features/dashboard/dashboard.component.ts",
"type": "standalone",
"purpose": "Main dashboard view"
}
],
"services": [
{
"name": "ApiService",
"path": "src/app/core/services/api.service.ts",
"purpose": "HTTP communication service"
}
],
"routing": {
"routes": [
{"path": "", "redirectTo": "/dashboard", "pathMatch": "full"},
{"path": "dashboard", "component": "DashboardComponent"}
]
},
"implementation_ready": True,
"requires_tagged_rules": True,
"tagged_rules_coverage": []
}