diff --git a/.github/prompts/autonomy-version.json b/.github/prompts/autonomy-version.json new file mode 100644 index 0000000..39afc29 --- /dev/null +++ b/.github/prompts/autonomy-version.json @@ -0,0 +1,3 @@ +{ + "version": "2025.11" +} diff --git a/.github/prompts/autonomy.manifest.json b/.github/prompts/autonomy.manifest.json new file mode 100644 index 0000000..25f7f88 --- /dev/null +++ b/.github/prompts/autonomy.manifest.json @@ -0,0 +1,8 @@ +{ + "version": "2025.11", + "consent": { + "phrase": "", + "expiresMinutes": 0 + }, + "actions": [] +} \ No newline at end of file diff --git a/.kilo/kilo.jsonc b/.kilo/kilo.jsonc new file mode 100644 index 0000000..0603291 --- /dev/null +++ b/.kilo/kilo.jsonc @@ -0,0 +1,4 @@ +{ + "$schema": "https://app.kilo.ai/config.json", + "snapshot": false +} \ No newline at end of file diff --git a/frontend/app/(protected)/admin/activity/page.tsx b/frontend/app/(protected)/admin/activity/page.tsx new file mode 100644 index 0000000..1022d1e --- /dev/null +++ b/frontend/app/(protected)/admin/activity/page.tsx @@ -0,0 +1,80 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card"; +import { CircleUser } from "lucide-react"; + +// Define the type for the activity data +interface Activity { + id: string; + user_email: string; + action: string; + created_at: string; +} + +async function getActivity(): Promise { + // This is a server component, so we can fetch data directly. + // In a real app, you'd fetch from your API endpoint. + // The URL should be absolute, e.g., process.env.API_URL. + // For this example, we'll use mock data. + try { + // const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/activity`); + // if (!res.ok) { + // throw new Error('Failed to fetch activity data'); + // } + // const data = await res.json(); + // return data; + + // Mock data for demonstration + const mockData: Activity[] = [ + { id: '1', user_email: 'admin@example.com', action: 'User login', created_at: new Date().toISOString() }, + { id: '2', user_email: 'test@example.com', action: 'File upload: "document.pdf"', created_at: new Date(Date.now() - 1000 * 60 * 5).toISOString() }, + { id: '3', user_email: 'admin@example.com', action: 'File delete: "image.jpg"', created_at: new Date(Date.now() - 1000 * 60 * 10).toISOString() }, + { id: '4', user_email: 'guest@example.com', action: 'Viewed page: "Dashboard"', created_at: new Date(Date.now() - 1000 * 60 * 15).toISOString() }, + ]; + return mockData; + } catch (error) { + console.error(error); + return []; + } +} + +export default async function AdminActivityPage() { + const activities = await getActivity(); + + return ( +
+

User Activity Timeline

+ + + Activity Feed + + An overview of recent user actions on the platform. + + + +
+ {/* The timeline line */} +
+ + {activities.map((activity, index) => ( +
+ {/* The timeline dot */} +
+ +
+ +

{activity.user_email}

+
+

{activity.action}

+

+ {new Date(activity.created_at).toLocaleString()} +

+
+ ))} + {activities.length === 0 && ( +

No recent activity.

+ )} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/app/(protected)/admin/audit-logs/page.tsx b/frontend/app/(protected)/admin/audit-logs/page.tsx new file mode 100644 index 0000000..6a01ce1 --- /dev/null +++ b/frontend/app/(protected)/admin/audit-logs/page.tsx @@ -0,0 +1,90 @@ +import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card"; +import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table"; + +// Define the type for the audit log data +interface AuditLog { + id: string; + ip_address: string; + timestamp: string; + method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; + path: string; + status_code: number; + user_agent: string; +} + +async function getAuditLogs(): Promise { + // This is a server component, so we can fetch data directly. + // In a real app, you'd fetch from your API endpoint. + // For this example, we'll use mock data. + try { + // const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/audit`); + // if (!res.ok) { + // throw new Error('Failed to fetch audit logs'); + // } + // const data = await res.json(); + // return data; + + // Mock data for demonstration + const mockData: AuditLog[] = [ + { id: '1', ip_address: '192.168.1.1', timestamp: new Date().toISOString(), method: 'GET', path: '/api/users', status_code: 200, user_agent: 'Mozilla/5.0' }, + { id: '2', ip_address: '10.0.0.1', timestamp: new Date(Date.now() - 1000 * 60 * 2).toISOString(), method: 'POST', path: '/api/auth/login', status_code: 200, user_agent: 'Chrome/91.0' }, + { id: '3', ip_address: '172.16.0.1', timestamp: new Date(Date.now() - 1000 * 60 * 5).toISOString(), method: 'GET', path: '/api/files/123', status_code: 404, user_agent: 'curl/7.64.1' }, + { id: '4', ip_address: '192.168.1.2', timestamp: new Date(Date.now() - 1000 * 60 * 10).toISOString(), method: 'DELETE', path: '/api/files/456', status_code: 204, user_agent: 'Mozilla/5.0' }, + ]; + return mockData; + } catch (error) { + console.error(error); + return []; + } +} + +export default async function AdminAuditLogsPage() { + const auditLogs = await getAuditLogs(); + + return ( +
+

HTTP Access Logs

+ + + Audit Logs + + A record of all HTTP requests made to the server. + + + + + + + IP Address + Timestamp + Method + Path + Status + User Agent + + + + {auditLogs.map((log) => ( + + {log.ip_address} + {new Date(log.timestamp).toLocaleString()} + {log.method} + {log.path} + {log.status_code} + {log.user_agent} + + ))} + {auditLogs.length === 0 && ( + + + No audit logs found. + + + )} + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/app/[locale]/layout.tsx b/frontend/app/[locale]/layout.tsx index e1572c9..6e25391 100644 --- a/frontend/app/[locale]/layout.tsx +++ b/frontend/app/[locale]/layout.tsx @@ -1,30 +1,3 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import { notFound } from "next/navigation"; -import { NextIntlClientProvider } from "next-intl"; -import { getMessages, setRequestLocale } from "next-intl/server"; -import { routing } from "@/i18n/routing"; -import "../globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "SMALDA", - description: "Secure land document verification and analysis", -}; - -export function generateStaticParams() { - return routing.locales.map((locale) => ({ locale })); -} - export default async function LocaleLayout({ children, params, @@ -34,26 +7,5 @@ export default async function LocaleLayout({ }>) { const { locale } = await params; - // Guard against unsupported locales reaching the layout. - if (!(routing.locales as readonly string[]).includes(locale)) { - notFound(); - } - - // Enable static rendering for the resolved locale. - setRequestLocale(locale); - - // Provide the message catalog to Client Components below. - const messages = await getMessages(); - - return ( - - - - {children} - - - - ); + return children; } diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx new file mode 100644 index 0000000..c552994 --- /dev/null +++ b/frontend/app/layout.tsx @@ -0,0 +1,55 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import { NextIntlClientProvider } from "next-intl"; +import { getMessages, setRequestLocale } from "next-intl/server"; +import { routing } from "@/i18n/routing"; +import { notFound } from "next/navigation"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "SMALDA", + description: "Secure land document verification and analysis", +}; + +export function generateStaticParams() { + return routing.locales.map((locale) => ({ locale })); +} + +export default async function RootLayout({ + children, + params, +}: Readonly<{ + children: React.ReactNode; + params: Promise<{ locale: string }>; +}>) { + const { locale } = await params; + + if (!routing.locales.includes(locale as (typeof routing.locales)[number])) { + notFound(); + } + + setRequestLocale(locale); + const messages = await getMessages(); + + return ( + + + + {children} + + + + ); +} diff --git a/frontend/components/ui/card.tsx b/frontend/components/ui/card.tsx new file mode 100644 index 0000000..0852b78 --- /dev/null +++ b/frontend/components/ui/card.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +type CardProps = React.HTMLAttributes; + +export function Card({ className, children, ...props }: CardProps) { + return ( +
+ {children} +
+ ); +} + +export function CardHeader({ className, children, ...props }: CardProps) { + return ( +
+ {children} +
+ ); +} + +export function CardTitle({ className, children, ...props }: CardProps) { + return ( +

+ {children} +

+ ); +} + +export function CardDescription({ className, children, ...props }: CardProps) { + return ( +

+ {children} +

+ ); +} + +export function CardContent({ className, children, ...props }: CardProps) { + return ( +
+ {children} +
+ ); +} diff --git a/frontend/components/ui/table.tsx b/frontend/components/ui/table.tsx new file mode 100644 index 0000000..97a1749 --- /dev/null +++ b/frontend/components/ui/table.tsx @@ -0,0 +1,51 @@ +import React from "react"; + +type TableProps = React.HTMLAttributes; + +export function Table({ className, children, ...props }: TableProps) { + return ( + + {children} +
+ ); +} + +export function TableHeader({ className, children, ...props }: React.HTMLAttributes) { + return ( + + {children} + + ); +} + +export function TableRow({ className, children, ...props }: React.HTMLAttributes) { + return ( + + {children} + + ); +} + +export function TableHead({ className, children, ...props }: React.ThHTMLAttributes) { + return ( + + {children} + + ); +} + +export function TableBody({ className, children, ...props }: React.HTMLAttributes) { + return ( + + {children} + + ); +} + +export function TableCell({ className, children, ...props }: React.TdHTMLAttributes) { + return ( + + {children} + + ); +} diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 0c05faf..4491209 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -4,7 +4,6 @@ import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin("./i18n/request.ts"); const nextConfig: NextConfig = { - /* config options here */ webpack: (config) => { config.resolve.alias = { ...config.resolve.alias, diff --git a/frontend/next.config.ts.bak b/frontend/next.config.ts.bak new file mode 100644 index 0000000..0c05faf --- /dev/null +++ b/frontend/next.config.ts.bak @@ -0,0 +1,17 @@ +import type { NextConfig } from "next"; +import createNextIntlPlugin from "next-intl/plugin"; + +const withNextIntl = createNextIntlPlugin("./i18n/request.ts"); + +const nextConfig: NextConfig = { + /* config options here */ + webpack: (config) => { + config.resolve.alias = { + ...config.resolve.alias, + canvas: false, + }; + return config; + }, +}; + +export default withNextIntl(nextConfig); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b1bc887..7bd61f5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,8 +27,9 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4", - "@testing-library/jest-dom": "^6.1.4", + "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.0.0", + "@types/jest": "^30.0.0", "@types/leaflet": "^1.9.20", "@types/node": "^20", "@types/react": "^19.1.12", @@ -1556,6 +1557,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -1617,6 +1628,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/globals": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", @@ -1633,6 +1654,30 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern/node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/reporters": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", @@ -3172,6 +3217,232 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jest/node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@types/jest/node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@types/jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", @@ -9023,17 +9294,6 @@ } } }, - "node_modules/next-intl/node_modules/@swc/helpers": { - "version": "0.5.23", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", - "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -9802,6 +10062,22 @@ "license": "MIT", "peer": true }, + "node_modules/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "dev": true, + "license": "MIT" + }, "node_modules/react-leaflet": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index fab7322..b57d238 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,8 +31,9 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4", - "@testing-library/jest-dom": "^6.1.4", + "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.0.0", + "@types/jest": "^30.0.0", "@types/leaflet": "^1.9.20", "@types/node": "^20", "@types/react": "^19.1.12", diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index d8b9323..5044554 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,27 +1,40 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + "target": "ES2017", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/frontend/types/lucide-react.d.ts b/frontend/types/lucide-react.d.ts new file mode 100644 index 0000000..b5ffa53 --- /dev/null +++ b/frontend/types/lucide-react.d.ts @@ -0,0 +1,16 @@ +declare module "lucide-react" { + import { FC, SVGProps } from "react"; + + type LucideIcon = FC>; + + export const CircleUser: LucideIcon; + export const AlertTriangle: LucideIcon; + export const CheckCircle2: LucideIcon; + export const IdCard: LucideIcon; + export const Landmark: LucideIcon; + export const RefreshCw: LucideIcon; + export const XCircle: LucideIcon; + export const Building2: LucideIcon; + + export type { LucideIcon }; +} diff --git a/frontend/types/test-setup.d.ts b/frontend/types/test-setup.d.ts new file mode 100644 index 0000000..d0de870 --- /dev/null +++ b/frontend/types/test-setup.d.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom"; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index ba7937e..0000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "SMALDA", - "lockfileVersion": 3, - "requires": true, - "packages": {} -}