This document provides a comprehensive overview of the Issue Tracker module, its architecture, functionalities, and key implementation details. It is intended for developers and stakeholders to understand the various aspects of this module within the Nimbly Audit Lite application.
2. Screens and Routes
This section outlines the primary user interface screens that comprise the Issue Tracker module and their corresponding navigation routes within the Expo application. The primary screens for the Issue Tracker module are located within the apps/expo/app/issues/ directory. You can explore the source code for these screens on GitHub.
Screen File Name
Route Path (Approximate)
Purpose & Functionality
Main Feature Component
(index.tsx/home.tsx)
/issues or /issues/home
Displays the list of issues; allows viewing, searching.
app/features/issues/home/screen.tsx
bulkEdit.tsx
/issues/bulkEdit
Allows users to edit multiple issues at once.
BulkEditList from app/features/issues/components/bulkEdit
create-generic.tsx
/issues/create-generic
Provides a generic form for creating new issues.
GenericIssueCreate from app/features/issues/create-generic/screen
create.tsx
/issues/create
Provides a specific form for creating new issues.
IssueCreate from app/features/issues/create/screen
details.tsx
/issues/details
Displays the detailed information of a specific issue.
IssueDetails from app/features/issues/details/screen
filter-option.tsx
/issues/filter-option
Allows users to select and apply filters for the issue list.
IssuesFilterOptionScreen from app/features/issues/filter-option/index
review-request.tsx
/issues/review-request
Handles the process of requesting a review for an issue.
IssueReviewRequestScreen from app/features/issues/review/screen
3. API Interactions
This section details how the Issue Tracker module interacts with the backend APIs, including the specific endpoints used and the repository/usecase functions that manage these interactions. The primary logic for these interactions can be found in the repository and usecase files:
Removes a reviewer from an issue. Params: AddIssueReviewerParams (check if specific params needed).
Upload Issue Attachments
POST
/v1.0/miscellaneous/upload-file/attachment
uploadAttachments
issueUploadAttachments
Uploads file attachments for an issue. Uses FormData, expects UploadFileBody.
Create Issue Message
POST
/v1.0/issues/issueMessages/create/v2
createIssueMessage
createIssueMessage
Creates a new message/comment on an issue. Request body: CreateIssueMessageRequest.
Translate Issue Message
POST
/v1.0/vertex-ai/translation/v1
getIssueMessageTranslate
getIssueTranslationMessage
Translates an issue message using Vertex AI. Request body: IssueMessageTranslationI.
4. Key Packages and Dependencies
This section lists the key external libraries and internal packages that the Issue Tracker module depends on, along with their versions and primary roles in the module’s functionality. The versions for the following packages are sourced from packages/app/package.json. You can view the full file on GitHub.
Package Name
Version (from package.json if available)
Usage in Issue Tracker
@tanstack/react-query
^5.62.3
Managing server state, caching, and asynchronous data fetching for issues, issue details, etc. Handles loading states, error states, and refetching logic.
react-hook-form
^7.48.2
Handling forms for creating and editing issues, managing form state, validation, and submission.
zod
^3.22.4
Schema definition and validation, often used in conjunction with react-hook-form to validate issue form data.
jotai
^2.10.3
Client-side state management for UI-related state that doesn’t need to be persisted on the server (e.g., managing [visibility](../Settings/Data Visibility/DataVisibilityOverview.md) of modals, local UI preferences).
tamagui (e.g. @tamagui/core)
1.108.4 (for related packages)
Provides the UI component library (e.g., YStack, Text, Button) for building the user interface of the issue tracker screens and components.
expo-router
Not specified in main app package.json
Handles navigation and routing between different screens of the issue tracker within the Expo application.
dayjs
^1.11.11
Utility library for parsing, validating, manipulating, and displaying dates and times (e.g., for due dates, created/updated timestamps).
query-string
^7.0.0
Parsing and stringifying URL query parameters, particularly for managing filter states in the URL for the issue list.
apisauce
^3.0.1
HTTP client for making API requests to the backend for issue-related operations.
@nimbly-technologies/nimbly-common
Local package / Not specified in main app package.json
Provides common types, interfaces, and possibly utility functions shared across different parts of the Nimbly application, including request/response types for issue APIs.
fuse.js
^7.0.0
Likely used for client-side fuzzy searching capabilities within lists, such as filtering users or categories in selection components.
react-native-mmkv
^2.10.2
Efficient mobile key-value storage, potentially used for persisting user preferences or caching small pieces of data locally.
@shopify/react-native-skia
1.2.3
If used directly by issues components, mention its role (e.g., for custom drawing or UI effects). Otherwise, usage is likely indirect via other UI libraries.
moti
^0.29.0
Animation library, likely used for UI animations and transitions within the issue tracker components.
Firebase Messaging SDK, potentially for push notifications for issue updates.
@react-native-firebase/storage
^18.6.0
Firebase Storage SDK, potentially for storing attachments if not handled by a custom backend.
5. Core Logic, Flows, and Components
This section delves into the fundamental operations of the Issue Tracker module, detailing the logic, user flows, and key software components involved in each core functionality.
5.1 Issue Listing & Filtering
This subsection describes how issues are fetched, displayed, filtered, and searched within the application. The core functionality revolves around the main issue listing screen (packages/app/features/issues/home/screen.tsx), filter components, and state management controllers that enable users to efficiently find and manage issues.
5.1.1 Core Components and Their Roles
IssueScreen (packages/app/features/issues/home/screen.tsx): Serves as the main container for the issue listing functionality. It orchestrates the display of the search bar, filter options, and the issue list itself. It also handles the selection mode for [bulk operationss](../Bulk Operation/BulkOperationOverview.md).
IssueList (packages/app/features/issues/components/list/IssueList.tsx): Responsible for rendering the list of issues. It utilizes a VirtualList for efficient rendering of potentially large datasets and integrates with useAllIssueQuery for data fetching and infinite scrolling. It also manages the state for bulk selection of issues.
IssueCard (packages/app/features/issues/components/list/IssueCard.tsx): Displays a concise summary of a single issue within the list, including key fields like title, status, priority, assignee, and timestamps. It’s designed for quick scanning and also handles actions like selection for [bulk operationss](../Bulk Operation/BulkOperationOverview.md) or navigation to issue details.
ListSearchBar (packages/app/features/issues/components/list/ListSearchBar.tsx): Provides the UI for users to input search terms. It incorporates debouncing logic to optimize performance by delaying the search query until the user has paused typing.
Filter UI Components:
IssuesDefaultFilter (packages/app/features/issues/components/filters/defaultFilters.tsx): Presents a set of predefined, commonly used filters (e.g., “My Open Issues,” “Recently Updated”) for quick access.
Filter Option Screen (apps/expo/app/issues/filter-option.tsx): A dedicated screen that hosts more comprehensive and advanced filter options, allowing users to build complex queries using various filter criteria components (e.g., status, priority, assignee, dates, custom fields) primarily located in packages/app/features/issues/components/filters/.
5.1.2 Data Fetching and State Management (Controllers)
The listing and filtering mechanism relies heavily on a set of interconnected controllers that manage URL parameters, filter state, and data fetching via API calls.
URL as Source of Truth: This hook is fundamental to the filtering mechanism. It establishes the URL query parameters as the single source of truth for the current filter and search state. It parses parameters from the URL (using expo-router’s useLocalSearchParams) and provides them to other parts of the application.
Preset Filter Application: It can apply default or preset filters (e.g., “My Open Issues”) if no specific filters are present in the URL, ensuring a sensible default view for the user.
Updating URL: It also provides a setParams function that allows other controllers (like useFilterController) to update the URL query parameters when a filter changes, thus triggering a data refresh.
Orchestration: Acts as the central coordinator for filter-related actions. It takes filter changes from UI components (like IssuesDefaultFilter or the advanced filter screen) and uses useIssueParams’ setParams function to update the URL.
State Management: Manages the state of currently applied filters, often deriving this from useIssueParams. It might also handle the logic for saving and applying custom user-defined filter sets.
Form State for Advanced Filters: Specifically manages the state of the forms used within the advanced filter screen (apps/expo/app/issues/filter-option.tsx). It uses react-hook-form to handle user input for various filter criteria, including validation if necessary.
Submission Logic: On submitting the filter form, it typically collaborates with useFilterController to update the URL parameters with the selected filter values.
Data Fetching Core: This hook is the cornerstone of data retrieval for the issue list. It utilizes @tanstack/react-query’s useInfiniteQuery hook to fetch issues from the backend API (/v1.0/issues/issues/v2).
Parameter Construction: It receives filter parameters (parsed by useIssueParams and potentially modified by useFilterController). These parameters (e.g., sortBy, page, qUser, status, priorityID, search keyword q) are then constructed into the query string for the API request.
Default Filter Logic: Incorporates logic to apply default filters if no specific parameters are provided. For example, it might default to fetching issues assigned to the current user (qUser=me) or issues with an open status.
Pagination: Handles pagination by managing page numbers and requesting subsequent pages of data as needed for infinite scrolling. The getNextPageParam function within its configuration is crucial for determining how to fetch the next chunk of data.
Caching: Leverages @tanstack/react-query’s caching capabilities. Fetched issue data is cached, reducing API calls on subsequent visits or when re-applying filters, and enabling features like stale-while-revalidate for background updates.
5.1.3 Detailed Data Flow and Interactions
Initial Load / Navigation:
User navigates to the issue list screen (IssueScreen).
useIssueParams reads the URL. If no specific filters are set, it might apply default parameters (e.g., for “My Open Issues”).
useAllIssueQuery is invoked with these initial parameters.
It calls the getAllIssues function from issueUsecase (which in turn uses issueRepository) to make an API GET request to /v1.0/issues/issues/v2 with the constructed query parameters.
The API returns the first page of issues.
IssueList receives this data and renders the initial set of IssueCard components.
Applying a Filter (e.g., via IssuesDefaultFilter or Filter Screen):
User interacts with a filter component (e.g., clicks “Status: Open” on the advanced filter screen).
The filter UI component (managed by useFilterForm if it’s a form) communicates the change to useFilterController.
useFilterController calls the setParams function provided by useIssueParams, updating the URL query parameters (e.g., adding status=OPEN).
useIssueParams detects this change in URL parameters (as expo-router updates the local search params).
This change in parameters causes useAllIssueQuery to automatically refetch data because its query key (which includes these parameters) has changed.
A new API request is made with the updated filter parameters.
The API returns the filtered list of issues.
IssueList re-renders with the new data.
Searching:
User types into ListSearchBar.
The ListSearchBar uses a debouncing mechanism (e.g., useDebounce hook) to delay triggering the search. This means it waits for a brief pause in typing (e.g., 300-500ms) before proceeding.
Once the debounce period elapses, the search term is passed to useFilterController (or directly updates params via useIssueParams).
useFilterController updates the URL with the search term (e.g., q=searchTerm).
This triggers useAllIssueQuery to refetch with the new q parameter, and the list updates with search results. The server handles the actual search logic based on the q parameter.
Infinite Scrolling:
As the user scrolls down the IssueList, the VirtualList component detects when the user is nearing the end of the currently loaded data.
IssueList then calls the fetchNextPage function provided by useAllIssueQuery (which comes from @tanstack/react-query’s useInfiniteQuery).
useAllIssueQuery uses its getNextPageParam logic to determine the parameters for the next page (e.g., incrementing the page number).
An API request is made for the next page of issues.
The new data is appended to the existing list of issues within @tanstack/react-query’s cache.
IssueList receives the updated, longer list and renders the additional IssueCard components, creating a seamless infinite scroll effect.
5.1.4 Special Features in Issue Listing
Bulk Selection in List:
The IssueScreen can enter a “selection mode,” typically triggered by a user action like a long press on an IssueCard or a dedicated “Select” button.
When in selection mode, IssueList and IssueCard components adapt their behavior:
IssueCard displays a checkbox and handles press events to toggle the selection state of the individual issue.
IssueList maintains a list of selected issue IDs (e.g., in a React state variable like selectIssues). When an issue is selected/deselected, this list is updated.
The IssueList might interact with issueRepository functions like setBulkEditSelectedIds and getBulkEditSelectedIds if the selection needs to be persisted across navigations or accessed by other parts of the module (e.g., for the Bulk Edit screen). These repository functions often use mmkvStore (packages/app/shared/infrastructure/mmkvStore.ts) for local persistence of these IDs.
Once issues are selected, actions like “Bulk Edit” become available, typically navigating the user to apps/expo/app/issues/bulkEdit.tsx with the selected issue IDs.
This means that as the user types, the search query is not sent immediately with each keystroke. Instead, the component waits for a short period of inactivity (e.g., 300-500 milliseconds) before it considers the input complete and triggers the search.
This is typically achieved using a custom useDebounce hook or a similar utility.
Benefit: Debouncing significantly reduces the number of API requests made while the user is typing, improving application performance, reducing server load, and preventing a disruptive UI experience where the list refreshes too frequently.
5.1.5 Data Flow for Issue Listing and Filtering (Sequence Diagram)
The following diagram illustrates the detailed data flow and component interactions for listing and filtering issues:
sequenceDiagram
actor User
participant HomeScreen as IssueScreen (`home/screen.tsx`)
participant SearchBar as ListSearchBar (`list/ListSearchBar.tsx`)
participant FilterUI as FilterComponents (e.g., `defaultFilters.tsx`, `filter-option.tsx`)
participant IssueListCmp as IssueList (`list/IssueList.tsx`)
participant ParamsCtrl as useIssueParams (`params-controller.ts`)
participant FilterCtrl as useFilterController (`filter-controller.ts`)
participant FilterFormCtrl as useFilterForm (`filter-form-controller.ts`)
participant QueryCtrl as useAllIssueQuery (`all-issue-query.ts`)
participant IssueRepo as IssueRepository (`issue-repository.ts`)
participant IssueAPI as BackendAPI
User->>HomeScreen: Navigates to Issues List
HomeScreen->>ParamsCtrl: `useIssueParams` reads URL (initial params or defaults)
ParamsCtrl-->>QueryCtrl: Provides initial/default params
QueryCtrl->>IssueRepo: `getAllIssues` (with params)
IssueRepo->>IssueAPI: GET `/v1.0/issues/issues/v2`
IssueAPI-->>IssueRepo: Returns issues data (page 1)
IssueRepo-->>QueryCtrl: Data received
QueryCtrl-->>IssueListCmp: Provides fetched data
IssueListCmp-->>HomeScreen: Renders initial list of issues
alt User applies a filter (e.g. from `filter-option.tsx`)
User->>FilterUI: Selects filter criteria
FilterUI->>FilterFormCtrl: Manages form state for filter criteria
FilterFormCtrl->>FilterCtrl: Filter form submitted with values
FilterCtrl->>ParamsCtrl: `setParams` updates URL query parameters (e.g., `status=OPEN&priorityID=HIGH`)
ParamsCtrl detects URL change
ParamsCtrl-->>QueryCtrl: Provides new params (including `q`)
QueryCtrl->>IssueRepo: `getAllIssues` (with search param `q`)
IssueRepo->>IssueAPI: GET `/v1.0/issues/issues/v2` (with `q`)
IssueAPI-->>IssueRepo: Returns searched issues data
IssueRepo-->>QueryCtrl: Data received
QueryCtrl-->>IssueListCmp: Provides updated (searched) data
IssueListCmp-->>HomeScreen: Re-renders issue list
end
alt User scrolls for more issues (Infinite Scroll)
User->>IssueListCmp: Scrolls to bottom of VirtualList
IssueListCmp->>QueryCtrl: `fetchNextPage()` called on `useAllIssueQuery`
QueryCtrl->>IssueRepo: `getAllIssues` (with params for next page, e.g., `page=2`)
IssueRepo->>IssueAPI: GET `/v1.0/issues/issues/v2` (with next page param)
IssueAPI-->>IssueRepo: Returns next page of issues
IssueRepo-->>QueryCtrl: Data received, appends to existing pages
QueryCtrl-->>IssueListCmp: Provides combined data
IssueListCmp-->>HomeScreen: Renders additional issues
end
alt User enters bulk selection mode and selects issues
User->>HomeScreen: Long press on IssueCard or taps "Select" button
HomeScreen->>IssueListCmp: Activates selection mode (e.g. sets `isSelectionMode = true`)
User->>IssueListCmp: Taps on checkboxes on IssueCards
IssueListCmp->>IssueListCmp: Updates internal state of selected issue IDs (`selectIssues`)
IssueListCmp->>IssueRepo: Optionally calls `setBulkEditSelectedIds` (persists to MMKV)
end
5.2 Issue Creation
This section details the process of creating new issues within the application, covering two distinct flows: Contextual Issue Creation and Generic Issue Creation. Both flows aim to capture comprehensive information through user input, validate this data, handle optional attachments, and finally submit it to the backend.
5.2.1 Process Overview and Flow Differentiation
The choice between contextual and generic issue creation depends on the user’s entry point and the nature of the issue being reported.
Contextual Issue Creation:
Trigger: Initiated when an issue is directly linked to another entity within the application, such as a specific question in an audit, a task, or a piece of equipment.
Data: This flow often starts with pre-filled contextual information (e.g., site ID, related entity ID, question text) passed as parameters, simplifying user input.
Purpose: Tailored for creating issues that are intrinsically part of a larger workflow or data structure within the application.
Generic Issue Creation:
Trigger: Used for standalone issues not directly linked to a specific in-app entity, or when a user initiates creation from a general “new issue” button.
Data: Requires the user to manually input all necessary details as it lacks pre-filled contextual data.
Purpose: Provides a general-purpose form for reporting any type of issue, offering more flexibility when the issue isn’t tied to a specific context.
Both screens ultimately guide the user through a structured form to ensure all required information for a CreateIssueRequest (or its generic variant) is captured.
5.2.2 Screen Structure and Key Components
Both IssueCreate and GenericIssueCreate screens share a common structure, built using components from @my/ui and custom feature components:
FormProvider: Both screens wrap their content in a FormProvider from react-hook-form, which provides the form context to all nested input components.
ScrollView: The main form content is wrapped in a ScrollView to accommodate various input fields and ensure usability on different screen sizes.
YStack: Used for vertical layout of form elements.
Submission Button: A Button component is used for submitting the form. Its state (enabled/disabled) is controlled by form validity (formState?.isValid), API call pending status (isPending), and attachment upload status (issueAttachmentUploading).
Key Input Components for Contextual Issues (packages/app/features/issues/create/components/):
QuestionText.tsx: Displays pre-filled question text (if applicable).
Category.tsx: Selector for issue category.
[Sites](../Sites/SitesOverview.md).tsx: Selector for the site.
Priority.tsx: Selector for issue priority.
DueDate.tsx: Date picker for the due date.
AssignedDepartment.tsx: Selector for assigning a department.
ReporterDepartment.tsx: Selector for the reporter’s department.
FlagSeverity.tsx: Component to set issue severity.
AssignedUser.tsx: Selector for assigning a user.
Comments.tsx: Input field for additional comments.
IssueAttachments.tsx: Component for handling file attachments.
ApprovalRequest.tsx (from create-generic/components): Allows initiating an approval request upon creation.
Key Input Components for Generic Issues (packages/app/features/issues/create-generic/components/):
Summary.tsx: Input for the issue title/summary. (Equivalent to QuestionText in contextual)
Description.tsx: Input for detailed issue description.
Category.tsx: Selector for issue category.
AssignedTo.tsx: Selector for assigning a user or department.
StartDate.tsx / EndDate.tsx: Date pickers for start and end dates (relevant for task-like issues).
DueDate.tsx: Date picker for the due date.
Priority.tsx: Selector for issue priority.
IssueAttachments.tsx: Component for handling file attachments.
ApprovalRequest.tsx: Allows initiating an approval request upon creation.
5.2.3 Controllers and State Management
The primary logic for both creation screens is managed by their respective screen controllers and form hooks.
Role: Initializes the form using useCreateIssueForm with specific initial values (issueCreationInitialvalue). It fetches the authenticated user’s ID (data?.userID) and sets it as the createdBy field in the initial values. It then returns the form methods (createIssueForm, errors, control, formState, setValue, handleSubmit) to be used by the screen component.
issueCreationInitialvalue:
Contextual (CreateIssueRequest type): Defines default fields like assignedDepartments: [], priority: 'normal', status: 'open', photoLimit: 10, and importantly, createdBy which is populated with the current user’s ID. Context-specific fields like questionIndex, section, siteID would be populated based on navigation parameters.
Generic (CreateGenericIssueRequest type): Similar defaults for common fields, but includes questionDescription: '', type: 'generic', triggerNotification: true. It lacks audit-specific fields like section or questionIndex by default.
Shared Jotai Atoms:
issueAttachmentUploadingAtom (from create-issue-controller.ts in both contexts, though the generic path is ../create-generic/controller/create-issue-controller): A boolean atom that is true when any attachment is actively being uploaded. This is used to disable the submission button to prevent submitting incomplete data.
issueApprovalDataAtom (from create-generic/controller/create-issue-controller.ts): Stores the data for a potential approval request (ApprovalData type: { approvalType, approvers, comments, attachments }) that can be initiated alongside issue creation. Used by both flows.
isCreateApprovalAtom (from create-generic/controller/create-issue-controller.ts): A boolean atom indicating whether an approval process should be initiated upon creating the issue. Used by both flows.
5.2.4 Form Validation (useCreateIssueForm and Yup)
useForm<CreateIssueRequest | CreateGenericIssueRequest> is initialized with a Yup validation schema via resolver: useYupValidation(validationSchema).
mode: 'onChange' and reValidateMode: 'onChange' ensure that validation occurs as the user types and when field values change, providing immediate feedback.
questionText (Summary): Required string, min 10 characters, max 200 characters.
questionDescription: String, optional.
startDate, endDate, dueDate: Not required. If provided, startDate and endDate are validated to be after one month ago. endDate must be after startDate. dueDate must be after the current time.
referenceKeys: An object memoized from attachments, mapping attachment IDs to their server-provided referenceKey upon successful upload. These keys are crucial for associating the uploaded files with the issue on the backend.
Client-Side Validation:
Before attempting upload, _isValidNewAttachment checks files against predefined rules:
FILE_COUNT_EXCEEDED: Total files (new + existing) must not exceed ATTACHMENT_LIMIT (10).
FILE_SIZE_EXCEEDED: Each file’s size must not exceed FILE_SIZE_LIMIT (7MB).
FILE_TYPE_INVALID: File extension must be in acceptedFileType list (images, videos, common documents).
Error messages are shown via toast.show() if validation fails.
Upload Flow:
addAttachments(files: Array<UploadFileI>):
If files are valid, new AttachmentContent entries are created with status: UPLOAD_STATUS.NOT_STARTED.
The local attachments state is updated.
Iterates through attachments and calls _uploadAttachment for any with status NOT_STARTED.
Sets the attachment’s status to UPLOAD_STATUS.IN_PROGRESS and updates state.
Calls mutateAsync(attachment.file) from useIssueUploadAttachmentMutation (which uses issueUsecase.issueUploadAttachments). This function sends the file as FormData to the backend.
On Success: Updates status to UPLOAD_STATUS.COMPLETED and stores the uploadedFile data (containing the referenceKey) in AttachmentContent.
On Error: Sets status to UPLOAD_STATUS.ERROR (though detailed error propagation to the individual attachment item in the code snippet is minimal, primarily relying on global toast for some errors).
The issueAttachmentUploadingAtom is likely set to true at the beginning of addAttachments or _uploadAttachment and false when all uploads are completed or have failed, managed by observing the statuses of all attachments.
Collecting referenceKeys: The referenceKeys memoized in useUploadFileAttachment are then collected by the main form logic. When the issue creation payload is assembled, these referenceKeys (as an array of strings) are included in the attachments field of the CreateIssueRequest.
5.2.6 Submission Process and Mutation Handling
handleCreateIssue Function (in create/screen.tsx and create-generic/screen.tsx):
This function is an async wrapper around handleSubmit from react-hook-form.
handleSubmit(async (data) => { ... }) ensures that handleCreateIssue is only called if client-side Yup validation passes.
Payload Construction:
The validated form data is taken.
isCreateApproval (from Jotai atom) and approvalData (from Jotai atom) are added to the payload if an approval flow is initiated.
For generic issues, endDate is specially handled: if not provided, it defaults to the end of startDate or the end of the current day.
Crucially, the attachments field in data should already contain the array of referenceKeys collected from successful uploads via the IssueAttachments component and its underlying useUploadFileAttachment hook.
API Call:mutateAsync(payload) is called. mutateAsync is from useCreateIssueMutation.
mutationFn: (payload: CreateIssueRequest) => issueUsecase.createIssue(payload): Defines the actual API call.
onSuccess:
Displays a success toast: “Issue Created”.
Navigates the user back (e.g., route.back()).
Invalidates queries with keys including 'all-issues' using queryClient.invalidateQueries(). This ensures that any screen displaying a list of issues will refetch data to show the newly created issue.
onError:
Displays an error toast. The example message “Please provide a Due Date as it is required when the SLA is set to manual” suggests it can handle specific backend validation errors, though the error message itself might be generic if the backend error is not specific.
issueRepository.uploadAttachments constructs FormData, appends the file, and makes a POST request to issueEndpoint.ISSUE_UPLOAD_ATTACHMENTS (/v1.0/miscellaneous/upload-file/attachment). It sets the Content-Type header to multipart/form-data.
The backend returns a structure including the referenceKey for the uploaded file.
issueRepository.createIssue makes a POST request to issueEndpoint.CREATE_ISSUE (/v1.0/issues/issues/create/v2) with the final CreateIssueRequest payload (which includes the referenceKeys for attachments).
If the API call in the repository results in a res.problem, an error is thrown, which is then caught by useCreateIssueMutation’s onError handler.
The following diagram illustrates the comprehensive issue creation flow, incorporating details for both contextual and generic paths, validation, and attachment handling.
graph TD
A[User Initiates Issue Creation] --> B{Contextual or Generic?}
B -- Contextual --> C1[Route: issues/create.tsx - Screen: IssueCreate]
C1 --> D1[Controller: useCreateIssueScreenController - Contextual]
D1 --> E1[Form: useCreateIssueForm - Yup Validation - Initial values from context and user ID]
B -- Generic --> C2[Route: issues/create-generic.tsx - Screen: GenericIssueCreate]
C2 --> D2[Controller: useCreateIssueScreenController - Generic]
D2 --> E2[Form: useCreateIssueForm - Yup Validation - Initial values from user ID only]
E1 --> F[User Fills Form Fields]
E2 --> F
F --> G{Add Attachments?}
G -- Yes --> H[Hook: useUploadFileAttachment]
H --> I1[Validate Files - count, size, type]
I1 -- Fail --> I2[Show Toast: Attachment Error]
I1 -- Pass --> I3[Call addAttachments - then _uploadAttachment]
I3 --> I4[Set issueAttachmentUploadingAtom to true]
I3 --> I5[Call API: issueUsecase.issueUploadAttachments]
I5 -- Success --> I6[Store referenceKey in AttachmentContent]
I5 -- Fail --> I7[Status: ERROR - Show Toast]
I6 --> I8[All uploads done - Set issueAttachmentUploadingAtom to false]
I7 --> I8
I8 --> J[User Clicks Submit]
G -- No --> J
J --> K[Client-side Validation via Yup]
K -- Fail --> L[Display Inline Validation Errors]
K -- Pass --> M[Construct Payload - includes form data, approval flags, referenceKeys]
M --> N[Call useCreateIssueMutation.mutateAsync]
N --> O[Call API: issueUsecase.createIssue]
O -- Success --> P[Show Success Toast - Navigate Back - Invalidate all-issues query]
O -- Fail --> Q[Show Error Toast - e.g. validation error]
Q --> F
5.3 Issue Details
This section describes how individual issue details are displayed and interacted with. It covers the screen structure, data fetching, tab management for “Details” and “Activity” views, state handling with Jotai, key controllers, permission management, and various user interactions like commenting, resolving, and reacting.
Below the tabs, the content area conditionally renders either <IssueDetailTab /> or <IssueActivity /> based on selectedTab, or a loading spinner/empty state.
Initial Data Fetching and State:
The issueID is obtained from route parameters using createParam<{ issueID: string }>().
A loading state is managed by issueDetailsLoadingAtom (Jotai atom), set to true during fetch/refetch and false otherwise.
Automatic Mark as Read: If the fetched data.unreadStatus is true, useUpdateReadStatusInvisibe().mutateAsync() is called to mark the issue as read without explicit user feedback for this action.
State Reset on Tab Change/Unmount:useEffect hooks are used to reset various Jotai atoms related to comment input (issueCommentAtom, issueCommentAttachment, parentCommentDetails, etc.) when selectedTab changes or when the component unmounts, ensuring a clean state for these inputs.
AssignedDepartment: Displays assigned department (for non-generic).
AssignedUser: Displays assigned user.
IssueMember: Displays other involved members.
IssueReviewer: Displays reviewers and review status.
IssueReviewComment: Displays review comments.
IssueAttachments: Lists issue attachments with links (signedURL).
Timestamps: Date Created (selectedIssue?.createdAt) and Last Updated (selectedIssue?.updatedAt), formatted with dayjs.
Conditional Rendering: Many fields are conditionally rendered based on selectedIssue.type (e.g., IssueTypeEnum.GENERIC, customer-feedback).
Action Buttons:
IssueReviewButton (packages/app/features/issues/review/components/issue-review-button.tsx): Its appearance and available actions (e.g., request review, approve, reject) depend on approvalDetails (fetched by useIssueApprovalDetailsController) such as approvalRequest, cancelRequestVisible, status, and approvers.
useIssueApprovalDetailsController (which uses useGetIssueApprovalDetails from issue-query.ts) fetches approvalDetails.
The isLoading state from this hook and whether an approval has been actioned (approvalDetails?.approvers?.find((a) => a.status !== IssueApprovalStatus.PENDING)) can disable editing of fields like Start/End dates (isDateDisabled).
Approval details are refetched when the tab gains focus (useIsFocused) or on initial mount.
issueDetailsAtom: Holds the IssueResponse object for the currently viewed issue. Populated by useIssueQuery.
issueDetailsLoadingAtom: Boolean, true when issueDetailsAtom is being fetched or refetched.
Main Comment Input (IssueComment sheet):
issueMessageSheetAtom: Boolean, controls the visibility of the main comment input sheet.
issueCommentAtom: String, stores the text content of the comment being drafted.
issueCommentAttachmentUpload: Boolean, true if attachments for the current comment are uploading.
issueCommentAttachmentReferenceKeys: Array of strings, stores reference keys of successfully uploaded comment attachments.
issueCommentAttachment: Object, stores Attachment instances for files selected in the comment sheet.
parentCommentDetails: Object { parentComment: string, parentCommentKey: string, parentCommentUserName: string }. Used to store context when replying to a specific comment. It’s populated by handleReply in activity-screen-controlller.tsx.
issueBlockMessageSheetAtom: Boolean, controls visibility of the sheet used for entering a reason when blocking an issue.
issueBlockCommentAtom, issueBlockCommentAttachmentUpload, issueBlockCommentAttachmentReferenceKeys, issueBlockCommentAttachment: Similar to the main comment atoms but specifically for the “block issue” flow.
Review/Approval Comment Input:
issueReviewMessageSheetAtom: Boolean, controls visibility of the sheet for adding comments during review/approval actions.
approveRejectIssueStatusAtom: IssueApprovalStatus | undefined, stores if the action is ‘approve’ or ‘reject’.
reviewerDepartmentAtom: string | undefined, stores department ID if a department reviewer is acting.
issueApprovalCommentAtom: String, stores the text for approval/rejection comments.
Side Effects:onSuccess shows a success toast (for the visible version). onError shows an error toast and logs the error. Commonly used for updating status, priority, assignee, etc., from the IssueDetailTab.
Side Effects: No explicit onSuccess/onError in the hook, but typically triggers a refetch of issue messages via Firebase listener in activity-screen-controlller.tsx.
Dependency: Enabled only when selectedIssue (from issueDetailsAtom) is available. Query key includes selectedIssue?.issueID and selectedIssue?.status to refetch if these change.
It sources permissions from userData?.roleDetails?.permissionMap and organization settings (issueTrackerSettings?.allowAuditorEditIssueMeta).
Key Permission Flags:
allowEditAssignedUser: Governs if the assigned user field can be changed.
allowEditAssignedDepartment: Governs if the assigned department field can be changed.
allowEditDueDate: Governs if the due date can be changed. For auditors, this is additionally checked against allowAuditorEditIssueMeta.
allowEditStatus: Governs if the issue status can be changed. For auditors, also checked against allowAuditorEditIssueMeta.
allowAddMember: Governs if new members can be added to the issue.
These flags are used within components in IssueDetailTab to conditionally enable editing capabilities (e.g., making a field pressable to open an editor, or enabling/disabling action buttons like QuickResolveButton).
5.3.7 User Interactions
Adding Comments/Replies (IssueComment via issue-comments.tsx):
The IssueComment component is displayed in a Sheet controlled by issueMessageSheetAtom.
@Mentions: Uses MentionInput from react-native-controlled-mentions. Suggestions are populated from usersMentionsList (derived from useOverAllUsersQuery). User IDs are extracted using extractUserIds before sending the message and replaced with names for display.
Attachments:IssueAddCommentAttachments component manages attachments for the comment, using issueCommentAttachment atoms. Uploaded attachment referenceKeys are included in the message payload.
Reply Context: If parentCommentDetails.parentCommentKey is set (by handleReply in activity-screen-controlller.tsx), the UI indicates a reply is being composed, and parentCommentKey, parentComment, and parentCommentUserName are included in the message payload.
Submission (handleCreateMessage):
Calls useCreateIssueMessage().mutateAsync with the comment text, attachments, creator info, and parent comment details.
If users were mentioned (mentionUserIDs.length > 0), calls handleAddMember which uses useUpdateSingleIssue().mutateAsync to update the members field of the issue on the backend, effectively adding mentioned users as members.
Resets comment state atoms and closes the sheet.
Blocking Issues (IssueBlockComment via issue-block-comment.tsx):
A specialized comment flow initiated when a user decides to “block” an issue (typically this action would be available on the IssueDetailTab or an options menu, though the trigger isn’t detailed in provided files).
The IssueBlockComment component is displayed in a Sheet controlled by issueBlockMessageSheetAtom.
User provides a reason for blocking, which is submitted as a comment.
Submission (handleCreateMessage):
Locally updates the selectedIssue.status in issueDetailsAtom to 'blocked' via setSelectedIssue.
Creates a comment message similar to the regular comment flow (including mentions and attachments) using useCreateIssueMessage().mutateAsync.
Calls useUpdateSingleIssue().mutateAsync (aliased as mutateAsyncMembers) to update the issue’s status to 'blocked' on the backend and set hasBlockedReason: true.
Resets block comment state atoms and closes the sheet.
Quick Resolve (quick-resolve-issue.tsx):
The QuickResolveButton is available on both “Details” and “Activity” tabs.
It’s enabled if allowEditStatus is true and selectedIssue.status !== 'resolved'.
Clicking it opens an AlertDialog for confirmation.
Confirmation (handleResolveIssue):
Calls useQuickResolveIssue().mutateAsync with issueID and resolvedBy (current user’s ID).
Locally updates selectedIssue.status in issueDetailsAtom to IssueStatusType.RESOLVED.
Closes the dialog and triggers an NPS survey (triggerNps).
Reactions (Displayed in IssueMessage, added via EmojiDropdown):
Selecting an emoji from the dropdown calls an onSelect(emojiValue) function. This function (likely defined in IssueMessage or passed to it) would then trigger a mutation (not explicitly detailed in provided issue-query.ts but would be necessary) to send the reaction update (add/remove for the current user and selected emoji) to the backend for that specific message. Firebase would then update other clients in real-time.
This section covers the functionalities for modifying existing issues, both individually and in bulk. These processes ensure data accuracy and reflect changes throughout the system by interacting with specific backend endpoints.
5.4.1 Individual Issue Editing
Users with appropriate permissions can edit the details of an existing issue. This process is typically initiated from the issue details screen, often by clicking an “Edit” button or interacting with an editable field, which then leads to a form pre-filled with the current issue’s information.
To pre-populate the form, the application first fetches the complete data for the specific issue using a query like useIssueQuery (from details/controllers/issue-query.ts). This fetched issue object (type IssueResponse) is then passed as the values prop when initializing the form via useEditIssueForm.
useEditIssueForm itself is a wrapper around react-hook-form’s useForm<UpdateIssueRequest>(). It’s configured with mode: 'onChange' and reValidateMode: 'onChange' for dynamic feedback as the user types. The existing issue data is passed to values in useForm to pre-fill the fields.
Client-Side Validation:
Notably, useEditIssueForm (in edit-issue-form-controller.ts) does not define a Yup validation schema resolver.
This implies that client-side validation for individual issue edits relies on:
Basic react-hook-form validations based on field types.
Any specific validation logic embedded within the individual UI input components used in the form.
The primary validation occurs on the backend upon submission. The frontend will display errors returned by the backend.
Submission Process:
When the user submits the form, the handleSubmit function (provided by useEditIssueScreenController) is invoked.
This function receives the data object from react-hook-form, which contains all fields from the form (changed or unchanged).
An UpdateIssueRequest payload is constructed. This typically includes the issueID and all editable fields.
A mutation hook, such as useUpdateSingleIssue (from packages/app/features/issues/details/controllers/issue-query.ts), is called with this payload.
API Interaction:
The useUpdateSingleIssue mutation calls issueUsecase.updateSingleIssue(payload).
The repository method makes a PUT request to the API endpoint /v1.0/issues/issues/update (as defined by issueEndpoint.UPDATE_SINGLE_ISSUE).
Side Effects: On successful update, useUpdateSingleIssue typically shows a success toast and may trigger a refetch of the issue data (e.g., by invalidating the relevant query in @tanstack/react-query) to update the UI. On error, it displays an error toast.
5.4.2 Individual Issue Editing Flowchart
graph TD
A[User on Issue Details Screen] --> B{Initiates Edit Action}
B --> C[Fetch Full Issue Data - useIssueQuery]
C --> D[Navigate to Edit Screen or Form]
D --> E[Initialize useEditIssueForm with fetched data as values]
E --> F[User Modifies Form Fields]
F --> G[User Submits Form]
G --> H[Client-side checks - react-hook-form]
H -- Passes --> I[Construct UpdateIssueRequest payload]
I --> J[Call useUpdateSingleIssue.mutateAsync with payload]
J --> K[Call issueUsecase.updateSingleIssue]
K --> L[Call issueRepository.updateSingleIssue]
L --> M[API PUT to endpoint /v1-0/issues/issues/update]
M -- Success --> N[Show Success Toast - Refetch Issue Data - Navigate Back or Update UI]
M -- Failure --> O[Show Error Toast]
H -- Fails (unlikely without schema) --> G
5.4.3 Bulk Issue Editing
Bulk editing allows users to modify multiple issues simultaneously, enhancing productivity for common changes like status or assignee updates.
Initiation and Interface:
Users select multiple issues from the main issue list (functionality within IssueList.tsx). The IDs of these selected issues are collected.
BulkEditList displays a message like Use the dropdown below to update each issue details for the selected {ids?.length} issue(s).
The UI presents a limited set of fields for modification, re-using components from the create/components/ directory:
Status.tsx
FlagSeverity.tsx (Severity)
DueDate.tsx
AssignedDepartment.tsx
AssignedUser.tsx
Priority.tsx
Form Management and Selected IDs:
BulkEditList utilizes useEditIssueScreenController for its form structure. This means it reuses the same form setup as individual issue editing.
Since no initial values are passed to useEditIssueScreenController in this context, the form fields start empty. Only fields that the user interacts with and assigns a value to will be part_of the submitted data.
Payload Construction and Submission (handleUpdateIssue in BulkEditList.tsx):
The handleSubmit(async (data: UpdateIssueRequest) => { ... }) function is triggered on form submission. Here, data (of type UpdateIssueRequest, though used for bulk context) will only contain values for fields the user explicitly changed.
A BulkEditIssuesRequest (defined in packages/app/features/issues/type.ts) payload is constructed:
data: An object containing the modified fields from the form, with triggerNotification: true explicitly added.
issueIDs: An array of strings, these are the ids retrieved via bulkEditIssueController.
This payload is then passed to mutateAsync from useUpdateIssueMutation.
Mutation Handling (useUpdateIssueMutation from edit-issue-mutation.ts):
mutationFn: (payload: BulkEditIssuesRequest) => issueUsecase.updateIssue(payload) defines the core API call.
onSuccess Behavior:
Navigates the user to the issues list (/home/issues) using route.push.
Cache Busting/Refetch: It includes key: Date() in the route query parameters. This is a technique to ensure the issue list refetches data. useIssueParams (and subsequently useAllIssueQuery which depends on its output) will see a change in query params, invalidating its current cache and triggering a fresh data load to reflect the bulk changes.
Displays a “Bulk Update - Issue’s updated successfully” toast message.
onError Behavior: (Standard for useMutation) Would typically display an error toast if the bulk update API call fails.
API Interaction:
The issueUsecase.updateIssue(payload) method is called.
The repository method makes a PUT request to the API endpoint /v1.0/issues/issues/bulkUpdate (defined by issueEndpoint.UPDATE_ISSUE).
This dual approach to editing provides flexibility, allowing users to make detailed changes to a single issue or apply sweeping changes across multiple issues efficiently.
5.4.4 Bulk Issue Editing Flowchart
graph TD
A[User selects multiple issues from list screen] --> B[Navigate to bulk edit screen with selected IDs]
B --> C[Render BulkEditList component]
C --> D[Retrieve selected issue IDs using useIssueIdsParams]
D --> E[Initialize useEditIssueScreenController with empty form]
E --> F[User modifies desired fields like status, priority, assignee]
F --> G[User submits form]
G --> H[Construct BulkEditIssuesRequest payload with changed fields and trigger notification]
H --> I[Call useUpdateIssueMutation.mutateAsync with payload]
I --> J[Call issueUsecase.updateIssue]
J --> K[Call issueRepository.updateIssue]
K --> L[Send API PUT request to bulk update endpoint]
L -- Success --> M[Show success toast and navigate to issue list with refreshed cache]
L -- Failure --> N[Show error toast]
5.5 Issue Review/Approval
This section details the workflow and components involved in the formal review and approval process for issues. This process is often used for quality assurance, final sign-off, or to ensure specific workflow steps are completed and validated by designated personnel.
5.5.1 Workflow Overview
The issue review/approval process provides a structured way to get formal agreement or validation on an issue’s status or resolution.
Initiation: A user (Requester) with appropriate permissions initiates a review request, typically from the issue details screen. This navigates them to the “Request for Approval” screen. This can also be done during new issue creation.
Request Configuration: The Requester selects an approval type (e.g., sequential, anyone), adds one or more Reviewers/Approvers (users or departments), includes comments, and optionally attaches files.
Submission: The request is submitted, and the issue’s status typically changes to “In Review.”
Notification & Reviewer Action: Reviewers are notified. They access the issue, review its details and the approval request specifics. They can then Approve or Reject the request, usually providing comments.
Status Updates & Completion: The issue’s and approval’s status are updated based on reviewer actions and the approval type logic (e.g., if all sequential approvers have approved).
Modification/Cancellation: Depending on permissions and current state, a request might be cancellable by the Requester, or reviewers might be added/removed.
5.5.2 Roles Involved
Requester: The user initiating the approval request.
Reviewer/Approver: User(s) or Department(s) designated to review and approve/reject the issue.
(System): Manages notifications, status updates, and workflow progression.
This controller orchestrates the logic for the IssueApprovalRequestPage.
Modes of Operation (via router.query.type):
type === 'new': This mode is used when an approval is being set up during the creation of a new issue. Instead of directly calling an API, handleSetApprovalData populates Jotai atoms:
type === 'edit': For modifying reviewers of an existing, active approval request. handleSubmit calls handleUpdateReviewers.
handleUpdateReviewers compares current selectedIssueReviewerIdsAtom with initial reviewers from issueApprovalDetailsAtom.
It calls useAddIssueReviewer().mutateAsync for added reviewers and useRemoveIssueReviewer().mutateAsync for removed ones.
Default Mode (No specific type or other values): For creating a new approval request for an existing issue. handleSubmit calls handleCreateApprovalRequest.
handleCreateApprovalRequest constructs a RequestIssueApprovalParams object using:
selectedIssue?.issueID (from issueDetailsAtom).
selectedIssueApprovalTypeAtom (Jotai atom for approval type).
selectedIssueReviewerIdsAtom (Jotai atom for selected reviewers).
issueRequestCommentAtom (Jotai atom for the comment).
issueRequestAttachmentsAtom (Jotai atom for attachment reference keys).
It then calls useRequestApprovalIssue().mutateAsync(payload).
On success, it calls useUpdateSingleIssueInvisible().mutateAsync to change the issue’s status to IssueStatusEnum.IN_REVIEW.
State Initialization: If router.query.type exists (e.g., ‘new’), it initializes its local state (which then updates Jotai atoms) from issueApprovalData.
Submit Logic (handleSubmit): Determines which handler to call based on router.query.type. The submit button is enabled if isSubmitAllowed is true (i.e., selectedIssueApprovalType and selectedIssueReviewerIds are populated).
isEditable: A flag (true if router.query.type !== 'edit') to control if form fields can be changed.
UI Components on IssueApprovalRequestPage:
ApprovalTypeSelector: Allows choosing the approval type (e.g., Sequential, Anyone).
IssueReviewerSelector: For selecting users and/or departments as reviewers.
IssueApprovalRequestCommentInput: A text input for adding notes or comments to the approval request, managed by issueRequestCommentAtom.
IssueReviewRequestAttachments: Component for managing attachments specific to the approval request, likely using a similar mechanism to issue attachments (uploading and collecting reference keys into issueRequestAttachmentsAtom).
Users via useGetIssueReviewerUsers (excluding the current user).
Departments via useOverAllDepartmentQuery.
Manages selectedUserIds (local state for user IDs) and selectedDepartmentIds (local state for department IDs).
Updates the global selectedIssueReviewerIdsAtom (app/shared/state/issues/issue-details.ts) which stores an array of user IDs (strings) or DepartmentReviewer objects ({ type: 'dept', departmentID: string }).
userOptions and departmentOptions are memoized lists for the Select components, filtered to exclude already selected reviewers.
handleAddUser/handleAddDepartment: Add individual selections to selectedReviewerIdsAtom.
handleRemoveUser: Removes a reviewer (user or department) from selectedReviewerIdsAtom.
UI: Uses Tamagui Select components that trigger SingleSelector bottom sheets for users and departments.
requestAction.handleClickRequest: Navigates to /issues/review-request to initiate a new approval request for the current issue.
requestAction.handleClickCancelRequest: Sets isIssueApprovalCancelModalVisibleAtom (Jotai atom) to true. A separate modal component then handles the actual cancellation, likely calling useCancelIssueApprovalRequest.
Sets approveRejectIssueStatusAtom (Jotai atom from details-screen-controller.ts) to IssueApprovalStatus.APPROVED or IssueApprovalStatus.REJECTED.
Sets reviewerDepartmentAtom (Jotai atom) if departmentID is provided (for department-based approvals).
Sets issueReviewMessageSheetAtom (Jotai atom) to true. This opens a sheet (likely IssueReviewCommentSheet or a similar component) where the reviewer can add a comment for their decision. This sheet, upon submission, will call the useApproveRejectIssue().mutateAsync mutation with the status, comment, and request ID.
Conditional Rendering of Buttons in IssueReviewButton:
“Request for Approval”: Shown if props.isLoading is false, !props.approvalRequest && !props.status (no existing request) or props.status !== IssueApprovalStatus.PENDING (existing request is not pending), AND the issue status is OPEN or IN_PROGRESS.
“Cancel Approval Request”: Shown if props.cancelRequestVisible is true (this flag is determined by backend logic, e.g., if the current user is the requester and no reviewer has acted yet).
“Approve” / “Reject”: Shown if currentApprover?.isCurrentApprover is true and currentApproverStatus === IssueApprovalStatus.PENDING. currentApprover is found by matching userID from authedUserIdAtom with the approvers list in the props.
requestIssueApproval(payload: RequestIssueApprovalParams): Submits the initial request for approval. Payload includes issueID, approvalType, approvers (array of user IDs or DepartmentReviewer objects), comments, and attachments (reference keys).
getIssueApprovalDetails(payload: GetIssueApprovalDetailsParams): Fetches the current status, history, and details (like approvers list, cancelRequestVisible flag) of an ongoing or completed approval request for a specific issue.
approveRejectIssue(payload: ApproveRejectIssueApprovalParams): Used by Reviewers to submit their decision (approve/reject status) along with any comments and the requestID.
cancelIssueApprovalRequest(payload: CancelIssueApprovalRequestParams): Allows the Requester (or an admin) to withdraw an approval request.
addIssueReviewer(payload: AddIssueReviewerParams): Allows modification of the reviewer list (adding new ones) for an active approval request. userID in params can be an array of user IDs or DepartmentReviewer objects.
removeIssueReviewer(payload: AddIssueReviewerParams): Allows removal of a reviewer from an active approval request.
This structured review process, integrating UI components, controllers, Jotai state, and backend API calls, ensures accountability and formal validation for critical issues.
5.5.8 Issue Approval Lifecycle Flowchart
graph TD
subgraph Requester_Actions
A[User on Issue Details or Issue Creation Screen] --> B{Initiate Approval}
B -- Yes --> C[Navigate to Review Request Screen]
C --> D[Render Issue Approval Request Page]
D --> E{Determine Mode}
E -- new request --> F[Set approval data in Jotai atoms]
F --> G_IssueCreate[Continue with issue creation using approval data]
E -- edit request --> H[Load reviewers from atom]
H --> I[User modifies reviewers or type]
I --> J_Update[Call handleUpdateReviewers]
J_Update --> K_AddRemove[Use add or remove reviewer usecases]
K_AddRemove --> L_API_Mod[Call API to update reviewers]
L_API_Mod --> M_Updated[Approval request updated]
M_Updated --> N_Back[Navigate back]
E -- default mode --> P[User sets type reviewers comment attachments]
P --> Q_Submit[Call handleCreateApprovalRequest]
Q_Submit --> R_ReqApp[Use requestIssueApproval usecase]
R_ReqApp --> S_API_NewReq[Call API to create approval request]
S_API_NewReq --> T[Update issue status to IN_REVIEW]
T --> N_Back
Details_Cancel[On Details Screen with Pending Request] --> U_Cancel{Cancel Request}
U_Cancel -- Yes --> V_Modal[Show confirmation modal]
V_Modal --> W_CancelReq[Call cancelIssueApprovalRequest usecase]
W_CancelReq --> X_API_Cancel[Call API to cancel request]
X_API_Cancel --> Y_StatusUpdate[Update issue and approval status]
end
subgraph Reviewer_Actions
Z[Reviewer views issue or gets notified] --> AA[Use getIssueApprovalDetails]
AA --> AB[Show approval status and reviewer actions]
AB --> AC{Approve or Reject}
AC -- Approve --> AD_Approve[Set status APPROVED in Jotai]
AD_Approve --> AE_CommentSheet[Show review comment input]
AC -- Reject --> AF_Reject[Set status REJECTED in Jotai]
AF_Reject --> AE_CommentSheet
AE_CommentSheet --> AG_SubmitComment[Submit comment and decision]
AG_SubmitComment --> AH_AppRej[Use approveRejectIssue usecase]
AH_AppRej --> AI_API_AppRej[Call API with decision]
AI_API_AppRej --> Y_StatusUpdate
end
subgraph System_Notifications
S_API_NewReq --> SN_NotifyInit[Notify reviewers - initial request]
AI_API_AppRej --> SN_NotifyUpdate[Notify requester and reviewers - decision]
Y_StatusUpdate --> SN_UIUpdate[Update UI with new status]
end
6. State Management
The Issue Tracker module employs a combination of libraries to manage different types of state effectively: server state (data fetched from APIs), client/UI state (local component or feature state), and form state (managing user input in forms). This separation of concerns leads to a more organized, performant, and maintainable codebase.
6.1 Server State and Caching (@tanstack/react-query)
@tanstack/react-query is the cornerstone for managing asynchronous server state. It excels at data fetching, caching, synchronization with server data, and handling mutations (updates, creations, deletions).
Rationale for Use: Ideal for server state due to its robust caching, automatic refetching, and declarative approach to data fetching and mutations, simplifying complex asynchronous logic.
Fetching Data (useQuery & useInfiniteQuery):
useQuery: Used for fetching data that doesn’t require pagination or has a known, finite dataset.
queryKey: Typically an array, e.g., ['issue', issueID], uniquely identifying the query.
queryFn: An async function that fetches data, e.g., () => issueUsecase.getIssueByID(issueID).
select: An optional function to transform or select a part of the data, e.g., (data) => data?.data?.data to extract the core issue object from the API response structure.
enabled: Boolean, can be used to conditionally enable/disable the query.
useInfiniteQuery: Used for lists of data that support pagination, enabling features like “infinite scrolling.”
queryKey: Includes parameters that might change the dataset, like filters or sort order, e.g., ['all-issues', params]. params would be an object containing filter criteria from useIssueParams.
queryFn: Fetches a page of data, e.g., ({ pageParam = 1 }) => issueUsecase.getAllIssue({ ...params, page: pageParam }).
initialPageParam: Sets the starting page number (e.g., 1).
getNextPageParam: A crucial function that determines how to get the parameters for the next page request, e.g., if the current page has data, it returns the next page number; otherwise, it returns undefined to signal the end of data.
Updating Server Data (useMutation):
Used for all Create, Update, and Delete (CUD) operations.
Navigation (e.g., route.back() using expo-router).
Query Invalidation: Crucial for ensuring data consistency. After creating an issue, queryClient.invalidateQueries({ predicate: (query) => query.queryKey.includes('all-issues') }) is called. This tells React Query that any active queries whose keys include ‘all-issues’ are now stale and should be refetched, ensuring the new issue appears in lists.
onError: Callback for handling errors, typically showing an error toast.
onSuccess: Shows a success toast. Often followed by query invalidation for the specific issue ID and potentially ‘all-issues’ list if relevant fields were changed.
onError: Shows an error toast.
Approval-Related Mutations (e.g., useRequestApprovalIssue, useApproveRejectIssue in issue-query.ts): These follow a similar pattern, calling specific usecases and providing user feedback via toasts in onSuccess or onError.
Query Invalidation Strategies:
After a mutation successfully completes (e.g., creating, updating, or deleting an issue, or changing its status through approval), relevant queries are invalidated to trigger a refetch.
This is commonly done using queryClient.invalidateQueries(). Invalidation can be by specific queryKey (e.g., ['issue', issueID]) or by a predicate matching multiple query keys (e.g., (query) => query.queryKey.includes('all-issues') to refresh all issue lists). This ensures that the UI reflects the latest server state.
6.2 Client/UI State (jotai)
jotai is used for managing client-side UI state that is ephemeral, local to a component tree, or needs to be shared across components without prop drilling, especially when the state isn’t directly derived from server data.
Rationale for Use: Jotai’s atomic model provides a simple, flexible, and performant way to manage React state. It’s less boilerplate than Redux for many common UI state scenarios and avoids unnecessary re-renders by allowing components to subscribe only to the atoms they care about.
issueDetailsAtom: Stores the full IssueResponse object for the issue being viewed. Populated after useIssueQuery successfully fetches data.
issueDetailsLoadingAtom: Boolean, indicates if the main issue details are currently loading.
issueMessageSheetAtom: Boolean, controls the visibility of the bottom sheet used for adding a new comment.
issueCommentAtom: String, holds the text content of the comment being drafted in the main comment sheet.
issueCommentAttachment, issueCommentAttachmentReferenceKeys, issueCommentAttachmentUpload: Manage attachments for the new comment being drafted (selected files, uploaded reference keys, upload status).
parentCommentDetails: Object ({ parentComment, parentCommentKey, parentCommentUserName }), stores context when replying to a specific comment, enabling the UI to show “Replying to X” and linking the new comment correctly.
issueBlockMessageSheetAtom, issueBlockCommentAtom, etc.: Similar set of atoms for managing the state of the “Block Issue” comment sheet.
issueReviewMessageSheetAtom, approveRejectIssueStatusAtom, reviewerDepartmentAtom, issueApprovalCommentAtom: Manage state for the review/approval comment sheet (visibility, selected action, comment text).
Issue Creation & Review Request (packages/app/features/issues/create-generic/controller/create-issue-controller.ts and app/shared/state/issues/issue-details.ts):
issueAttachmentUploadingAtom: Boolean, globally indicates if any issue attachments are currently being uploaded during issue creation. Shared across both contextual and generic creation.
issueApprovalDataAtom: Stores ApprovalData ({ approvalType, approvers, comments, attachments }) when an approval is configured as part of new issue creation.
isCreateApprovalAtom: Boolean, true if an approval request should be initiated when the new issue is created.
selectedIssueApprovalTypeAtom: Stores the chosen approval type (e.g., SEQUENTIAL, ANYONE) for a new review request.
selectedIssueReviewerIdsAtom: Array of strings/objects, stores the IDs/details of selected reviewers for a review request.
issueRequestCommentAtom: String, comment for a new review request.
issueRequestAttachmentsAtom: Array of strings, attachment reference keys for a new review request.
Usage in Components:
useAtom(atomName): Subscribes to an atom and gets both its value and a setter function.
useAtomValue(atomName): Subscribes to an atom and gets only its value (read-only).
useSetAtom(atomName): Gets only the setter function for an atom (write-only).
Components use these hooks to read UI state (e.g., to control sheet visibility with issueMessageSheetAtom) or to update it (e.g., updating issueCommentAtom as the user types in the comment input).
6.3 Form State (react-hook-form)
react-hook-form is the standard library for managing form state within the Issue Tracker, particularly for creating and editing issues. It optimizes form performance and simplifies form handling.
Rationale for Use:react-hook-form minimizes re-renders and provides a robust API for validation, submission, and form state management, making it efficient for complex forms.
useForm<CreateIssueRequest | CreateGenericIssueRequest>({...}) is called.
resolver: useYupValidation(validationSchema): Integrates Yup schemas for validation. The respective validationSchema (defined in the same file) specifies rules for each field (e.g., required, min/max length, date constraints).
values: issueCreationInitialvalue: Sets the default values for the form fields using a predefined initial object.
mode: 'onChange', reValidateMode: 'onChange': Configures validation to trigger on field changes for immediate user feedback.
values: value: Pre-fills the form with existing issue data passed as value when editing an individual issue.
No Yup resolver is specified here, meaning validation is more basic or handled at the component/backend level for individual edits. For bulk edits, since this hook is reused, only fields explicitly changed by the user get submitted, and validation relies on the backend.
handleSubmit:
The handleSubmit function returned by useForm is a wrapper. When called (e.g., onSubmit={handleSubmit(actualSubmitFunction)}), it first executes the configured validation (Yup + react-hook-form’s own).
If validation is successful, it calls the actualSubmitFunction (e.g., handleCreateIssue in screen components) with the validated form data. This function then typically prepares the payload and calls a React Query mutateAsync function.
By leveraging these three libraries, the Issue Tracker module achieves a robust and maintainable state management architecture. @tanstack/react-query handles complex server-side interactions, jotai manages dynamic UI states with precision, and react-hook-form ensures efficient and valid form submissions. This separation allows developers to manage different aspects of state with tools best suited for each job, contributing to overall code clarity and application performance.
7. Key Reusable UI Components
The Issue Tracker module leverages a variety of reusable UI components to maintain consistency and accelerate development. These components are primarily located within packages/app/features/issues/components/ and its subdirectories, promoting a modular and DRY (Don’t Repeat Yourself) approach to UI development.
7.1 List Components
Location: list/ (relative to packages/app/features/issues/components/)
Component Name
Location
Brief Description & Key Features
Common Usage
IssueCard.tsx
list/
Displays a concise summary of a single issue, including key fields like title, status, priority, and assignee.
Used in the main issue list and other list views (e.g., search results) to represent an issue.
IssueList.tsx
list/
Renders a list of IssueCard components, handles infinite scrolling logic, and displays loading/empty states.
The primary component for displaying lists of issues in various screens.
ListSearchBar.tsx
list/
Provides a search input field for filtering the issue list based on keywords. Includes clear button and debouncing.
Used at the top of the issue list screen.
7.2 Filter Components
Location: filters/ (relative to packages/app/features/issues/components/)
Component Name
Location
Brief Description & Key Features
Common Usage
defaultFilters.tsx
filters/
Displays a set of pre-defined filter toggles/buttons for quick filtering (e.g., “My Issues”, “Open Issues”).
On the issue list screen for quick, one-tap filtering.
filterByDate.tsx
filters/
Provides UI (e.g., date pickers) for selecting date ranges (created, due) for filtering issues.
In the advanced filter screen or dedicated date filter modals.
assignedUserFilter.tsx
filters/
Allows selection of one or more users from a list to filter issues by assignee.
In the advanced filter screen.
statusFilter.tsx
filters/
Allows selection of one or more issue statuses from a list for filtering.
In the advanced filter screen.
priorityFilter.tsx
filters/
Allows selection of one or more issue priorities from a list for filtering.
In the advanced filter screen.
multiSelector.tsx
filters/
A generic component for selecting multiple options from a list, often with search capability.
Base for various specific multi-select filters (status, priority, categories, etc.).
categorySelector.tsx
filters/
Component for selecting a single issue category.
In filter screens or forms where single category selection is needed.
categoryMultiSelector.tsx
filters/
Component for selecting multiple issue categories.
In filter screens or forms where multiple category selection is needed.
approvalStatusFilter.tsx
filters/
Allows selection of issue approval statuses for filtering.
In the advanced filter screen, for filtering by review/approval status.
filterOptionHeader.tsx
filters/
Standardized header for filter option sheets/screens.
At the top of the advanced filter screen or individual filter sheets.
filterSortBy.tsx
filters/
Component to select a field to sort the issue list by.
In the advanced filter screen or sort options menu.
flagSeverityFilter.tsx
filters/
Allows selection of issue severities (flags) for filtering.
In the advanced filter screen.
issueSource.tsx
filters/
Allows selection of issue sources (e.g., Audit, Manual) for filtering.
In the advanced filter screen.
questionnaireFilter.tsx
filters/
Allows selection of specific questionnaires to filter issues from.
In the advanced filter screen, for context-specific issues.
reporterDepartmentFilter.tsx
filters/
Allows selection of reporter departments for filtering.
In the advanced filter screen.
saveFilterAlertDialog.tsx
filters/
Dialog for confirming the save action for custom filter sets.
Used when saving a new set of applied filters.
savedFiltersList.tsx
filters/
Displays a list of previously saved custom filters for quick application.
In the advanced filter screen or a dedicated saved filters menu.
secondaryStatusFilter.tsx
filters/
Allows selection of secondary issue statuses (e.g., Overdue) for filtering.
In the advanced filter screen.
sitesFilter.tsx
filters/
Allows selection of one or more sites to filter issues by location.
In the advanced filter screen.
sortBySelection.tsx
filters/
UI for choosing sort order (Ascending/Descending) along with filterSortBy.
In the advanced filter screen or sort options menu.
surveyFilter.tsx
filters/
Allows selection of specific surveys to filter customer feedback issues.
In the advanced filter screen, for customer feedback issues.
7.3 Form Input & Creation/Editing Components
These components are primarily located within the create/components/ or create-generic/components/ subdirectories of features/issues/, or are generic selectors found in features/issues/components/selector/. They are listed here due to their conceptual reusability in forms, even if specific wrappers are in create/*.
Component Name
Location (approx.)
Brief Description & Key Features
Common Usage
(Various Selectors)
create/components/ or create-generic/components/
Components like priority.tsx, category.tsx, assigned-user.tsx, due-date.tsx are wrappers around base input/selector components.
Issue creation, editing, and bulk editing forms.
issue-attachment.tsx
create/components/ or create-generic/components/
Handles file selection, preview, and removal for attachments.
Issue creation and editing forms where attachments can be added.
single-selector.tsx
selector/
A generic base for single-item selection from a list, often in a sheet.
Foundation for specific selectors like priority, category, user, etc. in forms.
7.4 Detail View Components
These are primarily located within packages/app/features/issues/details/components/. Some may use generic components from features/issues/components/.
Component Name
Location
Brief Description & Key Features
Common Usage
issue-activity-tab.tsx
details/components/
Tab content for displaying issue history, comments, and system activities.
In the issue details screen, within the tabbed layout.
issue-comments.tsx
details/components/
Renders user comments and provides input for new comments (sheet).
Within the issue-activity-tab.tsx or directly in the details screen.
issue-block-comment.tsx
details/components/
Sheet component for inputting a reason when blocking an issue.
Used when changing an issue’s status to “blocked”.
issue-attachments.tsx
details/components/
Displays a list of attachments for an issue, with download/preview.
In the issue details screen, often in a dedicated “Attachments” tab or section.
issue-priority.tsx
issue-priority/ (shared) or details/components/
Displays the priority of the issue (e.g., with an icon and text).
Issue cards, issue details screen.
quick-resolve-issue.tsx
details/components/
Button with confirmation dialog to quickly resolve an issue.
On the issue details screen for quick actions.
emoji-dropdown.tsx
details/components/
Dropdown for selecting an emoji reaction for a message.
Used within issue messages/comments.
issue-reaction-list.tsx
details/components/
Displays a list of aggregated reactions on an issue message.
Used within issue messages/comments.
7.5 General Shared Components
Location: Root of packages/app/features/issues/components/ or specific subdirectories like header/.
Component Name
Location
Brief Description & Key Features
Common Usage
MultiChipField.tsx
./
Displays a list of selected items (e.g., users, categories) as “chips” with removal options.
In forms or display areas where multiple selections need to be shown clearly (e.g., reviewer selection).
bottomSheetEditIssue.tsx
./
A bottom sheet pre-configured for quick editing actions on an issue (e.g., change status, priority).
Triggered from the issue list or details for quick modifications.
moreSheet.tsx
./
A generic bottom sheet for displaying a list of additional actions or options.
Used in various contexts (e.g., on an IssueCard or issue details header) for overflow menu actions.
sheet-header.tsx
header/
Provides a standardized header for bottom sheets and modals, including title and close button.
Used across various bottom sheets and modals within the issue tracker for consistency.
issue-priority.tsx
issue-priority/
Displays issue priority with appropriate styling (icon, color, text).
Used in IssueCard.tsx, details/components/issue-priority.tsx, and potentially other places where priority is shown.
bulkEdit.tsx
./
Main component/screen layout for the bulk editing feature.
Wraps the form used for bulk editing selected issues.
This list is not exhaustive but highlights some of the most frequently reused components that form the building blocks of the Issue Tracker module’s user interface, promoting consistency and development efficiency.
8. Error Handling and System Resilience
Robust error handling and system resilience are crucial for providing a reliable user experience. The Issue Tracker module incorporates several strategies to manage errors gracefully and maintain functionality under various conditions, ensuring users are informed and data integrity is preserved.
8.1 Client-Side Error Handling
Client-side errors are addressed proactively to prevent invalid data submission and to provide immediate feedback.
Form Validation & Error Display:
As detailed in the “State Management” section, forms for issue creation and editing (e.g., in useCreateIssueForm from packages/app/features/issues/create/controller/use-create-issue-form.tsx) utilize react-hook-form along with Yup for schema-based validation (Note: zod was mentioned previously, but current file readings confirm Yup for issue creation forms).
Validation rules (required fields, data types, formats, character limits) are defined in Yup schemas.
When validation fails, react-hook-form prevents submission. The formState.errors object is populated, and errors are typically displayed as inline messages next to the respective form fields within the UI components, guiding the user to correct their input. This immediate feedback mechanism is standard practice with react-hook-form.
UI Feedback for Other Client-Side Issues:
Toast Notifications: The burnt library (via useToastController from @my/ui/src) is commonly used for displaying brief, non-intrusive toast messages for various client-side errors (e.g., “Invalid file type for attachment” during attachment validation in useUploadFileAttachment) or success confirmations.
Alert Dialogs: For critical actions requiring confirmation or more disruptive error messages, AlertDialog components (from @my/ui/src) are used, such as in the QuickResolveButton for confirming resolution.
Network Connectivity (Pre-API Call):
While direct usage of NetInfo from react-native wasn’t explicitly observed in the core controller logic reviewed for this documentation pass, it’s a common mobile development pattern to check network status before initiating critical API calls. This can prevent unnecessary requests when offline and allow the UI to respond appropriately (e.g., disabling buttons, showing an offline banner). If not implemented, this could be a future enhancement for resilience. The current structure relies on apisauce and @tanstack/react-query to handle network-related errors during the API call itself.
After each API call (e.g., createIssue, updateSingleIssue, getAllIssues), the response object (res) is checked for problems: if (res.problem) { ... }.
If res.problem is true (indicating network errors, non-2xx status codes, etc.), the repository methods typically throw new Error(res.data?.message || res.data?.error || 'An unexpected API error occurred');. This attempts to extract a meaningful error message from the backend response (res.data?.message or res.data?.error).
This pattern is consistent across POST/PUT methods. For GET requests like getAllIssues or getIssueByID, while the apiClient call itself might result in res.problem, the repository methods often return the res object directly. The error handling for these GET requests is then primarily managed by @tanstack/react-query in the custom hooks that call these repository methods (e.g., useAllIssueQuery, useIssueQuery).
Error Propagation to Mutation Hooks:
Errors thrown by the issueRepository are caught by the calling issueUsecase function, which typically re-throws them or returns a problematic response.
These errors then propagate to the onError callbacks of @tanstack/react-query’s useMutation hooks used throughout the feature controllers (e.g., in packages/app/features/issues/details/controllers/issue-query.ts or packages/app/features/issues/create/controller/create-issue-mutation.ts).
Displaying API Errors via Mutation Hooks:
onError Callbacks: Most useMutation hooks have an onError callback defined.
Example (useCreateIssueMutation):
onError: () => { toast.show('Failed', { burntOptions: { haptic: 'error', preset: 'error' }, message: 'Please provide a Due Date as it is required when the SLA is set to manual.', // This specific message might be too static. });},
In this case, a somewhat specific message is hardcoded. Ideally, the actual error message from the Error object (propagated from the repository) would be used here for more dynamic feedback.
Approval Mutations (e.g., useCancelIssueApprovalRequest, useAddIssueReviewer): These often extract e.message (the error message from the repository/usecase) to display in the toast, providing more specific feedback if the backend supplies it.
onError: (e) => { // e is the Error object console.error(e); toast.show(LL.issue.issues.issueReview.failed.cancelRequest(), { burntOptions: { haptic: 'error', preset: 'error' }, message: e.message, // Using the message from the thrown error });},
Most mutations use toast.show() within their onError handlers to inform the user about the failure. Haptic feedback (error) and error preset icons are common.
@tanstack/react-query Error States for Queries:
For data fetching hooks (useQuery, useInfiniteQuery), properties like isError (boolean) and error (error object) are available.
UI components use these flags to conditionally render error messages or alternative UI states (e.g., “Could not load issues. Please try again.”). This is evident in how IssueDetails/screen.tsx handles the loading state of useIssueQuery.
8.3 System Resilience and Data Integrity
Loading States & Timeouts:
@tanstack/react-query provides isLoading, isFetching (for useQuery) and isPending (for useMutation). These are used extensively to provide user feedback:
Showing spinners (<Spinner />) or loading indicators on screens or over lists while data is being fetched (e.g., in IssueDetails/screen.tsx).
apisauce (used by apiClient) has default timeout configurations. @tanstack/react-query also has its own timeout settings for queries. Specific overrides for the Issue Tracker module were not detailed in this review, but these defaults provide a baseline for handling unresponsive network requests.
Optimistic Updates & Rollbacks:
@tanstack/react-query supports optimistic updates. However, a review of the common mutation hooks (e.g., useCreateIssueMutation, useUpdateSingleIssue) did not reveal explicit implementations of optimistic updates (i.e., using onMutate to update local state before the API call and onError to roll back).
The current pattern relies on refetching/invalidating queries onSuccess to update the UI, which is a simpler and very common approach. While optimistic updates can improve perceived performance, they add complexity; their absence here suggests a preference for a more straightforward data synchronization model post-mutation.
Caching with Stale-While-Revalidate:
@tanstack/react-query’s default caching behavior (stale-while-revalidate) significantly enhances resilience. If data is in the cache, it’s shown immediately. A background refetch happens; if it fails (e.g., temporary network issue), the user still sees the cached (stale) data, making the app feel more responsive and available.
Offline Support (Speculative):
The presence of expo-sqlite and react-native-mmkv suggests capabilities for local data storage.
react-native-mmkv is used by issueRepository (via SecureStorage) for storing bulkEditSelectedIds, bulkEditReadIds, etc. This helps persist temporary user selections across sessions.
Full offline support for creating/editing issues with expo-sqlite would require a more complex architecture (local database, synchronization logic, conflict resolution) not detailed in the reviewed files. Currently, actions requiring server interaction would fail if offline, relying on the error handling mechanisms described above.
Data Consistency via Query Invalidation:
As mentioned, after successful mutations, queryClient.invalidateQueries() is a key mechanism.
For example, in useCreateIssueMutation, queryClient.invalidateQueries({ predicate: (query) => query.queryKey.includes('all-issues') }) ensures that any list displaying issues is refreshed.
In useUpdateIssueMutation (for bulk edits), navigation with a changing key: Date() in route params effectively serves as a cache-busting mechanism, prompting a refetch of the issue list on the target screen. This ensures that the UI consistently reflects the latest server state.
These strategies collectively aim to make the Issue Tracker module more robust, user-friendly in error scenarios, and resilient to common issues like network interruptions or temporary server problems.
9. Mermaid Diagrams
Mermaid diagrams illustrating key flows are embedded within their relevant sections under ‘Core Logic, Flows, and Components’. This includes a sequence diagram for Issue Listing & Filtering and a flowchart for Issue Creation.