Skip to content

[Due for payment 2026-07-13] [$250] Add First Approver and First Approved date columns #90507

Closed

Description

@twisterdotcom

Summary

Add First Approver and First Approved date columns to the reports columns to match what's available in the detailed export template and close the gap between in-product column configuration and exports.

This is part of a larger effort to add custom columns:

Expected Behavior

References to the template below are included here.

  • Users can display the First Approver column (person who approved the report): ${report.approvers[0].email}
  • Users can display the First Approved column (timestamp of first approval): ${report.approvers[0].date?date("yyyy-MM-dd")?string("yyyy-MM-dd")}

Context

Currently, ~29 columns are available in-product; the detailed export template has 67. Top customers have requested the ability to configure columns in the product that match their export templates to reduce downstream Excel manipulation.

Reference: Web-Expensify/lib/Integrations/defaultExportTemplates/detailed_export.ftl (expensify_detailed_export.txt) for context on available columns.

Performance Considerations

  • Verify no performance impact on accounts with large report histories
  • Monitor render times when displaying these columns alongside other data

Testing

  • Verify columns display correctly with various data states
  • Verify columns are configurable via the column selection menu
  • Test on accounts with large report histories for performance impact
Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~022054555736846849292
  • Upwork Job ID: 2054555736846849292
  • Last Price Increase: 2026-05-13
Issue OwnerCurrent Issue Owner: @mallenexpensify

Activity

  1. melvin-bot commented on May 13, 2026

    @melvin-bot

    Triggered auto assignment to Design team member for new feature review - @dubielzyk-expensify (NewFeature)

  2. twisterdotcom commented on May 13, 2026

    @twisterdotcom
    ContributorAuthor

    I don't think we need a design review here.

  3. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on May 13, 2026
  4. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on May 13, 2026
  5. KJ21-ENG commented on May 13, 2026

    @KJ21-ENG
    Contributor

    🚨 Edited by proposal-police: This proposal was edited at 2026-05-15 10:26:58 UTC.

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    The Reports table column picker does not offer the report-level First Approver or First Approved date columns, even though those fields are available in the detailed export template. Users can export those values, but cannot configure the in-product Reports table to show the same report approval metadata.

    What is the root cause of that problem?

    The Reports table column pipeline only models the existing approved date column. CONST.SEARCH.TYPE_CUSTOM_COLUMNS.EXPENSE_REPORT exposes APPROVED, but there are no FIRST_APPROVER / FIRST_APPROVED table column IDs in the column constants or the expense-report allowlist:

    App/src/CONST/index.ts

    Lines 7686 to 7704 in 9d65949

    EXPENSE_REPORT: {
    AVATAR: this.TABLE_COLUMNS.AVATAR,
    DATE: this.TABLE_COLUMNS.DATE,
    SUBMITTED: this.TABLE_COLUMNS.SUBMITTED,
    APPROVED: this.TABLE_COLUMNS.APPROVED,
    EXPORTED: this.TABLE_COLUMNS.EXPORTED,
    STATUS: this.TABLE_COLUMNS.STATUS,
    TITLE: this.TABLE_COLUMNS.TITLE,
    FROM: this.TABLE_COLUMNS.FROM,
    TO: this.TABLE_COLUMNS.TO,
    POLICY_NAME: this.TABLE_COLUMNS.POLICY_NAME,
    REIMBURSABLE_TOTAL: this.TABLE_COLUMNS.REIMBURSABLE_TOTAL,
    NON_REIMBURSABLE_TOTAL: this.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL,
    REPORT_ID: this.TABLE_COLUMNS.REPORT_ID,
    BASE_62_REPORT_ID: this.TABLE_COLUMNS.BASE_62_REPORT_ID,
    AMOUNT: this.TABLE_COLUMNS.TOTAL,
    EXPORTED_ICON: this.TABLE_COLUMNS.EXPORTED_TO,
    ACTION: this.TABLE_COLUMNS.ACTION,
    },

    The report-row normalizer already receives filtered report actions, and that filtered set includes APPROVED actions, but getReportSections() only formats requester/manager/status/exported fields into the row model. It never extracts the first APPROVED action's created timestamp or actorAccountID, so the row renderer has no value to display for the requested columns:

    const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, {
    selector: selectFilteredReportActions,
    });

    App/src/libs/ReportUtils.ts

    Lines 12957 to 12965 in 9d65949

    const filteredActions = actions.filter(
    (action): action is ReportAction =>
    isExportIntegrationAction(action) ||
    isIntegrationMessageAction(action) ||
    isDynamicExternalWorkflowSubmitFailedAction(action) ||
    isDynamicExternalWorkflowApproveFailedAction(action) ||
    isSubmittedAction(action) ||
    isApprovedAction(action),
    );

    App/src/libs/SearchUIUtils.ts

    Lines 2670 to 2752 in 9d65949

    for (const key of orderedKeys) {
    if (isReportEntry(key) && (data[key].type === CONST.REPORT.TYPE.IOU || data[key].type === CONST.REPORT.TYPE.EXPENSE || data[key].type === CONST.REPORT.TYPE.INVOICE)) {
    const reportItem = {...data[key]} as OnyxTypes.Report;
    const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${reportItem.reportID}`;
    const transactions = reportIDToTransactions[reportKey]?.transactions ?? [];
    const isIOUReport = reportItem.type === CONST.REPORT.TYPE.IOU;
    const actions = reportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportItem.reportID}`];
    let shouldShow = true;
    const isActionLoading = isActionLoadingSet?.has(`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${reportItem.reportID}`);
    if (currentQueryJSON && !isActionLoading) {
    if (currentQueryJSON.type === CONST.SEARCH.DATA_TYPES.EXPENSE) {
    const status = currentQueryJSON.status;
    if (Array.isArray(status)) {
    shouldShow = status.some((expenseStatus) => {
    return isValidExpenseStatus(expenseStatus) ? expenseStatusActionMapping[expenseStatus](reportItem) : false;
    });
    } else {
    shouldShow = isValidExpenseStatus(status) ? expenseStatusActionMapping[status](reportItem) : false;
    }
    }
    }
    if (shouldShow) {
    const reportPendingAction =
    reportItem?.pendingAction ??
    (reportItem?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE
    ? reportItem.pendingFields.preview
    : (reportItem?.pendingFields?.total ?? reportItem?.pendingFields?.preview));
    const shouldShowBlankTo = !reportItem || isOpenExpenseReport(reportItem);
    const reportMetadata = allReportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportItem.reportID}`] ?? {};
    const allReportTransactions = transactionsByReportID.get(reportItem.reportID) ?? [];
    const allActions = getActions(data, allViolations, key, currentSearch, currentUserEmail, currentAccountID, bankAccountList, reportMetadata, actions, allReportTransactions);
    const fromDetails =
    mergedPersonalDetails?.[reportItem.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID] ??
    getPersonalDetailsForAccountID(reportItem.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID) ??
    emptyPersonalDetails;
    const toDetails = !shouldShowBlankTo && reportItem.managerID ? mergedPersonalDetails?.[reportItem.managerID] : emptyPersonalDetails;
    const formattedFrom = formatPhoneNumber(getDisplayNameOrDefault(fromDetails));
    const formattedTo = !shouldShowBlankTo ? formatPhoneNumber(getDisplayNameOrDefault(toDetails)) : '';
    const formattedStatus = getReportStatusTranslation({stateNum: reportItem.stateNum, statusNum: reportItem.statusNum, translate});
    const policyFromKey = getPolicyFromKey(data, reportItem);
    const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem?.policyID ?? String(CONST.DEFAULT_NUMBER_ID)}`] ?? policyFromKey;
    const shouldShowStatusAsPending = !!isOffline && reportItem?.pendingFields?.nextStep === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE;
    const hasVisibleViolationsForReport = hasVisibleViolations(
    reportItem,
    allTransactionViolations ?? allViolations,
    currentUserEmail,
    currentAccountID ?? CONST.DEFAULT_NUMBER_ID,
    allReportTransactions,
    policy,
    );
    const {totalDisplaySpend, nonReimbursableSpend, reimbursableSpend} = getMoneyRequestSpendBreakdown(reportItem);
    const reportIsArchived = isArchivedReport(getReportNameValuePairsFromKey(data, reportItem));
    const avatarProps = getSearchReportAvatarProps(reportItem, formatPhoneNumber, mergedPersonalDetails, policy, reportIsArchived);
    const isRejectedReport =
    reportItem.stateNum === CONST.REPORT.STATE_NUM.OPEN &&
    reportItem.ownerAccountID === currentAccountID &&
    reportItem.nextStep?.messageKey === CONST.NEXT_STEP.MESSAGE_KEY.REJECTED_REPORT;
    const shouldHidePayAsPrimaryAction = hasOnlyNonReimbursableTransactions(reportItem.reportID, allReportTransactions);
    const primaryActionExclusions: SearchTransactionAction[] = shouldHidePayAsPrimaryAction ? [CONST.SEARCH.ACTION_TYPES.PAY] : [];
    reportIDToTransactions[reportKey] = {
    ...reportItem,
    action: getAction(allActions, primaryActionExclusions),
    allActions,
    keyForList: String(reportItem.reportID),
    groupedBy: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT,
    from: (fromDetails ?? emptyPersonalDetails) as OnyxTypes.PersonalDetails,
    to: (toDetails ?? emptyPersonalDetails) as OnyxTypes.PersonalDetails,
    exported: lastExportedActionByReportID.get(reportItem.reportID)?.created ?? '',
    formattedFrom,
    formattedTo,
    formattedStatus,

    Finally, the Reports header and wide row renderer only know how to display the existing APPROVED date from item.approved, so a newly selected first-approval column would currently have no header/cell implementation:

    const getExpenseReportHeaders = (profileIcon?: IconAsset): SearchColumnConfig[] => [
    {
    columnName: CONST.SEARCH.TABLE_COLUMNS.AVATAR,
    translationKey: undefined,
    icon: profileIcon,
    isColumnSortable: false,
    },
    {
    columnName: CONST.SEARCH.TABLE_COLUMNS.DATE,
    translationKey: 'common.date',
    },
    {
    columnName: CONST.SEARCH.TABLE_COLUMNS.SUBMITTED,
    translationKey: 'common.submitted',
    },
    {
    columnName: CONST.SEARCH.TABLE_COLUMNS.APPROVED,
    translationKey: 'search.filters.approved',
    },

    [CONST.SEARCH.TABLE_COLUMNS.APPROVED]: (
    <View style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.APPROVED, {isApprovedColumnWide: item.shouldShowYearApproved})]}>
    <DateCell
    date={item.approved ?? ''}
    showTooltip
    isLargeScreenWidth
    />
    </View>

    What changes do you think we should make in order to solve the problem?

    Add two report-level table columns, for example FIRST_APPROVER and FIRST_APPROVED, to CONST.SEARCH.TABLE_COLUMNS, TYPE_CUSTOM_COLUMNS.EXPENSE_REPORT, user-friendly values, and the search column translation mapping. Add corresponding labels such as common.firstApprover and common.firstApproved through the normal language files.

    In SearchUIUtils.getReportSections(), derive the first approval once per report from the already supplied filtered reportActions array: find the earliest CONST.REPORT.ACTIONS.TYPE.APPROVED action by created timestamp, using reportActionID as the same deterministic tie-breaker used by report-action ordering. Store firstApproved as that action's created value, and store firstApprover / formattedFirstApprover from the action's actorAccountID via the existing merged personal details lookup. Add those fields to ExpenseReportListItemType, but keep both FIRST_APPROVER and FIRST_APPROVED out of expenseReportColumnNamesToSortingProperty and mark both headers as non-sortable. These values are derived client-side from report actions, while clicking a sortable header writes sortBy:<column> into the search query and sends it to the backend Search API; since there is no backend sort support for sortBy:firstApproved or sortBy:firstApprover, exposing them as sortable would risk an unsupported sort path.

    In SearchTableHeader.getExpenseReportHeaders() add the two headers with isColumnSortable: false on both first-approval columns. In ExpenseReportListItemRowWide, render FIRST_APPROVED with DateCell and render FIRST_APPROVER with UserInfoCell, matching the existing SUBMITTED, APPROVED, FROM, and TO cell patterns. Add date-width/year handling for FIRST_APPROVED beside the existing approved-date year logic so old approval dates do not truncate.

    Scope boundaries:

    • Files touched (with call sites): src/CONST/index.ts — TABLE_COLUMNS, TYPE_CUSTOM_COLUMNS.EXPENSE_REPORT, user-friendly column values; src/libs/SearchUIUtils.ts — nonSortableColumns, getReportSections(), getSearchColumnTranslationKey(), year/width helpers; src/components/Search/SearchTableHeader.tsx — getExpenseReportHeaders(); src/components/Search/SearchList/ListItem/ExpenseReportListItemRow/ExpenseReportListItemRowWide.tsx — columnComponents; src/components/Search/SearchList/ListItem/types.ts — ExpenseReportListItemType; language files for the two labels.
    • Platforms: all platforms that use the shared Search Reports table; wide/table layout displays the new cells, narrow layout remains unchanged like the other configurable table-only columns.
    • Code paths / callers explicitly NOT touched: detailed export generation in Web-Expensify, approval workflow configuration, report-details transaction columns, and backend search/API contracts.
    • API contracts or backend behavior changes: no; this uses REPORT_ACTIONS data already subscribed by Search and already filtered to include approved actions.

    What alternative solutions did you explore? (Optional)

    Using report.approved for the new First Approved column would be simpler, but it would only duplicate the existing Approved column and would not identify the approver. The export template's first-approval fields come from the first approver entry, so deriving both values from the first APPROVED report action keeps the in-product column aligned with the report approval event rather than adding another alias for the current approved date.

    No human or bot proposals existed at draft time, so there was no duplicate technical claim to address.

  6. melvin-bot commented on May 13, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @DylanDylann (External)

  7. changed the title [-]Add First Approver and First Approved date columns[/-] [+][$250] Add First Approver and First Approved date columns[/+] on May 13, 2026
  8. melvin-bot commented on May 13, 2026

    @melvin-bot
  9. aswin-s commented on May 13, 2026

    @aswin-s
    Contributor

    Please re-state the problem that we are trying to solve in this issue.

    Currently, the in-product report column configuration menu displays ~29 columns, while the detailed export template includes 67 columns. Users cannot select First Approver (the email of the person who approved the report) or First Approved date (the timestamp of first approval) columns in the Reports view, even though these fields are available in the detailed export template. This forces users to perform downstream Excel manipulation to access these common reporting fields.

    What is the current gap in the implementation?

    The First Approver and First Approved date columns are missing from the report column configuration system. The CONST.SEARCH.TYPE_CUSTOM_COLUMNS structure in src/CONST/index.ts (lines 7686–7704) defines the canonical list of selectable columns for the EXPENSE_REPORT data type. The EXPENSE_REPORT block registers available columns by referencing TABLE_COLUMNS keys, but lacks entries for the requested fields. To make these columns functional, they must be: (1) registered as new TABLE_COLUMNS keys in src/CONST/index.ts, (2) added to TYPE_CUSTOM_COLUMNS.EXPENSE_REPORT, (3) mapped in getSearchColumnTranslationKey() in src/libs/SearchUIUtils.ts to enable locale lookup, (4) included in getExpenseReportHeaders() in src/components/Search/SearchTableHeader.tsx to render in the table, and (5) given translatable strings in src/languages/en.ts and src/languages/es.ts.

    The working pattern is visible in the existing column registrations:

    App/src/CONST/index.ts

    Lines 7686 to 7704 in 9d65949

    EXPENSE_REPORT: {
    AVATAR: this.TABLE_COLUMNS.AVATAR,
    DATE: this.TABLE_COLUMNS.DATE,
    SUBMITTED: this.TABLE_COLUMNS.SUBMITTED,
    APPROVED: this.TABLE_COLUMNS.APPROVED,
    EXPORTED: this.TABLE_COLUMNS.EXPORTED,
    STATUS: this.TABLE_COLUMNS.STATUS,
    TITLE: this.TABLE_COLUMNS.TITLE,
    FROM: this.TABLE_COLUMNS.FROM,
    TO: this.TABLE_COLUMNS.TO,
    POLICY_NAME: this.TABLE_COLUMNS.POLICY_NAME,
    REIMBURSABLE_TOTAL: this.TABLE_COLUMNS.REIMBURSABLE_TOTAL,
    NON_REIMBURSABLE_TOTAL: this.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL,
    REPORT_ID: this.TABLE_COLUMNS.REPORT_ID,
    BASE_62_REPORT_ID: this.TABLE_COLUMNS.BASE_62_REPORT_ID,
    AMOUNT: this.TABLE_COLUMNS.TOTAL,
    EXPORTED_ICON: this.TABLE_COLUMNS.EXPORTED_TO,
    ACTION: this.TABLE_COLUMNS.ACTION,
    },

    What changes do you think we should make in order to implement this feature?

    1. File: src/CONST/index.ts
      Property: TABLE_COLUMNS
      Description: Add two new column key definitions alongside existing column definitions:

      FIRST_APPROVER: 'firstApprover',
      FIRST_APPROVED_DATE: 'firstApprovedDate',
    2. File: src/CONST/index.ts
      Property: TYPE_CUSTOM_COLUMNS.EXPENSE_REPORT
      Description: Register the new columns in the EXPENSE_REPORT block:

      FIRST_APPROVER: this.TABLE_COLUMNS.FIRST_APPROVER,
      FIRST_APPROVED_DATE: this.TABLE_COLUMNS.FIRST_APPROVED_DATE,
    3. File: src/libs/SearchUIUtils.ts
      Function: getSearchColumnTranslationKey
      Description: Add cases for the new columns that return the corresponding translation keys:

      case CONST.SEARCH.TABLE_COLUMNS.FIRST_APPROVER:
          return 'common.firstApprover';
      case CONST.SEARCH.TABLE_COLUMNS.FIRST_APPROVED_DATE:
          return 'common.firstApprovedDate';
    4. File: src/components/Search/SearchTableHeader.tsx
      Function: getExpenseReportHeaders
      Description: Add entries in the headers array for the new columns. Both columns source their data from report.approvers, which is an array that may be empty or contain entries with missing fields. The rendering logic must guard against this:

      {
          key: CONST.SEARCH.TABLE_COLUMNS.FIRST_APPROVER,
          // report.approvers may be empty; only render if the first entry exists and has an email
          render: (report) => {
              const firstApprover = report.approvers?.[0];
              return firstApprover?.email ?? null;
          },
      },
      {
          key: CONST.SEARCH.TABLE_COLUMNS.FIRST_APPROVED_DATE,
          // report.approvers may be empty; only render if the first entry exists and has a date
          render: (report) => {
              const firstApprover = report.approvers?.[0];
              return firstApprover?.date ?? null;
          },
      },

      The columns intentionally surface only approvers[0] — the first approver — to match the detailed export template field ${report.approvers[0].email}. Reports with multiple approvers retain all approver data in the underlying structure; only the first approver is displayed in these columns, consistent with the export template behavior. Returning null when the array is empty or the field is absent keeps the cell blank rather than crashing.

      Platform rendering: SearchTableHeader.tsx is shared across web and native (iOS/Android). No platform-specific branching is required for the column definitions themselves; the existing header rendering infrastructure already handles responsive column visibility. Verify that the column is included in the shouldShowColumn guard (if one exists in this file) for the native narrowed layout, so the columns are hidden on small screens just as other optional columns are.

    5. File: src/languages/en.ts
      Description: Add translation strings under the appropriate section:

      firstApprover: 'First Approver',
      firstApprovedDate: 'First Approved Date',
    6. File: src/languages/es.ts
      Description: Add Spanish translations for the same keys (Spanish locale strings are required for all user-visible text):

      firstApprover: 'Primer aprobador',
      firstApprovedDate: 'Fecha de primera aprobación',

    Performance note: These columns read report.approvers[0] — a field already present on the report object fetched for the Search view. No additional network requests or Onyx subscriptions are introduced. For accounts with large report histories the column data is loaded as part of the existing report list query; no extra caching layer is needed. If profiling reveals render-time regressions when both columns are enabled simultaneously, the render callbacks above can be memoized at the call site, but no pre-emptive memoization is added here.

    What alternative approaches did you explore?

    N/A

  10. 83 remaining items

  11. melvin-bot commented on Jul 12, 2026

    @melvin-bot

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  12. melvin-bot commented on Jul 12, 2026

    @melvin-bot

    Payment Summary

    Upwork Job

    • Reviewer: @mukhrr owed $250 via NewDot
    • Contributor: @c3024 does not require payment (Contractor)
    • Reviewer: @DylanDylann owed $250 via NewDot

    BugZero Checklist (@mallenexpensify)

    • I have verified the correct assignees and roles are listed above and updated the necessary manual offers
    • I have verified that there are no duplicate or incorrect contracts on Upwork for this job (https://www.upwork.com/ab/applicants/2054555736846849292/hired)
    • I have verified the PR was not reverted
    • I have applied any discounts due to bugs/regressions introduced by this PR
    • I have paid out the Upwork contracts or cancelled the ones that are incorrect
    • I have verified the payment summary above is correct
  13. KJ21-ENG commented on Jul 12, 2026

    @KJ21-ENG
    Contributor

    Seems like payment summary has switched roles (Incorrectly) 👀😅

    cc: @mallenexpensify

  14. mallenexpensify commented on Jul 13, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor: @mukhrr due $375 via NewDot
    Contributor+: @DylanDylann due $375 via NewDot

    ^ Test case created. Thx

    Seems like payment summary has switched roles (Incorrectly) 👀😅

    @KJ21-ENG melv often does mixed up, I think it'll be fixed soon. Thx

  15. KJ21-ENG commented on Jul 13, 2026

    @KJ21-ENG
    Contributor

    NW! 👍🏻

  16. mukhrr commented on Jul 14, 2026

    @mukhrr
    Contributor

    @mallenexpensify thanks! what about this request on price increase here? #90507 (comment) there was an improvement besides orignal bug fix. cc @twisterdotcom @grgia

  17. DylanDylann commented on Jul 14, 2026

    @DylanDylann
    Contributor

    I agree that we expanded the scope of the original issue to make the styling consistent. If a bonus bounty is issued, I think $125 makes sense, bringing the total to $375.

  18. twisterdotcom commented on Jul 14, 2026

    @twisterdotcom
    ContributorAuthor

    $375 seems adequate to me. Have you both already requested this? Or can I update the payment summary and you update your requested amounts?

  19. mukhrr commented on Jul 14, 2026

    @mukhrr
    Contributor

    THanks. I requested with $375 in ND

  20. twisterdotcom commented on Jul 14, 2026

    @twisterdotcom
    ContributorAuthor

    I updated the Payment Summary above.

  21. quinthar commented on Jul 31, 2026

    @quinthar
    Contributor

    Approved $375 to @DylanDylann

  22. flaviadefaria commented on Jul 31, 2026

    @flaviadefaria
    Contributor

    Approved: @mukhrr paid $375 via NewDot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionDailyKSv2ExternalAdded to denote the issue can be worked on by a contributorNewFeatureSomething to build that is a new item.

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions