import { defineSchema, defineTable } from "convex/server"; import { v } from "convex/values"; export default defineSchema({ users: defineTable({ tenantId: v.string(), name: v.string(), email: v.string(), role: v.optional(v.string()), avatarUrl: v.optional(v.string()), teams: v.optional(v.array(v.string())), companyId: v.optional(v.id("companies")), }) .index("by_tenant_email", ["tenantId", "email"]) .index("by_tenant_role", ["tenantId", "role"]) .index("by_tenant", ["tenantId"]) .index("by_tenant_company", ["tenantId", "companyId"]), companies: defineTable({ tenantId: v.string(), name: v.string(), slug: v.string(), provisioningCode: v.optional(v.string()), isAvulso: v.optional(v.boolean()), contractedHoursPerMonth: v.optional(v.number()), cnpj: v.optional(v.string()), domain: v.optional(v.string()), phone: v.optional(v.string()), description: v.optional(v.string()), address: v.optional(v.string()), createdAt: v.number(), updatedAt: v.number(), }) .index("by_tenant_slug", ["tenantId", "slug"]) .index("by_tenant", ["tenantId"]) .index("by_provisioning_code", ["provisioningCode"]), alerts: defineTable({ tenantId: v.string(), companyId: v.optional(v.id("companies")), companyName: v.string(), usagePct: v.number(), threshold: v.number(), range: v.string(), recipients: v.array(v.string()), createdAt: v.number(), deliveredCount: v.number(), }) .index("by_tenant_created", ["tenantId", "createdAt"]) .index("by_tenant", ["tenantId"]), queues: defineTable({ tenantId: v.string(), name: v.string(), slug: v.string(), teamId: v.optional(v.id("teams")), }) .index("by_tenant_slug", ["tenantId", "slug"]) .index("by_tenant", ["tenantId"]), teams: defineTable({ tenantId: v.string(), name: v.string(), description: v.optional(v.string()), }).index("by_tenant_name", ["tenantId", "name"]), slaPolicies: defineTable({ tenantId: v.string(), name: v.string(), description: v.optional(v.string()), timeToFirstResponse: v.optional(v.number()), // minutes timeToResolution: v.optional(v.number()), // minutes }).index("by_tenant_name", ["tenantId", "name"]), tickets: defineTable({ tenantId: v.string(), reference: v.number(), subject: v.string(), summary: v.optional(v.string()), description: v.optional(v.string()), status: v.string(), priority: v.string(), channel: v.string(), queueId: v.optional(v.id("queues")), categoryId: v.optional(v.id("ticketCategories")), subcategoryId: v.optional(v.id("ticketSubcategories")), requesterId: v.id("users"), requesterSnapshot: v.optional( v.object({ name: v.string(), email: v.optional(v.string()), avatarUrl: v.optional(v.string()), teams: v.optional(v.array(v.string())), }) ), assigneeId: v.optional(v.id("users")), assigneeSnapshot: v.optional( v.object({ name: v.string(), email: v.optional(v.string()), avatarUrl: v.optional(v.string()), teams: v.optional(v.array(v.string())), }) ), companyId: v.optional(v.id("companies")), companySnapshot: v.optional( v.object({ name: v.string(), slug: v.optional(v.string()), isAvulso: v.optional(v.boolean()), }) ), working: v.optional(v.boolean()), slaPolicyId: v.optional(v.id("slaPolicies")), dueAt: v.optional(v.number()), // ms since epoch firstResponseAt: v.optional(v.number()), resolvedAt: v.optional(v.number()), closedAt: v.optional(v.number()), updatedAt: v.number(), createdAt: v.number(), tags: v.optional(v.array(v.string())), customFields: v.optional( v.array( v.object({ fieldId: v.id("ticketFields"), fieldKey: v.string(), label: v.string(), type: v.string(), value: v.any(), displayValue: v.optional(v.string()), }) ) ), totalWorkedMs: v.optional(v.number()), internalWorkedMs: v.optional(v.number()), externalWorkedMs: v.optional(v.number()), activeSessionId: v.optional(v.id("ticketWorkSessions")), }) .index("by_tenant_status", ["tenantId", "status"]) .index("by_tenant_queue", ["tenantId", "queueId"]) .index("by_tenant_assignee", ["tenantId", "assigneeId"]) .index("by_tenant_reference", ["tenantId", "reference"]) .index("by_tenant_requester", ["tenantId", "requesterId"]) .index("by_tenant_company", ["tenantId", "companyId"]) .index("by_tenant", ["tenantId"]), ticketComments: defineTable({ ticketId: v.id("tickets"), authorId: v.id("users"), visibility: v.string(), // PUBLIC | INTERNAL body: v.string(), authorSnapshot: v.optional( v.object({ name: v.string(), email: v.optional(v.string()), avatarUrl: v.optional(v.string()), teams: v.optional(v.array(v.string())), }) ), attachments: v.optional( v.array( v.object({ storageId: v.id("_storage"), name: v.string(), size: v.optional(v.number()), type: v.optional(v.string()), }) ) ), createdAt: v.number(), updatedAt: v.number(), }) .index("by_ticket", ["ticketId"]) .index("by_author", ["authorId"]), ticketEvents: defineTable({ ticketId: v.id("tickets"), type: v.string(), payload: v.optional(v.any()), createdAt: v.number(), }).index("by_ticket", ["ticketId"]), commentTemplates: defineTable({ tenantId: v.string(), kind: v.optional(v.string()), title: v.string(), body: v.string(), createdBy: v.id("users"), updatedBy: v.optional(v.id("users")), createdAt: v.number(), updatedAt: v.number(), }) .index("by_tenant", ["tenantId"]) .index("by_tenant_title", ["tenantId", "title"]) .index("by_tenant_kind", ["tenantId", "kind"]), ticketWorkSessions: defineTable({ ticketId: v.id("tickets"), agentId: v.id("users"), workType: v.optional(v.string()), // INTERNAL | EXTERNAL startedAt: v.number(), stoppedAt: v.optional(v.number()), durationMs: v.optional(v.number()), pauseReason: v.optional(v.string()), pauseNote: v.optional(v.string()), }) .index("by_ticket", ["ticketId"]) .index("by_ticket_agent", ["ticketId", "agentId"]), ticketCategories: defineTable({ tenantId: v.string(), name: v.string(), slug: v.string(), description: v.optional(v.string()), order: v.number(), createdAt: v.number(), updatedAt: v.number(), }) .index("by_tenant_slug", ["tenantId", "slug"]) .index("by_tenant_order", ["tenantId", "order"]) .index("by_tenant", ["tenantId"]), ticketSubcategories: defineTable({ tenantId: v.string(), categoryId: v.id("ticketCategories"), name: v.string(), slug: v.string(), order: v.number(), createdAt: v.number(), updatedAt: v.number(), }) .index("by_category_order", ["categoryId", "order"]) .index("by_category_slug", ["categoryId", "slug"]) .index("by_tenant_slug", ["tenantId", "slug"]), ticketFields: defineTable({ tenantId: v.string(), key: v.string(), label: v.string(), type: v.string(), description: v.optional(v.string()), required: v.boolean(), order: v.number(), options: v.optional( v.array( v.object({ value: v.string(), label: v.string(), }) ) ), createdAt: v.number(), updatedAt: v.number(), }) .index("by_tenant_key", ["tenantId", "key"]) .index("by_tenant_order", ["tenantId", "order"]) .index("by_tenant", ["tenantId"]), userInvites: defineTable({ tenantId: v.string(), inviteId: v.string(), email: v.string(), name: v.optional(v.string()), role: v.string(), status: v.string(), token: v.string(), expiresAt: v.number(), createdAt: v.number(), createdById: v.optional(v.string()), acceptedAt: v.optional(v.number()), acceptedById: v.optional(v.string()), revokedAt: v.optional(v.number()), revokedById: v.optional(v.string()), revokedReason: v.optional(v.string()), }) .index("by_tenant", ["tenantId"]) .index("by_token", ["tenantId", "token"]) .index("by_invite", ["tenantId", "inviteId"]), machines: defineTable({ tenantId: v.string(), companyId: v.optional(v.id("companies")), companySlug: v.optional(v.string()), authUserId: v.optional(v.string()), authEmail: v.optional(v.string()), persona: v.optional(v.string()), assignedUserId: v.optional(v.id("users")), assignedUserEmail: v.optional(v.string()), assignedUserName: v.optional(v.string()), assignedUserRole: v.optional(v.string()), hostname: v.string(), osName: v.string(), osVersion: v.optional(v.string()), architecture: v.optional(v.string()), macAddresses: v.array(v.string()), serialNumbers: v.array(v.string()), fingerprint: v.string(), metadata: v.optional(v.any()), lastHeartbeatAt: v.optional(v.number()), status: v.optional(v.string()), isActive: v.optional(v.boolean()), createdAt: v.number(), updatedAt: v.number(), registeredBy: v.optional(v.string()), }) .index("by_tenant", ["tenantId"]) .index("by_tenant_company", ["tenantId", "companyId"]) .index("by_tenant_fingerprint", ["tenantId", "fingerprint"]) .index("by_tenant_assigned_email", ["tenantId", "assignedUserEmail"]) .index("by_auth_email", ["authEmail"]), machineAlerts: defineTable({ tenantId: v.string(), machineId: v.id("machines"), companyId: v.optional(v.id("companies")), kind: v.string(), message: v.string(), severity: v.string(), createdAt: v.number(), }) .index("by_machine_created", ["machineId", "createdAt"]) .index("by_tenant_created", ["tenantId", "createdAt"]) .index("by_tenant_machine", ["tenantId", "machineId"]), machineTokens: defineTable({ tenantId: v.string(), machineId: v.id("machines"), tokenHash: v.string(), expiresAt: v.number(), revoked: v.boolean(), createdAt: v.number(), lastUsedAt: v.optional(v.number()), usageCount: v.optional(v.number()), type: v.optional(v.string()), }) .index("by_token_hash", ["tokenHash"]) .index("by_machine", ["machineId"]) .index("by_tenant_machine", ["tenantId", "machineId"]), });