1"use strict"; 2(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[3290],{ 3 4/***/ 17906: 5/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 6 7/* harmony export */ __webpack_require__.d(__webpack_exports__, { 8/* harmony export */ i1: () => (/* binding */ getCompanyForUser), 9/* harmony export */ p6: () => (/* binding */ getUserPrimaryCompany) 10/* harmony export */ }); 11/* unused harmony export updateCompany */ 12/* harmony import */ var _lib_supabase_v2_client__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(29326); 13/* harmony import */ var _lib_supabase_service_role_client__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(37314); 14/* harmony import */ var _lib_supabase_field_transformer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(68762); 15/* harmony import */ var _lib_validation_api_schemas__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(83636); 16 17 18 19 20async function getUserPrimaryCompany() { 21 let client = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : _lib_supabase_v2_client__WEBPACK_IMPORTED_MODULE_0__/* .enhancedSupabase */ .XL; 22 const { data: authData, error: authError } = await client.auth.getUser(); 23 if (authError) { 24 var _message_toLowerCase; 25 var _authError_status; 26 const status = (_authError_status = authError === null || authError === void 0 ? void 0 : authError.status) !== null && _authError_status !== void 0 ? _authError_status : authError === null || authError === void 0 ? void 0 : authError.code; 27 const message = (authError === null || authError === void 0 ? void 0 : authError.message) || ""; 28 const isSessionMissing = status === 400 || ((_message_toLowerCase = message.toLowerCase) === null || _message_toLowerCase === void 0 ? void 0 : _message_toLowerCase.call(message).includes("auth session missing")) || authError.name === "AuthSessionMissingError"; 29 if (isSessionMissing) { 30 // Expected on cold SSR refresh when cookies are missing; defer to client hydration 31 return null; 32 } 33 console.error("[getUserPrimaryCompany] auth error", authError); 34 return null; 35 } 36 const user = authData === null || authData === void 0 ? void 0 : authData.user; 37 if (!user) { 38 return null; 39 } 40 const { data: membership, error: membershipError } = await client.from("company_members").select("company_id").eq("user_id", user.id).limit(1).maybeSingle(); 41 if (membershipError) { 42 console.error("[getUserPrimaryCompany] membership error", membershipError); 43 return null; 44 } 45 var _membership_companyId; 46 const membershipCompanyId = (_membership_companyId = membership === null || membership === void 0 ? void 0 : membership.companyId) !== null && _membership_companyId !== void 0 ? _membership_companyId : membership === null || membership === void 0 ? void 0 : membership.company_id; 47 if (!membershipCompanyId) { 48 return null; 49 } 50 const { data: company, error: companyError } = await client.from("companies").select("*").eq("id", membershipCompanyId).single(); 51 if (companyError) { 52 console.error("[getUserPrimaryCompany] company error", companyError); 53 return null; 54 } 55 return company; 56} 57const getCompanyForUser = getUserPrimaryCompany; 58const normalizeLastKnownUpdatedAt = (value)=>{ 59 if (value instanceof Date) { 60 const time = value.getTime(); 61 return Number.isNaN(time) ? null : value.toISOString(); 62 } 63 if (typeof value !== "string") { 64 return null; 65 } 66 const trimmed = value.trim(); 67 if (!trimmed || Number.isNaN(Date.parse(trimmed))) { 68 return null; 69 } 70 return trimmed; 71}; 72async function updateCompany(patch, idempotencyKey) { 73 const company = await getCompanyForUser(); 74 if (!company) throw new Error("No company found"); 75 const patchWithMetadata = patch; 76 const { lastKnownUpdatedAt, updatedAt: _updatedAt, ...patchWithoutCamelMetadata } = patchWithMetadata; 77 const normalizedLastKnownUpdatedAt = normalizeLastKnownUpdatedAt(lastKnownUpdatedAt); 78 const { ["updated_at"]: _updated_at, ...safePatch } = patchWithoutCamelMetadata; 79 const updateData = { 80 ...safePatch 81 }; 82 // Prefer service role when available to avoid RLS update denials 83 const writeClient = serviceRoleClient !== null && serviceRoleClient !== void 0 ? serviceRoleClient : enhancedSupabase; 84 const dbUpdate = transformToDatabase(updateData); 85 const updateBaseQuery = writeClient.from("companies").update(dbUpdate).eq("id", company.id);
86 const updateQuery = normalizedLastKnownUpdatedAt ? updateBaseQuery.eq("updated_at", normalizedLastKnownUpdatedAt) : updateBaseQuery; 87 const { data: updated, error } = normalizedLastKnownUpdatedAt ? await updateQuery.select().maybeSingle() : await updateQuery.select().single(); 88 if (error) { 89 // Preserve structured details from EnhancedSupabase normalization 90 const err = new Error("Failed to update company: ".concat(error.message || "unknown error")); 91 try { 92 err.code = error === null || error === void 0 ? void 0 : error.code; 93 err.details = error === null || error === void 0 ? void 0 : error.details; 94 err.hint = error === null || error === void 0 ? void 0 : error.hint; 95 err.raw = error; 96 } catch (e) {} 97 throw err; 98 } 99 if (normalizedLastKnownUpdatedAt && !updated) { 100 const { data: existingCompany, error: existenceError } = await writeClient.from("companies").select("id").eq("id", company.id).maybeSingle(); 101 if (existenceError) { 102 throw new Error("Failed to verify company version: ".concat(existenceError.message || "unknown error")); 103 } 104 if (existingCompany) { 105 throw new SettingsVersionMismatchError(); 106 } 107 throw new Error("Failed to update company: No rows found"); 108 } 109 // Optionally refresh cached company data by returning success; callers may refetch. 110 try { 111 console.debug("updateCompany: updated row", updated); 112 } catch (e) {} 113 return { 114 success: true, 115 applied: true 116 }; 117} 118 119 120/***/ }), 121 122/***/ 37314: 123/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 124 125/* unused harmony exports serviceRoleClient, isServiceRoleEnabled, classifyAuthVerificationFailure, getCurrentUserClassified, getCurrentUser, getUserCompanyRole, readActivePermissionMemberships, verifyPermission, createAuditLog, errorResponses */ 126/* harmony import */ var _supabase_supabase_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(42367); 127/* harmony import */ var _lib_permissions__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(57791); 128/* harmony import */ var _lib_supabase_field_transformer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(68762); 129/* provided dependency */ var process = __webpack_require__(44822); 130/* provided dependency */ var Buffer = __webpack_require__(44226)["hp"]; 131 132 133 134// Service role client for server-side operations that bypass RLS 135// This should ONLY be used in API routes with proper permission checks 136const supabaseUrl = "https://api.sendwork.com" || 0; 137const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY; 138const supabaseAuthIssuer = process.env.SUPABASE_AUTH_ISSUER; 139// Validate required env var (dev-only to avoid noisy production builds) 140if (false) {} 141// Create service role client only if key is available 142const serviceRoleClient = supabaseServiceRoleKey && "https://api.sendwork.com" && hasTrustedIssuerConfiguration("".concat(supabaseUrl.replace(/\/$/, ""), "/auth/v1")) ? (0,_supabase_supabase_js__WEBPACK_IMPORTED_MODULE_2__/* .createClient */ .UU)(supabaseUrl, supabaseServiceRoleKey, { 143 auth: { 144 autoRefreshToken: false, 145 persistSession: false, 146 detectSessionInUrl: false 147 } 148}) : null; 149// Helper to check if service role is enabled 150const isServiceRoleEnabled = ()=>serviceRoleClient !== null; 151function isJwtClaimsRecord(value) { 152 return typeof value === "object" && value !== null && !Array.isArray(value); 153} 154function hasTrustedIssuerConfiguration(issuer) { 155 if (!issuer || issuer.trim() !== issuer) return false; 156 try { 157 const url = new URL(issuer); 158 const loopback = [ 159 "localhost", 160 "127.0.0.1", 161 "[::1]" 162 ].includes(url.hostname); 163 return (url.protocol === "https:" || url.protocol === "http:" && loopback) && !url.username && !url.password && !url.search && !url.hash && url.pathname === "/auth/v1" && url.href === issuer; 164 } catch (e) { 165 return false; 166 } 167} 168function hasJwtStructure(token, issuer) { 169 if (!/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(token)) return false;
170 try { 171 const [headerSegment, payloadSegment] = token.split("."); 172 const header = JSON.parse(Buffer.from(headerSegment, "base64url").toString("utf8")); 173 const payload = JSON.parse(Buffer.from(payloadSegment, "base64url").toString("utf8")); 174 return isJwtClaimsRecord(header) && hasValidUserClaims(payload, issuer) && [ 175 "HS256", 176 "ES256", 177 "RS256" 178 ].includes(String(header.alg)); 179 } catch (e) { 180 return false; 181 } 182} 183// Signature verification belongs to the installed SDK. These checks constrain 184// already verified claims to this application's explicit user-token contract. 185function hasValidUserClaims(claims, issuer) { 186 if (!isJwtClaimsRecord(claims)) return false; 187 const now = Math.floor(Date.now() / 1000); 188 const numericDate = (value)=>typeof value === "number" && Number.isSafeInteger(value) && value >= 0; 189 const audience = claims.aud; 190 return claims.iss === issuer && typeof claims.sub === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(claims.sub) && claims.role === "authenticated" && (audience === "authenticated" || Array.isArray(audience) && audience.length === 1 && audience[0] === "authenticated") && numericDate(claims.iat) && claims.iat <= now && numericDate(claims.exp) && claims.exp > now && claims.exp > claims.iat && (claims.nbf === undefined || numericDate(claims.nbf) && claims.nbf <= now && claims.nbf < claims.exp); 191} 192function createAuthFailure(status, code, message) { 193 return { 194 status, 195 code, 196 message 197 }; 198} 199function isInvalidAuthError(error) { 200 if (error instanceof AuthApiError) { 201 return error.status === 401 || error.status >= 400 && error.status < 500 && (error.code === "bad_jwt" || error.code === "invalid_jwt"); 202 } 203 if (typeof error !== "object" || error === null) { 204 return false; 205 } 206 const candidate = error; 207 return candidate.status === 401 || candidate.code === "invalid_jwt" || candidate.name === "AuthInvalidJwtError" || typeof candidate.message === "string" && /^(Missing exp claim|JWT has expired|Invalid alg claim)$/.test(candidate.message); 208} 209function classifyAuthVerificationFailure(error) { 210 let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; 211 const { authHeader, serviceRoleConfigured = true, user = null } = options; 212 if (!serviceRoleConfigured) { 213 return createAuthFailure(503, "AUTH_UNAVAILABLE", "Authentication verification unavailable"); 214 } 215 if (authHeader !== undefined && (!authHeader || !authHeader.startsWith("Bearer ") || authHeader.length <= "Bearer ".length)) { 216 return createAuthFailure(401, "AUTH_REQUIRED", "Unauthorized"); 217 } 218 if (!error && !user) { 219 return createAuthFailure(401, "AUTH_REQUIRED", "Unauthorized"); 220 } 221 if (isAuthSessionMissingError(error)) { 222 return createAuthFailure(401, "AUTH_REQUIRED", "Unauthorized"); 223 } 224 if (isAuthRetryableFetchError(error)) { 225 return createAuthFailure(504, "AUTH_TIMEOUT", "Authentication verification timed out"); 226 } 227 if (isInvalidAuthError(error)) { 228 return createAuthFailure(401, "AUTH_INVALID", "Unauthorized"); 229 } 230 return createAuthFailure(502, "AUTH_UPSTREAM_ERROR", "Authentication verification failed"); 231} 232async function getCurrentUserClassified(request) { 233 if (!serviceRoleClient || !hasTrustedIssuerConfiguration(supabaseAuthIssuer)) { 234 return { 235 user: null, 236 authFailure: classifyAuthVerificationFailure(null, { 237 serviceRoleConfigured: false 238 }) 239 }; 240 } 241 const authHeader = request.headers.get("authorization"); 242 if (!authHeader || !authHeader.startsWith("Bearer ") || authHeader.length <= "Bearer ".length) { 243 return { 244 user: null, 245 authFailure: classifyAuthVerificationFailure(null, { 246 authHeader 247 }) 248 }; 249 } 250 const token = authHeader.slice("Bearer ".length); 251 const invalid = ()=>({ 252 user: null, 253 authFailure: createAuthFailure(401, "AUTH_INVALID", "Unauthorized") 254 }); 255 // Syntax rejection is not verification and never creates an identity. 256 if (!hasJwtStructure(token, supabaseAuthIssuer)) return invalid(); 257 try { 258 const { data: verified, error: claimsError } = await serviceRoleClient.auth.getClaims(token); 259 if (claimsError) { 260 return { 261 user: null, 262 authFailure: classifyAuthVerificationFailure(claimsError, { 263 authHeader 264 }) 265 }; 266 } 267 if (!hasValidUserClaims(verified === null || verified === void 0 ? void 0 : verified.claims, supabaseAuthIssuer)) return invalid(); 268 const { data: { user }, error } = await serviceRoleClient.auth.getUser(token); 269 if (error || !user) { 270 return { 271 user: null, 272 authFailure: classifyAuthVerificationFailure(error, { 273 authHeader, 274 user 275 }) 276 }; 277 } 278 if (user.id !== verified.claims.sub || !hasValidUserClaims(verified.claims, supabaseAuthIssuer)) return invalid(); 279 // Current Auth identity/metadata comes only from getUser, never the JWT. 280 return { 281 user, 282 authFailure: null 283 }; 284 } catch (error) { 285 return { 286 user: null, 287 authFailure: classifyAuthVerificationFailure(error, { 288 authHeader 289 }) 290 }; 291 } 292} 293// Helper to get current user from request headers 294async function getCurrentUser(request) { 295 const { user } = await getCurrentUserClassified(request); 296 return user; 297} 298// Helper to get user's company and role 299async function getUserCompanyRole(userId) { 300 var _data_roles, _data_role; 301 if (!serviceRoleClient) { 302 return null; 303 } 304 // Fetch primary membership and join role name; avoid filtering on non-existent columns. 305 const { data, error } = await serviceRoleClient.from("company_members").select("company_id, role_id, roles(name)").eq("user_id", userId).eq("is_primary", true).maybeSingle(); 306 if (error || !data) { 307 return null; 308 } 309 const companyId = data.company_id; 310 const roleId = data.role_id; 311 const roleName = (data === null || data === void 0 ? void 0 : (_data_roles = data.roles) === null || _data_roles === void 0 ? void 0 : _data_roles.name) || (data === null || data === void 0 ? void 0 : (_data_role = data.role) === null || _data_role === void 0 ? void 0 : _data_role.name); 312 return { 313 companyId, 314 roleId, 315 roleName 316 }; 317} 318function readOwnDataProperty(value, property) { 319 const descriptor = Object.getOwnPropertyDescriptor(value, property); 320 if (!descriptor || !("value" in descriptor)) { 321 return { 322 ok: false 323 }; 324 } 325 return { 326 ok: true, 327 value: descriptor.value 328 }; 329} 330function hasExactEnumerableKeys(value, expected) {
331 const keys = Object.keys(value).sort(); 332 return keys.length === expected.length && expected.every((key, index)=>keys[index] === key); 333} 334/** 335 * Reads the bounded active-membership authority shape used by the canonical 336 * permission resolver. The principal is already server-derived; this adapter 337 * never accepts a role, status, company, or capability claim from a client. 338 */ async function readActivePermissionMemberships(principal) { 339 let client = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : serviceRoleClient; 340 try { 341 if (typeof principal !== "object" || principal === null || !client) { 342 return { 343 ok: false 344 }; 345 } 346 const userIdProperty = readOwnDataProperty(principal, "userId"); 347 const companyIdProperty = readOwnDataProperty(principal, "companyId"); 348 if (!userIdProperty.ok || typeof userIdProperty.value !== "string" || userIdProperty.value.length === 0 || userIdProperty.value.trim() !== userIdProperty.value || !companyIdProperty.ok || typeof companyIdProperty.value !== "string" || companyIdProperty.value.length === 0 || companyIdProperty.value.trim() !== companyIdProperty.value) { 349 return { 350 ok: false 351 }; 352 } 353 const userId = userIdProperty.value; 354 const companyId = companyIdProperty.value; 355 const queryResult = await client.from("company_members").select("status, roles(name)").eq("user_id", userId).eq("company_id", companyId).limit(2); 356 if (typeof queryResult !== "object" || queryResult === null) { 357 return { 358 ok: false 359 }; 360 } 361 const dataProperty = readOwnDataProperty(queryResult, "data"); 362 const errorProperty = readOwnDataProperty(queryResult, "error"); 363 if (!dataProperty.ok || !errorProperty.ok || errorProperty.value !== null || !Array.isArray(dataProperty.value)) { 364 return { 365 ok: false 366 }; 367 } 368 const rows = dataProperty.value; 369 const lengthProperty = readOwnDataProperty(rows, "length"); 370 if (!lengthProperty.ok || typeof lengthProperty.value !== "number" || !Number.isInteger(lengthProperty.value) || lengthProperty.value < 0 || lengthProperty.value > 2) { 371 return { 372 ok: false 373 }; 374 } 375 const memberships = []; 376 for(let index = 0; index < lengthProperty.value; index += 1){ 377 const rowProperty = readOwnDataProperty(rows, index); 378 if (!rowProperty.ok || typeof rowProperty.value !== "object" || rowProperty.value === null || Array.isArray(rowProperty.value) || !hasExactEnumerableKeys(rowProperty.value, [ 379 "roles", 380 "status" 381 ])) { 382 return { 383 ok: false 384 }; 385 } 386 const statusProperty = readOwnDataProperty(rowProperty.value, "status"); 387 const rolesProperty = readOwnDataProperty(rowProperty.value, "roles"); 388 if (!statusProperty.ok || !rolesProperty.ok || typeof rolesProperty.value !== "object" || rolesProperty.value === null || Array.isArray(rolesProperty.value) || !hasExactEnumerableKeys(rolesProperty.value, [ 389 "name" 390 ])) { 391 return { 392 ok: false 393 }; 394 } 395 const roleNameProperty = readOwnDataProperty(rolesProperty.value, "name"); 396 if (!roleNameProperty.ok) { 397 return { 398 ok: false 399 }; 400 } 401 memberships.push({ 402 status: statusProperty.value, 403 roleName: roleNameProperty.value 404 }); 405 } 406 return { 407 ok: true, 408 memberships 409 }; 410 } catch (e) { 411 return { 412 ok: false 413 }; 414 } 415} 416async function verifyPermission(userId, companyId) { 417 let requiredPermission = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : [ 418 "owner", 419 "admin", 420 "staff" 421 ]; 422 var _data_roles, _data_role; 423 if (isPermissionCapability(requiredPermission)) { 424 return resolvePermission({ 425 userId, 426 companyId 427 }, requiredPermission, readActivePermissionMemberships); 428 } 429 if (typeof requiredPermission === "string") { 430 return false;
431 } 432 if (!serviceRoleClient) { 433 return false; 434 } 435 const { data, error } = await serviceRoleClient.from("company_members").select("role_id, roles(name)").eq("user_id", userId).eq("company_id", companyId).maybeSingle(); 436 if (error || !data) return false; 437 const roleNameRaw = (data === null || data === void 0 ? void 0 : (_data_roles = data.roles) === null || _data_roles === void 0 ? void 0 : _data_roles.name) || (data === null || data === void 0 ? void 0 : (_data_role = data.role) === null || _data_role === void 0 ? void 0 : _data_role.name); 438 const roleName = typeof roleNameRaw === "string" ? roleNameRaw.toLowerCase() : undefined; 439 if (!roleName) return false; 440 return requiredPermission.map((r)=>r.toLowerCase()).includes(roleName); 441} 442// Helper to create audit log entry 443async function createAuditLog(tableName, operation, userId, companyId, recordId, oldData, newData) { 444 if (!serviceRoleClient) { 445 console.warn("[createAuditLog] Service role client not available, skipping audit log"); 446 return; 447 } 448 try { 449 const payload = transformToDatabase({ 450 tableName, 451 operation, 452 userId, 453 companyId, 454 recordId, 455 oldData: oldData ? JSON.stringify(oldData) : null, 456 newData: newData ? JSON.stringify(newData) : null 457 }); 458 await serviceRoleClient.from("audit_logs").insert(payload); 459 } catch (error) { 460 console.error("Failed to create audit log:", error); 461 // Don't throw - audit logging should not break the operation 462 } 463} 464// Standard error responses 465const errorResponses = { 466 unauthorized: ()=>Response.json({ 467 error: "Unauthorized" 468 }, { 469 status: 401 470 }), 471 authRequired: ()=>Response.json({ 472 error: "Unauthorized", 473 code: "AUTH_REQUIRED" 474 }, { 475 status: 401 476 }), 477 authInvalid: ()=>Response.json({ 478 error: "Unauthorized", 479 code: "AUTH_INVALID" 480 }, { 481 status: 401 482 }), 483 authTimeout: ()=>Response.json({ 484 error: "Authentication verification timed out", 485 code: "AUTH_TIMEOUT" 486 }, { 487 status: 504 488 }), 489 authUpstreamError: ()=>Response.json({ 490 error: "Authentication verification failed", 491 code: "AUTH_UPSTREAM_ERROR" 492 }, { 493 status: 502 494 }), 495 authUnavailable: ()=>Response.json({ 496 error: "Authentication verification unavailable", 497 code: "AUTH_UNAVAILABLE" 498 }, { 499 status: 503 500 }), 501 authFailure: (failure)=>{ 502 switch(failure.code){ 503 case "AUTH_REQUIRED": 504 return errorResponses.authRequired(); 505 case "AUTH_INVALID": 506 return errorResponses.authInvalid(); 507 case "AUTH_TIMEOUT": 508 return errorResponses.authTimeout(); 509 case "AUTH_UNAVAILABLE": 510 return errorResponses.authUnavailable(); 511 default: 512 return errorResponses.authUpstreamError(); 513 } 514 }, 515 forbidden: ()=>Response.json({ 516 error: "Forbidden" 517 }, { 518 status: 403 519 }), 520 notFound: ()=>Response.json({ 521 error: "Not found" 522 }, { 523 status: 404 524 }), 525 badRequest: (message)=>Response.json({ 526 error: message 527 }, { 528 status: 400 529 }), 530 serverError: function() { 531 let message = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "Internal server error"; 532 return Response.json({ 533 error: message 534 }, { 535 status: 500 536 }); 537 } 538}; 539 540 541/***/ }), 542 543/***/ 57791: 544/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 545 546/* harmony export */ __webpack_require__.d(__webpack_exports__, { 547/* harmony export */ pl: () => (/* binding */ hasDefaultPermission) 548/* harmony export */ }); 549/* unused harmony exports PERMISSION_ROLES, DEFAULT_PERMISSION_MATRIX, PERMISSION_CAPABILITIES, isPermissionRole, isPermissionCapability, resolvePermission */ 550const PERMISSION_ROLES = Object.freeze([ 551 "owner", 552 "admin", 553 "manager", 554 "member", 555 "staff", 556 "viewer", 557 "customer" 558]); 559const freezeRoleDecisions = (decisions)=>Object.freeze(decisions); 560const permissionMatrix = { 561 "dashboard.access": freezeRoleDecisions({ 562 owner: true, 563 admin: true, 564 manager: true, 565 member: true, 566 staff: true, 567 viewer: true, 568 customer: true 569 }), 570 "tasks.view": freezeRoleDecisions({ 571 owner: true, 572 admin: true, 573 manager: true, 574 member: true, 575 staff: true, 576 viewer: true, 577 customer: false 578 }), 579 "tasks.create": freezeRoleDecisions({ 580 owner: true, 581 admin: true, 582 manager: true, 583 member: true, 584 staff: true, 585 viewer: false, 586 customer: false 587 }), 588 "tasks.edit": freezeRoleDecisions({ 589 owner: true, 590 admin: true, 591 manager: true, 592 member: true, 593 staff: true, 594 viewer: false, 595 customer: false 596 }), 597 "assignment.visibility": freezeRoleDecisions({ 598 owner: true, 599 admin: true, 600 manager: true, 601 member: false, 602 staff: false, 603 viewer: true, 604 customer: false 605 }), 606 "scheduling.view": freezeRoleDecisions({ 607 owner: true, 608 admin: true, 609 manager: true, 610 member: true, 611 staff: true, 612 viewer: true, 613 customer: false 614 }), 615 "timeline.view": freezeRoleDecisions({ 616 owner: true, 617 admin: true, 618 manager: true, 619 member: true, 620 staff: true, 621 viewer: true, 622 customer: false 623 }), 624 "customers.view": freezeRoleDecisions({ 625 owner: true, 626 admin: true, 627 manager: true, 628 member: true, 629 staff: true, 630 viewer: true, 631 customer: false 632 }), 633 "customers.viewContactDetails": freezeRoleDecisions({ 634 owner: true, 635 admin: true, 636 manager: true, 637 member: true, 638 staff: true, 639 viewer: false, 640 customer: false 641 }), 642 "customers.create": freezeRoleDecisions({ 643 owner: true, 644 admin: true, 645 manager: true, 646 member: true, 647 staff: true, 648 viewer: false, 649 customer: false 650 }), 651 "customers.edit": freezeRoleDecisions({ 652 owner: true, 653 admin: true, 654 manager: true, 655 member: true, 656 staff: true, 657 viewer: false, 658 customer: false 659 }), 660 "team.viewRoster": freezeRoleDecisions({ 661 owner: true, 662 admin: true, 663 manager: true, 664 member: true, 665 staff: true, 666 viewer: true, 667 customer: false 668 }), 669 "team.viewInvitationStatus": freezeRoleDecisions({ 670 owner: true, 671 admin: true, 672 manager: false, 673 member: false, 674 staff: false, 675 viewer: false, 676 customer: false 677 }), 678 "team.viewFullCoworkerEmail": freezeRoleDecisions({ 679 owner: true, 680 admin: true, 681 manager: false, 682 member: false, 683 staff: false, 684 viewer: false, 685 customer: false 686 }), 687 "team.viewMaskedCoworkerEmail": freezeRoleDecisions({ 688 owner: false, 689 admin: false, 690 manager: true, 691 member: true, 692 staff: true, 693 viewer: true, 694 customer: false 695 }), 696 "team.manage": freezeRoleDecisions({ 697 owner: true, 698 admin: true, 699 manager: false, 700 member: false, 701 staff: false, 702 viewer: false, 703 customer: false 704 }), 705 "team.manageRoles": freezeRoleDecisions({ 706 owner: true, 707 admin: true, 708 manager: false, 709 member: false, 710 staff: false, 711 viewer: false, 712 customer: false 713 }), 714 "invoices.view": freezeRoleDecisions({ 715 owner: true, 716 admin: true, 717 manager: true, 718 member: true, 719 staff: true, 720 viewer: true, 721 customer: false 722 }), 723 "invoices.create": freezeRoleDecisions({ 724 owner: true, 725 admin: true, 726 manager: true, 727 member: true, 728 staff: true, 729 viewer: false, 730 customer: false 731 }), 732 "invoices.edit": freezeRoleDecisions({ 733 owner: true, 734 admin: true, 735 manager: true, 736 member: true, 737 staff: true, 738 viewer: false, 739 customer: false 740 }), 741 "invoices.send": freezeRoleDecisions({ 742 owner: true, 743 admin: true, 744 manager: false, 745 member: true, 746 staff: true, 747 viewer: false, 748 customer: false 749 }), 750 "quotes.view": freezeRoleDecisions({ 751 owner: true, 752 admin: true, 753 manager: true, 754 member: true, 755 staff: true, 756 viewer: false, 757 customer: false 758 }), 759 "quotes.create": freezeRoleDecisions({ 760 owner: true, 761 admin: true, 762 manager: true, 763 member: true, 764 staff: true, 765 viewer: false, 766 customer: false 767 }), 768 "quotes.edit": freezeRoleDecisions({ 769 owner: true, 770 admin: true, 771 manager: true, 772 member: true, 773 staff: true, 774 viewer: false, 775 customer: false 776 }), 777 "quotes.send": freezeRoleDecisions({ 778 owner: true, 779 admin: true, 780 manager: false, 781 member: false, 782 staff: true, 783 viewer: false, 784 customer: false 785 }), 786 "payments.view": freezeRoleDecisions({ 787 owner: true, 788 admin: true, 789 manager: true, 790 member: true, 791 staff: true, 792 viewer: true, 793 customer: false 794 }), 795 "payments.record": freezeRoleDecisions({ 796 owner: true, 797 admin: true, 798 manager: false, 799 member: false, 800 staff: true, 801 viewer: false, 802 customer: false 803 }), 804 "payments.collect": freezeRoleDecisions({ 805 owner: true, 806 admin: true, 807 manager: false, 808 member: false, 809 staff: true, 810 viewer: false, 811 customer: false 812 }), 813 "ledger.export": freezeRoleDecisions({ 814 owner: true, 815 admin: true, 816 manager: false, 817 member: false, 818 staff: true, 819 viewer: false, 820 customer: false 821 }), 822 "reports.view": freezeRoleDecisions({ 823 owner: true, 824 admin: true, 825 manager: true, 826 member: false, 827 staff: false, 828 viewer: false, 829 customer: false 830 }), 831 "analytics.view": freezeRoleDecisions({ 832 owner: true, 833 admin: true, 834 manager: false, 835 member: false, 836 staff: false, 837 viewer: false, 838 customer: false 839 }), 840 "settings.view": freezeRoleDecisions({ 841 owner: true, 842 admin: true, 843 manager: false, 844 member: false, 845 staff: false, 846 viewer: false, 847 customer: false 848 }), 849 "settings.edit": freezeRoleDecisions({ 850 owner: true, 851 admin: true, 852 manager: false, 853 member: false, 854 staff: false, 855 viewer: false, 856 customer: false 857 }), 858 "billing.manage": freezeRoleDecisions({ 859 owner: true, 860 admin: true, 861 manager: false, 862 member: false, 863 staff: false, 864 viewer: false, 865 customer: false 866 }), 867 "aiCommandCenter.access": freezeRoleDecisions({ 868 owner: true, 869 admin: true, 870 manager: true, 871 member: true, 872 staff: true, 873 viewer: true, 874 customer: false 875 }), 876 "aiCommandCenter.executeActions": freezeRoleDecisions({ 877 owner: true, 878 admin: true, 879 manager: true, 880 member: true, 881 staff: true, 882 viewer: false, 883 customer: false 884 }) 885}; 886const DEFAULT_PERMISSION_MATRIX = Object.freeze(permissionMatrix); 887const PERMISSION_CAPABILITIES = Object.freeze(Object.keys(DEFAULT_PERMISSION_MATRIX)); 888const permissionRoleSet = new Set(PERMISSION_ROLES); 889const permissionCapabilitySet = new Set(PERMISSION_CAPABILITIES); 890function isPermissionRole(value) { 891 return typeof value === "string" && permissionRoleSet.has(value); 892} 893function isPermissionCapability(value) { 894 return typeof value === "string" && permissionCapabilitySet.has(value); 895} 896function hasDefaultPermission(role, capability) { 897 if (!isPermissionRole(role) || !isPermissionCapability(capability)) { 898 return false;
899 } 900 return DEFAULT_PERMISSION_MATRIX[capability][role]; 901} 902const isValidPrincipalIdentifier = (value)=>typeof value === "string" && value.length > 0 && value.trim() === value; 903const readOwnDataProperty = (value, property)=>{ 904 const descriptor = Object.getOwnPropertyDescriptor(value, property); 905 if (!descriptor || !("value" in descriptor)) { 906 return { 907 ok: false 908 }; 909 } 910 return { 911 ok: true, 912 value: descriptor.value 913 }; 914}; 915/** 916 * Resolves a capability for a principal whose identity and tenant were already 917 * derived by a trusted server boundary. This module deliberately has no live 918 * data client; consumers must inject the narrow membership reader. 919 */ async function resolvePermission(principal, capability, readMemberships) { 920 try { 921 if (typeof principal !== "object" || principal === null || Array.isArray(principal) || !isPermissionCapability(capability)) { 922 return false; 923 } 924 const userIdProperty = readOwnDataProperty(principal, "userId"); 925 const companyIdProperty = readOwnDataProperty(principal, "companyId"); 926 if (!userIdProperty.ok || !companyIdProperty.ok || !isValidPrincipalIdentifier(userIdProperty.value) || !isValidPrincipalIdentifier(companyIdProperty.value)) { 927 return false; 928 } 929 const userId = userIdProperty.value; 930 const companyId = companyIdProperty.value; 931 const readResult = await readMemberships({ 932 userId, 933 companyId 934 }); 935 if (typeof readResult !== "object" || readResult === null || Array.isArray(readResult)) { 936 return false; 937 } 938 const okProperty = readOwnDataProperty(readResult, "ok"); 939 const membershipsProperty = readOwnDataProperty(readResult, "memberships"); 940 if (!okProperty.ok || okProperty.value !== true || !membershipsProperty.ok || !Array.isArray(membershipsProperty.value)) { 941 return false; 942 } 943 const memberships = membershipsProperty.value; 944 const lengthProperty = readOwnDataProperty(memberships, "length"); 945 const membershipProperty = readOwnDataProperty(memberships, 0); 946 if (!lengthProperty.ok || lengthProperty.value !== 1 || !membershipProperty.ok) { 947 return false; 948 } 949 const membership = membershipProperty.value; 950 if (typeof membership !== "object" || membership === null || Array.isArray(membership)) { 951 return false; 952 } 953 const statusProperty = readOwnDataProperty(membership, "status"); 954 const roleNameProperty = readOwnDataProperty(membership, "roleName"); 955 if (!statusProperty.ok || statusProperty.value !== "active" || !roleNameProperty.ok || !isPermissionRole(roleNameProperty.value)) { 956 return false; 957 } 958 const roleName = roleNameProperty.value; 959 return DEFAULT_PERMISSION_MATRIX[capability][roleName]; 960 } catch (e) { 961 return false; 962 } 963} 964 965 966/***/ }), 967 968/***/ 68177: 969/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 970 971 972// EXPORTS 973__webpack_require__.d(__webpack_exports__, { 974 cn: () => (/* binding */ cn), 975 sg: () => (/* binding */ debounce), 976 z3: () => (/* binding */ formatBytes), 977 Yq: () => (/* reexport */ formatDate) 978}); 979 980// UNUSED EXPORTS: COUNTRIES, INDUSTRIES, formatCurrency, formatDateTime, formatDateToLocal 981 982// EXTERNAL MODULE: ./node_modules/.pnpm/[email protected]/node_modules/clsx/dist/clsx.mjs 983var clsx = __webpack_require__(79533); 984// EXTERNAL MODULE: ./node_modules/.pnpm/[email protected]/node_modules/tailwind-merge/dist/bundle-mjs.mjs 985var bundle_mjs = __webpack_require__(50712); 986// EXTERNAL MODULE: ./lib/company/settings.ts 987var settings = __webpack_require__(92008); 988;// ./lib/utils/date-formatter.ts 989 990function formatDate(dateString) { 991 if (!dateString) return "—"; 992 try { 993 const date = typeof dateString === "string" ? new Date(dateString) : dateString; 994 if (isNaN(date.getTime())) { 995 return "—"; 996 } 997 return new Intl.DateTimeFormat((0,settings/* getCompanyLocale */.WG)(), { 998 year: "numeric", 999 month: "short", 1000 day: "numeric" 1001 }).format(date); 1002 } catch (error) { 1003 return "—"; 1004 } 1005} 1006function formatPhoneNumber(phone) { 1007 if (!phone) return "—"; 1008 // Remove all non-digits 1009 const cleaned = phone.replace(/\D/g, ""); 1010 // Format based on length 1011 if (cleaned.length === 10) { 1012 return "(".concat(cleaned.slice(0, 3), ") ").concat(cleaned.slice(3, 6), "-").concat(cleaned.slice(6)); 1013 } else if (cleaned.length === 11 && cleaned[0] === "1") { 1014 return "+1 (".concat(cleaned.slice(1, 4), ") ").concat(cleaned.slice(4, 7), "-").concat(cleaned.slice(7)); 1015 } 1016 // Return original if it doesn't match expected formats 1017 return phone; 1018} 1019function formatAddress(address) { 1020 if (!address) return "—"; 1021 const parts = []; 1022 if (address.street) parts.push(address.street); 1023 if (address.city) parts.push(address.city); 1024 if (address.state) parts.push(address.state); 1025 if (address.zip) parts.push(address.zip); 1026 return parts.length > 0 ? parts.join(", ") : "—"; 1027} 1028function formatCurrency(amount, currency) { 1029 if (amount === null || amount === undefined) return "—"; 1030 return new Intl.NumberFormat(getCompanyLocale(), { 1031 style: "currency", 1032 currency: currency || getCompanyCurrency(), 1033 minimumFractionDigits: 2, 1034 maximumFractionDigits: 2 1035 }).format(amount); 1036} 1037function formatDateTime(dateString) { 1038 if (!dateString) return "—"; 1039 try { 1040 const date = typeof dateString === "string" ? new Date(dateString) : dateString; 1041 if (isNaN(date.getTime())) { 1042 return "—"; 1043 } 1044 return new Intl.DateTimeFormat(getCompanyLocale(), { 1045 year: "numeric", 1046 month: "short", 1047 day: "numeric", 1048 hour: "numeric", 1049 minute: "2-digit", 1050 hour12: true 1051 }).format(date); 1052 } catch (error) { 1053 return "—"; 1054 } 1055} 1056 1057;// ./lib/utils.ts 1058 1059 1060function cn() { 1061 for(var _len = arguments.length, inputs = new Array(_len), _key = 0; _key < _len; _key++){ 1062 inputs[_key] = arguments[_key]; 1063 } 1064 return (0,bundle_mjs/* twMerge */.QP)((0,clsx/* clsx */.$)(inputs)); 1065} 1066// Static data for dropdown options 1067const COUNTRIES = [ 1068 { 1069 value: "USA", 1070 label: "USA" 1071 }, 1072 { 1073 value: "UK", 1074 label: "UK" 1075 }, 1076 { 1077 value: "Canada", 1078 label: "Canada" 1079 }, 1080 { 1081 value: "Colombia", 1082 label: "Colombia" 1083 }, 1084 { 1085 value: "Chile", 1086 label: "Chile" 1087 }, 1088 { 1089 value: "Argentina", 1090 label: "Argentina" 1091 }, 1092 { 1093 value: "Brazil", 1094 label: "Brazil" 1095 }, 1096 { 1097 value: "Mexico", 1098 label: "Mexico" 1099 }, 1100 { 1101 value: "Spain", 1102 label: "Spain" 1103 }, 1104 { 1105 value: "Portugal", 1106 label: "Portugal" 1107 }, 1108 { 1109 value: "France", 1110 label: "France" 1111 }, 1112 { 1113 value: "Ireland", 1114 label: "Ireland" 1115 }, 1116 { 1117 value: "Australia", 1118 label: "Australia" 1119 }, 1120 { 1121 value: "New Zealand", 1122 label: "New Zealand" 1123 } 1124]; 1125const INDUSTRIES = [ 1126 { 1127 value: "air-conditioning", 1128 label: "Air Conditioning" 1129 }, 1130 { 1131 value: "child-elderly-services",
1132 label: "Child & Elderly Services" 1133 }, 1134 { 1135 value: "cleaning-services", 1136 label: "Cleaning Services" 1137 }, 1138 { 1139 value: "dog-grooming", 1140 label: "Dog Grooming" 1141 }, 1142 { 1143 value: "gardening", 1144 label: "Gardening" 1145 }, 1146 { 1147 value: "general-contractors", 1148 label: "General Contractors" 1149 }, 1150 { 1151 value: "painting", 1152 label: "Painting" 1153 }, 1154 { 1155 value: "pest-control", 1156 label: "Pest Control" 1157 }, 1158 { 1159 value: "rentals", 1160 label: "Rentals" 1161 }, 1162 { 1163 value: "spa-services", 1164 label: "Spa Services" 1165 }, 1166 { 1167 value: "vacation-rentals", 1168 label: "Vacation Rentals" 1169 }, 1170 { 1171 value: "window-services", 1172 label: "Window Services" 1173 }, 1174 { 1175 value: "electrical-services", 1176 label: "Electrical Services" 1177 }, 1178 { 1179 value: "locksmith", 1180 label: "Locksmith" 1181 }, 1182 { 1183 value: "housekeeping", 1184 label: "Housekeeping" 1185 }, 1186 { 1187 value: "moving-services", 1188 label: "Moving Services" 1189 }, 1190 { 1191 value: "plumbing", 1192 label: "Plumbing" 1193 }, 1194 { 1195 value: "pool-services", 1196 label: "Pool Services" 1197 }, 1198 { 1199 value: "handyman-services", 1200 label: "Handyman Services" 1201 }, 1202 { 1203 value: "roofing-contractors", 1204 label: "Roofing Contractors" 1205 }, 1206 { 1207 value: "lawn-care-landscaping", 1208 label: "Lawn Care & Landscaping" 1209 }, 1210 { 1211 value: "hvac", 1212 label: "HVAC (Heating, Ventilation, and Air Conditioning) Services" 1213 }, 1214 { 1215 value: "appliance-repair", 1216 label: "Appliance Repair" 1217 }, 1218 { 1219 value: "smart-home-installation", 1220 label: "Smart Home Installation" 1221 }, 1222 { 1223 value: "security-system-installation", 1224 label: "Security System Installation" 1225 }, 1226 { 1227 value: "gutter-cleaning-repair", 1228 label: "Gutter Cleaning & Repair" 1229 }, 1230 { 1231 value: "junk-removal-hauling", 1232 label: "Junk Removal & Hauling" 1233 }, 1234 { 1235 value: "solar-panel-installation", 1236 label: "Solar Panel Installation & Maintenance" 1237 }, 1238 { 1239 value: "home-inspection", 1240 label: "Home Inspection Services" 1241 }, 1242 { 1243 value: "garage-door-repair", 1244 label: "Garage Door Repair & Installation" 1245 }, 1246 { 1247 value: "carpentry-woodworking", 1248 label: "Carpentry & Woodworking" 1249 }, 1250 { 1251 value: "tile-flooring-installation", 1252 label: "Tile & Flooring Installation" 1253 }, 1254 { 1255 value: "paving-concrete", 1256 label: "Paving & Concrete Services" 1257 }, 1258 { 1259 value: "interior-design", 1260 label: "Interior Design & Home Staging" 1261 }, 1262 { 1263 value: "home-organization", 1264 label: "Home Organization & Decluttering" 1265 }, 1266 { 1267 value: "well-septic-services", 1268 label: "Well & Septic System Services" 1269 }, 1270 { 1271 value: "pressure-washing", 1272 label: "Pressure Washing & Exterior Cleaning" 1273 }, 1274 { 1275 value: "custom-cabinet-furniture", 1276 label: "Custom Cabinet & Furniture Making" 1277 }, 1278 { 1279 value: "other", 1280 label: "Other" 1281 } 1282]; 1283function formatDateToLocal(date) { 1284 let locale = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "en-US"; 1285 return new Date(date).toLocaleDateString(locale, { 1286 day: "numeric", 1287 month: "short", 1288 year: "numeric" 1289 }); 1290} 1291// Re-export utility functions from date-formatter 1292 1293// Format bytes to human readable format 1294function formatBytes(bytes) { 1295 let decimals = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 2; 1296 if (bytes === 0) return '0 Bytes'; 1297 const k = 1024; 1298 const dm = decimals < 0 ? 0 : decimals; 1299 const sizes = [ 1300 'Bytes', 1301 'KB', 1302 'MB', 1303 'GB', 1304 'TB', 1305 'PB', 1306 'EB', 1307 'ZB', 1308 'YB' 1309 ]; 1310 const i = Math.floor(Math.log(bytes) / Math.log(k)); 1311 return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; 1312} 1313// Debounce utility function 1314function debounce(func, delay) { 1315 let timeoutId; 1316 function debounced() { 1317 for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ 1318 args[_key] = arguments[_key]; 1319 } 1320 clearTimeout(timeoutId); 1321 timeoutId = setTimeout(()=>{ 1322 func(...args); 1323 }, delay); 1324 } 1325 debounced.cancel = ()=>clearTimeout(timeoutId); 1326 return debounced; 1327} 1328 1329 1330/***/ }), 1331 1332/***/ 68762: 1333/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1334 1335/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1336/* harmony export */ vQ: () => (/* binding */ transformToDatabase) 1337/* harmony export */ }); 1338/* unused harmony exports FIELD_NAME_MAP, transformFromDatabase, TransformingSupabaseClient, createTransformingClient */ 1339// Supabase Field Name Transformer 1340// Auto-transforms between database snake_case and TypeScript camel
1340Case 1341// Database field name mapping 1342const FIELD_NAME_MAP = { 1343 // Database (snake_case) → TypeScript (camelCase) 1344 "pricing_type": "pricingType", 1345 "hourly_rate": "hourlyRate", 1346 "estimated_hours": "estimatedHours", 1347 "actual_hours": "actualHours", 1348 "payment_status": "paymentStatus", 1349 "deposit_percentage": "depositPercent", 1350 "deposit_amount": "depositAmount", 1351 "customer_address": "customerAddress", 1352 "assigned_to": "assignedTo", 1353 "special_instructions": "specialInstructions", 1354 "customer_id": "customerId", 1355 "company_id": "companyId", 1356 "locked_country": "lockedCountry", 1357 "country_code": "countryCode", 1358 "country_dial_code": "countryDialCode", 1359 "scheduled_date": "scheduledDate", 1360 "scheduled_time": "scheduledTime", 1361 "cancel_reason": "cancelReason", 1362 "canceled_at": "canceledAt", 1363 "created_at": "createdAt", 1364 "total_tax_amount": "totalTaxAmount", 1365 "quote_id": "quoteId", 1366 "quote_status": "quoteStatus", 1367 "invoice_status": "invoiceStatus", 1368 "require_deposit": "requireDeposit", 1369 "has_deposit": "hasDeposit", 1370 "recurrence_end_date": "recurrenceEndDate", 1371 "recurrence_count": "recurrenceCount", 1372 "recurrence_end_type": "recurrenceEndType", 1373 // Keep these as-is only within DB objects; do not surface as TS identifiers 1374 "deposit_paid": "deposit_paid" 1375}; 1376// Transform database record to TypeScript camelCase 1377function transformFromDatabase(dbRecord) { 1378 if (!dbRecord || typeof dbRecord !== "object") { 1379 return dbRecord; 1380 } 1381 const transformed = {}; 1382 for (const [dbField, tsField] of Object.entries(FIELD_NAME_MAP)){ 1383 if (dbRecord[dbField] !== undefined) { 1384 transformed[tsField] = dbRecord[dbField]; 1385 } 1386 } 1387 // Copy any unmapped fields as-is 1388 for (const [key, value] of Object.entries(dbRecord)){ 1389 if (!FIELD_NAME_MAP[key]) { 1390 transformed[key] = value; 1391 } 1392 } 1393 return transformed; 1394} 1395// --- 1396// Minimal transformToDatabase helper for strict DB boundary enforcement 1397function transformToDatabase(obj) { 1398 const snake = {}; 1399 Object.keys(obj).forEach((key)=>{ 1400 if (obj[key] === undefined) { 1401 return; 1402 } 1403 const snakeKey = key.replace(/[A-Z]/g, (letter)=>"_".concat(letter.toLowerCase())); 1404 snake[snakeKey] = obj[key]; 1405 }); 1406 return snake; 1407} 1408// ---
1409// Enhanced Supabase client wrapper with auto-transform 1410class TransformingSupabaseClient { 1411 // Simple delegation approach - just transform the data 1412 from(table) { 1413 const originalQuery = this.supabase.from(table); 1414 // Return a proxy that intercepts method calls 1415 return new Proxy(originalQuery, { 1416 get (target, prop) { 1417 if (prop === "select") { 1418 return async function() { 1419 let query = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "*"; 1420 const { data, error } = await target.select(query); 1421 if (error) return { 1422 data: null, 1423 error 1424 }; 1425 const transformedData = Array.isArray(data) ? data.map(transformFromDatabase) : data ? transformFromDatabase(data) : null; 1426 return { 1427 data: transformedData, 1428 error 1429 }; 1430 }; 1431 } 1432 if (prop === "insert") { 1433 return async function(record) { 1434 const dbRecord = transformToDatabase(record); 1435 return await target.insert(dbRecord); 1436 }; 1437 } 1438 if (prop === "update") { 1439 return async function(record) { 1440 const dbRecord = transformToDatabase(record); 1441 return await target.update(dbRecord); 1442 }; 1443 } 1444 if (prop === "upsert") { 1445 return async function(record) { 1446 const dbRecord = transformToDatabase(record); 1447 return await target.upsert(dbRecord); 1448 }; 1449 } 1450 // Pass through all other methods 1451 const value = target[prop]; 1452 return typeof value === "function" ? value.bind(target) : value; 1453 } 1454 }); 1455 } 1456 constructor(supabase){ 1457 this.supabase = supabase; 1458 } 1459} 1460// Helper to create transformed supabase client 1461function createTransformingClient(supabase) { 1462 return new TransformingSupabaseClient(supabase); 1463} 1464 1465 1466/***/ }), 1467 1468/***/ 83636: 1469/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1470 1471/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1472/* harmony export */ QE: () => (/* binding */ TaskVersionMismatchError), 1473/* harmony export */ Sr: () => (/* binding */ sanitizeTaskWriteInput) 1474/* harmony export */ }); 1475/* unused harmony exports createTaskSchema, updateTaskSchema, deleteTaskSchema, stopRecurringTaskSeriesSchema, ProtectedTaskFieldError, CustomerVersionMismatchError, SettingsVersionMismatchError, assertNoProtectedTaskFields, sanitizeTaskUpdateInput, filterUuidArray */ 1476/* harmony import */ var zod__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(36619); 1477 1478const taskStatusSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1479 "pending", 1480 "active", 1481 "completed", 1482 "canceled", 1483 "quotePending" 1484]); 1485const paymentStatusSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1486 "paid", 1487 "partial", 1488 "unpaid", 1489 "no-charge" 1490]); 1491const pricingTypeSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1492 "fixed", 1493 "hourly" 1494]); 1495const recurrenceSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1496 "none", 1497 "daily", 1498 "weekly", 1499 "biweekly", 1500 "monthly", 1501 "bimonthly", 1502 "quarterly", 1503 "semiannual", 1504 "custom" 1505]); 1506function snakeField() { 1507 for(var _len = arguments.length, parts = new Array(_len), _key = 0; _key < _len; _key++){ 1508 parts[_key] = arguments[_key]; 1509 } 1510 return parts.join("_"); 1511} 1512const createTaskSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* .object */ .Ik({ 1513 title: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().min(1).max(255), 1514 description: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional(), 1515 customerId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid(), 1516 customerAddressId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional().nullable(), 1517 customerEmail: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().email().optional(), 1518 scheduledDate: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().min(1), 1519 scheduledTime: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1520 boardId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional(), 1521 columnId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional(), 1522 amount: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().nonnegative().optional(), 1523 priority: zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1524 "low", 1525 "medium", 1526 "high", 1527 "urgent" 1528 ]).optional(), 1529 status: taskStatusSchema.optional(), 1530 paymentStatus: paymentStatusSchema.optional(), 1531 // Nullable/optional persistence plumbing for explicit fixed-base storage. 1532 fixedBaseAmount: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().nonnegative().optional().nullable(), 1533 pricingType: pricingTypeSchema.optional(), 1534 hourlyRate: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().nonnegative().optional().nullable(), 1535 estimatedHours: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().nonnegative().optional().nullable(), 1536 subtotal: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().optional().nullable(), 1537 taxAmount: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().optional().nullable(), 1538 totalTaxAmount: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().optional().nullable(), 1539 taxes: zod__WEBPACK_IMPORTED_MODULE_0__/* .unknown */ .L5().optional().nullable(), 1540 taxSelection: zod__WEBPACK_IMPORTED_MODULE_0__/* .unknown */ .L5().optional().nullable(), 1541 specialInstructions: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1542 internalNotes: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1543 hasDeposit: zod__WEBPACK_IMPORTED_MODULE_0__/* .boolean */ .zM().optional().nullable(), 1544 depositPercentage: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().nonnegative().optional().nullable(), 1545 depositAmount: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().nonnegative().optional().nullable(), 1546 depositPaid: zod__WEBPACK_IMPORTED_MODULE_0__/* .boolean */ .zM().optional().nullable(), 1547 assignedTo: zod__WEBPACK_IMPORTED_MODULE_0__/* .array */ .YO(zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj()).optional().nullable(), 1548 quoteId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional().nullable(), 1549 quoteStatus: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1550 invoiceId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional().nullable(), 1551 invoiceStatus: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1552 sendEmail: zod__WEBPACK_IMPORTED_MODULE_0__/* .boolean */ .zM().optional(),
1553 createRequestKey: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().min(1).max(200).optional(), 1554 recurrence: recurrenceSchema.optional(), 1555 recurrenceEndType: zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1556 "date", 1557 "count", 1558 "never" 1559 ]).optional().nullable(), 1560 recurrenceEndDate: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1561 recurrenceCount: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().int().min(1).max(365).optional().nullable(), 1562 occurrenceIds: zod__WEBPACK_IMPORTED_MODULE_0__/* .array */ .YO(zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid()).optional(), 1563 recurrenceSeriesId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional() 1564}).passthrough(); 1565const updateTaskSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* .object */ .Ik({ 1566 id: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid(), 1567 lastKnownUpdatedAt: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().datetime().nullable().optional(), 1568 patch: zod__WEBPACK_IMPORTED_MODULE_0__/* .object */ .Ik({ 1569 title: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().min(1).max(255).optional(), 1570 description: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional(), 1571 customerId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional(), 1572 customerAddressId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional().nullable(), 1573 scheduledDate: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional(), 1574 scheduledTime: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1575 priority: zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1576 "low", 1577 "medium", 1578 "high", 1579 "urgent" 1580 ]).optional(), 1581 status: taskStatusSchema.optional(), 1582 cancelReason: zod__WEBPACK_IMPORTED_MODULE_0__/* .unknown */ .L5().optional(), 1583 canceledAt: zod__WEBPACK_IMPORTED_MODULE_0__/* .unknown */ .L5().optional(), 1584 [snakeField("cancel", "reason")]: zod__WEBPACK_IMPORTED_MODULE_0__/* .unknown */ .L5().optional(), 1585 [snakeField("canceled", "at")]: zod__WEBPACK_IMPORTED_MODULE_0__/* .unknown */ .L5().optional(), 1586 specialInstructions: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1587 internalNotes: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1588 assignedTo: zod__WEBPACK_IMPORTED_MODULE_0__/* .array */ .YO(zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj()).optional().nullable(), 1589 boardId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional().nullable(), 1590 columnId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional().nullable(), 1591 position: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().int().nonnegative().optional(), 1592 recurrence: recurrenceSchema.optional(), 1593 recurrenceEndType: zod__WEBPACK_IMPORTED_MODULE_0__/* ["enum"] */ .k5([ 1594 "date", 1595 "count", 1596 "never" 1597 ]).optional().nullable(), 1598 recurrenceEndDate: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().optional().nullable(), 1599 recurrenceCount: zod__WEBPACK_IMPORTED_MODULE_0__/* .number */ .ai().int().min(1).max(365).optional().nullable() 1600 }).strict().refine((patch)=>Object.keys(patch).length > 0, { 1601 message: "At least one field required" 1602 }) 1603}); 1604const deleteTaskSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* .object */ .Ik({ 1605 id: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid() 1606}); 1607const stopRecurringTaskSeriesSchema = zod__WEBPACK_IMPORTED_MODULE_0__/* .object */ .Ik({ 1608 taskId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid(), 1609 lastKnownUpdatedAt: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().datetime(), 1610 recurrenceSeriesId: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().uuid().optional(), 1611 cancelReason: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().trim().min(1).max(2000).optional(), 1612 includeCancelReasonInEmail: zod__WEBPACK_IMPORTED_MODULE_0__/* .boolean */ .zM().optional(), 1613 canceledAt: zod__WEBPACK_IMPORTED_MODULE_0__/* .string */ .Yj().datetime().optional() 1614}
1614).strict(); 1615const taskPhantomKeys = new Set([ 1616 "id", 1617 "files", 1618 "customer", 1619 "customerAddress", 1620 "lineItems", 1621 "markAsQuote", 1622 "selectedAddressIndex", 1623 "companyId", 1624 "createdBy", 1625 "sendEmail", 1626 "customerEmail", 1627 "createRequestKey", 1628 "quoteProductionIntentId" 1629]); 1630const taskGenericUpdateBlockedKeys = new Set([ 1631 "status", 1632 "cancelReason", 1633 "canceledAt", 1634 "cancel_reason", 1635 "canceled_at" 1636]); 1637const protectedTaskUpdateFields = new Set([ 1638 "taxSelection", 1639 snakeField("tax", "selection"), 1640 "amount", 1641 "subtotal", 1642 "taxAmount", 1643 snakeField("tax", "amount"), 1644 "totalTaxAmount", 1645 snakeField("total", "tax", "amount"), 1646 "taxes", 1647 "fixedBaseAmount", 1648 snakeField("fixed", "base", "amount"), 1649 "lineItemsTotal", 1650 snakeField("line", "items", "total"), 1651 "hasLineItems", 1652 snakeField("has", "line", "items"), 1653 "pricingType", 1654 snakeField("pricing", "type"), 1655 "hourlyRate", 1656 snakeField("hourly", "rate"), 1657 "estimatedHours", 1658 snakeField("estimated", "hours"), 1659 "paymentStatus", 1660 snakeField("payment", "status"), 1661 "depositPaid", 1662 snakeField("deposit", "paid"), 1663 "depositPaidDate", 1664 snakeField("deposit", "paid", "date"), 1665 "hasDeposit", 1666 snakeField("has", "deposit"), 1667 "depositPercentage", 1668 snakeField("deposit", "percentage"), 1669 "depositPercent", 1670 snakeField("deposit", "percent"), 1671 "depositAmount", 1672 snakeField("deposit", "amount"), 1673 "depositRequired", 1674 snakeField("deposit", "required"), 1675 "requireDeposit", 1676 snakeField("require", "deposit"), 1677 "quoteId", 1678 snakeField("quote", "id"), 1679 "quoteStatus", 1680 snakeField("quote", "status"), 1681 "quoteSentAt", 1682 snakeField("quote", "sent", "at"), 1683 "invoiceId", 1684 snakeField("invoice", "id"), 1685 "invoiceStatus", 1686 snakeField("invoice", "status"), 1687 "invoiceSentAt", 1688 snakeField("invoice", "sent", "at"), 1689 "operationalEmailSentAt", 1690 snakeField("operational", "email", "sent", "at"), 1691 "sendEmailIntent", 1692 snakeField("send", "email", "intent") 1693]); 1694class ProtectedTaskFieldError extends Error { 1695 constructor(fields){ 1696 super("Protected task update field".concat(fields.length === 1 ? "" : "s", ": ").concat(fields.join(", "))), this.fields = fields, this.code = "PROTECTED_FIELD"; 1697 this.name = "ProtectedTaskFieldError"; 1698 } 1699} 1700class TaskVersionMismatchError extends Error { 1701 constructor(){ 1702 super("Task version mismatch"), this.code = "VERSION_MISMATCH"; 1703 this.name = "TaskVersionMismatchError"; 1704 } 1705} 1706class CustomerVersionMismatchError extends Error { 1707 constructor(){ 1708 super("Customer version mismatch"), this.code = "VERSION_MISMATCH"; 1709 this.name = "CustomerVersionMismatchError"; 1710 } 1711} 1712class SettingsVersionMismatchError extends Error { 1713 constructor(){ 1714 super("Settings version mismatch"), this.code = "VERSION_MISMATCH"; 1715 this.name = "SettingsVersionMismatchError"; 1716 } 1717} 1718function assertNoProtectedTaskFields(patch) { 1719 if (!patch || typeof patch !== "object" || Array.isArray(patch)) { 1720 return; 1721 } 1722 const fields = Object.keys(patch).filter((key)=>protectedTaskUpdateFields.has(key)); 1723 if (fields.length > 0) { 1724 throw new ProtectedTaskFieldError(fields); 1725 } 1726} 1727function sanitizeTaskWriteInput(input) { 1728 const sanitized = {}; 1729 for (const [key, value] of Object.entries(input)){ 1730 if (taskPhantomKeys.has(key) || key === "requireDeposit" || key === "depositPercent") { 1731 continue; 1732 } 1733 sanitized[key] = value; 1734 } 1735 return sanitized; 1736} 1737function sanitizeTaskUpdateInput(input) { 1738 const sanitized = sanitizeTaskWriteInput(input); 1739 for (const key of taskGenericUpdateBlockedKeys){ 1740 delete sanitized[key]; 1741 } 1742 return sanitized; 1743} 1744function filterUuidArray(values) { 1745 if (!Array.isArray(values)) return undefined; 1746 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; 1747 const filtered = values.filter((value)=>typeof value === "string" && uuidRegex.test(value)); 1748 return filtered; 1749} 1750 1751 1752/***/ }), 1753 1754/***/ 92008: 1755/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 1756 1757/* harmony export */ __webpack_require__.d(__webpack_exports__, { 1758/* harmony export */ E7: () => (/* binding */ getCompanyTimeFormat), 1759/* harmony export */ WG: () => (/* binding */ getCompanyLocale), 1760/* harmony export */ j7: () => (/* binding */ getCompanyDateFormat), 1761/* harmony export */ z5: () => (/* binding */ getCompanyCurrency) 1762/* harmony export */ }); 1763/* unused harmony exports getCompanyCountry, getCompanyCountryKey, saveCompanyCountry, countryCurrencyMap */ 1764// Company settings utilities 1765// This file provides helper functions to get company-specific settings 1766// These now come from Supabase via the useCompanySettings hook 1767// Note: These functions now serve as fallbacks when Supabase data isn't available 1768// The primary source should be the useCompanySettings hook 1769function getCompanyCountry() { 1770 // This is now a fallback - prefer using useCompanySettings hook 1771 // Default to USA (payment methods format) 1772 return "USA"; 1773} 1774function getCompanyCountryKey() { 1775 // This is now a fallback - prefer using useCompanySettings hook 1776 // Default to united_states (settings format) 1777 return "united_states"; 1778} 1779function normalizeCurrency(value) { 1780 if (typeof value !== "string") return null; 1781 const normalized = value.trim().toUpperCase(); 1782 return /^[A-Z]{3}$/.test(normalized) ? normalized : null; 1783} 1784function readCompanyCurrency(input) { 1785 return typeof input === "object" && input !== null ? input.currency : input; 1786} 1787function readLegacyCurrency(input) { 1788 if (typeof input !== "object" || input === null) return input; 1789 var _input_defaultCurrency; 1790 return (_input_defaultCurrency = input["defaultCurrency"]) !== null && _input_defaultCurrency !== void 0 ? _input_defaultCurrency : input["default_currency"]; 1791} 1792function getCompanyCurrency(company, settings) { 1793 var _normalizeCurrency, _ref; 1794 return (_ref = (_normalizeCurrency = normalizeCurrency(readCompanyCurrency(company))) !== null && _normalizeCurrency !== void 0 ? _normalizeCurrency : normalizeCurrency(readLegacyCurrency(settings))) !== null && _ref !== void 0 ? _ref : "USD"; 1795} 1796function getCompanyLocale() { 1797 // This is now a fallback - prefer using useCompanySettings hook 1798 // Default to en 1799 return "en"; 1800} 1801function getCompanyDateFormat() { 1802 // This is now a fallback - prefer using useCompanySettings hook 1803 // Default to MM/DD/YYYY for USA 1804 return "MM/DD/YYYY"; 1805} 1806function getCompanyTimeFormat() { 1807 // This is now a fallback - prefer using useCompanySettings hook 1808 // Default to 12h for USA 1809 return "12h"; 1810} 1811// Deprecated - kept for backward compatibility 1812function saveCompanyCountry(country) { 1813 console.warn('saveCompanyCountry is deprecated. Use useCompanySettings hook to update settings in Supabase.'); 1814} 1815// Country to currency mappings (for reference) 1816const countryCurrencyMap = { 1817 "united_states": "USD", 1818 "united_kingdom": "GBP", 1819 "canada": "CAD", 1820 "colombia": "COP", 1821 "chile": "CLP", 1822 "argentina": "ARS", 1823 "brazil": "BRL", 1824 "france": "EUR", 1825 "germany": "EUR", 1826 "spain": "EUR", 1827 "netherlands": "EUR", 1828 "italy": "EUR", 1829 "australia": "AUD", 1830 "japan": "JPY", 1831 "mexico": "MXN", 1832 "india": "INR", 1833 "china": "CNY",
1834 "south_korea": "KRW" 1835}; 1836 1837 1838/***/ }) 1839 1840}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.