All checks were successful
Trigger Cloudarix Deploy / call-webhook (push) Successful in 2s
✨ New Features - Advanced data table with TanStack Table v8 + Server Actions - Server-side pagination (10/25/50/100 rows per page) - Multi-column sorting with visual indicators - Column management (show/hide, resize) persisted to localStorage - URL state synchronization for shareable filtered views - Sticky header with compact/comfortable density modes 📦 Components Added - PolicyTableV2.tsx - Main table with TanStack integration - PolicyTableColumns.tsx - 7 column definitions with sorting - PolicyTablePagination.tsx - Pagination controls - PolicyTableToolbar.tsx - Density toggle + column visibility menu - ColumnVisibilityMenu.tsx - Show/hide columns dropdown 🔧 Hooks Added - usePolicyTable.ts - TanStack Table initialization - useURLState.ts - URL query param sync with nuqs - useTablePreferences.ts - localStorage persistence 🎨 Server Actions Updated - getPolicySettingsV2 - Pagination + sorting + filtering + Zod validation - exportPolicySettingsCSV - Server-side CSV generation (max 5000 rows) 📚 Documentation Added - Intune Migration Guide (1400+ lines) - Reverse engineering strategy - Intune Reference Version tracking - Tasks completed: 22/62 (Phase 1-3) ✅ Zero TypeScript compilation errors ✅ All MVP success criteria met (pagination, sorting, column management) ✅ Ready for Phase 4-7 (filtering, export, detail view, polish) Refs: specs/004-policy-explorer-v2/tasks.md
63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
import { pgTable, text, timestamp, index, unique } from 'drizzle-orm/pg-core';
|
|
import { createId } from '@paralleldrive/cuid2';
|
|
|
|
export const POLICY_TYPES = [
|
|
'deviceConfiguration',
|
|
'compliancePolicy',
|
|
'windowsUpdateForBusiness',
|
|
'endpointSecurity',
|
|
'appConfiguration',
|
|
'enrollmentRestriction',
|
|
'conditionalAccess',
|
|
] as const;
|
|
|
|
export type PolicyType = (typeof POLICY_TYPES)[number];
|
|
|
|
export const policySettings = pgTable(
|
|
'policy_settings',
|
|
{
|
|
id: text('id')
|
|
.primaryKey()
|
|
.$defaultFn(() => createId()),
|
|
|
|
tenantId: text('tenant_id').notNull(),
|
|
|
|
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').notNull(),
|
|
|
|
lastSyncedAt: timestamp('last_synced_at', { mode: 'date' })
|
|
.defaultNow()
|
|
.notNull(),
|
|
|
|
createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
|
|
},
|
|
(table) => ({
|
|
tenantIdIdx: index('policy_settings_tenant_id_idx').on(table.tenantId),
|
|
settingNameIdx: index('policy_settings_setting_name_idx').on(
|
|
table.settingName
|
|
),
|
|
// Composite index for sorting performance (pagination + sorting queries)
|
|
sortingIdx: index('policy_settings_sorting_idx').on(
|
|
table.tenantId,
|
|
table.policyType,
|
|
table.settingName
|
|
),
|
|
// Unique constraint for ON CONFLICT upsert
|
|
upsertUnique: unique('policy_settings_upsert_unique').on(
|
|
table.tenantId,
|
|
table.graphPolicyId,
|
|
table.settingName
|
|
),
|
|
})
|
|
);
|
|
|
|
export type PolicySetting = typeof policySettings.$inferSelect;
|
|
export type NewPolicySetting = typeof policySettings.$inferInsert;
|