import { today } from './dates' import type { TaskIndex } from './store/TaskIndex' export type TaskStatus = string export type TaskPriority = string export type GanttGranularity = 'day' | 'week' | 'month' | 'quarter' export type GanttWeekLabel = 'weekNumber' | 'dateRange' | 'both' export type ViewMode = 'table' | 'gantt' | 'kanban' export type DueDateFilter = 'any' | 'overdue' | 'this-week' | 'this-month' | 'no-date' export type TaskType = 'task' | 'milestone' | 'subtask' export interface Recurrence { interval: 'daily' | 'weekly' | 'monthly' | 'yearly' every: number // e.g. every 2 weeks endDate?: string // YYYY-MM-DD } export interface TimeLog { date: string // YYYY-MM-DD hours: number note: string } export interface CustomFieldDef { id: string name: string type: 'text' | 'number' | 'date' | 'select' | 'multiselect' | 'person' | 'checkbox' | 'url' options?: string[] // for select / multiselect icon?: string // emoji or lucide icon name } export interface Task { id: string title: string description: string type: TaskType // 'task' or 'milestone' (zero-duration) status: TaskStatus priority: TaskPriority start: string // YYYY-MM-DD, empty string = unset due: string // YYYY-MM-DD, empty string = unset progress: number // 0–100 completed: string // YYYY-MM-DD, empty string = not completed; stamped when status becomes complete assignees: string[] tags: string[] subtasks: Task[] dependencies: string[] // task IDs recurrence?: Recurrence timeEstimate?: number // hours timeLogs?: TimeLog[] customFields: Record /** UI state, persisted per project in plugin settings (data.json), not in frontmatter. */ collapsed: boolean createdAt: string updatedAt: string filePath?: string // vault path to this task's .md file archived?: boolean // runtime only — derived from file location in Archive/ subfolder } export interface Project { id: string title: string description: string color: string // hex icon: string // emoji tasks: Task[] customFields: CustomFieldDef[] teamMembers: string[] createdAt: string updatedAt: string filePath: string // resolved vault path savedViews: SavedView[] /** Per-project overrides for the global settings. Absent fields inherit. */ config?: ProjectConfig /** Not serialized. Rebuilt on load, maintained by the store's mutators. */ taskIndex: TaskIndex } /** Tasks are excluded: they change through the task mutators, never a whole-project write. */ export type ProjectPatch = Partial< Pick > export interface FilterState { text: string statuses: TaskStatus[] priorities: TaskPriority[] assignees: string[] tags: string[] dueDateFilter: DueDateFilter showArchived: boolean } export interface SavedView { id: string name: string filter: FilterState sortKey: string sortDir: 'asc' | 'desc' viewMode?: ViewMode } export interface PerProjectFilter { filter: FilterState activeSavedViewId: string | null } export interface StatusConfig { id: string label: string color: string icon: string complete: boolean } /** Overrides a project may set in its own file. An absent field falls back to the global settings. */ export interface ProjectConfig { statuses?: StatusConfig[] priorities?: PriorityConfig[] defaultView?: ViewMode autoSchedule?: boolean pullForwardOnEarlyFinish?: boolean kanbanShowSubtasks?: boolean kanbanShowDescriptionPreview?: boolean } /** * A project's config with every fallback applied. Views and modals read this rather * than the global settings, so another task source can supply its own catalogs. */ export interface ResolvedProjectConfig { statuses: StatusConfig[] priorities: PriorityConfig[] defaultView: ViewMode autoSchedule: boolean pullForwardOnEarlyFinish: boolean kanbanShowSubtasks: boolean kanbanShowDescriptionPreview: boolean } export interface PriorityConfig { id: TaskPriority label: string color: string icon: string } export interface PMSettings { projectsFolder: string defaultView: ViewMode ganttGranularity: GanttGranularity ganttWeekLabel: GanttWeekLabel statuses: StatusConfig[] priorities: PriorityConfig[] globalTeamMembers: string[] notificationsEnabled: boolean notificationLeadDays: number autoSchedule: boolean pullForwardOnEarlyFinish: boolean kanbanShowSubtasks: boolean kanbanShowDescriptionPreview: boolean showTagColors: boolean saveTaskOnClose: boolean projectFilters: Record /** Collapsed task ids per project path. Lives here so a toggle doesn't rewrite task files. */ collapsedTasks: Record } export const DEFAULT_STATUSES: StatusConfig[] = [ { id: 'todo', label: 'To Do', color: '#8a94a0', icon: '', complete: false }, { id: 'in-progress', label: 'In Progress', color: '#8b72be', icon: '', complete: false }, { id: 'blocked', label: 'Blocked', color: '#c47070', icon: '', complete: false }, { id: 'review', label: 'In Review', color: '#b8a06b', icon: '', complete: false }, { id: 'done', label: 'Done', color: '#79b58d', icon: '', complete: true }, { id: 'cancelled', label: 'Cancelled', color: '#767491', icon: '', complete: true } ] export const DEFAULT_PRIORITIES: PriorityConfig[] = [ { id: 'critical', label: 'Critical', color: '#c47070', icon: '' }, { id: 'high', label: 'High', color: '#b8a06b', icon: '' }, { id: 'medium', label: 'Medium', color: '#8a94a0', icon: '' }, { id: 'low', label: 'Low', color: '#79b58d', icon: '' } ] export const DEFAULT_SETTINGS: PMSettings = { projectsFolder: 'Projects', defaultView: 'table', ganttGranularity: 'week', ganttWeekLabel: 'weekNumber', statuses: DEFAULT_STATUSES, priorities: DEFAULT_PRIORITIES, globalTeamMembers: [], kanbanShowSubtasks: false, kanbanShowDescriptionPreview: false, showTagColors: true, notificationsEnabled: true, notificationLeadDays: 2, autoSchedule: true, pullForwardOnEarlyFinish: false, saveTaskOnClose: true, projectFilters: {}, collapsedTasks: {} } export function makeId(): string { return Math.random().toString(36).slice(2, 10) + Date.now().toString(36) } export function makeTask(overrides: Partial = {}): Task { const now = new Date().toISOString() return { id: makeId(), title: 'New Task', description: '', type: 'task', status: 'todo', priority: 'medium', start: today().toString(), due: '', progress: 0, completed: '', assignees: [], tags: [], subtasks: [], dependencies: [], customFields: {}, collapsed: false, createdAt: now, updatedAt: now, ...overrides } } export function makeProject(title: string, filePath: string): Project { const now = new Date().toISOString() return { id: makeId(), title, description: '', color: '#8b72be', icon: '📋', tasks: [], customFields: [], teamMembers: [], createdAt: now, updatedAt: now, filePath, savedViews: [], taskIndex: new Map() } } export function makeDefaultFilter(): FilterState { return { text: '', statuses: [], priorities: [], assignees: [], tags: [], dueDateFilter: 'any', showArchived: false } }