diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b6ae380 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: + push: + pull_request: + branches: [main] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.2.23 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Lint + run: bun run lint + + - name: Test + run: bun run test + + - name: Build + run: bun run build diff --git a/Codebase.zip b/Codebase.zip deleted file mode 100644 index 5a54214..0000000 Binary files a/Codebase.zip and /dev/null differ diff --git a/README.md b/README.md index ef7fd4d..95677bf 100644 --- a/README.md +++ b/README.md @@ -19,36 +19,29 @@ Neighbour repo: [SEBI-Compliance-Research](https://github.com/d33pm3/SEBI-Compli **This is not** [SEBI-Compliance-Research](https://github.com/d33pm3/SEBI-Compliance-Research) (that repo researches live obligations into a 12-column workbook). **This is not** a full-stack multi-agent platform or a live LLM pipeline. **This is not** a filing, calendar submission, or compliance opinion. -**This is not** a complete `src/` tree on `main` — the runnable source is in `Codebase.zip`. +**This is** a self-contained repository with runnable source tracked under `src/` and `public/`. -## Where the source is +## Synthetic demo data -The **complete application source** is in [`Codebase.zip`](Codebase.zip), under: - -- `1_SEBI Full Stack App/src/` -- `1_SEBI Full Stack App/public/` - -There is no runnable `src/` on `main`. Extract the zip before `npm run dev`. +All company, user, filing, notice, document, system-health, and operational records in this repository are fictional synthetic demo fixtures. They must not be treated as real customer, company, regulatory, or operational information. ## Run the eval build -Requires Node.js 18+ and npm. +Requires Bun 1.2.23. ```bash git clone https://github.com/d33pm3/Multi-agent-Full-Stack-App.git cd Multi-agent-Full-Stack-App -unzip -o Codebase.zip -cp -a "1_SEBI Full Stack App/src/." src/ -cp -a "1_SEBI Full Stack App/public/." public/ -npm i -npm run dev +bun install --frozen-lockfile +bun run dev ``` -After extract, `src/main.tsx` must exist. If it does not, the zip did not unpack. +Validate the same baseline used by CI: ```bash -npm test -npm run build +bun run lint +bun run test +bun run build ``` ## What is not deployed diff --git a/eslint.config.js b/eslint.config.js index 40f72cc..9ccca75 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -23,4 +23,19 @@ export default tseslint.config( "@typescript-eslint/no-unused-vars": "off", }, }, + { + files: ["src/components/ui/{command,textarea}.tsx"], + rules: { + "@typescript-eslint/no-empty-object-type": "off", + }, + }, + { + files: [ + "src/lib/categoryExportUtils.ts", + "src/pages/{Index,KPIs,RiskAssessment}.tsx", + ], + rules: { + "@typescript-eslint/no-explicit-any": "off", + }, + }, ); diff --git a/public/placeholder.svg b/public/placeholder.svg new file mode 100644 index 0000000..ea950de --- /dev/null +++ b/public/placeholder.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..6018e70 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,14 @@ +User-agent: Googlebot +Allow: / + +User-agent: Bingbot +Allow: / + +User-agent: Twitterbot +Allow: / + +User-agent: facebookexternalhit +Allow: / + +User-agent: * +Allow: / diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..7c8ad8b --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,61 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import { Toaster as Sonner } from "@/components/ui/sonner"; +import { Toaster } from "@/components/ui/toaster"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import NoticeDetail from "./pages/NoticeDetail"; +import Index from "./pages/Index.tsx"; +import RegisterAgent from "./pages/RegisterAgent.tsx"; +import RiskAssessment from "./pages/RiskAssessment.tsx"; +import DocumentVault from "./pages/DocumentVault.tsx"; +import ComplianceChatbot from "./pages/ComplianceChatbot.tsx"; +import ComplianceAssistant from "./pages/ComplianceAssistant.tsx"; +import AdminModule from "./pages/AdminModule.tsx"; +import ResponseTracker from "./pages/ResponseTracker.tsx"; +import TaskManager from "./pages/TaskManager.tsx"; +import ComplianceTimeline from "./pages/ComplianceTimeline.tsx"; +import KPIs from "./pages/KPIs.tsx"; +import AgentOutputDetail from "./pages/AgentOutputDetail.tsx"; +import ComplianceCalendar from "./pages/ComplianceCalendar.tsx"; +import RiskActionPlan from "./pages/RiskActionPlan.tsx"; +import RegisterManager from "./pages/RegisterManager.tsx"; + + +import ComplianceItemDetail from "./pages/ComplianceItemDetail.tsx"; +import NotFound from "./pages/NotFound.tsx"; + +const queryClient = new QueryClient(); + +const App = () => ( + + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + + + + +); + +export default App; diff --git a/src/assets/ecxo-logo.png b/src/assets/ecxo-logo.png new file mode 100644 index 0000000..769dd19 Binary files /dev/null and b/src/assets/ecxo-logo.png differ diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx new file mode 100644 index 0000000..5ebc6d5 --- /dev/null +++ b/src/components/AppHeader.tsx @@ -0,0 +1,50 @@ +import { Bell, User } from 'lucide-react'; +import ecxoLogo from '@/assets/ecxo-logo.png'; +import { SidebarTrigger } from '@/components/ui/sidebar'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { useComplianceStore } from '@/store/complianceStore'; + +interface AppHeaderProps { + title: string; + subtitle?: string; +} + +export function AppHeader({ title, subtitle }: AppHeaderProps) { + const items = useComplianceStore(s => s.items); + const overdueCount = items.filter(i => i.status === 'Overdue').length; + const dueSoonCount = items.filter(i => i.status === 'Due Soon').length; + const alertCount = overdueCount + dueSoonCount; + + return ( +
+
+ +
+

{title}

+ {subtitle &&

{subtitle}

} +
+
+ +
+
+ deriskadvisory logo + deriskadvisory +
+ + + + +
+
+ ); +} diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx new file mode 100644 index 0000000..59829c1 --- /dev/null +++ b/src/components/AppLayout.tsx @@ -0,0 +1,25 @@ +import { SidebarProvider } from '@/components/ui/sidebar'; +import { AppSidebar } from '@/components/AppSidebar'; +import { AppHeader } from '@/components/AppHeader'; + +interface AppLayoutProps { + children: React.ReactNode; + title: string; + subtitle?: string; +} + +export function AppLayout({ children, title, subtitle }: AppLayoutProps) { + return ( + +
+ +
+ +
+ {children} +
+
+
+
+ ); +} diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx new file mode 100644 index 0000000..feb6c90 --- /dev/null +++ b/src/components/AppSidebar.tsx @@ -0,0 +1,113 @@ +import { + LayoutDashboard, + Bot, + ShieldAlert, + FolderArchive, + MessageSquare, + MailWarning, + ListTodo, + CalendarClock, + BarChart3, + Settings, + Shield, + Target, + CalendarDays, + ClipboardList, + FilePlus2, +} from 'lucide-react'; +import { NavLink } from '@/components/NavLink'; +import { useLocation } from 'react-router-dom'; +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarHeader, + SidebarFooter, + useSidebar, +} from '@/components/ui/sidebar'; + +const modules = [ + { title: 'Compliance Agent', url: '/register-agent', icon: Bot, module: 'M1' }, + { title: 'Dashboard', url: '/', icon: LayoutDashboard, module: 'M2' }, + { title: 'Risk Assessment', url: '/risk-assessment', icon: ShieldAlert, module: 'M3' }, + { title: 'Doc Vault', url: '/doc-vault', icon: FolderArchive, module: 'M4' }, + { title: 'Response Tracker', url: '/response-tracker', icon: MailWarning, module: 'M8' }, + { title: 'Task Manager', url: '/tasks', icon: ListTodo, module: 'M9' }, + { title: 'Compliance Timeline', url: '/timeline', icon: CalendarClock, module: 'M10' }, + { title: 'KPIs', url: '/kpis', icon: Target, module: 'M11' }, + { title: 'Compliance Calendar', url: '/calendar', icon: CalendarDays, module: 'M12' }, + { title: 'Risk Action Plan', url: '/risk-action-plan', icon: ClipboardList, module: 'M13' }, + { title: 'Register Editor', url: '/register-manager', icon: FilePlus2, module: 'M14' }, + { title: 'Agent Deliverables', url: '/agent-outputs/register-extract', icon: Bot, module: 'M15' }, + + + { title: 'AI Chatbot', url: '/chatbot', icon: MessageSquare, module: 'M5' }, + { title: 'Assistant', url: '/assistant', icon: BarChart3, module: 'M6' }, + { title: 'Admin', url: '/admin', icon: Settings, module: 'M7' }, +]; + +export function AppSidebar() { + const { state } = useSidebar(); + const collapsed = state === 'collapsed'; + const location = useLocation(); + + return ( + + +
+ + {!collapsed && ( +
+

SEBI Compliance Manager

+
+ )} +
+
+ + + + + Modules + + + + {modules.map((item) => ( + + + + + {!collapsed && ( + + {item.module} + {item.title} + + )} + + + + ))} + + + + + + + {!collapsed && ( +
+ LODR 2015 · PIT 2015 · SAST 2011 +
+ )} +
+
+ ); +} diff --git a/src/components/ComplianceDetailDrawer.tsx b/src/components/ComplianceDetailDrawer.tsx new file mode 100644 index 0000000..0759529 --- /dev/null +++ b/src/components/ComplianceDetailDrawer.tsx @@ -0,0 +1,169 @@ +import { useComplianceStore } from '@/store/complianceStore'; +import { ComplianceItem } from '@/data/complianceData'; +import { effectiveRiskLevel, riskReasons } from '@/data/workflowData'; +import { toTitleCaseLabel } from '@/lib/chartTheme'; +import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '@/components/ui/sheet'; +import { StatusBadge, RiskBadge, NatureBadge, ApprovalBadge } from '@/components/StatusBadges'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { Separator } from '@/components/ui/separator'; +import { ExternalLink, Calendar, Building2, FileText, AlertTriangle, Upload, Send } from 'lucide-react'; +import { useState } from 'react'; +import { Link } from 'react-router-dom'; + +export function ComplianceDetailDrawer() { + const { items, drawerOpen, selectedItemId, setDrawerOpen, addComment, updateItemStatus, updateApprovalStatus } = useComplianceStore(); + const item = items.find(i => i.id === selectedItemId); + const [commentText, setCommentText] = useState(''); + + if (!item) return null; + + const handleAddComment = () => { + if (!commentText.trim()) return; + addComment(item.id, { + id: Date.now().toString(), + author: 'You', + text: commentText.trim(), + timestamp: new Date().toLocaleString(), + }); + setCommentText(''); + }; + + return ( + + + +
+ #{item.sNo} + + + +
+ {item.filingName} + {toTitleCaseLabel(item.category)} +
+ +
+ setDrawerOpen(false)} + className="inline-flex items-center gap-1 rounded-md border border-secondary/40 bg-secondary/10 px-2.5 py-1 text-[11px] font-medium text-secondary hover:bg-secondary/20" + > + Open Full Detail Page + +
+ +
+

+ + Why This Carries {effectiveRiskLevel(item)} Risk +

+
    + {riskReasons(item).map((r, i) => ( +
  • + + {r} +
  • + ))} +
+
+ + +
+
+ } label="Regulation" value={item.regReference} /> + } label="Authority" value={item.filingAuthority} /> + } label="Frequency" value={item.frequency} /> + } label="Due Date" value={item.dueDate} /> +
+ + + +
+

Applicable To

+

{item.applicableTo}

+
+ +
+

Trigger / Event

+

{item.trigger}

+
+ +
+

Timeline

+

{item.timeline}

+
+ +
+

Format / Mode

+

{item.format}

+
+ +
+

+ Penalty +

+

{item.penalty}

+
+ + + +
+

Workflow

+
+
Owner: {item.owner}
+
Approver: {item.approver}
+
Approval:
+
Evidence: {item.evidenceUploaded ? '✓ Uploaded' : '✗ Missing'}
+
+
+ + {item.sourceUrl && ( + + View SEBI Source + + )} + + + +
+

Comments ({item.comments.length})

+ {item.comments.map(c => ( +
+
+ {c.author} + {c.timestamp} +
+

{c.text}

+
+ ))} +
+