diff --git a/portal/Dockerfile b/portal/Dockerfile deleted file mode 100644 index e977b85..0000000 --- a/portal/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:20-alpine - -WORKDIR /app - -COPY package*.json ./ -RUN npm install - -COPY . . - -EXPOSE 3000 - -CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] diff --git a/portal/index.html b/portal/index.html deleted file mode 100644 index 3478b05..0000000 --- a/portal/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - JIRA AI Fixer - Portal - - -
- - - diff --git a/portal/package.json b/portal/package.json deleted file mode 100644 index f197cb8..0000000 --- a/portal/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "jira-ai-fixer-portal", - "version": "0.1.0", - "private": true, - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.21.0", - "@tanstack/react-query": "^5.17.0", - "axios": "^1.6.0", - "lucide-react": "^0.303.0", - "clsx": "^2.1.0" - }, - "devDependencies": { - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.0", - "@vitejs/plugin-react": "^4.2.0", - "autoprefixer": "^10.4.0", - "postcss": "^8.4.0", - "tailwindcss": "^3.4.0", - "typescript": "^5.3.0", - "vite": "^5.0.0" - } -} diff --git a/portal/postcss.config.js b/portal/postcss.config.js deleted file mode 100644 index 2e7af2b..0000000 --- a/portal/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/portal/src/App.tsx b/portal/src/App.tsx deleted file mode 100644 index 225fc2c..0000000 --- a/portal/src/App.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Routes, Route } from 'react-router-dom' -import Layout from './components/Layout' -import Dashboard from './pages/Dashboard' -import Issues from './pages/Issues' -import Repositories from './pages/Repositories' -import Modules from './pages/Modules' -import Settings from './pages/Settings' - -function App() { - return ( - - - } /> - } /> - } /> - } /> - } /> - - - ) -} - -export default App diff --git a/portal/src/components/Layout.tsx b/portal/src/components/Layout.tsx deleted file mode 100644 index ce4d554..0000000 --- a/portal/src/components/Layout.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Link, useLocation } from 'react-router-dom' -import { - LayoutDashboard, - AlertCircle, - GitBranch, - Brain, - Settings, - Cpu -} from 'lucide-react' -import { clsx } from 'clsx' - -const navigation = [ - { name: 'Dashboard', href: '/', icon: LayoutDashboard }, - { name: 'Issues', href: '/issues', icon: AlertCircle }, - { name: 'Repositórios', href: '/repositories', icon: GitBranch }, - { name: 'Módulos', href: '/modules', icon: Brain }, - { name: 'Configurações', href: '/settings', icon: Settings }, -] - -interface LayoutProps { - children: React.ReactNode -} - -export default function Layout({ children }: LayoutProps) { - const location = useLocation() - - return ( -
- {/* Sidebar */} -
-
- -
-

JIRA AI Fixer

-

v0.1.0

-
-
- - -
- - {/* Main content */} -
- {/* Header */} -
-
-

- {navigation.find(n => n.href === location.pathname)?.name || 'JIRA AI Fixer'} -

-
- - Ambiente: Desenvolvimento - -
-
-
- - {/* Page content */} -
- {children} -
-
-
- ) -} diff --git a/portal/src/index.css b/portal/src/index.css deleted file mode 100644 index c2f490f..0000000 --- a/portal/src/index.css +++ /dev/null @@ -1,7 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -body { - @apply bg-gray-50 text-gray-900; -} diff --git a/portal/src/main.tsx b/portal/src/main.tsx deleted file mode 100644 index f630e51..0000000 --- a/portal/src/main.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { BrowserRouter } from 'react-router-dom' -import App from './App' -import './index.css' - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 1000 * 60 * 5, // 5 minutes - retry: 1, - }, - }, -}) - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - - - , -) diff --git a/portal/src/pages/Dashboard.tsx b/portal/src/pages/Dashboard.tsx deleted file mode 100644 index 7c5202a..0000000 --- a/portal/src/pages/Dashboard.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { - CheckCircle, - Clock, - AlertTriangle, - XCircle, - TrendingUp, - Activity -} from 'lucide-react' -import { api } from '../lib/api' - -interface StatsCardProps { - title: string - value: string | number - icon: React.ReactNode - trend?: string - color: string -} - -function StatsCard({ title, value, icon, trend, color }: StatsCardProps) { - return ( -
-
-
-

{title}

-

{value}

- {trend && ( -

- - {trend} -

- )} -
-
- {icon} -
-
-
- ) -} - -export default function Dashboard() { - const { data: stats, isLoading } = useQuery({ - queryKey: ['stats'], - queryFn: () => api.get('/api/issues/stats/summary').then(r => r.data), - }) - - if (isLoading) { - return ( -
- -
- ) - } - - return ( -
- {/* Stats Grid */} -
- } - color="bg-blue-500" - /> - } - color="bg-yellow-500" - /> - } - color="bg-green-500" - trend={stats?.success_rate ? `${(stats.success_rate * 100).toFixed(0)}% sucesso` : undefined} - /> - } - color="bg-red-500" - /> -
- - {/* Recent Activity */} -
-
-

Atividade Recente

-
-
-
- -

Nenhuma atividade recente

-

- Configure os webhooks do JIRA para começar a receber issues -

-
-
-
- - {/* Quick Actions */} -
-
-

Ações Rápidas

-
-
- - - -
-
-
- ) -} diff --git a/portal/src/pages/Issues.tsx b/portal/src/pages/Issues.tsx deleted file mode 100644 index 225f09d..0000000 --- a/portal/src/pages/Issues.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { useState } from 'react' -import { - Search, - Filter, - RefreshCw, - ExternalLink, - CheckCircle, - Clock, - XCircle, - Activity -} from 'lucide-react' -import { api } from '../lib/api' -import { clsx } from 'clsx' - -type IssueStatus = 'pending' | 'analyzing' | 'analyzed' | 'fix_generated' | 'pr_created' | 'accepted' | 'rejected' | 'failed' - -interface Issue { - id: string - jira_key: string - title: string - status: IssueStatus - module?: string - confidence?: number - created_at: string - pr_url?: string -} - -const statusConfig: Record = { - pending: { label: 'Pendente', color: 'bg-gray-100 text-gray-800', icon: }, - analyzing: { label: 'Analisando', color: 'bg-blue-100 text-blue-800', icon: }, - analyzed: { label: 'Analisado', color: 'bg-purple-100 text-purple-800', icon: }, - fix_generated: { label: 'Fix Gerado', color: 'bg-indigo-100 text-indigo-800', icon: }, - pr_created: { label: 'PR Criado', color: 'bg-cyan-100 text-cyan-800', icon: }, - accepted: { label: 'Aceito', color: 'bg-green-100 text-green-800', icon: }, - rejected: { label: 'Rejeitado', color: 'bg-red-100 text-red-800', icon: }, - failed: { label: 'Falhou', color: 'bg-red-100 text-red-800', icon: }, -} - -export default function Issues() { - const [search, setSearch] = useState('') - const [statusFilter, setStatusFilter] = useState('') - - const { data, isLoading, refetch } = useQuery({ - queryKey: ['issues', statusFilter], - queryFn: () => api.get('/api/issues', { - params: { status: statusFilter || undefined } - }).then(r => r.data), - }) - - const issues: Issue[] = data?.items || [] - - const filteredIssues = issues.filter(issue => - issue.jira_key.toLowerCase().includes(search.toLowerCase()) || - issue.title.toLowerCase().includes(search.toLowerCase()) - ) - - return ( -
- {/* Filters */} -
-
-
- - setSearch(e.target.value)} - className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
- -
- - -
- - -
-
- - {/* Issues Table */} -
- {isLoading ? ( -
- -
- ) : filteredIssues.length === 0 ? ( -
- -

Nenhuma issue encontrada

-

- Issues do JIRA aparecerão aqui após configurar os webhooks -

-
- ) : ( - - - - - - - - - - - - - {filteredIssues.map(issue => { - const status = statusConfig[issue.status] - return ( - - - - - - - - - ) - })} - -
JIRA KeyTítuloMóduloStatusConfiançaAções
- - {issue.jira_key} - - - {issue.title} - - {issue.module || '-'} - - - {status.icon} - {status.label} - - - {issue.confidence ? ( -
-
-
-
- - {(issue.confidence * 100).toFixed(0)}% - -
- ) : '-'} -
-
- - {issue.pr_url && ( - - PR - - )} -
-
- )} -
-
- ) -} diff --git a/portal/src/pages/Modules.tsx b/portal/src/pages/Modules.tsx deleted file mode 100644 index b4e402b..0000000 --- a/portal/src/pages/Modules.tsx +++ /dev/null @@ -1,291 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' -import { useState } from 'react' -import { - Plus, - Edit2, - Trash2, - Brain, - Save, - X, - Activity, - BookOpen -} from 'lucide-react' -import { api } from '../lib/api' - -interface Module { - name: string - description?: string - program_patterns: string[] - keywords: string[] - rules: string[] - restrictions: string[] -} - -export default function Modules() { - const queryClient = useQueryClient() - const [editingModule, setEditingModule] = useState(null) - const [isNew, setIsNew] = useState(false) - - const { data: modules, isLoading } = useQuery({ - queryKey: ['modules'], - queryFn: () => api.get('/api/config/modules').then(r => r.data), - }) - - const saveMutation = useMutation({ - mutationFn: (module: Module) => - isNew - ? api.post('/api/config/modules', module) - : api.put(`/api/config/modules/${module.name}`, module), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['modules'] }) - setEditingModule(null) - setIsNew(false) - }, - }) - - const deleteMutation = useMutation({ - mutationFn: (name: string) => api.delete(`/api/config/modules/${name}`), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['modules'] }) - }, - }) - - const modulesList: Module[] = modules || [] - - const handleNewModule = () => { - setEditingModule({ - name: '', - description: '', - program_patterns: [], - keywords: [], - rules: [], - restrictions: [], - }) - setIsNew(true) - } - - return ( -
- {/* Header */} -
-
-

- Módulos de regras de negócio para contextualização da IA -

-
- -
- - {/* Modules List */} - {isLoading ? ( -
- -
- ) : modulesList.length === 0 && !editingModule ? ( -
- -

Nenhum módulo configurado

-

- Módulos definem regras de negócio específicas que a IA usa para entender o contexto do sistema -

- -
- ) : ( -
- {modulesList.map(module => ( -
-
-
- -
-

{module.name}

-

{module.description || 'Sem descrição'}

-
-
-
- - -
-
- -
-
- Padrões de programa: -
- {module.program_patterns.map(p => ( - - {p} - - ))} - {module.program_patterns.length === 0 && -} -
-
-
- Keywords: -
- {module.keywords.slice(0, 5).map(k => ( - - {k} - - ))} - {module.keywords.length > 5 && ( - +{module.keywords.length - 5} - )} - {module.keywords.length === 0 && -} -
-
-
-
- ))} -
- )} - - {/* Edit/Create Modal */} - {editingModule && ( -
-
-
-

- {isNew ? 'Novo Módulo' : `Editar: ${editingModule.name}`} -

- -
- -
-
- - setEditingModule({ ...editingModule, name: e.target.value })} - disabled={!isNew} - placeholder="ACQ-Auth" - className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100" - /> -
- -
- -