Technical Reference
Tasks

Tasks — Technical Reference

This page covers the data models, completion lifecycle, and daily digest architecture behind the Tasks feature.

Data Models

tasks

ColumnTypeDescription
iduuidPrimary key
workspace_iduuidFK → workspaces.id — tenant isolation (ON DELETE CASCADE)
titletextRequired. Short description of the action item
descriptiontextOptional detailed notes (defaults to empty string)
due_datetimestamptzWhen the task is due. Drives the dashboard widget and daily digest
statustext'open' or 'completed' (CHECK constraint)
contact_iduuidFK → contacts.id (ON DELETE SET NULL) — optional contact link
deal_iduuidFK → deals.id (ON DELETE SET NULL) — optional deal link
assigned_touuidFK → user_profiles.id — assigned workspace member
completed_attimestamptzTimestamp when marked as completed (NULL if open)
reminder_sent_attimestamptzUsed by the daily digest to prevent duplicate notifications
created_byuuidFK → user_profiles.id — who created the task
created_attimestamptzWhen the task was created
updated_attimestamptzWhen the task was last modified
stripe_payment_intent_idtextStripe Payment Intent ID (e.g. pi_xxx) associated with automated refund review tasks

Soft links: Contact and deal FKs use ON DELETE SET NULL — if the linked contact or deal is deleted, the task survives as a general to-do item.

Architecture

Completion Lifecycle

ActionField Changes
Completestatus'completed', completed_at → current timestamp
Reopenstatus'open', completed_at → NULL, reminder_sent_at → NULL

Clearing reminder_sent_at on reopen ensures the task re-enters the daily digest cycle.

Task List Sorting & getTasks

The getTasks server action retrieves a paginated, filtered, and sorted list of tasks.

export interface TaskListParams {
    page?: number;
    pageSize?: number;
    search?: string;
    sortBy?: string;
    sortDir?: "asc" | "desc";
}

Sorting & Parameters:

  • sortBy: Specifies the column to sort by. Supported values:
    • "due_date" (default): Sorts chronologically by due_date using a date-aware stacked-bar sort icon.
  • sortDir: The sort direction ("asc" or "desc"). If undefined, defaults to "asc".
  • Null Handling: Tasks without a due date (where due_date is NULL) are always forced to the bottom of the list (nulls last), regardless of whether sorting is ascending or descending.
  • Status Independence: The sorting settings apply uniformly across all status tabs (Open, Completed, or Any), decoupling task sorting from status filter selections.

Consolidated Dialog Components

To eliminate UI redundancy and synchronize task management across multiple dashboards, task creation and detailed viewing/editing are consolidated into two self-hydrating React components:

  • TaskCreateDialog: A unified dialog for creating new tasks. It supports context-aware contact and deal prefilling, handles the cascading picker logic, and dynamically toggles the visibility of the deal selector based on the workspace's dealsEnabled setting.
  • TaskDetailDialog: A centralized interface for task detail visualization, field modification, task deletion, and note CRUD management (with workspace note templates support). To prevent linked records from disappearing during completion toggles, the dialog uses a functional state update pattern to merge raw database updates from toggleTaskStatus while preserving the nested contact and deal relationship objects.

These components are used across 5 main CRM surfaces: Dashboard, Tasks page, Notes page, Contacts detail page, and Deals board.

Cascading Contact / Deal Pickers

Inside TaskCreateDialog and TaskDetailDialog, the contact and deal pickers work in tandem to ensure clean relational data binding:

  1. Selecting a deal → auto-fills contact_id with the deal's linked contact.
  2. Selecting a contact → filters the deal dropdown list to show only the selected contact's deals.
  3. Changing the contact → clears any existing, now incompatible deal_id selection.

When creating a task from a Contact Detail card, the contact is preselected and locked. When creating from a Deal Detail page, both the deal and contact fields are prefilled.

Deep Linking

Tasks support URL-based deep linking: /tasks?taskId=<id> automatically opens the target task's TaskDetailDialog. This is used by Global Search and the Notification Center to display specific tasks instantly.

When a deep link is resolved, the parent Tasks page automatically widens its view filters (setting the status filter to "Any" and the assignee filter to "Everyone's") to guarantee that the target task is visible and loaded in the list regardless of its assignee or completion status.

Completed Today Section

The "Completed Today" section displays tasks completed within the current day. Tasks are rendered with strikethrough text and reduced opacity. The section respects the active assignee filter — filtering to "Mine" shows only your completed tasks.

Present on four surfaces: /tasks page, dashboard widget, Contact Detail, Deal Detail.

Daily Digest Architecture

Cron Configuration

PropertyValue
ScheduleDaily at 8:00 AM US Central (1:00 PM UTC)
Cron expression0 13 * * *
Endpoint/api/cron/task-digest
AuthenticationAuthorization: Bearer {CRON_SECRET}
Email typeTransactional (system notification)
SenderPLATFORM_SENDER_EMAIL
{
    "path": "/api/cron/task-digest",
    "schedule": "0 13 * * *"
}

Digest Pipeline

  1. Authenticate — Verify CRON_SECRET bearer token
  2. Fetch eligible tasks — Query: status = 'open', assigned_to IS NOT NULL, due_date ≤ end of today, reminder_sent_at is NULL or before today
  3. Filter already-reminded — Exclude tasks where reminder_sent_at matches today
  4. Group by user — Each user receives one email regardless of task count
  5. Build digest — Resolve user email via auth.admin.getUserById(), classify tasks into "Due Today" and "Overdue", build HTML email
  6. Send and stamp — Send via Resend, stamp reminder_sent_at on all included tasks

Deduplication Logic

The reminder_sent_at field prevents duplicate emails within the same day. Overdue tasks re-appear in the next day's digest because their reminder_sent_at will be yesterday's date. Reopened tasks have reminder_sent_at cleared, so they immediately re-enter the digest cycle.

Email Template

SectionStyle
HeaderGreen gradient (#059669 → #10b981), "Gordon CRM — Task Digest"
GreetingPersonalized: "Good morning, {first_name}!"
OverdueRed heading (⚠️), red border, task title + contact/deal + original due date
Due TodayStandard heading (📋), task title + contact/deal
CTAGreen button → /tasks
Footer"This is an automated notification from Gordon CRM."

Workspace Grouping

For users who are members of multiple workspaces, both the task digest and the appointment digest templates support automatic workspace grouping.

  • Trigger: If the digest contains tasks/appointments from more than one workspace, a bold workspace name subheader is displayed above the items belonging to that workspace.
  • Single Workspace: If all tasks/appointments belong to a single workspace, no workspace subheaders are rendered, keeping the email layout clean.

Related Cron Jobs

Cron JobSchedulePurpose
Campaign SweeperEvery 5 minutesProcess due campaign enrollments
Broadcast SweeperEvery 5 minutesProcess scheduled broadcasts
Birthday SweeperDaily at 8:00 AM UTCFire birthday automation triggers
Task DigestDaily at 1:00 PM UTCSend daily task summary emails
Mention DigestDaily at 2:00 PM UTCSend batched unread mention emails

Automation Integration

The create_task automation action creates a task pre-linked to the triggering contact with configurable fields:

  • title — Task title (supports template variables)
  • description — Optional description
  • assigned_to — Workspace member to assign
  • due_date_offset — Number of days from trigger to set the due date

See Automations → Actions for the full specification.

Performance & Hydration Optimization

Server/Client Page Split & Lazy Loading

  • Server Component (pages/tasks/page.tsx): Resolves workspace context and pre-fetches the initial task directory bundle on the server, avoiding rendering waterfalls.
  • Client Component (tasks-client.tsx): Manages client-side lists rendering, task search filters, sorting, and pagination.
  • Dynamic Imports: Defer loading of heavy components (TaskCreateDialog and TaskDetailDialog) using next/dynamic dynamic imports with ssr: false to only download chunks on user interaction.
  • Hydration Guards: Integrates a workspace transition synchronization gate via initialWorkspaceIdRef and a client mount ref guard (isFirstQueryRef) to prevent duplicate mounting API fetches.

Security

RLS Policies

OperationPolicy
SELECTAll workspace members can view tasks
INSERTAll workspace members can create tasks
UPDATEAll workspace members can edit tasks
DELETEAdmin or Owner only