forked from jsnbuchanan/crowd-funder-for-time-pwa
Fix: offer validation prematurely closes dialog
- Transferred form validation error handling to an earlier step - Added validation for negative input (similar to gifting forms) - Switched amount input to component version for consistency
This commit is contained in:
@@ -15,26 +15,25 @@ Raymer */
|
|||||||
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||||
placeholder="Description of what is offered"
|
placeholder="Description of what is offered"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-row mt-2">
|
<div class="flex mb-4">
|
||||||
<span :class="unitCodeDisplayClasses" @click="changeUnitCode()">
|
<AmountInput
|
||||||
{{ libsUtil.UNIT_SHORT[amountUnitCode] }}
|
:value="parseFloat(amountInput) || 0"
|
||||||
</span>
|
:onUpdateValue="handleAmountUpdate"
|
||||||
<div
|
|
||||||
v-if="showDecrementButton"
|
|
||||||
:class="controlButtonClasses"
|
|
||||||
@click="decrement()"
|
|
||||||
>
|
|
||||||
<font-awesome icon="chevron-left" />
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
v-model="amountInput"
|
|
||||||
data-testId="inputOfferAmount"
|
data-testId="inputOfferAmount"
|
||||||
type="number"
|
|
||||||
:class="amountInputClasses"
|
|
||||||
/>
|
/>
|
||||||
<div :class="incrementButtonClasses" @click="increment()">
|
|
||||||
<font-awesome icon="chevron-right" />
|
<select
|
||||||
</div>
|
v-model="amountUnitCode"
|
||||||
|
class="flex-1 rounded border border-slate-400 ms-2 px-3 py-2"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(displayName, code) in unitOptions"
|
||||||
|
:key="code"
|
||||||
|
:value="code"
|
||||||
|
>
|
||||||
|
{{ displayName }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 flex justify-center">
|
<div class="mt-4 flex justify-center">
|
||||||
<span>
|
<span>
|
||||||
@@ -73,10 +72,15 @@ import {
|
|||||||
NOTIFY_OFFER_CREATION_ERROR,
|
NOTIFY_OFFER_CREATION_ERROR,
|
||||||
NOTIFY_OFFER_SUCCESS,
|
NOTIFY_OFFER_SUCCESS,
|
||||||
NOTIFY_OFFER_SUBMISSION_ERROR,
|
NOTIFY_OFFER_SUBMISSION_ERROR,
|
||||||
|
NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
|
import AmountInput from "./AmountInput.vue";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
|
components: {
|
||||||
|
AmountInput,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
export default class OfferDialog extends Vue {
|
export default class OfferDialog extends Vue {
|
||||||
@Prop projectId?: string;
|
@Prop projectId?: string;
|
||||||
@@ -122,35 +126,10 @@ export default class OfferDialog extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS classes for unit code selector and increment/decrement buttons
|
* Computed property to get unit options for the select dropdown
|
||||||
* Reduces template complexity for repeated border and styling patterns
|
|
||||||
*/
|
*/
|
||||||
get controlButtonClasses(): string {
|
get unitOptions() {
|
||||||
return "border border-r-0 border-slate-400 bg-slate-200 px-4 py-2";
|
return this.libsUtil.UNIT_SHORT;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS classes for unit code display span
|
|
||||||
* Reduces template complexity for unit code button styling
|
|
||||||
*/
|
|
||||||
get unitCodeDisplayClasses(): string {
|
|
||||||
return "rounded-l border border-r-0 border-slate-400 bg-slate-200 w-1/3 text-center text-blue-500 px-2 py-2";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS classes for amount input field
|
|
||||||
* Reduces template complexity for input styling
|
|
||||||
*/
|
|
||||||
get amountInputClasses(): string {
|
|
||||||
return "w-full border border-r-0 border-slate-400 px-2 py-2 text-center";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS classes for the right-most increment button
|
|
||||||
* Reduces template complexity for border styling
|
|
||||||
*/
|
|
||||||
get incrementButtonClasses(): string {
|
|
||||||
return "rounded-r border border-slate-400 bg-slate-200 px-4 py-2";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -173,13 +152,7 @@ export default class OfferDialog extends Vue {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the decrement button should be visible
|
|
||||||
* Encapsulates conditional logic from template
|
|
||||||
*/
|
|
||||||
get showDecrementButton(): boolean {
|
|
||||||
return this.amountInput !== "0";
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================================
|
// =================================================
|
||||||
// COMPONENT METHODS
|
// COMPONENT METHODS
|
||||||
@@ -226,30 +199,14 @@ export default class OfferDialog extends Vue {
|
|||||||
this.visible = false;
|
this.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cycle through available unit codes
|
|
||||||
*/
|
|
||||||
changeUnitCode() {
|
|
||||||
const units = Object.keys(this.libsUtil.UNIT_SHORT);
|
|
||||||
const index = units.indexOf(this.amountUnitCode);
|
|
||||||
this.amountUnitCode = units[(index + 1) % units.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment the amount input
|
* Handle amount updates from AmountInput component
|
||||||
|
* @param value - New amount value
|
||||||
*/
|
*/
|
||||||
increment() {
|
handleAmountUpdate(value: number) {
|
||||||
this.amountInput = `${(parseFloat(this.amountInput) || 0) + 1}`;
|
this.amountInput = value.toString();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrement the amount input
|
|
||||||
*/
|
|
||||||
decrement() {
|
|
||||||
this.amountInput = `${Math.max(
|
|
||||||
0,
|
|
||||||
(parseFloat(this.amountInput) || 1) - 1,
|
|
||||||
)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,6 +230,28 @@ export default class OfferDialog extends Vue {
|
|||||||
* Confirm and submit the offer
|
* Confirm and submit the offer
|
||||||
*/
|
*/
|
||||||
async confirm() {
|
async confirm() {
|
||||||
|
if (!this.activeDid) {
|
||||||
|
this.notify.error(NOTIFY_OFFER_IDENTITY_REQUIRED.message, TIMEOUTS.LONG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseFloat(this.amountInput) < 0) {
|
||||||
|
this.notify.error(
|
||||||
|
NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT.message,
|
||||||
|
TIMEOUTS.SHORT,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.description && !parseFloat(this.amountInput)) {
|
||||||
|
const message = NOTIFY_OFFER_DESCRIPTION_REQUIRED.message.replace(
|
||||||
|
"{unit}",
|
||||||
|
this.libsUtil.UNIT_LONG[this.amountUnitCode],
|
||||||
|
);
|
||||||
|
this.notify.error(message, TIMEOUTS.SHORT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
this.notify.toast(NOTIFY_OFFER_RECORDING.text, undefined, TIMEOUTS.BRIEF);
|
this.notify.toast(NOTIFY_OFFER_RECORDING.text, undefined, TIMEOUTS.BRIEF);
|
||||||
|
|
||||||
@@ -301,20 +280,6 @@ export default class OfferDialog extends Vue {
|
|||||||
unitCode: string = "HUR",
|
unitCode: string = "HUR",
|
||||||
expirationDateInput?: string,
|
expirationDateInput?: string,
|
||||||
) {
|
) {
|
||||||
if (!this.activeDid) {
|
|
||||||
this.notify.error(NOTIFY_OFFER_IDENTITY_REQUIRED.message, TIMEOUTS.LONG);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!description && !amount) {
|
|
||||||
const message = NOTIFY_OFFER_DESCRIPTION_REQUIRED.message.replace(
|
|
||||||
"{unit}",
|
|
||||||
this.libsUtil.UNIT_LONG[unitCode],
|
|
||||||
);
|
|
||||||
this.notify.error(message, TIMEOUTS.MODAL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await createAndSubmitOffer(
|
const result = await createAndSubmitOffer(
|
||||||
this.axios,
|
this.axios,
|
||||||
@@ -363,7 +328,7 @@ export default class OfferDialog extends Vue {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 1000;
|
z-index: 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
|
|||||||
Reference in New Issue
Block a user