tenantpilot/app/(app)/search/page.tsx
2025-12-05 22:06:22 +01:00

149 lines
4.6 KiB
TypeScript

'use client';
import { useState, useTransition, useCallback } from 'react';
import { SearchInput } from '@/components/search/SearchInput';
import { ResultsTable } from '@/components/search/ResultsTable';
import { EmptyState } from '@/components/search/EmptyState';
import {
searchPolicySettings,
seedMyTenantData,
type PolicySettingSearchResult,
} from '@/lib/actions/policySettings';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { toast } from 'sonner';
import { Database } from 'lucide-react';
import { useRouter } from 'next/navigation';
export default function SearchPage() {
const router = useRouter();
const [results, setResults] = useState<PolicySettingSearchResult[]>([]);
const [searchTerm, setSearchTerm] = useState('');
const [hasSearched, setHasSearched] = useState(false);
const [isPending, startTransition] = useTransition();
const [isSeeding, startSeedTransition] = useTransition();
const handleSearch = useCallback((query: string) => {
setSearchTerm(query);
if (query.length === 0) {
setResults([]);
setHasSearched(false);
return;
}
if (query.length < 2) {
return;
}
startTransition(async () => {
try {
const result = await searchPolicySettings(query);
if (result.success) {
setResults(result.data ?? []);
setHasSearched(true);
} else {
toast.error(result.error ?? 'Search failed');
setResults([]);
setHasSearched(true);
}
} catch (error) {
toast.error('An unexpected error occurred');
setResults([]);
setHasSearched(true);
}
});
}, []);
const handleSeedData = () => {
startSeedTransition(async () => {
try {
const result = await seedMyTenantData();
if (result.success) {
toast.success(result.message ?? 'Test data created successfully');
router.refresh();
} else {
toast.error(result.error ?? 'Failed to seed data');
}
} catch (error) {
toast.error('An unexpected error occurred');
}
});
};
return (
<main className="flex flex-1 flex-col gap-4 p-4 md:gap-8 md:p-8">
<div className="mx-auto w-full max-w-6xl">
<Card>
<CardHeader>
<CardTitle>Global Policy Search</CardTitle>
<CardDescription>
Search across all your Intune policy settings by keyword
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex flex-col gap-6">
<SearchInput onSearch={handleSearch} isSearching={isPending} />
{isPending && (
<div className="flex items-center justify-center py-8">
<div className="flex items-center gap-2">
<div className="h-5 w-5 animate-spin rounded-full border-2 border-primary border-t-transparent" />
<span className="text-sm text-muted-foreground">
Searching...
</span>
</div>
</div>
)}
{!isPending && hasSearched && (
<>
{results.length > 0 ? (
<div className="space-y-4">
<p className="text-sm text-muted-foreground">
Found {results.length} result{results.length !== 1 ? 's' : ''}
</p>
<ResultsTable results={results} />
</div>
) : (
<EmptyState searchTerm={searchTerm} />
)}
</>
)}
{!hasSearched && !isPending && (
<EmptyState />
)}
</div>
</CardContent>
</Card>
{/* Seed Data Button - Development Helper */}
<div className="mt-4 flex justify-end">
<Button
onClick={handleSeedData}
disabled={isSeeding}
variant="outline"
size="sm"
className="gap-2"
>
{isSeeding ? (
<>
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
Seeding...
</>
) : (
<>
<Database className="h-4 w-4" />
Seed My Data
</>
)}
</Button>
</div>
</div>
</main>
);
}