diff --git a/app/pages/join.vue b/app/pages/join.vue
index 59d943a..24cad1a 100644
--- a/app/pages/join.vue
+++ b/app/pages/join.vue
@@ -32,7 +32,7 @@
Contribution
- ${{ memberData?.contributionTier || "0" }} CAD/month
+ ${{ memberData?.contributionAmount ?? 0 }} CAD/month
@@ -69,14 +69,14 @@
Pay what you can
- $0 I need support right now
- - {{ formatTierAmount('5') }} I can contribute
+ - {{ formatContributionAmount(5) }} I can contribute
-
- {{ formatTierAmount('15') }} I can sustain the community
+ {{ formatContributionAmount(15) }} I can sustain the community
(suggested)
- - {{ formatTierAmount('30') }} I can support others too
+ - {{ formatContributionAmount(30) }} I can support others too
-
- {{ formatTierAmount('50') }} I want to sponsor multiple
+ {{ formatContributionAmount(50) }} I want to sponsor multiple
members
@@ -207,27 +207,38 @@
>
-
Contribution{{ selectedTier.label }}
+ Contribution{{ formatContributionAmount(form.contributionAmount) }}
@@ -409,9 +420,8 @@ import { reactive, ref, computed, onMounted, onUnmounted } from "vue";
import { getCircleOptions } from "~/config/circles";
import {
requiresPayment,
- getContributionTierByValue,
- getTierAmount,
- CONTRIBUTION_TIERS,
+ CONTRIBUTION_PRESETS,
+ getGuidanceLabel,
} from "~/config/contributions";
// Auth state
@@ -427,7 +437,7 @@ const form = reactive({
email: "",
name: "",
circle: "community",
- contributionTier: "15",
+ contributionAmount: 15,
agreedToGuidelines: false,
billingAddress: {
street: "",
@@ -461,29 +471,11 @@ const paymentToken = ref(null);
// Circle options from central config
const circleOptions = getCircleOptions();
-// Minimal labels for the dropdown — reactive to cadence.
-// In annual mode, show both monthly and annual price so $50/yr (the $5 tier annual)
-// is visually distinct from $500/yr (the $50 tier annual).
-const contributionItems = computed(() => {
- return Object.values(CONTRIBUTION_TIERS).map((tier) => {
- const base = tier.amount;
- if (base === 0) return { value: tier.value, label: "$0" };
- const monthlyLabel = `$${base}/mo`;
- const priceLabel =
- cadence.value === "annual"
- ? `${monthlyLabel} → $${getTierAmount(tier, "annual")}/yr`
- : monthlyLabel;
- const hint = tier.value === "15" ? " (suggested)" : "";
- return { value: tier.value, label: `${priceLabel}${hint}` };
- });
-});
-
-const formatTierAmount = (value) => {
- const tier = getContributionTierByValue(value);
- if (!tier || tier.amount === 0) return "$0";
- const amt = getTierAmount(tier, cadence.value);
+const formatContributionAmount = (amount) => {
+ if (!amount || amount === 0) return "$0";
+ const display = cadence.value === "annual" ? amount * 12 : amount;
const suffix = cadence.value === "annual" ? "/yr" : "/mo";
- return `$${amt}${suffix}`;
+ return `$${display}${suffix}`;
};
// Initialize composables
@@ -499,20 +491,17 @@ const isFormValid = computed(() => {
form.name &&
form.email &&
form.circle &&
- form.contributionTier &&
+ Number.isInteger(form.contributionAmount) && form.contributionAmount >= 0 &&
form.agreedToGuidelines
);
});
// Check if payment is required
const needsPayment = computed(() => {
- return requiresPayment(form.contributionTier);
+ return requiresPayment(form.contributionAmount);
});
-// Get selected tier info
-const selectedTier = computed(() => {
- return getContributionTierByValue(form.contributionTier);
-});
+const guidanceLabel = computed(() => getGuidanceLabel(form.contributionAmount));
const flowStepLabel = computed(() => {
switch (flowState.value) {
@@ -546,7 +535,7 @@ const handleSubmit = async () => {
name: form.name,
email: form.email,
circle: form.circle,
- contributionTier: form.contributionTier,
+ contributionAmount: form.contributionAmount,
agreedToGuidelines: form.agreedToGuidelines,
billingAddress: form.billingAddress,
},
@@ -617,7 +606,7 @@ const createSubscription = async (cardToken = null) => {
body: {
customerId: customerId.value,
customerCode: customerCode.value,
- contributionTier: form.contributionTier,
+ contributionAmount: form.contributionAmount,
cadence: cadence.value,
cardToken: cardToken,
},
@@ -883,6 +872,52 @@ onUnmounted(() => {
gap: 10px;
}
+/* ---- CONTRIBUTION AMOUNT INPUT + CHIPS ---- */
+.contribution-input-row {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+}
+.contribution-currency {
+ font-weight: 600;
+}
+.contribution-input {
+ flex: 1;
+ padding: 0.5rem 0.75rem;
+ background: var(--input-bg);
+ border: 1px solid var(--parch);
+ font-family: 'Commit Mono', monospace;
+ font-size: 1rem;
+}
+.contribution-input:focus {
+ outline: none;
+ border-color: var(--candle);
+}
+.contribution-presets {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin-top: 0.5rem;
+}
+.contribution-preset-chip {
+ padding: 0.25rem 0.75rem;
+ background: transparent;
+ border: 1px dashed var(--parch);
+ font-family: 'Commit Mono', monospace;
+ font-size: 0.875rem;
+ cursor: pointer;
+}
+.contribution-preset-chip:hover {
+ border-style: solid;
+ border-color: var(--candle);
+}
+.contribution-guidance {
+ margin-top: 0.5rem;
+ font-size: 0.875rem;
+ font-style: italic;
+ color: var(--ink-soft, currentColor);
+}
+
/* ---- CIRCLE RADIOS ---- */
.circle-radios {
display: grid;