Initial commit: Port Nimara CRM (Layers 0-4)
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled

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>
This commit is contained in:
2026-03-26 11:52:51 +01:00
commit 67d7e6e3d5
572 changed files with 86496 additions and 0 deletions

65
src/types/api.ts Normal file
View File

@@ -0,0 +1,65 @@
/**
* Common API response and utility types used across the CRM.
*/
/** Generic wrapper for all API responses */
export interface ApiResponse<T> {
data: T;
success: true;
}
/** Wrapper for API error responses */
export interface ApiErrorResponse {
success: false;
error: ApiError;
}
/** Paginated list response */
export interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
pageSize: number;
total: number;
totalPages: number;
hasNextPage: boolean;
hasPreviousPage: boolean;
};
}
/** Standard API error shape */
export interface ApiError {
code: string;
message: string;
field?: string;
details?: Record<string, unknown>;
}
/** Sort configuration for list queries */
export interface SortConfig<T extends string = string> {
field: T;
direction: 'asc' | 'desc';
}
/** Filter configuration for list queries */
export interface FilterConfig {
field: string;
operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'in' | 'notIn' | 'isNull' | 'isNotNull';
value: string | number | boolean | string[] | number[] | null;
}
/** Standard list query parameters */
export interface ListQueryParams {
page?: number;
pageSize?: number;
sort?: SortConfig;
filters?: FilterConfig[];
search?: string;
}
/** Standard mutation response */
export interface MutationResponse {
success: boolean;
id?: string;
message?: string;
}

56
src/types/auth.ts Normal file
View File

@@ -0,0 +1,56 @@
import type { RolePermissions, UserPreferences } from '@/lib/db/schema/users';
import type { Port } from '@/lib/db/schema/ports';
/** Typed Better Auth user object */
export interface AuthUser {
id: string;
email: string;
name: string;
image: string | null;
emailVerified: boolean;
createdAt: Date;
updatedAt: Date;
}
/** Typed Better Auth session object */
export interface AuthSession {
id: string;
userId: string;
token: string;
expiresAt: Date;
ipAddress: string | null;
userAgent: string | null;
createdAt: Date;
updatedAt: Date;
user: AuthUser | null;
}
/** Extended user profile (from user_profiles table) */
export interface UserProfile {
id: string;
userId: string;
displayName: string;
avatarUrl: string | null;
phone: string | null;
isSuperAdmin: boolean;
isActive: boolean;
lastLoginAt: Date | null;
preferences: UserPreferences;
createdAt: Date;
updatedAt: Date;
}
/** Port context as seen by the authenticated user */
export interface PortContext {
port: Port;
roleId: string;
roleName: string;
permissions: RolePermissions;
}
/** Full authenticated user context (session + profile + port roles) */
export interface AuthenticatedUser extends AuthUser {
profile: UserProfile | null;
portContexts: PortContext[];
currentPortContext: PortContext | null;
}

61
src/types/domain.ts Normal file
View File

@@ -0,0 +1,61 @@
import type {
PIPELINE_STAGES,
BERTH_STATUSES,
LEAD_CATEGORIES,
DOCUMENT_TYPES,
DOCUMENT_STATUSES,
} from '@/lib/constants';
export type PipelineStage = (typeof PIPELINE_STAGES)[number];
export type BerthStatus = (typeof BERTH_STATUSES)[number];
export type LeadCategory = (typeof LEAD_CATEGORIES)[number];
export type DocumentType = (typeof DOCUMENT_TYPES)[number];
export type DocumentStatus = (typeof DOCUMENT_STATUSES)[number];
export interface Port {
id: string;
name: string;
slug: string;
defaultCurrency: string;
timezone: string;
isActive: boolean;
}
export interface Client {
id: string;
portId: string;
fullName: string;
companyName?: string | null;
nationality?: string | null;
source?: string | null;
archivedAt?: Date | null;
createdAt: Date;
updatedAt: Date;
}
export interface Interest {
id: string;
portId: string;
clientId: string;
berthId?: string | null;
pipelineStage: PipelineStage;
leadCategory?: LeadCategory | null;
source?: string | null;
archivedAt?: Date | null;
createdAt: Date;
updatedAt: Date;
}
export interface Berth {
id: string;
portId: string;
mooringNumber: string;
area?: string | null;
status: BerthStatus;
lengthFt?: string | null;
widthFt?: string | null;
price?: string | null;
priceCurrency: string;
createdAt: Date;
updatedAt: Date;
}