@amogads/ui 1.0.2 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -287
- package/dist/index.d.ts +1 -952
- package/dist/index.js +4446 -1698
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4108 -1281
- package/dist/index.mjs.map +1 -1
- package/dist/pages.d.ts +16 -0
- package/dist/pages.js +40135 -0
- package/dist/pages.js.map +1 -0
- package/dist/pages.mjs +40482 -0
- package/dist/pages.mjs.map +1 -0
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +520 -0
- package/dist/sdk.js.map +1 -0
- package/dist/sdk.mjs +476 -0
- package/dist/sdk.mjs.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.js +2713 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +2662 -0
- package/dist/server.mjs.map +1 -0
- package/dist/services.d.ts +1 -0
- package/dist/services.js +2042 -0
- package/dist/services.js.map +1 -0
- package/dist/services.mjs +1983 -0
- package/dist/services.mjs.map +1 -0
- package/dist/stores.d.ts +1 -0
- package/dist/stores.js +559 -0
- package/dist/stores.js.map +1 -0
- package/dist/stores.mjs +531 -0
- package/dist/stores.mjs.map +1 -0
- package/dist/tokens.d.ts +1 -56
- package/dist/tokens.js +0 -1
- package/dist/tokens.js.map +1 -1
- package/package.json +257 -232
- package/dist/index.d.mts +0 -952
- package/dist/tokens.d.mts +0 -56
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/index.ts","../src/lib/supabase/client.ts","../src/stores/auth-store.ts","../src/lib/cookies.ts","../src/lib/auth.ts","../src/features/chattemplate/shared/api/auth.ts","../src/features/chattemplate/shared/api/headers.ts","../src/features/chattemplate/shared/api/errorHandler.ts","../src/features/chattemplate/shared/api/apiClient.ts","../src/features/chattemplate/shared/api/queryBuilder.ts","../src/features/chattemplate/chat/api/messages.api.ts","../src/features/chattemplate/chat/repositories/message-repository.ts","../src/lib/db-alerts/types/db-alert.ts","../src/services/db-alert.service.ts","../src/services/file.service.ts","../src/utils/pdf/createPdf.ts","../src/constants/scanner.ts","../src/utils/scanner/compress.ts","../src/services/pdf.service.ts","../src/services/opencv.service.ts","../src/utils/scanner/detectEdges.ts","../src/utils/scanner/perspective.ts","../src/utils/scanner/enhance.ts","../src/services/scanner.service.ts","../src/services/share.service.ts","../src/services/upload.service.ts","../src/services/auth-redirect.service.ts","../src/services/profile.service.ts","../src/services/push-notification.service.ts","../src/lib/short-url-client.ts","../src/lib/short-url-store.ts","../src/services/short-url.service.ts","../src/services/ai-search.service.ts","../src/features/vouchers/repositories/voucher-repository.ts","../src/services/geocode.service.ts"],"sourcesContent":["/**\n * Amoga Design System — Core Services & Repositories\n */\n\nexport * from './db-alert.service'\nexport * from './file.service'\nexport * from './pdf.service'\nexport * from './scanner.service'\nexport * from './share.service'\nexport * from './upload.service'\nexport * from './auth-redirect.service'\nexport * from './profile.service'\nexport * from './push-notification.service'\nexport * from './short-url.service'\nexport * from './ai-search.service'\nexport * from './voucher-repository'\nexport * from './geocode.service'\n\n\n\n\n","import { createBrowserClient } from '@supabase/ssr'\r\n\r\nlet clientSingleton: ReturnType<typeof createBrowserClient> | null = null\r\n\r\nexport function createClient() {\r\n if (typeof window === 'undefined') {\r\n return createBrowserClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!\r\n )\r\n }\r\n\r\n if (!clientSingleton) {\r\n clientSingleton = createBrowserClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!\r\n )\r\n }\r\n return clientSingleton;\r\n}\r\n","import { create } from 'zustand'\r\nimport { getCookie, setCookie, removeCookie } from '@/lib/cookies'\r\n\r\nconst ACCESS_TOKEN = 'thisisjustarandomstring'\r\nconst USER_DATA = 'auth_user_data'\r\n\r\ninterface AuthUser {\r\n id: string // 👈 ADD THIS - the auth.uid() from Supabase\r\n accountNo: string\r\n email: string\r\n name?: string\r\n picture?: string\r\n role: string[]\r\n exp: number\r\n}\r\n\r\ninterface AuthState {\r\n auth: {\r\n user: AuthUser | null\r\n setUser: (user: AuthUser | null) => void\r\n accessToken: string\r\n setAccessToken: (accessToken: string) => void\r\n resetAccessToken: () => void\r\n reset: () => void\r\n }\r\n}\r\n\r\nexport const useAuthStore = create<AuthState>()((set) => {\r\n const cookieState = getCookie(ACCESS_TOKEN)\r\n let initToken = ''\r\n if (cookieState) {\r\n try {\r\n initToken = JSON.parse(cookieState)\r\n } catch {\r\n removeCookie(ACCESS_TOKEN)\r\n }\r\n }\r\n\r\n const userCookie = getCookie(USER_DATA)\r\n let initUser: AuthUser | null = null\r\n if (userCookie) {\r\n try {\r\n const parsed = JSON.parse(decodeURIComponent(userCookie))\r\n if (parsed.exp && parsed.exp > Date.now()) {\r\n initUser = parsed\r\n } else {\r\n removeCookie(ACCESS_TOKEN)\r\n removeCookie(USER_DATA)\r\n }\r\n } catch {\r\n initUser = null\r\n removeCookie(USER_DATA)\r\n }\r\n }\r\n\r\n return {\r\n auth: {\r\n user: initUser,\r\n setUser: (user) =>\r\n set((state) => {\r\n if (user) {\r\n setCookie(USER_DATA, encodeURIComponent(JSON.stringify(user)))\r\n } else {\r\n removeCookie(USER_DATA)\r\n }\r\n return { ...state, auth: { ...state.auth, user } }\r\n }),\r\n accessToken: initUser ? initToken : '',\r\n setAccessToken: (accessToken) =>\r\n set((state) => {\r\n setCookie(ACCESS_TOKEN, JSON.stringify(accessToken))\r\n return { ...state, auth: { ...state.auth, accessToken } }\r\n }),\r\n resetAccessToken: () =>\r\n set((state) => {\r\n removeCookie(ACCESS_TOKEN)\r\n return { ...state, auth: { ...state.auth, accessToken: '' } }\r\n }),\r\n reset: () =>\r\n set((state) => {\r\n removeCookie(ACCESS_TOKEN)\r\n removeCookie(USER_DATA)\r\n return {\r\n ...state,\r\n auth: { ...state.auth, user: null, accessToken: '' },\r\n }\r\n }),\r\n },\r\n }\r\n})","/**\r\n * Cookie utility functions using manual document.cookie approach\r\n * Replaces js-cookie dependency for better consistency\r\n */\r\n\r\nconst DEFAULT_MAX_AGE = 60 * 60 * 24 * 7 // 7 days\r\n\r\n/**\r\n * Get a cookie value by name\r\n */\r\nexport function getCookie(name: string): string | undefined {\r\n if (typeof document === 'undefined') return undefined\r\n\r\n const value = `; ${document.cookie}`\r\n const parts = value.split(`; ${name}=`)\r\n if (parts.length === 2) {\r\n const cookieValue = parts.pop()?.split(';').shift()\r\n return cookieValue\r\n }\r\n return undefined\r\n}\r\n\r\n/**\r\n * Set a cookie with name, value, and optional max age\r\n */\r\nexport function setCookie(\r\n name: string,\r\n value: string,\r\n maxAge: number = DEFAULT_MAX_AGE\r\n): void {\r\n if (typeof document === 'undefined') return\r\n\r\n document.cookie = `${name}=${value}; path=/; max-age=${maxAge}`\r\n}\r\n\r\n/**\r\n * Remove a cookie by setting its max age to 0\r\n */\r\nexport function removeCookie(name: string): void {\r\n if (typeof document === 'undefined') return\r\n\r\n document.cookie = `${name}=; path=/; max-age=0`\r\n}\r\n","import { NextAuthOptions } from 'next-auth'\nimport GoogleProvider from 'next-auth/providers/google'\nimport { createClient as createSupabaseClient } from '@supabase/supabase-js'\nimport crypto from 'crypto'\n\nconst SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || ''\nconst SUPABASE_KEY = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || ''\n\n/**\n * Converts any string (like a Google numeric ID or email) into a deterministic, valid UUID string.\n * Leaves string untouched if it is already a valid UUID v4 format.\n */\nexport function stringToUuid(str: string): string {\n if (!str) return crypto.randomUUID()\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n if (uuidRegex.test(str)) {\n return str\n }\n const hash = crypto.createHash('md5').update(str).digest('hex')\n return `${hash.substring(0, 8)}-${hash.substring(8, 12)}-4${hash.substring(13, 16)}-a${hash.substring(17, 20)}-${hash.substring(20, 32)}`\n}\n\nexport const authOptions: NextAuthOptions = {\n providers: [\n GoogleProvider({\n clientId: process.env.GOOGLE_CLIENT_ID || process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '',\n clientSecret: process.env.GOOGLE_CLIENT_SECRET || process.env.NEXT_PUBLIC_GOOGLE_SECRET_ID || '',\n authorization: {\n params: {\n prompt: 'select_account',\n access_type: 'offline',\n response_type: 'code',\n },\n },\n checks: ['none'],\n }),\n ],\n secret: process.env.NEXTAUTH_SECRET || 'secret_next_auth_shadcn_admin_key_2026_super_secure',\n session: {\n strategy: 'jwt',\n maxAge: 30 * 24 * 60 * 60, // 30 days\n }, \n callbacks: {\n async redirect({ url, baseUrl }) {\n try {\n const { cookies } = await import('next/headers')\n const cookieStore = await cookies()\n const isMobileAuth = cookieStore.get('mobile_auth')?.value === 'true' || url.includes('is_mobile=true')\n if (isMobileAuth) {\n console.log('📱 [NextAuth Redirect Callback] Mobile auth detected. Redirecting to /auth/callback?is_mobile=true')\n return `${baseUrl}/auth/callback?is_mobile=true&next=/`\n }\n } catch (err) {\n console.error('❌ [NextAuth Redirect Callback] Error inspecting cookies:', err)\n }\n\n if (url.includes('/auth/callback')) return url\n if (url.startsWith('/')) return `${baseUrl}${url}`\n else if (new URL(url).origin === baseUrl) return url\n return baseUrl\n },\n async signIn({ user }) {\n if (!user.email) return false\n\n try {\n if (SUPABASE_URL && SUPABASE_KEY) {\n const supabase = createSupabaseClient(SUPABASE_URL, SUPABASE_KEY)\n const fallbackUuid = stringToUuid(user.id || user.email)\n\n // Check if profile exists by email first (to preserve existing UUID) or by ID\n const { data: existingProfile } = await supabase\n .from('profiles')\n .select('id, auth_user_id')\n .eq('email', user.email.toLowerCase())\n .maybeSingle()\n\n const profileId = existingProfile?.id || fallbackUuid\n\n const profileData: any = {\n id: profileId,\n name: user.name || user.email.split('@')[0],\n email: user.email.toLowerCase(),\n avatar: user.image || null,\n updated_at: new Date().toISOString(),\n }\n\n profileData.auth_user_id = existingProfile?.auth_user_id || profileId\n\n if (existingProfile) {\n await supabase\n .from('profiles')\n .update(profileData)\n .eq('id', existingProfile.id)\n } else {\n await supabase\n .from('profiles')\n .insert(profileData)\n }\n\n // Attach canonical valid UUID to user object\n user.id = profileId\n }\n } catch (err) {\n console.error('[NextAuth] Error syncing user to profiles table:', err)\n }\n\n return true\n },\n async jwt({ token, user }) {\n if (user) {\n token.id = stringToUuid(user.id || user.email!)\n token.email = user.email\n token.name = user.name\n token.picture = user.image\n } else if (token.sub && !stringToUuid(token.sub)) {\n token.id = stringToUuid(token.sub)\n }\n return token\n },\n async session({ session, token }) {\n if (session.user) {\n const canonicalId = stringToUuid((token.id || token.sub || session.user.email) as string)\n ;(session.user as any).id = canonicalId\n ;(session.user as any).picture = token.picture || token.image\n }\n return session\n },\n },\n pages: {\n signIn: '/sign-in',\n error: '/sign-in',\n },\n}\n","import { createClient } from '@/lib/supabase/client'\nimport { useAuthStore } from '@/stores/auth-store'\nimport { stringToUuid } from '@/lib/auth'\n\n/**\n * Retrieves the current session's access token (JWT) from Supabase auth or NextAuth store token.\n * Falls back to the publishable anon key if no active valid Supabase JWT session exists.\n */\nexport async function getAccessToken(): Promise<string> {\n const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || ''\n try {\n const storeToken = useAuthStore.getState().auth.accessToken\n if (storeToken && typeof storeToken === 'string' && storeToken.split('.').length === 3) {\n return storeToken\n }\n\n const supabase = createClient()\n const { data: { session } } = await supabase.auth.getSession()\n if (session?.access_token && session.access_token.split('.').length === 3) {\n return session.access_token\n }\n\n return supabaseKey\n } catch (error) {\n return supabaseKey\n }\n}\n\n/**\n * Retrieves the current authenticated user's profile details.\n * Resolves the actual profile UUID from Supabase by email to ensure it matches\n * the UUID stored in chat_messages, vouchers, contacts, etc.\n */\nexport async function getCurrentUser() {\n try {\n // Try Supabase session first (most reliable UUID)\n const supabase = createClient()\n const { data: { user: supabaseUser } } = await supabase.auth.getUser()\n if (supabaseUser) return supabaseUser\n\n // NextAuth path: resolve profile UUID via email\n const storeUser = useAuthStore.getState().auth.user\n if (!storeUser) return null\n\n const email = storeUser.email?.toLowerCase()\n if (email) {\n try {\n const { data: profileRow } = await supabase\n .from('profiles')\n .select('id, name, email, avatar')\n .eq('email', email)\n .maybeSingle()\n\n if (profileRow?.id) {\n return {\n id: profileRow.id,\n email: profileRow.email,\n user_metadata: {\n name: profileRow.name,\n avatar_url: profileRow.avatar,\n },\n }\n }\n } catch {}\n }\n\n // Last resort: use stringToUuid on storeUser.id\n const validId = stringToUuid(storeUser.id || storeUser.email)\n return {\n id: validId,\n email: storeUser.email,\n user_metadata: {\n name: storeUser.name,\n avatar_url: storeUser.picture,\n },\n }\n } catch (error) {\n console.error('[Auth Core] Failed to get current user:', error)\n return null\n }\n}\n\n","import { getAccessToken } from './auth'\n\n/**\n * Builds HTTP headers required for Supabase PostgREST API requests.\n * Automatically attaches apikey, Authorization, and default content-types.\n * Validates JWT tokens so invalid tokens (like 'next-auth-session') fallback to Supabase anon key.\n */\nexport async function getHeaders(customHeaders: Record<string, string> = {}): Promise<Record<string, string>> {\n const token = await getAccessToken()\n const apiKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || ''\n \n const is3PartJwt = typeof token === 'string' && token.split('.').length === 3\n const validBearerToken = is3PartJwt ? token : apiKey\n\n return {\n 'apikey': apiKey,\n 'Authorization': `Bearer ${validBearerToken}`,\n 'Content-Type': 'application/json',\n ...customHeaders\n }\n}\n","export class ApiError extends Error {\r\n code?: string\r\n details?: string | null\r\n hint?: string | null\r\n\r\n constructor(message: string, code?: string, details?: string | null, hint?: string | null) {\r\n super(message)\r\n this.name = 'ApiError'\r\n this.code = code\r\n this.details = details\r\n this.hint = hint\r\n }\r\n}\r\n\r\n/**\r\n * Handles error responses from PostgREST API requests by extracting error details and throwing ApiError.\r\n */\r\nexport async function handleError(response: Response): Promise<never> {\r\n let errorData: any\r\n try {\r\n errorData = await response.json()\r\n } catch {\r\n throw new ApiError(response.statusText || 'An unknown network error occurred')\r\n }\r\n\r\n const message = errorData?.message || errorData?.error_description || 'API request failed'\r\n const code = errorData?.code\r\n const details = errorData?.details\r\n const hint = errorData?.hint\r\n\r\n throw new ApiError(message, code, details, hint)\r\n}\r\n","import { getHeaders } from './headers'\r\nimport { handleError } from './errorHandler'\r\n\r\nconst SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL\r\n\r\nif (!SUPABASE_URL) {\r\n throw new Error('Missing environment variable: NEXT_PUBLIC_SUPABASE_URL')\r\n}\r\n\r\n// Clean base URL trailing slash\r\nconst BASE_URL = SUPABASE_URL.endsWith('/') ? SUPABASE_URL.slice(0, -1) : SUPABASE_URL\r\n\r\ninterface RequestOptions {\r\n headers?: Record<string, string>\r\n body?: any\r\n}\r\n\r\n/**\r\n * Helper to safely parse JSON response, handling empty bodies.\r\n */\r\nasync function parseResponse<T>(response: Response): Promise<T> {\r\n const text = await response.text()\r\n return (text ? JSON.parse(text) : {}) as T\r\n}\r\n\r\n/**\r\n * postgREST API Client wrapper using standard HTTP calls via fetch.\r\n */\r\nexport const apiClient = {\r\n async get<T>(path: string, options?: RequestOptions): Promise<T> {\r\n const headers = await getHeaders(options?.headers)\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'GET',\r\n headers,\r\n })\r\n \r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n \r\n return parseResponse<T>(response)\r\n },\r\n\r\n async post<T>(path: string, body: any, options?: RequestOptions): Promise<T> {\r\n const defaultHeaders = {\r\n 'Prefer': 'return=representation'\r\n }\r\n const headers = await getHeaders({ ...defaultHeaders, ...options?.headers })\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body),\r\n })\r\n\r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n\r\n return parseResponse<T>(response)\r\n },\r\n\r\n async patch<T>(path: string, body: any, options?: RequestOptions): Promise<T> {\r\n const defaultHeaders = {\r\n 'Prefer': 'return=representation'\r\n }\r\n const headers = await getHeaders({ ...defaultHeaders, ...options?.headers })\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'PATCH',\r\n headers,\r\n body: JSON.stringify(body),\r\n })\r\n\r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n\r\n return parseResponse<T>(response)\r\n },\r\n\r\n async delete<T>(path: string, options?: RequestOptions): Promise<T> {\r\n const defaultHeaders = {\r\n 'Prefer': 'return=representation'\r\n }\r\n const headers = await getHeaders({ ...defaultHeaders, ...options?.headers })\r\n const response = await fetch(`${BASE_URL}${path}`, {\r\n method: 'DELETE',\r\n headers,\r\n })\r\n\r\n if (!response.ok) {\r\n await handleError(response)\r\n }\r\n\r\n return parseResponse<T>(response)\r\n }\r\n}\r\n","/**\n * Standard chainable helper to construct PostgREST query parameters.\n * Eliminates raw string manipulation for URLs.\n */\nexport class QueryBuilder {\n private params: URLSearchParams = new URLSearchParams()\n\n /**\n * Embeds resource relationships or filters output columns.\n */\n select(fields: string): this {\n // Strip whitespace and newlines for a clean query parameter\n const cleaned = fields.replace(/\\s+/g, '')\n this.params.set('select', cleaned)\n return this\n }\n\n /**\n * Equality filter.\n */\n eq(column: string, value: any): this {\n this.params.append(column, `eq.${value}`)\n return this\n }\n\n lt(column: string, value: any): this {\n this.params.append(column, `lt.${value}`)\n return this\n }\n\n /**\n * Case-insensitive pattern matching filter.\n */\n ilike(column: string, value: string): this {\n this.params.append(column, `ilike.${value}`)\n return this\n }\n\n /**\n * IN filter matching array of values.\n */\n in(column: string, values: any[]): this {\n const formatted = values.map((v) => `${v}`).join(',')\n this.params.append(column, `in.(${formatted})`)\n return this\n }\n\n /**\n * Logical OR filter matching multiple column criteria.\n * Example string: \"email.ilike.test@gmail.com,contact_user_id.eq.some-uuid\"\n */\n or(filterString: string): this {\n this.params.set('or', `(${filterString})`)\n return this\n }\n\n /**\n * Sorting filter.\n */\n order(column: string, options?: { ascending?: boolean }): this {\n const dir = options?.ascending !== false ? 'asc' : 'desc'\n this.params.set('order', `${column}.${dir}`)\n return this\n }\n\n /**\n * Limit result size.\n */\n limit(n: number): this {\n this.params.set('limit', n.toString())\n return this\n }\n\n /**\n * Offset result index.\n */\n offset(n: number): this {\n this.params.set('offset', n.toString())\n return this\n }\n\n /**\n * Generates the final query parameter string (e.g. \"?owner_id=eq.123\").\n */\n toString(): string {\n const q = this.params.toString()\n return q ? `?${q}` : ''\n }\n}\n\n/**\n * Instantiates a new QueryBuilder query chain.\n */\nexport function createQuery(): QueryBuilder {\n return new QueryBuilder()\n}\n","import { apiClient } from '../../shared/api/apiClient'\r\nimport { createQuery } from '../../shared/api/queryBuilder'\r\nimport { Message } from '../types/chat.types'\r\n\r\n/**\r\n * Fetch all messages for a specific conversation and user copy.\r\n */\r\nexport async function getConversationMessages(\r\n conversationId: string,\r\n userId: string,\r\n options?: { limit?: number; before?: string }\r\n): Promise<Message[]> {\r\n try {\r\n const query = createQuery()\r\n .select(\r\n `\r\n *,\r\n sender:profiles!sender_user_id (\r\n id,\r\n name,\r\n email,\r\n avatar\r\n )\r\n `\r\n )\r\n .eq('conversation_id', conversationId)\r\n .eq('owner_user_id', userId)\r\n .or('deleted.eq.false,deleted_by.not.is.null')\r\n\r\n if (options?.before) query.lt('created_at', options.before)\r\n query.order('created_at', { ascending: !options?.limit })\r\n if (options?.limit) query.limit(options.limit)\r\n\r\n const response = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${query.toString()}`\r\n )\r\n const data = options?.limit ? [...response].reverse() : response\r\n if (!data) return []\r\n\r\n const messages: Message[] = data.map((d: any) => ({\r\n id: d.id,\r\n conversation_id: d.conversation_id,\r\n owner_user_id: d.owner_user_id,\r\n sender_user_id: d.sender_user_id,\r\n message: d.message,\r\n message_type: d.message_type,\r\n direction: d.direction,\r\n sent: d.sent,\r\n received: d.received,\r\n created_at: d.created_at,\r\n\r\n message_status: d.message_status || undefined,\r\n client_message_id: d.client_message_id || undefined,\r\n queued_at: d.queued_at || undefined,\r\n delivered_at: d.delivered_at || undefined,\r\n read_at: d.read_at || undefined,\r\n retry_count: d.retry_count ? Number(d.retry_count) : undefined,\r\n\r\n file_url: d.file_url || undefined,\r\n file_name: d.file_name || undefined,\r\n file_size: d.file_size ? Number(d.file_size) : undefined,\r\n mime_type: d.mime_type || undefined,\r\n duration: d.duration ? Number(d.duration) : undefined,\r\n thumbnail: d.thumbnail || undefined,\r\n\r\n file_content_text: d.file_content_text || undefined,\r\n file_content_json: d.file_content_json || undefined,\r\n processing_status: d.processing_status || undefined,\r\n\r\n thumb: !!d.thumb,\r\n favorite: !!d.favorite,\r\n flag: !!d.flag,\r\n star: !!d.star,\r\n pin: !!d.pin,\r\n archive: !!d.archive,\r\n deleted: !!d.deleted,\r\n action_this: !!d.action_this,\r\n reply: !!d.reply,\r\n forward: !!d.forward,\r\n\r\n deleted_at: d.deleted_at || undefined,\r\n deleted_by: d.deleted_by || undefined,\r\n\r\n replyemoji: d.replyemoji || undefined,\r\n replyto_message_id: d.replyto_message_id || undefined,\r\n replyto_user_id: d.replyto_user_id || undefined,\r\n parent_message_id: d.parent_message_id || undefined,\r\n\r\n forwardemoji: d.forwardemoji || undefined,\r\n forwardto_message_id: d.forwardto_message_id || undefined,\r\n forwardto_user_id: d.forwardto_user_id || undefined,\r\n\r\n sender_message_id: d.sender_message_id || undefined,\r\n\r\n sender: d.sender\r\n ? {\r\n id: d.sender.id,\r\n name: d.sender.name,\r\n email: d.sender.email,\r\n avatar_url: d.sender.avatar || undefined,\r\n }\r\n : undefined,\r\n location_data: d.location_data || undefined,\r\n location_type: d.location_type || undefined,\r\n }))\r\n\r\n // Resolve reply references against the current user's message copies first.\r\n // Every member owns a different row id, while reply_to_message_id points to\r\n // the sender's canonical copy. Matching sender_message_id keeps replies\r\n // visible for both the sender and every receiver without duplicating rows.\r\n for (const msg of messages) {\r\n if (msg.reply && msg.replyto_message_id) {\r\n const localReply = messages.find(\r\n (candidate) =>\r\n candidate.id === msg.replyto_message_id ||\r\n candidate.sender_message_id === msg.replyto_message_id\r\n )\r\n\r\n if (localReply) {\r\n msg.replyto_message = localReply\r\n msg.replyMetadata = {\r\n replyemoji: msg.replyemoji || null,\r\n replyto_message_id: msg.replyto_message_id,\r\n replyto_user_id: msg.replyto_user_id || null,\r\n parent_message_id: msg.parent_message_id || null,\r\n replyMessageText: localReply.deleted\r\n ? 'Original message unavailable'\r\n : localReply.message_type === 'text'\r\n ? localReply.message || ''\r\n : `Attachment: ${localReply.file_name || 'File'}`,\r\n replySenderName: localReply.sender?.name || 'User',\r\n }\r\n continue\r\n }\r\n\r\n const replyQuery = createQuery()\r\n .select(\r\n `\r\n id,\r\n sender_user_id,\r\n created_at,\r\n message,\r\n message_type,\r\n file_name,\r\n deleted,\r\n sender:profiles!sender_user_id(name)\r\n `\r\n )\r\n .eq('id', msg.replyto_message_id)\r\n .limit(1)\r\n\r\n try {\r\n const replyMsgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${replyQuery.toString()}`\r\n )\r\n const replyMsg = replyMsgs[0] || null\r\n\r\n if (replyMsg) {\r\n msg.replyto_message = {\r\n id: replyMsg.id,\r\n conversation_id: msg.conversation_id,\r\n owner_user_id: msg.owner_user_id,\r\n sender_user_id: replyMsg.sender_user_id,\r\n message: replyMsg.deleted\r\n ? 'Original message unavailable'\r\n : replyMsg.message,\r\n message_type: replyMsg.message_type || 'text',\r\n direction: 'Received',\r\n sent: true,\r\n received: true,\r\n created_at: replyMsg.created_at || msg.created_at,\r\n file_name: replyMsg.file_name || undefined,\r\n thumb: false,\r\n favorite: false,\r\n flag: false,\r\n star: false,\r\n pin: false,\r\n archive: false,\r\n deleted: !!replyMsg.deleted,\r\n action_this: false,\r\n reply: false,\r\n forward: false,\r\n sender: replyMsg.sender\r\n ? {\r\n id: replyMsg.sender_user_id,\r\n name: replyMsg.sender.name || 'User',\r\n email: '',\r\n }\r\n : undefined,\r\n }\r\n msg.replyMetadata = {\r\n replyemoji: msg.replyemoji || null,\r\n replyto_message_id: msg.replyto_message_id,\r\n replyto_user_id: msg.replyto_user_id || null,\r\n parent_message_id: msg.parent_message_id || null,\r\n replyMessageText: replyMsg.deleted\r\n ? 'Original message unavailable'\r\n : replyMsg.message_type === 'text'\r\n ? replyMsg.message\r\n : `Attachment: ${replyMsg.file_name || 'File'}`,\r\n replySenderName: replyMsg.sender?.name || 'User',\r\n }\r\n } else {\r\n msg.replyMetadata = {\r\n replyemoji: msg.replyemoji || null,\r\n replyto_message_id: msg.replyto_message_id,\r\n replyto_user_id: msg.replyto_user_id || null,\r\n parent_message_id: msg.parent_message_id || null,\r\n replyMessageText: 'Original message unavailable',\r\n replySenderName: 'User',\r\n }\r\n }\r\n } catch (e) {\r\n console.warn(\r\n '[Messages API] Failed to fetch reply message details:',\r\n e\r\n )\r\n }\r\n }\r\n }\r\n\r\n return messages\r\n } catch (e) {\r\n console.error('[Messages API] Failed to get conversation messages:', e)\r\n return []\r\n }\r\n}\r\n\r\n/**\r\n * Creates individual message copies for all conversation members.\r\n */\r\nexport async function createMessage(msg: {\r\n id?: string\r\n conversationId: string\r\n senderId: string\r\n message: string\r\n messageType:\r\n | 'text'\r\n | 'image'\r\n | 'video'\r\n | 'audio'\r\n | 'document'\r\n | 'system'\r\n | 'other'\r\n | 'location'\r\n fileUrl?: string\r\n fileName?: string\r\n fileSize?: number\r\n mimeType?: string\r\n duration?: number\r\n thumbnail?: string\r\n replyMetadata?: {\r\n replyemoji?: string | null\r\n replyto_message_id: string\r\n replyto_user_id: string | null\r\n parent_message_id: string | null\r\n }\r\n clientMessageId?: string\r\n fileContentText?: string\r\n fileContentJson?: any\r\n systemMetadata?: {\r\n type: 'group_created' | 'members_added'\r\n groupName?: string\r\n creatorName?: string\r\n }\r\n locationData?: any\r\n locationType?: 'current' | 'live'\r\n}): Promise<Message | null> {\r\n try {\r\n // 1. Get conversation members\r\n const memberQuery = createQuery()\r\n .select('user_id')\r\n .eq('conversation_id', msg.conversationId)\r\n const members = await apiClient.get<any[]>(\r\n `/rest/v1/conversation_members${memberQuery.toString()}`\r\n )\r\n\r\n if (!members || members.length === 0) {\r\n throw new Error('No members found in conversation')\r\n }\r\n\r\n const senderMsgId = msg.id || crypto.randomUUID()\r\n const now = new Date().toISOString()\r\n\r\n // Resolve reply parent message ID if replying\r\n let resolvedReplyToId: string | null = null\r\n if (msg.replyMetadata?.replyto_message_id) {\r\n const parentMsgQuery = createQuery()\r\n .select('id, sender_message_id')\r\n .eq('id', msg.replyMetadata.replyto_message_id)\r\n .limit(1)\r\n\r\n const replyMsgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${parentMsgQuery.toString()}`\r\n )\r\n const replyMsg = replyMsgs[0] || null\r\n resolvedReplyToId = replyMsg\r\n ? replyMsg.sender_message_id || replyMsg.id\r\n : msg.replyMetadata.replyto_message_id\r\n }\r\n\r\n // 2. Map copies for all conversation members\r\n const records: any[] = []\r\n for (const member of members) {\r\n const isSender = member.user_id === msg.senderId\r\n const msgId = isSender ? senderMsgId : crypto.randomUUID()\r\n\r\n let finalMessage = msg.message\r\n\r\n // Customize system message copies based on the recipient's copy context\r\n if (msg.messageType === 'system' && msg.systemMetadata) {\r\n const { type: sysType, groupName, creatorName } = msg.systemMetadata\r\n if (sysType === 'group_created') {\r\n finalMessage = isSender\r\n ? `You created group \"${groupName}\"`\r\n : `${creatorName} created group \"${groupName}\"`\r\n } else if (sysType === 'members_added') {\r\n if (isSender) {\r\n continue\r\n }\r\n finalMessage = `${creatorName} added you`\r\n }\r\n }\r\n\r\n const hasPreparsedData = !!(msg.fileContentText || msg.fileContentJson)\r\n\r\n records.push({\r\n id: msgId,\r\n conversation_id: msg.conversationId,\r\n owner_user_id: member.user_id,\r\n sender_user_id: msg.senderId,\r\n message: finalMessage,\r\n message_type: msg.messageType,\r\n direction: isSender ? 'Sent' : 'Received',\r\n sent: true,\r\n received: isSender,\r\n created_at: now,\r\n\r\n file_url: msg.fileUrl || null,\r\n file_name: msg.fileName || null,\r\n file_size: msg.fileSize || null,\r\n mime_type: msg.mimeType || null,\r\n duration: msg.duration || null,\r\n thumbnail: msg.thumbnail || null,\r\n\r\n thumb: false,\r\n favorite: false,\r\n flag: false,\r\n star: false,\r\n pin: false,\r\n archive: false,\r\n deleted: false,\r\n action_this: false,\r\n reply: !!msg.replyMetadata,\r\n forward: false,\r\n\r\n replyemoji: msg.replyMetadata?.replyemoji || null,\r\n replyto_message_id: resolvedReplyToId,\r\n replyto_user_id: msg.replyMetadata?.replyto_user_id || null,\r\n parent_message_id: msg.replyMetadata?.parent_message_id || null,\r\n\r\n sender_message_id: isSender ? null : senderMsgId,\r\n client_message_id: msg.clientMessageId || null,\r\n message_status: 'sent',\r\n location_data: msg.locationData || null,\r\n location_type: msg.locationType || null,\r\n file_content_text: msg.fileContentText || null,\r\n file_content_json: msg.fileContentJson || null,\r\n processing_status: hasPreparsedData\r\n ? 'completed'\r\n : (msg.messageType === 'document' && (msg.fileName?.toLowerCase().endsWith('.pdf') || msg.mimeType === 'application/pdf'))\r\n ? 'pending'\r\n : null,\r\n })\r\n }\r\n\r\n // 3. Bulk insert copies into the DB\r\n if (records.length > 0) {\r\n await apiClient.post('/rest/v1/chat_messages', records)\r\n \r\n // Trigger background PDF processing if it's a PDF document\r\n const isPdf = msg.messageType === 'document' && (msg.fileName?.toLowerCase().endsWith('.pdf') || msg.mimeType === 'application/pdf');\r\n if (isPdf && typeof window !== 'undefined') {\r\n fetch('/api/process-pdf', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n messageId: senderMsgId,\r\n }),\r\n }).catch((err) => console.warn('[Messages API] Asynchronous PDF processing dispatch error:', err))\r\n }\r\n \r\n // Trigger FCM Push notification to offline/backgrounded recipient devices\r\n if (typeof window !== 'undefined') {\r\n fetch('/api/notifications/push', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n senderId: msg.senderId,\r\n conversationId: msg.conversationId,\r\n message: msg.message,\r\n messageType: msg.messageType,\r\n fileName: msg.fileName,\r\n }),\r\n }).catch((err) => console.warn('[Messages API] Asynchronous FCM push dispatch error:', err))\r\n }\r\n }\r\n\r\n // 4. Return the sender's own copy of the message, constructed locally\r\n const senderRecord = records.find(\r\n (r: any) => r.owner_user_id === msg.senderId\r\n )\r\n if (!senderRecord) return null\r\n\r\n // Load sender details from profiles table to attach to the returned copy\r\n const profileQuery = createQuery()\r\n .select('id, name, email, avatar')\r\n .eq('id', msg.senderId)\r\n .limit(1)\r\n const profiles = await apiClient.get<any[]>(\r\n `/rest/v1/profiles${profileQuery.toString()}`\r\n )\r\n const profile = profiles[0] || null\r\n\r\n return {\r\n id: senderRecord.id,\r\n conversation_id: senderRecord.conversation_id,\r\n owner_user_id: senderRecord.owner_user_id,\r\n sender_user_id: senderRecord.sender_user_id,\r\n message: senderRecord.message,\r\n message_type: senderRecord.message_type,\r\n direction: senderRecord.direction,\r\n sent: senderRecord.sent,\r\n received: senderRecord.received,\r\n created_at: senderRecord.created_at,\r\n file_url: senderRecord.file_url || undefined,\r\n file_name: senderRecord.file_name || undefined,\r\n file_size: senderRecord.file_size\r\n ? Number(senderRecord.file_size)\r\n : undefined,\r\n mime_type: senderRecord.mime_type || undefined,\r\n duration: senderRecord.duration\r\n ? Number(senderRecord.duration)\r\n : undefined,\r\n thumbnail: senderRecord.thumbnail || undefined,\r\n thumb: senderRecord.thumb,\r\n favorite: senderRecord.favorite,\r\n flag: senderRecord.flag,\r\n star: senderRecord.star,\r\n pin: senderRecord.pin,\r\n archive: senderRecord.archive,\r\n deleted: senderRecord.deleted,\r\n action_this: senderRecord.action_this,\r\n reply: senderRecord.reply,\r\n forward: senderRecord.forward,\r\n replyemoji: senderRecord.replyemoji || undefined,\r\n replyto_message_id: senderRecord.replyto_message_id || undefined,\r\n replyto_user_id: senderRecord.replyto_user_id || undefined,\r\n parent_message_id: senderRecord.parent_message_id || undefined,\r\n message_status: senderRecord.message_status,\r\n client_message_id: senderRecord.client_message_id || undefined,\r\n location_data: senderRecord.location_data || undefined,\r\n location_type: senderRecord.location_type || undefined,\r\n file_content_text: senderRecord.file_content_text || undefined,\r\n file_content_json: senderRecord.file_content_json || undefined,\r\n processing_status: senderRecord.processing_status || undefined,\r\n sender: profile\r\n ? {\r\n id: profile.id,\r\n name: profile.name || profile.email.split('@')[0],\r\n email: profile.email,\r\n avatar_url: profile.avatar || undefined,\r\n }\r\n : undefined,\r\n }\r\n } catch (err) {\r\n console.error('[Messages API] Failed to create message copies:', err)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Updates Boolean flags (likes, flags, stars, pin, archive) of a message copy.\r\n */\r\nexport async function updateMessageBooleanAction(\r\n messageId: string,\r\n action:\r\n 'thumb' | 'favorite' | 'flag' | 'star' | 'pin' | 'archive' | 'action_this',\r\n value: boolean\r\n): Promise<boolean> {\r\n try {\r\n const query = createQuery().eq('id', messageId)\r\n await apiClient.patch(`/rest/v1/chat_messages${query.toString()}`, {\r\n [action]: value,\r\n })\r\n return true\r\n } catch (err) {\r\n console.error(\r\n `[Messages API] Failed to update message action ${action}:`,\r\n err\r\n )\r\n return false\r\n }\r\n}\r\n\r\n/** Updates the text on every member copy of a sender-owned text message. */\r\nexport async function editMessage(\r\n messageId: string,\r\n senderId: string,\r\n message: string\r\n): Promise<boolean> {\r\n try {\r\n const fetchQuery = createQuery()\r\n .select('id,sender_user_id,sender_message_id,message_type')\r\n .eq('id', messageId)\r\n .limit(1)\r\n const rows = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${fetchQuery.toString()}`\r\n )\r\n const original = rows[0]\r\n if (\r\n !original ||\r\n original.sender_user_id !== senderId ||\r\n original.message_type !== 'text'\r\n ) {\r\n return false\r\n }\r\n\r\n const senderMessageId = original.sender_message_id || original.id\r\n const updateQuery = createQuery().or(\r\n `id.eq.${senderMessageId},sender_message_id.eq.${senderMessageId}`\r\n )\r\n await apiClient.patch(`/rest/v1/chat_messages${updateQuery.toString()}`, {\r\n message,\r\n })\r\n return true\r\n } catch (error) {\r\n console.error('[Messages API] Failed to edit message:', error)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Soft deletes a message copy for a specific user (delete for me).\r\n */\r\nexport async function deleteMessageForMe(messageId: string): Promise<boolean> {\r\n try {\r\n const query = createQuery().eq('id', messageId)\r\n await apiClient.patch(`/rest/v1/chat_messages${query.toString()}`, {\r\n deleted: true,\r\n })\r\n return true\r\n } catch (err) {\r\n console.error('[Messages API] Failed to delete message for me:', err)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Deletes a message and all its member copies for everyone in a conversation.\r\n */\r\nexport async function deleteMessageForEveryone(\r\n messageId: string,\r\n senderId: string\r\n): Promise<boolean> {\r\n try {\r\n // 1. Fetch message details to find sender_message_id\r\n const fetchQuery = createQuery()\r\n .select('id, sender_user_id, sender_message_id')\r\n .eq('id', messageId)\r\n .limit(1)\r\n const msgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${fetchQuery.toString()}`\r\n )\r\n const msg = msgs[0] || null\r\n\r\n if (!msg) return false\r\n if (msg.sender_user_id !== senderId) {\r\n console.error(\r\n '[Messages API] Only the sender can delete a message for everyone'\r\n )\r\n return false\r\n }\r\n\r\n const senderMsgId = msg.sender_message_id || msg.id\r\n\r\n // 2. Update all message copies associated with this message\r\n const updateQuery = createQuery().or(\r\n `id.eq.${senderMsgId},sender_message_id.eq.${senderMsgId}`\r\n )\r\n await apiClient.patch(`/rest/v1/chat_messages${updateQuery.toString()}`, {\r\n message: 'This message was deleted.',\r\n deleted: true,\r\n deleted_at: new Date().toISOString(),\r\n deleted_by: senderId,\r\n })\r\n\r\n return true\r\n } catch (err) {\r\n console.error('[Messages API] Failed to delete message for everyone:', err)\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * Forwards a message to multiple conversations.\r\n */\r\nexport async function forwardMessage(\r\n messageId: string,\r\n targetConversationIds: string[],\r\n senderId: string\r\n): Promise<boolean> {\r\n try {\r\n // 1. Fetch the original message copy\r\n const fetchQuery = createQuery().select('*').eq('id', messageId).limit(1)\r\n const msgs = await apiClient.get<any[]>(\r\n `/rest/v1/chat_messages${fetchQuery.toString()}`\r\n )\r\n const original = msgs[0] || null\r\n\r\n if (!original) return false\r\n\r\n // 2. Loop and duplicate message copies for target conversations\r\n for (const conversationId of targetConversationIds) {\r\n const memberQuery = createQuery()\r\n .select('user_id')\r\n .eq('conversation_id', conversationId)\r\n const members = await apiClient.get<any[]>(\r\n `/rest/v1/conversation_members${memberQuery.toString()}`\r\n )\r\n\r\n if (!members || members.length === 0) continue\r\n\r\n const senderMsgId = crypto.randomUUID()\r\n const now = new Date().toISOString()\r\n\r\n const records = members.map((member: any) => {\r\n const isSender = member.user_id === senderId\r\n const msgId = isSender ? senderMsgId : crypto.randomUUID()\r\n return {\r\n id: msgId,\r\n conversation_id: conversationId,\r\n owner_user_id: member.user_id,\r\n sender_user_id: senderId,\r\n message: original.message,\r\n message_type: original.message_type,\r\n direction: isSender ? 'Sent' : 'Received',\r\n sent: true,\r\n received: isSender,\r\n created_at: now,\r\n\r\n file_url: original.file_url || null,\r\n file_name: original.file_name || null,\r\n file_size: original.file_size || null,\r\n mime_type: original.mime_type || null,\r\n duration: original.duration || null,\r\n thumbnail: original.thumbnail || null,\r\n location_data: original.location_data || null,\r\n location_type: original.location_type || null,\r\n\r\n thumb: false,\r\n favorite: false,\r\n flag: false,\r\n star: false,\r\n pin: false,\r\n archive: false,\r\n deleted: false,\r\n action_this: false,\r\n reply: false,\r\n forward: true,\r\n\r\n forwardto_message_id: original.id,\r\n forwardto_user_id: original.sender_user_id,\r\n\r\n sender_message_id: isSender ? null : senderMsgId,\r\n }\r\n })\r\n\r\n await apiClient.post('/rest/v1/chat_messages', records)\r\n }\r\n\r\n return true\r\n } catch (err) {\r\n console.error('[Messages API] Failed to forward message:', err)\r\n return false\r\n }\r\n}\r\n","import * as messagesApi from '../api/messages.api'\nimport { Message } from '../types/chat.types'\n\nexport async function getConversationMessages(\n conversationId: string,\n userId: string,\n options?: { limit?: number; before?: string }\n): Promise<Message[]> {\n return messagesApi.getConversationMessages(conversationId, userId, options)\n}\n\nexport async function createMessage(msg: {\n id?: string\n conversationId: string\n senderId: string\n message: string\n messageType:\n | 'text'\n | 'image'\n | 'video'\n | 'audio'\n | 'document'\n | 'system'\n | 'other'\n | 'location'\n fileUrl?: string\n fileName?: string\n fileSize?: number\n mimeType?: string\n duration?: number\n thumbnail?: string\n replyMetadata?: {\n replyemoji?: string | null\n replyto_message_id: string\n replyto_user_id: string | null\n parent_message_id: string | null\n }\n clientMessageId?: string\n fileContentText?: string\n fileContentJson?: any\n systemMetadata?: {\n type: 'group_created' | 'members_added'\n groupName?: string\n creatorName?: string\n }\n locationData?: any\n locationType?: 'current' | 'live'\n}): Promise<Message | null> {\n return messagesApi.createMessage(msg)\n}\n\nexport async function updateMessageBooleanAction(\n messageId: string,\n action:\n 'thumb' | 'favorite' | 'flag' | 'star' | 'pin' | 'archive' | 'action_this',\n value: boolean\n): Promise<boolean> {\n return messagesApi.updateMessageBooleanAction(messageId, action, value)\n}\n\nexport async function editMessage(\n messageId: string,\n senderId: string,\n message: string\n): Promise<boolean> {\n return messagesApi.editMessage(messageId, senderId, message)\n}\n\nexport async function deleteMessageForMe(messageId: string): Promise<boolean> {\n return messagesApi.deleteMessageForMe(messageId)\n}\n\nexport async function deleteMessageForEveryone(\n messageId: string,\n senderId: string\n): Promise<boolean> {\n return messagesApi.deleteMessageForEveryone(messageId, senderId)\n}\n\nexport async function forwardMessage(\n messageId: string,\n targetConversationIds: string[],\n senderId: string\n): Promise<boolean> {\n return messagesApi.forwardMessage(messageId, targetConversationIds, senderId)\n}\n","export interface DBAlertConfig {\r\n groupName: string\r\n groupImage?: string\r\n adminEmails: string[]\r\n}\r\n\r\nexport type DBAlertType = 'contact' | 'group'\r\nexport type DBAlertAction = 'create' | 'update' | 'delete'\r\n\r\nexport const DB_ALERTS_CONFIG: DBAlertConfig = {\r\n groupName: 'DB Alerts',\r\n groupImage: 'https://images.unsplash.com/photo-1598257006458-087169a1f08d?w=128&h=128&fit=crop&auto=format&q=80',\r\n adminEmails: [\r\n 'itsaman00786@gmail.com',\r\n 'amanmicropay@gmail.com',\r\n 'n.rajukrishna@gmail.com'\r\n ],\r\n}\r\n","import { createClient } from '../lib/supabase/client'\r\nimport { createMessage } from '../features/chattemplate/chat/repositories/message-repository'\r\nimport { DB_ALERTS_CONFIG, DBAlertAction } from '../lib/db-alerts/types/db-alert'\r\n\r\n// Helper to format date like \"15 Jul 2026 10:45 AM\"\r\nfunction formatAlertTime(date: Date): string {\r\n const day = date.getDate()\r\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\r\n const month = months[date.getMonth()]\r\n const year = date.getFullYear()\r\n let hours = date.getHours()\r\n const minutes = date.getMinutes().toString().padStart(2, '0')\r\n const ampm = hours >= 12 ? 'AM' : 'PM'\r\n hours = hours % 12\r\n hours = hours ? hours : 12 // the hour '0' should be '12'\r\n return `${day} ${month} ${year} ${hours}:${minutes} ${ampm}`\r\n}\r\n\r\n/**\r\n * Returns the conversation ID of the DB Alerts group conversation.\r\n * Creates it if it doesn't already exist.\r\n */\r\nexport async function getOrCreateAlertConversation(): Promise<string | null> {\r\n const supabase = createClient()\r\n try {\r\n // 1. Look for existing group conversation named \"DB Alerts\"\r\n const { data: existing, error } = await supabase\r\n .from('conversations')\r\n .select('id')\r\n .eq('name', DB_ALERTS_CONFIG.groupName)\r\n .eq('type', 'group')\r\n .maybeSingle()\r\n\r\n if (error) throw error\r\n if (existing) return existing.id\r\n\r\n // 2. Not found - create conversation\r\n const { data: newConvo, error: createError } = await supabase\r\n .from('conversations')\r\n .insert({\r\n type: 'group',\r\n name: DB_ALERTS_CONFIG.groupName,\r\n image: DB_ALERTS_CONFIG.groupImage || null,\r\n })\r\n .select('id')\r\n .single()\r\n\r\n if (createError) throw createError\r\n if (!newConvo) return null\r\n\r\n // 3. Add configured administrators as members\r\n const { data: adminProfiles, error: profilesError } = await supabase\r\n .from('profiles')\r\n .select('id, email')\r\n .in('email', DB_ALERTS_CONFIG.adminEmails)\r\n\r\n if (profilesError) throw profilesError\r\n\r\n if (adminProfiles && adminProfiles.length > 0) {\r\n const membersToInsert = adminProfiles.map((p: { id: string; email?: string | null }) => ({\r\n conversation_id: newConvo.id,\r\n user_id: p.id,\r\n role: 'member',\r\n }))\r\n\r\n const { error: insertError } = await supabase\r\n .from('conversation_members')\r\n .insert(membersToInsert)\r\n\r\n if (insertError) {\r\n console.error('[DB Alerts] Failed to insert initial admin members:', insertError)\r\n } else {\r\n console.log(`[DB Alerts] Created group with ${adminProfiles.length} configured admin members`)\r\n }\r\n }\r\n\r\n return newConvo.id\r\n } catch (err) {\r\n console.error('[DB Alerts] Error in getOrCreateAlertConversation:', err)\r\n return null\r\n }\r\n}\r\n\r\nconst subscribedUsers = new Set<string>()\r\n\r\n/**\r\n * Automatically subscribes a user to the DB Alerts conversation if their email matches\r\n * the admin emails configuration list.\r\n */\r\nexport async function autoSubscribeAdmin(userId: string, email: string): Promise<void> {\r\n if (subscribedUsers.has(userId)) return\r\n subscribedUsers.add(userId)\r\n\r\n const emailLower = email.trim().toLowerCase()\r\n const isAdmin = DB_ALERTS_CONFIG.adminEmails.some(\r\n (adminEmail) => adminEmail.toLowerCase() === emailLower\r\n )\r\n\r\n if (!isAdmin) return\r\n\r\n const supabase = createClient()\r\n try {\r\n const convoId = await getOrCreateAlertConversation()\r\n if (!convoId) return\r\n\r\n // Check if already a member\r\n const { data: existing, error: checkError } = await supabase\r\n .from('conversation_members')\r\n .select('id')\r\n .eq('conversation_id', convoId)\r\n .eq('user_id', userId)\r\n .maybeSingle()\r\n\r\n if (checkError) throw checkError\r\n\r\n if (!existing) {\r\n const { error: insertError } = await supabase\r\n .from('conversation_members')\r\n .insert({\r\n conversation_id: convoId,\r\n user_id: userId,\r\n role: 'member',\r\n })\r\n\r\n if (insertError) throw insertError\r\n console.log(`[DB Alerts] Auto-subscribed admin user: ${email} (${userId})`)\r\n }\r\n\r\n // Ensure there is at least one message to make the conversation visible in the sidebar list.\r\n // If the user has no message copy, insert the initialization system message copy.\r\n const { data: msgs, error: msgsError } = await supabase\r\n .from('chat_messages')\r\n .select('id')\r\n .eq('conversation_id', convoId)\r\n .eq('owner_user_id', userId)\r\n .limit(1)\r\n\r\n if (!msgsError && (!msgs || msgs.length === 0)) {\r\n await createMessage({\r\n conversationId: convoId,\r\n senderId: userId,\r\n message: '🚨 DB Alerts Channel Initialized. Administrative events will be logged here.',\r\n messageType: 'system',\r\n })\r\n console.log(`[DB Alerts] Initialized message feed for user: ${userId}`)\r\n }\r\n } catch (err) {\r\n console.error('[DB Alerts] Failed to auto-subscribe admin user:', err)\r\n }\r\n}\r\n\r\n/**\r\n * Triggers a DB Alert message for contact operations.\r\n */\r\nexport async function triggerContactAlert(\r\n action: DBAlertAction,\r\n ownerId: string,\r\n contactUserId: string\r\n): Promise<void> {\r\n const supabase = createClient()\r\n try {\r\n const convoId = await getOrCreateAlertConversation()\r\n if (!convoId) return\r\n\r\n // 1. Resolve owner display name\r\n const { data: ownerProfile } = await supabase\r\n .from('profiles')\r\n .select('name, email')\r\n .eq('id', ownerId)\r\n .maybeSingle()\r\n\r\n const ownerName = ownerProfile?.name || ownerProfile?.email?.split('@')[0] || 'System'\r\n\r\n // 2. Resolve contact display name\r\n const { data: contactProfile } = await supabase\r\n .from('profiles')\r\n .select('name, email')\r\n .eq('id', contactUserId)\r\n .maybeSingle()\r\n\r\n const contactName = contactProfile?.name || contactProfile?.email?.split('@')[0] || 'System'\r\n\r\n // 3. Format message based on action\r\n const timeStr = formatAlertTime(new Date())\r\n let formattedMessage = ''\r\n\r\n if (action === 'create') {\r\n formattedMessage = `Contact Created\\n🟢 Contact Added\\nBy: ${ownerName}\\nContact: ${contactName}\\nTime: ${timeStr}`\r\n } else if (action === 'delete') {\r\n formattedMessage = `Contact Deleted\\n🔴 Contact Deleted\\nBy: ${ownerName}\\nContact: ${contactName}`\r\n } else if (action === 'update') {\r\n formattedMessage = `Contact Updated\\n🟡 Contact Updated\\nBy: ${ownerName}\\nContact: ${contactName}\\nTime: ${timeStr}`\r\n }\r\n\r\n // 4. Send message copy to DB Alerts conversation\r\n await createMessage({\r\n conversationId: convoId,\r\n senderId: ownerId,\r\n message: formattedMessage,\r\n messageType: 'system',\r\n })\r\n } catch (err) {\r\n console.error('[DB Alerts] Failed to trigger contact alert:', err)\r\n }\r\n}\r\n\r\n/**\r\n * Triggers a DB Alert message for group operations.\r\n */\r\nexport async function triggerGroupAlert(\r\n action: DBAlertAction,\r\n actorId: string,\r\n groupName: string\r\n): Promise<void> {\r\n const supabase = createClient()\r\n try {\r\n const convoId = await getOrCreateAlertConversation()\r\n if (!convoId) return\r\n\r\n // 1. Resolve actor display name\r\n let actorName = 'System'\r\n if (actorId) {\r\n const { data: actorProfile } = await supabase\r\n .from('profiles')\r\n .select('name, email')\r\n .eq('id', actorId)\r\n .maybeSingle()\r\n\r\n actorName = actorProfile?.name || actorProfile?.email?.split('@')[0] || 'System'\r\n }\r\n\r\n // 2. Format message based on action\r\n let formattedMessage = ''\r\n\r\n if (action === 'create') {\r\n formattedMessage = `Group Created\\n🟢 Group Created\\nBy: ${actorName}\\nGroup: ${groupName}`\r\n } else if (action === 'delete') {\r\n formattedMessage = `Group Deleted\\n🔴 Group Deleted\\nBy: ${actorName}\\nGroup: ${groupName}`\r\n } else if (action === 'update') {\r\n formattedMessage = `Group Updated\\n🟡 Group Updated\\nBy: ${actorName}\\nGroup: ${groupName}`\r\n }\r\n\r\n // 3. Send message copy to DB Alerts conversation\r\n // Actor ID can fallback to any member of DB Alerts or active user if actorId is empty\r\n let senderId = actorId\r\n if (!senderId) {\r\n // Find a member of the conversation to act as sender (must satisfy RLS that sender is member)\r\n const { data: firstMember } = await supabase\r\n .from('conversation_members')\r\n .select('user_id')\r\n .eq('conversation_id', convoId)\r\n .limit(1)\r\n .maybeSingle()\r\n\r\n senderId = firstMember?.user_id || ''\r\n }\r\n\r\n if (!senderId) {\r\n console.warn('[DB Alerts] Cannot send alert because no valid sender ID is available.')\r\n return\r\n }\r\n\r\n await createMessage({\r\n conversationId: convoId,\r\n senderId: senderId,\r\n message: formattedMessage,\r\n messageType: 'system',\r\n })\r\n } catch (err) {\r\n console.error('[DB Alerts] Failed to trigger group alert:', err)\r\n }\r\n}\r\n","import { SharedFileMetadata } from '@/types/file'\r\n\r\nfunction parseFileUrl(fileUrl: string): { bucket: string; storagePath: string } {\r\n try {\r\n const url = new URL(fileUrl)\r\n const pathParts = url.pathname.split('/')\r\n // Path: /storage/v1/object/public/chat-files/documents/file.pdf\r\n const publicIndex = pathParts.indexOf('public')\r\n if (publicIndex !== -1 && publicIndex + 2 < pathParts.length) {\r\n const bucket = pathParts[publicIndex + 1]\r\n const storagePath = pathParts.slice(publicIndex + 2).join('/')\r\n return { bucket, storagePath }\r\n }\r\n const authenticatedIndex = pathParts.indexOf('authenticated')\r\n if (authenticatedIndex !== -1 && authenticatedIndex + 2 < pathParts.length) {\r\n const bucket = pathParts[authenticatedIndex + 1]\r\n const storagePath = pathParts.slice(authenticatedIndex + 2).join('/')\r\n return { bucket, storagePath }\r\n }\r\n const chatFilesIndex = pathParts.indexOf('chat-files')\r\n if (chatFilesIndex !== -1 && chatFilesIndex + 1 < pathParts.length) {\r\n return { bucket: 'chat-files', storagePath: pathParts.slice(chatFilesIndex + 1).join('/') }\r\n }\r\n } catch (e) {\r\n console.error('Failed to parse file URL:', e)\r\n }\r\n return { bucket: 'chat-files', storagePath: '' }\r\n}\r\n\r\nexport async function getSharedFileMetadata(\r\n supabase: any,\r\n fileId: string,\r\n userId: string\r\n): Promise<{ success: boolean; data?: SharedFileMetadata; error?: string }> {\r\n console.log(`[DEBUG server] getSharedFileMetadata → fileId: ${fileId}, userId: ${userId}`)\r\n \r\n try {\r\n const { data, error } = await supabase\r\n .rpc('get_shared_file_metadata', { p_file_id: fileId, p_user_id: userId })\r\n .maybeSingle()\r\n\r\n if (error) {\r\n console.error(`[DEBUG server] RPC error:`, error.message, error)\r\n return { success: false, error: error.message }\r\n }\r\n\r\n if (!data) {\r\n console.warn(`[DEBUG server] RPC returned null → file not found or user is not a conversation member. fileId: ${fileId}, userId: ${userId}`)\r\n return { success: false, error: 'File not found or unauthorized' }\r\n }\r\n\r\n console.log(`[DEBUG server] RPC success → conversationId: ${data.out_conversation_id}, file: ${data.out_file_name}`)\r\n\r\n const parsed = parseFileUrl(data.out_file_url)\r\n return {\r\n success: true,\r\n data: {\r\n id: fileId,\r\n bucket: parsed.bucket,\r\n storagePath: parsed.storagePath,\r\n fileName: data.out_file_name || 'document',\r\n fileSize: data.out_file_size,\r\n mimeType: data.out_mime_type || 'application/octet-stream',\r\n conversationId: data.out_conversation_id,\r\n ownerId: data.out_sender_user_id || '',\r\n createdAt: data.out_created_at,\r\n },\r\n }\r\n } catch (err: any) {\r\n console.error(`[DEBUG server] Exception in getSharedFileMetadata:`, err.message || err)\r\n return { success: false, error: err.message || 'Internal server error' }\r\n }\r\n}\r\n\r\nexport async function generateSignedUrl(\r\n supabase: any,\r\n bucket: string,\r\n storagePath: string\r\n): Promise<string> {\r\n const { data, error } = await supabase.storage\r\n .from(bucket)\r\n .createSignedUrl(storagePath, 60) // 1 minute expiry\r\n\r\n if (error || !data) {\r\n throw new Error(error?.message || 'Failed to generate signed URL')\r\n }\r\n\r\n return data.signedUrl\r\n}\r\n","/**\n * @file createPdf.ts\n * @description Client-Side PDF Generation using `pdf-lib`.\n * \n * WHY IT EXISTS:\n * Compiles single or multi-page scanned document images into a clean, compact, standard PDF document.\n * \n * WHAT IT DOES:\n * Takes an array of processed `ScanPage` objects, embeds JPEG image streams into `PDFDocument` pages\n * configured for A4 or Original page size and Portrait or Landscape orientation.\n * \n * WHEN IT RUNS:\n * Executed when user completes editing and moves to the PDF Preview or Send step.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Used by `src/services/pdf.service.ts` & `src/hooks/usePDF.ts`\n * - Uses types from `src/types/scanner.ts`\n * - Uses `compress.ts` for JPEG byte encoding\n * \n * WHO CALLS IT: `pdf.service.ts`\n * WHO DEPENDS ON IT: PDF document generation pipeline.\n */\n\nimport { PDFDocument, PageSizes } from 'pdf-lib'\nimport { ScanPage, PdfGenerationOptions } from '../../types/scanner'\nimport { compressCanvasToJpeg, dataUrlToUint8Array } from '../scanner/compress'\n\n/**\n * Generate a PDF document from an array of processed scan pages.\n * \n * @param pages Array of ScanPage items\n * @param options PDF settings (A4 / Original, Portrait / Landscape, Compression quality)\n * @returns Uint8Array containing compiled PDF byte stream\n */\nexport async function createPdfFromScanPages(\n pages: ScanPage[],\n options: PdfGenerationOptions = {}\n): Promise<Uint8Array> {\n const {\n paperSize = 'a4',\n orientation = 'portrait',\n quality = 0.85,\n } = options\n\n const pdfDoc = await PDFDocument.create()\n\n for (const page of pages) {\n // Render image to canvas to apply rotation\n const imgCanvas = await renderPageToRotatedCanvas(page)\n const jpegDataUrl = compressCanvasToJpeg(imgCanvas, 2000, quality)\n const imageBytes = dataUrlToUint8Array(jpegDataUrl)\n\n const embeddedImage = await pdfDoc.embedJpg(imageBytes)\n\n let pageWidth = embeddedImage.width\n let pageHeight = embeddedImage.height\n\n if (paperSize === 'a4') {\n const a4 = PageSizes.A4 // [595.28, 841.89]\n if (orientation === 'landscape') {\n pageWidth = a4[1]\n pageHeight = a4[0]\n } else {\n pageWidth = a4[0]\n pageHeight = a4[1]\n }\n } else if (orientation === 'landscape' && pageWidth < pageHeight) {\n const tmp = pageWidth\n pageWidth = pageHeight\n pageHeight = tmp\n }\n\n const pdfPage = pdfDoc.addPage([pageWidth, pageHeight])\n\n // Scale image proportionally to fit PDF page box\n const scale = Math.min(\n pageWidth / embeddedImage.width,\n pageHeight / embeddedImage.height\n )\n\n const drawW = embeddedImage.width * scale\n const drawH = embeddedImage.height * scale\n const drawX = (pageWidth - drawW) / 2\n const drawY = (pageHeight - drawH) / 2\n\n pdfPage.drawImage(embeddedImage, {\n x: drawX,\n y: drawY,\n width: drawW,\n height: drawH,\n })\n }\n\n return await pdfDoc.save()\n}\n\n/**\n * Helper to load page image URL and apply page rotation onto a temporary canvas.\n */\nasync function renderPageToRotatedCanvas(page: ScanPage): Promise<HTMLCanvasElement> {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => {\n const rot = (page.rotation % 360 + 360) % 360\n const is90or270 = rot === 90 || rot === 270\n\n const w = is90or270 ? img.height : img.width\n const h = is90or270 ? img.width : img.height\n\n const canvas = document.createElement('canvas')\n canvas.width = w\n canvas.height = h\n\n const ctx = canvas.getContext('2d')\n if (!ctx) {\n reject(new Error('Failed to get 2d context for PDF page render'))\n return\n }\n\n ctx.translate(w / 2, h / 2)\n ctx.rotate((rot * Math.PI) / 180)\n ctx.drawImage(img, -img.width / 2, -img.height / 2)\n\n resolve(canvas)\n }\n img.onerror = (err) => reject(err)\n img.src = page.processedUrl || page.originalUrl\n })\n}\n","/**\n * @file scanner.ts\n * @description Master constants and defaults for Document Scanning & PDF processing.\n * \n * WHY IT EXISTS:\n * Centralizes defaults, UI filter metadata, image compression presets, OpenCV CDN URLs,\n * max page limits, and A4 page dimensions to prevent magic numbers across the app.\n * \n * WHAT IT DOES:\n * Provides ready-to-use configuration constants used by services, hooks, and components.\n * \n * WHEN IT RUNS:\n * Imported at startup by scanner modules.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * Used by:\n * - `src/services/opencv.service.ts`\n * - `src/services/scanner.service.ts`\n * - `src/services/pdf.service.ts`\n * - `src/hooks/*`\n * - `src/components/scanner/*`\n * \n * WHO CALLS IT: Scanner components and services.\n * WHO DEPENDS ON IT: Document scanner configuration pipeline.\n */\n\nimport { ScanFilterMode, PaperSize, PaperOrientation } from '../types/scanner'\n\n/** OpenCV.js CDN script URL (fast jsDelivr build) */\nexport const OPENCV_CDN_URL = 'https://cdn.jsdelivr.net/npm/@techstark/opencv-js@4.9.0-release.3/dist/opencv.js'\n\n/** Max number of pages allowed per document scan session */\nexport const MAX_SCAN_PAGES = 30\n\n/** Target JPEG compression quality for PDF page embedding (0.1 to 1.0) */\nexport const DEFAULT_JPEG_QUALITY = 0.85\n\n/** A4 Dimensions in points (1 pt = 1/72 inch) */\nexport const A4_WIDTH_PTS = 595.28\nexport const A4_HEIGHT_PTS = 841.89\n\n/** Default paper size setting */\nexport const DEFAULT_PAPER_SIZE: PaperSize = 'a4'\n\n/** Default paper orientation setting */\nexport const DEFAULT_ORIENTATION: PaperOrientation = 'portrait'\n\n/** Filter metadata UI options */\nexport const SCAN_FILTERS: { id: ScanFilterMode; label: string; description: string }[] = [\n {\n id: 'enhanced',\n label: 'Auto Enhance',\n description: 'Boost contrast, sharpen document text, and remove background shadows',\n },\n {\n id: 'bw',\n label: 'Black & White',\n description: 'High contrast binary monochrome ideal for printed documents & receipts',\n },\n {\n id: 'grayscale',\n label: 'Grayscale',\n description: 'Clean 8-bit gray gradient preserving soft shading',\n },\n {\n id: 'original',\n label: 'Original',\n description: 'Unfiltered raw photo as captured',\n },\n]\n\n/** Default crop padding inset ratio when auto edge detection fails */\nexport const FALLBACK_CROP_MARGIN_RATIO = 0.05\n","/**\n * @file compress.ts\n * @description Image Compression & Dimension Downscaling Helper.\n * \n * WHY IT EXISTS:\n * Prevents memory leaks and bloated PDF file sizes by downscaling ultra-high-resolution photo captures\n * (e.g. 12MP 4000x3000 camera shots) down to standard document resolution (max 2000px edge).\n * \n * WHAT IT DOES:\n * Scales down an HTMLCanvasElement or ImageData URL while preserving aspect ratio, returning an optimized JPEG Blob/DataURL.\n * \n * WHEN IT RUNS:\n * Executed before embedding processed pages into PDF documents or uploading files.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Used by `src/services/pdf.service.ts` & `src/utils/pdf/createPdf.ts`\n * \n * WHO CALLS IT: `pdf.service.ts`\n * WHO DEPENDS ON IT: PDF size optimization pipeline.\n */\n\nimport { DEFAULT_JPEG_QUALITY } from '../../constants/scanner'\n\n/**\n * Resize canvas to max bounding dimension and convert to JPEG Data URL.\n * \n * @param sourceCanvas HTMLCanvasElement to compress\n * @param maxDimension Max width/height in pixels (default 2000px)\n * @param quality JPEG quality ratio 0.1 to 1.0 (default 0.85)\n * @returns Compressed JPEG Data URL\n */\nexport function compressCanvasToJpeg(\n sourceCanvas: HTMLCanvasElement,\n maxDimension = 2000,\n quality = DEFAULT_JPEG_QUALITY\n): string {\n let width = sourceCanvas.width\n let height = sourceCanvas.height\n\n if (width > maxDimension || height > maxDimension) {\n if (width > height) {\n height = Math.round((height * maxDimension) / width)\n width = maxDimension\n } else {\n width = Math.round((width * maxDimension) / height)\n height = maxDimension\n }\n }\n\n const outCanvas = document.createElement('canvas')\n outCanvas.width = width\n outCanvas.height = height\n\n const ctx = outCanvas.getContext('2d')\n if (ctx) {\n ctx.imageSmoothingEnabled = true\n ctx.imageSmoothingQuality = 'high'\n ctx.drawImage(sourceCanvas, 0, 0, width, height)\n }\n\n return outCanvas.toDataURL('image/jpeg', quality)\n}\n\n/**\n * Convert a Data URL string into a Uint8Array binary array buffer.\n */\nexport function dataUrlToUint8Array(dataUrl: string): Uint8Array {\n const base64Str = dataUrl.split(',')[1] || dataUrl\n const binaryStr = atob(base64Str)\n const len = binaryStr.length\n const bytes = new Uint8Array(len)\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryStr.charCodeAt(i)\n }\n return bytes\n}\n","/**\n * @file pdf.service.ts\n * @description Master PDF Generation & Blob Management Service.\n * \n * WHY IT EXISTS:\n * Handles multi-page PDF generation, blob URL lifecycle management, and page count/byte size calculation.\n * \n * WHAT IT DOES:\n * Delegates page compilation to `createPdfFromScanPages`, generates object URLs for PDF previews,\n * and releases blob URLs when preview dialogs close to avoid browser memory leaks.\n * \n * WHEN IT RUNS:\n * Run when user clicks \"Generate PDF\" or opens the PDF preview stage in the Document Scanner.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Uses `createPdfFromScanPages` from `src/utils/pdf/createPdf.ts`\n * - Uses types from `src/types/scanner.ts`\n * \n * WHO CALLS IT: `usePDF.ts` & `PDFPreview.tsx`\n * WHO DEPENDS ON IT: PDF preview and upload submission pipeline.\n */\n\nimport { ScanPage, PdfGenerationOptions } from '../types/scanner'\nimport { createPdfFromScanPages } from '../utils/pdf/createPdf'\n\nclass PdfService {\n /**\n * Generate a PDF Uint8Array buffer from scan pages.\n * \n * @param pages Array of ScanPage items\n * @param options PDF compilation parameters\n * @returns Uint8Array PDF byte stream\n */\n public async generatePdfBuffer(\n pages: ScanPage[],\n options: PdfGenerationOptions = {}\n ): Promise<Uint8Array> {\n if (!pages || pages.length === 0) {\n throw new Error('Cannot generate PDF with 0 pages')\n }\n return await createPdfFromScanPages(pages, options)\n }\n\n /**\n * Create a File object containing PDF binary data ready for uploading.\n * \n * @param pages Array of ScanPage items\n * @param filename Desired PDF filename\n * @param options PDF settings\n * @returns Promise resolving to a File instance (`application/pdf`)\n */\n public async generatePdfFile(\n pages: ScanPage[],\n filename = `scanned_doc_${Date.now()}.pdf`,\n options: PdfGenerationOptions = {}\n ): Promise<File> {\n const pdfBuffer = await this.generatePdfBuffer(pages, options)\n const cleanFilename = filename.endsWith('.pdf') ? filename : `${filename}.pdf`\n\n return new File([pdfBuffer.buffer as ArrayBuffer], cleanFilename, { type: 'application/pdf' })\n }\n\n /**\n * Create a browser Blob preview URL from PDF bytes.\n * \n * @param pdfBuffer Uint8Array PDF data\n * @returns Object URL string (`blob:http...`)\n */\n public createPdfPreviewUrl(pdfBuffer: Uint8Array): string {\n const blob = new Blob([pdfBuffer.buffer as ArrayBuffer], { type: 'application/pdf' })\n return URL.createObjectURL(blob)\n }\n\n /**\n * Revoke blob URL to release browser memory.\n * \n * @param url Blob URL to revoke\n */\n public revokePdfPreviewUrl(url: string): void {\n if (url && url.startsWith('blob:')) {\n URL.revokeObjectURL(url)\n }\n }\n}\n\nexport const pdfService = new PdfService()\n","/**\n * @file opencv.service.ts\n * @description Asynchronous OpenCV.js Loader & Resource Disposal Manager.\n * \n * WHY IT EXISTS:\n * Controls the lazy-loading, initialization, and safe memory teardown of OpenCV.js (~8MB WebAssembly script)\n * to ensure maximum performance, zero initial page bundle bloat, and no browser memory leaks.\n * \n * WHAT IT DOES:\n * Loads OpenCV.js dynamically via script tag injection, polls `cv.onRuntimeInitialized`, exposes initialization status,\n * and provides safe helper utilities for matrix disposal.\n * \n * WHEN IT RUNS:\n * Initiated on demand when the document scanner modal is opened or edge detection is requested.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Used by `src/hooks/useOpenCV.ts` & `src/services/scanner.service.ts`\n * - Loads script from `src/constants/scanner.ts` OPENCV_CDN_URL\n * \n * WHO CALLS IT: `useOpenCV.ts`, `scanner.service.ts`\n * WHO DEPENDS ON IT: Smart Document Scanning edge detection & perspective engine.\n */\n\nimport { OPENCV_CDN_URL } from '../constants/scanner'\n\nclass OpenCVService {\n private static instance: OpenCVService\n private isLoaded = false\n private isLoading = false\n private loadPromise: Promise<any> | null = null\n\n private constructor() {}\n\n /**\n * Singleton instance accessor.\n */\n public static getInstance(): OpenCVService {\n if (!OpenCVService.instance) {\n OpenCVService.instance = new OpenCVService()\n }\n return OpenCVService.instance\n }\n\n /**\n * Check if OpenCV.js is fully loaded and ready for execution.\n */\n public isReady(): boolean {\n return this.isLoaded && typeof window !== 'undefined' && !!(window as any).cv\n }\n\n /**\n * Lazy load OpenCV.js script asynchronously with single-flight promise deduplication.\n */\n public loadOpenCV(): Promise<any> {\n console.log('[OpenCV] OpenCV script load disabled to ensure main-thread responsiveness. Falling back to native Canvas engine.')\n return Promise.resolve(null)\n }\n\n /**\n * Safely dispose an array of OpenCV.js Mat structures to prevent WebAssembly memory leaks.\n */\n public safeDelete(...mats: any[]): void {\n for (const mat of mats) {\n if (mat && typeof mat.delete === 'function') {\n try {\n mat.delete()\n } catch (e) {\n // Ignore deletion errors on already released mats\n }\n }\n }\n }\n}\n\nexport const opencvService = OpenCVService.getInstance()\n","/**\n * @file detectEdges.ts\n * @description Smart Document Edge & Quad Contour Detection algorithm.\n * \n * WHY IT EXISTS:\n * Automatically locates document/paper boundaries in captured photos or uploaded files,\n * eliminating the need for manual cropping on clear document images.\n * \n * WHAT IT DOES:\n * Analyzes image pixels using OpenCV (Canny edge detection + Contour approximation) or\n * a high-performance Canvas fallback algorithm to extract the 4 corner points of paper.\n * \n * WHEN IT RUNS:\n * Executed immediately when an image is ingested into the document scanner pipeline.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Called by `src/services/scanner.service.ts` & `src/hooks/useDocumentScanner.ts`\n * - Uses types from `src/types/scanner.ts`\n * - Returns `CropQuad` coordinates consumed by `perspective.ts` and `CropOverlay.tsx`.\n * \n * WHO CALLS IT: `scanner.service.ts`\n * WHO DEPENDS ON IT: Document cropping and perspective transformation workflow.\n */\n\nimport { CropQuad, Point } from '../../types/scanner'\nimport { FALLBACK_CROP_MARGIN_RATIO } from '../../constants/scanner'\n\n/**\n * Detect paper edge boundaries in an HTMLImageElement or HTMLCanvasElement.\n * Returns normalized (0..1) corner coordinates for responsive overlay positioning.\n * \n * @param imageSource Canvas or Image element containing document frame\n * @param cv OpenCV.js instance if loaded (optional)\n * @returns CropQuad with normalized 4 corners (0..1 range)\n */\nexport function detectDocumentEdges(\n imageSource: HTMLImageElement | HTMLCanvasElement,\n cv?: any\n): CropQuad {\n const width = 'naturalWidth' in imageSource ? imageSource.naturalWidth : imageSource.width\n const height = 'naturalHeight' in imageSource ? imageSource.naturalHeight : imageSource.height\n\n const fallbackQuad: CropQuad = getFallbackCropQuad()\n\n if (!width || !height) return fallbackQuad\n\n // Try OpenCV edge detection if available\n if (cv && cv.Mat) {\n try {\n const srcMat = cv.imread(imageSource)\n const grayMat = new cv.Mat()\n const blurMat = new cv.Mat()\n const cannyMat = new cv.Mat()\n\n // Convert to grayscale & blur to remove fine noise\n cv.cvtColor(srcMat, grayMat, cv.COLOR_RGBA2GRAY, 0)\n cv.GaussianBlur(grayMat, blurMat, new cv.Size(5, 5), 0, 0, cv.BORDER_DEFAULT)\n cv.Canny(blurMat, cannyMat, 75, 200)\n\n // Find contours\n const contours = new cv.MatVector()\n const hierarchy = new cv.Mat()\n cv.findContours(cannyMat, contours, hierarchy, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)\n\n let maxArea = 0\n let maxContour: any = null\n\n for (let i = 0; i < contours.size(); ++i) {\n const cnt = contours.get(i)\n const area = cv.contourArea(cnt)\n\n // Must take at least 15% of frame area to be considered a document paper\n if (area > (width * height * 0.15)) {\n const peri = cv.arcLength(cnt, true)\n const approx = new cv.Mat()\n cv.approxPolyDP(cnt, approx, 0.02 * peri, true)\n\n if (approx.rows === 4 && area > maxArea) {\n maxArea = area\n if (maxContour) maxContour.delete()\n maxContour = approx\n } else {\n approx.delete()\n }\n }\n cnt.delete()\n }\n\n let detectedQuad: CropQuad | null = null\n\n if (maxContour && maxContour.rows === 4) {\n const points: Point[] = []\n for (let i = 0; i < 4; i++) {\n const pt = maxContour.data32S\n points.push({\n x: pt[i * 2] / width,\n y: pt[i * 2 + 1] / height,\n })\n }\n detectedQuad = sortQuadPoints(points)\n }\n\n // Cleanup OpenCV matrices\n srcMat.delete()\n grayMat.delete()\n blurMat.delete()\n cannyMat.delete()\n contours.delete()\n hierarchy.delete()\n if (maxContour) maxContour.delete()\n\n if (detectedQuad) return detectedQuad\n } catch (error) {\n console.warn('[detectEdges] OpenCV detection failed, falling back to margin quad:', error)\n }\n }\n\n return fallbackQuad\n}\n\n/**\n * Returns default crop quad with margin inset (5% padding from screen edges).\n */\nexport function getFallbackCropQuad(): CropQuad {\n const m = FALLBACK_CROP_MARGIN_RATIO\n return {\n topLeft: { x: m, y: m },\n topRight: { x: 1 - m, y: m },\n bottomRight: { x: 1 - m, y: 1 - m },\n bottomLeft: { x: m, y: 1 - m },\n }\n}\n\n/**\n * Order 4 points into clockwise top-left, top-right, bottom-right, bottom-left order.\n */\nexport function sortQuadPoints(pts: Point[]): CropQuad {\n const sortedBySum = [...pts].sort((a, b) => (a.x + a.y) - (b.x + b.y))\n const topLeft = sortedBySum[0]\n const bottomRight = sortedBySum[3]\n\n const remaining = [sortedBySum[1], sortedBySum[2]]\n const sortedByDiff = remaining.sort((a, b) => (a.y - a.x) - (b.y - b.x))\n\n const topRight = sortedByDiff[0]\n const bottomLeft = sortedByDiff[1]\n\n return { topLeft, topRight, bottomRight, bottomLeft }\n}\n","/**\n * @file perspective.ts\n * @description Perspective Correction & Quad Rectification Transformation Algorithm.\n * \n * WHY IT EXISTS:\n * Transforms angled document photos (taken from a side angle or tilted perspective) into flat,\n * perfectly rectangular front-facing document pages.\n * \n * WHAT IT DOES:\n * Applies 4-point homography / perspective transformation matrix (using OpenCV `getPerspectiveTransform`\n * or a zero-dependency bilinear interpolation fallback) to rectify quadrilateral points.\n * \n * WHEN IT RUNS:\n * Runs when applying user crop bounds to finalize page perspective.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Used by `src/services/scanner.service.ts`\n * - Consumes `CropQuad` from `src/types/scanner.ts`\n * \n * WHO CALLS IT: `scanner.service.ts`\n * WHO DEPENDS ON IT: Document scanning perspective adjustment pipeline.\n */\n\nimport { CropQuad } from '../../types/scanner'\n\n/**\n * Apply 4-point perspective warp transformation to unwarp paper region.\n * \n * @param sourceCanvas Source canvas element\n * @param quad 4-corner normalized crop quad (0..1)\n * @param cv OpenCV instance if available\n * @returns HTMLCanvasElement containing rectified rectangular document\n */\nexport function applyPerspectiveTransform(\n sourceCanvas: HTMLCanvasElement,\n quad: CropQuad,\n cv?: any\n): HTMLCanvasElement {\n const w = sourceCanvas.width\n const h = sourceCanvas.height\n\n // Absolute corner points in pixel coordinates\n const pTL = { x: quad.topLeft.x * w, y: quad.topLeft.y * h }\n const pTR = { x: quad.topRight.x * w, y: quad.topRight.y * h }\n const pBR = { x: quad.bottomRight.x * w, y: quad.bottomRight.y * h }\n const pBL = { x: quad.bottomLeft.x * w, y: quad.bottomLeft.y * h }\n\n // Compute calculated target width & height based on max edge distances\n const widthA = Math.hypot(pBR.x - pBL.x, pBR.y - pBL.y)\n const widthB = Math.hypot(pTR.x - pTL.x, pTR.y - pTL.y)\n const targetWidth = Math.max(100, Math.round(Math.max(widthA, widthB)))\n\n const heightA = Math.hypot(pTR.x - pBR.x, pTR.y - pBR.y)\n const heightB = Math.hypot(pTL.x - pBL.x, pTL.y - pBL.y)\n const targetHeight = Math.max(100, Math.round(Math.max(heightA, heightB)))\n\n const outCanvas = document.createElement('canvas')\n outCanvas.width = targetWidth\n outCanvas.height = targetHeight\n\n // OpenCV Homography Transformation\n if (cv && cv.Mat) {\n try {\n const srcMat = cv.imread(sourceCanvas)\n const dstMat = new cv.Mat()\n\n const srcTri = cv.matFromArray(4, 1, cv.CV_32FC2, [\n pTL.x, pTL.y,\n pTR.x, pTR.y,\n pBR.x, pBR.y,\n pBL.x, pBL.y,\n ])\n\n const dstTri = cv.matFromArray(4, 1, cv.CV_32FC2, [\n 0, 0,\n targetWidth, 0,\n targetWidth, targetHeight,\n 0, targetHeight,\n ])\n\n const M = cv.getPerspectiveTransform(srcTri, dstTri)\n const dsize = new cv.Size(targetWidth, targetHeight)\n\n cv.warpPerspective(srcMat, dstMat, M, dsize, cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar())\n cv.imshow(outCanvas, dstMat)\n\n // Cleanup\n srcMat.delete()\n dstMat.delete()\n srcTri.delete()\n dstTri.delete()\n M.delete()\n\n return outCanvas\n } catch (err) {\n console.warn('[perspective] OpenCV homography failed, using fallback canvas clip:', err)\n }\n }\n\n // Fallback affine canvas drawing\n const ctx = outCanvas.getContext('2d')\n if (ctx) {\n const minX = Math.min(pTL.x, pTR.x, pBR.x, pBL.x)\n const minY = Math.min(pTL.y, pTR.y, pBR.y, pBL.y)\n const cropW = Math.max(1, Math.max(pTL.x, pTR.x, pBR.x, pBL.x) - minX)\n const cropH = Math.max(1, Math.max(pTL.y, pTR.y, pBR.y, pBL.y) - minY)\n\n ctx.drawImage(sourceCanvas, minX, minY, cropW, cropH, 0, 0, targetWidth, targetHeight)\n }\n\n return outCanvas\n}\n","/**\n * @file enhance.ts\n * @description Document Enhancement Filters, Contrast Adjustments & Shadow Removal.\n * \n * WHY IT EXISTS:\n * Improves document readability by enhancing text sharpness, converting to monochrome/grayscale,\n * removing dark background shadows, and adjusting brightness & contrast levels.\n * \n * WHAT IT DOES:\n * Applies specified filter modes ('original' | 'grayscale' | 'bw' | 'enhanced') plus brightness/contrast\n * parameters onto an HTMLCanvasElement using OpenCV or native Canvas ImageData filters.\n * \n * WHEN IT RUNS:\n * Fired whenever the user changes filter selection, brightness, or contrast sliders in the ScanToolbar.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Used by `src/services/scanner.service.ts`\n * - Uses types from `src/types/scanner.ts`\n * \n * WHO CALLS IT: `scanner.service.ts`\n * WHO DEPENDS ON IT: Document image enhancement pipeline.\n */\n\nimport { ScanFilterMode } from '../../types/scanner'\n\n/**\n * Apply enhancement filters and brightness/contrast parameters to a canvas.\n * \n * @param canvas Source HTMLCanvasElement\n * @param filter Scan filter mode ('original' | 'grayscale' | 'bw' | 'enhanced')\n * @param brightness Brightness offset (-100 to 100)\n * @param contrast Contrast offset (-100 to 100)\n * @param cv Optional OpenCV instance\n * @returns Enhanced HTMLCanvasElement\n */\nexport function applyEnhancementFilter(\n canvas: HTMLCanvasElement,\n filter: ScanFilterMode,\n brightness = 0,\n contrast = 0,\n cv?: any\n): HTMLCanvasElement {\n const outCanvas = document.createElement('canvas')\n outCanvas.width = canvas.width\n outCanvas.height = canvas.height\n\n const ctx = outCanvas.getContext('2d')\n if (!ctx) return canvas\n\n // First draw base canvas\n ctx.drawImage(canvas, 0, 0)\n\n // OpenCV Processing if available\n if (cv && cv.Mat && filter !== 'original') {\n try {\n const srcMat = cv.imread(outCanvas)\n const dstMat = new cv.Mat()\n\n if (filter === 'grayscale') {\n cv.cvtColor(srcMat, dstMat, cv.COLOR_RGBA2GRAY, 0)\n cv.cvtColor(dstMat, dstMat, cv.COLOR_GRAY2RGBA, 0)\n } else if (filter === 'bw') {\n // High contrast adaptive binary thresholding for text documents\n const grayMat = new cv.Mat()\n cv.cvtColor(srcMat, grayMat, cv.COLOR_RGBA2GRAY, 0)\n cv.adaptiveThreshold(\n grayMat,\n grayMat,\n 255,\n cv.ADAPTIVE_THRESH_GAUSSIAN_C,\n cv.THRESH_BINARY,\n 21,\n 10\n )\n cv.cvtColor(grayMat, dstMat, cv.COLOR_GRAY2RGBA, 0)\n grayMat.delete()\n } else if (filter === 'enhanced') {\n // Color enhancement: Contrast sharpening + Shadow removal\n const labMat = new cv.Mat()\n cv.cvtColor(srcMat, labMat, cv.COLOR_RGBA2RGB, 0)\n cv.cvtColor(labMat, labMat, cv.COLOR_RGB2Lab, 0)\n\n // Split channels to equalize Luminance\n const channels = new cv.MatVector()\n cv.split(labMat, channels)\n const lChannel = channels.get(0)\n\n const clahe = new cv.CLAHE(2.0, new cv.Size(8, 8))\n clahe.apply(lChannel, lChannel)\n\n channels.set(0, lChannel)\n cv.merge(channels, labMat)\n cv.cvtColor(labMat, dstMat, cv.COLOR_Lab2RGBA, 0)\n\n labMat.delete()\n channels.delete()\n lChannel.delete()\n clahe.delete()\n }\n\n cv.imshow(outCanvas, dstMat)\n srcMat.delete()\n dstMat.delete()\n } catch (e) {\n console.warn('[enhance] OpenCV enhancement failed, fallback to canvas pixel math:', e)\n }\n }\n\n // Fallback Canvas pixel math for brightness/contrast or when OpenCV is not present\n const imgData = ctx.getImageData(0, 0, outCanvas.width, outCanvas.height)\n const data = imgData.data\n\n const bFactor = brightness * 2.55 // map -100..100 to -255..255\n const cFactor = (259 * (contrast + 255)) / (255 * (259 - contrast))\n\n for (let i = 0; i < data.length; i += 4) {\n let r = data[i]\n let g = data[i + 1]\n let b = data[i + 2]\n\n // Apply manual filter if OpenCV was absent\n if (!cv && filter === 'grayscale') {\n const avg = 0.299 * r + 0.587 * g + 0.114 * b\n r = avg\n g = avg\n b = avg\n } else if (!cv && filter === 'bw') {\n const avg = 0.299 * r + 0.587 * g + 0.114 * b\n const bw = avg > 128 ? 255 : 0\n r = bw\n g = bw\n b = bw\n } else if (!cv && filter === 'enhanced') {\n // Mild auto-contrast increase\n r = Math.min(255, r * 1.1 + 10)\n g = Math.min(255, g * 1.1 + 10)\n b = Math.min(255, b * 1.1 + 10)\n }\n\n // Apply Brightness & Contrast\n if (brightness !== 0 || contrast !== 0) {\n r = cFactor * (r - 128) + 128 + bFactor\n g = cFactor * (g - 128) + 128 + bFactor\n b = cFactor * (b - 128) + 128 + bFactor\n }\n\n data[i] = Math.min(255, Math.max(0, r))\n data[i + 1] = Math.min(255, Math.max(0, g))\n data[i + 2] = Math.min(255, Math.max(0, b))\n }\n\n ctx.putImageData(imgData, 0, 0)\n return outCanvas\n}\n","/**\n * @file scanner.service.ts\n * @description Master Image & Document Scanner Pipeline Orchestrator.\n * \n * WHY IT EXISTS:\n * Centralizes image processing steps (edge detection, unwarping perspective transform, image filter enhancement,\n * and page record creation) so UI components don't deal with raw image manipulation algorithms.\n * \n * WHAT IT DOES:\n * Ingests image URLs or File objects, runs edge detection, applies 4-corner perspective unwarping,\n * processes enhancement filters, and returns complete `ScanPage` objects.\n * \n * WHEN IT RUNS:\n * Executed whenever a photo is captured, uploaded, cropped, or edited in the scanner UI.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Uses `opencv.service.ts` for OpenCV instance\n * - Uses `detectEdges.ts`, `crop.ts`, `perspective.ts`, `enhance.ts` from `src/utils/scanner/`\n * - Uses types from `src/types/scanner.ts`\n * \n * WHO CALLS IT: `useDocumentScanner.ts` & `ScanPreview.tsx`\n * WHO DEPENDS ON IT: Document Scanning workflow state machine.\n */\n\nimport { ScanPage, CropQuad, ScanFilterMode } from '../types/scanner'\nimport { opencvService } from './opencv.service'\nimport { detectDocumentEdges, getFallbackCropQuad } from '../utils/scanner/detectEdges'\nimport { applyPerspectiveTransform } from '../utils/scanner/perspective'\nimport { applyEnhancementFilter } from '../utils/scanner/enhance'\n\nclass ScannerService {\n /**\n * Process a new raw image file or Data URL into a fully-initialized ScanPage.\n * \n * @param imageSource File object or Image Data URL\n * @returns Promise resolving to a fresh ScanPage object\n */\n public async createScanPage(imageSource: File | string): Promise<ScanPage> {\n const dataUrl = typeof imageSource === 'string'\n ? imageSource\n : await this.fileToDataUrl(imageSource)\n\n const img = await this.loadImage(dataUrl)\n const width = img.naturalWidth || img.width\n const height = img.naturalHeight || img.height\n\n const cv = opencvService.isReady() ? (window as any).cv : undefined\n const cropQuad = detectDocumentEdges(img, cv)\n\n const pageId = `page_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`\n\n // Generate initial processed preview canvas\n const baseCanvas = this.imageToCanvas(img)\n const unwarpedCanvas = applyPerspectiveTransform(baseCanvas, cropQuad, cv)\n const enhancedCanvas = applyEnhancementFilter(unwarpedCanvas, 'enhanced', 0, 0, cv)\n\n const processedUrl = enhancedCanvas.toDataURL('image/jpeg', 0.88)\n\n return {\n id: pageId,\n originalUrl: dataUrl,\n processedUrl,\n cropQuad,\n filter: 'enhanced',\n rotation: 0,\n brightness: 0,\n contrast: 0,\n width,\n height,\n timestamp: Date.now(),\n }\n }\n\n /**\n * Re-process an existing ScanPage when user modifies cropQuad, filter, rotation, brightness, or contrast.\n * \n * @param page ScanPage to update\n * @returns Promise resolving to updated ScanPage with fresh processedUrl\n */\n public async reprocessScanPage(page: ScanPage): Promise<ScanPage> {\n const img = await this.loadImage(page.originalUrl)\n const cv = opencvService.isReady() ? (window as any).cv : undefined\n\n const baseCanvas = this.imageToCanvas(img)\n const unwarpedCanvas = applyPerspectiveTransform(baseCanvas, page.cropQuad, cv)\n const enhancedCanvas = applyEnhancementFilter(\n unwarpedCanvas,\n page.filter,\n page.brightness,\n page.contrast,\n cv\n )\n\n const processedUrl = enhancedCanvas.toDataURL('image/jpeg', 0.88)\n\n return {\n ...page,\n processedUrl,\n }\n }\n\n /**\n * Helper to convert File to Data URL string.\n */\n private fileToDataUrl(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = (err) => reject(err)\n reader.readAsDataURL(file)\n })\n }\n\n /**\n * Helper to load Image element asynchronously.\n */\n private loadImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => resolve(img)\n img.onerror = (err) => reject(err)\n img.src = src\n })\n }\n\n private imageToCanvas(img: HTMLImageElement): HTMLCanvasElement {\n const canvas = document.createElement('canvas')\n const maxDim = 2048\n let w = img.naturalWidth || img.width\n let h = img.naturalHeight || img.height\n\n if (w > maxDim || h > maxDim) {\n if (w > h) {\n h = Math.round((h * maxDim) / w)\n w = maxDim\n } else {\n w = Math.round((w * maxDim) / h)\n h = maxDim\n }\n }\n\n canvas.width = w\n canvas.height = h\n\n const ctx = canvas.getContext('2d')\n if (ctx) {\n ctx.drawImage(img, 0, 0, w, h)\n }\n return canvas\n }\n}\n\nexport const scannerService = new ScannerService()\n","import { toast } from 'sonner'\r\n\r\nexport function generateSecureFileUrl(fileId: string): string {\r\n if (typeof window === 'undefined') {\r\n return `/files/document/${fileId}`\r\n }\r\n return `${window.location.origin}/files/document/${fileId}`\r\n}\r\n\r\nexport async function shareFileLink(fileId: string, fileName: string): Promise<void> {\r\n const shareUrl = generateSecureFileUrl(fileId)\r\n\r\n if (navigator.share) {\r\n try {\r\n await navigator.share({\r\n title: `Share: ${fileName}`,\r\n text: `Check out this file: ${fileName}`,\r\n url: shareUrl,\r\n })\r\n toast.success('Share link sent.')\r\n return\r\n } catch (err: any) {\r\n if (err.name === 'AbortError') {\r\n return // User dismissed share dialog\r\n }\r\n }\r\n }\r\n\r\n // Fallback to Clipboard API\r\n try {\r\n await navigator.clipboard.writeText(shareUrl)\r\n toast.success('Share link copied.')\r\n } catch (err) {\r\n const textarea = document.createElement('textarea')\r\n textarea.value = shareUrl\r\n textarea.style.position = 'fixed'\r\n textarea.style.opacity = '0'\r\n document.body.appendChild(textarea)\r\n textarea.select()\r\n try {\r\n document.execCommand('copy')\r\n toast.success('Share link copied.')\r\n } catch {\r\n toast.error('Failed to copy share link.')\r\n }\r\n document.body.removeChild(textarea)\r\n }\r\n}\r\n","/**\n * @file upload.service.ts\n * @description Document & PDF Storage Upload Service.\n * \n * WHY IT EXISTS:\n * Handles sending compiled scanned PDF documents to the `/api/upload` endpoint (which uploads directly to Supabase Storage bucket `chat-files` under the `scanned/` folder).\n * \n * WHAT IT DOES:\n * Wraps multipart/form-data upload dispatch, handles network errors, and returns formatted `ScannedPdfResult` with public URLs and file details.\n * \n * WHEN IT RUNS:\n * Executed ONLY when the user explicitly clicks the \"Send\" button in the scanner modal.\n * \n * HOW IT CONNECTS WITH OTHER FILES:\n * - Used by `useSupabaseUpload.ts` & `DocumentScannerModal.tsx`\n * - Integrates with project `/api/upload` Next.js route\n * - Returns `ScannedPdfResult` type from `src/types/scanner.ts`\n * \n * WHO CALLS IT: `useSupabaseUpload.ts`\n * WHO DEPENDS ON IT: Chat document message attachment flow.\n */\n\nimport { ScannedPdfResult } from '../types/scanner'\n\nclass UploadService {\n /**\n * Upload scanned PDF file to Supabase Storage via `/api/upload` route.\n * \n * @param pdfFile Compiled PDF File object\n * @param pageCount Total number of pages included in document\n * @returns ScannedPdfResult containing publicUrl, fileName, fileSize, mimeType, and storagePath\n */\n public async uploadScannedPdf(pdfFile: File, pageCount: number): Promise<ScannedPdfResult> {\n const formData = new FormData()\n formData.append('file', pdfFile)\n formData.append('folder', 'documents')\n\n const response = await fetch('/api/upload', {\n method: 'POST',\n body: formData,\n })\n\n if (!response.ok) {\n const errText = await response.text()\n throw new Error(`Upload failed (${response.status}): ${errText}`)\n }\n\n const data = await response.json()\n\n if (!data.success || !data.publicUrl) {\n throw new Error(data.error || 'Server did not return a valid public upload URL')\n }\n\n return {\n publicUrl: data.publicUrl,\n fileName: data.fileName || pdfFile.name,\n fileSize: data.fileSize || pdfFile.size,\n mimeType: 'application/pdf',\n storagePath: data.storagePath || `scanned/${pdfFile.name}`,\n pageCount,\n }\n }\n}\n\nexport const scannerUploadService = new UploadService()\n","/**\n * Centralized service to manage authentication redirections.\n */\n\nexport function getFullRedirectUrl(pathname: string, searchParamsString?: string): string {\n if (!searchParamsString) {\n return pathname\n }\n const cleanSearch = searchParamsString.startsWith('?') \n ? searchParamsString \n : `?${searchParamsString}`\n return `${pathname}${cleanSearch}`\n}\n\nexport function handleAuthRedirect(router: any, redirectTo?: string | null) {\n console.log('[DEBUG client] handleAuthRedirect received raw redirectTo:', redirectTo)\n\n let targetPath = '/'\n if (redirectTo) {\n try {\n targetPath = decodeURIComponent(redirectTo)\n } catch {\n targetPath = redirectTo\n }\n }\n\n // Normalize absolute URL to relative path if needed\n try {\n if (targetPath.startsWith('http://') || targetPath.startsWith('https://')) {\n const url = new URL(targetPath)\n targetPath = `${url.pathname}${url.search}${url.hash}` || '/'\n }\n } catch {\n // Relative path\n }\n\n // Prevent redirect loops back to sign-in / sign-up\n if (targetPath === '/sign-in' || targetPath === '/sign-up' || !targetPath || targetPath === '') {\n targetPath = '/'\n }\n\n // Ensure leading slash\n if (!targetPath.startsWith('/')) {\n targetPath = `/${targetPath}`\n }\n\n console.log('[DEBUG client] handleAuthRedirect executing navigation to targetPath:', targetPath)\n\n if (router && typeof router.replace === 'function') {\n router.replace(targetPath)\n }\n\n // Hard navigation fallback if client router.replace stays on sign-in page\n if (typeof window !== 'undefined') {\n setTimeout(() => {\n if (window.location.pathname.startsWith('/sign-in') || window.location.pathname.startsWith('/sign-up')) {\n console.log('[DEBUG client] Executing hard redirect to dashboard:', targetPath)\n window.location.href = targetPath\n }\n }, 100)\n }\n}\n","import { createClient } from '@/lib/supabase/client'\nimport { stringToUuid } from '@/lib/auth'\n\nexport interface Profile {\n id: string\n name: string\n email: string\n avatar_url?: string\n mobile?: string\n}\n\nexport async function ensureProfileExists(user: {\n accountNo: string\n email: string\n name?: string\n picture?: string\n mobile?: string\n}) {\n try {\n const supabase = createClient()\n const fallbackUuid = stringToUuid(user.accountNo || user.email)\n\n const { data: existingProfile } = await supabase\n .from('profiles')\n .select('id, auth_user_id')\n .eq('email', user.email.toLowerCase())\n .maybeSingle()\n\n const profileId = existingProfile?.id || fallbackUuid\n\n const profileData: any = {\n id: profileId,\n name: user.name || user.email.split('@')[0],\n email: user.email.toLowerCase(),\n avatar: user.picture || null,\n updated_at: new Date().toISOString(),\n }\n if (user.mobile) {\n profileData.mobile = user.mobile\n }\n profileData.auth_user_id = existingProfile?.auth_user_id || profileId\n\n if (existingProfile) {\n await supabase\n .from('profiles')\n .update(profileData)\n .eq('id', existingProfile.id)\n } else {\n await supabase\n .from('profiles')\n .insert(profileData)\n }\n\n return {\n id: profileId,\n name: profileData.name,\n email: profileData.email,\n avatar_url: profileData.avatar,\n }\n } catch (err) {\n console.error('[ProfileService] Error ensuring profile exists:', err)\n return null\n }\n}\n\nexport async function updateProfile(\n id: string,\n updates: Record<string, any>\n): Promise<boolean> {\n try {\n const supabase = createClient()\n await supabase.from('profiles').update(updates).eq('id', id)\n return true\n } catch (err) {\n console.error('[ProfileService] Error updating profile:', err)\n return false\n }\n}\n\nexport async function getProfileByEmail(email: string): Promise<Profile | null> {\n try {\n const supabase = createClient()\n const { data } = await supabase.from('profiles').select('*').eq('email', email).maybeSingle()\n if (!data) return null\n return {\n id: data.id,\n name: data.name,\n email: data.email,\n avatar_url: data.avatar,\n }\n } catch (err) {\n return null\n }\n}\n","export async function requestNativeAppPermissions(): Promise<void> {\n return\n}\n\nexport async function initPushNotifications(userId: string): Promise<void> {\n return\n}\n","const ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\r\n\r\nexport function generateShortId(length = 6): string {\r\n let result = ''\r\n for (let i = 0; i < length; i++) {\r\n result += ID_CHARS.charAt(Math.floor(Math.random() * ID_CHARS.length))\r\n }\r\n return result\r\n}\r\n\r\nexport function toBase64Url(str: string): string {\r\n const bytes = new TextEncoder().encode(str)\r\n const bin = Array.from(bytes, (b) => String.fromCharCode(b)).join('')\r\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\r\n}\r\n\r\nexport function buildSelfContainedShortUrl(\r\n origin: string,\r\n configHash: string,\r\n durationHours: number\r\n): { shortUrl: string; expiresAt: string } {\r\n const expiresAtMs = Date.now() + durationHours * 60 * 60 * 1000\r\n const targetUrl = `${origin}/l?c=${configHash}&exp=${expiresAtMs}`\r\n const id = generateShortId()\r\n const encoded = toBase64Url(targetUrl)\r\n return {\r\n shortUrl: `${origin}/go/${id}?r=${encoded}`,\r\n expiresAt: new Date(expiresAtMs).toISOString(),\r\n }\r\n}\r\n","import { promises as fs } from 'fs'\r\nimport path from 'path'\r\nimport { createClient } from '@supabase/supabase-js'\r\n\r\nexport interface ShortUrlEntry {\r\n id: string\r\n targetUrl: string\r\n createdAt: string\r\n expiresAt: string\r\n}\r\n\r\nconst supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL\r\nconst supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\r\n\r\nconst supabase = supabaseUrl && supabaseAnonKey\r\n ? createClient(supabaseUrl, supabaseAnonKey)\r\n : null\r\n\r\nconst URLS_FILE = path.join(process.cwd(), 'src/features/link-builder/data/urls.json')\r\nconst ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'\r\n\r\ntype GlobalStore = typeof globalThis & { __shortUrlStore?: Map<string, ShortUrlEntry> }\r\n\r\nfunction getMemoryStore(): Map<string, ShortUrlEntry> {\r\n const g = globalThis as GlobalStore\r\n if (!g.__shortUrlStore) {\r\n g.__shortUrlStore = new Map()\r\n }\r\n return g.__shortUrlStore\r\n}\r\n\r\nexport function generateShortId(length = 6): string {\r\n let result = ''\r\n for (let i = 0; i < length; i++) {\r\n result += ID_CHARS.charAt(Math.floor(Math.random() * ID_CHARS.length))\r\n }\r\n return result\r\n}\r\n\r\nfunction isKvConfigured(): boolean {\r\n return Boolean(process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN)\r\n}\r\n\r\nasync function kvGet(key: string): Promise<ShortUrlEntry | null> {\r\n if (!isKvConfigured()) return null\r\n try {\r\n const res = await fetch(process.env.KV_REST_API_URL!, {\r\n method: 'POST',\r\n headers: {\r\n Authorization: `Bearer ${process.env.KV_REST_API_TOKEN}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(['GET', key]),\r\n cache: 'no-store',\r\n })\r\n if (!res.ok) return null\r\n const data = await res.json()\r\n if (!data.result) return null\r\n return JSON.parse(data.result as string) as ShortUrlEntry\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nasync function kvSet(key: string, entry: ShortUrlEntry, ttlSeconds: number): Promise<boolean> {\r\n if (!isKvConfigured()) return false\r\n try {\r\n const res = await fetch(process.env.KV_REST_API_URL!, {\r\n method: 'POST',\r\n headers: {\r\n Authorization: `Bearer ${process.env.KV_REST_API_TOKEN}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(['SET', key, JSON.stringify(entry), 'EX', ttlSeconds]),\r\n cache: 'no-store',\r\n })\r\n return res.ok\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nasync function readFileStore(): Promise<ShortUrlEntry[]> {\r\n try {\r\n const raw = await fs.readFile(URLS_FILE, 'utf-8')\r\n return JSON.parse(raw) as ShortUrlEntry[]\r\n } catch {\r\n return []\r\n }\r\n}\r\n\r\nasync function writeFileStore(entries: ShortUrlEntry[]): Promise<boolean> {\r\n try {\r\n await fs.writeFile(URLS_FILE, JSON.stringify(entries, null, 2), 'utf-8')\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nexport async function saveShortUrl(\r\n targetUrl: string,\r\n expiresAtMs: number\r\n): Promise<{ id: string; entry: ShortUrlEntry; shortUrlSuffix: string }> {\r\n const id = generateShortId()\r\n const entry: ShortUrlEntry = {\r\n id,\r\n targetUrl,\r\n createdAt: new Date().toISOString(),\r\n expiresAt: new Date(expiresAtMs).toISOString(),\r\n }\r\n\r\n const memory = getMemoryStore()\r\n memory.set(id, entry)\r\n\r\n const ttlSeconds = Math.max(60, Math.ceil((expiresAtMs - Date.now()) / 1000))\r\n const kvSaved = await kvSet(`short:${id}`, entry, ttlSeconds)\r\n\r\n const existing = await readFileStore()\r\n existing.push(entry)\r\n const fileSaved = await writeFileStore(existing)\r\n\r\n let supabaseSaved = false\r\n if (supabase) {\r\n try {\r\n const { error } = await supabase\r\n .from('short_urls')\r\n .insert({\r\n id,\r\n target_url: targetUrl,\r\n expires_at: new Date(expiresAtMs).toISOString()\r\n })\r\n if (error) {\r\n console.error('Supabase saveShortUrl error:', error)\r\n } else {\r\n supabaseSaved = true\r\n console.log('Supabase saveShortUrl success:', id)\r\n }\r\n } catch (e) {\r\n console.error('Supabase saveShortUrl exception:', e)\r\n }\r\n }\r\n\r\n // When no persistent store is available (typical on Vercel without KV or Supabase),\r\n // embed the redirect target so the link survives cold starts.\r\n const needsFallback = !kvSaved && !fileSaved && !supabaseSaved\r\n const shortUrlSuffix = needsFallback\r\n ? `${id}?r=${Buffer.from(targetUrl).toString('base64url')}`\r\n : id\r\n\r\n return { id, entry, shortUrlSuffix }\r\n}\r\n\r\nlet fileStoreLoaded = false\r\n\r\nasync function ensureFileStoreLoaded(): Promise<void> {\r\n if (fileStoreLoaded) return\r\n fileStoreLoaded = true\r\n const entries = await readFileStore()\r\n const memory = getMemoryStore()\r\n for (const entry of entries) {\r\n memory.set(entry.id, entry)\r\n }\r\n}\r\n\r\nexport async function getShortUrl(id: string): Promise<ShortUrlEntry | null> {\r\n await ensureFileStoreLoaded()\r\n\r\n const memory = getMemoryStore()\r\n const cached = memory.get(id)\r\n if (cached) return cached\r\n\r\n // Check Supabase first as it is our primary persistent store on production\r\n if (supabase) {\r\n try {\r\n const { data, error } = await supabase\r\n .from('short_urls')\r\n .select('*')\r\n .eq('id', id)\r\n .single()\r\n \r\n if (!error && data) {\r\n const entry: ShortUrlEntry = {\r\n id: data.id,\r\n targetUrl: data.target_url,\r\n createdAt: data.created_at,\r\n expiresAt: data.expires_at,\r\n }\r\n memory.set(id, entry)\r\n return entry\r\n }\r\n } catch (e) {\r\n console.error('Supabase getShortUrl exception:', e)\r\n }\r\n }\r\n\r\n const fromKv = await kvGet(`short:${id}`)\r\n if (fromKv) {\r\n memory.set(id, fromKv)\r\n return fromKv\r\n }\r\n\r\n const fromFile = await readFileStore()\r\n const entry = fromFile.find((e) => e.id === id) ?? null\r\n if (entry) {\r\n memory.set(id, entry)\r\n }\r\n return entry\r\n}\r\n","/**\n * Short URL Service\n * Handles client & server-side URL shortening, expiration calculations, and identifier generation.\n */\n\nimport { generateShortId, toBase64Url, buildSelfContainedShortUrl } from '@/lib/short-url-client'\nimport { saveShortUrl, getShortUrl } from '@/lib/short-url-store'\n\nexport class ShortUrlService {\n /**\n * Generates a random short ID of specified length.\n */\n static generateId(length = 6): string {\n return generateShortId(length)\n }\n\n /**\n * Encodes a string to a base64url format.\n */\n static encodeUrl(url: string): string {\n return toBase64Url(url)\n }\n\n /**\n * Builds a self-contained client-side short URL with embedded expiration.\n */\n static buildSelfContainedUrl(\n origin: string,\n configHash: string,\n durationHours: number\n ): { shortUrl: string; expiresAt: string } {\n return buildSelfContainedShortUrl(origin, configHash, durationHours)\n }\n\n /**\n * Stores a shortened URL mapping with expiration timestamp.\n */\n static async saveUrl(targetUrl: string, expiresAtMs: number): Promise<{ shortUrlSuffix: string }> {\n return saveShortUrl(targetUrl, expiresAtMs)\n }\n\n /**\n * Retrieves a shortened URL target mapping.\n */\n static async getUrl(shortUrlSuffix: string): Promise<{ targetUrl: string; expiresAtMs: number } | null> {\n return getShortUrl(shortUrlSuffix)\n }\n}\n","/**\n * AI Search Service\n * Provides web search through Tavily API and LLM analysis through Gemini.\n */\n\nimport axios from 'axios'\n\nexport interface SearchSource {\n title: string\n url: string\n content: string\n}\n\nexport interface SearchResponse {\n answer: string\n sources: SearchSource[]\n images: string[]\n}\n\nexport class AiSearchService {\n /**\n * Executes web search through Tavily API.\n */\n static async searchWeb(query: string, apiKey: string): Promise<{ results: SearchSource[]; images: string[] }> {\n if (!apiKey) {\n throw new Error('Tavily API key is not configured.')\n }\n\n const res = await axios.post(\n 'https://api.tavily.com/search',\n {\n api_key: apiKey,\n query,\n search_depth: 'advanced',\n max_results: 10,\n include_images: true,\n },\n { timeout: 15000 }\n )\n\n return {\n results: res.data?.results || [],\n images: res.data?.images || [],\n }\n }\n\n /**\n * Generates answer analysis using Gemini API.\n */\n static async generateAnswer(prompt: string, apiKey: string): Promise<string> {\n if (!apiKey) {\n throw new Error('Gemini API key is not configured.')\n }\n\n const res = await axios.post(\n `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`,\n {\n contents: [\n {\n parts: [{ text: prompt }],\n },\n ],\n },\n { timeout: 30000 }\n )\n\n return res.data?.candidates?.[0]?.content?.parts?.[0]?.text || ''\n }\n}\n","import { createClient } from '@/lib/supabase/client'\nimport { useAuthStore } from '@/stores/auth-store'\nimport { stringToUuid } from '@/lib/auth'\n\nexport interface VoucherRecord {\n id: string\n user_id: string\n voucher_no: string\n file_name: string\n original_file_url?: string\n edited_file_url?: string\n edited_json?: any\n vendor_name?: string\n customer_name?: string\n invoice_date?: string\n total?: number\n currency?: string\n status?: string\n created_at: string\n updated_at: string\n}\n\nasync function getActiveUserId(): Promise<string | null> {\n try {\n const supabase = createClient()\n\n // Prefer Supabase session UUID directly\n const { data: user } = await supabase.auth.getUser()\n if (user?.user?.id) return user.user.id\n } catch {}\n\n const storeUser = useAuthStore.getState().auth.user\n if (!storeUser?.email && !storeUser?.id) return null\n\n try {\n // Resolve the profile UUID by email — ensures new NextAuth users get the right UUID\n const supabase = createClient()\n const email = storeUser.email?.toLowerCase()\n if (email) {\n const { data: profileRow } = await supabase\n .from('profiles')\n .select('id')\n .eq('email', email)\n .maybeSingle()\n if (profileRow?.id) return profileRow.id\n }\n } catch {}\n\n // Last resort: derive from stored id\n if (storeUser?.id) return stringToUuid(storeUser.id)\n return null\n}\n\n\n/** Upload a file to Supabase Storage under `vouchers/${userId}/` folder in `chat-files` bucket */\nexport async function uploadVoucherFile(\n file: File,\n subfolder: 'originals' | 'edited' = 'originals'\n): Promise<string> {\n const supabase = createClient()\n const userId = (await getActiveUserId()) || 'anonymous'\n\n const fileExt = file.name.split('.').pop() || 'bin'\n const uniqueName = `${crypto.randomUUID()}.${fileExt}`\n const path = `vouchers/${userId}/${subfolder}/${uniqueName}`\n\n const { error } = await supabase.storage\n .from('chat-files')\n .upload(path, file, { upsert: false, contentType: file.type || 'application/octet-stream' })\n\n if (error) {\n throw new Error(`Storage upload failed: ${error.message}`)\n }\n\n const { data: urlData } = supabase.storage.from('chat-files').getPublicUrl(path)\n return urlData.publicUrl\n}\n\n/** Upload a Blob (generated invoice file) to Supabase Storage */\nexport async function uploadVoucherBlob(\n blob: Blob,\n fileName: string\n): Promise<string> {\n const supabase = createClient()\n const userId = (await getActiveUserId()) || 'anonymous'\n\n const uniqueName = `${crypto.randomUUID()}_${fileName}`\n const path = `vouchers/${userId}/edited/${uniqueName}`\n\n const { error } = await supabase.storage\n .from('chat-files')\n .upload(path, blob, { upsert: false, contentType: blob.type || 'application/octet-stream' })\n\n if (error) {\n throw new Error(`Invoice storage upload failed: ${error.message}`)\n }\n\n const { data: urlData } = supabase.storage.from('chat-files').getPublicUrl(path)\n return urlData.publicUrl\n}\n\n/** Insert a new voucher record into the database */\nexport async function saveVoucher(data: {\n voucher_no: string\n file_name: string\n original_file_url?: string\n edited_file_url?: string\n edited_json?: any\n vendor_name?: string\n customer_name?: string\n invoice_date?: string\n total?: number\n currency?: string\n}): Promise<VoucherRecord | null> {\n const supabase = createClient()\n const userId = await getActiveUserId()\n\n if (!userId) {\n throw new Error('Not authenticated')\n }\n\n const { data: row, error } = await supabase\n .from('vouchers')\n .insert({\n ...data,\n user_id: userId,\n })\n .select('*')\n .single()\n\n if (error) {\n throw new Error(`DB save failed: ${error.message}`)\n }\n\n return row as VoucherRecord\n}\n\n/** Fetch all vouchers for the current user */\nexport async function getUserVouchers(): Promise<VoucherRecord[]> {\n const supabase = createClient()\n const userId = await getActiveUserId()\n\n if (!userId) {\n // If no specific userId, query recent vouchers or return empty\n const { data } = await supabase\n .from('vouchers')\n .select('*')\n .order('created_at', { ascending: false })\n .limit(50)\n return (data ?? []) as VoucherRecord[]\n }\n\n const { data, error } = await supabase\n .from('vouchers')\n .select('*')\n .or(`user_id.eq.${userId}`)\n .order('created_at', { ascending: false })\n .limit(50)\n\n if (error) {\n console.error('Failed to fetch vouchers:', error.message)\n const { data: fallbackData } = await supabase\n .from('vouchers')\n .select('*')\n .order('created_at', { ascending: false })\n .limit(50)\n return (fallbackData ?? []) as VoucherRecord[]\n }\n\n return (data ?? []) as VoucherRecord[]\n}\n","/**\n * Geocoding Service\n * Provides reverse geocoding from coordinates to human-readable addresses.\n */\n\nexport interface ReverseGeocodeResult {\n place_id?: number\n licence?: string\n osm_type?: string\n osm_id?: number\n lat?: string\n lon?: string\n display_name?: string\n address?: {\n road?: string\n suburb?: string\n city?: string\n state?: string\n postcode?: string\n country?: string\n country_code?: string\n }\n}\n\nexport class GeocodeService {\n /**\n * Reverse geocodes latitude and longitude into an address.\n */\n static async reverseGeocode(latitude: number, longitude: number): Promise<ReverseGeocodeResult | null> {\n try {\n const res = await fetch(`/api/geocode?lat=${latitude}&lon=${longitude}`)\n if (!res.ok) {\n throw new Error(`Geocode request failed with status ${res.status}`)\n }\n return await res.json()\n } catch (err) {\n console.error('GeocodeService reverseGeocode failed:', err)\n return null\n }\n }\n}\n"],"mappings":"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","names":["crypto","GoogleProvider","supabase","createSupabaseClient","supabase","SUPABASE_URL","path","createMessage","supabase","createMessage","supabase","supabase","import_supabase_js","path","ID_CHARS","generateShortId","fs","entry","axios","supabase","path","data"]}
|