Commit d1e69c0
authored
Feature-3984: Edit Project Implementation (#90)
# DevBoard Task
https://dev.azure.com/TDEI-UW/TDEI/_workitems/edit/3984/
## Changes Implemented
### Project Editing
- Added a project edit page at:
```text
/workspace/:workspaceId/projects/:projectId/edit
```
- Restricted project editing to project leads.
- Added editable sections for:
- Project description
- Rich-text instructions
- Custom imagery
- Team members and roles
- Project configuration
- Project actions
- Added dirty-state detection and confirmation before leaving with
unsaved changes.
- Disabled configuration changes after tasks have been generated or the
project has left draft status.
- Added project actions:
- Reset all tasks
- Close project
- Permanently delete project
- Added API methods for updating, resetting, closing, and deleting
projects.
- Added an edit button to the project details page for project leads.
- Standardized task mutation errors through the shared HTTP error
resolver.
### Shared API Error Handling
Expanded `resolveHttpErrorMessage()` to support:
- `detail` strings
- Nested `detail.message` objects
- FastAPI validation arrays using `msg`
- Validation arrays using `message`
- Top-level `message`
- Top-level `error`
- Plain-text responses
- Existing-lock metadata containing `task_number`
- Caller-provided fallback messages
The shared resolver is used by:
- Project creation and editing
- Contributor mutations
- Task generation and saving
- Task locking and unlocking
- Task submission
### Custom Imagery
- Added custom-imagery input to project creation and editing.
- Added JSON parsing and schema validation.
- Added validation debouncing and stale-request protection.
- Added typed custom-imagery models using `ImagerySource`.
- Added conversion from project imagery configuration to Rapid imagery
configuration.
- Added project imagery during initial Rapid startup.
- Added imagery when switching an already-loaded Rapid editor between
workspaces.
- Added custom imagery to the initial Rapid map hash.
- Selected the configured imagery as the active Rapid background.
### Rapid Authentication and Integration
- Fixed an initialization race that allowed Rapid to fall back to its
built-in OSM OAuth login.
- Rapid authentication is now applied immediately after initialization
begins.
- Editor and uploader event registration happens after initialization
finishes.
- Prevented redirects to the OSM `/login` endpoint during changeset
upload.
### Confirmation Dialog Accessibility
Improved the confirmation dialog with:
- `role="dialog"`
- `aria-modal="true"`
- Unique title IDs through `useId()`
- Initial focus placement
- Tab and Shift+Tab focus trapping
- Escape-key handling
- Focus restoration to the opening element
- Disabled-state handling while actions are running
- Event-listener cleanup during unmount
### Impacted Areas Of Testing
#### Project Editing
1. Sign in as a project lead.
2. Open a project details page.
3. Confirm the edit button is visible.
4. Open the edit page.
5. Verify all sections appear in the sidebar.
6. Change the project description.
7. Change the instructions.
8. Save the project.
9. Return to the project details page.
10. Confirm the updated description and instructions are displayed.
11. Sign in as a non-lead.
12. Confirm the edit button is hidden.
13. Navigate directly to the edit URL.
14. Confirm access is rejected with a `403` response.
**Contributor Management**
1. Open the **Team Members** section.
2. Search for a workspace user by name or email.
3. Assign a role and add the user.
4. Confirm the user appears in the project member list.
5. Change the user’s role.
6. Reload the page and confirm the new role persists.
7. Remove the user.
**Project Actions**
Reset Tasks
1. Open the **Actions** section.
2. Select **Reset All Tasks**.
3. Confirm the confirmation dialog appears.
4. Cancel and verify no action occurs.
5. Reopen and confirm the action.
6. Confirm navigation returns to the Tasks tab.
7. Confirm task assignments and progress were reset.
Close Project
1. Select **Close Project**.
2. Confirm the action.
3. Confirm the project status becomes completed.
Delete Project
1. Create a disposable project.
2. Select **Delete Project**.
3. Confirm the destructive action.
4. Confirm navigation returns to the project list.
5. Confirm the project no longer appears.
Custom Imagery Validation
1. Open project creation or editing.
2. Enter invalid JSON.
3. Confirm a validation error appears.
4. Enter a JSON array instead of one imagery object.
5. Confirm it is rejected.
6. Enter an object that does not match the schema.
7. Confirm schema validation errors appear.
8. Enter a valid imagery object.
9. Confirm the error clears.
10. Save the project.
11. Reload the page.
12. Confirm the imagery JSON persists.
Example:
```json
{
"id": "example-imagery",
"name": "Example Imagery",
"type": "xyz",
"url": "https://tiles.example.com/{z}/{x}/{y}.png",
"icon": "https://example.com/icon.svg",
"description": "Example imagery",
"extent": {
"polygon": [],
"max_zoom": 19
},
"attribution": {
"required": true,
"text": "Example attribution",
"url": "https://example.com/attribution"
}
}
```
## Rapid Custom Imagery
1. Open a task belonging to a project with custom imagery.
2. Confirm Rapid loads successfully.
3. Confirm the project imagery appears in the imagery list.
4. Confirm it is selected as the active background.
5. Return to the project page.
6. Open another project with different imagery without fully refreshing.
7. Confirm the second project’s imagery is added and selected.
8. Open a project without custom imagery.
9. Confirm Rapid still loads normally.
### Screenshots:
<img width="1470" height="956" alt="Screenshot 2026-07-14 at 6 03 21 PM"
src="https://github.com/user-attachments/assets/4027362e-d10d-4ed6-bf89-63d30075e81a"
/>
<img width="1470" height="956" alt="Screenshot 2026-07-14 at 6 03 34 PM"
src="https://github.com/user-attachments/assets/bc695f12-8da6-48b7-8298-33c05eac74d8"
/>
<img width="1470" height="956" alt="Screenshot 2026-07-14 at 6 04 02 PM"
src="https://github.com/user-attachments/assets/436fabe8-891b-44de-b4e3-3bc28d429909"
/>
<img width="1465" height="880" alt="Screenshot 2026-07-14 at 7 33 25 PM"
src="https://github.com/user-attachments/assets/515ab52b-14d9-4b43-ae16-0c1f77a7d960"
/>
<img width="1470" height="871" alt="Screenshot 2026-07-14 at 7 33 37 PM"
src="https://github.com/user-attachments/assets/c4b8be3c-36b1-4539-baf7-9b2fe77f6fc0"
/>
<img width="1470" height="878" alt="Screenshot 2026-07-14 at 7 33 47 PM"
src="https://github.com/user-attachments/assets/992e917b-4315-4cbf-b0ed-ff89ae17a4de"
/>
<img width="1470" height="872" alt="Screenshot 2026-07-14 at 7 33 59 PM"
src="https://github.com/user-attachments/assets/d8bcfb71-7b0f-4ba8-a443-e34567aeaef1"
/>
<img width="1470" height="828" alt="Screenshot 2026-07-14 at 7 34 05 PM"
src="https://github.com/user-attachments/assets/30e5926f-05a4-4eb8-9fdf-845697884a5f"
/>
<img width="1470" height="779" alt="Screenshot 2026-07-14 at 7 34 13 PM"
src="https://github.com/user-attachments/assets/1ee93c47-cd0e-4626-bb83-52fe5bfe0839"
/>
<img width="1470" height="956" alt="Screenshot 2026-07-16 at 1 46 16 PM"
src="https://github.com/user-attachments/assets/d9dd111b-f916-4a9c-8fa2-c5dd192ea771"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary
- Added project editing at
`/workspace/:workspaceId/projects/:projectId/edit`, restricted to
project leads, including editable fields for project description,
rich-text instructions, custom imagery (single JSON object + schema
validation), team members and roles, configuration (lock timeout +
review-required), and project actions (reset tasks, close project,
permanently delete).
- Implemented the edit-page flow with per-section dirty-state detection
(including rich-text normalization and trimmed comparisons), conditional
Save enablement, and unsaved-change protection via both
`onBeforeRouteLeave` and `beforeunload` using an accessible confirmation
dialog.
- Added project-edit support in the data/API layer and UI navigation:
- New workspace project API methods: `updateWorkspaceProject`,
`closeWorkspaceProject`, `resetWorkspaceProject`, and
`deleteWorkspaceProject`, plus improved normalization (e.g.,
`description`/`summary`, nullable `instructions`, `custom_imagery`) and
tighter user-role 404 handling.
- Updated project details page to show an “Edit project” button for
leads, and improved HTTP-aware error messaging throughout.
- Introduced shared infrastructure and UX/accessibility improvements:
- New `components/AppConfirmationDialog.vue` with focus restoration,
Escape-to-close, Tab focus trapping, busy/disabled states, and spinner
primary action.
- Enhanced dialog/toast text wrapping (`overflow-wrap: anywhere`,
including Toastify toast body layout) to prevent long unbroken messages
from overflowing.
- Improved rich-text editor interactions (prevent toolbar mousedown
default, safer model update emitting, avoid re-setting content while
focused).
- Added imagery field validation UI/accessibility on wizard/edit steps
(invalid/aria-describedby, role="alert" error messaging, and `aria-live`
validating help).
- Improved contributor UX with new error+retry state for user search,
plus shared pagination via new `composables/usePagination` (applied to
contributors and tasks tabs).
- Hardened project creation/wizard behavior around custom imagery and
persistence:
- Wizard now validates custom imagery in the “details” step via
`validateProjectCustomImagery` (debounced async + request-id to ignore
stale results) and blocks progression/creation on validation failure.
- Added strict parsing for custom imagery payloads (trims, ignores
empty, requires a single non-array JSON object) and sanitizes review
instructions HTML before persisting.
- Improved wizard stored-state hydration/validation
(`isValidProjectWizardStoredState`) and made localStorage persistence
safer (debounced writes, try/catch for quota/access failures).
- Improved Rapid integration for imagery + unsaved edits:
- `RapidManager` init/switch now accepts an optional
`customImagerySource` and wires it into Rapid after init/reset; Rapid
auth patching was refactored.
- Task editor navigation is blocked when Rapid has unsaved edits, using
the confirmation dialog + route-leave/beforeunload interception.
- Hash/background handling now injects background parameters based on
`project.customImagery`.
- Added supporting utilities/types and robustness changes:
- `services/http.ts` now provides consistent HTTP error message
resolution (JSON/non-JSON parsing, nested `detail` handling,
lock/task-specific messaging).
- `services/index.ts` now requires `VITE_NEW_API_URL` to be set.
- `services/project-wizard.ts` switches name availability checks to a
dedicated validate-name endpoint (`exists` boolean).
- Added `util/rapid-imagery.ts` conversion from `ImagerySource` to Rapid
imagery source.
- Added `useProjectEditActions` (reset/close/delete flows with dialog +
toasts + navigation) and `useProjectEditMembers` (role editing with lead
constraint enforcement, optimistic updates, rollback on failure, and
confirmation-based removals).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->33 files changed
Lines changed: 3468 additions & 306 deletions
File tree
- assets/scss
- components
- project-wizard
- steps
- workspace-project-details
- composables
- data
- pages/workspace/[id]/projects
- [projectId]
- tasks/[taskId]
- services
- types
- util
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
313 | 313 | | |
314 | 314 | | |
315 | 315 | | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
144 | 147 | | |
145 | 148 | | |
146 | 149 | | |
| |||
196 | 199 | | |
197 | 200 | | |
198 | 201 | | |
199 | | - | |
| 202 | + | |
200 | 203 | | |
201 | 204 | | |
202 | 205 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
35 | 49 | | |
36 | 50 | | |
37 | 51 | | |
| |||
119 | 133 | | |
120 | 134 | | |
121 | 135 | | |
| 136 | + | |
122 | 137 | | |
123 | 138 | | |
124 | 139 | | |
| |||
129 | 144 | | |
130 | 145 | | |
131 | 146 | | |
| 147 | + | |
132 | 148 | | |
133 | 149 | | |
134 | 150 | | |
| |||
0 commit comments