feat: improve settlement display and add split recalculation to payment edit
All checks were successful
CI / update (push) Successful in 1m13s
All checks were successful
CI / update (push) Successful in 1m13s
Settlement Display Improvements: - Redesigned settlement cards with distinct visual style - Added gradient background and colored top border stripe - Centered layout with prominent amount display - Added settlement badge with icon - Responsive vertical layout on mobile devices - Fixed overflow issues on small screens Payment Edit Enhancements: - Added automatic split recalculation when amount changes - Implemented editable personal amounts for personal_equal split method - Real-time validation showing total personal and remainder - Live split preview with automatic updates - Support for all split methods: equal, full, personal_equal, proportional - Foreign currency support with exchange rate recalculation - Safeguards against infinite recalculation loops - Improved UI with split method info display - Responsive design for mobile devices
This commit is contained in:
@@ -145,28 +145,44 @@
|
||||
{:else}
|
||||
<div class="payments-grid">
|
||||
{#each payments as payment}
|
||||
<a href="/cospend/payments/view/{payment._id}" class="payment-card" class:settlement-card={isSettlementPayment(payment)}>
|
||||
<div class="payment-header">
|
||||
{#if isSettlementPayment(payment)}
|
||||
<!-- Settlement Card - Distinct Layout -->
|
||||
<a href="/cospend/payments/view/{payment._id}" class="payment-card settlement-card">
|
||||
<div class="settlement-header">
|
||||
<div class="settlement-badge">
|
||||
<span class="settlement-icon">💸</span>
|
||||
<span class="settlement-label">Settlement</span>
|
||||
</div>
|
||||
<span class="settlement-date">{formatDate(payment.date)}</span>
|
||||
</div>
|
||||
|
||||
<div class="settlement-flow">
|
||||
<div class="settlement-user-from">
|
||||
<ProfilePicture username={payment.paidBy} size={32} />
|
||||
<div class="settlement-user">
|
||||
<ProfilePicture username={payment.paidBy} size={48} />
|
||||
<span class="username">{payment.paidBy}</span>
|
||||
</div>
|
||||
<div class="settlement-arrow">
|
||||
<span class="arrow">→</span>
|
||||
<span class="settlement-badge-small">Settlement</span>
|
||||
|
||||
<div class="settlement-arrow-container">
|
||||
<div class="settlement-amount-display">
|
||||
{formatAmountWithCurrency(payment)}
|
||||
</div>
|
||||
<div class="settlement-user-to">
|
||||
<ProfilePicture username={getSettlementReceiver(payment)} size={32} />
|
||||
<div class="settlement-arrow">→</div>
|
||||
</div>
|
||||
|
||||
<div class="settlement-user">
|
||||
<ProfilePicture username={getSettlementReceiver(payment)} size={48} />
|
||||
<span class="username">{getSettlementReceiver(payment)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settlement-amount">
|
||||
<span class="amount settlement-amount-text">{formatAmountWithCurrency(payment)}</span>
|
||||
<span class="date">{formatDate(payment.date)}</span>
|
||||
</div>
|
||||
|
||||
{#if payment.description}
|
||||
<p class="settlement-description">{payment.description}</p>
|
||||
{/if}
|
||||
</a>
|
||||
{:else}
|
||||
<!-- Regular Payment Card -->
|
||||
<a href="/cospend/payments/view/{payment._id}" class="payment-card">
|
||||
<div class="payment-header">
|
||||
<div class="payment-title-section">
|
||||
<ProfilePicture username={payment.paidBy} size={40} />
|
||||
<div class="payment-title">
|
||||
@@ -184,7 +200,6 @@
|
||||
{#if payment.image}
|
||||
<img src={payment.image} alt="Receipt" class="receipt-thumb" />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if payment.description}
|
||||
@@ -223,8 +238,8 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</a>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -413,79 +428,137 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Settlement Card Styles */
|
||||
.settlement-card {
|
||||
background: linear-gradient(135deg, var(--nord6), var(--nord5));
|
||||
background: linear-gradient(135deg, #e8f5e9, #f1f8e9);
|
||||
border: 2px solid var(--green);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.settlement-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--green), var(--lightblue));
|
||||
}
|
||||
|
||||
.settlement-card:hover {
|
||||
box-shadow: 0 4px 16px rgba(163, 190, 140, 0.3);
|
||||
box-shadow: 0 6px 20px rgba(163, 190, 140, 0.4);
|
||||
border-color: var(--lightblue);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.settlement-card {
|
||||
background: linear-gradient(135deg, var(--nord2), var(--nord1));
|
||||
background: linear-gradient(135deg, #1a2e1a, #1e2b1e);
|
||||
}
|
||||
|
||||
.settlement-card:hover {
|
||||
box-shadow: 0 4px 16px rgba(163, 190, 140, 0.2);
|
||||
box-shadow: 0 6px 20px rgba(163, 190, 140, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.settlement-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.settlement-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: linear-gradient(135deg, var(--green), var(--lightblue));
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 2rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
box-shadow: 0 2px 8px rgba(163, 190, 140, 0.3);
|
||||
}
|
||||
|
||||
.settlement-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.settlement-date {
|
||||
color: var(--nord3);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.settlement-date {
|
||||
color: var(--nord4);
|
||||
}
|
||||
}
|
||||
|
||||
.settlement-flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 1;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.settlement-user-from, .settlement-user-to {
|
||||
.settlement-user {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.settlement-user-from .username,
|
||||
.settlement-user-to .username {
|
||||
font-weight: 500;
|
||||
.settlement-user .username {
|
||||
font-weight: 600;
|
||||
color: var(--green);
|
||||
font-size: 0.95rem;
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.settlement-arrow-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.settlement-amount-display {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
color: var(--green);
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.settlement-arrow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.settlement-arrow .arrow {
|
||||
color: var(--green);
|
||||
font-size: 1.2rem;
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.settlement-badge-small {
|
||||
background: linear-gradient(135deg, var(--green), var(--lightblue));
|
||||
color: white;
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 0.75rem;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
.settlement-description {
|
||||
color: var(--nord2);
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--nord4);
|
||||
font-style: italic;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.settlement-amount {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 0.25rem;
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.settlement-description {
|
||||
color: var(--nord5);
|
||||
border-top-color: var(--nord3);
|
||||
}
|
||||
|
||||
.settlement-amount-text {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.payment-header {
|
||||
@@ -728,23 +801,64 @@
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
/* Make settlement flow more compact on very small screens */
|
||||
.settlement-flow {
|
||||
/* Make settlement more compact on small screens */
|
||||
.settlement-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.settlement-user-from, .settlement-user-to {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.settlement-user-from .username,
|
||||
.settlement-user-to .username {
|
||||
.settlement-badge {
|
||||
padding: 0.4rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.settlement-badge-small {
|
||||
font-size: 0.55rem;
|
||||
padding: 0.1rem 0.25rem;
|
||||
.settlement-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.settlement-date {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.settlement-flow {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.settlement-user {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settlement-user .username {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.settlement-arrow-container {
|
||||
transform: rotate(90deg);
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.settlement-amount-display {
|
||||
font-size: 1.1rem;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.settlement-arrow {
|
||||
font-size: 1.5rem;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Very small screens - simplify further */
|
||||
@media (max-width: 360px) {
|
||||
.settlement-amount-display {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.settlement-user .username {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,9 +22,105 @@
|
||||
let exchangeRateError = $state(null);
|
||||
let exchangeRateTimeout;
|
||||
let jsEnhanced = $state(false);
|
||||
let originalAmount = $state(null);
|
||||
|
||||
let categoryOptions = $derived(getCategoryOptions());
|
||||
|
||||
// Recalculate splits when amount changes
|
||||
function recalculateSplits() {
|
||||
try {
|
||||
if (!payment || !payment.splits || payment.splits.length === 0) return;
|
||||
|
||||
// For foreign currency, use converted amount if available, otherwise use CHF amount
|
||||
let amountNum;
|
||||
if (payment.currency !== 'CHF' && payment.originalAmount && convertedAmount) {
|
||||
amountNum = convertedAmount;
|
||||
} else {
|
||||
amountNum = parseFloat(payment.amount);
|
||||
}
|
||||
|
||||
if (isNaN(amountNum) || amountNum <= 0) return;
|
||||
|
||||
const paidBy = payment.paidBy;
|
||||
const users = payment.splits.map(s => s.username);
|
||||
|
||||
if (payment.splitMethod === 'equal') {
|
||||
// Equal split
|
||||
const splitAmount = amountNum / users.length;
|
||||
payment.splits = payment.splits.map(split => ({
|
||||
...split,
|
||||
amount: split.username === paidBy ? splitAmount - amountNum : splitAmount
|
||||
}));
|
||||
} else if (payment.splitMethod === 'full') {
|
||||
// Paid in full
|
||||
const otherUsers = users.filter(u => u !== paidBy);
|
||||
const amountPerOtherUser = otherUsers.length > 0 ? amountNum / otherUsers.length : 0;
|
||||
payment.splits = payment.splits.map(split => ({
|
||||
...split,
|
||||
amount: split.username === paidBy ? -amountNum : amountPerOtherUser
|
||||
}));
|
||||
} else if (payment.splitMethod === 'personal_equal') {
|
||||
// Personal + equal split
|
||||
const totalPersonal = payment.splits.reduce((sum, split) => {
|
||||
return sum + (split.personalAmount || 0);
|
||||
}, 0);
|
||||
const remainder = Math.max(0, amountNum - totalPersonal);
|
||||
const equalShare = remainder / users.length;
|
||||
|
||||
payment.splits = payment.splits.map(split => {
|
||||
const personalAmount = split.personalAmount || 0;
|
||||
const totalOwed = personalAmount + equalShare;
|
||||
return {
|
||||
...split,
|
||||
amount: split.username === paidBy ? totalOwed - amountNum : totalOwed
|
||||
};
|
||||
});
|
||||
} else if (payment.splitMethod === 'proportional') {
|
||||
// Proportional - recalculate based on stored proportions
|
||||
payment.splits = payment.splits.map(split => {
|
||||
const proportion = split.proportion || 0;
|
||||
const splitAmount = amountNum * proportion;
|
||||
return {
|
||||
...split,
|
||||
amount: split.username === paidBy ? splitAmount - amountNum : splitAmount
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error recalculating splits:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for amount changes and recalculate splits
|
||||
let lastCalculatedAmount = $state(null);
|
||||
let lastPersonalAmounts = $state(null);
|
||||
|
||||
$effect(() => {
|
||||
if (!jsEnhanced || !payment || !payment.splits || payment.splits.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentAmount = payment.currency !== 'CHF' && payment.originalAmount && convertedAmount
|
||||
? convertedAmount
|
||||
: payment.amount;
|
||||
|
||||
// For personal_equal, also track personal amounts
|
||||
let personalAmountsChanged = false;
|
||||
if (payment.splitMethod === 'personal_equal') {
|
||||
const currentPersonalAmounts = payment.splits.map(s => s.personalAmount || 0).join(',');
|
||||
if (lastPersonalAmounts !== currentPersonalAmounts) {
|
||||
personalAmountsChanged = true;
|
||||
lastPersonalAmounts = currentPersonalAmounts;
|
||||
}
|
||||
}
|
||||
|
||||
// Recalculate if amount changed or personal amounts changed
|
||||
if ((currentAmount !== lastCalculatedAmount && currentAmount > 0) || personalAmountsChanged) {
|
||||
lastCalculatedAmount = currentAmount;
|
||||
recalculateSplits();
|
||||
}
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
jsEnhanced = true;
|
||||
document.body.classList.add('js-loaded');
|
||||
@@ -40,7 +136,25 @@
|
||||
}
|
||||
const result = await response.json();
|
||||
payment = result.payment;
|
||||
|
||||
// Initialize personal amounts if undefined (for personal_equal split method)
|
||||
if (payment.splitMethod === 'personal_equal' && payment.splits) {
|
||||
payment.splits = payment.splits.map(split => ({
|
||||
...split,
|
||||
personalAmount: split.personalAmount || 0
|
||||
}));
|
||||
}
|
||||
|
||||
// Store original amount for comparison to prevent infinite recalculation
|
||||
originalAmount = payment.amount;
|
||||
// Set initial lastCalculatedAmount to prevent immediate recalculation on load
|
||||
lastCalculatedAmount = payment.amount;
|
||||
// Store initial personal amounts to prevent immediate recalculation
|
||||
if (payment.splitMethod === 'personal_equal') {
|
||||
lastPersonalAmounts = payment.splits.map(s => s.personalAmount || 0).join(',');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading payment:', err);
|
||||
error = err.message;
|
||||
} finally {
|
||||
loading = false;
|
||||
@@ -363,12 +477,65 @@
|
||||
/>
|
||||
|
||||
{#if payment.splits && payment.splits.length > 0}
|
||||
<FormSection title="Current Splits">
|
||||
<FormSection title="Split Configuration">
|
||||
<div class="split-method-info">
|
||||
<span class="label">Split Method:</span>
|
||||
<span class="value">
|
||||
{#if payment.splitMethod === 'equal'}
|
||||
Equal Split
|
||||
{:else if payment.splitMethod === 'full'}
|
||||
Paid in Full
|
||||
{:else if payment.splitMethod === 'personal_equal'}
|
||||
Personal + Equal Split
|
||||
{:else if payment.splitMethod === 'proportional'}
|
||||
Custom Proportions
|
||||
{:else}
|
||||
{payment.splitMethod}
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{#if payment.splitMethod === 'personal_equal'}
|
||||
<div class="personal-amounts-editor">
|
||||
<h3>Personal Amounts</h3>
|
||||
<p class="description">Enter personal amounts for each user. The remainder will be split equally.</p>
|
||||
{#each payment.splits as split, index}
|
||||
<div class="personal-input">
|
||||
<label for="personal_{split.username}">{split.username}</label>
|
||||
<input
|
||||
id="personal_{split.username}"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
value={split.personalAmount || 0}
|
||||
oninput={(e) => {
|
||||
split.personalAmount = parseFloat(e.target.value) || 0;
|
||||
}}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{#if payment.amount}
|
||||
{@const totalPersonal = payment.splits.reduce((sum, s) => sum + (s.personalAmount || 0), 0)}
|
||||
{@const remainder = Math.max(0, parseFloat(payment.amount) - totalPersonal)}
|
||||
{@const hasError = totalPersonal > parseFloat(payment.amount)}
|
||||
<div class="remainder-info" class:error={hasError}>
|
||||
<span>Total Personal: CHF {totalPersonal.toFixed(2)}</span>
|
||||
<span>Remainder to Split: CHF {remainder.toFixed(2)}</span>
|
||||
{#if hasError}
|
||||
<div class="error-message">⚠️ Personal amounts exceed total payment amount!</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="splits-display">
|
||||
<h3>Split Preview</h3>
|
||||
{#each payment.splits as split}
|
||||
<div class="split-item">
|
||||
<span>{split.username}</span>
|
||||
<span class:positive={split.amount < 0} class:negative={split.amount > 0}>
|
||||
<span class="split-username">{split.username}</span>
|
||||
<span class="split-amount" class:positive={split.amount < 0} class:negative={split.amount > 0}>
|
||||
{#if split.amount > 0}
|
||||
owes CHF {split.amount.toFixed(2)}
|
||||
{:else if split.amount < 0}
|
||||
@@ -380,7 +547,10 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<p class="note">Note: To modify splits, please delete and recreate the payment.</p>
|
||||
<p class="note">
|
||||
<span class="js-only">✓ Splits recalculate automatically when you change the amount{payment.splitMethod === 'personal_equal' ? ' or personal amounts' : ''}</span>
|
||||
<span class="no-js">Note: Split method and participants cannot be changed. To modify, please delete and recreate the payment.</span>
|
||||
</p>
|
||||
</FormSection>
|
||||
{/if}
|
||||
|
||||
@@ -517,6 +687,163 @@
|
||||
}
|
||||
}
|
||||
|
||||
.split-method-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.75rem;
|
||||
background-color: var(--nord14);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--green);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.split-method-info {
|
||||
background-color: var(--nord2);
|
||||
border-color: var(--nord3);
|
||||
}
|
||||
}
|
||||
|
||||
.split-method-info .label {
|
||||
font-weight: 600;
|
||||
color: var(--nord1);
|
||||
}
|
||||
|
||||
.split-method-info .value {
|
||||
color: var(--green);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.split-method-info .label {
|
||||
color: var(--nord5);
|
||||
}
|
||||
}
|
||||
|
||||
.personal-amounts-editor {
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
background-color: var(--nord5);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--nord4);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.personal-amounts-editor {
|
||||
background-color: var(--nord2);
|
||||
border-color: var(--nord3);
|
||||
}
|
||||
}
|
||||
|
||||
.personal-amounts-editor h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--nord0);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.personal-amounts-editor h3 {
|
||||
color: var(--font-default-dark);
|
||||
}
|
||||
}
|
||||
|
||||
.personal-amounts-editor .description {
|
||||
color: var(--nord2);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.personal-amounts-editor .description {
|
||||
color: var(--nord4);
|
||||
}
|
||||
}
|
||||
|
||||
.personal-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.personal-input label {
|
||||
min-width: 120px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.personal-input input {
|
||||
max-width: 150px;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--nord4);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 1rem;
|
||||
background-color: var(--nord6);
|
||||
color: var(--nord0);
|
||||
}
|
||||
|
||||
.personal-input input:focus {
|
||||
outline: none;
|
||||
border-color: var(--blue);
|
||||
box-shadow: 0 0 0 2px rgba(94, 129, 172, 0.2);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.personal-input input {
|
||||
background-color: var(--nord1);
|
||||
color: var(--font-default-dark);
|
||||
border-color: var(--nord3);
|
||||
}
|
||||
}
|
||||
|
||||
.remainder-info {
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem;
|
||||
background-color: var(--nord14);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--green);
|
||||
}
|
||||
|
||||
.remainder-info.error {
|
||||
background-color: var(--nord6);
|
||||
border-color: var(--red);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.remainder-info {
|
||||
background-color: var(--nord1);
|
||||
border-color: var(--nord3);
|
||||
}
|
||||
|
||||
.remainder-info.error {
|
||||
background-color: var(--accent-dark);
|
||||
border-color: var(--red);
|
||||
}
|
||||
}
|
||||
|
||||
.remainder-info span {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--nord0);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.remainder-info span {
|
||||
color: var(--font-default-dark);
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: var(--red);
|
||||
font-weight: 600;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.splits-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -524,6 +851,19 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.splits-display h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--nord0);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.splits-display h3 {
|
||||
color: var(--font-default-dark);
|
||||
}
|
||||
}
|
||||
|
||||
.split-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -541,12 +881,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
.positive {
|
||||
.split-username {
|
||||
font-weight: 500;
|
||||
color: var(--nord0);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.split-username {
|
||||
color: var(--font-default-dark);
|
||||
}
|
||||
}
|
||||
|
||||
.split-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.personal-amount {
|
||||
font-size: 0.85rem;
|
||||
color: var(--nord3);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.personal-amount {
|
||||
color: var(--nord4);
|
||||
}
|
||||
}
|
||||
|
||||
.split-amount.positive {
|
||||
color: var(--green);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.negative {
|
||||
.split-amount.negative {
|
||||
color: var(--red);
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -564,6 +934,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.js-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-js {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
:global(body.js-loaded) .js-only {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
:global(body.js-loaded) .no-js {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -743,5 +1129,19 @@
|
||||
.amount-currency select {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.personal-input {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.personal-input label {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.personal-input input {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user