"use client";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import { Suspense, use, useState } from "react";
import {
AlertTriangle,
Check,
CheckCheck,
ChevronDown,
ChevronRight,
Loader2,
Plus,
} from "lucide-react";
import { CompanyPillLink } from "@/components/ui/company-pill";
import { CopyableEmail } from "@/components/ui/copyable-email";
import { CompanyStatusBadge, SeverityBadge } from "@/components/ui/badge";
import { NotificationChannelBox } from "@/components/ui/notification-channel-box";
import { Select } from "@/components/ui/select";
import { useAlerts, useMarkAlertRead, useResolveAlert } from "@/hooks/use-alerts";
import { useSystemStatus } from "@/hooks/use-auth";
import {
useCompanies,
useCompany,
useDeleteCompany,
useSetCompanyPaused,
useUpdateMonitorConfiguration,
} from "@/hooks/use-companies";
import { useNotificationDestinations } from "@/hooks/use-notification-destinations";
import {
useCreateSource,
useDeleteSource,
useSources,
useTestSource,
useUpdateSource,
} from "@/hooks/use-sources";
import { useCompanyRuns, useRunCompanyNow } from "@/hooks/use-monitoring-runs";
import { useCompanyReports, useGenerateReport } from "@/hooks/use-reports";
import { useSnapshots } from "@/hooks/use-snapshots";
import type { MonitoringFrequency } from "@/lib/types";
import {
FREQUENCY_LABELS,
MONITORING_FREQUENCIES,
SEVERITY_LABELS,
SEVERITY_LEVELS,
} from "@/lib/types";
import {
companyNameFromUrl,
formatDateTime,
formatEnrichmentReason,
formatMoney,
formatRelative,
summarizeEnrichmentError,
} from "@/lib/format";
const TABS = [
"Overview",
"Enrichment",
"Latest report",
"Alerts",
"Sources",
"Monitoring history",
"Snapshots",
"Configuration",
] as const;
type Tab = (typeof TABS)[number];
const TAB_DESCRIPTIONS: Record = {
Overview: "A snapshot of this company — description, competitors, key details, and its monitoring schedule.",
Enrichment: "Rich company data — funding, leadership, products, and customers.",
"Latest report":
"The most recent AI-generated competitive intelligence report, grounded in everything collected so far.",
Alerts:
"Notable changes detected for this company, ranked by severity, with actions to mark them read or resolved.",
Sources: "Every data source being monitored for this company, and how often each one is checked.",
"Monitoring history":
"A log of every monitoring run for this company — what it collected and whether it succeeded.",
Snapshots:
"The raw content collected from each source on each run, so you can see exactly what was captured.",
Configuration: "Scheduling, severity threshold, and notification settings for this company.",
};
function charCount(text: string | null): string {
return `${(text ?? "").length.toLocaleString()} chars`;
}
const RUN_STATUS_CLASSES: Record = {
queued: "bg-slate-100 text-slate-700",
running: "bg-blue-100 text-blue-700",
successful: "bg-green-100 text-green-700",
partial: "bg-amber-100 text-amber-700",
failed: "bg-red-100 text-red-700",
};
const ERROR_TONE_CLASSES = {
amber: {
box: "border-amber-200 bg-amber-50 text-amber-800",
button: "border-amber-300 text-amber-800 hover:bg-amber-100",
},
red: {
box: "border-red-200 bg-red-50 text-red-800",
button: "border-red-300 text-red-800 hover:bg-red-100",
},
} as const;
function EnrichmentErrorSummary({
tone,
summary,
errors,
}: {
tone: keyof typeof ERROR_TONE_CLASSES;
summary: string;
errors: Record;
}) {
const [expanded, setExpanded] = useState(false);
const sections = Object.entries(errors);
const classes = ERROR_TONE_CLASSES[tone];
return (
{!Array.isArray(company.enrichment.data.competitors) ||
(company.enrichment.data.competitors as unknown[]).length === 0 ? (
None found.
) : (
{(
company.enrichment.data.competitors as Record[]
).map((c, i) => {
const name = companyNameFromUrl(String(c.name ?? ""));
if (!name) return null;
return (
);
})}
)}
{company.enrichment.credits_spent !== null && (
{company.enrichment.credits_spent} NinjaPear Credits spent fetching this data
{company.enrichment.fetched_at
? ` on ${formatDateTime(company.enrichment.fetched_at)}`
: ""}
.
No monitoring run has collected any evidence yet, so a report generated now will
come back mostly empty by design — this app never fabricates findings without real
evidence. Click "Run now" above first, then generate the report.
Website, GitHub, SEC EDGAR, and careers-page sources are found automatically the
first time this company runs. Add any other public URL or RSS feed here to
monitor it too.
{sourcesLoading ? (
Loading…
) : !sources || sources.length === 0 ? (
{runs && runs.length > 0
? "No sources configured yet."
: "Website, GitHub, and other sources are discovered automatically on the " +
'first monitoring run — click "Run now" above to get started, or add a ' +
"custom URL yourself above."}
{runs && runs.length > 0
? "No snapshots recorded yet."
: 'No snapshots yet — snapshots are captured during monitoring runs. Click "Run now" above to get started.'}