ShareCapitalLedger
A member's share capital contributions with a running balance, and the progress toward the share capital the co-op requires.
Demo
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/share_capital_ledger.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/share_capital_ledger.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/share_capital_ledger.qwe
view ShareCapitalLedgerDemo() = <div.demo-stack> <ShareCapitalLedger id="share-capital-ledger-demo" currency="PHP" required={25000} par={100} entries={[ { date: "2024-01-15", description: "Initial paid-up capital", ref: "OR-10233", amount: 5000 }, { date: "2024-02-29", description: "Salary deduction", amount: 1000 }, { date: "2024-03-31", description: "Salary deduction", amount: 1000 }, { date: "2024-06-28", description: "Cash contribution", ref: "OR-10877", amount: 2500 }, { date: "2025-03-31", description: "2024 dividend converted to shares", amount: 1840.25 }, { date: "2025-06-10", description: "Transfer to member M-009914", ref: "JV-2025-061", amount: -500 }, { date: "2025-09-30", description: "Salary deduction", amount: 1500 }, ]}/> <ShareCapitalLedger id="share-capital-ledger-demo-paid" title="Membership share" required={500} entries={[ { date: "2025-01-06", description: "First installment", amount: 250 }, { date: "2025-04-07", description: "Second installment", amount: 250 }, { date: "2026-01-31", description: "Patronage credited", amount: 25.5 }, ]}/> </div>Source
ui/share_capital_ledger.qwe
// ShareCapitalLedger: a member's share capital, contribution by contribution: a// table with the date, what it was, the amount and the balance after it, and above// it how far the member is toward the share capital the co-op requires.//// <ShareCapitalLedger currency="PHP" required={25000} par={100} entries={[// { date: "2024-01-15", description: "Initial paid-up capital", ref: "OR-10233", amount: 5000 },// { date: "2024-02-29", description: "Salary deduction", amount: 1000 },// { date: "2025-03-31", description: "Dividend converted to shares", amount: 1840.25 },// { date: "2025-06-10", description: "Transfer to M-0991", amount: -500 },// ]}/>//// entries: oldest first, each { date, description, amount, ref? }: date ISO// ("2024-01-15", shown "Jan 15, 2024"), amount a number or a decimal, above 0// paid in and below 0 taken out (a withdrawal or transfer, shown "−₱500.00"). The// balance after each is added up here, exactly, from `opening` (0). required: the// share capital a member must reach; with it the progress is shown ("74% of// ₱25,000.00", what is left to pay, or that it is reached). par: the par value of// one share, to show how many shares the balance makes. currency: ISO code// ("USD"); locale: how to write amounts (the program's). title: the heading// ("Share capital"). id: the element's id.//// share_capital_ledger_rows(entries, opening) gives the entries each with its// `balance`, a decimal, for a program's own use.import ui/theme.qwe use ui_clslet share_capital_ledger_css = css`.ui-share-capital { display: grid; gap: 16px; min-width: 0; padding: 18px 20px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); color: var(--ui-fg); font: 14px/1.45 var(--ui-font); }.ui-share-capital .ui-share-capital-title { display: block; margin: 0; font-size: 16px; font-weight: 650; }.ui-share-capital-goal { display: grid; gap: 8px; }.ui-share-capital-figures { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 16px; }.ui-share-capital-held { font-size: 24px; font-weight: 650; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }.ui-share-capital-of { color: var(--ui-muted); font-variant-numeric: tabular-nums; }.ui-share-capital-bar { height: 10px; border-radius: 999px; background: var(--ui-subtle); overflow: hidden; }.ui-share-capital-fill { height: 100%; border-radius: inherit; background: var(--ui-accent); }.ui-share-capital-done .ui-share-capital-fill { background: var(--ui-success); }.ui-share-capital-left { margin: 0; color: var(--ui-muted); font-size: 13px; }.ui-share-capital-done .ui-share-capital-left { color: var(--ui-success); font-weight: 500; }.ui-share-capital-scroll { overflow-x: auto; }.ui-share-capital table { width: 100%; border-collapse: collapse; }.ui-share-capital th { padding: 8px 10px; border-bottom: 1px solid var(--ui-border); color: var(--ui-muted); font-size: 12px; font-weight: 600; text-align: left; white-space: nowrap; }.ui-share-capital td { padding: 9px 10px; border-bottom: 1px solid var(--ui-border); vertical-align: top; }.ui-share-capital tbody tr:hover td { background: var(--ui-subtle); }.ui-share-capital .ui-share-capital-num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }.ui-share-capital-date { white-space: nowrap; color: var(--ui-muted); }.ui-share-capital-ref { display: block; color: var(--ui-muted); font-size: 12px; font-family: var(--ui-mono); }.ui-share-capital-in { color: var(--ui-success); }.ui-share-capital-out { color: var(--ui-danger); }.ui-share-capital tfoot td { border-bottom: 0; border-top: 2px solid var(--ui-fg); font-weight: 650; }.ui-share-capital-empty { color: var(--ui-muted); text-align: center; }`// An amount in its currency, without its sign.fn share_capital_ledger_money(amount, currency: str, locale: str?) -> str { let m = money.of(abs(dec(amount)), currency, "half_up") if is_err(m) { return str(amount) } locale != null ? money.format(m, locale) : money.format(m)}// The entries, each with the balance after it (a decimal), from `opening`.fn share_capital_ledger_rows(entries: list, opening = 0) -> list { let amounts = entries | map((e) => dec(e.amount)) for [i, e] in enumerate(entries) => set(e, "balance", dec(opening) + sum(slice(amounts, 0, i + 1)))}// An ISO date as "Jan 15, 2024"; other text as it is.fn share_capital_ledger_date(d, locale: str?) -> str = date.valid(str(d), "date") ? date.format(str(d), { locale: locale ?? "en-US", style: "medium" }) : str(d)view ShareCapitalLedger(entries: list = [], opening = 0, required = null, par = null, currency: str = "USD", locale: str? = null, title: str = "Share capital", id: str? = null, class: str? = null) { style global share_capital_ledger_css let rows = share_capital_ledger_rows(entries, opening) let balance = len(rows) > 0 ? rows[len(rows) - 1].balance : dec(opening) let need = required != null ? dec(required) : null let pct = need == null || need <= 0 ? null : (balance >= need ? 100 : (balance <= 0 ? 0 : int(floor(balance * 100 / need)))) let done = need != null && balance >= need let shown = (x) => share_capital_ledger_money(x, currency, locale) let has_ref = any(entries, (e) => e.ref != null) let heading = id != null ? `${id}-title` : null <section class={ui_cls(["ui-share-capital", done && "ui-share-capital-done", class])} id={id} aria-labelledby={heading}> <h3.ui-share-capital-title id={heading}>{title}</h3> <div.ui-share-capital-goal> <div.ui-share-capital-figures> <span.ui-share-capital-held>{balance < 0 ? "−" : ""}{shown(balance)}</span> {need != null && <span.ui-share-capital-of>{pct}% of {shown(need)} required</span>} {par != null && dec(par) > 0 && <span.ui-share-capital-of.ui-share-capital-shares>{int(floor(balance / dec(par)))} shares at {shown(par)}</span>} </div> {need != null && [ <div.ui-share-capital-bar role="progressbar" aria-label="Toward the required share capital" aria-valuemin="0" aria-valuemax="100" aria-valuenow={pct} aria-valuetext={`${pct}%, ${shown(balance)} of ${shown(need)}`}> <div.ui-share-capital-fill style={`width: ${pct}%`}></div> </div>, <p.ui-share-capital-left>{done ? "The required share capital is paid up." : `${shown(need - balance)} to go`}</p>, ]} </div> <div.ui-share-capital-scroll> <table> <thead><tr> <th scope="col">Date</th> <th scope="col">Description</th> <th.ui-share-capital-num scope="col">Amount</th> <th.ui-share-capital-num scope="col">Balance</th> </tr></thead> <tbody> {len(rows) == 0 && <tr><td.ui-share-capital-empty colspan="4">No contributions yet.</td></tr>} {for [i, r] in enumerate(rows) => { let out = dec(r.amount) < 0 <tr key={i}> <td.ui-share-capital-date><time datetime={str(r.date)}>{share_capital_ledger_date(r.date, locale)}</time></td> <td>{r.description}{has_ref && r.ref != null && <span.ui-share-capital-ref>{r.ref}</span>}</td> <td class={`ui-share-capital-num ${out ? "ui-share-capital-out" : "ui-share-capital-in"}`}> {out ? <span><span aria-hidden="true">−</span><span.ui-sr>minus </span></span> : ""}{shown(r.amount)} </td> <td.ui-share-capital-num>{r.balance < 0 ? "−" : ""}{shown(r.balance)}</td> </tr> }} </tbody> {len(rows) > 0 && <tfoot><tr> <td colspan="3">Balance</td> <td.ui-share-capital-num>{balance < 0 ? "−" : ""}{shown(balance)}</td> </tr></tfoot>} </table> </div> </section>}Related
AmortizationTableA loan's payment schedule worked out from its amount, rate and term: each month's principal, interest and balance, grouped by year with the year's totals.7 views · 7 downloadsCrossRatesA matrix of cross rates among a few currencies, each worked out from their rates against one base, the diagonal left blank.6 views · 6 downloadsFxRateTableA board of exchange rates: each pair with its flags, bid, ask, the spread in pips and the day's change from the open, coloured.7 views · 8 downloadsLoanCommitteeQueueLoan applications before the credit committee: member, amount, purpose, the votes so far, where each stands, and approve or decline per row.7 views · 10 downloadsAssemblyNoticeA general assembly notice: date and time, the venue or online link, the quorum, a numbered agenda and an RSVP form.6 views · 6 downloadsBankStatementA monthly statement that prints: the period, opening and closing balance, totals paid in and out, and each transaction with the balance after.7 views · 6 downloads
Thread
Thread 0 posts
On ShareCapitalLedger: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.