Browse Source

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
offer-validation-logic
Jose Olarte III 5 days ago
parent
commit
0277b05fef
  1. 141
      src/components/OfferDialog.vue

141
src/components/OfferDialog.vue

@ -15,26 +15,25 @@ Raymer */
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
placeholder="Description of what is offered"
/>
<div class="flex flex-row mt-2">
<span :class="unitCodeDisplayClasses" @click="changeUnitCode()">
{{ libsUtil.UNIT_SHORT[amountUnitCode] }}
</span>
<div
v-if="showDecrementButton"
:class="controlButtonClasses"
@click="decrement()"
>
<font-awesome icon="chevron-left" />
</div>
<input
v-model="amountInput"
<div class="flex mb-4">
<AmountInput
:value="parseFloat(amountInput) || 0"
:onUpdateValue="handleAmountUpdate"
data-testId="inputOfferAmount"
type="number"
:class="amountInputClasses"
/>
<div :class="incrementButtonClasses" @click="increment()">
<font-awesome icon="chevron-right" />
</div>
<select
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 class="mt-4 flex justify-center">
<span>
@ -73,10 +72,15 @@ import {
NOTIFY_OFFER_CREATION_ERROR,
NOTIFY_OFFER_SUCCESS,
NOTIFY_OFFER_SUBMISSION_ERROR,
NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT,
} from "@/constants/notifications";
import AmountInput from "./AmountInput.vue";
@Component({
mixins: [PlatformServiceMixin],
components: {
AmountInput,
},
})
export default class OfferDialog extends Vue {
@Prop projectId?: string;
@ -122,35 +126,10 @@ export default class OfferDialog extends Vue {
}
/**
* CSS classes for unit code selector and increment/decrement buttons
* Reduces template complexity for repeated border and styling patterns
*/
get controlButtonClasses(): string {
return "border border-r-0 border-slate-400 bg-slate-200 px-4 py-2";
}
/**
* CSS classes for unit code display span
* Reduces template complexity for unit code button styling
* Computed property to get unit options for the select dropdown
*/
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";
get unitOptions() {
return this.libsUtil.UNIT_SHORT;
}
/**
@ -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
@ -226,30 +199,14 @@ export default class OfferDialog extends Vue {
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
*/
increment() {
this.amountInput = `${(parseFloat(this.amountInput) || 0) + 1}`;
}
/**
* Decrement the amount input
* Handle amount updates from AmountInput component
* @param value - New amount value
*/
decrement() {
this.amountInput = `${Math.max(
0,
(parseFloat(this.amountInput) || 1) - 1,
)}`;
handleAmountUpdate(value: number) {
this.amountInput = value.toString();
}
/**
@ -273,6 +230,28 @@ export default class OfferDialog extends Vue {
* Confirm and submit the offer
*/
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.notify.toast(NOTIFY_OFFER_RECORDING.text, undefined, TIMEOUTS.BRIEF);
@ -301,20 +280,6 @@ export default class OfferDialog extends Vue {
unitCode: string = "HUR",
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 {
const result = await createAndSubmitOffer(
this.axios,
@ -363,7 +328,7 @@ export default class OfferDialog extends Vue {
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
z-index: 50;
}
.dialog {

Loading…
Cancel
Save