Skip to content

RE1-T112 Fixes#324

Merged
ucswift merged 3 commits intomasterfrom
develop
Apr 11, 2026
Merged

RE1-T112 Fixes#324
ucswift merged 3 commits intomasterfrom
develop

Conversation

@ucswift
Copy link
Copy Markdown
Member

@ucswift ucswift commented Apr 11, 2026

Summary by CodeRabbit

  • Refactor

    • Subscription plan UI switched to native range + numeric inputs with simplified pricing display and streamlined checkout flows
    • Removed legacy slider styling and jQuery UI from client bundles
    • Yearly pricing tiers adjusted to change marginal slot calculations for larger plans
  • Bug Fixes

    • Removed an inline error-handling override that could interfere with the page loading indicator
    • Added server-side validation for subscription pack counts and improved client checkout error handling (connection errors, missing price IDs)

@request-info
Copy link
Copy Markdown

request-info bot commented Apr 11, 2026

Thanks for opening this, but we'd appreciate a little more information. Could you update it with more details?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

📝 Walkthrough

Walkthrough

Removed Pace.js and its inline error-handling patch; replaced a jQuery UI pricing slider and stylesheet with native range+number inputs, refactored client-side pricing and checkout JS (Stripe/Paddle), added server-side input validation for pack counts, and adjusted a yearly pricing tier’s marginal slot size.

Changes

Cohort / File(s) Summary
Pace removal
Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml, Web/Resgrid.Web/Startup.cs
Removed inline monkey-patch around Pace.bar.finish and removed lib/pace/pace.js from the internal JS bundle.
Pricing UI & Checkout refactor
Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml
Replaced jQuery UI slider and external pricing stylesheet with native input[type="range"] + numeric input; rewrote sync/update logic into updatePricing(), unified amount derivation from #amount-input, added AJAX .fail() error handling and explicit Paddle PriceId checks, and moved buy-button wiring to JS.
Pricing tier tweak
Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml
Increased marginalUserSlots for yearly tier 3 from 1000 → 5000, affecting yearly cost calculations.
Server-side validation
Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs
Added input validation on GetStripeSession and GetPaddleCheckout to reject count outside [1,200] with BadRequest.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Client as Subscription Page (Client JS)
    participant Server as Web Server (AJAX)
    participant Stripe as Stripe API
    participant Paddle as Paddle API

    User->>Client: adjust range / number
    Client->>Client: updatePricing() → recalc amount, packs, toggle buttons
    User->>Client: click Buy (Stripe or Paddle)
    Client->>Server: POST /create-checkout (amount, packs)
    alt Stripe flow
        Server->>Stripe: create checkout/session
        Stripe-->>Server: session/url
        Server-->>Client: { success:true, url }
        Client->>Stripe: redirectToCheckout(url)
    else Paddle flow
        Server->>Paddle: create/get checkout (PriceId or params)
        Paddle-->>Server: response or error
        Server-->>Client: { success:true, url } or { success:false, message }
        Client->>Paddle: open checkout URL or show error swal
    end
    Note over Client,Server: AJAX .fail() shows "Connection Error" swal
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'RE1-T112 Fixes' is vague and does not convey meaningful information about the actual changes made in the pull request. Replace with a descriptive title that summarizes the main changes, such as 'Remove Pace.js integration and refactor subscription pricing UI' or 'Simplify subscription pricing controls and add entity pack validation'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
Web/Resgrid.Web/Startup.cs (1)

394-407: Pace.js bundle removal looks correct.

Removing lib/pace/pace.js from /js/int-bundle.js is consistent with the stated cleanup and is safe in this segment. Optional follow-up: remove the stale IntelliSense reference in Web/Resgrid.Web/wwwroot/_references.js (Line 1) to keep references in sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Startup.cs` around lines 394 - 407, The int-bundle was
updated to remove lib/pace/pace.js, but the stale IntelliSense entry remains;
open _references.js and remove the reference to lib/pace/pace.js (the old Pace
IntelliSense line) so editor references match the
pipeline.AddJavaScriptBundle("/js/int-bundle.js") contents, then save and verify
no other Pace references remain in _references.js.
Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml (1)

132-143: Collapse duplicated Razor branches for buy buttons.

Both @if (Model.IsPaddleDepartment) branches render identical anchors. Removing the conditional here reduces noise and future drift risk.

Suggested refactor
-@if (Model.IsPaddleDepartment) {
-    <a id="buyYearlyButton" title="Buy Yearly" class="btn btn-primary" href="#" style="display:none;">Buy Yearly</a>
-} else {
-    <a id="buyYearlyButton" title="Buy Yearly" class="btn btn-primary" href="#" style="display:none;">Buy Yearly</a>
-}
+<a id="buyYearlyButton" title="Buy Yearly" class="btn btn-primary" href="#" style="display:none;">Buy Yearly</a>
...
-@if (Model.IsPaddleDepartment) {
-    <a id="buyMonthlyButton" title="Buy Monthly" class="btn btn-primary" href="#" style="display:none;">Buy Monthly</a>
-} else {
-    <a id="buyMonthlyButton" title="Buy Monthly" class="btn btn-primary" href="#" style="display:none;">Buy Monthly</a>
-}
+<a id="buyMonthlyButton" title="Buy Monthly" class="btn btn-primary" href="#" style="display:none;">Buy Monthly</a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml`
around lines 132 - 143, The two Razor conditional blocks that check
Model.IsPaddleDepartment and render identical anchors (the buyYearlyButton and
buyMonthlyButton anchors) should be collapsed into a single unconditional
rendering to remove duplicate branches; update SelectRegistrationPlan view to
remove both `@if` (Model.IsPaddleDepartment) { ... } else { ... } wrappers and
render the anchor tags for buyYearlyButton and buyMonthlyButton once each
(keeping their id/title/class/href/style attributes intact) so the buttons
remain functionally identical without duplicated code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml`:
- Around line 118-127: The markup appends a static ".00" while the server-side
output for the monthly/yearly values may already include decimals, causing
malformed output (e.g., "15.5.00"); update the rendering logic that writes the
numeric values for the elements with ids monthly-label and yearly-label so it
outputs a fixed two-decimal formatted string (e.g., using ToString("F2") or
String.Format("{0:F2}", value)) and remove the hard-coded ".00" in the markup so
the displayed price is always a single correctly formatted monetary value.
- Around line 190-200: The AJAX call to GetStripeSession and the subsequent
stripe.redirectToCheckout only use .done and don't handle network/API failures
or missing session IDs; add .fail(...) handlers to the $.ajax calls that surface
a user-friendly error (e.g., swal with a clear message) and add a fallback in
the .done handler to check for data and data.SessionId before calling
stripe.redirectToCheckout, showing an error if missing; also add error handling
on the promise returned by stripe.redirectToCheckout (handle result.error) and
apply the same changes to the other checkout block around lines 212-235 where
GetStripeSession and stripe.redirectToCheckout are used.
- Around line 102-109: Add explicit, associated labels for the range slider and
numeric input to improve accessibility: create <label> elements tied to the
existing ids "entity-slider" and "amount-input" (using for="entity-slider" and
for="amount-input"), place them next to the descriptive text ("Entities" /
"Users or Units sold in packs of 10"), and ensure each input also has an
aria-label or aria-describedby pointing to the descriptive text if visual
placement differs; update any JavaScript that queries by label text to use the
ids if needed.
- Around line 249-254: The yearly pricing tiers array pricingTiersYearly
contains a duplicated marginalUserSlots: 1000 entry (with costPerUser 15.0 and
10.0); update the second occurrence to marginalUserSlots: 5000 (to match the
monthly progression 5 → 100 → 1000 → 5000 → 999999999) in both
SelectRegistrationPlan and Index views so the yearly tier breakpoints are
consistent with monthly tiers.

---

Nitpick comments:
In `@Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml`:
- Around line 132-143: The two Razor conditional blocks that check
Model.IsPaddleDepartment and render identical anchors (the buyYearlyButton and
buyMonthlyButton anchors) should be collapsed into a single unconditional
rendering to remove duplicate branches; update SelectRegistrationPlan view to
remove both `@if` (Model.IsPaddleDepartment) { ... } else { ... } wrappers and
render the anchor tags for buyYearlyButton and buyMonthlyButton once each
(keeping their id/title/class/href/style attributes intact) so the buttons
remain functionally identical without duplicated code.

In `@Web/Resgrid.Web/Startup.cs`:
- Around line 394-407: The int-bundle was updated to remove lib/pace/pace.js,
but the stale IntelliSense entry remains; open _references.js and remove the
reference to lib/pace/pace.js (the old Pace IntelliSense line) so editor
references match the pipeline.AddJavaScriptBundle("/js/int-bundle.js") contents,
then save and verify no other Pace references remain in _references.js.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e7c1fdfc-f752-4105-82ce-09237e2966f4

📥 Commits

Reviewing files that changed from the base of the PR and between 2204858 and 92b257a.

📒 Files selected for processing (3)
  • Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml
  • Web/Resgrid.Web/Startup.cs
💤 Files with no reviewable changes (1)
  • Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml (1)

133-145: Remove redundant conditionals; both branches render identical markup.

The @if (Model.IsPaddleDepartment) blocks for both buttons produce the same HTML regardless of the condition. The checkout logic is already handled in JavaScript via the isPaddle variable (line 310). Simplify by removing the conditionals.

♻️ Suggested simplification
 <div class="col-sm-6 text-center">
-    `@if` (Model.IsPaddleDepartment) {
-        <a id="buyYearlyButton" title="Buy Yearly" class="btn btn-primary" href="#" style="display:none;">Buy Yearly</a>
-    } else {
-        <a id="buyYearlyButton" title="Buy Yearly" class="btn btn-primary" href="#" style="display:none;">Buy Yearly</a>
-    }
+    <a id="buyYearlyButton" title="Buy Yearly" class="btn btn-primary" href="#" style="display:none;">Buy Yearly</a>
 </div>
 <div class="col-sm-6 text-center">
-    `@if` (Model.IsPaddleDepartment) {
-        <a id="buyMonthlyButton" title="Buy Monthly" class="btn btn-primary" href="#" style="display:none;">Buy Monthly</a>
-    } else {
-        <a id="buyMonthlyButton" title="Buy Monthly" class="btn btn-primary" href="#" style="display:none;">Buy Monthly</a>
-    }
+    <a id="buyMonthlyButton" title="Buy Monthly" class="btn btn-primary" href="#" style="display:none;">Buy Monthly</a>
 </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml`
around lines 133 - 145, The two conditional blocks using
Model.IsPaddleDepartment render identical anchors; remove the redundant `@if`
branches and render each button once (keep the existing <a> elements with ids
buyYearlyButton and buyMonthlyButton and their attributes), relying on the
existing client-side checkout logic that uses the isPaddle variable (referenced
around line 310) to handle behavior; ensure you delete the conditional wrappers
around buyYearlyButton and buyMonthlyButton while leaving their markup and IDs
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml`:
- Around line 185-211: The GetStripeSession and GetPaddleCheckout actions in
SubscriptionController accept the query parameter count directly and need
server-side bounds checking to prevent bypassing the UI; in each action
(SubscriptionController.GetStripeSession and
SubscriptionController.GetPaddleCheckout) validate and normalize the incoming
count to ensure it is an integer >= 1 (reflecting the frontend requirement that
amount > 10 yields at least one pack), optionally enforce an upper limit (e.g.,
a MAX_PACKS constant) and return a BadRequest/400 when the value is invalid, or
clamp it before passing to the service layer so only safe values are forwarded.

---

Nitpick comments:
In `@Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml`:
- Around line 133-145: The two conditional blocks using Model.IsPaddleDepartment
render identical anchors; remove the redundant `@if` branches and render each
button once (keep the existing <a> elements with ids buyYearlyButton and
buyMonthlyButton and their attributes), relying on the existing client-side
checkout logic that uses the isPaddle variable (referenced around line 310) to
handle behavior; ensure you delete the conditional wrappers around
buyYearlyButton and buyMonthlyButton while leaving their markup and IDs
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7df0d929-ebe1-4318-84e1-85cd3b5db1c5

📥 Commits

Reviewing files that changed from the base of the PR and between 92b257a and f0b0813.

📒 Files selected for processing (2)
  • Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml

Comment on lines 185 to 211
function stripeCheckout(id) {
const amount = slider == 1 ? val : $("#amount").val();
var amount = parseInt(document.getElementById('amount-input').value) || 0;

if (amount && amount > 10) {
const packs = (amount / 10) - 1;
if (amount > 10) {
var packs = (amount / 10) - 1;

$.ajax({
url: resgrid.absoluteBaseUrl + '/User/Subscription/GetStripeSession?id=' + id + '&count=' + packs + (discountCode ? '&discountCode=' + encodeURIComponent(discountCode) : ''),
contentType: 'application/json',
type: 'GET'
}).done(function (data) {
if (data) {
if (data.SessionId) {
stripe.redirectToCheckout({
sessionId: data.SessionId
}).then(function (result) {
swal({
title: "Purchase Error",
text: "Error redirecting to Stripe for checkout. Stripe error: " + result.error.message,
icon: "error",
buttons: true,
dangerMode: false
});
});
}
if (data && data.SessionId) {
stripe.redirectToCheckout({ sessionId: data.SessionId }).then(function (result) {
if (result.error) {
swal({ title: "Purchase Error", text: "Error redirecting to Stripe: " + result.error.message, icon: "error", buttons: true, dangerMode: false });
}
});
} else {
swal({ title: "Checkout Error", text: "Unable to create a checkout session. Please try again.", icon: "error", buttons: true, dangerMode: false });
}
}).fail(function () {
swal({ title: "Connection Error", text: "Unable to reach the server. Please check your connection and try again.", icon: "error", buttons: true, dangerMode: false });
});
} else {
swal({
title: "Cannot Purchase",
text: "Please select more than 10 entities to purchase a plan.",
icon: "warning",
buttons: true,
dangerMode: false
});
swal({ title: "Cannot Purchase", text: "Please select more than 10 entities to purchase a plan.", icon: "warning", buttons: true, dangerMode: false });
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if GetStripeSession and GetPaddleCheckout validate the count parameter
rg -n -A10 'GetStripeSession|GetPaddleCheckout' --type=cs -g '!*Test*' | head -80

Repository: Resgrid/Core

Length of output: 3130


🏁 Script executed:

#!/bin/bash
# Find the implementations of CreateStripeSessionForSub and CreatePaddleCheckoutForSub
rg -n 'CreateStripeSessionForSub|CreatePaddleCheckoutForSub' --type=cs -g '!*Test*' | grep -E '(public|private|async).*CreateStripe|CreatePaddle'

Repository: Resgrid/Core

Length of output: 1792


🏁 Script executed:

#!/bin/bash
# Search for CreateStripeSessionForSub implementation with validation context
rg -n -B2 -A15 'CreateStripeSessionForSub.*{' --type=cs -g '!*Test*' | head -100

Repository: Resgrid/Core

Length of output: 175


🏁 Script executed:

#!/bin/bash
# Read the CreateStripeSessionForSub method implementation
sed -n '1151,1180p' Core/Resgrid.Services/SubscriptionsService.cs

Repository: Resgrid/Core

Length of output: 1845


🏁 Script executed:

#!/bin/bash
# Read the CreatePaddleCheckoutForSub method implementation
sed -n '1215,1240p' Core/Resgrid.Services/SubscriptionsService.cs

Repository: Resgrid/Core

Length of output: 1775


Add count parameter validation to GetStripeSession and GetPaddleCheckout actions.

The frontend correctly validates amount > 10 and calculates packs, but the count parameter is passed directly to the API without validation. Users can bypass the UI by crafting direct requests with arbitrary count values (negative, zero, or excessively large), potentially causing unintended charges or API errors.

Add bounds validation in SubscriptionController.GetStripeSession (line 688) and GetPaddleCheckout (line 726) before forwarding to the service layer. Ensure count reflects the frontend's constraint (minimum 1 pack from amount > 10).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml`
around lines 185 - 211, The GetStripeSession and GetPaddleCheckout actions in
SubscriptionController accept the query parameter count directly and need
server-side bounds checking to prevent bypassing the UI; in each action
(SubscriptionController.GetStripeSession and
SubscriptionController.GetPaddleCheckout) validate and normalize the incoming
count to ensure it is an integer >= 1 (reflecting the frontend requirement that
amount > 10 yields at least one pack), optionally enforce an upper limit (e.g.,
a MAX_PACKS constant) and return a BadRequest/400 when the value is invalid, or
clamp it before passing to the service layer so only safe values are forwarded.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs (1)

690-692: Consider centralizing pack-count bounds to avoid drift.

The same 1..200 constraint is duplicated in both endpoints. Prefer a shared constant or private validator helper so Stripe/Paddle paths cannot diverge accidentally.

♻️ Suggested refactor
+		private const int MinEntityPackCount = 1;
+		private const int MaxEntityPackCount = 200;
+
+		private static bool IsValidEntityPackCount(int count) =>
+			count >= MinEntityPackCount && count <= MaxEntityPackCount;
...
-			if (count < 1 || count > 200)
+			if (!IsValidEntityPackCount(count))
 				return BadRequest("Invalid entity pack count.");
...
-			if (count < 1 || count > 200)
+			if (!IsValidEntityPackCount(count))
 				return BadRequest("Invalid entity pack count.");

Also applies to: 731-733

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs` around
lines 690 - 692, The pack-count bounds (1..200) are duplicated in
SubscriptionController; introduce a single shared definition and use it in both
places: add private const int PackCountMin = 1 and private const int
PackCountMax = 200 (or a private bool ValidatePackCount(int count) method) in
the SubscriptionController class, then replace the literal checks (the if (count
< 1 || count > 200) in the Stripe and Paddle endpoints) to reference
PackCountMin/PackCountMax or call ValidatePackCount to centralize the constraint
and prevent drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs`:
- Around line 690-692: The pack-count bounds (1..200) are duplicated in
SubscriptionController; introduce a single shared definition and use it in both
places: add private const int PackCountMin = 1 and private const int
PackCountMax = 200 (or a private bool ValidatePackCount(int count) method) in
the SubscriptionController class, then replace the literal checks (the if (count
< 1 || count > 200) in the Stripe and Paddle endpoints) to reference
PackCountMin/PackCountMax or call ValidatePackCount to centralize the constraint
and prevent drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3681fdfe-858e-439f-9fa9-f7bcfe6c3070

📥 Commits

Reviewing files that changed from the base of the PR and between f0b0813 and 82482bf.

📒 Files selected for processing (1)
  • Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs

@ucswift
Copy link
Copy Markdown
Member Author

ucswift commented Apr 11, 2026

Approve

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is approved.

@ucswift ucswift merged commit db6c18f into master Apr 11, 2026
19 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant