- Found {results.length} result{results.length !== 1 ? 's' : ''} -
-diff --git a/app/(app)/search/PolicyExplorerClient.tsx b/app/(app)/search/PolicyExplorerClient.tsx
new file mode 100644
index 0000000..afffef9
--- /dev/null
+++ b/app/(app)/search/PolicyExplorerClient.tsx
@@ -0,0 +1,37 @@
+'use client';
+
+import { useState } from 'react';
+import { PolicySearchContainer } from '@/components/policy-explorer/PolicySearchContainer';
+import { PolicyDetailSheet } from '@/components/policy-explorer/PolicyDetailSheet';
+import type { PolicySettingSearchResult } from '@/lib/actions/policySettings';
+
+interface PolicyExplorerClientProps {
+ initialPolicies: PolicySettingSearchResult[];
+}
+
+export function PolicyExplorerClient({
+ initialPolicies,
+}: PolicyExplorerClientProps) {
+ const [selectedPolicy, setSelectedPolicy] = useState
- Found {results.length} result{results.length !== 1 ? 's' : ''}
- {policy.policyName}
+ {policy.policyType.replace(/([A-Z])/g, ' $1').trim()}
+ {policy.settingName}
+ {displayValue}
+
+ {formatDistanceToNow(new Date(policy.lastSyncedAt), {
+ addSuffix: true,
+ locale: de,
+ })}
+
+ {new Date(policy.lastSyncedAt).toLocaleString('de-DE')}
+ Keine Policies gefunden - Starten Sie einen Sync
+ Policy Name
+
+
+ Policy Type
+
+
+ Setting Name
+
+
+ Setting Value
+
+ {isJson ? (
+
+
+ ) : (
+ {displayValue}
+
+ Last Synced
+
+
+
+
- Enter a search term to find policy settings + No policy settings available. Trigger a sync to import policies from Intune.
)} diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..f000e3e --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes` tag
+- **Option B**: Install syntax highlighter like `react-syntax-highlighter` or `prism-react-renderer`
+- **Option C**: Use browser-native `` with Tailwind prose styles
+- **Research needed**: Bundle size impact, accessibility of code blocks, whether syntax highlighting is needed for MVP
+
+#### R004: Badge color mapping for policy types?
+**Decision**: NEEDS CLARIFICATION
+- **Option A**: Hardcode mapping (Compliance=blue, Configuration=gray, Security=red)
+- **Option B**: Dynamic hashing (hash policyType string to generate consistent color)
+- **Option C**: Store colors in database (requires schema change - violates spec)
+- **Research needed**: Actual policy type values in production data, Shadcn Badge variant options
+
+#### R005: Should `/search` route redirect to `/policy-explorer` or be replaced?
+**Decision**: NEEDS CLARIFICATION
+- **Option A**: Keep both routes, redirect `/search` → `/policy-explorer` (backwards compatibility)
+- **Option B**: Replace `/search` entirely, update all internal links
+- **Option C**: Keep `/search` as-is for now, create `/policy-explorer` as new route (migration path)
+- **Research needed**: Check for external links to `/search` route, impact on bookmarks
+
+#### R006: How to handle Sheet component state with Server Actions?
+**Decision**: NEEDS CLARIFICATION
+- **Option A**: Client Component manages open/close state, fetches detail via Server Action on open
+- **Option B**: Pass full policy data to Sheet component on click (no additional fetch)
+- **Option C**: Use URL params for selected policy ID (deep linking support)
+- **Research needed**: Shadcn Sheet best practices, data freshness requirements
+
+### Research Deliverables
+- `research.md` documenting all decisions with rationale
+- Code examples for Server/Client composition pattern
+- Performance benchmarks for null-value filtering approaches
+- Badge color mapping specification
+
+## Phase 1: Design & Contracts
+
+**Status**: ⏳ Pending Phase 0 completion
+
+### Data Model
+
+**No database schema changes required**. Feature uses existing `policySettings` table:
+
+```typescript
+// Existing schema (no modifications)
+export const policySettings = pgTable('policy_settings', {
+ id: uuid('id').primaryKey().defaultRandom(),
+ tenantId: uuid('tenant_id').notNull(), // Multi-tenant isolation
+ policyName: text('policy_name').notNull(),
+ policyType: text('policy_type').notNull(),
+ settingName: text('setting_name').notNull(),
+ settingValue: text('setting_value').notNull(),
+ graphPolicyId: text('graph_policy_id'),
+ lastSyncedAt: timestamp('last_synced_at').notNull(),
+ createdAt: timestamp('created_at').notNull().defaultNow(),
+});
+```
+
+**Filtering Logic** (to be documented in `data-model.md`):
+- Null value detection: `settingValue === "null" || settingValue === null`
+- Applied at: [TBD in Phase 0 - backend vs frontend]
+- Performance impact: [TBD in Phase 0 - requires benchmarking]
+
+### API Contracts
+
+#### Server Action: `getRecentPolicySettings(limit?: number)`
+
+**File**: `lib/actions/policySettings.ts` (new function)
+
+```typescript
+export async function getRecentPolicySettings(
+ limit: number = 50
+): Promise {
+ // Returns: Recent policy settings, sorted by lastSyncedAt DESC
+ // Filters: tenantId (from session), exclude null values
+ // Security: Session validation, tenant isolation
+}
+```
+
+**Contract**:
+- **Input**: `limit` (optional, default 50, max 100)
+- **Output**: `{ success: boolean, data?: PolicySettingSearchResult[], error?: string, totalCount?: number }`
+- **Security**: Validates session, extracts tenantId, enforces tenant isolation
+- **Performance**: Database query with LIMIT clause, DESC index on lastSyncedAt
+
+#### Server Action: `searchPolicySettings(searchTerm: string, limit?: number)` [MODIFIED]
+
+**File**: `lib/actions/policySettings.ts` (extend existing function)
+
+**Changes**:
+- Add optional `limit` parameter (default 100, max 200)
+- Add null-value filtering (if backend approach chosen in Phase 0)
+- Keep existing security and tenant isolation logic
+
+#### Server Action: `getPolicySettingById(id: string)` [EXISTING]
+
+**File**: `lib/actions/policySettings.ts` (already exists, line ~164)
+
+**Usage**: Fetch full policy details for detail sheet (if on-click fetch approach chosen in Phase 0)
+
+### Component Contracts
+
+#### Component: `PolicyDetailSheet`
+
+**File**: `components/policy-explorer/PolicyDetailSheet.tsx` (new)
+
+**Props**:
+```typescript
+interface PolicyDetailSheetProps {
+ policy: PolicySettingSearchResult | null;
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
+```
+
+**Features**:
+- Shadcn Sheet component (slide-over from right)
+- JSON detection: `settingValue.trim().startsWith('{') || settingValue.trim().startsWith('[')`
+- JSON formatting: `JSON.stringify(JSON.parse(value), null, 2)` with error handling
+- Fallback: Display as plain text if JSON.parse fails
+
+#### Component: `PolicyTable`
+
+**File**: `components/policy-explorer/PolicyTable.tsx` (new, refactored from ResultsTable.tsx)
+
+**Props**:
+```typescript
+interface PolicyTableProps {
+ policies: PolicySettingSearchResult[];
+ onRowClick: (policy: PolicySettingSearchResult) => void;
+}
+```
+
+**Features**:
+- Badge rendering for `policyType` column
+- Row hover effect: `hover:bg-accent cursor-pointer`
+- Click handler on TableRow
+- Date formatting with `date-fns` (existing dependency)
+
+#### Component: `PolicySearchContainer`
+
+**File**: `components/policy-explorer/PolicySearchContainer.tsx` (new)
+
+**Props**:
+```typescript
+interface PolicySearchContainerProps {
+ initialPolicies: PolicySettingSearchResult[];
+}
+```
+
+**Features**:
+- Client Component wrapper ('use client')
+- Manages search state and detail sheet state
+- Calls `searchPolicySettings` Server Action on user input
+- Filters null values (if frontend approach chosen in Phase 0)
+- Renders SearchInput + PolicyTable + PolicyDetailSheet
+
+### Page Design
+
+#### Page: `/policy-explorer` (or refactored `/search`)
+
+**File**: `app/(app)/policy-explorer/page.tsx` (new) or `app/(app)/search/page.tsx` (refactor)
+
+**Composition**:
+```tsx
+export default async function PolicyExplorerPage() {
+ // Server Component - fetches initial data
+ const initialData = await getRecentPolicySettings(50);
+
+ return (
+
+
+
+ Policy Explorer
+ Browse and search Intune policy settings
+ {/* Keep existing sync button */}
+
+
+
+
+
+
+ );
+}
+```
+
+**Flow**:
+1. Server Component fetches initial 50 policies (no search term)
+2. Passes to Client Component as props
+3. Client Component renders table with initial data
+4. User searches → Client Component calls Server Action → Updates table
+5. User clicks row → Client Component opens detail sheet
+
+### Navigation Update
+
+**File**: `config/nav.ts`
+
+**Changes**:
+- Replace `{ title: "Search", href: "/search" }` with `{ title: "Policy Explorer", href: "/policy-explorer" }`
+- Remove `{ title: "All Settings", href: "/settings-overview" }` (if exists)
+- Keep icon (or change to relevant icon like FileSearch or Database)
+
+## Phase 2: Implementation Tasks
+
+**Status**: ⏳ Pending Phase 1 completion
+
+### Task Breakdown (High-Level)
+
+#### Epic 1: Backend - Server Actions (P1)
+- [ ] T001: Add `getRecentPolicySettings()` Server Action with null filtering
+- [ ] T002: Extend `searchPolicySettings()` with optional limit parameter
+- [ ] T003: Add null-value filtering to search results (if backend approach chosen)
+- [ ] T004: Add unit tests for Server Actions
+- [ ] T005: Verify tenant isolation in new query
+
+#### Epic 2: Components - Detail Sheet (P1)
+- [ ] T006: Install Shadcn Sheet component (if not present)
+- [ ] T007: Install Shadcn Badge component (if not present)
+- [ ] T008: Create `PolicyDetailSheet.tsx` with JSON detection logic
+- [ ] T009: Create JSON formatting utility with error handling
+- [ ] T010: Style Sheet component (width, padding, close button)
+- [ ] T011: Test Sheet with various JSON structures (nested objects, arrays)
+
+#### Epic 3: Components - Table & Search (P1)
+- [ ] T012: Create `PolicyTable.tsx` with row click handlers
+- [ ] T013: Add Badge rendering for policyType column
+- [ ] T014: Add hover styles and cursor pointer to rows
+- [ ] T015: Create `PolicySearchContainer.tsx` Client Component wrapper
+- [ ] T016: Implement search state management (useTransition)
+- [ ] T017: Implement detail sheet state management (open/close)
+- [ ] T018: Add null-value filtering (if frontend approach chosen)
+
+#### Epic 4: Page Refactor (P1)
+- [ ] T019: Create or refactor page to Server Component pattern
+- [ ] T020: Fetch initial 50 policies in Server Component
+- [ ] T021: Pass initial data to Client Component
+- [ ] T022: Update CardHeader title to "Policy Explorer"
+- [ ] T023: Test page with no policies (empty state)
+- [ ] T024: Test page with 50+ policies (initial load performance)
+
+#### Epic 5: Navigation & Routing (P2)
+- [ ] T025: Update `config/nav.ts` with new route
+- [ ] T026: Remove "All Settings" menu item (if exists)
+- [ ] T027: Decide on /search redirect vs replacement
+- [ ] T028: Implement redirect or route replacement
+- [ ] T029: Update internal links to use new route
+
+#### Epic 6: Visual Improvements (P3)
+- [ ] T030: Define policy type to badge color mapping
+- [ ] T031: Implement badge color logic in PolicyTable
+- [ ] T032: Test badge colors with production data
+- [ ] T033: Add loading skeletons for table (optional)
+- [ ] T034: Add empty state component for no results
+
+#### Epic 7: Testing & Validation (All Priorities)
+- [ ] T035: Manual test - Initial load shows 50 policies
+- [ ] T036: Manual test - Null values filtered correctly
+- [ ] T037: Manual test - Row click opens detail sheet
+- [ ] T038: Manual test - JSON formatted in detail sheet
+- [ ] T039: Manual test - Search filters null values
+- [ ] T040: Manual test - Badge colors correct
+- [ ] T041: E2E test - Page load and search flow
+- [ ] T042: E2E test - Detail sheet open/close
+- [ ] T043: Performance test - Initial load < 2s
+- [ ] T044: Performance test - Detail sheet < 300ms
+
+### Implementation Order
+1. **Phase 2.1** (P1 Backend): T001-T005 (Server Actions)
+2. **Phase 2.2** (P1 Components): T006-T011 (Detail Sheet) + T012-T018 (Table)
+3. **Phase 2.3** (P1 Page): T019-T024 (Page refactor)
+4. **Phase 2.4** (P2 Navigation): T025-T029 (Routing)
+5. **Phase 2.5** (P3 Polish): T030-T034 (Visual improvements)
+6. **Phase 2.6** (Testing): T035-T044 (Validation)
+
+## Known Constraints
+
+1. **No database migrations**: Feature must work with existing schema (✅ confirmed in spec)
+2. **Server-first architecture**: All data fetching via Server Actions (✅ plan follows this)
+3. **Backwards compatibility**: May need redirect from `/search` to `/policy-explorer` (TBD in Phase 1)
+4. **Bundle size**: Must evaluate syntax highlighter libraries if chosen (TBD in Phase 0)
+5. **Tenant isolation**: All queries must include tenantId filter (✅ existing pattern)
+
+## Risk Assessment
+
+| Risk | Impact | Mitigation |
+|------|--------|------------|
+| Null filtering on 1000+ records slow | High | Phase 0 benchmarking, backend index if needed |
+| JSON.parse() fails on malformed data | Medium | Try/catch with fallback to plain text |
+| Sheet component state management complex | Medium | Research Phase 0, follow Shadcn examples |
+| Breaking change to `/search` route | Low | Implement redirect, communicate to users |
+| Badge colors insufficient contrast | Low | Use Shadcn variants, test accessibility |
+
+## Success Criteria Mapping
+
+| Success Criterion (from spec) | Implementation |
+|-------------------------------|----------------|
+| SC-001: Initial load < 2s | Server Component with `getRecentPolicySettings(50)` + database indexes |
+| SC-002: 100% valid values (no nulls) | Backend or frontend filtering (Phase 0 decision) |
+| SC-003: Detail sheet < 300ms | Client-side state management, no additional data fetch |
+| SC-004: JSON formatted correctly | `JSON.parse()` + `JSON.stringify()` with `` tag |
+| SC-005: Badge colors distinguishable | Shadcn Badge variants, color mapping (Phase 1) |
+| SC-006: Single "Policy Explorer" menu | Update `config/nav.ts`, remove "All Settings" |
+
+## Deployment Checklist
+
+- [ ] All Phase 0 research documented in `research.md`
+- [ ] All Phase 1 contracts documented in `contracts/`
+- [ ] Code committed to `003-policy-explorer-ux` branch
+- [ ] Feature flag enabled (if applicable)
+- [ ] Navigation updated in `config/nav.ts`
+- [ ] Manual testing checklist completed
+- [ ] E2E tests passing (if implemented)
+- [ ] Performance benchmarks validated
+- [ ] Merge to `development` branch
+- [ ] Deploy to staging environment
+- [ ] User acceptance testing
+- [ ] Deploy to production
+- [ ] Monitor performance metrics
+- [ ] Gather user feedback
+
+## Next Steps
+
+1. **Immediate**: Complete Phase 0 research (resolve all NEEDS CLARIFICATION items)
+2. **After Phase 0**: Generate `research.md` with documented decisions
+3. **Phase 1**: Create `data-model.md`, `contracts/`, `quickstart.md`
+4. **Phase 2**: Run `/speckit.tasks` to generate detailed task breakdown
+5. **Implementation**: Execute tasks in priority order (P1 → P2 → P3)
+
+## References
+
+- Feature Spec: [spec.md](./spec.md)
+- Requirements Checklist: [checklists/requirements.md](./checklists/requirements.md)
+- Related Feature: [specs/001-global-policy-search/](../001-global-policy-search/) (base implementation)
+- Related Feature: [specs/002-manual-policy-sync/](../002-manual-policy-sync/) (SyncButton component)
+- Constitution: [.specify/memory/constitution.md](../../.specify/memory/constitution.md)
+- Shadcn UI Sheet Docs: https://ui.shadcn.com/docs/components/sheet
+- Shadcn UI Badge Docs: https://ui.shadcn.com/docs/components/badge
diff --git a/specs/003-policy-explorer-ux/spec.md b/specs/003-policy-explorer-ux/spec.md
new file mode 100644
index 0000000..9ba7146
--- /dev/null
+++ b/specs/003-policy-explorer-ux/spec.md
@@ -0,0 +1,121 @@
+# Feature Specification: Policy Explorer UX Upgrade
+
+**Feature Branch**: `003-policy-explorer-ux`
+**Created**: 2025-12-07
+**Status**: Draft
+**Input**: User description: "Policy Explorer UX Upgrade - Improve global search UI with detail view, data filtering, and better UX"
+
+## User Scenarios & Testing *(mandatory)*
+
+### User Story 1 - Browse Recent Policies on Page Load (Priority: P1)
+
+Als Intune-Admin möchte ich beim Öffnen des Policy Explorers sofort die neuesten 50 Policy-Einstellungen sehen, ohne erst einen Suchbegriff eingeben zu müssen.
+
+**Why this priority**: Dies ist die Kernverbesserung des Features. Ein leerer Bildschirm ("Empty State") ist schlechte UX. Der User sollte sofort produktiv sein können und einen Überblick über die neuesten Änderungen erhalten.
+
+**Independent Test**: Kann vollständig getestet werden durch Öffnen der `/policy-explorer` Seite ohne Suchbegriff. Liefert sofort Mehrwert, da Admins die neuesten Policies sehen können.
+
+**Acceptance Scenarios**:
+
+1. **Given** der Admin ist eingeloggt, **When** er die Policy Explorer Seite öffnet (ohne Suchbegriff), **Then** sieht er sofort eine Tabelle mit den 50 neuesten Policy-Einstellungen (sortiert nach `lastSyncedAt` DESC).
+2. **Given** der Admin ist auf der Policy Explorer Seite, **When** die Seite lädt, **Then** werden Einträge mit `settingValue = "null"` oder `null` automatisch ausgeblendet.
+3. **Given** es existieren keine Policy-Einstellungen für den Tenant, **When** die Seite lädt, **Then** sieht der Admin eine Nachricht "Keine Policies gefunden - Starten Sie einen Sync".
+
+---
+
+### User Story 2 - View Policy Details in Slide-Over Sheet (Priority: P1)
+
+Als Intune-Admin möchte ich auf eine Policy-Zeile klicken können, um eine detaillierte Ansicht mit formatierten JSON-Werten zu sehen, damit ich komplexe OMA-URI Settings verstehen kann.
+
+**Why this priority**: Dies löst das Hauptproblem des aktuellen Features - komplexe JSON-Werte sind in der Tabelle nicht lesbar. Die Detailansicht ist essenziell für produktive Nutzung.
+
+**Independent Test**: Kann getestet werden durch Klick auf eine beliebige Tabellenzeile. Funktioniert unabhängig von der Suchfunktion.
+
+**Acceptance Scenarios**:
+
+1. **Given** der Admin sieht die Policy-Tabelle, **When** er auf eine Zeile klickt, **Then** öffnet sich ein Slide-Over Sheet von rechts mit den Policy-Details.
+2. **Given** das Detail-Sheet ist geöffnet, **When** der `settingValue` ein JSON-String ist (startet mit `{` oder `[`), **Then** wird der Wert formatiert in einem Code-Block angezeigt.
+3. **Given** das Detail-Sheet ist geöffnet, **When** der `settingValue` ein einfacher String ist, **Then** wird der Wert als normaler Text angezeigt.
+4. **Given** das Detail-Sheet ist geöffnet, **When** der Admin auf "Schließen" (X) oder außerhalb des Sheets klickt, **Then** schließt sich das Sheet.
+
+---
+
+### User Story 3 - Search with Data Cleaning (Priority: P2)
+
+Als Intune-Admin möchte ich nach Policies suchen können und dabei nur relevante Einträge sehen (ohne "null"-Werte), damit die Ergebnisse übersichtlich bleiben.
+
+**Why this priority**: Die Suchfunktion existiert bereits (Feature 001-global-policy-search), muss aber nun ebenfalls die Datenfilterung respektieren. Dies ist P2, weil die Basisfunktionalität bereits vorhanden ist.
+
+**Independent Test**: Kann getestet werden durch Eingabe eines Suchbegriffs und Verifizierung, dass keine "null"-Werte in den Ergebnissen erscheinen.
+
+**Acceptance Scenarios**:
+
+1. **Given** der Admin ist auf der Policy Explorer Seite, **When** er "USB" in das Suchfeld eingibt, **Then** sieht er nur Ergebnisse, die "USB" enthalten UND deren `settingValue` nicht "null" ist.
+2. **Given** der Admin hat nach "Camera" gesucht, **When** er das Suchfeld leert, **Then** kehrt die Ansicht zu den 50 neuesten Einträgen zurück.
+
+---
+
+### User Story 4 - Improved Visual Hierarchy (Priority: P3)
+
+Als Intune-Admin möchte ich Policy-Typen auf einen Blick durch farbige Badges unterscheiden können, damit ich schneller relevante Policies finde.
+
+**Why this priority**: Dies ist eine visuelle Verbesserung, die die Usability erhöht, aber nicht funktional kritisch ist. Kann nach P1 und P2 implementiert werden.
+
+**Independent Test**: Kann visuell getestet werden durch Ansehen der Tabelle und Verifizierung der Badge-Farben.
+
+**Acceptance Scenarios**:
+
+1. **Given** der Admin sieht die Policy-Tabelle, **When** er die `policyType` Spalte ansieht, **Then** sieht er farbige Badges (z.B. Compliance = Blau, Configuration = Grau, Security = Rot).
+2. **Given** der Admin bewegt die Maus über eine Tabellenzeile, **When** die Zeile gehovered wird, **Then** ändert sich der Cursor zu `pointer` und die Zeile hebt sich visuell hervor.
+
+---
+
+### Edge Cases
+
+- Was passiert, wenn ein `settingValue` ein sehr langer JSON-String ist (>10KB)? → Im Detail-Sheet scrollen mit `max-height` und `overflow-auto`.
+- Wie verhält sich die Seite, wenn der User sehr schnell zwischen Zeilen klickt? → Debouncing oder Loading-State im Sheet.
+- Was passiert, wenn `lastSyncedAt` fehlt? → Fallback auf `createdAt` oder "Unbekannt" anzeigen.
+- Wie werden Sonderzeichen in JSON-Werten gehandelt? → Automatisches Escaping durch `JSON.parse()` und `` Tag.
+
+## Requirements *(mandatory)*
+
+### Functional Requirements
+
+- **FR-001**: System MUSS eine neue Route `/policy-explorer` bereitstellen (ersetzt `/search`).
+- **FR-002**: System MUSS beim Laden der Seite (ohne Suchbegriff) automatisch die 50 neuesten Policy-Einstellungen anzeigen (sortiert nach `lastSyncedAt` DESC).
+- **FR-003**: System MUSS Einträge mit `settingValue = "null"` (String) oder `settingValue = null` (Wert) aus allen Ergebnissen filtern (Backend-Filter bevorzugt).
+- **FR-004**: System MUSS die `policyType` Spalte als farbige Badge darstellen (Shadcn UI Badge Component).
+- **FR-005**: System MUSS Tabellenzeilen klickbar machen (Cursor: pointer, Hover-Effekt).
+- **FR-006**: System MUSS eine neue `PolicyDetailSheet` Komponente bereitstellen (Shadcn Sheet Component, Slide-Over von rechts).
+- **FR-007**: System MUSS im Detail-Sheet prüfen, ob `settingValue` ein JSON-String ist (startet mit `{` oder `[`).
+- **FR-008**: System MUSS JSON-Strings formatiert in einem Code-Block anzeigen (`` oder Syntax Highlighter).
+- **FR-009**: System MUSS normale String-Werte als Text im Detail-Sheet anzeigen.
+- **FR-010**: System MUSS den "All Settings" Menüpunkt entfernen (falls vorhanden) und durch "Policy Explorer" ersetzen.
+- **FR-011**: System MUSS weiterhin die bestehende Suchfunktion unterstützen (mit Datenfilterung).
+
+### Key Entities
+
+- **PolicySetting**: Erweitert das bestehende Entity aus Feature 001-global-policy-search
+ - Alle bisherigen Attribute bleiben bestehen
+ - Keine Datenbankänderungen erforderlich
+ - Neue Frontend-Filterlogik: `settingValue !== "null" && settingValue !== null`
+
+## Success Criteria *(mandatory)*
+
+### Measurable Outcomes
+
+- **SC-001**: Beim Öffnen der Policy Explorer Seite werden innerhalb von 2 Sekunden die 50 neuesten Einträge angezeigt.
+- **SC-002**: 100% der angezeigten Einträge haben einen gültigen `settingValue` (nicht "null" oder `null`).
+- **SC-003**: Klick auf eine Tabellenzeile öffnet das Detail-Sheet in unter 300ms.
+- **SC-004**: JSON-Strings im Detail-Sheet sind korrekt formatiert und lesbar (mit Syntax Highlighting oder mindestens Zeilenumbrüchen).
+- **SC-005**: Die `policyType` Badges sind visuell unterscheidbar (mindestens 3 verschiedene Farben für Compliance, Configuration, Security).
+- **SC-006**: Die Navigation enthält nur noch einen Menüpunkt "Policy Explorer" (kein separater "All Settings" Eintrag mehr).
+
+## Assumptions
+
+- Die bestehende Datenbankstruktur aus Feature 001-global-policy-search wird nicht geändert.
+- Die Server Action `searchPolicySettings` wird erweitert um einen optionalen `limit` Parameter (Default: 50).
+- Shadcn UI Komponenten (Sheet, Badge) sind bereits im Projekt integriert.
+- Die Route `/search` wird umbenannt zu `/policy-explorer` (oder beide bleiben bestehen mit Redirect).
+- Die Filterung von "null"-Werten kann initial im Frontend erfolgen, sollte aber später ins Backend verschoben werden für Performance.
+- Syntax Highlighting ist "Nice-to-have" - eine einfache ``-Darstellung reicht für MVP.
diff --git a/specs/003-policy-explorer-ux/tasks.md b/specs/003-policy-explorer-ux/tasks.md
new file mode 100644
index 0000000..e6c4940
--- /dev/null
+++ b/specs/003-policy-explorer-ux/tasks.md
@@ -0,0 +1,334 @@
+# Tasks: Policy Explorer UX Upgrade
+
+**Input**: Design documents from `/specs/003-policy-explorer-ux/`
+**Prerequisites**: spec.md ✅, plan.md ✅
+**Branch**: `003-policy-explorer-ux`
+**Generated**: 2025-12-07
+
+**Tests**: Tests are NOT included in this task list as they were not explicitly requested in the feature specification.
+
+**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
+
+## Format: `- [ ] [ID] [P?] [Story?] Description with file path`
+
+- **[P]**: Can run in parallel (different files, no dependencies)
+- **[Story]**: Which user story this task belongs to (US1, US2, US3, US4)
+- File paths are absolute from repository root
+
+---
+
+## Phase 1: Setup (Shared Infrastructure)
+
+**Purpose**: Verify prerequisites and install missing Shadcn UI components
+
+- [X] T001 [P] Verify Shadcn Sheet component exists at `components/ui/sheet.tsx`, install if missing via `npx shadcn@latest add sheet`
+- [X] T002 [P] Verify Shadcn Badge component exists at `components/ui/badge.tsx`, install if missing via `npx shadcn@latest add badge`
+- [X] T003 Create new directory `components/policy-explorer/` for new feature components
+
+---
+
+## Phase 2: Foundational (Blocking Prerequisites)
+
+**Purpose**: Core backend infrastructure that MUST be complete before ANY user story can be implemented
+
+**⚠️ CRITICAL**: No user story work can begin until this phase is complete
+
+### Backend Server Actions
+
+- [X] T004 Add `getRecentPolicySettings(limit?: number)` Server Action in `lib/actions/policySettings.ts` - returns 50 newest policies sorted by lastSyncedAt DESC, filtered by tenantId and excluding null values
+- [X] T005 Extend `searchPolicySettings()` Server Action in `lib/actions/policySettings.ts` - add optional `limit` parameter (default 100, max 200)
+- [X] T006 Add null-value filtering logic to Server Actions - filter out entries where `settingValue === "null"` or `settingValue === null` (backend approach for performance)
+- [X] T007 Add type exports for `PolicySettingSearchResult` interface if not already exported from `lib/actions/policySettings.ts`
+- [X] T008 Test Server Actions manually - verify tenant isolation, null filtering, and limit parameters work correctly
+
+**Checkpoint**: Backend ready - all Server Actions functional and tested
+
+---
+
+## Phase 3: User Story 1 - Browse Recent Policies on Page Load (Priority: P1) 🎯 MVP
+
+**Goal**: Admin sees 50 newest policy settings immediately when opening Policy Explorer (no empty state)
+
+**Independent Test**: Navigate to `/policy-explorer`, verify 50 newest entries load without search, no null values visible
+
+### Implementation for User Story 1
+
+- [X] T009 [P] [US1] Create `PolicyTable.tsx` component in `components/policy-explorer/PolicyTable.tsx` - display policies in table format with columns: Setting Name, Setting Value, Policy Name, Policy Type, Last Synced
+- [X] T010 [P] [US1] Add row click handler prop to `PolicyTable` component - accepts `onRowClick: (policy: PolicySettingSearchResult) => void`
+- [X] T011 [P] [US1] Add hover styles to table rows in `PolicyTable` - `hover:bg-accent cursor-pointer` classes
+- [X] T012 [P] [US1] Create `PolicySearchContainer.tsx` client component in `components/policy-explorer/PolicySearchContainer.tsx` - manages search state, accepts `initialPolicies` prop
+- [X] T013 [US1] Refactor `app/(app)/search/page.tsx` to Server Component pattern - call `getRecentPolicySettings(50)` in Server Component, pass data to `PolicySearchContainer`
+- [X] T014 [US1] Update page CardTitle to "Policy Explorer" and CardDescription in `app/(app)/search/page.tsx`
+- [X] T015 [US1] Add empty state handling - if no policies returned, show message "Keine Policies gefunden - Starten Sie einen Sync"
+- [X] T016 [US1] Keep existing `SyncButton` component in CardHeader of `app/(app)/search/page.tsx`
+
+**Checkpoint**: Page loads with 50 newest policies, no null values, empty state works
+
+---
+
+## Phase 4: User Story 2 - View Policy Details in Slide-Over Sheet (Priority: P1) 🎯 MVP
+
+**Goal**: Admin can click any policy row to see full details with formatted JSON in a slide-over sheet
+
+**Independent Test**: Click any table row, verify sheet opens from right with policy details, JSON is formatted
+
+### Implementation for User Story 2
+
+- [X] T017 [P] [US2] Create `PolicyDetailSheet.tsx` component in `components/policy-explorer/PolicyDetailSheet.tsx` - uses Shadcn Sheet, accepts `policy`, `open`, `onOpenChange` props
+- [X] T018 [P] [US2] Implement JSON detection logic in `PolicyDetailSheet` - check if `settingValue.trim().startsWith('{')` or `startsWith('[')`
+- [X] T019 [P] [US2] Implement JSON formatting utility - `JSON.stringify(JSON.parse(value), null, 2)` wrapped in try/catch, fallback to plain text on error
+- [X] T020 [P] [US2] Render JSON values in `` tag with Tailwind prose classes for readability in `PolicyDetailSheet`
+- [X] T021 [P] [US2] Render non-JSON values as normal text in `PolicyDetailSheet`
+- [X] T022 [P] [US2] Add Sheet styling - proper width (e.g., `w-[600px]`), padding, close button, scroll for long content (`max-height` + `overflow-auto`)
+- [X] T023 [US2] Integrate `PolicyDetailSheet` into `PolicySearchContainer` - add state for selected policy and sheet open/close
+- [X] T024 [US2] Connect row click handler from `PolicyTable` to open detail sheet with clicked policy data
+- [X] T025 [US2] Test detail sheet with various data - nested JSON objects, arrays, long strings (>10KB), plain text values, malformed JSON
+
+**Checkpoint**: Clicking rows opens detail sheet, JSON is formatted, sheet closes properly
+
+---
+
+## Phase 5: User Story 3 - Search with Data Cleaning (Priority: P2)
+
+**Goal**: Admin can search policies and results are filtered to exclude null values
+
+**Independent Test**: Enter search term "USB", verify results contain "USB" and no null values
+
+### Implementation for User Story 3
+
+- [X] T026 [US3] Integrate existing `SearchInput` component into `PolicySearchContainer` in `components/policy-explorer/PolicySearchContainer.tsx`
+- [X] T027 [US3] Add search handler in `PolicySearchContainer` - calls `searchPolicySettings()` Server Action with search term
+- [X] T028 [US3] Implement search state management using `useTransition()` hook for pending state
+- [X] T029 [US3] Update table to show search results when search term is entered, show initial 50 policies when search is cleared
+- [X] T030 [US3] Verify null values are filtered from search results (handled by Server Action from T006)
+- [X] T031 [US3] Add loading indicator while search is in progress (use `isPending` from `useTransition`)
+
+**Checkpoint**: Search works, results exclude null values, loading states correct
+
+---
+
+## Phase 6: User Story 4 - Improved Visual Hierarchy (Priority: P3)
+
+**Goal**: Admin sees policy types as colored badges and improved row hover effects
+
+**Independent Test**: View table, verify badges have distinct colors, rows highlight on hover
+
+### Implementation for User Story 4
+
+- [X] T032 [P] [US4] Define policy type to badge color mapping - create utility function or constant (e.g., Compliance=blue, Configuration=gray, Security=red, etc.)
+- [X] T033 [P] [US4] Implement badge rendering in `PolicyTable` - replace plain text `policyType` with Shadcn Badge component
+- [X] T034 [P] [US4] Apply badge variant/color based on policy type in `PolicyTable` component
+- [X] T035 [US4] Test badge colors with real data - verify at least 3 distinct colors are visible and accessible (contrast check)
+- [X] T036 [US4] Verify hover effects on table rows - cursor changes to pointer, background color changes
+
+**Checkpoint**: Badges show distinct colors, hover effects smooth, visual hierarchy improved
+
+---
+
+## Phase 7: Navigation & Route Consolidation (Cross-Cutting)
+
+**Purpose**: Update navigation and routing to consolidate under "Policy Explorer"
+
+- [X] T037 Update `config/nav.ts` - replace `{ title: "Search", href: "/search" }` with `{ title: "Policy Explorer", href: "/search" }` (keep /search route for now)
+- [X] T038 Remove "All Settings" menu item from `config/nav.ts` if it exists (from settings-overview feature)
+- [X] T039 Update page metadata (title, description) in `app/(app)/search/page.tsx` to reflect "Policy Explorer" branding
+- [ ] T040 Add redirect from `/settings-overview` to `/search` if that route exists (optional, for backwards compatibility)
+
+**Checkpoint**: Navigation shows single "Policy Explorer" entry, old routes redirect if needed
+
+---
+
+## Phase 8: Polish & Final Integration
+
+**Purpose**: Final refinements and end-to-end validation
+
+- [X] T041 Update `EmptyState` component message in `components/search/EmptyState.tsx` - change to Policy Explorer context if still used
+- [ ] T042 Add loading skeleton to table (optional) - show placeholder rows while initial data loads
+- [ ] T043 Verify responsive layout - test table and detail sheet on mobile viewport (< 768px)
+- [ ] T044 Add error boundary for detail sheet - graceful error handling if sheet rendering fails
+- [ ] T045 Performance check - verify initial page load < 2s, detail sheet opens < 300ms, search response < 2s
+- [ ] T046 Accessibility check - verify keyboard navigation (Tab, Enter to open sheet, Escape to close), screen reader labels
+- [ ] T047 Cross-browser test - verify in Chrome, Safari, Firefox (at minimum)
+
+**Checkpoint**: Feature complete, polished, and tested across browsers/devices
+
+---
+
+## Dependencies Visualization
+
+```mermaid
+graph TD
+ T001[T001: Install Sheet] --> T017[T017: Create PolicyDetailSheet]
+ T002[T002: Install Badge] --> T033[T033: Implement Badge Rendering]
+ T003[T003: Create Directory] --> T009[T009: Create PolicyTable]
+ T003 --> T012[T012: Create PolicySearchContainer]
+ T003 --> T017
+
+ T004[T004: getRecentPolicySettings] --> T013[T013: Refactor Page to Server Component]
+ T005[T005: Extend searchPolicySettings] --> T027[T027: Add Search Handler]
+ T006[T006: Null Filtering] --> T004
+ T006 --> T005
+ T007[T007: Type Exports] --> T009
+ T007 --> T012
+ T007 --> T017
+ T008[T008: Test Server Actions] --> T013
+
+ T009 --> T013
+ T010[T010: Row Click Handler] --> T024[T024: Connect Click to Sheet]
+ T011[T011: Hover Styles] --> T036[T036: Verify Hover Effects]
+ T012 --> T013
+ T013 --> T026[T026: Integrate SearchInput]
+
+ T017 --> T023[T023: Integrate Sheet into Container]
+ T018[T018: JSON Detection] --> T019[T019: JSON Formatting]
+ T019 --> T020[T020: Render JSON in pre]
+ T020 --> T023
+ T021[T021: Render Non-JSON] --> T023
+ T022[T022: Sheet Styling] --> T023
+ T023 --> T024
+
+ T026 --> T027
+ T027 --> T028[T028: Search State Management]
+ T028 --> T029[T029: Update Table with Results]
+ T029 --> T030[T030: Verify Null Filtering]
+ T030 --> T031[T031: Loading Indicator]
+
+ T032[T032: Badge Color Mapping] --> T033
+ T033 --> T034[T034: Apply Badge Variants]
+ T034 --> T035[T035: Test Badge Colors]
+
+ T013 --> T037[T037: Update Navigation]
+ T037 --> T038[T038: Remove All Settings]
+ T038 --> T039[T039: Update Page Metadata]
+```
+
+---
+
+## Parallel Execution Opportunities
+
+### Phase 1 (All parallel):
+- T001, T002, T003 can all run simultaneously
+
+### Phase 2:
+- **Parallel**: T007 can run while T004-T006 are being implemented
+- **Sequential**: T004-T006 must complete before T008
+- **Blocking**: T008 must complete before Phase 3 starts
+
+### Phase 3 (User Story 1):
+- **Parallel**: T009, T010, T011, T012 (different components)
+- **Sequential**: T013 requires T009 and T012 complete
+- **Sequential**: T014-T016 extend T013
+
+### Phase 4 (User Story 2):
+- **Parallel**: T017, T018, T019, T020, T021, T022 (all component work)
+- **Sequential**: T023 requires T017-T022 complete
+- **Sequential**: T024 requires T023 and T010 complete
+- **Sequential**: T025 is final testing
+
+### Phase 5 (User Story 3):
+- **Sequential**: T026 → T027 → T028 → T029 → T030 → T031 (build on each other)
+
+### Phase 6 (User Story 4):
+- **Parallel**: T032, T033, T034 (component work)
+- **Sequential**: T035, T036 (testing)
+
+### Phase 7 (Navigation):
+- **Parallel**: T037, T038 (different files)
+- **Sequential**: T039, T040 extend T037/T038
+
+### Phase 8 (Polish):
+- **Mostly parallel**: T041-T044 (different concerns)
+- **Sequential**: T045-T047 (validation after implementation)
+
+---
+
+## Suggested MVP Scope
+
+**Minimum Viable Product** = Phase 1 + Phase 2 + Phase 3 + Phase 4
+
+This delivers:
+- ✅ 50 newest policies on page load (no empty state)
+- ✅ Null values filtered automatically
+- ✅ Clickable rows with detail sheet
+- ✅ JSON formatting in detail view
+- ✅ Functional search (from existing feature)
+
+**Can be deferred post-MVP**:
+- User Story 3 tasks (search refinements) - existing search already works
+- User Story 4 tasks (visual improvements) - badges and hover are polish
+- Phase 7 tasks (navigation consolidation) - can keep both menu items temporarily
+- Phase 8 tasks (polish) - can iterate after core functionality validated
+
+---
+
+## Implementation Strategy
+
+### Week 1: Foundation + US1 (MVP Core)
+- Days 1-2: Phase 1 (Setup) + Phase 2 (Backend)
+- Days 3-5: Phase 3 (User Story 1 - Browse Recent)
+
+**Deliverable**: Page loads with 50 newest policies, no null values
+
+### Week 2: US2 + Testing (MVP Complete)
+- Days 1-3: Phase 4 (User Story 2 - Detail Sheet)
+- Days 4-5: Testing and bug fixes for US1 + US2
+
+**Deliverable**: Functional Policy Explorer with detail view (MVP ready)
+
+### Week 3: Polish + Enhancement
+- Days 1-2: Phase 5 (User Story 3 - Search refinements)
+- Days 3-4: Phase 6 (User Story 4 - Visual improvements)
+- Day 5: Phase 7 (Navigation) + Phase 8 (Polish)
+
+**Deliverable**: Full feature with all user stories, polished and production-ready
+
+---
+
+## Task Count Summary
+
+- **Phase 1 (Setup)**: 3 tasks
+- **Phase 2 (Foundation)**: 5 tasks ⚠️ BLOCKING
+- **Phase 3 (US1 - P1)**: 8 tasks 🎯 MVP
+- **Phase 4 (US2 - P1)**: 9 tasks 🎯 MVP
+- **Phase 5 (US3 - P2)**: 6 tasks
+- **Phase 6 (US4 - P3)**: 5 tasks
+- **Phase 7 (Navigation)**: 4 tasks
+- **Phase 8 (Polish)**: 7 tasks
+
+**Total**: 47 tasks
+
+**MVP Subset**: T001-T025 (25 tasks) = Foundation + US1 + US2
+
+---
+
+## Success Criteria Mapping
+
+| Success Criterion | Tasks |
+|-------------------|-------|
+| SC-001: Load < 2s | T004, T013, T045 |
+| SC-002: 100% valid values (no nulls) | T006, T030 |
+| SC-003: Detail sheet < 300ms | T017-T024, T045 |
+| SC-004: JSON formatted | T018-T020, T025 |
+| SC-005: Badge colors distinguishable | T032-T035 |
+| SC-006: Single "Policy Explorer" menu | T037-T038 |
+
+---
+
+## Notes
+
+- **No database migrations**: Feature works with existing schema ✅
+- **Server-first architecture**: All data via Server Actions ✅
+- **Shadcn UI components**: Sheet and Badge must be installed (T001, T002)
+- **Backwards compatibility**: Can keep `/search` route, update navigation text
+- **Performance**: Null filtering in backend (T006) for optimal performance
+- **Testing**: Manual testing focus, no automated E2E tests in scope
+- **Accessibility**: Covered in Phase 8 (T046)
+
+---
+
+## References
+
+- Feature Spec: [spec.md](./spec.md)
+- Implementation Plan: [plan.md](./plan.md)
+- Requirements Checklist: [checklists/requirements.md](./checklists/requirements.md)
+- Related Features: [001-global-policy-search](../001-global-policy-search/), [002-manual-policy-sync](../002-manual-policy-sync/)