-
Notifications
You must be signed in to change notification settings - Fork 0
[OpenSpec] [planix] projects #4
Copy link
Copy link
Closed
Description
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
- Directories
- 1.2 Verify
@conduction/nextcloud-vueexports-
CnListViewLayout,CnDetailPage,CnObjectSidebar,useObjectStore,useListView,useDetailViewall exported - If any export missing, ticket opened and local polyfill stub created
-
- 1.3 Confirm
register-schemasis applied-
projectschema exists in OpenRegister for theplanixregister
-
2. Project Store
- 2.1 Create
src/store/projects.js— base store withuseObjectStore- Pinia store
useProjectsStorecreated -
useObjectStoreimported withregister: planix,schema: project - Reactive state:
projects,activeProject,loading,errorexposed
- Pinia store
- 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
- Calls
- 2.3 Implement
fetchProject(id)- Calls
objectStore.getObject(id) - 403 sets
errortoforbiddenand redirects to/projects
- Calls
- 2.4 Implement
createProject(data)- POSTs with
status: activeandmembers: [currentUserUid] - Calls
createDefaultColumns(newProjectId)on success - Returns new project object; sets error and throws on failure
- POSTs with
- 2.5 Implement
updateProject(id, data)- Calls
objectStore.updateObject(id, data) - Updates
projectsarray in place andactiveProjectif matching
- Calls
- 2.6 Implement
createDefaultColumns(projectId)- Reads
default_columnsfrom admin settings with hardcoded fallback - POSTs each column; returns
{ created, failed }without throwing on partial failure
- Reads
- 2.7 Implement
archiveProject(id)- Calls
updateProject(id, { status: archived })and removes from list
- Calls
- 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
- Appends uid to
- 2.10 Implement
removeMember(projectId, userUid)- Filters out uid from
members[] - Returns count of tasks assigned to uid in this project
- Filters out uid from
- 2.11 Implement
leaveProject(projectId)- Calls
removeMember(projectId, currentUserUid) - Returns
{ isLastMember: true }without removing if members would become empty
- Calls
3. Project List View
- 3.1 Create
src/views/ProjectList.vue- Uses
CnListViewLayoutwithuseListView - Calls
fetchProjects()on mount; passes loading and error states
- Uses
- 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 tostatusquery param
- Active/Archived/Completed chips using
- 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
- "No projects yet" with create action when
- 3.6 Add "New project" button
-
NcButtontype primary opensProjectCreationDialog
-
4. Project Creation
- 4.1 Create
src/components/dialogs/ProjectCreationDialog.vue-
NcDialogwith 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
- Submit disabled until
- 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
- Dialog closes, success toast shown, navigates to
- 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-
CnDetailPageshell with header (title, icon, color, gear icon) - Placeholder content and "View Backlog" link; calls
fetchProjecton mount
-
- 5.2 Handle 403 in
ProjectBoard.vue- Access-denied
NcEmptyContentshown; no project data or gear icon exposed
- Access-denied
- 5.3 Create
src/views/ProjectBacklog.vue- Placeholder
NcEmptyContentwith breadcrumb
- Placeholder
- 5.4 Create
src/components/ProjectSettingsSidebar.vue- Details section: title, description, color, icon, caseReference (read-only)
- Members section: list with avatars, remove buttons,
MemberSearchinput, 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.projectsarray 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
addMemberon 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
deleteProjecton confirm
- Shows task count on open; red confirm button; calls
- 7.2 Cascade delete in store (see task 2.8)
- 7.3 Post-delete navigation
- Navigates to
/projectswith success toast after delete
- Navigates to
8. Navigation and Routing
- 8.1 Add routes to
src/router/index.js-
/projects,/projects/:id,/projects/:id/backlogadded with dynamic imports
-
- 8.2 Add Projects entry to
src/navigation/MainMenu.vue-
NcAppNavigationItemwith label,FolderOutlineicon, router link
-
- 8.3 Add PHP routes to
appinfo/routes.php- Page controller routes for all
/projects*paths
- Page controller routes for all
9. Access Control
- 9.1 Filter project list by current user membership
- Server-side filtering preferred; client-side fallback documented
- 9.2 Guard
/projects/:idroute 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
- All
- 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
Reactions are currently unavailable