Manage Lifecycles and Statuses
Lifecycles and statuses can be managed by maintainers of a root namespace (top-level group). It's a settings page (like custom fields) under `Issues` (soon to be renamed to `Work items`). In the first iteration it's probably the same page as custom fields and we'll use sections for status and custom fields.
### From the parent epic acceptance criteria
- [ ] Navigate to the root group's Settings > Work Items > Status.
- [ ] View the default statuses grouped by their category
- [ ] Update status names
- [ ] Create a new status
- [ ] Reorder statuses within a category
- [ ] Set a default status in each state (open/closed)
- [ ] Delete a status
- [ ] ~Map soon-to-be deleted status to other existing status.~ [Not part of Iteration 2](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items_custom_status/#:~:text=As%20part%20of,but%20not%20deleted.)
- [ ] ~Update all corresponding work items with the deleted status to the new status~ [Not part of Iteration 2](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items_custom_status/#:~:text=As%20part%20of,but%20not%20deleted.)
- [ ] Status configuration is only available for ~"GitLab Premium" and above.
- [ ] Cascade and apply configuration to all child namespaces (subgroups and projects)
- [ ] Instrument event tracking for create, update, delete status (record status and category name as custom attributes) [MAU/WAU + Total Counts)
- [ ] Status configuration and interactions are available in our GraphQL API (public). What we need to perform these actions in the frontend fulfills this requirement.
### Non-goals (because part of iteration 3)
1. Manage multiple lifecycles
1. Create new lifecycles
2. Attach work item types to lifecycles
For now we can assume two different "screens":
1. **Lifecycle overview**: Lists lifecycles and the work item types that are attached to it and its statuses. It only displays data, no actions just buttons. You can then edit the lifecycle.

3. **Edit lifecycle** shows a list of status categories and their statuses under those categories. Statuses can be reordered within their category. Status categories are hard coded. The category of a status cannot be changed for now. Actions:
1. Attach status to lifecycle
2. Change a status (maybe through modal because status is a namespace global entity and changes apply to all usages of it in different lifecycles)
3. Reorder statuses within their category
4. Set open/closed/duplicated defaults
5. remove status (from the lifecycle).

And additional modals for:
1. **Remove status (from lifecycle)**: The user needs to provide a new status for the items that currently have this status. If it's a default status (open/closed/duplicate), the user needs to set a new default.
## Lifecycle overview
We could form a single GraphQL API call to a new endpoint `lifecycles` and request everything that we're interested in. For example:
```
query getLifecycles {
namespace(fullPath: "flightjs") {
id
lifecycles {
id
name
default_open_status
default_closed_status
default_duplicated_status
# Maybe no connection type needed?
workItemTypes {
id
name
iconName
}
# Using only statuses would allow us to reuse existing approaches of
# just having a list of statuses.
statuses {
id
name
iconName
color
# We need this to distribute statuses to its categories
category
# Maybe we don't need to expose it because it's already sorted correctly
position
}
}
}
}
```
## Edit lifecycle
For status updates, only provide list of arrays. Backend will calculate new position and assignment based on that list.
## Add status
Add status separately:
```
mutation createStatus($input: CreateStatusInput!) {
StatusesCreate(input: $input) {
status {
id
name
iconName
color
category
}
errors
}
}
```
1. Fetch all lifecycles and assigned statuses in a lifecycle query for the namespace
1. Use lifecycle mutation to update the full lifecycle including assigned statuses.
1. Then pass everything to a `Lifecycle::UpdateService`. When it's first run for a namespace (when it doesn't have any custom lifecycles), we initiate the migration from system-defined to custom statuses/lifecycles.
2. We'll add all records for custom status and add an entry for each status migration (for example system defined status 3 to custom status 5) and enqueue a job to update work items.
3. The job performs updates in batches (without callbacks) using the namespace_id and the status id on `current_statuses`.
4. Once done it removes all migration records.
5. The lifecycle may have a field called `active_migration` which is a boolean to indicate whether a migration is ongoing (for the frontend).
6. TBD whether we'll do some kind of union query for that time for lists so filters continue to work as expected and/or modify select queries to map to new statuses... probably not part of initial iteration
3. Fetch all statuses using a status query for the namespace
4. Use a status mutation to add a new status and rename statuses
1. Question: What happens when a status is not used in any lifecycle? Do we "soft" delete it then? Or keep it forever?
Frontend handles displaying of the lifecycle and statuses.
When adding a status to a lifecycle, the user can choose from existing statuses (if at least one status which is not part of the lifecycle is available). There'll also be the option to create a new status.
epic