forge/arma/client/addons/cad/ui/_site/cad-sidepanel.css
Jacob Schmidt ff7ff0c4e5 Implement org credit line debt and bank repayment flow (#2)
## Summary

This finishes the org credit line workflow so it behaves like reserved treasury-backed credit instead of a simple member allowance.

## What changed

- reserve org funds immediately when a credit line is assigned
- track credit lines with:
  - approved amount
  - available amount
  - outstanding principal
  - interest rate
  - amount due
- consume reserved credit during store checkout without charging org funds a second time
- add credit line repayment through the bank app
- sync richer credit line state into org and bank payloads/UI
- keep legacy `amount` compatibility mapped to available credit for older consumers

## User-facing behavior

- assigning a credit line now reduces available org funds immediately
- spending on `credit_line` reduces available credit and creates debt with interest
- the bank app now shows outstanding credit debt and allows repayment from personal bank funds
- the org treasury view now shows reserved credit and outstanding due totals

## Validation

- `cargo fmt`
- `npm run build:webui`
- `cargo test -p forge-services --quiet`
- `cargo test -p forge-server --quiet`

## Follow-up checks

- validate in-game that assigning a credit line reduces org funds immediately
- validate store checkout with `credit_line` updates available credit and debt correctly
- validate bank repayment decreases player bank balance, increases org funds, and reduces amount due

Co-authored-by: Jacob Schmidt <innovativestudios@outlook.com>
Reviewed-on: #2
2026-04-02 16:50:38 -05:00

1 line
7.1 KiB
CSS

html,body{background:var(--panel);border-left:1px solid var(--stroke);width:100%;height:100%;box-shadow:var(--shadow);-webkit-backdrop-filter:blur(12px);margin:0;padding:0;overflow:hidden}body{opacity:1;visibility:visible}.panel-header{border-bottom:1px solid var(--stroke);background:linear-gradient(#ffffff0d,#0000);justify-content:space-between;align-items:center;padding:14px;display:flex}.panel-header h3{color:var(--accent);text-transform:uppercase;letter-spacing:.8px;font-size:14px;font-weight:650}.panel-content{height:calc(100% - 56px);padding:14px;overflow:auto}.placeholder-message{text-align:center;padding:20px}.placeholder-message p{color:var(--muted);font-size:13px;font-style:italic}.cad-tabs{grid-template-columns:repeat(4,1fr);gap:5px;margin-bottom:12px;display:grid}.cad-tabs.is-two-col{grid-template-columns:repeat(2,1fr)}.cad-tabs.is-three-col{grid-template-columns:repeat(3,1fr)}.cad-tab{color:#f3f6f9c7;text-transform:uppercase;letter-spacing:.08em;white-space:nowrap;cursor:pointer;background:#141b21e0;border:1px solid #ffffff24;min-width:0;padding:8px 7px;font-size:10px}.cad-tab:hover{color:#f3f6f9;background:#1f282ff0}.cad-tab.is-active{color:var(--accent);background:#0f283af5;border-color:#5bbbff6b}.cad-tab-panels{min-height:0}.cad-section{display:none}.cad-section.is-active{display:block}.cad-section-header{color:var(--accent);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;font-size:12px;font-weight:700}.task-accept-btn,.task-secondary-btn,.cad-select{color:#f3f6f9;background:#1e252be6;border:1px solid #fff3;width:100%;padding:8px 10px}.task-accept-btn,.task-secondary-btn{cursor:pointer}.task-accept-btn:hover,.task-secondary-btn:hover{background:#2e3942f2}.task-accept-btn:disabled,.task-secondary-btn:disabled{opacity:.55;cursor:default}.task-status-message{color:#cdd6dd;min-height:18px;margin-bottom:10px;font-size:12px}.task-status-message[data-type=success]{color:#79d28a}.task-status-message[data-type=error]{color:#ff8a80}.cad-modal{z-index:40;position:fixed;inset:0}.cad-modal.is-hidden{display:none}.cad-modal-backdrop{background:#04080cc2;position:absolute;inset:0}.cad-modal-dialog{background:#0b1118fa;border:1px solid #ffffff1f;width:min(480px,100% - 28px);margin:32px auto 0;position:relative;box-shadow:0 24px 64px #0000006b}.cad-modal-header,.cad-modal-actions{justify-content:space-between;align-items:center;gap:12px;padding:12px 14px;display:flex}.cad-modal-header{border-bottom:1px solid #ffffff14}.cad-modal-header h3{margin:4px 0 0;font-size:18px;font-weight:650}.cad-modal-body{max-height:62vh;padding:14px;overflow:auto}.cad-modal-fields{gap:10px;display:grid}.cad-field{gap:6px;display:grid}.cad-field span{text-transform:uppercase;letter-spacing:.08em;color:#e9f1f8b3;font-size:11px;font-weight:700}.cad-input,.cad-textarea{color:#f3f6f9;box-sizing:border-box;width:100%;font:inherit;background:#1e252be6;border:1px solid #fff3;padding:8px 10px}.cad-textarea{resize:vertical;min-height:74px}.cad-icon-btn{width:30px;height:30px;color:var(--text);cursor:pointer;background:#181f28eb;border:1px solid #ffffff24;padding:0}.cad-modal-actions{border-top:1px solid #ffffff14;justify-content:flex-end}.cad-danger-alert{color:#ffd4cf;letter-spacing:.06em;text-transform:uppercase;background:linear-gradient(90deg,#5c1212f0,#801d1dd1);border:1px solid #ff6b6b5c;margin-bottom:10px;padding:8px 10px;font-size:11px;font-weight:700;animation:1.35s ease-in-out infinite cad-danger-pulse}.cad-danger-alert.is-hidden{display:none}.cad-warning-alert{color:#ffe9b2;letter-spacing:.06em;text-transform:uppercase;background:linear-gradient(90deg,#59400cf0,#7d5c12d6);border:1px solid #f6c65466;margin-bottom:10px;padding:8px 10px;font-size:11px;font-weight:700;animation:1.35s ease-in-out infinite cad-warning-pulse}.cad-warning-alert.is-hidden{display:none}.task-list{flex-direction:column;gap:10px;display:flex}.cad-request-actions{gap:8px;display:grid}.cad-request-btn{text-align:left}.task-action-stack,.task-action-row{flex-direction:column;gap:8px;display:flex}.task-action-row{flex-direction:row}.task-card{background:#0c10149e;border:1px solid #ffffff14;padding:10px}.task-card.is-danger,.roster-summary-card.is-danger{background:linear-gradient(#451416c7,#1c1115eb);border-color:#ff6b6b57;animation:1.35s ease-in-out infinite cad-danger-pulse;box-shadow:inset 0 0 0 1px #ff6b6b1a}.task-card-header{justify-content:space-between;gap:8px;margin-bottom:8px;display:flex}.task-type{opacity:.7;text-transform:uppercase;font-size:11px}.task-description{margin:0 0 8px;font-size:12px;line-height:1.4}.task-meta{opacity:.8;justify-content:space-between;gap:8px;margin-bottom:8px;font-size:11px;display:flex}.task-secondary-btn{background:#3c302deb}.roster-summary-card{background:#10171dd1;border:1px solid #ffffff14;padding:10px}.task-alert-badge{color:#ffd8d1;letter-spacing:.08em;text-transform:uppercase;background:#5f1717e0;border:1px solid #ff6b6b70;align-items:center;padding:2px 8px;font-size:10px;font-weight:700;display:inline-flex}.roster-member-card{background:#0c1014bd}.dispatch-map-group-card{text-align:left;-webkit-appearance:none;appearance:none;width:100%;color:var(--text);font:inherit;cursor:pointer;border-radius:0;transition:border-color .12s,background .12s,transform .12s}.dispatch-map-group-card strong{color:var(--text)}.dispatch-map-group-card .task-type{color:var(--accent);opacity:.9}.dispatch-map-group-card .task-meta{color:var(--muted);opacity:1}.dispatch-map-group-card:hover{background:#121d26e6;border-color:#5bbbff42;transform:translate(-2px)}.dispatch-map-group-card.is-selected{background:#0f283aeb;border-color:#5bbbff85;box-shadow:inset 0 0 0 1px #5bbbff2e}.dispatch-map-group-card.is-danger:not(.is-selected){background:linear-gradient(#451416c7,#1c1115eb);border-color:#ff6b6b57}.dispatch-map-group-card.is-danger .task-meta,.roster-summary-card.is-danger .task-meta{color:#ffe8e4d1}.dispatch-map-card{text-align:left;-webkit-appearance:none;appearance:none;width:100%;color:var(--text);font:inherit;cursor:pointer;border-radius:0;transition:border-color .12s,background .12s,transform .12s}.dispatch-map-card strong{color:var(--text)}.dispatch-map-card .task-type{color:var(--accent);opacity:.9}.dispatch-map-card .task-description{color:var(--muted)}.dispatch-map-card .task-meta{color:var(--muted);opacity:1}.dispatch-map-card:hover{background:#121d26e6;border-color:#5bbbff42;transform:translate(-2px)}.dispatch-map-card.is-selected{background:#0f283aeb;border-color:#5bbbff85;box-shadow:inset 0 0 0 1px #5bbbff2e}.dispatch-map-card.is-warning:not(.is-selected){background:linear-gradient(#564011c7,#221b10eb);border-color:#f6c65457}.dispatch-map-card.is-warning .task-meta,.dispatch-map-card.is-warning .task-description{color:#fff3d6d6}.roster-leader-badge{color:var(--accent);letter-spacing:.06em;text-transform:uppercase;background:#0f283ad1;border:1px solid #5bbbff47;align-items:center;padding:2px 8px;font-size:10px;font-weight:700;display:inline-flex}@keyframes cad-danger-pulse{0%,to{box-shadow:inset 0 0 0 1px #ff6b6b14,0 0 #ff6b6b00}50%{box-shadow:inset 0 0 0 1px #ff8d8d38,0 0 14px #ff6b6b24}}@keyframes cad-warning-pulse{0%,to{box-shadow:inset 0 0 0 1px #f6c65414,0 0 #f6c65400}50%{box-shadow:inset 0 0 0 1px #fbd47638,0 0 18px #f6c65429}}