Skip to content

feat(statements): periodic statements playground under Cards - #1032

Closed
jaymantri wants to merge 59 commits into
mainfrom
ajay/grid-statements-playground
Closed

jaymantri wants to merge 59 commits into
mainfrom
ajay/grid-statements-playground

Conversation

@jaymantri

@jaymantri jaymantri commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

What

Adds the Statements playground under Cards. It builds a consumer or commercial periodic statement from sample Grid customer, account, and transaction data, and shows the exact API calls behind it.
The app is a re-fork of the Cards playground chrome, with the Wallet AppShell for the device frame. It exports the statement as a one-page PDF or a self-contained HTML file.
The docs page embeds the production app at /cards/statements.

Try it

What's in the diff

  • components/grid-statements-demo/ — new Next.js app (port 4003). Statement model, fixtures, and API samples live in src/statement/. scripts/check-chrome-parity.mjs checks byte parity with Cards and Wallet.
  • mintlify/cards/statements.mdx + mintlify/snippets/cards/statements-demo-embed.mdx — the docs page and its iframe snippet.
  • mintlify/docs.json — new Statements group after Transactions; head script adds /cards/statements to the ls-page-playground class.
  • mintlify/sidebar-toggle.js — same /cards/statements route for the playground class.
  • components/grid-statements-demo/README.md — develop, parity, deploy, embed, and data contract.

No OpenAPI changes. No changes to Cards, Wallet, or Flow Builder.

Parity with Cards/Wallet

npm run check:chrome compares every copied file to Cards (or Wallet) by bytes. It passes at 194e8552.

Byte-equal with Cards: next.config.mjs, public/fonts, src/app/fonts, src/app/globals.scss, src/apps/shared/typography, src/components/ApiPanel, ColumnResizeHandle, liquid-glass/squircle.ts, GridWordmark.tsx, PanelHeader, SectionDivider, ThemeSync.tsx, Tooltip, src/data/actions.ts, flow.ts, src/hooks/useColumnResize.ts, useNowTick.ts, useTheme.ts, useThemeMode.ts, src/lib/apiCodeFormat.tsx, easing.ts, formatRelativeTime.ts, groupApiEntries.ts, layout.ts, src/styles/breakpoints.scss.

Byte-equal with Wallet: src/apps/shared/AppShell/* except the two adapted files below.

Adapted, with the exact transform pinned in scripts/check-chrome-parity.mjs:

File Source Reason
src/components/PlaygroundIntro/PlaygroundIntro.tsx Cards Statements product copy
src/app/layout.tsx Cards Statements metadata and production URL; pre-paint script unchanged
src/app/page.module.scss Cards Adds print-only rules that hide playground chrome
src/data/flowIcons.tsx Cards Adds the Load statement icon
src/apps/shared/AppShell/AppShell.tsx Wallet Accepts a device preset (phone or iPhone Duo landscape)
src/apps/shared/AppShell/usePhoneFitScale.ts Wallet Duo outer/screen dimensions
src/components/ShareSheet/StageShareButton.* Cards Export/Cancel labels and fixed bottom placement

Deploy notes

Vercel project grid-statements-demo, root directory components/grid-statements-demo. vercel.json skips builds for commits that do not touch this directory. After merge, connect the project to GitHub with production branch main.

Held

A periodic statements guide page is not part of this PR. Only the playground ships.

Review guidance

Squash-merge. The branch has 59 commits of iterative polish; the final tree is what matters.

Verification at 194e8552

  • npm test: 72 tests pass in 18 files.
  • npm run check:chrome: passes.
  • npm run build and npx tsc --noEmit: pass on Node 22.
  • make lint: 0 errors.

jaymantri and others added 7 commits September 17, 2026 15:39
Add the standalone statement renderer and its focused tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
Set an explicit TypeScript base URL for clean remote builds.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add the Mintlify page, restore its Overview navigation entry, and point the embed at the public playground project.

Co-authored-by: Cursor <cursoragent@cursor.com>
Fork the Cards chrome and layout contract into Statements.
Drive the statement, API samples, and totals from one typed model.
Reuse Cards presets and real Grid OpenAPI operation IDs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Document the cp-based chrome port contract.
Add byte-parity, behavior, and visual audit scripts.
Keep generated browser evidence out of Git.

Co-authored-by: Cursor <cursoragent@cursor.com>
Map Mintlify preview branches to the Statements Vercel preview host.
Pass embed, theme, and navigation state in the iframe URL.

Co-authored-by: Cursor <cursoragent@cursor.com>
State that Grid account balances are current data and that the platform stores period balances.
Generate distinct transaction identities for each statement period and pin the rebuild behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
@mintlify

mintlify Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
Grid 🟢 Ready View Preview Sep 23, 2026, 7:52 PM

@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
grid-statements-demo Ready Ready Preview Sep 23, 2026 8:00pm UTC
grid-statements-playground Ready Ready Preview Sep 23, 2026 8:00pm UTC
3 Skipped Deployments
Project Deployment Actions Updated
grid-cards-demo Ignored Ignored Preview Sep 23, 2026 8:00pm UTC
grid-flow-builder Ignored Ignored Preview Sep 23, 2026 8:00pm UTC
grid-wallet-demo Ignored Ignored Preview Sep 23, 2026 8:00pm UTC

Request Review

Rename the Vercel project to grid-statements-demo to match the Cards
and Wallet projects and the components directory. Mirror the Cards
embed snippet so Mintlify previews use the branch-derived Vercel host.
Update the layout metadata base and the chrome parity exception.
Document the 22-character branch name budget.

Co-authored-by: Cursor <cursoragent@cursor.com>
Reg E periodic statements cover the Lead Bank prepaid account behind
the card program, and card purchases are the statement line items, so
the playground belongs in the Cards tab next to the Cards playground.

- global-accounts/periodic-statements-playground -> cards/statements
- snippet moved to snippets/cards/statements-demo-embed.mdx
- redirect added for the old path
- ls-page-playground path lists updated (head raw + sidebar-toggle.js)
- sidebar label is the single word "Statements" to avoid wrapping

Co-authored-by: Cursor <cursoragent@cursor.com>
@jaymantri

jaymantri commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

Docs nav: the statements playground now lives in the Cards tab (commit 0e88008).

  • New path: /cards/statements (mintlify/cards/statements.mdx), listed in Cards → Overview right after the Cards Playground.
  • Redirect: /global-accounts/periodic-statements-playground/cards/statements (verified 307 locally).
  • Sidebar label: Statements — one word, so it does not wrap in the sidebar.
  • Snippet moved to mintlify/snippets/cards/statements-demo-embed.mdx; ls-page-playground path lists in docs.json head and sidebar-toggle.js updated.

Why Cards: Reg E periodic statements cover the Lead Bank prepaid account behind the card program, and card purchases are the statement line items.

Update (b45defc): both statements pages moved out of Overview into a new Cards sidebar group, Statements, placed directly after Transactions. Cards group order: Overview → Onboarding → Card management → Transactions → Statements → Card design → Platform tools. Pages in the group: cards/statements-guide ("Periodic statements", icon IconNewspaper2.svg) then cards/statements ("Playground", icon receipt-check.svg). Icons are unique within the Cards tab; the Cards Playground keeps phone.svg. Paths and the redirect are unchanged. Verified with mint 4.2.284 on node 22: both pages 200, mint broken-links reports no hits for either page, labels render on one line at 1440 wide.

Update (ab8b389): the guide page cards/statements-guide is held and removed from the branch. The Statements group stays after Transactions with one page, cards/statements ("Playground", receipt-check.svg). The guide is out of scope for this PR.

jaymantri and others added 8 commits September 18, 2026 13:21
Add four typed statement color tokens, WCAG checks, print colors, and Cards-style controls.

Co-authored-by: Cursor <cursoragent@cursor.com>
Parameterize the Cards AppShell shape for standard iPhone and iPhone Duo Mail views.

Co-authored-by: Cursor <cursoragent@cursor.com>
Refresh calls for preset, account, and period changes without reacting to brand edits.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add a two-action export preview, print isolation, and self-contained HTML downloads.

Co-authored-by: Cursor <cursoragent@cursor.com>
Lock the Duo outer preset, finish control and accessibility polish, and enforce browser audit evidence.

Co-authored-by: Cursor <cursoragent@cursor.com>
Match the official open-body and inner-display proportions, and remove the invented camera treatment from the device preset.

Co-authored-by: Cursor <cursoragent@cursor.com>
Assert both the official open-body ratio and inner-display ratio.

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the portrait camera offset so the landscape status bar uses the shared safe inset.

Co-authored-by: Cursor <cursoragent@cursor.com>
jaymantri and others added 3 commits September 18, 2026 14:28
Use the measured closed outer geometry required for the Mail preview.
Restore the asymmetric hinge bezel, corner radii, and camera hole.

Co-authored-by: Cursor <cursoragent@cursor.com>
Restore the locked open-device geometry after a concurrent commit changed the view to the closed outer display. Assert both official ratios and use the neutral landscape safe inset.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the Mail preview on the measured closed outer display.
Restore the asymmetric hinge bezel, corner radii, and camera hole.

Co-authored-by: Cursor <cursoragent@cursor.com>
jaymantri and others added 9 commits September 22, 2026 12:09
Move the live brand into a full-height sidebar with empty content
and footer zones. Keep the main header empty and the statement
scrolling inside the right column.

Pin the shared direct and Export frame across desktop sizes and themes.

Co-authored-by: Cursor <cursoragent@cursor.com>
- simplify the desktop shell and recess its document surface
- make the mobile statement full bleed with scroll elevation
- remove the redundant commercial footer divider
- audit consumer and commercial screen, HTML, and PDF output

Co-authored-by: Cursor <cursoragent@cursor.com>
Empty the desktop rail and sink the whole frame so only the statement
card lifts. Bleed the mobile document under a scrolled header, drop the
spare Commercial footer hairline, and match Cards API rail copy.

Co-authored-by: Cursor <cursoragent@cursor.com>
Compare every chrome surface to the token-derived sunken mix, not the
primary background, so the lifted statement card is the only exception.

Co-authored-by: Cursor <cursoragent@cursor.com>
The frame surfaces now share the token-derived sunken mix. Keep the
document on primary background as the only lifted element.

Co-authored-by: Cursor <cursoragent@cursor.com>
Validate primary chrome around the token-derived recessed document area.

Co-authored-by: Cursor <cursoragent@cursor.com>
Apply the recessed token only behind the statement document.

Co-authored-by: Cursor <cursoragent@cursor.com>
The playground already groups Load statement under one divider; tests must reject the old explanatory sentence and require unified sunken frame surfaces.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add mintlify/cards/statements-guide.mdx and list it in the Cards tab,
Overview group, after the statements playground.

The page covers who receives a statement, the monthly and quarterly
cadence (12 CFR 1005.9(b)), the section-to-field mapping for each
Reg E item, consumer versus commercial differences, the three API
calls the playground uses to build a statement view, delivery, and a
note on what is not yet available (no statement endpoint, webhook, or
balance snapshots).

No back-link from the playground page: its CSS hides every child of

Co-authored-by: Cursor <cursoragent@cursor.com>
#content except the embed container.
@jaymantri

jaymantri commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Held at ab8b3898. The periodic statements guide page is not shipping in this PR. Only the playground page ships.

mintlify/cards/statements-guide.mdx and its docs.json entry are removed. The Statements group keeps one page, cards/statements ("Playground"). Verified on node 22 with mint 4.2.284: docs.json parses; mint broken-links has 0 hits for cards/statements.

Reframe the guide around what the API returns today. The Platform
builds and delivers the statement from getCustomerById,
listCustomerInternalAccounts, and listTransactions. Remove every
sentence that said Grid generates, renders, or provides a statement
artifact, and drop the "Not yet available" note. The note's content
moves verbatim to components/grid-statements-demo/docs/prd-checklist.md
under "Open items (not for public docs)".

Balances: the page now says the Platform records the balance at period
close and carries it forward, or derives the opening balance from the
period's activity.

Co-authored-by: Cursor <cursoragent@cursor.com>
Move the periodic statements guide and playground out of Cards → Overview
into a new `Statements` group placed directly after `Transactions`.
Labels are one word each within the group: "Periodic statements" (guide)
and "Playground" (playground). Icons are unique within the Cards tab:
IconNewspaper2.svg for the guide, receipt-check.svg for the playground
(phone.svg stays with the Cards Playground). Paths and the
/global-accounts/periodic-statements-playground redirect are unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Ship only the Statements playground page. Remove
mintlify/cards/statements-guide.mdx from the docs tree and drop
cards/statements-guide from the Statements sidebar group. The group
stays after Transactions with the single page cards/statements
(Playground, receipt-check.svg).

The guide is held, not deleted. The draft moves to
components/grid-statements-demo/docs/statements-guide-draft.md with a
header that records the hold reasons. prd-checklist.md points to it.

Held 2026-09-22 pending:

- Settled ownership of statement generation and delivery. The PRD says
  Grid generates; MSA 2.6(c) says Lightspark provides required notices;
  the draft said the partner produces.
- Legal review of 12 CFR 1005.18 prepaid applicability and of
  commercial error language.
- Accuracy fixes: paymentRail is not a top-level transaction field; the
  amount rule must key on type, not direction; balance is current-only;
  card transactions carry no fees; RTP Reg E coverage is unverified; the
  60-day window runs from the send date.

Verified on node 22 with mint 4.2.284: docs.json parses, mint
broken-links has 0 hits for cards/statements, /cards/statements renders
the Playground, /cards/statements-guide renders Page Not Found, and
markdownlint passes on the moved draft.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jaymantri jaymantri changed the title [preview] Grid Statements Playground — assembly (do not merge) feat(statements): periodic statements playground under Cards Sep 23, 2026
@jaymantri
jaymantri marked this pull request as ready for review September 23, 2026 19:18
@greptile-apps

greptile-apps Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 3/5

The PR should not merge until share links preserve uploaded branding and mobile navigation stops accumulating browser-history entries.

Findings

  1. P1 Uploaded logos are lost
  2. P1 Mobile history entries accumulate
  3. P2 Iframe messages trust all senders
Fix with agent prompt
### Issue 1
components/grid-statements-demo/src/statement/shareState.ts:31
When a user uploads a custom logo and copies a share link, this code serializes the logo as `logo=preset`. Opening that link reconstructs the selected preset’s icon instead of the uploaded logo, so the shared statement does not preserve the branding that was shown when the link was copied. The share state should either preserve uploaded logos or clearly exclude them from sharing.

### Issue 2
components/grid-statements-demo/src/app/page.tsx:217-226
Every transition into the mobile playground pushes a history entry, but the Configure button only changes React state and leaves that entry on the stack. Repeating Explore → Configure therefore accumulates same-document entries, making users press Back multiple times to leave the page. The `popstate` handler also switches to Configure for every pop event without checking whether the entry belongs to this view.

### Issue 3
mintlify/snippets/cards/statements-demo-embed.mdx:77-94
The embed accepts `theme-sync` and `nav-request` messages based only on their type, without checking the sender window or origin, and replies using `targetOrigin="*"`. Another frame or window with a reference to the docs page can therefore spoof these messages, alter the page theme, or solicit synchronization traffic. Restrict incoming messages to the expected iframe and origin, and send replies to that specific origin.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR adds an embedded periodic-statements playground with consumer and commercial fixtures, API-call projections, branding controls, mobile and desktop previews, and HTML/PDF export.

  • Adds statement modeling, reconciliation, presentation, sharing, and export behavior.
  • Introduces a standalone Next.js playground and its Cards/Wallet-derived application chrome.
  • Integrates the playground into Mintlify navigation and embeds the deployed application.
  • The share-link logo state and mobile history lifecycle need correction; iframe message validation should also be hardened.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  C[Statement configuration] --> M[Statement model]
  M --> P[Mobile or desktop preview]
  M --> A[Sample Grid API calls]
  M --> E[PDF or self-contained HTML]
  C --> S[Share URL state]
  D[Mintlify Statements page] --> I[Embedded playground iframe]
  I <--> T[Theme and navigation sync]
Loading

Reviews (1) · Last reviewed commit: "docs(cards): hold periodic statements gu..."

url.searchParams.set('background', state.brand.colors.primaryBackground);
url.searchParams.set('text', state.brand.colors.primaryText);
url.searchParams.set('muted', state.brand.colors.secondaryText);
url.searchParams.set('logo', state.brand.logo.kind === 'none' ? 'none' : 'preset');

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.

P1 Uploaded logos are lost

When a user uploads a custom logo and copies a share link, this code serializes the logo as logo=preset. Opening that link reconstructs the selected preset’s icon instead of the uploaded logo, so the shared statement does not preserve the branding that was shown when the link was copied. The share state should either preserve uploaded logos or clearly exclude them from sharing.

Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-statements-demo/src/statement/shareState.ts
Line: 31

Comment:
**Uploaded logos are lost**

When a user uploads a custom logo and copies a share link, this code serializes the logo as `logo=preset`. Opening that link reconstructs the selected preset’s icon instead of the uploaded logo, so the shared statement does not preserve the branding that was shown when the link was copied. The share state should either preserve uploaded logos or clearly exclude them from sharing.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in e64f73b. statementShareUrl now emits logo=none when the logo src starts with blob: (new isUploadedLogo helper in components/grid-statements-demo/src/statement/shareState.ts). Tests added in components/grid-statements-demo/src/statement/shareState.test.ts and components/grid-statements-demo/src/components/ShareSheet/ShareSheet.test.tsx. The Export sheet shows "Uploaded logos are not included in the link." when the current logo is an upload.

Comment on lines +217 to +226
useEffect(() => {
if (mobileView !== 'playground') return;
history.pushState({ mobileView: 'playground' }, '');
const onPop = () => {
setMobileView('configure');
window.scrollTo(0, 0);
};
window.addEventListener('popstate', onPop);
return () => window.removeEventListener('popstate', onPop);
}, [mobileView]);

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.

P1 Mobile history entries accumulate

Every transition into the mobile playground pushes a history entry, but the Configure button only changes React state and leaves that entry on the stack. Repeating Explore → Configure therefore accumulates same-document entries, making users press Back multiple times to leave the page. The popstate handler also switches to Configure for every pop event without checking whether the entry belongs to this view.

Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-statements-demo/src/app/page.tsx
Line: 217-226

Comment:
**Mobile history entries accumulate**

Every transition into the mobile playground pushes a history entry, but the Configure button only changes React state and leaves that entry on the stack. Repeating Explore → Configure therefore accumulates same-document entries, making users press Back multiple times to leave the page. The `popstate` handler also switches to Configure for every pop event without checking whether the entry belongs to this view.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed. This block is verbatim from the sibling playgrounds: Cards components/grid-cards-demo/src/app/page.tsx:99-108, Wallet components/grid-wallet-demo/src/app/page.tsx:99-108, and the same pattern in Visualizer components/grid-visualizer/src/app/page.tsx:81-90. The fix (call history.back() from goConfigure when history.state?.mobileView === 'playground', and guard onPop on the popped state) lands in one follow-up PR across all four playgrounds so mobile Back behaves the same everywhere. Tracking: #1059

Comment on lines +77 to +94
const handleMessage = (e) => {
if (e.data && e.data.type === 'theme-request') {
sendTheme();
return;
}
if (e.data && e.data.type === 'nav-request') {
sendNav();
return;
}
if (e.data && e.data.type === 'theme-sync') {
const wantsDark = e.data.theme === 'dark';
if (isDark() !== wantsDark) {
ignoreNextMutation = true;
document.documentElement.classList.toggle('dark');
}
}
};
window.addEventListener('message', handleMessage);

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.

P2 Iframe messages trust all senders

The embed accepts theme-sync and nav-request messages based only on their type, without checking the sender window or origin, and replies using targetOrigin="*". Another frame or window with a reference to the docs page can therefore spoof these messages, alter the page theme, or solicit synchronization traffic. Restrict incoming messages to the expected iframe and origin, and send replies to that specific origin.

Prompt To Fix With AI
This is a comment left during a code review.
Path: mintlify/snippets/cards/statements-demo-embed.mdx
Line: 77-94

Comment:
**Iframe messages trust all senders**

The embed accepts `theme-sync` and `nav-request` messages based only on their type, without checking the sender window or origin, and replies using `targetOrigin="*"`. Another frame or window with a reference to the docs page can therefore spoof these messages, alter the page theme, or solicit synchronization traffic. Restrict incoming messages to the expected iframe and origin, and send replies to that specific origin.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed. handleMessage is byte-identical to mintlify/snippets/cards/cards-demo-embed.mdx:77-94 and mintlify/snippets/global-accounts/wallet-demo-embed.mdx:68-85. Impact is a docs-page theme flip and extra sync traffic; no data is exposed. The fix (e.source === iframe.contentWindow check, targetOrigin = new URL(base).origin) lands in the same follow-up across all three snippets, together with the parity-pinned src/hooks/useTheme.ts in Cards, Wallet, and Statements, which must change in one commit to keep check-chrome-parity.mjs green. Tracking: #1059

Uploaded logos are blob: URLs and cannot travel in a URL. The share
link previously encoded them as logo=preset, so recipients saw the
preset icon instead. Emit logo=none for uploads, add a test, and show
a hint in the Export sheet.

Addresses Greptile review on #1032.

Co-authored-by: Cursor <cursoragent@cursor.com>
Delete dead modules and exports (refresh.ts, reconcileApiEntries,
unused brand constants, dead CSS), enable noUnusedLocals/Parameters,
fix keyboard dead-end on custom color swatches, correct the Reg E
60-day wording to run from send, clear all API replay timers on
unmount, use normalizeHexColor in the color picker, replace nested
main/h1 landmarks, drop branch-only redirect, and move held internal
docs out of the public repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove audit scripts, their npm entries, and the playwright dev
dependency; remove internal planning docs; trim README deploy notes
to what is needed to run and embed the playground.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jaymantri

Copy link
Copy Markdown
Contributor Author

Superseded by #1060 (single-commit resubmission with review feedback applied and shared chrome re-synced to main).

@jaymantri jaymantri closed this Sep 23, 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