Skip to content
This repository was archived by the owner on Apr 10, 2026. It is now read-only.
This repository was archived by the owner on Apr 10, 2026. It is now read-only.

feat: Import tasks from Todoist, Trello, and Microsoft To-Do #55

@dajbelshaw

Description

@dajbelshaw

Overview

Allow users to migrate their existing tasks into TaskDial from Todoist, Trello, or Microsoft To-Do via file upload.

Approach: file-based import (not OAuth). The user exports their data from the source app and uploads the file to TaskDial. Parsing happens entirely client-side in the browser — the raw import data never touches the server. Imported tasks go through the normal encrypted task creation API, preserving E2EE.

Benefits over OAuth:

  • No access tokens to manage or store
  • No external API maintenance or rate-limit concerns
  • Works even if the source service changes its API
  • Privacy-preserving: TaskDial never has credentials to read a user's third-party account

Export formats

Todoist → CSV

How to export: Settings → Backups → Export as CSV

Relevant columns:

Todoist column TaskDial field Notes
CONTENT title Direct
DESCRIPTION details Direct
DATE date Parse to YYYY-MM-DD; tasks with no date → today
DATE (time part) fixedStartTime Extract HH:MM if a time is present
PRIORITY important 4 (p1 in Todoist) → important: true; others → false
Section rows (TYPE=section) tag Tasks under a section inherit the section name as their tag
durationMinutes Default: 25
completed Todoist CSV only exports incomplete tasks; set false

Skip rows where TYPE is not task.

Trello → JSON

How to export: Board menu → More → Print and export → Export as JSON

Relevant fields from cards[]:

Trello field TaskDial field Notes
name title Direct
desc details Direct
due date + fixedStartTime ISO datetime → split into date + time
dueComplete completed Direct
labels[0].name tag Use first label name; ignore colour
List name (resolved from lists[] via idList) tag Fallback if no label; list name used as tag
closed Skip archived cards (closed: true)
durationMinutes Default: 25
pos sortOrder Preserve relative order within each list

The board export contains a top-level lists array and cards array. Match card.idList to list.id to get the list name.

Microsoft To-Do → CSV

How to export: Settings → Export data → downloads a ZIP with one CSV per list

Relevant columns:

To-Do column TaskDial field Notes
TaskName title Direct
Notes details Direct
DueDate date Parse to YYYY-MM-DD; no date → today
ReminderDate fixedStartTime Extract time component if present
Importance important "High"true; "Normal"false
IsCompleted completed "True"true
List filename tag Derive from the CSV filename (the list name)
durationMinutes Default: 25

If the user exports a ZIP, unzip client-side (using a lib like fflate) and process each CSV as a separate list. Alternatively, accept individual CSV files and let the user import one list at a time.


UI

Entry point: Settings panel → new "Import" section (below existing settings groups).

Flow:

  1. User selects source (Todoist / Trello / Microsoft To-Do) via segmented control or radio group
  2. Brief instruction: "Export your tasks from [service] and upload the file below." + a link to the relevant export help page for each service
  3. File picker (drag-and-drop + click): accepts .csv for Todoist/To-Do, .json for Trello, .zip for To-Do multi-list
  4. On file select: parse client-side, show preview table of tasks to be imported (title, date, tag columns; paginated if >20 rows)
  5. User can set a target date for tasks with no due date (default: today)
  6. Import button → bulk POST to /api/tasks/bulk (see bulk endpoint note below)
  7. Progress indicator (n of N created) → success summary: "47 tasks imported"
  8. Errors shown inline per-task (if any fail, the rest still succeed)

Implementation notes

Always use the frontend encryption path

Imported tasks must always go through the frontend: parse client-side → encrypt via encryptTask() → POST to the internal /api/tasks/bulk endpoint. Do not route import through the public API (/api/v1/) — tasks created via the public API in v1 plaintext mode are stored unencrypted (see #56). Identical data imported via the UI vs. the public API would have different security properties, which would be confusing and inconsistent. The import UI must use the cookie-authenticated internal endpoint only.

Bulk endpoint — coordinate with #56

A /api/tasks/bulk internal endpoint is needed to avoid N individual API calls for large imports (cap at 500 tasks per request, single DB transaction). The public API (#56) may also want to expose a bulk creation endpoint at /api/v1/tasks/bulk. Design the internal endpoint first; the public version can wrap it. Keeping them separate allows the internal endpoint to remain cookie-auth only while the public endpoint uses API key auth.

Client-side parsing

Create app/src/services/importParsers.ts with three pure functions:

parseTodoistCsv(csvText: string): ImportedTask[]
parseTrelloJson(jsonText: string): ImportedTask[]
parseMicrosoftTodoCsv(csvText: string, listName: string): ImportedTask[]

Where ImportedTask is a partial Task type (no id, createdAt, updatedAt, sortOrder — those are assigned on creation).

Use the browser's built-in FileReader API for file reading. For CSV parsing, a lightweight lib like papaparse avoids edge cases with quoted fields and line endings.

E2EE

Parsing and field mapping happen in the browser. The ImportedTask[] array is passed to the existing task creation flow, which encrypts each task before sending. No special handling needed — imports are indistinguishable from manual task creation at the API level.

Duplicate detection

Out of scope for v1. Add a note to the import summary: "Re-importing the same file will create duplicate tasks."


Out of scope (for now)

  • OAuth-based live sync with any service
  • Incremental / delta imports
  • Exporting TaskDial tasks back to these services
  • Notion, Asana, TickTick, OmniFocus (could be follow-up issues)
  • Preserving sub-task hierarchy (TaskDial has no sub-tasks)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestuxUser experience / interface

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions