feat(frontend): rename contributionTier → contributionAmount across remaining pages
This commit is contained in:
parent
5ef0cc845f
commit
b17e006d65
6 changed files with 133 additions and 81 deletions
|
|
@ -77,7 +77,7 @@
|
|||
<th class="sortable" @click="toggleSort('name')">Name <span class="sort-ind">{{ sortIndicator('name') }}</span></th>
|
||||
<th class="sortable" @click="toggleSort('email')">Email <span class="sort-ind">{{ sortIndicator('email') }}</span></th>
|
||||
<th class="sortable" @click="toggleSort('circle')">Circle <span class="sort-ind">{{ sortIndicator('circle') }}</span></th>
|
||||
<th class="sortable" @click="toggleSort('contributionTier')">Tier <span class="sort-ind">{{ sortIndicator('contributionTier') }}</span></th>
|
||||
<th class="sortable" @click="toggleSort('contributionAmount')">Contribution <span class="sort-ind">{{ sortIndicator('contributionAmount') }}</span></th>
|
||||
<th class="sortable" @click="toggleSort('status')">Status <span class="sort-ind">{{ sortIndicator('status') }}</span></th>
|
||||
<th>Invite</th>
|
||||
<th>Slack</th>
|
||||
|
|
@ -112,7 +112,7 @@
|
|||
member.circle
|
||||
}}</span>
|
||||
</td>
|
||||
<td class="col-mono">${{ member.contributionTier }}/mo</td>
|
||||
<td class="col-mono">${{ member.contributionAmount ?? 0 }}/mo</td>
|
||||
<td>
|
||||
<span class="badge status" :class="`status-${member.status || 'pending_payment'}`">{{ statusLabel(member.status) }}</span>
|
||||
</td>
|
||||
|
|
@ -186,14 +186,8 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Contribution Tier</label>
|
||||
<select v-model="newMember.contributionTier">
|
||||
<option value="0">$0/month</option>
|
||||
<option value="5">$5/month</option>
|
||||
<option value="15">$15/month</option>
|
||||
<option value="30">$30/month</option>
|
||||
<option value="50">$50/month</option>
|
||||
</select>
|
||||
<label>Contribution ($/mo)</label>
|
||||
<input v-model.number="newMember.contributionAmount" type="number" min="0" step="1">
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn" @click="showCreateModal = false">
|
||||
|
|
@ -223,11 +217,10 @@
|
|||
<div v-if="!csvRows.length">
|
||||
<p class="help-text">
|
||||
Upload a CSV with columns:
|
||||
<code>name,email,circle,contributionTier</code>
|
||||
<code>name,email,circle,contributionAmount</code>
|
||||
</p>
|
||||
<p class="help-text" style="margin-bottom: 12px">
|
||||
Valid circles: community, founder, practitioner. Valid tiers: 0,
|
||||
5, 15, 30, 50.
|
||||
Valid circles: community, founder, practitioner. Contribution: whole number ≥ 0.
|
||||
</p>
|
||||
<input
|
||||
ref="csvFileInput"
|
||||
|
|
@ -287,7 +280,7 @@
|
|||
<td>{{ row.name }}</td>
|
||||
<td class="col-email">{{ row.email }}</td>
|
||||
<td>{{ row.circle }}</td>
|
||||
<td>${{ row.contributionTier }}/mo</td>
|
||||
<td>${{ row.contributionAmount }}/mo</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -367,14 +360,8 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Contribution Tier</label>
|
||||
<select v-model="editingMember.contributionTier">
|
||||
<option value="0">$0/month</option>
|
||||
<option value="5">$5/month</option>
|
||||
<option value="15">$15/month</option>
|
||||
<option value="30">$30/month</option>
|
||||
<option value="50">$50/month</option>
|
||||
</select>
|
||||
<label>Contribution ($/mo)</label>
|
||||
<input v-model.number="editingMember.contributionAmount" type="number" min="0" step="1">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Status</label>
|
||||
|
|
@ -550,7 +537,7 @@ const newMember = reactive({
|
|||
name: "",
|
||||
email: "",
|
||||
circle: "community",
|
||||
contributionTier: "0",
|
||||
contributionAmount: 0,
|
||||
});
|
||||
|
||||
const filteredMembers = computed(() => {
|
||||
|
|
@ -576,7 +563,7 @@ const filteredMembers = computed(() => {
|
|||
return [...filtered].sort((a, b) => {
|
||||
let av = a[key];
|
||||
let bv = b[key];
|
||||
if (key === "contributionTier") {
|
||||
if (key === "contributionAmount") {
|
||||
av = Number(av) || 0;
|
||||
bv = Number(bv) || 0;
|
||||
} else if (key === "createdAt") {
|
||||
|
|
@ -668,7 +655,7 @@ const createMember = async () => {
|
|||
name: "",
|
||||
email: "",
|
||||
circle: "community",
|
||||
contributionTier: "0",
|
||||
contributionAmount: 0,
|
||||
});
|
||||
|
||||
await refresh();
|
||||
|
|
@ -687,7 +674,6 @@ const createMember = async () => {
|
|||
|
||||
// --- CSV Import ---
|
||||
const VALID_CIRCLES = ["community", "founder", "practitioner"];
|
||||
const VALID_TIERS = ["0", "5", "15", "30", "50"];
|
||||
|
||||
const handleCsvFile = (event) => {
|
||||
const file = event.target.files[0];
|
||||
|
|
@ -716,10 +702,10 @@ const parseCsv = (text) => {
|
|||
const nameIdx = header.indexOf("name");
|
||||
const emailIdx = header.indexOf("email");
|
||||
const circleIdx = header.indexOf("circle");
|
||||
const tierIdx = header.indexOf("contributiontier");
|
||||
const amountIdx = header.indexOf("contributionamount");
|
||||
|
||||
if (nameIdx === -1 || emailIdx === -1 || circleIdx === -1 || tierIdx === -1) {
|
||||
csvParseError.value = `Missing required columns. Found: ${header.join(", ")}. Need: name, email, circle, contributionTier`;
|
||||
if (nameIdx === -1 || emailIdx === -1 || circleIdx === -1 || amountIdx === -1) {
|
||||
csvParseError.value = `Missing required columns. Found: ${header.join(", ")}. Need: name, email, circle, contributionAmount`;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -731,20 +717,21 @@ const parseCsv = (text) => {
|
|||
const name = cols[nameIdx] || "";
|
||||
const email = (cols[emailIdx] || "").toLowerCase();
|
||||
const circle = (cols[circleIdx] || "").toLowerCase();
|
||||
const contributionTier = cols[tierIdx] || "";
|
||||
const rawAmount = cols[amountIdx] || "";
|
||||
const contributionAmount = Number(rawAmount);
|
||||
|
||||
let error = null;
|
||||
if (!name) error = "Missing name";
|
||||
else if (!email || !email.includes("@")) error = "Invalid email";
|
||||
else if (!VALID_CIRCLES.includes(circle))
|
||||
error = `Invalid circle: ${circle}`;
|
||||
else if (!VALID_TIERS.includes(contributionTier))
|
||||
error = `Invalid tier: ${contributionTier}`;
|
||||
else if (!Number.isInteger(contributionAmount) || contributionAmount < 0)
|
||||
error = `Invalid contribution: ${rawAmount}`;
|
||||
else if (seenEmails.has(email)) error = "Duplicate email in CSV";
|
||||
|
||||
if (!error) seenEmails.add(email);
|
||||
|
||||
rows.push({ name, email, circle, contributionTier, error });
|
||||
rows.push({ name, email, circle, contributionAmount, error });
|
||||
}
|
||||
|
||||
csvRows.value = rows;
|
||||
|
|
@ -771,11 +758,11 @@ const submitImport = async () => {
|
|||
importing.value = true;
|
||||
try {
|
||||
const payload = csvValidRows.value.map(
|
||||
({ name, email, circle, contributionTier }) => ({
|
||||
({ name, email, circle, contributionAmount }) => ({
|
||||
name,
|
||||
email,
|
||||
circle,
|
||||
contributionTier,
|
||||
contributionAmount,
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -854,7 +841,7 @@ const editingMember = reactive({
|
|||
name: "",
|
||||
email: "",
|
||||
circle: "community",
|
||||
contributionTier: "0",
|
||||
contributionAmount: 0,
|
||||
status: "pending_payment",
|
||||
});
|
||||
|
||||
|
|
@ -864,7 +851,7 @@ const editMember = (member) => {
|
|||
name: member.name,
|
||||
email: member.email,
|
||||
circle: member.circle,
|
||||
contributionTier: String(member.contributionTier),
|
||||
contributionAmount: member.contributionAmount ?? 0,
|
||||
status: member.status || "pending_payment",
|
||||
});
|
||||
showEditModal.value = true;
|
||||
|
|
@ -879,7 +866,7 @@ const submitEditMember = async () => {
|
|||
name: editingMember.name,
|
||||
email: editingMember.email,
|
||||
circle: editingMember.circle,
|
||||
contributionTier: editingMember.contributionTier,
|
||||
contributionAmount: editingMember.contributionAmount,
|
||||
status: editingMember.status,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue