refactor: migrate to common eslint+prettier configs (#4168)

* refactor: migrate to common eslint+prettier configs

* fix: prettier frontend

* feat: config changes

* fix: lint issues

* fix: lint

* fix: type imports

* fix: cyclical import issue

* fix: lockfile

* fix: missing dep

* fix: switch to tabs

* fix: continue switch to tabs

* fix: rustfmt parity

* fix: moderation lint issue

* fix: lint issues

* fix: ui intl

* fix: lint issues

* Revert "fix: rustfmt parity"

This reverts commit cb99d2376c321d813d4b7fc7e2a213bb30a54711.

* feat: revert last rs
This commit is contained in:
Cal H.
2025-08-14 21:48:38 +01:00
committed by GitHub
parent 82697278dc
commit 2aabcf36ee
702 changed files with 101360 additions and 102020 deletions

View File

@@ -1,536 +1,538 @@
<template>
<section class="universal-card">
<Breadcrumbs
current-title="Withdraw"
:link-stack="[{ href: '/dashboard/revenue', label: 'Revenue' }]"
/>
<section class="universal-card">
<Breadcrumbs
current-title="Withdraw"
:link-stack="[{ href: '/dashboard/revenue', label: 'Revenue' }]"
/>
<h2>Withdraw</h2>
<h2>Withdraw</h2>
<h3>Region</h3>
<Multiselect
id="country-multiselect"
v-model="country"
class="country-multiselect"
placeholder="Select country..."
track-by="id"
label="name"
:options="countries"
:searchable="true"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
<h3>Region</h3>
<Multiselect
id="country-multiselect"
v-model="country"
class="country-multiselect"
placeholder="Select country..."
track-by="id"
label="name"
:options="countries"
:searchable="true"
:close-on-select="true"
:show-labels="false"
:allow-empty="false"
/>
<h3>Withdraw method</h3>
<h3>Withdraw method</h3>
<div class="iconified-input">
<label class="hidden" for="search">Search</label>
<SearchIcon aria-hidden="true" />
<input
id="search"
v-model="search"
name="search"
placeholder="Search options..."
autocomplete="off"
/>
</div>
<div class="withdraw-options-scroll">
<div class="withdraw-options">
<button
v-for="method in payoutMethods
.filter((x) => x.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) =>
a.type !== 'tremendous'
? -1
: a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
)"
:key="method.id"
class="withdraw-option button-base"
:class="{ selected: selectedMethodId === method.id }"
@click="() => (selectedMethodId = method.id)"
>
<div class="preview" :class="{ 'show-bg': !method.image_url || method.name === 'ACH' }">
<template v-if="method.image_url && method.name !== 'ACH'">
<div class="preview-badges">
<span class="badge">
{{
getRangeOfMethod(method)
.map($formatMoney)
.map((i) => i.replace(".00", ""))
.join("")
}}
</span>
</div>
<img
v-if="method.image_url && method.name !== 'ACH'"
class="preview-img"
:src="method.image_url"
:alt="method.name"
/>
</template>
<div v-else class="placeholder">
<template v-if="method.type === 'venmo'">
<VenmoIcon class="enlarge" />
</template>
<template v-else>
<PayPalIcon v-if="method.type === 'paypal'" />
<span>{{ method.name }}</span>
</template>
</div>
</div>
<div class="label">
<RadioButtonCheckedIcon v-if="selectedMethodId === method.id" class="radio" />
<RadioButtonIcon v-else class="radio" />
<span>{{ method.name }}</span>
</div>
</button>
</div>
</div>
<div class="iconified-input">
<label class="hidden" for="search">Search</label>
<SearchIcon aria-hidden="true" />
<input
id="search"
v-model="search"
name="search"
placeholder="Search options..."
autocomplete="off"
/>
</div>
<div class="withdraw-options-scroll">
<div class="withdraw-options">
<button
v-for="method in payoutMethods
.filter((x) => x.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) =>
a.type !== 'tremendous'
? -1
: a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
)"
:key="method.id"
class="withdraw-option button-base"
:class="{ selected: selectedMethodId === method.id }"
@click="() => (selectedMethodId = method.id)"
>
<div class="preview" :class="{ 'show-bg': !method.image_url || method.name === 'ACH' }">
<template v-if="method.image_url && method.name !== 'ACH'">
<div class="preview-badges">
<span class="badge">
{{
getRangeOfMethod(method)
.map($formatMoney)
.map((i) => i.replace('.00', ''))
.join('')
}}
</span>
</div>
<img
v-if="method.image_url && method.name !== 'ACH'"
class="preview-img"
:src="method.image_url"
:alt="method.name"
/>
</template>
<div v-else class="placeholder">
<template v-if="method.type === 'venmo'">
<VenmoIcon class="enlarge" />
</template>
<template v-else>
<PayPalIcon v-if="method.type === 'paypal'" />
<span>{{ method.name }}</span>
</template>
</div>
</div>
<div class="label">
<RadioButtonCheckedIcon v-if="selectedMethodId === method.id" class="radio" />
<RadioButtonIcon v-else class="radio" />
<span>{{ method.name }}</span>
</div>
</button>
</div>
</div>
<h3>Amount</h3>
<p>
You are initiating a transfer of your revenue from Modrinth's Creator Monetization Program.
How much of your
<strong>{{ $formatMoney(userBalance.available) }}</strong> balance would you like to transfer
transfer to {{ selectedMethod.name }}?
</p>
<div class="confirmation-input">
<template v-if="selectedMethod.interval.fixed">
<Chips
v-model="amount"
:items="selectedMethod.interval.fixed.values"
:format-label="(val) => '$' + val"
/>
</template>
<template v-else-if="minWithdrawAmount == maxWithdrawAmount">
<div>
<p>
This method has a fixed transfer amount of
<strong>{{ $formatMoney(minWithdrawAmount) }}</strong
>.
</p>
</div>
</template>
<template v-else>
<div>
<p>
This method has a minimum transfer amount of
<strong>{{ $formatMoney(minWithdrawAmount) }}</strong> and a maximum transfer amount of
<strong>{{ $formatMoney(maxWithdrawAmount) }}</strong
>.
</p>
<input
id="confirmation"
v-model="amount"
type="text"
pattern="^\d*(\.\d{0,2})?$"
autocomplete="off"
placeholder="Amount to transfer..."
/>
<p>
You have entered <strong>{{ $formatMoney(parsedAmount) }}</strong> to transfer.
</p>
</div>
</template>
</div>
<h3>Amount</h3>
<p>
You are initiating a transfer of your revenue from Modrinth's Creator Monetization Program.
How much of your
<strong>{{ $formatMoney(userBalance.available) }}</strong> balance would you like to transfer
transfer to {{ selectedMethod.name }}?
</p>
<div class="confirmation-input">
<template v-if="selectedMethod.interval.fixed">
<Chips
v-model="amount"
:items="selectedMethod.interval.fixed.values"
:format-label="(val) => '$' + val"
/>
</template>
<template v-else-if="minWithdrawAmount == maxWithdrawAmount">
<div>
<p>
This method has a fixed transfer amount of
<strong>{{ $formatMoney(minWithdrawAmount) }}</strong
>.
</p>
</div>
</template>
<template v-else>
<div>
<p>
This method has a minimum transfer amount of
<strong>{{ $formatMoney(minWithdrawAmount) }}</strong> and a maximum transfer amount of
<strong>{{ $formatMoney(maxWithdrawAmount) }}</strong
>.
</p>
<input
id="confirmation"
v-model="amount"
type="text"
pattern="^\d*(\.\d{0,2})?$"
autocomplete="off"
placeholder="Amount to transfer..."
/>
<p>
You have entered <strong>{{ $formatMoney(parsedAmount) }}</strong> to transfer.
</p>
</div>
</template>
</div>
<div class="confirm-text">
<template v-if="knownErrors.length === 0 && amount">
<Checkbox v-if="fees > 0" v-model="agreedFees" description="Consent to fee">
I acknowledge that an estimated
{{ formatMoney(fees) }} will be deducted from the amount I receive to cover
{{ formatWallet(selectedMethod.type) }} processing fees.
</Checkbox>
<Checkbox v-model="agreedTransfer" description="Confirm transfer">
<template v-if="selectedMethod.type === 'tremendous'">
I confirm that I am initiating a transfer and I will receive further instructions on how
to redeem this payment via email to: {{ withdrawAccount }}
</template>
<template v-else>
I confirm that I am initiating a transfer to the following
{{ formatWallet(selectedMethod.type) }} account: {{ withdrawAccount }}
</template>
</Checkbox>
<Checkbox v-model="agreedTerms" class="rewards-checkbox">
I agree to the
<nuxt-link to="/legal/cmp" class="text-link">Rewards Program Terms</nuxt-link>
</Checkbox>
</template>
<template v-else>
<span v-for="(error, index) in knownErrors" :key="index" class="invalid">
{{ error }}
</span>
</template>
</div>
<div class="button-group">
<nuxt-link to="/dashboard/revenue" class="iconified-button">
<XIcon />
Cancel
</nuxt-link>
<button
:disabled="
knownErrors.length > 0 ||
!amount ||
!agreedTransfer ||
!agreedTerms ||
(fees > 0 && !agreedFees)
"
class="iconified-button brand-button"
@click="withdraw"
>
<TransferIcon />
Withdraw
</button>
</div>
</section>
<div class="confirm-text">
<template v-if="knownErrors.length === 0 && amount">
<Checkbox v-if="fees > 0" v-model="agreedFees" description="Consent to fee">
I acknowledge that an estimated
{{ formatMoney(fees) }} will be deducted from the amount I receive to cover
{{ formatWallet(selectedMethod.type) }} processing fees.
</Checkbox>
<Checkbox v-model="agreedTransfer" description="Confirm transfer">
<template v-if="selectedMethod.type === 'tremendous'">
I confirm that I am initiating a transfer and I will receive further instructions on how
to redeem this payment via email to:
{{ withdrawAccount }}
</template>
<template v-else>
I confirm that I am initiating a transfer to the following
{{ formatWallet(selectedMethod.type) }} account: {{ withdrawAccount }}
</template>
</Checkbox>
<Checkbox v-model="agreedTerms" class="rewards-checkbox">
I agree to the
<nuxt-link to="/legal/cmp" class="text-link">Rewards Program Terms</nuxt-link>
</Checkbox>
</template>
<template v-else>
<span v-for="(error, index) in knownErrors" :key="index" class="invalid">
{{ error }}
</span>
</template>
</div>
<div class="button-group">
<nuxt-link to="/dashboard/revenue" class="iconified-button">
<XIcon />
Cancel
</nuxt-link>
<button
:disabled="
knownErrors.length > 0 ||
!amount ||
!agreedTransfer ||
!agreedTerms ||
(fees > 0 && !agreedFees)
"
class="iconified-button brand-button"
@click="withdraw"
>
<TransferIcon />
Withdraw
</button>
</div>
</section>
</template>
<script setup>
import {
PayPalIcon,
RadioButtonCheckedIcon,
RadioButtonIcon,
SearchIcon,
TransferIcon,
XIcon,
} from "@modrinth/assets";
import { Breadcrumbs, Checkbox, Chips, injectNotificationManager } from "@modrinth/ui";
import { formatMoney, formatWallet } from "@modrinth/utils";
import { all } from "iso-3166-1";
import { Multiselect } from "vue-multiselect";
import VenmoIcon from "~/assets/images/external/venmo.svg?component";
PayPalIcon,
RadioButtonCheckedIcon,
RadioButtonIcon,
SearchIcon,
TransferIcon,
XIcon,
} from '@modrinth/assets'
import { Breadcrumbs, Checkbox, Chips, injectNotificationManager } from '@modrinth/ui'
import { formatMoney, formatWallet } from '@modrinth/utils'
import { all } from 'iso-3166-1'
import { Multiselect } from 'vue-multiselect'
const { addNotification } = injectNotificationManager();
const auth = await useAuth();
const data = useNuxtApp();
import VenmoIcon from '~/assets/images/external/venmo.svg?component'
const { addNotification } = injectNotificationManager()
const auth = await useAuth()
const data = useNuxtApp()
const countries = computed(() =>
all().map((x) => ({
id: x.alpha2,
name: x.alpha2 === "TW" ? "Taiwan" : x.country,
})),
);
const search = ref("");
all().map((x) => ({
id: x.alpha2,
name: x.alpha2 === 'TW' ? 'Taiwan' : x.country,
})),
)
const search = ref('')
const amount = ref("");
const amount = ref('')
const country = ref(
countries.value.find((x) => x.id === (auth.value.user.payout_data.paypal_region ?? "US")),
);
countries.value.find((x) => x.id === (auth.value.user.payout_data.paypal_region ?? 'US')),
)
const [{ data: userBalance }, { data: payoutMethods, refresh: refreshPayoutMethods }] =
await Promise.all([
useAsyncData(`payout/balance`, () => useBaseFetch(`payout/balance`, { apiVersion: 3 })),
useAsyncData(`payout/methods?country=${country.value.id}`, () =>
useBaseFetch(`payout/methods?country=${country.value.id}`, { apiVersion: 3 }),
),
]);
await Promise.all([
useAsyncData(`payout/balance`, () => useBaseFetch(`payout/balance`, { apiVersion: 3 })),
useAsyncData(`payout/methods?country=${country.value.id}`, () =>
useBaseFetch(`payout/methods?country=${country.value.id}`, { apiVersion: 3 }),
),
])
const selectedMethodId = ref(payoutMethods.value[0].id);
const selectedMethodId = ref(payoutMethods.value[0].id)
const selectedMethod = computed(() =>
payoutMethods.value.find((x) => x.id === selectedMethodId.value),
);
payoutMethods.value.find((x) => x.id === selectedMethodId.value),
)
const parsedAmount = computed(() => {
const regex = /^\$?(\d*(\.\d{2})?)$/gm;
const matches = regex.exec(amount.value);
return matches && matches[1] ? parseFloat(matches[1]) : 0.0;
});
const regex = /^\$?(\d*(\.\d{2})?)$/gm
const matches = regex.exec(amount.value)
return matches && matches[1] ? parseFloat(matches[1]) : 0.0
})
const fees = computed(() => {
return Math.min(
Math.max(
selectedMethod.value.fee.min,
selectedMethod.value.fee.percentage * parsedAmount.value,
),
selectedMethod.value.fee.max ?? Number.MAX_VALUE,
);
});
return Math.min(
Math.max(
selectedMethod.value.fee.min,
selectedMethod.value.fee.percentage * parsedAmount.value,
),
selectedMethod.value.fee.max ?? Number.MAX_VALUE,
)
})
const getIntervalRange = (intervalType) => {
if (!intervalType) {
return [];
}
if (!intervalType) {
return []
}
const { min, max, values } = intervalType;
if (values) {
const first = values[0];
const last = values.slice(-1)[0];
return first === last ? [first] : [first, last];
}
const { min, max, values } = intervalType
if (values) {
const first = values[0]
const last = values.slice(-1)[0]
return first === last ? [first] : [first, last]
}
return min === max ? [min] : [min, max];
};
return min === max ? [min] : [min, max]
}
const getRangeOfMethod = (method) => {
return getIntervalRange(method.interval?.fixed || method.interval?.standard);
};
return getIntervalRange(method.interval?.fixed || method.interval?.standard)
}
const maxWithdrawAmount = computed(() => {
const interval = selectedMethod.value.interval;
return interval?.standard ? interval.standard.max : (interval?.fixed?.values.slice(-1)[0] ?? 0);
});
const interval = selectedMethod.value.interval
return interval?.standard ? interval.standard.max : (interval?.fixed?.values.slice(-1)[0] ?? 0)
})
const minWithdrawAmount = computed(() => {
const interval = selectedMethod.value.interval;
return interval?.standard ? interval.standard.min : (interval?.fixed?.values?.[0] ?? fees.value);
});
const interval = selectedMethod.value.interval
return interval?.standard ? interval.standard.min : (interval?.fixed?.values?.[0] ?? fees.value)
})
const withdrawAccount = computed(() => {
if (selectedMethod.value.type === "paypal") {
return auth.value.user.payout_data.paypal_address;
} else if (selectedMethod.value.type === "venmo") {
return auth.value.user.payout_data.venmo_handle;
} else {
return auth.value.user.email;
}
});
if (selectedMethod.value.type === 'paypal') {
return auth.value.user.payout_data.paypal_address
} else if (selectedMethod.value.type === 'venmo') {
return auth.value.user.payout_data.venmo_handle
} else {
return auth.value.user.email
}
})
const knownErrors = computed(() => {
const errors = [];
if (selectedMethod.value.type === "paypal" && !auth.value.user.payout_data.paypal_address) {
errors.push("Please link your PayPal account in the dashboard to proceed.");
}
if (selectedMethod.value.type === "venmo" && !auth.value.user.payout_data.venmo_handle) {
errors.push("Please set your Venmo handle in the dashboard to proceed.");
}
if (selectedMethod.value.type === "tremendous") {
if (!auth.value.user.email) {
errors.push("Please set your email address in your account settings to proceed.");
}
if (!auth.value.user.email_verified) {
errors.push("Please verify your email address to proceed.");
}
}
const errors = []
if (selectedMethod.value.type === 'paypal' && !auth.value.user.payout_data.paypal_address) {
errors.push('Please link your PayPal account in the dashboard to proceed.')
}
if (selectedMethod.value.type === 'venmo' && !auth.value.user.payout_data.venmo_handle) {
errors.push('Please set your Venmo handle in the dashboard to proceed.')
}
if (selectedMethod.value.type === 'tremendous') {
if (!auth.value.user.email) {
errors.push('Please set your email address in your account settings to proceed.')
}
if (!auth.value.user.email_verified) {
errors.push('Please verify your email address to proceed.')
}
}
if (!parsedAmount.value && amount.value.length > 0) {
errors.push(`${amount.value} is not a valid amount`);
} else if (
parsedAmount.value > userBalance.value.available ||
parsedAmount.value > maxWithdrawAmount.value
) {
const maxAmount = Math.min(userBalance.value.available, maxWithdrawAmount.value);
errors.push(`The amount must be no more than ${data.$formatMoney(maxAmount)}`);
} else if (parsedAmount.value <= fees.value || parsedAmount.value < minWithdrawAmount.value) {
const minAmount = Math.max(fees.value + 0.01, minWithdrawAmount.value);
errors.push(`The amount must be at least ${data.$formatMoney(minAmount)}`);
}
if (!parsedAmount.value && amount.value.length > 0) {
errors.push(`${amount.value} is not a valid amount`)
} else if (
parsedAmount.value > userBalance.value.available ||
parsedAmount.value > maxWithdrawAmount.value
) {
const maxAmount = Math.min(userBalance.value.available, maxWithdrawAmount.value)
errors.push(`The amount must be no more than ${data.$formatMoney(maxAmount)}`)
} else if (parsedAmount.value <= fees.value || parsedAmount.value < minWithdrawAmount.value) {
const minAmount = Math.max(fees.value + 0.01, minWithdrawAmount.value)
errors.push(`The amount must be at least ${data.$formatMoney(minAmount)}`)
}
return errors;
});
return errors
})
const agreedTransfer = ref(false);
const agreedFees = ref(false);
const agreedTerms = ref(false);
const agreedTransfer = ref(false)
const agreedFees = ref(false)
const agreedTerms = ref(false)
watch(country, async () => {
await refreshPayoutMethods();
if (payoutMethods.value && payoutMethods.value[0]) {
selectedMethodId.value = payoutMethods.value[0].id;
}
});
await refreshPayoutMethods()
if (payoutMethods.value && payoutMethods.value[0]) {
selectedMethodId.value = payoutMethods.value[0].id
}
})
watch(selectedMethod, () => {
if (selectedMethod.value.interval?.fixed) {
amount.value = selectedMethod.value.interval.fixed.values[0];
}
if (maxWithdrawAmount.value === minWithdrawAmount.value) {
amount.value = maxWithdrawAmount.value;
}
agreedTransfer.value = false;
agreedFees.value = false;
agreedTerms.value = false;
});
if (selectedMethod.value.interval?.fixed) {
amount.value = selectedMethod.value.interval.fixed.values[0]
}
if (maxWithdrawAmount.value === minWithdrawAmount.value) {
amount.value = maxWithdrawAmount.value
}
agreedTransfer.value = false
agreedFees.value = false
agreedTerms.value = false
})
async function withdraw() {
startLoading();
try {
const auth = await useAuth();
startLoading()
try {
const auth = await useAuth()
await useBaseFetch(`payout`, {
method: "POST",
body: {
amount: parsedAmount.value,
method: selectedMethod.value.type,
method_id: selectedMethod.value.id,
},
apiVersion: 3,
});
await useAuth(auth.value.token);
await navigateTo("/dashboard/revenue");
addNotification({
title: "Withdrawal complete",
text:
selectedMethod.value.type === "tremendous"
? "An email has been sent to your account with further instructions on how to redeem your payout!"
: `Payment has been sent to your ${formatWallet(selectedMethod.value.type)} account!`,
type: "success",
});
} catch (err) {
addNotification({
title: "An error occurred",
text: err.data ? err.data.description : err,
type: "error",
});
}
stopLoading();
await useBaseFetch(`payout`, {
method: 'POST',
body: {
amount: parsedAmount.value,
method: selectedMethod.value.type,
method_id: selectedMethod.value.id,
},
apiVersion: 3,
})
await useAuth(auth.value.token)
await navigateTo('/dashboard/revenue')
addNotification({
title: 'Withdrawal complete',
text:
selectedMethod.value.type === 'tremendous'
? 'An email has been sent to your account with further instructions on how to redeem your payout!'
: `Payment has been sent to your ${formatWallet(selectedMethod.value.type)} account!`,
type: 'success',
})
} catch (err) {
addNotification({
title: 'An error occurred',
text: err.data ? err.data.description : err,
type: 'error',
})
}
stopLoading()
}
</script>
<style lang="scss" scoped>
.withdraw-options-scroll {
max-height: 460px;
overflow-y: auto;
max-height: 460px;
overflow-y: auto;
&::-webkit-scrollbar {
width: var(--gap-md);
border: 3px solid var(--color-bg);
}
&::-webkit-scrollbar {
width: var(--gap-md);
border: 3px solid var(--color-bg);
}
&::-webkit-scrollbar-track {
background: var(--color-bg);
border: 3px solid var(--color-bg);
}
&::-webkit-scrollbar-track {
background: var(--color-bg);
border: 3px solid var(--color-bg);
}
&::-webkit-scrollbar-thumb {
background-color: var(--color-raised-bg);
border-radius: var(--radius-lg);
border: 3px solid var(--color-bg);
}
&::-webkit-scrollbar-thumb {
background-color: var(--color-raised-bg);
border-radius: var(--radius-lg);
border: 3px solid var(--color-bg);
}
}
.withdraw-options {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: var(--gap-lg);
padding-right: 0.5rem;
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: var(--gap-lg);
padding-right: 0.5rem;
@media screen and (min-width: 300px) {
grid-template-columns: repeat(2, 1fr);
}
@media screen and (min-width: 300px) {
grid-template-columns: repeat(2, 1fr);
}
@media screen and (min-width: 600px) {
grid-template-columns: repeat(3, 1fr);
}
@media screen and (min-width: 600px) {
grid-template-columns: repeat(3, 1fr);
}
}
.withdraw-option {
width: 100%;
border-radius: var(--radius-md);
padding: 0;
overflow: hidden;
border: 1px solid var(--color-divider);
background-color: var(--color-button-bg);
color: var(--color-text);
width: 100%;
border-radius: var(--radius-md);
padding: 0;
overflow: hidden;
border: 1px solid var(--color-divider);
background-color: var(--color-button-bg);
color: var(--color-text);
&.selected {
color: var(--color-contrast);
&.selected {
color: var(--color-contrast);
.label svg {
color: var(--color-brand);
}
}
.label svg {
color: var(--color-brand);
}
}
.preview {
display: flex;
justify-content: center;
aspect-ratio: 30 / 19;
position: relative;
.preview {
display: flex;
justify-content: center;
aspect-ratio: 30 / 19;
position: relative;
.preview-badges {
// These will float over the image in the bottom right corner
position: absolute;
bottom: 0;
right: 0;
padding: var(--gap-sm) var(--gap-xs);
.preview-badges {
// These will float over the image in the bottom right corner
position: absolute;
bottom: 0;
right: 0;
padding: var(--gap-sm) var(--gap-xs);
.badge {
background-color: var(--color-button-bg);
border-radius: var(--radius-xs);
padding: var(--gap-xs) var(--gap-sm);
font-size: var(--font-size-xs);
}
}
.badge {
background-color: var(--color-button-bg);
border-radius: var(--radius-xs);
padding: var(--gap-xs) var(--gap-sm);
font-size: var(--font-size-xs);
}
}
&.show-bg {
background-color: var(--color-bg);
}
&.show-bg {
background-color: var(--color-bg);
}
img {
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
user-select: none;
width: 100%;
height: auto;
object-fit: cover;
}
img {
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
user-select: none;
width: 100%;
height: auto;
object-fit: cover;
}
.placeholder {
display: flex;
align-items: center;
gap: var(--gap-xs);
.placeholder {
display: flex;
align-items: center;
gap: var(--gap-xs);
svg {
width: 2rem;
height: auto;
}
svg {
width: 2rem;
height: auto;
}
span {
font-weight: var(--font-weight-bold);
font-size: 2rem;
font-style: italic;
}
span {
font-weight: var(--font-weight-bold);
font-size: 2rem;
font-style: italic;
}
.enlarge {
width: auto;
height: 1.5rem;
}
}
}
.enlarge {
width: auto;
height: 1.5rem;
}
}
}
.label {
display: flex;
align-items: center;
padding: var(--gap-md) var(--gap-lg);
.label {
display: flex;
align-items: center;
padding: var(--gap-md) var(--gap-lg);
svg {
min-height: 1rem;
min-width: 1rem;
margin-right: 0.5rem;
}
svg {
min-height: 1rem;
min-width: 1rem;
margin-right: 0.5rem;
}
span {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
span {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
.invalid {
color: var(--color-red);
color: var(--color-red);
}
.confirm-text {
margin: var(--spacing-card-md) 0;
display: flex;
flex-direction: column;
gap: var(--spacing-card-sm);
margin: var(--spacing-card-md) 0;
display: flex;
flex-direction: column;
gap: var(--spacing-card-sm);
}
.iconified-input {
margin-bottom: var(--spacing-card-md);
margin-bottom: var(--spacing-card-md);
}
.country-multiselect,
.iconified-input {
max-width: 16rem;
max-width: 16rem;
}
.rewards-checkbox {
a {
margin-left: 0.5ch;
}
a {
margin-left: 0.5ch;
}
}
</style>