Skip to content

fix(frontend): hydrate receipt display settings on layout load - #1202

Merged
khaira777 merged 3 commits into
mainfrom
fix/1188-frontend-receipt-settings-hydration
Oct 17, 2026
Merged

khaira777 merged 3 commits into
mainfrom
fix/1188-frontend-receipt-settings-hydration

Conversation

@khaira777

Copy link
Copy Markdown
Contributor

Intent

Fix issue #1188: Frontend browser-print receipt settings remain stale until Settings page is opened (ARCH-011b)

What Changed

  • Added useReceiptSettingsSync hook to the dashboard layout to hydrate receipt display and business settings into usePosSettingsStore on initial load and window focus.
  • Added hydrateReceiptSettings to usePosSettingsStore supporting camelCase and snake_case properties, refactored settings page hydration to use it, and eliminated redundant settings fetching in sidebar navigation.
  • Included bill_footer_message and bill_template in backend /api/settings/business response shape, with test coverage for store hydration and receipt print HTML generation.

Risk Assessment

✅ Low: The changes cleanly hydrate receipt display settings and layout state on initial load without touching terminal-local hardware printer settings or extending route mutation contracts.

Testing

Exercised backend build, frontend static build, focused test:receipt-settings-hydration unit/integration suite, and 6 live Playwright end-to-end browser scenarios against the running product (ports 3001, 3002, 3003). Verified cold-start POS layout hydration, receipt generation with custom address, phone, tax ID, and footer note without navigating to Settings, dynamic window-focus sync, terminal-local hardware printer preference preservation, permission boundary gating without 403 errors, single-request GET /api/settings/business payload contract, and adversarial empty-string boundary normalization. Visual screenshots and HTML artifacts were generated directly in the evidence directory. All checks passed with verdict 'go'.

  • Live validation: ✅ go - 6 of 6 scenarios driven live against the product
Scenario Result Live Evidence
Cold-start login to POS hydrates receipt settings and renders customized business address, phone, tax ID, and footer on browser receipt without visiting Settings ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/receipt-cold-start-hydrated.png
Dynamic sync via window focus event updates store receipt settings without page reload ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/pos-dynamic-focus-synced.png
Terminal-local hardware printer settings remain preserved during receipt hydration ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/terminal-printer-settings-preserved.json
Staff role without settings permission gracefully skips settings hydration without 403 ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/staff-no-settings-permission.png
Backend GET /api/settings/business returns unified receipt display configuration including bill_footer_message and bill_template ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/backend-settings-business.json
Adversarial boundary check: Partial or empty backend settings normalize cleanly without rendering undefined ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/receipt-partial-boundary.png

receipt-cold-start-hydrated.png

Evidence: receipt-cold-start-hydrated.png
Rendered browser receipt showing hydrated business address ('742 Evergreen Terrace, Springfield, OR'), phone ('+15415550199'), tax ID ('OR-TAX-998877'), and custom footer note ('Thank you for dining at FloCafe! Present this receipt for 10% off next visit.') without visiting the Settings page.
Evidence: receipt-cold-start-hydrated.html

Complete emitted HTML document for browser-print receipt with hydrated store headers and footer.

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>Bill # INV-20261017-0001</title>
  <style>
    
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: -apple-system, 'Segoe UI', Tahoma, 'Noto Naskh Arabic', 'Noto Sans Bengali', 'Vrinda', 'Bangla Sangam MN', 'Noto Sans Devanagari', 'Nirmala UI', 'Kohinoor Devanagari', 'Devanagari Sangam MN', 'Noto Sans Thai', 'Leelawadee UI', Thonburi, 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Hiragino Sans', 'Yu Gothic', 'Meiryo', 'Noto Sans CJK JP', 'Noto Sans JP', 'Helvetica Neue', Arial, sans-serif; font-size: 12px; line-height: 1.4; color: #333; }
    body:lang(zh-TW) { font-family: -apple-system, 'Segoe UI', Tahoma, 'Noto Naskh Arabic', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans CJK TC', 'Noto Sans TC', 'Helvetica Neue', Arial, sans-serif; }
    .bill-container { max-width: 100%; margin: 0 auto; }
    .reprint-banner { text-align: center; font-size: 22px; font-weight: bold; letter-spacing: 2px; color: #c00; border: 3px solid #c00; padding: 6px; margin-bottom: 15px; }
    .online-order-banner { text-align: center; font-size: 18px; font-weight: bold; letter-spacing: 1px; border: 2px solid #333; padding: 6px; margin-bottom: 15px; }
    .online-order-banner .online-order-detail { font-size: 13px; font-weight: normal; letter-spacing: normal; margin-top: 2px; }
    .header { text-align: center; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #ccc; }
    .receipt-logo { max-width: 80%; max-height: 120px; object-fit: contain; margin: 0 auto 8px auto; display: block; }
    .header h1 { font-size: 24px; margin-bottom: 5px; }
    .bill-details { margin-bottom: 15px; }
    .bill-details table { width: 100%; }
    .items-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
    .items-table th, .items-table td { padding: 8px; border-bottom: 1px solid #eee; text-align: start; }
    .items-table th { background: #f5f5f5; font-weight: bold; }
    .tax-table, .payments-table { width: 50%; margin-inline-start: 50%; border-collapse: collapse; margin-bottom: 15px; }
    .tax-table th, .tax-table td, .payments-table th, .payments-table td { padding: 6px 8px; }
    .tax-table th, .payments-table th { background: #f9f9f9; text-align: start; }
    .totals-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
    .totals-table td { padding: 6px 8px; }
    .total-row { border-top: 2px solid #333; font-size: 16px; }
    .footer { text-align: center; margin-top: 30px; padding-top: 15px; border-top: 1px solid #ccc; }
    .powered-by { font-size: 10px; margin-top: 8px; color: #555; }
    .text-end { text-align: end !important; }
    .num { unicode-bidi: isolate; white-space: nowrap; }
    .ltr { direction: ltr; unicode-bidi: isolate; }
    .text-muted { color: #666; }
    .text-italic { font-style: italic; color: #888; }
  
        .bill-container { padding: 5px; max-width: 58mm; font-size: 10px; }
        .header h1 { font-size: 14px; }
        .items-table th, .items-table td, .tax-table td, .totals-table td, .payments-table td { padding: 2px 4px; }
      
    @media print {
      .no-print { display: none !important; }
      body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    }
  </style>
</head>
<body>
  <div class="bill-container">
    
    
    <!-- Header -->
    <div class="header">
      
      <h1>Store</h1>
      <p>742 Evergreen Terrace, Springfield, OR</p>
      <p>Ph: +15415550199</p>
      <p>Tax ID: OR-TAX-998877</p>
    </div>

    <!-- Bill Details -->
    <div class="bill-details">
      <table>
        <tr>
          <td><strong>Bill #</strong> INV-20261017-0001</td>
          <td class="text-end"><strong>Date</strong> Oct 17, 2569 BE, 06:18 PM</td>
        </tr>
        
        
        
        
        
      </table>
    </div>

    <!-- Items Table -->
    <table class="items-table">
      <thead>
        <tr>
          <th>Item</th>
          <th class="text-end">Qty</th>
          <th class="text-end">Rate</th>
          <th class="text-end">Amount</th>
        </tr>
      </thead>
      <tbody>
        
          <tr>
            <td>
              E2E Coffee
              
              
            </td>
            <td class="text-end num">2</td>
            <td class="text-end num">฿60.00</td>
            <td class="text-end num">฿128.40</td>
          </tr>
        
      </tbody>
    </table>

    <!-- Tax Breakdown -->
    
    <table class="tax-table">
      <thead>
        <tr><th colspan="2">Tax Details</th></tr>
      </thead>
      <tbody>
        
          <tr><td>Tax @7%</td><td class="text-end num">฿8.40</td></tr>
        
      </tbody>
    </table>
    

    <!-- Totals -->
    <table class="totals-table">
      
      
      <tr><td>Subtotal</td><td class="text-end num">฿120.00</td></tr>
      
      
      
      
      
      
      <tr class="total-row"><td><strong>Grand Total</strong></td><td class="text-end num"><strong>฿128.40</strong></td></tr>
      
      
      
    </table>

    <!-- Payments -->
    

    <!-- Footer -->
    <div class="footer">
      <p>Thank you for dining at FloCafe! Present this receipt for 10% off next visit.</p>
      <p>Tax included where applicable</p>
      <p class="powered-by">Powered by FloPOS (flopos.com)</p>
    </div>
  </div>

  <div class="no-print" style="text-align:center;margin-top:20px;">
    <button onclick="window.print()" style="padding:10px 20px;font-size:16px;cursor:pointer;">Print Bill</button>
  </div>
</body>
</html>
  
![receipt-partial-boundary.png](https://github.com/user-attachments/assets/730b4e3a-c265-49fa-8d5f-3dea0450bf7d)
Evidence: receipt-partial-boundary.png
Rendered browser receipt verifying that empty backend fields normalize cleanly without emitting literal 'undefined' or 'null'.
Evidence: receipt-partial-boundary.html

Rendered HTML document for receipt with empty/partial backend settings.

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>Bill # INV-20261017-0002</title>
  <style>
    
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: -apple-system, 'Segoe UI', Tahoma, 'Noto Naskh Arabic', 'Noto Sans Bengali', 'Vrinda', 'Bangla Sangam MN', 'Noto Sans Devanagari', 'Nirmala UI', 'Kohinoor Devanagari', 'Devanagari Sangam MN', 'Noto Sans Thai', 'Leelawadee UI', Thonburi, 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Hiragino Sans', 'Yu Gothic', 'Meiryo', 'Noto Sans CJK JP', 'Noto Sans JP', 'Helvetica Neue', Arial, sans-serif; font-size: 12px; line-height: 1.4; color: #333; }
    body:lang(zh-TW) { font-family: -apple-system, 'Segoe UI', Tahoma, 'Noto Naskh Arabic', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans CJK TC', 'Noto Sans TC', 'Helvetica Neue', Arial, sans-serif; }
    .bill-container { max-width: 100%; margin: 0 auto; }
    .reprint-banner { text-align: center; font-size: 22px; font-weight: bold; letter-spacing: 2px; color: #c00; border: 3px solid #c00; padding: 6px; margin-bottom: 15px; }
    .online-order-banner { text-align: center; font-size: 18px; font-weight: bold; letter-spacing: 1px; border: 2px solid #333; padding: 6px; margin-bottom: 15px; }
    .online-order-banner .online-order-detail { font-size: 13px; font-weight: normal; letter-spacing: normal; margin-top: 2px; }
    .header { text-align: center; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #ccc; }
    .receipt-logo { max-width: 80%; max-height: 120px; object-fit: contain; margin: 0 auto 8px auto; display: block; }
    .header h1 { font-size: 24px; margin-bottom: 5px; }
    .bill-details { margin-bottom: 15px; }
    .bill-details table { width: 100%; }
    .items-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
    .items-table th, .items-table td { padding: 8px; border-bottom: 1px solid #eee; text-align: start; }
    .items-table th { background: #f5f5f5; font-weight: bold; }
    .tax-table, .payments-table { width: 50%; margin-inline-start: 50%; border-collapse: collapse; margin-bottom: 15px; }
    .tax-table th, .tax-table td, .payments-table th, .payments-table td { padding: 6px 8px; }
    .tax-table th, .payments-table th { background: #f9f9f9; text-align: start; }
    .totals-table { width: 100%; border-collapse: collapse; margin-bottom: 15px; }
    .totals-table td { padding: 6px 8px; }
    .total-row { border-top: 2px solid #333; font-size: 16px; }
    .footer { text-align: center; margin-top: 30px; padding-top: 15px; border-top: 1px solid #ccc; }
    .powered-by { font-size: 10px; margin-top: 8px; color: #555; }
    .text-end { text-align: end !important; }
    .num { unicode-bidi: isolate; white-space: nowrap; }
    .ltr { direction: ltr; unicode-bidi: isolate; }
    .text-muted { color: #666; }
    .text-italic { font-style: italic; color: #888; }
  
        .bill-container { padding: 5px; max-width: 58mm; font-size: 10px; }
        .header h1 { font-size: 14px; }
        .items-table th, .items-table td, .tax-table td, .totals-table td, .payments-table td { padding: 2px 4px; }
      
    @media print {
      .no-print { display: none !important; }
      body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    }
  </style>
</head>
<body>
  <div class="bill-container">
    
    
    <!-- Header -->
    <div class="header">
      
      <h1>Store</h1>
      
      
      
    </div>

    <!-- Bill Details -->
    <div class="bill-details">
      <table>
        <tr>
          <td><strong>Bill #</strong> INV-20261017-0002</td>
          <td class="text-end"><strong>Date</strong> Oct 17, 2569 BE, 06:18 PM</td>
        </tr>
        
        
        
        
        
      </table>
    </div>

    <!-- Items Table -->
    <table class="items-table">
      <thead>
        <tr>
          <th>Item</th>
          <th class="text-end">Qty</th>
          <th class="text-end">Rate</th>
          <th class="text-end">Amount</th>
        </tr>
      </thead>
      <tbody>
        
          <tr>
            <td>
              E2E Coffee
              
              
            </td>
            <td class="text-end num">1</td>
            <td class="text-end num">฿60.00</td>
            <td class="text-end num">฿64.20</td>
          </tr>
        
      </tbody>
    </table>

    <!-- Tax Breakdown -->
    
    <table class="tax-table">
      <thead>
        <tr><th colspan="2">Tax Details</th></tr>
      </thead>
      <tbody>
        
          <tr><td>Tax @7%</td><td class="text-end num">฿4.20</td></tr>
        
      </tbody>
    </table>
    

    <!-- Totals -->
    <table class="totals-table">
      
      
      <tr><td>Subtotal</td><td class="text-end num">฿60.00</td></tr>
      
      
      
      
      
      
      <tr class="total-row"><td><strong>Grand Total</strong></td><td class="text-end num"><strong>฿64.20</strong></td></tr>
      
      
      
    </table>

    <!-- Payments -->
    

    <!-- Footer -->
    <div class="footer">
      <p>Thank you for your visit!</p>
      <p>Tax included where applicable</p>
      <p class="powered-by">Powered by FloPOS (flopos.com)</p>
    </div>
  </div>

  <div class="no-print" style="text-align:center;margin-top:20px;">
    <button onclick="window.print()" style="padding:10px 20px;font-size:16px;cursor:pointer;">Print Bill</button>
  </div>
</body>
</html>
  
![pos-dynamic-focus-synced.png](https://github.com/user-attachments/assets/b273f269-3d30-4953-8d60-e64a4373a38f)
Evidence: pos-dynamic-focus-synced.png
POS dashboard screenshot after window focus event dynamically re-hydrated updated store settings.
Evidence: dynamic-focus-sync-state.json

Client Zustand store snapshot after window focus event dynamically updated the footer note and address.

{
  "showProductImages": true,
  "customerMandatory": false,
  "enforcePhoneLength": false,
  "billingType": "prepaid",
  "tablesRequired": false,
  "language": "en",
  "printerPaperSize": "thermal58",
  "printerEnabled": false,
  "printerPrintMode": "escpos",
  "autoPrintKot": false,
  "autoPrintBill": false,
  "whatsappShareEnabled": true,
  "defaultPrintMode": "thermal",
  "billTemplate": "classic",
  "billTemplateSource": "core",
  "billFooterMessage": "Holiday Season Special: Free dessert on orders over $50!",
  "billTaxRegistrationNumber": "OR-TAX-998877",
  "billAddress": "999 Innovation Parkway, Silicon Valley, CA",
  "billPhone": "+15415550199",
  "receiptLogo": "",
  "receiptLogoRaster": "",
  "billShowLogo": false,
  "billShowName": true,
  "billShowAddress": true,
  "billShowPhone": true,
  "billShowTaxId": true,
  "billShowTaxBreakdown": true,
  "billShowCustomerName": true,
  "billShowCustomerPhone": true,
  "billDeliveryShowCustomerPhoneAlways": true,
  "billShowTableNumber": true,
  "printerUseUnicode": false,
  "printerArabicShaping": false,
  "printerTrimDecimals": false,
  "kdsEnabled": true,
  "kotPrintingEnabled": true
}
Evidence: terminal-printer-settings-preserved.json

Verified JSON state demonstrating terminal-local hardware printer settings (paper size 'thermal58', printerUseUnicode true, autoPrintBill false) remain preserved during layout receipt hydration.

{
  "printerPaperSize": "thermal58",
  "printerUseUnicode": true,
  "autoPrintBill": false,
  "billAddress": "999 Innovation Parkway, Silicon Valley, CA",
  "billFooterMessage": "Holiday Season Special: Free dessert on orders over $50!"
}
![staff-no-settings-permission.png](https://github.com/user-attachments/assets/d131866f-171e-4032-be18-7d1b4ee99b44)
Evidence: staff-no-settings-permission.png
Dashboard screen showing that restricted staff role without settings.view bypasses settings hydration without any 403 Forbidden errors.
Evidence: backend-settings-business.json

GET /api/settings/business response payload verifying unified return of bill_footer_message, bill_template, and business details.

{
  "business_name": "",
  "timezone": "Asia/Bangkok",
  "business_day_start_time": "00:00",
  "currency": "THB",
  "country": "TH",
  "language": "en",
  "tax_registration_number": "OR-TAX-998877",
  "state_code": "",
  "business_address": "999 Innovation Parkway, Silicon Valley, CA",
  "business_phone": "+15415550199",
  "instagram_handle": "",
  "billing_type": "prepaid",
  "tables_required": false,
  "tax_registered": false,
  "bill_show_name": true,
  "bill_show_address": true,
  "bill_show_phone": true,
  "bill_show_tax_id": true,
  "bill_show_tax_breakdown": true,
  "bill_show_customer_name": true,
  "bill_show_customer_phone": true,
  "bill_delivery_show_customer_phone_always": true,
  "bill_show_table_number": true,
  "bill_show_logo": false,
  "receipt_logo": "",
  "receipt_logo_raster": "",
  "bill_footer_message": "Holiday Season Special: Free dessert on orders over $50!",
  "bill_template": "{\"source\":\"core\",\"id\":\"classic\"}",
  "currency_display": "rial",
  "number_digits": "locale",
  "calendar": "locale",
  "tax_id_format": null
}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 3 issues found → auto-fixed ✅
  • ⚠️ frontend/src/hooks/useReceiptSettingsSync.ts:12 - frontend/src/hooks/useReceiptSettingsSync.ts:12 exports an uncalled standalone async function syncReceiptSettings() that duplicates the hook's fetching logic but has 0 call sites across the codebase. No requirement in User Intent (issue Frontend browser-print receipt settings remain stale until Settings page is opened (ARCH-011b) #1188) requires this extra export. Recommend removing syncReceiptSettings as an unrequired component.
  • ⚠️ frontend/src/store/pos-settings.ts:131 - frontend/src/store/pos-settings.ts:131-148 and 299-317 define multi-tier fallback aliases (taxRegistrationNumber and billTaxRegistrationNumber at line 300; billAddress and businessAddress at line 306; billPhone and businessPhone at line 310; billFooterMessage at line 314) that are neither returned by the backend GET /api/settings/business endpoint nor passed by settings forms. No requirement in User Intent (issue Frontend browser-print receipt settings remain stale until Settings page is opened (ARCH-011b) #1188) requires supporting these uncalled alias chains. Recommend narrowing ReceiptSettingsHydrationData and hydrateReceiptSettings to the canonical backend keys.
  • ⚠️ main/routes/settings.ts:310 - main/routes/settings.ts:310 and 459-460 introduce a second acceptance path for updating bill_footer_message via PUT /api/settings/business in addition to the existing PUT /api/settings/bill_footer_message endpoint used by the settings page. No requirement in User Intent (issue Frontend browser-print receipt settings remain stale until Settings page is opened (ARCH-011b) #1188, which is about layout hydration) needs an additional write path on the business settings route. Recommend removing bill_footer_message from PUT /api/settings/business to keep the route mutation contract strictly minimal.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 6 of 6 scenarios driven live against the product
Scenario Result Live Evidence
Cold-start login to POS hydrates receipt settings and renders customized business address, phone, tax ID, and footer on browser receipt without visiting Settings ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/receipt-cold-start-hydrated.png
Dynamic sync via window focus event updates store receipt settings without page reload ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/pos-dynamic-focus-synced.png
Terminal-local hardware printer settings remain preserved during receipt hydration ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/terminal-printer-settings-preserved.json
Staff role without settings permission gracefully skips settings hydration without 403 ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/staff-no-settings-permission.png
Backend GET /api/settings/business returns unified receipt display configuration including bill_footer_message and bill_template ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/backend-settings-business.json
Adversarial boundary check: Partial or empty backend settings normalize cleanly without rendering undefined ✅ pass live ~/.no-mistakes/evidence/01M54R1J4BWGH0F7020DA2EC9F/receipt-partial-boundary.png
  • npm run build
  • npm run build:frontend
  • npm run test:receipt-settings-hydration
  • npx playwright test e2e/receipt-settings-hydration.spec.ts (6 live end-to-end scenarios)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: FreeOpenSourcePOS/FloCafe/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9cb8da98-00b9-44d1-9a1d-6ac5806d6f19

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

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