diff --git a/.github/actions/javascript/bumpVersion/index.js b/.github/actions/javascript/bumpVersion/index.js index da08d1a060b6..830dbf626548 100644 --- a/.github/actions/javascript/bumpVersion/index.js +++ b/.github/actions/javascript/bumpVersion/index.js @@ -298,9 +298,6 @@ function getPreviousVersion(currentVersion, level) { if (patch === 0) { return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.MINOR); } - if (major === 1 && minor === 3 && patch === 83) { - return getVersionStringFromNumber(major, minor, patch - 2, 0); - } return getVersionStringFromNumber(major, minor, patch - 1, 0); } diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index 22ad59ed9588..561b8e61bc21 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -998,9 +998,6 @@ function getPreviousVersion(currentVersion, level) { if (patch === 0) { return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.MINOR); } - if (major === 1 && minor === 3 && patch === 83) { - return getVersionStringFromNumber(major, minor, patch - 2, 0); - } return getVersionStringFromNumber(major, minor, patch - 1, 0); } diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 3aafda798c54..e42f97508bc5 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -961,9 +961,6 @@ function getPreviousVersion(currentVersion, level) { if (patch === 0) { return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.MINOR); } - if (major === 1 && minor === 3 && patch === 83) { - return getVersionStringFromNumber(major, minor, patch - 2, 0); - } return getVersionStringFromNumber(major, minor, patch - 1, 0); } diff --git a/.github/actions/javascript/getPreviousVersion/index.js b/.github/actions/javascript/getPreviousVersion/index.js index 6770ba99ba69..37db08db93e9 100644 --- a/.github/actions/javascript/getPreviousVersion/index.js +++ b/.github/actions/javascript/getPreviousVersion/index.js @@ -148,9 +148,6 @@ function getPreviousVersion(currentVersion, level) { if (patch === 0) { return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.MINOR); } - if (major === 1 && minor === 3 && patch === 83) { - return getVersionStringFromNumber(major, minor, patch - 2, 0); - } return getVersionStringFromNumber(major, minor, patch - 1, 0); } diff --git a/.github/libs/versionUpdater.js b/.github/libs/versionUpdater.js index b78178f443e6..78e8085621bd 100644 --- a/.github/libs/versionUpdater.js +++ b/.github/libs/versionUpdater.js @@ -118,9 +118,6 @@ function getPreviousVersion(currentVersion, level) { if (patch === 0) { return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.MINOR); } - if (major === 1 && minor === 3 && patch === 83) { - return getVersionStringFromNumber(major, minor, patch - 2, 0); - } return getVersionStringFromNumber(major, minor, patch - 1, 0); } diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml index f5a5dc5e1616..1105f78da27a 100644 --- a/.github/workflows/platformDeploy.yml +++ b/.github/workflows/platformDeploy.yml @@ -104,6 +104,13 @@ jobs: name: android-sourcemap path: android/app/build/generated/sourcemaps/react/release/*.map + - name: Upload Android version to GitHub artifacts + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + uses: actions/upload-artifact@v3 + with: + name: app-production-release.aab + path: android/app/build/outputs/bundle/productionRelease/app-production-release.aab + - name: Upload Android version to Browser Stack if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} run: curl -u "$BROWSERSTACK" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@./android/app/build/outputs/bundle/productionRelease/app-production-release.aab" @@ -238,6 +245,13 @@ jobs: name: ios-sourcemap path: main.jsbundle.map + - name: Upload iOS version to GitHub artifacts + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + uses: actions/upload-artifact@v3 + with: + name: New Expensify.ipa + path: /Users/runner/work/App/App/New Expensify.ipa + - name: Upload iOS version to Browser Stack if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} run: curl -u "$BROWSERSTACK" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@/Users/runner/work/App/App/New Expensify.ipa" diff --git a/android/app/build.gradle b/android/app/build.gradle index ddffb8acbe09..1b8eac0c5c20 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -90,8 +90,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001038407 - versionName "1.3.84-7" + versionCode 1001038500 + versionName "1.3.85-0" } flavorDimensions "default" diff --git a/docs/articles/expensify-classic/expense-and-report-features/Expense-Rules.md b/docs/articles/expensify-classic/expense-and-report-features/Expense-Rules.md index 304c93d1da6d..ae6a9ca77db1 100644 --- a/docs/articles/expensify-classic/expense-and-report-features/Expense-Rules.md +++ b/docs/articles/expensify-classic/expense-and-report-features/Expense-Rules.md @@ -1,5 +1,55 @@ --- title: Expense Rules -description: Expense Rules +description: Expense rules allow you to automatically categorize, tag, and report expenses based on the merchant's name. + --- -## Resource Coming Soon! +# Overview +Expense rules allow you to automatically categorize, tag, and report expenses based on the merchant’s name. + +# How to use Expense Rules +**To create an expense rule, follow these steps:** +1. Navigate to **Settings > Account > Expense Rules** +2. Click on **New Rule** +3. Fill in the required information to set up your rule + +When creating an expense rule, you will be able to apply the following rules to expenses: + +![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_01.png){:width="100%"} + +- **Merchant:** Updates the merchant name, e.g., “Starbucks #238” could be changed to “Starbucks” +- **Category:** Applies a workspace category to the expense +- **Tag:** Applies a tag to the expense, e.g., a Department or Location +- **Description:** Adds a description to the description field on the expense +- **Reimbursability:** Determines whether the expense will be marked as reimbursable or non-reimbursable +- **Billable**: Determines whether the expense is billable +- **Add to a report named:** Adds the expense to a report with the name you type into the field. If no report with that name exists, a new report will be created + +## Tips on using Expense Rules +- If you'd like to apply a rule to all expenses (“Universal Rule”) rather than just one merchant, simply enter a period [.] and nothing else into the **“When the merchant name contains:”** field. **Note:** Universal Rules will always take precedence over all other rules for category (more on this below). +- You can apply a rule to previously entered expenses by checking the **Apply to existing matching expenses** checkbox. Click “Preview Matching Expenses” to see if your rule matches the intended expenses. +- You can create expense rules while editing an expense. To do this, simply check the box **“Create a rule based on your changes"** at the time of editing. Note that the expense must be saved, reopened, and edited for this option to appear. + + +![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_02.png){:width="100%"} + + +To delete an expense rule, go to **Settings > Account > Expense Rules**, scroll down to the rule you’d like to remove, and then click the trash can icon in the upper right corner of the rule: + +![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_03.png){:width="100%"} + +# Deep Dive +In general, your expense rules will be applied in order, from **top to bottom**, i.e., from the first rule. However, other settings can impact how expense rules are applied. Here is the hierarchy that determines how these are applied: +1. A Universal Rule will **always** precede over any other expense category rules. Rules that would otherwise change the expense category will **not** override the Universal Rule. +2. If Scheduled Submit and the setting “Enforce Default Report Title” are enabled on the workspace, this will take precedence over any rules trying to add the expense to a report. +3. If the expense is from a Company Card that is forced to a workspace with strict rule enforcement, those rules will take precedence over individual expense rules. +4. If you belong to a workspace that is tied to an accounting integration, the configuration settings for this connection may update your expense details upon export, even if the expense rules were successfully applied to the expense. + + +# FAQ +## How can I use Expense Rules to vendor match when exporting to an accounting package? +When exporting non-reimbursable expenses to your connected accounting package, the payee field will list "Credit Card Misc." if the merchant name on the expense in Expensify is not an exact match to a vendor in the accounting package. +When an exact match is unavailable, "Credit Card Misc." prevents multiple variations of the same vendor (e.g., Starbucks and Starbucks #1234, as is often seen in credit card statements) from being created in your accounting package. +For repeated expenses, the best practice is to use Expense Rules, which will automatically update the merchant name without having to do it manually each time. +This only works for connections to QuickBooks Online, Desktop, and Xero. Vendor matching cannot be performed in this manner for NetSuite or Sage Intacct due to limitations in the API of the accounting package. + + diff --git a/docs/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct.md b/docs/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct.md index 3ee1c8656b4b..ac0a90ba6d37 100644 --- a/docs/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct.md +++ b/docs/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct.md @@ -1,5 +1,568 @@ --- -title: Coming Soon -description: Coming Soon +title: Sage Intacct +description: Connect your Expensify workspace with Sage Intacct --- -## Resource Coming Soon! +# Overview +Expensify’s seamless integration with Sage Intacct allows you to connect using either Role-based permissions or User-based permissions. + +Once connected to Intacct you’re able to automate report exports, customize your coding preferences, and utilize Sage Intacct’s advanced features. When you’ve configured these settings in Expensify correctly, you can use the integration's settings to automate many tasks, streamlining your workflow for increased efficiency. + +# How to connect to Sage Intacct +We support setting up Sage Intacct with both User-based permissions and Role-based permissions for Expense Reports and Vendor Bills. +- User-based Permissions - Expense Reports +- User-based Permissions - Vendor Bills +- Role-based Permissions - Expense Reports +- Role-based Permissions - Vendor Bills + + +## User-based Permissions - Expense Reports + +Please follow these steps if exporting as Expense Reports with **user-based permissions**. + + +### Checklist of items to complete: +1. Create a web services user and set up permissions. +2. Enable the Time & Expenses module **(Required if exporting as Expense Reports)**. +3. Set up Employees in Sage Intacct **(Required if exporting as Expense Reports)**. +4. Set up Expense Types in Sage Intacct **(Required if exporting as Expense Reports)**. +5. Enable Customization Services (only applicable if you don't already use Platform Services). +6. Create a test workspace and download the [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage). +7. Upload the Package in Sage Intacct. +8. Add web services authorization. +9. Enter credentials and connect Expensify and Sage Intacct. +10. Configure integration sync options. +11. Export a test report. +12. Connect Sage Intacct to the production workspace. + + +### Step 1: Create a web services user with user-based permissions + +_Note: If the steps in this section look different in your Sage Intacct instance, you likely use role-based permissions. If that's the case, see the steps below on creating a web services user for role-based permissions._ +To connect to Sage Intacct, you'll need to create a special web services user. This user is essential for tracking actions in Sage Intacct, such as exporting expense reports and credit card charges from Expensify. It also helps ensure smooth operations when new members join or leave your accounting team. The good news is that setting up this web services user won't cost you anything. Just follow these steps: +Go to **Company > Web Services Users > New** +Setup the user using these configurations: + - **User ID:** "xmlgateway_expensify" + - **Last Name and First Name:** "Expensify" + - **Email Address:** Your shared accounting team email + - **User Type:** "Business" + - **Admin Privileges:** "Full" + - **Status:** "Active" +Once you've created the user, you'll need to set the correct permissions. To set those, go to the **subscription** link for this user in the user list, **click on the checkbox** next to the Application/Module and then click on the **Permissions** link to modify those. + +These are the permissions required for a user to export reimbursable expenses as Expense Reports: +- **Administration (All)** +- **Company (Read-only)** +- **Cash Management (All)** +- **General Ledger (All)** +- **Time & Expense (All)** +- **Projects (Read-only)** (only needed if using Projects and Customers) +- **Accounts Payable (All)** (only needed for exporting non-reimbursable expenses as vendor bills) + +**Note:** you can set permissions for each Application/Module by selecting the radio button next to the desired Permission and clicking **Save**. + + +### Step 2: Enable the Time & Expenses Module (Only required if exporting reimbursable expenses as Expense Reports) +The Time & Expenses (T&E) module is often included in your Sage Intacct instance, but if it wasn't part of your initial Sage Intacct setup, you may need to enable it. **Enabling the T&E module is a paid subscription through Sage Intacct. For information on the costs of enabling this module, please contact your Sage Intacct account manager**. It's necessary for our integration and only takes a few minutes to configure. +1. In Sage Intacct, go to the **Company menu > Subscriptions > Time & Expenses** and toggle the switch to subscribe. +2. After enabling T&E, configure it as follows: + - Ensure that **Expense types** is checked: + - Under **Auto-numbering sequences** set the following: + - **Expense Report:** EXP + - **Employee:** EMP + - **Duplicate Numbers:** Select “Do not allow creation” + + - To create the EXP sequence, **click on the down arrow on the expense report line and select **Add**: + - **Sequence ID:** EXP + - **Print Title:** EXPENSE REPORT + - **Starting Number:** 1 + - **Next Number:** 2 +3. Select **Advanced Settings** and configure the following: +- **Fixed Number Length:** 4 +- **Fixed Prefix:** EXP +4. Click **Save** +5. Under Expense Report approval settings, ensure that **Enable expense report approval** is unchecked +6. Click **Save** to confirm your configurations. + + +### Step 3: Set up Employees in Sage Intacct (Only required if exporting reimbursable expenses as Expense Reports) +To set up Employees in Sage Intacct, follow these steps: +1. Navigate to **Time & Expenses** and click the plus button next to **Employees**. + - If you don't see the Time & Expense option in the top ribbon, you may need to adjust your settings. Go to **Company > Roles > Time & Expenses** and enable all permissions. +2. To create an employee, you'll need to provide the following information: + - **Employee ID** + - **Primary contact name** + - **Email address** + - In the **Primary contact name** field, click the dropdown arrow. + - Select the employee if they've already been created. + - Otherwise, click **+ Add** to create a new employee. + - Fill in their **Primary Email Address** along with any other required information. + + +### Step 4: Set up Expense Types in Sage Intacct (Only required if exporting reimbursable expenses as Expense Reports) + +Expense Types provide a user-friendly way to display the names of your expense accounts to your employees. They are essential for our integration. To set up Expense Types, follow these steps: +1. **Setup Your Chart of Accounts:** Before configuring Expense Types, ensure your Chart of Accounts is set up. You can set up accounts in bulk by going to **Company > Open Setup > Company Setup Checklist > click Import**. +2. **Set up Expense Types:** + - Go to **Time & Expense**. + - Open Setup and click the plus button next to **Expense Types**. +3. For each Expense Type, provide the following information: + - **Expense Type** + - **Description** + - **Account Number** (from your General Ledger) +This step is necessary if you are exporting reimbursable expenses as Expense Reports. + + +### Step 5: Enable Customization Services +To enable Customization Services go to **Company > Subscriptions > Customization Services**. + - If you already have Platform Services enabled, you can skip this step. + + +### Step 6: Create a Test Workspace in Expensify and Download the [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage) +Creating a test workspace in Expensify allows you to have a sandbox environment for testing before implementing the integration live. If you are already using Expensify, creating a test workspace ensures that your existing group workspace rules and approval workflows remain intact. Here's how to set it up: +1. Go to **expensify.com > Settings > Workspaces > New Workspace**. +2. Name the workspace something like "Sage Intacct Test Workspace." +3. Go to **Connections > Sage Intacct > Connect to Sage Intacct**. +4. Select **Download Package** (You only need to download the file; we'll upload it from your Downloads folder later). + + +### Step 7: Upload Package in Sage Intacct + + +If you use **Customization Services**: +1. Go to **Customization Services > Custom Packages > New Package**. +2. Click on **Choose File** and select the Package file from your downloads folder. +3. Click **Import**. + + +If you use **Platform Services**: +1. Go to **Platform Services > Custom Packages > New Package**. +2. Click on **Choose File** and select the Package file from your downloads folder. +3. Click **Import**. + + +### Step 8: Add Web Services Authorization +1. Go to **Company > Company Info > Security** in Intacct and click **Edit**. +2. Scroll down to **Web Services Authorizations** and add "expensify" (all lower case) as a Sender ID. + + +### Step 9: Enter Credentials and Connect Expensify and Sage Intacct + + +1. Go back to **Settings > Workspaces > Group > [Workspace Name] > Connections > Configure**. +2. Click **Connect to Sage Intacct** and enter the credentials you've set for your web services user. +3. Click **Send** once you're done. + +Next, you’ll configure the Export, Coding, and Advanced tabs of the connection configuration in Expensify. + + +## User-based Permissions - Vendor Bills +In this setup guide, we'll take you through the steps to establish your connection for Vendor Bills with user-based permissions. Please follow this checklist of items to complete: +1. Create a web services user and set up permissions. +2. Enable Customization Services (only required if you don't already use Platform Services). +3. Create a test workspace in Expensify and download the [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage) +4. Upload the Package in Sage Intacct. +5. Add web services authorization. +6. Enter credentials and connect Expensify and Sage Intacct. +7. Configure integration sync options. + + +### Step 1: Create a web services user with user-based permissions +**Note:** If the steps in this section look different in your Sage Intacct instance, you likely use role-based permissions. If that's the case, see the steps below on creating a web services user for role-based permissions. +To connect to Sage Intacct, it's necessary to set up a web services user. This user simplifies tracking activity within Sage Intacct, such as exporting expense reports and credit card charges from Expensify. It also ensures a seamless transition when someone joins or leaves your accounting department. Setting up the web services user is free of charge. Please follow these steps: +1. Go to **Company > Web Services Users > New**. +2. Configure the user as shown in the screenshot below, making sure to follow these guidelines: + - **User ID:** "xmlgateway_expensify" + - **Last Name and First Name:** "Expensify" + - **Email Address:** Your shared accounting team email + - **User Type:** "Business" + - **Admin Privileges:** "Full" + - **Status:** "Active" + + +Once you've created the user, you'll need to set the correct permissions. To do this, follow these steps: +1. Go to the subscription link for this user in the user list. +2. Click on the checkbox next to the Application/Module you want to modify permissions for. +3. Click on the **Permissions** link to make modifications. + +These are the permissions the user needs to have if exporting reimbursable expenses as Vendor Bills: +- **Administration (All)** +- **Company (Read-only)** +- **Cash Management (All)** +- **General Ledger (All)** +- **Accounts Payable (All)** +- **Projects (Read-only)** (required if you're going to be using Projects and Customers) + +**Note:** that selecting the radio button next to the Permission you want and clicking **Save** will set the permission for that particular Application/Module. + + +### Step 2: Enable Customization Services (only applicable if you don't already use Platform Services) +To enable Customization Services go to **Company > Subscriptions > Customization Services**. + - If you already have Platform Services enabled, you can skip this step. + +### Step 3: Create a Test Workspace in Expensify and Download [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage) +Creating a test workspace in Expensify allows you to establish a sandbox environment for testing before implementing the integration in a live environment. If you're already using Expensify, creating a test workspace ensures that your existing company workspace rules and approval workflows remain intact. Here's how to set it up: +1. Go to **expensify.com > Settings > Workspaces > Groups > New Workspace**. +2. Name the workspace something like "Sage Intacct Test Workspace." +3. Go to **Connections > Sage Intacct > Connect to Sage Intacct**. +4. Select "I've completed these" if you've downloaded the [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage) and completed the previous steps in Sage Intacct. +5. Select **Download Package** (You only need to download the file; we'll upload it from your Downloads folder later). + +### Step 4: Upload the Package in Sage Intacct +If you use **Customization Services**: + +1. Go to **Customization Services > Custom Packages > New Package**. +2. Click on **Choose File** and select the Package file from your downloads folder. +3. Click **Import**. + + +If you use **Platform Services**: + +1. Go to **Platform Services > Custom Packages > New Package**. +2. Click on **Choose File** and select the Package file from your downloads folder. +3. Click **Import**. + +### Step 5: Add Web Services Authorization +1. Go to **Company > Company Info > Security** in Intacct and click **Edit**. +2. Scroll down to **Web Services Authorizations** and add "expensify" (all lowercase) as a Sender ID. + +### Step 6: Enter Credentials and Connect Expensify with Sage Intacct +1. Go back to **Settings > Workspaces > Groups > [Workspace Name] > Connections > Configure**. +2. Click on **Connect to Sage Intacct**. +3. Enter the credentials that you've previously set for your web services user. +4. Click **Send** once you've finished entering the credentials. + +Next, you’ll configure the Export, Coding, and Advanced tabs of the connection configuration in Expensify. + + + +## Role-based Permissions - Expense Reports + +For this setup guide, we're going to walk you through how to get your connection up and running as Expense Reports with role-based permissions. + +### Checklist of items to complete: + +1. Create web services user and set up permissions +2. Enable Time & Expenses module +3. Set up Employees in Sage Intacct +4. Set up Expense Types in Sage Intacct +5. Enable Customization Services (only applicable if you don't already use Platform Services) +6. Create a test workspace and download the [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage) +7. Upload the Package in Sage Intacct +8. Add web services authorization +9. Enter credentials and connect Expensify and Sage Intacct +10. Configure integration sync options + + +### Step 1: Create a web services user with role-based permissions + +In Sage Intacct, click **Company**, then click on the **+** button next to **Roles**. + +Name the role, then click **Save**. + +Go to **Roles > Subscriptions** for the "Expensify" role you just created. + +Set the permissions for this role by clicking the checkbox and then clicking on the **Permissions** hyperlink. + +These are the permissions required for a user to export reimbursable expenses as Expense Reports: +- **Administration (All)** +- **Company (Read-only)** +- **Cash Management (All)** +- **General Ledger (All)** +- **Time & Expense (All)** +- **Projects (Read-only)** (only needed if using Projects and Customers) +- **Accounts Payable (All)** (only needed for exporting non-reimbursable expenses as vendor bills) + +Now, you'll need to create a web services user and assign this role to that user. + +- **Company > Web Services Users > New** +- Set up the user like the screenshot below, making sure to do the following: + - User ID: “xmlgateway_expensify" + - Last name and First name: "Expensify" + - Email address: your shared accounting team email + - User type: "Business" + - Admin privileges: "Full" + - Status: "Active" + +To assign the role, go to **Roles Information**: + +- Click the **+** button, then find the "Expensify" role and click **Save**. + +### Step 2: Enable the Time & Expenses module (Only required if exporting reimbursable expenses as Expense Reports) + +The T&E module often comes standard on your Sage Intacct instance, but you may need to enable it if it was not a part of your initial Sage Intacct implementation. Enabling the T&E module is a paid subscription through Sage Intacct. Please reach out to your Sage Intacct account manager with any questions on the costs of enabling this module. It's required for our integration and takes just a few minutes to configure. + +In Sage Intacct, click on the **Company** menu > **Subscriptions** > **Time & Expenses** and click the toggle to subscribe. + +Once you've enabled T&E, you'll need to configure it properly: +- Ensure that **Expense types** is checked. +- Under Auto-numbering sequences, please set the following: + - To create the EXP sequence, click on the down arrow on the expense report line > **Add** + - Sequence ID: EXP + - Print Title: EXPENSE REPORT + - Starting Number: 1 + - Next Number: 2 + - Once you've done this, select **Advanced Settings** + - Fixed Number Length: 4 + - Fixed Prefix: EXP + - Once you've done this, hit **Save** +- Under Expense Report approval settings, make sure the "Enable expense report approval" is unchecked. +- Click **Save**! + +### Step 3: Set up Employees in Sage Intacct (Only required if exporting reimbursable expenses as Expense Reports) + +In order to set up Employees, go to **Time & Expenses** > click the plus button next to **Employees**. If you don't see Time & Expense in the top ribbon, you may need to adjust your settings by going to **Company > Roles > Time & Expenses > Enable all permissions**. To create an employee, you'll need to insert the following information: +- Employee ID +- Primary contact name +- Email address (click the dropdown arrow in the Primary contact name field) > select the employee if they've already been created. Otherwise click **+ Add** > fill in their Primary Email Address along with any other information you require. + +### Step 4: Set up Expense Types in Sage Intacct (only required if exporting reimbursable expenses as Expense Reports) + +Expense Types are a user-friendly way of displaying the names of your expense accounts to your employees. They are required for our integration. In order to set up Expense Types, you'll first need to setup your Chart of Accounts (these can be set up in bulk by going to **Company > Open Setup > Company Setup Checklist > click Import**). + +Once you've setup your Chart of Accounts, to set Expense Types, go to **Time & Expense** > **Open Setup** > click the plus button next to **Expense Types**. For each Expense Type, you'll need to include the following information: +- Expense Type +- Description +- Account Number (from your GL) + +### Step 5: Enable Customization Services + +To enable, go **Company > Subscriptions > Customization Services** (if you already have Platform Services enabled, you will skip this step). + +### Step 6: Create a test workspace in Expensify and download [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage) + +The test workspace will be used as a sandbox environment where we can test before going live with the integration. If you're already using Expensify, creating a test workspace will ensure that your existing group workspace rules, approval workflow, etc remain intact. In order to set this up: + +- Go to **expensify.com > Settings > Workspaces > New Workspace** +- Name the workspace something like "Sage Intacct Test Workspace" +- Go to **Connections > Sage Intacct > Connect to Sage Intacct** +- Select **Download Package** (All you need to do is download the file. We'll upload it from your Downloads folder later). + +### Step 7: Upload Package in Sage Intacct + +If you use Customization Services: + +- **Customization Services > Custom Packages > New Package > Choose File >** select the Package file from your downloads folder > Import + +If you use Platform Services: + +- **Platform Services > Custom Packages > New Package > Choose File >** select the Package file from your downloads folder > Import + +### Step 8: Add web services authorization + +- Go to **Company > Company Info > Security** in Intacct and click Edit. Next, scroll down to Web Services authorizations and add "expensify" (this must be all lower case) as a Sender ID. + +### Step 9: Enter credentials and connect Expensify and Sage Intacct + +- Now, go back to **Settings > Workspaces > Group > [Workspace Name] > Connections > Configure > Connect to Sage Intacct** and enter the credentials that you've set for your web services user. Click Send once you're done. + +Next, follow the links in the related articles section below to complete the configuration for the Export, Coding, and Advanced tabs of the connection settings. + +## Role-based Permissions - Vendor Bills + +Follow the steps below to set up Sage Intacct with role-based permissions and export Vendor Bills: + +### Checklist of items to complete: + +1. Create a web services user and configure permissions. +2. Enable Customization Services (if not using Platform Services). +3. Create a test workspace in Expensify and download the [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage). +4. Upload the Package in Sage Intacct. +5. Add web services authorization. +6. Enter credentials and connect Expensify and Sage Intacct. +7. Configure integration sync options. + + +### Step 1: Create a web services user with role-based permissions + +In Sage Intacct: +- Navigate to "Company" and click the **+** button next to "Roles." +- Name the role and click **Save**. +- Go to "Roles" > "Subscriptions" for the "Expensify" role you created. +- Set the permissions for this role by clicking the checkbox and then clicking on the Permissions hyperlink + + +These are the permissions required for a user to export reimbursable expenses as Vendor Bills: +- **Administration (All)** +- **Company (Read-only)** +- **Cash Management (All)** +- **General Ledger (All)** +- **Time & Expense (All)** +- **Projects (Read-only)** (only needed if using Projects and Customers) +- **Accounts Payable (All)** (only needed for exporting non-reimbursable expenses as vendor bills) + + +- Create a web services user: + - Go to **Company > Web Services Users > New** + - Configure the user as follows: + - User ID: "xmlgateway_expensify" + - Last Name and First Name: "Expensify" + - Email Address: Your shared accounting team email + - User Type: "Business" + - Admin Privileges: "Full" + - Status: "Active" + - To assign the role, go to "Roles Information", click the **+** button, find the "Expensify" role, and click **Save** + +### Step 2: Enable Customization Services + +Only required if you don't already use Platform Services: +- To enable, go to **Company > Subscriptions > Customization Services** + +### Step 3: Create a test workspace in Expensify and download the [Expensify Package](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fwww.expensify.com%2Ftools%2Fintegrations%2FdownloadPackage) + +Create a test workspace in Expensify: +- Go to **Settings > Workspaces** and click **New Workspace** on the Expensify website. +- Name the workspace something like "Sage Intacct Test Workspace." +- Once created, navigate to **Settings > Workspaces > [Group Workspace Name] > Connections > Accounting Integrations > Connect to Sage Intacct** +- Select **Create a new Sage Intacct connection/Connect to Sage Intacct** +- Select **Download Package** (We'll upload it from your Downloads folder later.) + +### Step 4: Upload Package in Sage Intacct + +If you use **Customization Services**: +- Go to **Customization Services > Custom Packages > New Package > Choose File > select the Package file from your downloads folder > Import**. + +If you use **Platform Services**: +- Go to **Platform Services > Custom Packages > New Package > Choose File > select the Package file from your downloads folder > Import**. + +### Step 5: Add web services authorization + +- Go to **Company > Company Info > Security** in Intacct and click **Edit** +- Scroll down to **Web Services Authorizations** and add **expensify** (all lowercase) as a Sender ID. + +### Step 6: Enter credentials and connect Expensify and Sage Intacct + +Now, go back to **Settings > Workspaces > [Group Workspace Name] > Connections > Accounting Integrations > Configure > Connect to Sage Intacct** and enter the credentials you set for your web services user. Click **Send** when finished. + +### Step 7: Configure your connection + +Once the initial sync completes, you may receive the error "No Expense Types Found" if you're not using the Time and Expenses module. Close the error dialogue, and your configuration options will appear. Switch the reimbursable export option to **Vendor Bills** and click **Save** before completing your configuration. + +Next, refer to the related articles section below to finish configuring the Export, Coding, and Advanced tabs of the connection configuration. + +# How to configure export settings + +When you connect Intacct with Expensify, you can configure how information appears once exported. To do this, Admins who are connected to Intacct can go to **Settings > Workspaces > Group > [Workspace Name] > Connections**, and then click on **Configure** under Intacct. This is where you can set things up the way you want. + + +## Preferred Exporter + +Any workspace admin can export to Sage Intacct, but only the preferred exporter will see reports that are ready for export in their Inbox. + + + +## Date + +Choose which date you would like your Expense Reports or Vendor Bills to use when exported. + +- **Date of last expense:** Uses the date on the most recent expense added to the report. +- **Exported date:** Is the date you export the report to Sage Intacct. +- **Submitted date:** Is the date the report creator originally submitted the report. + +All export options except credit cards use the date in the drop-down. Credit card transactions use the transaction date. + +## Reimbursable Expenses + +Depending on your initial setup, your **reimbursable expenses** will be exported as either **Expense Reports** or **Vendor Bills** to Sage Intacct. + +## Non-Reimbursable Expenses + +**Non-reimbursable expenses** will export separately from reimbursable expenses, either as **Vendor Bills**, or as **credit card charges** to the account you select. It is not an option to export non-reimbursable expenses as **Journal** entries. + + +If you are centrally managing your company cards through Domain Control, you can export expenses from each individual card to a specific account in Intacct. +Please note, Credit Card Transactions cannot be exported to Sage Intacct at the top-level if you have **Multi-Currency** enabled, so you will need to select an entity in the configuration of your Expensify Workspace by going to **Settings > Workspaces > Groups > [Workspace Name] > Connections > Configure**. + +## Exporting Negative Expenses + +You can export negative expenses successfully to Intacct regardless of which Export Option you choose. The one thing to keep in mind is that if you have Expense Reports selected as your export option, the **total** of the report can not be negative. + +# How to configure coding settings + +The appearance of your expense data in Sage Intacct depends on how you've configured it in Expensify. It's important to understand each available option to achieve the desired results. + +## Expense Types + +Categories are always enabled and are the primary means of matching expenses to the correct accounts in Sage Intact. The Categories in Expensify depend on your **Reimbursable** export options: +- If your Reimbursable export option is set to **Expense Reports** (the default), your Categories will be your **Expense Types**. +- If your Reimbursable export option is set to **Vendor Bills**, your Categories will be your **Chart of Accounts** (also known as GL Codes or Account Codes). + +You can disable unnecessary categories from your **Settings > Workspaces > Group > [Workspace Name] > Categories** page if your list is too extensive. Note that every expense must be coded with a Category, or it will not export. Also, when you first set up the integration, your existing categories will be overwritten. + +## Billable Expenses + +Enabling Billable expenses allows you to map your expense types or accounts to items in Sage Intacct. To do this, you'll need to enable the correct permissions on your Sage Intacct user or role. This may vary based on the modules you use in Sage Intacct, so you should enable read-only permissions for relevant modules such as Projects, Purchasing, Inventory Control, and Order Entry. + +Once permissions are set, you can map your categories (expense types or accounts, depending on your export settings) to specific items, which will then export to Sage Intacct. When an expense is marked as Billable in Expensify, users must select the correct billable Category (Item), or there will be an error during export. + +## Dimensions - Departments, Classes, and Locations + +If you enable these dimensions, you can choose from three data options: +- Not pulled into Expensify: Employee default (available when the reimbursable export option is set to Expense Reports) +- Pulled into Expensify and selectable on reports/expenses: Tags (useful for cross-charging between Departments or Locations) +- Report Fields (applies at the header level, useful when an employee's Location varies from one report to another) + +Please note that the term "tag" may appear instead of "Department" on your reports, so ensure that "Projects" is not disabled in your Tags configuration within your workspace settings. Make sure it's enabled within your coding settings of the Intacct configuration settings. When multiple options are available, the term will default to Tags. + +## Customers and Projects + +These settings are particularly relevant to billable expenses and can be configured as Tags or Report Fields. + +## Tax + +As of September 2023, our Sage Intacct integration supports native VAT and GST tax. To enable this feature, open the Sage Intacct configuration settings in your workspace, go to the Coding tab, and enable Tax. For existing Sage Intacct connectings, simply resync your workspace and the tax toggle will appear. For new Sage Intacct connections, the tax toggle will be available when you complete the integration steps. +Having this option enabled will then import your native tax rates from Sage Intacct into Expensify. From there, you can select default rates for each category. + +## User-Defined Dimensions + +You can add User-Defined Dimensions (UDD) to your workspace by locating the "Integration Name" in Sage Intacct. Please note that you must be logged in as an administrator in Sage Intacct to find the required fields. + +To find the Integration Name in Sage Intacct: +1. Go to **Platform Services > Objects > List** +2. Set "filter by application" to "user-defined dimensions." + +Now, in Expensify, navigate to **Settings > Workspaces > Group > [Workspace Name] > Connections**, and click **Configure** under Sage Intacct. On the Coding tab, enable the toggle next to User Defined Dimensions. Enter the "Integration name" and choose whether to import it into Expensify as an expense-level Tag or as a Report Field, then click **Save**. + +You'll now see the values for your custom segment available under Tags settings or Report Fields settings in Expensify. + + + +# How to configure advanced settings +In multi-entity environments, you'll find a dropdown at the top of the sync options menu, where you can choose to sync with the top-level or a specific entity in your Sage Intacct instance. If you sync at the top level, we pull in employees and dimensions shared at the top level and export transactions to the top level. Otherwise, we sync information with the selected entity. +## Auto Sync +When a non-reimbursable report is finally approved, it will be automatically exported to Sage Intacct. Typically, non-reimbursable expenses will sync to the next open period in Sage Intacct by default. If your company uses Expensify's ACH reimbursement, reimbursable expenses will be held back and exported to Sage when the report is reimbursed. +## Inviting Employees +Enabling **Invite Employees** allows the integration to automatically add your employees to your workspace and create an Expensify account for them if they don't have one. +If you have your domain verified on your account, ensure that the Expensify account connected to Sage Intacct is an admin on your domain. +When you toggle on "Invite Employees" on the Advanced tab, all employees in Sage Intacct who haven't been invited to the Expensify group workspace you're connecting will receive an email invitation to join the group workspace. Approval workflow will default to Manager Approval and can be further configured on the People settings page. +## Import Sage Intacct Approvals +When the "Import Sage Intacct Approvals" setting is enabled, Expensify will automatically set each user's manager listed in Sage Intacct as their first approver in Expensify. If no manager exists in Sage Intacct, the approver can be set in the Expensify People table. You can also add a second level of approval to your Sage Intacct integration by setting a final approver in Expensify. +Please note that if you need to add or edit an optional final approver, you will need to select the **Manager Approval** option in the workflow. Here is how each option works: +- **Basic Approval:** All users submit to one user. +- **Manager Approval:** Each user submits to the manager (imported from Sage Intacct). Each manager forwards to one final approver (optional). +- **Configure Manually:** Import employees only, configure workflow in Expensify. + + +## Sync Reimbursed Reports +When using Expensify ACH, reimbursable reports exported to Intacct are exported: +- As Vendor Bills to the default Accounts Payable account set in your Intacct Accounts Payable module configuration, OR +- As Expense Reports to the Employee Liabilities account in your Time & Expenses module configuration. +When ACH reimbursement is enabled, the "Sync Reimbursed Reports" feature will additionally export a Bill Payment to the selected Cash and Cash Equivalents account listed. If **Auto Sync** is enabled, the payment will be created when the report is reimbursed; otherwise, it will be created the next time you manually sync the workspace. +Intacct requires that the target account for the Bill Payment be a Cash and Cash Equivalents account type. If you aren't seeing the account you want in that list, please first confirm that the category on the account is Cash and Cash Equivalents. + + +# FAQ +## What if my report isn't automatically exported to Sage Intacct? +There are a number of factors that can cause automatic export to fail. If this happens, the preferred exporter will receive an email and an Inbox task outlining the issue and any associated error messages. +The same information will be populated in the comments section of the report. +The fastest way to find a resolution for a specific error is to search the Community, and if you get stuck, give us a shout! +Once you've resolved any errors, you can manually export the report to Sage Intacct. +## How can I make sure that I final approve reports before they're exported to Sage Intacct? +Make sure your approval workflow is configured correctly so that all reports are reviewed by the appropriate people within Expensify before exporting to Sage Intacct. +Also, if you have verified your domain, consider strictly enforcing expense workspace workflows. You can set this up via Settings > Domains > [Domain Name] > Groups. + + +## If I enable Auto Sync, what happens to existing approved and reimbursed reports? +If your workspace has been connected to Intacct with Auto Sync disabled, you can safely turn on Auto Sync without affecting existing reports which have not been exported. +If a report has been exported to Intacct and reimbursed via ACH in Expensify, we'll automatically mark it as paid in Intacct during the next sync. +If a report has been exported to Intacct and marked as paid in Intacct, we'll automatically mark it as reimbursed in Expensify during the next sync. +If a report has not been exported to Intacct, it will not be exported to Intacct automatically. diff --git a/docs/articles/expensify-classic/integrations/other-integrations/Google-Apps-SSO.md b/docs/articles/expensify-classic/integrations/other-integrations/Google-Apps-SSO.md index 9fd745838caf..a034d13dd143 100644 --- a/docs/articles/expensify-classic/integrations/other-integrations/Google-Apps-SSO.md +++ b/docs/articles/expensify-classic/integrations/other-integrations/Google-Apps-SSO.md @@ -22,6 +22,7 @@ To enable Expensify for your Google Apps domain and add an “Expenses” link t 6. Click **Finish**. You can configure access for specific Organizational Units later if needed. 7. All account holders on your domain can now access Expensify from the Google Apps directory by clicking **More** and choosing **Expensify**. 8. Now, follow the steps below to sync your users with Expensify automatically. + # How to Sync Users from Google Apps to Expensify To sync your Google Apps users to your Expensify Workspace, follow these steps: 1. Follow the above steps to install Expensify in your Google Apps directory. diff --git a/fastlane/Fastfile b/fastlane/Fastfile index dac53193fdc6..78abf8074155 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -305,7 +305,10 @@ platform :ios do export_compliance_contains_proprietary_cryptography: false, # We do not show any third party content - content_rights_contains_third_party_content: false + content_rights_contains_third_party_content: false, + + # Indicate that our key has admin permissions + content_rights_has_rights: true }, release_notes: { 'en-US' => "Improvements and bug fixes" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index f30650acf703..f022cf3452a8 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.84 + 1.3.85 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.3.84.7 + 1.3.85.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 7dc3d576f77a..db42a9dc6d55 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.3.84 + 1.3.85 CFBundleSignature ???? CFBundleVersion - 1.3.84.7 + 1.3.85.0 diff --git a/package-lock.json b/package-lock.json index 5b2bab03b936..922c2a158654 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.84-7", + "version": "1.3.85-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.84-7", + "version": "1.3.85-0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -43,7 +43,6 @@ "@types/node": "^18.14.0", "@ua/react-native-airship": "^15.2.6", "awesome-phonenumber": "^5.4.0", - "babel-plugin-transform-remove-console": "^6.9.4", "babel-polyfill": "^6.26.0", "canvas-size": "^1.2.6", "core-js": "^3.32.0", @@ -138,8 +137,6 @@ "@babel/runtime": "^7.20.0", "@electron/notarize": "^1.2.3", "@jest/globals": "^29.5.0", - "@kie/act-js": "^2.0.1", - "@kie/mock-github": "^1.0.0", "@octokit/core": "4.0.4", "@octokit/plugin-paginate-rest": "3.1.0", "@octokit/plugin-throttling": "4.1.0", @@ -5465,7 +5462,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@kie/act-js/-/act-js-2.3.0.tgz", "integrity": "sha512-Q9k0b05uA46jXKWmVfoGDW+0xsCcE7QPiHi8IH7h41P36DujHKBj4k28RCeIEx3IwUCxYHKwubN8DH4Vzc9XcA==", - "dev": true, "hasInstallScript": true, "dependencies": { "@kie/mock-github": "^2.0.0", @@ -5485,7 +5481,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-2.0.0.tgz", "integrity": "sha512-od6UyICJYKMnz9HgEWCQAFT/JsCpKkLp+JQH8fV23tf+ZmmQI1dK3C20k6aO5uJhAHA0yOcFtbKFVF4+8i3DTg==", - "dev": true, "dependencies": { "@octokit/openapi-types-ghec": "^18.0.0", "ajv": "^8.11.0", @@ -5500,14 +5495,12 @@ "node_modules/@kie/act-js/node_modules/@octokit/openapi-types-ghec": { "version": "18.1.1", "resolved": "https://registry.npmjs.org/@octokit/openapi-types-ghec/-/openapi-types-ghec-18.1.1.tgz", - "integrity": "sha512-5Ri7FLYX4gJSdG+G0Q8QDca/gOLfkPN4YR2hkbVg6hEL+0N62MIsJPTyNaT9pGEXCLd1KbYV6Lh3T2ggsmyBJw==", - "dev": true + "integrity": "sha512-5Ri7FLYX4gJSdG+G0Q8QDca/gOLfkPN4YR2hkbVg6hEL+0N62MIsJPTyNaT9pGEXCLd1KbYV6Lh3T2ggsmyBJw==" }, "node_modules/@kie/act-js/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5521,7 +5514,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, "engines": { "node": ">=6" } @@ -5530,7 +5522,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-1.1.0.tgz", "integrity": "sha512-fD+utlOiyZSOutOcXL0G9jfjbtvOO44PLUyTfgfkrm1+575R/dbvU6AcJfjc1DtHeRv7FC7f4ebyU+a1wgL6CA==", - "dev": true, "dependencies": { "@octokit/openapi-types-ghec": "^14.0.0", "ajv": "^8.11.0", @@ -5546,7 +5537,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5560,7 +5550,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, "engines": { "node": ">=6" } @@ -5569,7 +5558,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "dev": true, "dependencies": { "debug": "^4.1.1" } @@ -5577,8 +5565,7 @@ "node_modules/@kwsites/promise-deferred": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", - "dev": true + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", @@ -5957,7 +5944,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -5971,7 +5957,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -5981,7 +5966,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -6112,8 +6096,7 @@ "node_modules/@octokit/openapi-types-ghec": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types-ghec/-/openapi-types-ghec-14.0.0.tgz", - "integrity": "sha512-xhd9oEvn2aroGn+sk09Ptx/76Y7aKU0EIgHukHPCU1+rGJreO36baEEk6k8ZPblieHNM39FcykJQmtDrETm0KA==", - "dev": true + "integrity": "sha512-xhd9oEvn2aroGn+sk09Ptx/76Y7aKU0EIgHukHPCU1+rGJreO36baEEk6k8ZPblieHNM39FcykJQmtDrETm0KA==" }, "node_modules/@octokit/plugin-paginate-rest": { "version": "3.1.0", @@ -21218,7 +21201,6 @@ "version": "0.5.10", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "dev": true, "engines": { "node": ">=6.0" } @@ -21849,7 +21831,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true, "license": "MIT" }, "node_modules/array-includes": { @@ -23292,7 +23273,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-4.0.2.tgz", "integrity": "sha512-jxJ0PbXR8eQyPlExCvCs3JFnikvs1Yp4gUJt6nmgathdOwvur+q22KWC3h20gvWl4T/14DXKj2IlkJwwZkZPOw==", - "dev": true, "dependencies": { "cmd-shim": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", @@ -23307,7 +23287,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -23319,7 +23298,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -23402,7 +23380,6 @@ }, "node_modules/body-parser": { "version": "1.20.0", - "dev": true, "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -23427,7 +23404,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -23437,7 +23413,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -23447,7 +23422,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -23460,7 +23434,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, "license": "MIT" }, "node_modules/bonjour-service": { @@ -24536,7 +24509,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -24965,7 +24937,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.1.tgz", "integrity": "sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q==", - "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -25408,7 +25379,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -25421,7 +25391,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -25440,7 +25409,6 @@ }, "node_modules/content-type": { "version": "1.0.4", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -25455,7 +25423,6 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -25465,7 +25432,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true, "license": "MIT" }, "node_modules/copy-concurrently": { @@ -30320,7 +30286,6 @@ }, "node_modules/express": { "version": "4.18.1", - "dev": true, "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -30363,7 +30328,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -30373,14 +30337,12 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, "license": "MIT" }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -30582,7 +30544,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -30667,7 +30628,6 @@ }, "node_modules/fastq": { "version": "1.13.0", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -30901,7 +30861,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -30920,7 +30879,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -30930,7 +30888,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, "license": "MIT" }, "node_modules/find-babel-config": { @@ -31117,7 +31074,6 @@ "version": "1.15.3", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", - "dev": true, "funding": [ { "type": "individual", @@ -31378,7 +31334,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -31455,7 +31410,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -31720,7 +31674,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -33540,7 +33493,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.10" @@ -33865,7 +33817,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -33941,7 +33892,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -37368,7 +37318,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, "license": "ISC" }, "node_modules/json5": { @@ -38884,7 +38833,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -39124,7 +39072,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true, "license": "MIT" }, "node_modules/merge-options": { @@ -39160,7 +39107,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -39170,7 +39116,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -41067,7 +41012,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -41118,7 +41062,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -41215,7 +41158,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -41451,7 +41393,6 @@ "version": "13.3.3", "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.3.tgz", "integrity": "sha512-z+KUlILy9SK/RjpeXDiDUEAq4T94ADPHE3qaRkf66mpEhzc/ytOMm3Bwdrbq6k1tMWkbdujiKim3G2tfQARuJw==", - "dev": true, "dependencies": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", @@ -41681,7 +41622,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -42893,7 +42833,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true, "license": "MIT" }, "node_modules/path-type": { @@ -43610,7 +43549,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true, "engines": { "node": ">= 8" } @@ -43638,7 +43576,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -43926,7 +43863,6 @@ "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, "dependencies": { "side-channel": "^1.0.4" }, @@ -43987,7 +43923,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -44069,7 +44004,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -44085,7 +44019,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -44095,7 +44028,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -45947,7 +45879,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", - "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -47102,7 +47033,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -47197,7 +47127,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -47932,7 +47861,6 @@ "version": "3.19.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.0.tgz", "integrity": "sha512-hyH2p9Ptxjf/xPuL7HfXbpYt9gKhC1yWDh3KYIAYJJePAKV7AEjLN4xhp7lozOdNiaJ9jlVvAbBymVlcS2jRiA==", - "dev": true, "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -49474,7 +49402,6 @@ "version": "6.1.15", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -49491,7 +49418,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -50323,7 +50249,6 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, "license": "MIT", "dependencies": { "media-typer": "0.3.0", @@ -56888,7 +56813,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@kie/act-js/-/act-js-2.3.0.tgz", "integrity": "sha512-Q9k0b05uA46jXKWmVfoGDW+0xsCcE7QPiHi8IH7h41P36DujHKBj4k28RCeIEx3IwUCxYHKwubN8DH4Vzc9XcA==", - "dev": true, "requires": { "@kie/mock-github": "^2.0.0", "adm-zip": "^0.5.10", @@ -56904,7 +56828,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-2.0.0.tgz", "integrity": "sha512-od6UyICJYKMnz9HgEWCQAFT/JsCpKkLp+JQH8fV23tf+ZmmQI1dK3C20k6aO5uJhAHA0yOcFtbKFVF4+8i3DTg==", - "dev": true, "requires": { "@octokit/openapi-types-ghec": "^18.0.0", "ajv": "^8.11.0", @@ -56919,14 +56842,12 @@ "@octokit/openapi-types-ghec": { "version": "18.1.1", "resolved": "https://registry.npmjs.org/@octokit/openapi-types-ghec/-/openapi-types-ghec-18.1.1.tgz", - "integrity": "sha512-5Ri7FLYX4gJSdG+G0Q8QDca/gOLfkPN4YR2hkbVg6hEL+0N62MIsJPTyNaT9pGEXCLd1KbYV6Lh3T2ggsmyBJw==", - "dev": true + "integrity": "sha512-5Ri7FLYX4gJSdG+G0Q8QDca/gOLfkPN4YR2hkbVg6hEL+0N62MIsJPTyNaT9pGEXCLd1KbYV6Lh3T2ggsmyBJw==" }, "fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -56936,8 +56857,7 @@ "totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==" } } }, @@ -56945,7 +56865,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-1.1.0.tgz", "integrity": "sha512-fD+utlOiyZSOutOcXL0G9jfjbtvOO44PLUyTfgfkrm1+575R/dbvU6AcJfjc1DtHeRv7FC7f4ebyU+a1wgL6CA==", - "dev": true, "requires": { "@octokit/openapi-types-ghec": "^14.0.0", "ajv": "^8.11.0", @@ -56961,7 +56880,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -56971,8 +56889,7 @@ "totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==" } } }, @@ -56980,7 +56897,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "dev": true, "requires": { "debug": "^4.1.1" } @@ -56988,8 +56904,7 @@ "@kwsites/promise-deferred": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", - "dev": true + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, "@leichtgewicht/ip-codec": { "version": "2.0.4", @@ -57271,7 +57186,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -57280,14 +57194,12 @@ "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -57399,8 +57311,7 @@ "@octokit/openapi-types-ghec": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types-ghec/-/openapi-types-ghec-14.0.0.tgz", - "integrity": "sha512-xhd9oEvn2aroGn+sk09Ptx/76Y7aKU0EIgHukHPCU1+rGJreO36baEEk6k8ZPblieHNM39FcykJQmtDrETm0KA==", - "dev": true + "integrity": "sha512-xhd9oEvn2aroGn+sk09Ptx/76Y7aKU0EIgHukHPCU1+rGJreO36baEEk6k8ZPblieHNM39FcykJQmtDrETm0KA==" }, "@octokit/plugin-paginate-rest": { "version": "3.1.0", @@ -68313,8 +68224,7 @@ "adm-zip": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "dev": true + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==" }, "agent-base": { "version": "6.0.2", @@ -68792,8 +68702,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-includes": { "version": "3.1.6", @@ -69849,7 +69758,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-4.0.2.tgz", "integrity": "sha512-jxJ0PbXR8eQyPlExCvCs3JFnikvs1Yp4gUJt6nmgathdOwvur+q22KWC3h20gvWl4T/14DXKj2IlkJwwZkZPOw==", - "dev": true, "requires": { "cmd-shim": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", @@ -69860,14 +69768,12 @@ "signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" }, "write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -69940,7 +69846,6 @@ }, "body-parser": { "version": "1.20.0", - "dev": true, "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -69959,14 +69864,12 @@ "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -69975,7 +69878,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -69983,8 +69885,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -70751,8 +70652,7 @@ "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, "chrome-trace-event": { "version": "1.0.3", @@ -71053,8 +70953,7 @@ "cmd-shim": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.1.tgz", - "integrity": "sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q==", - "dev": true + "integrity": "sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q==" }, "co": { "version": "4.6.0", @@ -71388,7 +71287,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, "requires": { "safe-buffer": "5.2.1" }, @@ -71396,14 +71294,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, "content-type": { - "version": "1.0.4", - "dev": true + "version": "1.0.4" }, "convert-source-map": { "version": "1.9.0", @@ -71413,14 +71309,12 @@ "cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "copy-concurrently": { "version": "1.0.5", @@ -74954,7 +74848,6 @@ }, "express": { "version": "4.18.1", - "dev": true, "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -74993,7 +74886,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -75001,14 +74893,12 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -75148,7 +75038,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -75207,7 +75096,6 @@ }, "fastq": { "version": "1.13.0", - "dev": true, "requires": { "reusify": "^1.0.4" } @@ -75386,7 +75274,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -75401,7 +75288,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -75409,8 +75295,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -75548,8 +75433,7 @@ "follow-redirects": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", - "dev": true + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" }, "for-each": { "version": "0.3.3", @@ -75715,8 +75599,7 @@ "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fraction.js": { "version": "4.3.4", @@ -75768,7 +75651,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "requires": { "minipass": "^3.0.0" } @@ -75951,7 +75833,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, "requires": { "is-glob": "^4.0.1" } @@ -77241,8 +77122,7 @@ "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-absolute-url": { "version": "3.0.3", @@ -77437,8 +77317,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-finalizationregistry": { "version": "1.0.2", @@ -77485,7 +77364,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, "requires": { "is-extglob": "^2.1.1" } @@ -79896,8 +79774,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "json5": { "version": "2.2.3", @@ -81020,8 +80897,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "mem": { "version": "8.1.1", @@ -81196,8 +81072,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "merge-options": { "version": "3.0.4", @@ -81223,14 +81098,12 @@ "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "metro": { "version": "0.76.8", @@ -82593,7 +82466,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -82629,7 +82501,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -82703,8 +82574,7 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "mock-fs": { "version": "4.14.0", @@ -82876,7 +82746,6 @@ "version": "13.3.3", "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.3.tgz", "integrity": "sha512-z+KUlILy9SK/RjpeXDiDUEAq4T94ADPHE3qaRkf66mpEhzc/ytOMm3Bwdrbq6k1tMWkbdujiKim3G2tfQARuJw==", - "dev": true, "requires": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", @@ -83052,8 +82921,7 @@ "npm-normalize-package-bin": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==" }, "npm-run-path": { "version": "4.0.1", @@ -83899,8 +83767,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "path-type": { "version": "4.0.0", @@ -84405,8 +84272,7 @@ "propagate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==" }, "property-information": { "version": "5.6.0", @@ -84426,7 +84292,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, "requires": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -84643,7 +84508,6 @@ "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, "requires": { "side-channel": "^1.0.4" } @@ -84684,8 +84548,7 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "quick-lru": { "version": "5.1.1", @@ -84735,7 +84598,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -84746,14 +84608,12 @@ "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -86004,8 +85864,7 @@ "read-cmd-shim": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", - "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", - "dev": true + "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==" }, "read-config-file": { "version": "6.3.2", @@ -86860,8 +86719,7 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "right-align": { "version": "0.1.3", @@ -86926,7 +86784,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "requires": { "queue-microtask": "^1.2.2" } @@ -87472,7 +87329,6 @@ "version": "3.19.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.0.tgz", "integrity": "sha512-hyH2p9Ptxjf/xPuL7HfXbpYt9gKhC1yWDh3KYIAYJJePAKV7AEjLN4xhp7lozOdNiaJ9jlVvAbBymVlcS2jRiA==", - "dev": true, "requires": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -88623,7 +88479,6 @@ "version": "6.1.15", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -88636,8 +88491,7 @@ "minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" } } }, @@ -89234,7 +89088,6 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" diff --git a/package.json b/package.json index ae9720003f71..cd5c6034161a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.84-7", + "version": "1.3.85-0", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", @@ -88,7 +88,6 @@ "@types/node": "^18.14.0", "@ua/react-native-airship": "^15.2.6", "awesome-phonenumber": "^5.4.0", - "babel-plugin-transform-remove-console": "^6.9.4", "babel-polyfill": "^6.26.0", "canvas-size": "^1.2.6", "core-js": "^3.32.0", @@ -183,8 +182,6 @@ "@babel/runtime": "^7.20.0", "@electron/notarize": "^1.2.3", "@jest/globals": "^29.5.0", - "@kie/act-js": "^2.0.1", - "@kie/mock-github": "^1.0.0", "@octokit/core": "4.0.4", "@octokit/plugin-paginate-rest": "3.1.0", "@octokit/plugin-throttling": "4.1.0", diff --git a/src/CONST.ts b/src/CONST.ts index b9e9181afd5b..501bc2e3aa19 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1017,8 +1017,10 @@ const CONST = { ACTIVATE: 'ActivateStep', }, TIER_NAME: { + PLATINUM: 'PLATINUM', GOLD: 'GOLD', SILVER: 'SILVER', + BRONZE: 'BRONZE', }, WEB_MESSAGE_TYPE: { STATEMENT: 'STATEMENT_NAVIGATE', @@ -1273,6 +1275,8 @@ const CONST = { CARD_EXPIRATION_DATE: /^(0[1-9]|1[0-2])([^0-9])?([0-9]{4}|([0-9]{2}))$/, ROOM_NAME: /^#[\p{Ll}0-9-]{1,80}$/u, + // eslint-disable-next-line max-len, no-misleading-character-class + EMOJI: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, // eslint-disable-next-line max-len, no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}](\u200D[\p{Extended_Pictographic}]|[\u{1F3FB}-\u{1F3FF}]|[\u{E0020}-\u{E007F}]|\uFE0F|\u20E3)*|[\u{1F1E6}-\u{1F1FF}]{2}|[#*0-9]\uFE0F?\u20E3/gu, @@ -1291,18 +1295,26 @@ const CONST = { HAS_COLON_ONLY_AT_THE_BEGINNING: /^:[^:]+$/, HAS_AT_MOST_TWO_AT_SIGNS: /^@[^@]*@?[^@]*$/, - SPECIAL_CHAR_OR_EMOJI: - // eslint-disable-next-line no-misleading-character-class - /[\n\s,/?"{}[\]()&_~^%\\;`$=#<>!*\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, + SPECIAL_CHAR: /[,/?"{}[\]()&^%;`$=#<>!*]/g, + + get SPECIAL_CHAR_OR_EMOJI() { + return new RegExp(`[_~\\n\\s]|${this.SPECIAL_CHAR.source}|${this.EMOJI.source}`, 'gu'); + }, - SPACE_OR_EMOJI: - // eslint-disable-next-line no-misleading-character-class - /(\s+|(?:[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3)+)/gu, + get SPACE_OR_EMOJI() { + return new RegExp(`(\\s+|(?:${this.EMOJI.source})+)`, 'gu'); + }, + + // Define the regular expression pattern to find a potential end of a mention suggestion: + // It might be a space, a newline character, an emoji, or a special character (excluding underscores & tildes, which might be used in usernames) + get MENTION_BREAKER() { + return new RegExp(`[\\n\\s]|${this.SPECIAL_CHAR.source}|${this.EMOJI.source}`, 'gu'); + }, // Define the regular expression pattern to match a string starting with an at sign and ending with a space or newline character - MENTION_REPLACER: - // eslint-disable-next-line no-misleading-character-class - /^@[^\n\r]*?(?=$|[\s,/?"{}[\]()&^%\\;`$=#<>!*\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3)/u, + get MENTION_REPLACER() { + return new RegExp(`^@[^\\n\\r]*?(?=$|\\s|${this.SPECIAL_CHAR.source}|${this.EMOJI.source})`, 'u'); + }, MERGED_ACCOUNT_PREFIX: /^(MERGED_\d+@)/, diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d9ea3488f85f..f60506ab7245 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -235,6 +235,8 @@ const ONYXKEYS = { DOWNLOAD: 'download_', POLICY: 'policy_', POLICY_MEMBERS: 'policyMembers_', + POLICY_DRAFTS: 'policyDrafts_', + POLICY_MEMBERS_DRAFTS: 'policyMembersDrafts_', POLICY_CATEGORIES: 'policyCategories_', POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_', POLICY_TAGS: 'policyTags_', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 6219e545c8ca..b7896225557d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -73,22 +73,26 @@ export default { SETTINGS_ABOUT: 'settings/about', SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links', SETTINGS_WALLET: 'settings/wallet', - SETTINGS_WALLET_DOMAINCARDS: { + SETTINGS_WALLET_DOMAINCARD: { route: '/settings/wallet/card/:domain', getRoute: (domain: string) => `/settings/wallet/card/${domain}`, }, SETTINGS_REPORT_FRAUD: { - route: '/settings/wallet/cards/:domain/report-virtual-fraud', - getRoute: (domain: string) => `/settings/wallet/cards/${domain}/report-virtual-fraud`, + route: '/settings/wallet/card/:domain/report-virtual-fraud', + getRoute: (domain: string) => `/settings/wallet/card/${domain}/report-virtual-fraud`, }, SETTINGS_ADD_DEBIT_CARD: 'settings/wallet/add-debit-card', SETTINGS_ADD_BANK_ACCOUNT: 'settings/wallet/add-bank-account', SETTINGS_ENABLE_PAYMENTS: 'settings/wallet/enable-payments', + SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS: { + route: 'settings/wallet/card/:domain/digital-details/update-address', + getRoute: (domain: string) => `settings/wallet/card/${domain}/digital-details/update-address`, + }, SETTINGS_WALLET_TRANSFER_BALANCE: 'settings/wallet/transfer-balance', SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT: 'settings/wallet/choose-transfer-account', SETTINGS_WALLET_CARD_ACTIVATE: { - route: 'settings/wallet/cards/:domain/activate', - getRoute: (domain: string) => `settings/wallet/cards/${domain}/activate`, + route: 'settings/wallet/card/:domain/activate', + getRoute: (domain: string) => `settings/wallet/card/${domain}/activate`, }, SETTINGS_PERSONAL_DETAILS: 'settings/profile/personal-details', SETTINGS_PERSONAL_DETAILS_LEGAL_NAME: 'settings/profile/personal-details/legal-name', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 99d5eb6277cd..8ef787edec2e 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -2,17 +2,15 @@ * This is a file containing constants for all of the screen names. In most cases, we should use the routes for * navigation. But there are situations where we may need to access screen names directly. */ -const PROTECTED_SCREENS = { - HOME: 'Home', - CONCIERGE: 'Concierge', - REPORT_ATTACHMENTS: 'ReportAttachments', -} as const; - export default { - ...PROTECTED_SCREENS, + HOME: 'Home', LOADING: 'Loading', REPORT: 'Report', + REPORT_ATTACHMENTS: 'ReportAttachments', NOT_FOUND: 'not-found', + TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', + VALIDATE_LOGIN: 'ValidateLogin', + CONCIERGE: 'Concierge', SETTINGS: { ROOT: 'Settings_Root', PREFERENCES: 'Settings_Preferences', @@ -25,12 +23,10 @@ export default { SAVE_THE_WORLD: { ROOT: 'SaveTheWorld_Root', }, - TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop', SIGN_IN_WITH_GOOGLE_DESKTOP: 'GoogleSignInDesktop', DESKTOP_SIGN_IN_REDIRECT: 'DesktopSignInRedirect', SAML_SIGN_IN: 'SAMLSignIn', - VALIDATE_LOGIN: 'ValidateLogin', // Iframe screens from olddot HOME_OLDDOT: 'Home_OLDDOT', @@ -45,5 +41,3 @@ export default { GROUPS_WORKSPACES_OLDDOT: 'GroupWorkspaces_OLDDOT', CARDS_AND_DOMAINS_OLDDOT: 'CardsAndDomains_OLDDOT', } as const; - -export {PROTECTED_SCREENS}; diff --git a/src/components/AddressSearch/CurrentLocationButton.js b/src/components/AddressSearch/CurrentLocationButton.js new file mode 100644 index 000000000000..893ec031ab7f --- /dev/null +++ b/src/components/AddressSearch/CurrentLocationButton.js @@ -0,0 +1,52 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import {Text} from 'react-native'; +import colors from '../../styles/colors'; +import styles from '../../styles/styles'; +import Icon from '../Icon'; +import * as Expensicons from '../Icon/Expensicons'; +import PressableWithFeedback from '../Pressable/PressableWithFeedback'; +import getButtonState from '../../libs/getButtonState'; +import * as StyleUtils from '../../styles/StyleUtils'; +import useLocalize from '../../hooks/useLocalize'; + +const propTypes = { + /** Callback that runs when location button is clicked */ + onPress: PropTypes.func, + + /** Boolean to indicate if the button is clickable */ + isDisabled: PropTypes.bool, +}; + +const defaultProps = { + isDisabled: false, + onPress: () => {}, +}; + +function CurrentLocationButton({onPress, isDisabled}) { + const {translate} = useLocalize(); + + return ( + e.preventDefault()} + onTouchStart={(e) => e.preventDefault()} + > + + {translate('location.useCurrent')} + + ); +} + +CurrentLocationButton.displayName = 'CurrentLocationButton'; +CurrentLocationButton.propTypes = propTypes; +CurrentLocationButton.defaultProps = defaultProps; + +export default CurrentLocationButton; diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index fe220d442674..3e676b811c16 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -1,7 +1,7 @@ import _ from 'underscore'; -import React, {useMemo, useRef, useState} from 'react'; +import React, {useEffect, useMemo, useRef, useState} from 'react'; import PropTypes from 'prop-types'; -import {LogBox, ScrollView, View, Text, ActivityIndicator} from 'react-native'; +import {Keyboard, LogBox, ScrollView, View, Text, ActivityIndicator} from 'react-native'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; import lodashGet from 'lodash/get'; import compose from '../../libs/compose'; @@ -11,12 +11,16 @@ import themeColors from '../../styles/themes/default'; import TextInput from '../TextInput'; import * as ApiUtils from '../../libs/ApiUtils'; import * as GooglePlacesUtils from '../../libs/GooglePlacesUtils'; +import getCurrentPosition from '../../libs/getCurrentPosition'; import CONST from '../../CONST'; import * as StyleUtils from '../../styles/StyleUtils'; -import resetDisplayListViewBorderOnBlur from './resetDisplayListViewBorderOnBlur'; +import isCurrentTargetInsideContainer from './isCurrentTargetInsideContainer'; import variables from '../../styles/variables'; +import FullScreenLoadingIndicator from '../FullscreenLoadingIndicator'; +import LocationErrorMessage from '../LocationErrorMessage'; import {withNetwork} from '../OnyxProvider'; import networkPropTypes from '../networkPropTypes'; +import CurrentLocationButton from './CurrentLocationButton'; // The error that's being thrown below will be ignored until we fork the // react-native-google-places-autocomplete repo and replace the @@ -61,6 +65,9 @@ const propTypes = { /** Should address search be limited to results in the USA */ isLimitedToUSA: PropTypes.bool, + /** Shows a current location button in suggestion list */ + canUseCurrentLocation: PropTypes.bool, + /** A list of predefined places that can be shown when the user isn't searching for something */ predefinedPlaces: PropTypes.arrayOf( PropTypes.shape({ @@ -115,6 +122,7 @@ const defaultProps = { defaultValue: undefined, containerStyles: [], isLimitedToUSA: false, + canUseCurrentLocation: false, renamedInputKeys: { street: 'addressStreet', street2: 'addressStreet2', @@ -135,6 +143,11 @@ const defaultProps = { function AddressSearch(props) { const [displayListViewBorder, setDisplayListViewBorder] = useState(false); const [isTyping, setIsTyping] = useState(false); + const [isFocused, setIsFocused] = useState(false); + const [searchValue, setSearchValue] = useState(props.value || props.defaultValue || ''); + const [locationErrorCode, setLocationErrorCode] = useState(null); + const [isFetchingCurrentLocation, setIsFetchingCurrentLocation] = useState(false); + const shouldTriggerGeolocationCallbacks = useRef(true); const containerRef = useRef(); const query = useMemo( () => ({ @@ -144,6 +157,7 @@ function AddressSearch(props) { }), [props.preferredLocale, props.resultTypes, props.isLimitedToUSA], ); + const shouldShowCurrentLocationButton = props.canUseCurrentLocation && searchValue.trim().length === 0 && isFocused; const saveLocationDetails = (autocompleteData, details) => { const addressComponents = details.address_components; @@ -262,6 +276,72 @@ function AddressSearch(props) { props.onPress(values); }; + /** Gets the user's current location and registers success/error callbacks */ + const getCurrentLocation = () => { + if (isFetchingCurrentLocation) { + return; + } + + setIsTyping(false); + setIsFocused(false); + setDisplayListViewBorder(false); + setIsFetchingCurrentLocation(true); + + Keyboard.dismiss(); + + getCurrentPosition( + (successData) => { + if (!shouldTriggerGeolocationCallbacks.current) { + return; + } + + setIsFetchingCurrentLocation(false); + setLocationErrorCode(null); + + const location = { + lat: successData.coords.latitude, + lng: successData.coords.longitude, + address: CONST.YOUR_LOCATION_TEXT, + }; + props.onPress(location); + }, + (errorData) => { + if (!shouldTriggerGeolocationCallbacks.current) { + return; + } + + setIsFetchingCurrentLocation(false); + setLocationErrorCode(errorData.code); + }, + { + maximumAge: 0, // No cache, always get fresh location info + timeout: 5000, + }, + ); + }; + + const renderHeaderComponent = () => + props.predefinedPlaces.length > 0 && ( + <> + {/* This will show current location button in list if there are some recent destinations */} + {shouldShowCurrentLocationButton && ( + + )} + {!props.value && {props.translate('common.recentDestinations')}} + + ); + + // eslint-disable-next-line arrow-body-style + useEffect(() => { + return () => { + // If the component unmounts we don't want any of the callback for geolocation to run. + shouldTriggerGeolocationCallbacks.current = false; + }; + }, []); + return ( /* * The GooglePlacesAutocomplete component uses a VirtualizedList internally, @@ -269,119 +349,149 @@ function AddressSearch(props) { * To work around this, we wrap the GooglePlacesAutocomplete component with a horizontal ScrollView * that has scrolling disabled and would otherwise not be needed */ - - + - {props.translate('common.noResultsFound')} - ) - } - listLoaderComponent={ - - - - } - renderHeaderComponent={() => - !props.value && - props.predefinedPlaces && ( - {props.translate('common.recentDestinations')} - ) - } - onPress={(data, details) => { - saveLocationDetails(data, details); - setIsTyping(false); - - // After we select an option, we set displayListViewBorder to false to prevent UI flickering - setDisplayListViewBorder(false); - }} - query={query} - requestUrl={{ - useOnPlatform: 'all', - url: props.network.isOffline ? null : ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}), - }} - textInputProps={{ - InputComp: TextInput, - ref: (node) => { - if (!props.innerRef) { - return; - } - - if (_.isFunction(props.innerRef)) { - props.innerRef(node); - return; - } - - // eslint-disable-next-line no-param-reassign - props.innerRef.current = node; - }, - label: props.label, - containerStyles: props.containerStyles, - errorText: props.errorText, - hint: displayListViewBorder ? undefined : props.hint, - value: props.value, - defaultValue: props.defaultValue, - inputID: props.inputID, - shouldSaveDraft: props.shouldSaveDraft, - onBlur: (event) => { - resetDisplayListViewBorderOnBlur(setDisplayListViewBorder, event, containerRef); - props.onBlur(); - }, - autoComplete: 'off', - onInputChange: (text) => { - setIsTyping(true); - if (props.inputID) { - props.onInputChange(text); - } else { - props.onInputChange({street: text}); - } - - // If the text is empty and we have no predefined places, we set displayListViewBorder to false to prevent UI flickering - if (_.isEmpty(text) && _.isEmpty(props.predefinedPlaces)) { - setDisplayListViewBorder(false); - } - }, - maxLength: props.maxInputLength, - spellCheck: false, - }} - styles={{ - textInputContainer: [styles.flexColumn], - listView: [StyleUtils.getGoogleListViewStyle(displayListViewBorder), styles.overflowAuto, styles.borderLeft, styles.borderRight], - row: [styles.pv4, styles.ph3, styles.overflowAuto], - description: [styles.googleSearchText], - separator: [styles.googleSearchSeparator], - }} - numberOfLines={2} - isRowScrollable={false} - listHoverColor={themeColors.border} - listUnderlayColor={themeColors.buttonPressedBG} - onLayout={(event) => { - // We use the height of the element to determine if we should hide the border of the listView dropdown - // to prevent a lingering border when there are no address suggestions. - setDisplayListViewBorder(event.nativeEvent.layout.height > variables.googleEmptyListViewHeight); - }} - /> - - + + {props.translate('common.noResultsFound')} + ) + } + listLoaderComponent={ + + + + } + renderHeaderComponent={renderHeaderComponent} + onPress={(data, details) => { + saveLocationDetails(data, details); + setIsTyping(false); + + // After we select an option, we set displayListViewBorder to false to prevent UI flickering + setDisplayListViewBorder(false); + setIsFocused(false); + + // Clear location error code after address is selected + setLocationErrorCode(null); + }} + query={query} + requestUrl={{ + useOnPlatform: 'all', + url: props.network.isOffline ? null : ApiUtils.getCommandURL({command: 'Proxy_GooglePlaces&proxyUrl='}), + }} + textInputProps={{ + InputComp: TextInput, + ref: (node) => { + if (!props.innerRef) { + return; + } + + if (_.isFunction(props.innerRef)) { + props.innerRef(node); + return; + } + + // eslint-disable-next-line no-param-reassign + props.innerRef.current = node; + }, + label: props.label, + containerStyles: props.containerStyles, + errorText: props.errorText, + hint: + displayListViewBorder || (props.predefinedPlaces.length === 0 && shouldShowCurrentLocationButton) || (props.canUseCurrentLocation && isTyping) + ? undefined + : props.hint, + value: props.value, + defaultValue: props.defaultValue, + inputID: props.inputID, + shouldSaveDraft: props.shouldSaveDraft, + onFocus: () => { + setIsFocused(true); + }, + onBlur: (event) => { + if (!isCurrentTargetInsideContainer(event, containerRef)) { + setDisplayListViewBorder(false); + setIsFocused(false); + setIsTyping(false); + } + props.onBlur(); + }, + autoComplete: 'off', + onInputChange: (text) => { + setSearchValue(text); + setIsTyping(true); + if (props.inputID) { + props.onInputChange(text); + } else { + props.onInputChange({street: text}); + } + + // If the text is empty and we have no predefined places, we set displayListViewBorder to false to prevent UI flickering + if (_.isEmpty(text) && _.isEmpty(props.predefinedPlaces)) { + setDisplayListViewBorder(false); + } + }, + maxLength: props.maxInputLength, + spellCheck: false, + }} + styles={{ + textInputContainer: [styles.flexColumn], + listView: [StyleUtils.getGoogleListViewStyle(displayListViewBorder), styles.overflowAuto, styles.borderLeft, styles.borderRight, !isFocused && {height: 0}], + row: [styles.pv4, styles.ph3, styles.overflowAuto], + description: [styles.googleSearchText], + separator: [styles.googleSearchSeparator], + }} + numberOfLines={2} + isRowScrollable={false} + listHoverColor={themeColors.border} + listUnderlayColor={themeColors.buttonPressedBG} + onLayout={(event) => { + // We use the height of the element to determine if we should hide the border of the listView dropdown + // to prevent a lingering border when there are no address suggestions. + setDisplayListViewBorder(event.nativeEvent.layout.height > variables.googleEmptyListViewHeight); + }} + inbetweenCompo={ + // We want to show the current location button even if there are no recent destinations + props.predefinedPlaces.length === 0 && shouldShowCurrentLocationButton ? ( + + + + ) : ( + <> + ) + } + /> + setLocationErrorCode(null)} + locationErrorCode={locationErrorCode} + /> + + + {isFetchingCurrentLocation && } + ); } diff --git a/src/components/AddressSearch/isCurrentTargetInsideContainer.js b/src/components/AddressSearch/isCurrentTargetInsideContainer.js new file mode 100644 index 000000000000..18bfc10a8dcb --- /dev/null +++ b/src/components/AddressSearch/isCurrentTargetInsideContainer.js @@ -0,0 +1,8 @@ +function isCurrentTargetInsideContainer(event, containerRef) { + // The related target check is required here + // because without it when we select an option, the onBlur will still trigger setting displayListViewBorder to false + // it will make the auto complete component re-render before onPress is called making selecting an option not working. + return containerRef.current && event.target && containerRef.current.contains(event.relatedTarget); +} + +export default isCurrentTargetInsideContainer; diff --git a/src/components/AddressSearch/isCurrentTargetInsideContainer.native.js b/src/components/AddressSearch/isCurrentTargetInsideContainer.native.js new file mode 100644 index 000000000000..dbf0004b08d9 --- /dev/null +++ b/src/components/AddressSearch/isCurrentTargetInsideContainer.native.js @@ -0,0 +1,6 @@ +function isCurrentTargetInsideContainer() { + // The related target check is not required here because in native there is no race condition rendering like on the web + return false; +} + +export default isCurrentTargetInsideContainer; diff --git a/src/components/AddressSearch/resetDisplayListViewBorderOnBlur.js b/src/components/AddressSearch/resetDisplayListViewBorderOnBlur.js deleted file mode 100644 index def4da13a9a2..000000000000 --- a/src/components/AddressSearch/resetDisplayListViewBorderOnBlur.js +++ /dev/null @@ -1,11 +0,0 @@ -function resetDisplayListViewBorderOnBlur(setDisplayListViewBorder, event, containerRef) { - // The related target check is required here - // because without it when we select an option, the onBlur will still trigger setting displayListViewBorder to false - // it will make the auto complete component re-render before onPress is called making selecting an option not working. - if (containerRef.current && event.target && containerRef.current.contains(event.relatedTarget)) { - return; - } - setDisplayListViewBorder(false); -} - -export default resetDisplayListViewBorderOnBlur; diff --git a/src/components/AddressSearch/resetDisplayListViewBorderOnBlur.native.js b/src/components/AddressSearch/resetDisplayListViewBorderOnBlur.native.js deleted file mode 100644 index 7ae5a44cae71..000000000000 --- a/src/components/AddressSearch/resetDisplayListViewBorderOnBlur.native.js +++ /dev/null @@ -1,7 +0,0 @@ -function resetDisplayListViewBorderOnBlur(setDisplayListViewBorder) { - // The related target check is not required here because in native there is no race condition rendering like on the web - // onPress still called when cliking the option - setDisplayListViewBorder(false); -} - -export default resetDisplayListViewBorderOnBlur; diff --git a/src/components/AnonymousReportFooter.js b/src/components/AnonymousReportFooter.js index dd1a0864b0cf..43933210dc0b 100644 --- a/src/components/AnonymousReportFooter.js +++ b/src/components/AnonymousReportFooter.js @@ -36,6 +36,7 @@ function AnonymousReportFooter(props) { report={props.report} personalDetails={props.personalDetails} isAnonymous + shouldEnableDetailPageNavigation /> diff --git a/src/components/DatePicker/index.ios.js b/src/components/DatePicker/index.ios.js index 5d87636a9365..ef40aecb6f8c 100644 --- a/src/components/DatePicker/index.ios.js +++ b/src/components/DatePicker/index.ios.js @@ -1,147 +1,136 @@ -import React from 'react'; -// eslint-disable-next-line no-restricted-imports +import React, {useState, useRef, useCallback, useEffect} from 'react'; import {Button, View, Keyboard} from 'react-native'; import RNDatePicker from '@react-native-community/datetimepicker'; import moment from 'moment'; -import _ from 'underscore'; -import compose from '../../libs/compose'; +import isFunction from 'lodash/isFunction'; import TextInput from '../TextInput'; -import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import Popover from '../Popover'; import CONST from '../../CONST'; import styles from '../../styles/styles'; import themeColors from '../../styles/themes/default'; import {propTypes, defaultProps} from './datepickerPropTypes'; -import withKeyboardState, {keyboardStatePropTypes} from '../withKeyboardState'; +import useKeyboardState from '../../hooks/useKeyboardState'; +import useLocalize from '../../hooks/useLocalize'; -const datepickerPropTypes = { - ...propTypes, - ...withLocalizePropTypes, - ...keyboardStatePropTypes, -}; +function DatePicker({value, defaultValue, innerRef, onInputChange, preferredLocale, minDate, maxDate, label, disabled, onBlur, placeholder, containerStyles, errorText}) { + const [isPickerVisible, setIsPickerVisible] = useState(false); + const [selectedDate, setSelectedDate] = useState(moment(value || defaultValue).toDate()); + const {isKeyboardShown} = useKeyboardState(); + const {translate} = useLocalize(); + const initialValue = useRef(null); + const inputRef = useRef(null); -class DatePicker extends React.Component { - constructor(props) { - super(props); - - this.state = { - isPickerVisible: false, - selectedDate: props.value || props.defaultValue ? moment(props.value || props.defaultValue).toDate() : new Date(), - }; - - this.showPicker = this.showPicker.bind(this); - this.reset = this.reset.bind(this); - this.selectDate = this.selectDate.bind(this); - this.updateLocalDate = this.updateLocalDate.bind(this); - } - - showPicker() { - this.initialValue = this.state.selectedDate; + const showPicker = useCallback(() => { + initialValue.current = selectedDate; // Opens the popover only after the keyboard is hidden to avoid a "blinking" effect where the keyboard was on iOS // See https://github.com/Expensify/App/issues/14084 for more context - if (!this.props.isKeyboardShown) { - this.setState({isPickerVisible: true}); + if (!isKeyboardShown) { + setIsPickerVisible(true); return; } + const listener = Keyboard.addListener('keyboardDidHide', () => { - this.setState({isPickerVisible: true}); + setIsPickerVisible(true); listener.remove(); }); Keyboard.dismiss(); - } + }, [isKeyboardShown, selectedDate]); + + useEffect(() => { + if (!isFunction(innerRef)) { + return; + } + + const input = inputRef.current; + + if (input && input.focus && isFunction(input.focus)) { + innerRef({...input, focus: showPicker}); + return; + } + + innerRef(input); + }, [innerRef, showPicker]); /** * Reset the date spinner to the initial value */ - reset() { - this.setState({selectedDate: this.initialValue}); - } + const reset = () => { + setSelectedDate(initialValue.current); + }; /** * Accept the current spinner changes, close the spinner and propagate the change - * to the parent component (props.onInputChange) + * to the parent component (onInputChange) */ - selectDate() { - this.setState({isPickerVisible: false}); - const asMoment = moment(this.state.selectedDate, true); - this.props.onInputChange(asMoment.format(CONST.DATE.MOMENT_FORMAT_STRING)); - } + const selectDate = () => { + setIsPickerVisible(false); + const asMoment = moment(selectedDate, true); + onInputChange(asMoment.format(CONST.DATE.MOMENT_FORMAT_STRING)); + }; /** * @param {Event} event - * @param {Date} selectedDate + * @param {Date} date */ - updateLocalDate(event, selectedDate) { - this.setState({selectedDate}); - } + const updateLocalDate = (event, date) => { + setSelectedDate(date); + }; - render() { - const dateAsText = this.props.value || this.props.defaultValue ? moment(this.props.value || this.props.defaultValue).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; - return ( - <> - { - if (!_.isFunction(this.props.innerRef)) { - return; - } - if (el && el.focus && typeof el.focus === 'function') { - let inputRef = {...el}; - inputRef = {...inputRef, focus: this.showPicker}; - this.props.innerRef(inputRef); - return; - } + const dateAsText = value || defaultValue ? moment(value || defaultValue).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; - this.props.innerRef(el); - }} - /> - - -