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

View File

@@ -0,0 +1,26 @@
import { create } from 'zustand';
interface FileBrowserStore {
viewMode: 'grid' | 'list';
currentFolder: string;
selectedFiles: string[];
setViewMode: (mode: 'grid' | 'list') => void;
setCurrentFolder: (folder: string) => void;
toggleFileSelection: (fileId: string) => void;
clearSelection: () => void;
}
export const useFileBrowserStore = create<FileBrowserStore>((set) => ({
viewMode: 'grid',
currentFolder: '',
selectedFiles: [],
setViewMode: (mode) => set({ viewMode: mode }),
setCurrentFolder: (folder) => set({ currentFolder: folder, selectedFiles: [] }),
toggleFileSelection: (fileId) =>
set((state) => ({
selectedFiles: state.selectedFiles.includes(fileId)
? state.selectedFiles.filter((id) => id !== fileId)
: [...state.selectedFiles, fileId],
})),
clearSelection: () => set({ selectedFiles: [] }),
}));

View File

@@ -0,0 +1,19 @@
import { create } from 'zustand';
import type { RolePermissions } from '@/lib/db/schema/users';
interface PermissionsState {
permissions: RolePermissions | null;
isSuperAdmin: boolean;
userId: string | null;
setPermissions: (permissions: RolePermissions | null, isSuperAdmin: boolean, userId: string | null) => void;
reset: () => void;
}
export const usePermissionsStore = create<PermissionsState>()((set) => ({
permissions: null,
isSuperAdmin: false,
userId: null,
setPermissions: (permissions, isSuperAdmin, userId) =>
set({ permissions, isSuperAdmin, userId }),
reset: () => set({ permissions: null, isSuperAdmin: false, userId: null }),
}));

View File

@@ -0,0 +1,33 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface PipelineStore {
viewMode: 'board' | 'table';
boardFilters: {
leadCategory?: string;
search?: string;
};
setViewMode: (mode: 'board' | 'table') => void;
setBoardFilter: (key: keyof PipelineStore['boardFilters'], value: string | undefined) => void;
clearBoardFilters: () => void;
}
export const usePipelineStore = create<PipelineStore>()(
persist(
(set) => ({
viewMode: 'table',
boardFilters: {},
setViewMode: (mode) => set({ viewMode: mode }),
setBoardFilter: (key, value) =>
set((s) => ({ boardFilters: { ...s.boardFilters, [key]: value } })),
clearBoardFilters: () => set({ boardFilters: {} }),
}),
{
name: 'pn-crm-pipeline',
partialize: (state) => ({
viewMode: state.viewMode,
boardFilters: state.boardFilters,
}),
},
),
);

38
src/stores/ui-store.ts Normal file
View File

@@ -0,0 +1,38 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface UIStore {
sidebarCollapsed: boolean;
currentPortId: string | null;
currentPortSlug: string | null;
darkMode: boolean;
toggleSidebar: () => void;
setPort: (portId: string, portSlug: string) => void;
toggleDarkMode: () => void;
}
export const useUIStore = create<UIStore>()(
persist(
(set) => ({
sidebarCollapsed: false,
currentPortId: null,
currentPortSlug: null,
darkMode: false,
toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })),
setPort: (portId, portSlug) => set({ currentPortId: portId, currentPortSlug: portSlug }),
toggleDarkMode: () => set((s) => ({ darkMode: !s.darkMode })),
}),
{
name: 'pn-crm-ui',
partialize: (state) => ({
sidebarCollapsed: state.sidebarCollapsed,
currentPortId: state.currentPortId,
currentPortSlug: state.currentPortSlug,
darkMode: state.darkMode,
}),
},
),
);
// Alias for port-specific access
export const usePortStore = useUIStore;