Allow users to skip the first N data rows when importing CSV files.
This is useful when the CSV contains metadata rows before the actual
task data begins. Adds skip_rows to ImportConfig (backend) and a
number input in the parsing options UI (frontend).
Wrap all <select> elements in Bulma's <div class="select"> wrapper
and remove custom scoped select styles. This fixes readability in
dark mode and ensures consistent styling with the rest of the app.
The CSV handler imported echo/v4 instead of echo/v5 (which the project
uses), used value receiver echo.Context instead of pointer
*echo.Context, and called a non-existent handler.HandleHTTPError
function. Update to match existing handler patterns.
resetToUpload() cleared state but not the file input's value, so
re-selecting the same file after cancel would not trigger a change
event in the browser.
The error Message was only rendered inside the upload step div, so
preview/import failures during the mapping step were invisible to
users. Move it above the step-conditional sections and remove dead
preview-errors template code.
ErrorCount and Errors fields were never populated by PreviewImport,
making the API contract misleading. Remove them from both the Go
struct and the TypeScript interface.
Previously all parseCSV errors were mapped to ErrNotACSVFile, hiding
the actual cause. Now ErrFileIsEmpty is propagated correctly instead
of being misreported as an invalid CSV file.
parseCSV previously ignored the quoteChar parameter (named _). Since
Go's csv.Reader only supports double-quote, we now pre-process the
data to replace alternative quote characters before parsing.
The migration list linked all migrators to the 'migrate.service' route
by name, which loads MigrationHandler.vue. For the CSV migrator this
would result in a 400 error since it requires the multi-step
MigrationCSV.vue flow with config. Now CSV migrators link to
'migrate.csv' instead.
Remove labels from service structs and move them to the component.
The service now exports simple arrays of values, while the component
handles translation through mapping functions.
Add a new CSV migration module that allows users to import tasks from
any CSV file with custom column mapping and parsing options.
Backend changes:
- New CSV migrator module with detection, preview, and import endpoints
- Auto-detection of delimiter, quote character, and date format
- Suggested column mappings based on column name patterns
- Transactional import using InsertFromStructure
Frontend changes:
- New CSV migration UI with two-step flow (upload -> mapping -> import)
- Column mapping selectors for all task attributes
- Live preview showing first 5 tasks with current mapping
- Parsing option controls for delimiter and date format
The CSV migrator creates a parent "Imported from CSV" project with
child projects based on the project column if provided, or a default
"Tasks" project for tasks without a specified project.
Previously only the "To-Do" default bucket was deleted, leaving "Doing"
and "Done" as duplicates alongside migration-provided buckets. Now all
default-created buckets are removed when migration data already provides
bucket assignments for all tasks.
Add comprehensive tests for the WeKan conversion function including
edge cases (empty board, orphan cards, color mapping, multiple
checklists, unsupported fields) and a realistic JSON fixture file.
Add a file-based migration importer that reads WeKan board JSON exports
and creates Vikunja projects with kanban buckets, tasks, labels,
checklists, and comments.
WeKan lists become kanban buckets. Checklists are converted to HTML
task lists in the description. Card descriptions and comments are
converted from markdown to HTML using goldmark. Label colors are
mapped from WeKan's CSS color names to their actual hex values.
Resolves#2490. Users with team access on a parent project were not seeing
subtask relations for tasks in child projects because getUserProjectsStatement
does not walk the project hierarchy. The fix wraps the base query in a
recursive CTE that traverses child projects via parent_project_id.
When closing a task modal opened from the Gantt view, the date range
query parameters were lost because closeModal() reconstructed the
route with only projectId and viewId. Now preserves query parameters
from the backdrop view.
Replace static initialDateRange snapshot with reactive filters.value
references inside the computed flatPickerConfig. This ensures the
Flatpickr defaultDate always reflects the current user-selected range
instead of the mount-time values.
Ref #2462
With truncateAll wiping all tables, the test user has no projects,
so ShowTasks never renders and tasksLoaded stays false — meaning
ImportHint (which is gated on tasksLoaded) never appears. Seed a
project with default views so the empty-state hint is visible.
Add isFilteredView parameter to shouldShowTaskInListView() that skips
the parent-hiding logic when viewing tasks through a saved filter.
This ensures all filter-matching tasks are shown.
Ref: #2494