Skip to content

[OpenSpec] [planix] projects #4

@WilcoLouwerse

Description

@WilcoLouwerse

The register-schemas change established the Project schema in OpenRegister, and 3 seed projects are already loadable on a fresh install. However, there is no frontend or backend to interact with them — Planix currently renders a blank app shell with no navigation items and no usable views.

Projects are the foundational entity in Planix: every kanban board, task, and time entry belongs to a project. Nothing else in the roadmap is usable until users can create, view, and manage projects. This change builds the minimum viable project management surface so the rest of the roadmap can be developed and tested in a real, working UI.

Specs

  • projects — full project lifecycle: list, create, read, update, archive, delete, member management

Tasks

1. Setup

  • 1.1 Create directory structure for new files
    • Directories src/views/, src/store/, src/components/dialogs/, src/navigation/ created if needed
    • Router file confirmed at src/router/index.js
  • 1.2 Verify @conduction/nextcloud-vue exports
    • CnListViewLayout, CnDetailPage, CnObjectSidebar, useObjectStore, useListView, useDetailView all exported
    • If any export missing, ticket opened and local polyfill stub created
  • 1.3 Confirm register-schemas is applied
    • project schema exists in OpenRegister for the planix register

2. Project Store

  • 2.1 Create src/store/projects.js — base store with useObjectStore
    • Pinia store useProjectsStore created
    • useObjectStore imported with register: planix, schema: project
    • Reactive state: projects, activeProject, loading, error exposed
  • 2.2 Implement fetchProjects(filters)
    • Calls objectStore.getObjects({ members: currentUserUid, ...filters })
    • Loading and error state handled
    • Client-side fallback documented if API does not support member filtering
  • 2.3 Implement fetchProject(id)
    • Calls objectStore.getObject(id)
    • 403 sets error to forbidden and redirects to /projects
  • 2.4 Implement createProject(data)
    • POSTs with status: active and members: [currentUserUid]
    • Calls createDefaultColumns(newProjectId) on success
    • Returns new project object; sets error and throws on failure
  • 2.5 Implement updateProject(id, data)
    • Calls objectStore.updateObject(id, data)
    • Updates projects array in place and activeProject if matching
  • 2.6 Implement createDefaultColumns(projectId)
    • Reads default_columns from admin settings with hardcoded fallback
    • POSTs each column; returns { created, failed } without throwing on partial failure
  • 2.7 Implement archiveProject(id)
    • Calls updateProject(id, { status: archived }) and removes from list
  • 2.8 Implement deleteProject(id)
    • Fetches tasks, timeEntries, columns for the project
    • Deletes in order: timeEntries → tasks → columns → project
    • Shows toast on failure and halts cascade
  • 2.9 Implement addMember(projectId, userUid)
    • Appends uid to members[] with duplicate guard
  • 2.10 Implement removeMember(projectId, userUid)
    • Filters out uid from members[]
    • Returns count of tasks assigned to uid in this project
  • 2.11 Implement leaveProject(projectId)
    • Calls removeMember(projectId, currentUserUid)
    • Returns { isLastMember: true } without removing if members would become empty

3. Project List View

  • 3.1 Create src/views/ProjectList.vue
    • Uses CnListViewLayout with useListView
    • Calls fetchProjects() on mount; passes loading and error states
  • 3.2 Create src/components/ProjectListItem.vue
    • Renders color swatch, icon, title, member count badge, status chip
    • CSS variables only (no hardcoded colors)
  • 3.3 Add search filter (debounced 300 ms)
    • Computed property filters by title and description; does not mutate store
  • 3.4 Add status filter chips
    • Active/Archived/Completed chips using NcChip; maps to status query param
  • 3.5 Add empty state with NcEmptyContent
    • "No projects yet" with create action when projects.length === 0
    • Separate message when search/filter returns no results
  • 3.6 Add "New project" button
    • NcButton type primary opens ProjectCreationDialog

4. Project Creation

  • 4.1 Create src/components/dialogs/ProjectCreationDialog.vue
    • NcDialog with Title (required), Description, Color, Icon fields
  • 4.2 Implement form validation
    • Submit disabled until title.trim().length > 0
    • Inline error shown on blur if title empty
  • 4.3 Implement loading state during creation
    • Spinner on submit button; button disabled; dialog close prevented
  • 4.4 Handle creation success
    • Dialog closes, success toast shown, navigates to /projects/{newId}
    • Warning toast if column creation had partial failures
  • 4.5 Handle creation error
    • Error toast shown; dialog stays open; field values preserved

5. Project Detail / Settings

  • 5.1 Create src/views/ProjectBoard.vue
    • CnDetailPage shell with header (title, icon, color, gear icon)
    • Placeholder content and "View Backlog" link; calls fetchProject on mount
  • 5.2 Handle 403 in ProjectBoard.vue
    • Access-denied NcEmptyContent shown; no project data or gear icon exposed
  • 5.3 Create src/views/ProjectBacklog.vue
    • Placeholder NcEmptyContent with breadcrumb
  • 5.4 Create src/components/ProjectSettingsSidebar.vue
    • Details section: title, description, color, icon, caseReference (read-only)
    • Members section: list with avatars, remove buttons, MemberSearch input, leave link
    • Danger Zone: archive and delete buttons
  • 5.5 Implement immediate metadata reflection after save
    • Page header and breadcrumb update without route reload
    • projectsStore.projects array updated to reflect change

6. Member Management

  • 6.1 Create src/components/MemberSearch.vue
    • Calls Nextcloud Users API with search input; dropdown of results
    • Calls addMember on select; prevents adding existing members
  • 6.2 Implement member removal with assigned-task warning
    • Fetches assigned task count before removing
    • Shows inline warning if count > 0; requires second click to confirm
  • 6.3 Create src/components/dialogs/ProjectLeaveDialog.vue
    • Last-member warning included; explicit confirmation required in both cases

7. Project Deletion

  • 7.1 Create src/components/dialogs/ProjectDeleteDialog.vue
    • Shows task count on open; red confirm button; calls deleteProject on confirm
  • 7.2 Cascade delete in store (see task 2.8)
  • 7.3 Post-delete navigation
    • Navigates to /projects with success toast after delete

8. Navigation and Routing

  • 8.1 Add routes to src/router/index.js
    • /projects, /projects/:id, /projects/:id/backlog added with dynamic imports
  • 8.2 Add Projects entry to src/navigation/MainMenu.vue
    • NcAppNavigationItem with label, FolderOutline icon, router link
  • 8.3 Add PHP routes to appinfo/routes.php
    • Page controller routes for all /projects* paths

9. Access Control

  • 9.1 Filter project list by current user membership
    • Server-side filtering preferred; client-side fallback documented
  • 9.2 Guard /projects/:id route against non-members
    • Access-denied state shown (not silent redirect)

10. i18n

  • 10.1 Add all project strings to l10n/en.json
    • All t(planix, ...) strings from design.md added
  • 10.2 Add Dutch translations to l10n/nl.json
    • All strings translated; key translations verified (Projects→Projecten, etc.)

11. Testing and Quality

  • 11.1 Manual smoke test — project list
  • 11.2 Manual smoke test — project creation
  • 11.3 Manual smoke test — project settings
  • 11.4 Manual smoke test — archive and delete
  • 11.5 Manual smoke test — access control
  • 11.6 Run ESLint
    • All ESLint errors/warnings from this change fixed
  • 11.7 Run composer check:strict
    • All PHP quality issues from this change fixed
  • 11.8 Verify WCAG AA compliance
    • Color swatches have aria-label; keyboard navigation works; dialogs trap focus; contrast meets WCAG AA

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions