|
21 | 21 | let rolesMetadata = $state<Map<string, boolean>>(new Map()); |
22 | 22 | let loadingMetadata = $state(false); |
23 | 23 | let selectedBankId = $state(currentBank.bankId); |
| 24 | + // "bank" = bank-scoped (send bank_id), "system" = system-wide (send empty string) |
| 25 | + let scopeChoice = $state<"bank" | "system">("bank"); |
24 | 26 |
|
25 | 27 | $effect(() => { |
26 | 28 | selectedBankId = currentBank.bankId; |
|
38 | 40 | }); |
39 | 41 | rolesMetadata = metadataMap; |
40 | 42 |
|
| 43 | + // Default scope based on whether role requires bank_id |
| 44 | + const anyRequiresBank = Array.from(metadataMap.values()).some((v) => v); |
| 45 | + if (!anyRequiresBank) { |
| 46 | + scopeChoice = "system"; |
| 47 | + } else { |
| 48 | + scopeChoice = "bank"; |
| 49 | + } |
41 | 50 | } catch (error) { |
42 | 51 | console.error("Failed to fetch role metadata:", error); |
43 | 52 | } finally { |
|
50 | 59 | Array.from(rolesMetadata.values()).some((requires) => requires), |
51 | 60 | ); |
52 | 61 |
|
53 | | - // The effective bank_id to use (provided prop or from currentBank) |
54 | | - let effectiveBankId = $derived(bankId || selectedBankId); |
| 62 | + // The effective bank_id to use: empty for system-wide, otherwise provided prop or currentBank |
| 63 | + let effectiveBankId = $derived( |
| 64 | + scopeChoice === "system" ? "" : (bankId || selectedBankId) |
| 65 | + ); |
55 | 66 |
|
56 | 67 | async function handleRequestClick() { |
57 | 68 | if (isSubmitting) return; |
58 | 69 |
|
59 | | - // Validate bank selection if required |
60 | | - if (requiresBankId && !bankId && !selectedBankId) { |
61 | | - submitError = "Please select a bank in My Account for this role."; |
| 70 | + // Validate bank selection if bank-scoped |
| 71 | + if (scopeChoice === "bank" && !bankId && !selectedBankId) { |
| 72 | + submitError = "Please select a bank in My Account for this bank-scoped role."; |
62 | 73 | return; |
63 | 74 | } |
64 | 75 |
|
|
129 | 140 | {/each} |
130 | 141 | </div> |
131 | 142 |
|
132 | | - {#if bankId} |
| 143 | + <!-- Scope selector: System-wide vs Bank-scoped --> |
| 144 | + {#if !bankId} |
| 145 | + <div class="scope-selector"> |
| 146 | + <p class="scope-label"><strong>Role scope:</strong></p> |
| 147 | + <label class="scope-option"> |
| 148 | + <input |
| 149 | + type="radio" |
| 150 | + name="scope-choice" |
| 151 | + value="system" |
| 152 | + bind:group={scopeChoice} |
| 153 | + /> |
| 154 | + <span class="scope-option-text"> |
| 155 | + System-wide <span class="scope-hint">(no bank_id — for system roles)</span> |
| 156 | + </span> |
| 157 | + </label> |
| 158 | + <label class="scope-option"> |
| 159 | + <input |
| 160 | + type="radio" |
| 161 | + name="scope-choice" |
| 162 | + value="bank" |
| 163 | + bind:group={scopeChoice} |
| 164 | + /> |
| 165 | + <span class="scope-option-text"> |
| 166 | + Bank-scoped |
| 167 | + {#if selectedBankId} |
| 168 | + <code class="bank-code">{selectedBankId}</code> |
| 169 | + <span class="scope-hint">(from current bank selection)</span> |
| 170 | + {:else} |
| 171 | + <span class="scope-hint">— <a href="/user" style="color: #3b82f6; text-decoration: underline;">select a bank</a> first</span> |
| 172 | + {/if} |
| 173 | + </span> |
| 174 | + </label> |
| 175 | + </div> |
| 176 | + {:else} |
133 | 177 | <p class="bank-info"> |
134 | 178 | <strong>Bank ID:</strong> <code class="bank-code">{bankId}</code> |
135 | 179 | </p> |
136 | 180 | {/if} |
137 | 181 |
|
138 | | - {#if requiresBankId && !bankId} |
139 | | - {#if selectedBankId} |
140 | | - <p class="bank-info"> |
141 | | - <strong>Bank ID:</strong> <code class="bank-code">{selectedBankId}</code> |
142 | | - <span class="bank-hint">(from current bank selection)</span> |
143 | | - </p> |
144 | | - {:else} |
145 | | - <div class="bank-selector"> |
146 | | - <p>This role requires a bank. Please select a bank in <a href="/user" style="color: #3b82f6; text-decoration: underline;">My Account</a> first.</p> |
147 | | - </div> |
148 | | - {/if} |
149 | | - {/if} |
150 | | - |
151 | 182 | {#if message} |
152 | 183 | <MessageBox {message} type="error" /> |
153 | 184 | {/if} |
|
475 | 506 | margin-left: 0.25rem; |
476 | 507 | } |
477 | 508 |
|
| 509 | + .scope-selector { |
| 510 | + margin: 1rem 0; |
| 511 | + padding: 0.75rem 1rem; |
| 512 | + background: rgba(59, 130, 246, 0.1); |
| 513 | + border: 1px solid rgba(59, 130, 246, 0.3); |
| 514 | + border-radius: 6px; |
| 515 | + } |
| 516 | +
|
| 517 | + :global([data-mode="dark"]) .scope-selector { |
| 518 | + background: rgba(59, 130, 246, 0.15); |
| 519 | + border-color: rgba(59, 130, 246, 0.4); |
| 520 | + } |
| 521 | +
|
| 522 | + .scope-label { |
| 523 | + margin: 0 0 0.5rem 0; |
| 524 | + font-size: 0.875rem; |
| 525 | + } |
| 526 | +
|
| 527 | + .scope-option { |
| 528 | + display: flex; |
| 529 | + align-items: center; |
| 530 | + gap: 0.5rem; |
| 531 | + padding: 0.375rem 0; |
| 532 | + font-size: 0.875rem; |
| 533 | + cursor: pointer; |
| 534 | + } |
| 535 | +
|
| 536 | + .scope-option input[type="radio"] { |
| 537 | + margin: 0; |
| 538 | + cursor: pointer; |
| 539 | + } |
| 540 | +
|
| 541 | + .scope-option-text { |
| 542 | + display: inline-flex; |
| 543 | + align-items: center; |
| 544 | + gap: 0.375rem; |
| 545 | + flex-wrap: wrap; |
| 546 | + } |
| 547 | +
|
| 548 | + .scope-hint { |
| 549 | + font-size: 0.75rem; |
| 550 | + color: #6b7280; |
| 551 | + font-weight: normal; |
| 552 | + } |
| 553 | +
|
| 554 | + :global([data-mode="dark"]) .scope-hint { |
| 555 | + color: rgb(var(--color-surface-400)); |
| 556 | + } |
| 557 | +
|
478 | 558 | </style> |
0 commit comments