diff --git a/.gitignore b/.gitignore index f984dcb..8a2ebd6 100644 --- a/.gitignore +++ b/.gitignore @@ -224,6 +224,8 @@ next-env.d.ts # Database files *.db +*.db-shm +*.db-wal *.sqlite? # IDE @@ -232,6 +234,7 @@ next-env.d.ts .idea/ *.sublime-project *.sublime-workspace +*-instructions.* # logs logs diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index f42aae3..904995c 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -27,21 +27,21 @@ function DashboardContent() { const [metrics, setMetrics] = useState(null); const [company, setCompany] = useState(null); const [, setLoading] = useState(false); - const [csvUrl, setCsvUrl] = useState(""); + // Remove unused csvUrl state variable const [refreshing, setRefreshing] = useState(false); const isAdmin = session?.user?.role === "admin"; const isAuditor = session?.user?.role === "auditor"; useEffect(() => { - // Fetch metrics, company, and CSV URL on mount + // Fetch metrics and company on mount const fetchData = async () => { setLoading(true); const res = await fetch("/api/dashboard/metrics"); const data = await res.json(); setMetrics(data.metrics); setCompany(data.company); - setCsvUrl(data.csvUrl); + // Removed unused csvUrl assignment setLoading(false); }; fetchData(); @@ -51,15 +51,27 @@ function DashboardContent() { if (isAuditor) return; // Prevent auditors from refreshing try { setRefreshing(true); + + // Make sure we have a company ID to send + if (!company?.id) { + console.error("Cannot refresh: Company ID is missing"); + return; + } + const res = await fetch("/api/admin/refresh-sessions", { method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ companyId: company.id }), }); + if (res.ok) { // Refetch metrics const metricsRes = await fetch("/api/dashboard/metrics"); const data = await metricsRes.json(); setMetrics(data.metrics); + } else { + const errorData = await res.json(); + console.error("Failed to refresh sessions:", errorData.error); } } finally { setRefreshing(false); @@ -127,11 +139,11 @@ function DashboardContent() {

Sessions by Day

- +

Categories

- +
diff --git a/app/dashboard/settings.tsx b/app/dashboard/settings.tsx index a49eca7..671d629 100644 --- a/app/dashboard/settings.tsx +++ b/app/dashboard/settings.tsx @@ -42,18 +42,26 @@ export default function DashboardSettings({ return (

Company Config

-
+
{ + e.preventDefault(); + handleSave(); + }} + > setCsvUrl(e.target.value)} + autoComplete="url" /> setCsvUsername(e.target.value)} + autoComplete="username" /> setCsvPassword(e.target.value)} + autoComplete="new-password" /> setSentimentThreshold(e.target.value)} /> -
{message}
-
+
); } diff --git a/lib/csvFetcher.ts b/lib/csvFetcher.ts index b23f92e..95c9adb 100644 --- a/lib/csvFetcher.ts +++ b/lib/csvFetcher.ts @@ -87,11 +87,18 @@ export async function fetchAndParseCsv( trim: true, }); + // Helper function to safely parse dates + function safeParseDate(dateStr?: string): Date | null { + if (!dateStr) return null; + const date = new Date(dateStr); + return !isNaN(date.getTime()) ? date : null; + } + // Coerce types for relevant columns return records.map((r) => ({ id: r.session_id, - startTime: new Date(r.start_time), - endTime: r.end_time ? new Date(r.end_time) : null, + startTime: safeParseDate(r.start_time) || new Date(), // Fallback to current date if invalid + endTime: safeParseDate(r.end_time), ipAddress: r.ip_address, country: r.country, language: r.language, diff --git a/lib/metrics.ts b/lib/metrics.ts index 06b6b13..fc8bb94 100644 --- a/lib/metrics.ts +++ b/lib/metrics.ts @@ -96,5 +96,6 @@ export function sessionMetrics( totalTokens, totalTokensEur, sentimentThreshold: threshold, + lastUpdated: Date.now(), // Add current timestamp } as MetricsResult; } diff --git a/lib/types.ts b/lib/types.ts index dee713d..80a9c24 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -85,6 +85,7 @@ export interface MetricsResult { totalTokens?: number; totalTokensEur?: number; sentimentThreshold?: number | null; + lastUpdated?: number; // Timestamp for when metrics were last updated } export interface ApiResponse { diff --git a/pages/api/admin/refresh-sessions.ts b/pages/api/admin/refresh-sessions.ts index ebc9c85..9abe6a4 100644 --- a/pages/api/admin/refresh-sessions.ts +++ b/pages/api/admin/refresh-sessions.ts @@ -70,14 +70,26 @@ export default async function handler( startTime: session.startTime || new Date(), }; + // Validate dates to prevent "Invalid Date" errors + const startTime = + sessionData.startTime instanceof Date && + !isNaN(sessionData.startTime.getTime()) + ? sessionData.startTime + : new Date(); + + const endTime = + session.endTime instanceof Date && !isNaN(session.endTime.getTime()) + ? session.endTime + : new Date(); + // Only include fields that are properly typed for Prisma await prisma.session.create({ data: { id: sessionData.id, companyId: sessionData.companyId, - startTime: sessionData.startTime, - // endTime is required in the schema, so use startTime if not available - endTime: session.endTime || new Date(), + startTime: startTime, + // endTime is required in the schema, so use valid startTime if not available + endTime: endTime, ipAddress: session.ipAddress || null, country: session.country || null, language: session.language || null, diff --git a/prisma/seed.js b/prisma/seed.js index d2bda5d..36c64da 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -1,6 +1,6 @@ // seed.js - Create initial admin user and company -import { PrismaClient } from '@prisma/client'; -import bcrypt from 'bcryptjs'; +import { PrismaClient } from "@prisma/client"; +import bcrypt from "bcryptjs"; const prisma = new PrismaClient(); @@ -8,30 +8,30 @@ async function main() { // Create a company const company = await prisma.company.create({ data: { - name: 'Demo Company', - csvUrl: 'https://example.com/data.csv', // Replace with a real URL if available - } + name: "Demo Company", + csvUrl: "https://example.com/data.csv", // Replace with a real URL if available + }, }); // Create an admin user - const hashedPassword = await bcrypt.hash('admin123', 10); + const hashedPassword = await bcrypt.hash("admin123", 10); await prisma.user.create({ data: { - email: 'admin@demo.com', + email: "admin@demo.com", password: hashedPassword, - role: 'admin', - companyId: company.id - } + role: "admin", + companyId: company.id, + }, }); - console.log('Seed data created successfully:'); - console.log('Company: Demo Company'); - console.log('Admin user: admin@demo.com (password: admin123)'); + console.log("Seed data created successfully:"); + console.log("Company: Demo Company"); + console.log("Admin user: admin@demo.com (password: admin123)"); } main() - .catch(e => { - console.error('Error seeding database:', e); + .catch((e) => { + console.error("Error seeding database:", e); process.exit(1); }) .finally(async () => {