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 6 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" 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 {
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
*/ */
get unitCodeDisplayClasses(): string { get unitOptions() {
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"; return this.libsUtil.UNIT_SHORT;
}
/**
* 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
*/
increment() {
this.amountInput = `${(parseFloat(this.amountInput) || 0) + 1}`;
}
/** /**
* Decrement the amount input * Handle amount updates from AmountInput component
* @param value - New amount value
*/ */
decrement() { handleAmountUpdate(value: number) {
this.amountInput = `${Math.max( this.amountInput = value.toString();
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 {

Loading…
Cancel
Save