Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
import { z } from 'zod';
|
|
|
|
|
|
feat(search): full-platform search overhaul + view tracking + notes bucket
Service rewrite covers 14 entity buckets (clients, residential clients,
yachts, companies, interests, residential interests, berths, invoices,
expenses, documents, files, reminders, brochures, tags, notes, navigation)
with prefix tsquery + trigram fallback, phone-digit normalization,
and JOINs to client_contacts for email matching.
New `notes` bucket searches across the four note tables (client,
interest, yacht, company) via UNION + parent-entity label resolution
(berth mooring for interests, name for yachts/companies). Renders at
the bottom of the dropdown so broad-content matches don't crowd
entity-specific hits — per the user's "low-noise" preference.
Recently-viewed tracking persists last 20 entity views per user in
Redis sorted set; CommandSearch surfaces them as the dropdown's
default state and applies affinity ranking when the user types.
ID-resolve endpoint accepts pasted UUIDs (or invoice numbers like
`INV-2025-001`) and routes the rep straight to the entity, skipping
the normal search bucket.
Audit search service gains `entityIds[]` array filter for the new
loadClientActivityAggregated() path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:58:34 +02:00
|
|
|
const BUCKET_TYPES = [
|
|
|
|
|
'clients',
|
|
|
|
|
'residentialClients',
|
|
|
|
|
'yachts',
|
|
|
|
|
'companies',
|
|
|
|
|
'interests',
|
|
|
|
|
'residentialInterests',
|
|
|
|
|
'berths',
|
|
|
|
|
'invoices',
|
|
|
|
|
'expenses',
|
|
|
|
|
'documents',
|
|
|
|
|
'files',
|
|
|
|
|
'reminders',
|
|
|
|
|
'brochures',
|
|
|
|
|
'tags',
|
|
|
|
|
'navigation',
|
|
|
|
|
] as const;
|
|
|
|
|
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
export const searchQuerySchema = z.object({
|
feat(search): full-platform search overhaul + view tracking + notes bucket
Service rewrite covers 14 entity buckets (clients, residential clients,
yachts, companies, interests, residential interests, berths, invoices,
expenses, documents, files, reminders, brochures, tags, notes, navigation)
with prefix tsquery + trigram fallback, phone-digit normalization,
and JOINs to client_contacts for email matching.
New `notes` bucket searches across the four note tables (client,
interest, yacht, company) via UNION + parent-entity label resolution
(berth mooring for interests, name for yachts/companies). Renders at
the bottom of the dropdown so broad-content matches don't crowd
entity-specific hits — per the user's "low-noise" preference.
Recently-viewed tracking persists last 20 entity views per user in
Redis sorted set; CommandSearch surfaces them as the dropdown's
default state and applies affinity ranking when the user types.
ID-resolve endpoint accepts pasted UUIDs (or invoice numbers like
`INV-2025-001`) and routes the rep straight to the entity, skipping
the normal search bucket.
Audit search service gains `entityIds[]` array filter for the new
loadClientActivityAggregated() path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:58:34 +02:00
|
|
|
// 2-char minimum keeps `to_tsquery('a:*')` from returning every word
|
|
|
|
|
// starting with "a" — short queries return overwhelming match sets.
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
q: z.string().min(2).max(200),
|
feat(search): full-platform search overhaul + view tracking + notes bucket
Service rewrite covers 14 entity buckets (clients, residential clients,
yachts, companies, interests, residential interests, berths, invoices,
expenses, documents, files, reminders, brochures, tags, notes, navigation)
with prefix tsquery + trigram fallback, phone-digit normalization,
and JOINs to client_contacts for email matching.
New `notes` bucket searches across the four note tables (client,
interest, yacht, company) via UNION + parent-entity label resolution
(berth mooring for interests, name for yachts/companies). Renders at
the bottom of the dropdown so broad-content matches don't crowd
entity-specific hits — per the user's "low-noise" preference.
Recently-viewed tracking persists last 20 entity views per user in
Redis sorted set; CommandSearch surfaces them as the dropdown's
default state and applies affinity ranking when the user types.
ID-resolve endpoint accepts pasted UUIDs (or invoice numbers like
`INV-2025-001`) and routes the rep straight to the entity, skipping
the normal search bucket.
Audit search service gains `entityIds[]` array filter for the new
loadClientActivityAggregated() path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:58:34 +02:00
|
|
|
/** Restrict the result set to a single bucket. */
|
|
|
|
|
type: z.enum(BUCKET_TYPES).optional(),
|
|
|
|
|
/** Per-bucket cap. Defaults to 5 (dropdown). 25 is the typical /search-page value. */
|
|
|
|
|
limit: z.coerce.number().int().min(1).max(50).optional(),
|
|
|
|
|
/** Super-admin only — search ports beyond the current one. */
|
|
|
|
|
includeOtherPorts: z
|
|
|
|
|
.union([z.literal('true'), z.literal('1'), z.literal('false'), z.literal('0')])
|
|
|
|
|
.transform((v) => v === 'true' || v === '1')
|
|
|
|
|
.optional(),
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type SearchQuery = z.infer<typeof searchQuerySchema>;
|
feat(search): full-platform search overhaul + view tracking + notes bucket
Service rewrite covers 14 entity buckets (clients, residential clients,
yachts, companies, interests, residential interests, berths, invoices,
expenses, documents, files, reminders, brochures, tags, notes, navigation)
with prefix tsquery + trigram fallback, phone-digit normalization,
and JOINs to client_contacts for email matching.
New `notes` bucket searches across the four note tables (client,
interest, yacht, company) via UNION + parent-entity label resolution
(berth mooring for interests, name for yachts/companies). Renders at
the bottom of the dropdown so broad-content matches don't crowd
entity-specific hits — per the user's "low-noise" preference.
Recently-viewed tracking persists last 20 entity views per user in
Redis sorted set; CommandSearch surfaces them as the dropdown's
default state and applies affinity ranking when the user types.
ID-resolve endpoint accepts pasted UUIDs (or invoice numbers like
`INV-2025-001`) and routes the rep straight to the entity, skipping
the normal search bucket.
Audit search service gains `entityIds[]` array filter for the new
loadClientActivityAggregated() path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:58:34 +02:00
|
|
|
|
|
|
|
|
const RECENTLY_VIEWED_TYPES = [
|
|
|
|
|
'client',
|
|
|
|
|
'residential-client',
|
|
|
|
|
'yacht',
|
|
|
|
|
'company',
|
|
|
|
|
'interest',
|
|
|
|
|
'residential-interest',
|
|
|
|
|
'berth',
|
|
|
|
|
'invoice',
|
|
|
|
|
'expense',
|
|
|
|
|
'document',
|
|
|
|
|
] as const;
|
|
|
|
|
|
|
|
|
|
export const trackViewSchema = z.object({
|
|
|
|
|
type: z.enum(RECENTLY_VIEWED_TYPES),
|
|
|
|
|
id: z.string().min(1).max(100),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type TrackViewPayload = z.infer<typeof trackViewSchema>;
|