Browse Source
- Create EntitySelectionStep.vue for complete step 1 interface * Dynamic step labeling based on context (giver/recipient/projects) * EntityGrid integration for unified entity display * Conflict detection and prevention with visual feedback * Special entity handling (You, Unnamed) with proper conditions * Show All navigation with context preservation through query params * Cancel functionality with event delegation * Comprehensive prop interface for all dialog contexts - Create GiftDetailsStep.vue for complete step 2 interface * Entity summary display using EntitySummaryButton components * Gift description input with placeholder support * AmountInput integration with increment/decrement controls * Unit code selection (HUR, USD, BTC, BX, ETH) * Photo & more options navigation with computed route * Conflict detection and warning display * Form validation and submission with disabled states * Local reactive state management with prop synchronization * Edit entity functionality with structured events - Update GiftedDialog-Decomposition-Plan.md * Mark Phase 3 as completed with detailed specifications * Add comprehensive integration examples for step components * Update component count and progress tracking * Add usage patterns for EntitySelectionStep and GiftDetailsStep * Update project status to 'Integration Phase Ready' Phase 3 completes the major UI section extraction, creating two comprehensive step components that can directly replace the existing step logic in GiftedDialog. These components maintain all existing functionality while providing clean, testable interfaces. Components: 9 total (4 Phase 1 + 3 Phase 2 + 2 Phase 3) Next: Integration phase - Replace GiftedDialog step logic with new componentsmatthew-scratch-2025-06-28
3 changed files with 692 additions and 17 deletions
@ -0,0 +1,235 @@ |
|||||
|
/** |
||||
|
* EntitySelectionStep.vue - Entity selection step component |
||||
|
* |
||||
|
* Extracted from GiftedDialog.vue to handle the complete step 1 |
||||
|
* entity selection interface with dynamic labeling and grid display. |
||||
|
* |
||||
|
* @author Matthew Raymer |
||||
|
*/ |
||||
|
<template> |
||||
|
<div id="sectionGiftedGiver"> |
||||
|
<label class="block font-bold mb-4"> |
||||
|
{{ stepLabel }} |
||||
|
</label> |
||||
|
|
||||
|
<EntityGrid |
||||
|
:entity-type="shouldShowProjects ? 'projects' : 'people'" |
||||
|
:entities="shouldShowProjects ? projects : allContacts" |
||||
|
:max-items="10" |
||||
|
:active-did="activeDid" |
||||
|
:all-my-dids="allMyDids" |
||||
|
:all-contacts="allContacts" |
||||
|
:conflict-checker="conflictChecker" |
||||
|
:show-you-entity="shouldShowYouEntity" |
||||
|
:you-selectable="youSelectable" |
||||
|
:show-all-route="showAllRoute" |
||||
|
:show-all-query-params="showAllQueryParams" |
||||
|
@entity-selected="handleEntitySelected" |
||||
|
/> |
||||
|
|
||||
|
<button |
||||
|
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg" |
||||
|
@click="handleCancel" |
||||
|
> |
||||
|
Cancel |
||||
|
</button> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { Component, Prop, Vue } from "vue-facing-decorator"; |
||||
|
import EntityGrid from "./EntityGrid.vue"; |
||||
|
import { Contact } from "@/interfaces/contact"; |
||||
|
import { PlanData } from "@/interfaces/plan-data"; |
||||
|
|
||||
|
/** |
||||
|
* Entity selection event data structure |
||||
|
*/ |
||||
|
interface EntitySelectionEvent { |
||||
|
type: "person" | "project" | "special"; |
||||
|
entityType?: string; |
||||
|
data: any; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* EntitySelectionStep - Complete step 1 entity selection interface |
||||
|
* |
||||
|
* Features: |
||||
|
* - Dynamic step labeling based on context |
||||
|
* - EntityGrid integration for unified entity display |
||||
|
* - Conflict detection and prevention |
||||
|
* - Special entity handling (You, Unnamed) |
||||
|
* - Show All navigation with context preservation |
||||
|
* - Cancel functionality |
||||
|
* - Event delegation for entity selection |
||||
|
*/ |
||||
|
@Component({ |
||||
|
components: { |
||||
|
EntityGrid, |
||||
|
}, |
||||
|
}) |
||||
|
export default class EntitySelectionStep extends Vue { |
||||
|
/** Type of step: 'giver' or 'recipient' */ |
||||
|
@Prop({ required: true }) |
||||
|
stepType!: "giver" | "recipient"; |
||||
|
|
||||
|
/** Type of giver entity: 'person' or 'project' */ |
||||
|
@Prop({ required: true }) |
||||
|
giverEntityType!: "person" | "project"; |
||||
|
|
||||
|
/** Type of recipient entity: 'person' or 'project' */ |
||||
|
@Prop({ required: true }) |
||||
|
recipientEntityType!: "person" | "project"; |
||||
|
|
||||
|
/** Whether to show projects instead of people */ |
||||
|
@Prop({ default: false }) |
||||
|
showProjects!: boolean; |
||||
|
|
||||
|
/** Whether this is from a project view */ |
||||
|
@Prop({ default: false }) |
||||
|
isFromProjectView!: boolean; |
||||
|
|
||||
|
/** Array of available projects */ |
||||
|
@Prop({ required: true }) |
||||
|
projects!: PlanData[]; |
||||
|
|
||||
|
/** Array of available contacts */ |
||||
|
@Prop({ required: true }) |
||||
|
allContacts!: Contact[]; |
||||
|
|
||||
|
/** Active user's DID */ |
||||
|
@Prop({ required: true }) |
||||
|
activeDid!: string; |
||||
|
|
||||
|
/** All user's DIDs */ |
||||
|
@Prop({ required: true }) |
||||
|
allMyDids!: string[]; |
||||
|
|
||||
|
/** Function to check if a DID would create a conflict */ |
||||
|
@Prop({ required: true }) |
||||
|
conflictChecker!: (did: string) => boolean; |
||||
|
|
||||
|
/** Project ID for context (giver) */ |
||||
|
@Prop({ default: "" }) |
||||
|
fromProjectId!: string; |
||||
|
|
||||
|
/** Project ID for context (recipient) */ |
||||
|
@Prop({ default: "" }) |
||||
|
toProjectId!: string; |
||||
|
|
||||
|
/** Current giver entity for context */ |
||||
|
@Prop() |
||||
|
giver?: any; |
||||
|
|
||||
|
/** Current receiver entity for context */ |
||||
|
@Prop() |
||||
|
receiver?: any; |
||||
|
|
||||
|
/** |
||||
|
* Computed step label based on context |
||||
|
*/ |
||||
|
get stepLabel(): string { |
||||
|
if (this.stepType === "recipient") { |
||||
|
return "Choose who received the gift:"; |
||||
|
} else if (this.showProjects) { |
||||
|
return "Choose a project benefitted from:"; |
||||
|
} else { |
||||
|
return "Choose a person received from:"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Whether to show projects in the grid |
||||
|
*/ |
||||
|
get shouldShowProjects(): boolean { |
||||
|
return ( |
||||
|
(this.stepType === "giver" && this.giverEntityType === "project") || |
||||
|
(this.stepType === "recipient" && this.recipientEntityType === "project") |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Whether to show the "You" entity |
||||
|
*/ |
||||
|
get shouldShowYouEntity(): boolean { |
||||
|
return ( |
||||
|
this.stepType === "recipient" || |
||||
|
(this.stepType === "giver" && this.isFromProjectView) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Whether the "You" entity is selectable |
||||
|
*/ |
||||
|
get youSelectable(): boolean { |
||||
|
return !this.conflictChecker(this.activeDid); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Route name for "Show All" navigation |
||||
|
*/ |
||||
|
get showAllRoute(): string { |
||||
|
if (this.shouldShowProjects) { |
||||
|
return "discover"; |
||||
|
} else if (this.allContacts.length > 0) { |
||||
|
return "contact-gift"; |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Query parameters for "Show All" navigation |
||||
|
*/ |
||||
|
get showAllQueryParams(): Record<string, any> { |
||||
|
if (this.shouldShowProjects) { |
||||
|
return {}; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
stepType: this.stepType, |
||||
|
giverEntityType: this.giverEntityType, |
||||
|
recipientEntityType: this.recipientEntityType, |
||||
|
...(this.stepType === "giver" |
||||
|
? { |
||||
|
recipientProjectId: this.toProjectId, |
||||
|
recipientProjectName: this.receiver?.name, |
||||
|
recipientProjectImage: this.receiver?.image, |
||||
|
recipientProjectHandleId: this.receiver?.handleId, |
||||
|
recipientDid: this.receiver?.did, |
||||
|
} |
||||
|
: { |
||||
|
giverProjectId: this.fromProjectId, |
||||
|
giverProjectName: this.giver?.name, |
||||
|
giverProjectImage: this.giver?.image, |
||||
|
giverProjectHandleId: this.giver?.handleId, |
||||
|
giverDid: this.giver?.did, |
||||
|
}), |
||||
|
fromProjectId: this.fromProjectId, |
||||
|
toProjectId: this.toProjectId, |
||||
|
showProjects: this.showProjects.toString(), |
||||
|
isFromProjectView: this.isFromProjectView.toString(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle entity selection from EntityGrid |
||||
|
*/ |
||||
|
handleEntitySelected(event: EntitySelectionEvent): void { |
||||
|
this.$emit("entity-selected", { |
||||
|
stepType: this.stepType, |
||||
|
...event, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle cancel button click |
||||
|
*/ |
||||
|
handleCancel(): void { |
||||
|
this.$emit("cancel"); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* Component-specific styles if needed */ |
||||
|
</style> |
@ -0,0 +1,371 @@ |
|||||
|
/** |
||||
|
* GiftDetailsStep.vue - Gift details step component |
||||
|
* |
||||
|
* Extracted from GiftedDialog.vue to handle the complete step 2 |
||||
|
* gift details form interface with entity summaries and validation. |
||||
|
* |
||||
|
* @author Matthew Raymer |
||||
|
*/ |
||||
|
<template> |
||||
|
<div id="sectionGiftedGift"> |
||||
|
<!-- Entity Summary Buttons --> |
||||
|
<div class="grid grid-cols-2 gap-2 mb-4"> |
||||
|
<!-- Giver Button --> |
||||
|
<EntitySummaryButton |
||||
|
:entity="giver" |
||||
|
:entity-type="giverEntityType" |
||||
|
:label="giverLabel" |
||||
|
:editable="canEditGiver" |
||||
|
@edit-requested="handleEditGiver" |
||||
|
/> |
||||
|
|
||||
|
<!-- Recipient Button --> |
||||
|
<EntitySummaryButton |
||||
|
:entity="receiver" |
||||
|
:entity-type="recipientEntityType" |
||||
|
:label="recipientLabel" |
||||
|
:editable="canEditRecipient" |
||||
|
@edit-requested="handleEditRecipient" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Gift Description Input --> |
||||
|
<input |
||||
|
v-model="localDescription" |
||||
|
type="text" |
||||
|
class="block w-full rounded border border-slate-400 px-3 py-2 mb-4 placeholder:italic" |
||||
|
:placeholder="prompt || 'What was given?'" |
||||
|
@input="handleDescriptionChange" |
||||
|
/> |
||||
|
|
||||
|
<!-- Amount Input and Unit Selection --> |
||||
|
<div class="flex mb-4"> |
||||
|
<AmountInput |
||||
|
:value="localAmount" |
||||
|
:min="0" |
||||
|
input-id="inputGivenAmount" |
||||
|
@update:value="handleAmountChange" |
||||
|
/> |
||||
|
|
||||
|
<select |
||||
|
v-model="localUnitCode" |
||||
|
class="flex-1 rounded border border-slate-400 ms-2 px-3 py-2" |
||||
|
@change="handleUnitCodeChange" |
||||
|
> |
||||
|
<option value="HUR">Hours</option> |
||||
|
<option value="USD">US $</option> |
||||
|
<option value="BTC">BTC</option> |
||||
|
<option value="BX">BX</option> |
||||
|
<option value="ETH">ETH</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Photo & More Options Link --> |
||||
|
<router-link |
||||
|
:to="photoOptionsRoute" |
||||
|
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg mb-4" |
||||
|
> |
||||
|
Photo & more options… |
||||
|
</router-link> |
||||
|
|
||||
|
<!-- Sign & Send Info --> |
||||
|
<p class="text-center text-sm mb-4"> |
||||
|
<b class="font-medium">Sign & Send</b> to publish to the world |
||||
|
<font-awesome |
||||
|
icon="circle-info" |
||||
|
class="fa-fw text-blue-500 text-base cursor-pointer" |
||||
|
@click="handleExplainData" |
||||
|
/> |
||||
|
</p> |
||||
|
|
||||
|
<!-- Conflict Warning --> |
||||
|
<div |
||||
|
v-if="hasConflict" |
||||
|
class="mb-4 p-3 bg-red-50 border border-red-200 rounded-md" |
||||
|
> |
||||
|
<p class="text-red-700 text-sm text-center"> |
||||
|
<font-awesome icon="exclamation-triangle" class="fa-fw mr-1" /> |
||||
|
Cannot record: Same person selected as both giver and recipient |
||||
|
</p> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Action Buttons --> |
||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> |
||||
|
<button |
||||
|
:disabled="hasConflict" |
||||
|
:class="submitButtonClasses" |
||||
|
@click="handleSubmit" |
||||
|
> |
||||
|
Sign & Send |
||||
|
</button> |
||||
|
<button |
||||
|
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg" |
||||
|
@click="handleCancel" |
||||
|
> |
||||
|
Cancel |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { Component, Prop, Vue, Watch } from "vue-facing-decorator"; |
||||
|
import EntitySummaryButton from "./EntitySummaryButton.vue"; |
||||
|
import AmountInput from "./AmountInput.vue"; |
||||
|
import { RouteLocationRaw } from "vue-router"; |
||||
|
|
||||
|
/** |
||||
|
* Entity data interface for giver/receiver |
||||
|
*/ |
||||
|
interface EntityData { |
||||
|
did?: string; |
||||
|
handleId?: string; |
||||
|
name?: string; |
||||
|
image?: string; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* GiftDetailsStep - Complete step 2 gift details form interface |
||||
|
* |
||||
|
* Features: |
||||
|
* - Entity summary display with edit capability |
||||
|
* - Gift description input with placeholder support |
||||
|
* - Amount input with increment/decrement controls |
||||
|
* - Unit code selection (HUR, USD, BTC, etc.) |
||||
|
* - Photo & more options navigation |
||||
|
* - Conflict detection and warning display |
||||
|
* - Form validation and submission |
||||
|
* - Cancel functionality |
||||
|
*/ |
||||
|
@Component({ |
||||
|
components: { |
||||
|
EntitySummaryButton, |
||||
|
AmountInput, |
||||
|
}, |
||||
|
}) |
||||
|
export default class GiftDetailsStep extends Vue { |
||||
|
/** Giver entity data */ |
||||
|
@Prop({ required: true }) |
||||
|
giver!: EntityData | null; |
||||
|
|
||||
|
/** Receiver entity data */ |
||||
|
@Prop({ required: true }) |
||||
|
receiver!: EntityData | null; |
||||
|
|
||||
|
/** Type of giver entity: 'person' or 'project' */ |
||||
|
@Prop({ required: true }) |
||||
|
giverEntityType!: "person" | "project"; |
||||
|
|
||||
|
/** Type of recipient entity: 'person' or 'project' */ |
||||
|
@Prop({ required: true }) |
||||
|
recipientEntityType!: "person" | "project"; |
||||
|
|
||||
|
/** Gift description */ |
||||
|
@Prop({ default: "" }) |
||||
|
description!: string; |
||||
|
|
||||
|
/** Gift amount */ |
||||
|
@Prop({ default: 0 }) |
||||
|
amount!: number; |
||||
|
|
||||
|
/** Unit code (HUR, USD, etc.) */ |
||||
|
@Prop({ default: "HUR" }) |
||||
|
unitCode!: string; |
||||
|
|
||||
|
/** Input placeholder text */ |
||||
|
@Prop({ default: "" }) |
||||
|
prompt!: string; |
||||
|
|
||||
|
/** Whether this is from a project view */ |
||||
|
@Prop({ default: false }) |
||||
|
isFromProjectView!: boolean; |
||||
|
|
||||
|
/** Whether there's a conflict between giver and receiver */ |
||||
|
@Prop({ default: false }) |
||||
|
hasConflict!: boolean; |
||||
|
|
||||
|
/** Offer ID for context */ |
||||
|
@Prop({ default: "" }) |
||||
|
offerId!: string; |
||||
|
|
||||
|
/** Project ID for context (giver) */ |
||||
|
@Prop({ default: "" }) |
||||
|
fromProjectId!: string; |
||||
|
|
||||
|
/** Project ID for context (recipient) */ |
||||
|
@Prop({ default: "" }) |
||||
|
toProjectId!: string; |
||||
|
|
||||
|
/** Local reactive copies of props for v-model */ |
||||
|
private localDescription: string = ""; |
||||
|
private localAmount: number = 0; |
||||
|
private localUnitCode: string = "HUR"; |
||||
|
|
||||
|
/** |
||||
|
* Initialize local values from props |
||||
|
*/ |
||||
|
mounted(): void { |
||||
|
this.localDescription = this.description; |
||||
|
this.localAmount = this.amount; |
||||
|
this.localUnitCode = this.unitCode; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Watch for external prop changes |
||||
|
*/ |
||||
|
@Watch("description") |
||||
|
onDescriptionChange(newValue: string): void { |
||||
|
this.localDescription = newValue; |
||||
|
} |
||||
|
|
||||
|
@Watch("amount") |
||||
|
onAmountChange(newValue: number): void { |
||||
|
this.localAmount = newValue; |
||||
|
} |
||||
|
|
||||
|
@Watch("unitCode") |
||||
|
onUnitCodeChange(newValue: string): void { |
||||
|
this.localUnitCode = newValue; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Computed label for giver entity |
||||
|
*/ |
||||
|
get giverLabel(): string { |
||||
|
return this.giverEntityType === "project" |
||||
|
? "Benefited from:" |
||||
|
: "Received from:"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Computed label for recipient entity |
||||
|
*/ |
||||
|
get recipientLabel(): string { |
||||
|
return this.recipientEntityType === "project" |
||||
|
? "Given to project:" |
||||
|
: "Given to:"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Whether the giver can be edited |
||||
|
*/ |
||||
|
get canEditGiver(): boolean { |
||||
|
return !(this.isFromProjectView && this.giverEntityType === "project"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Whether the recipient can be edited |
||||
|
*/ |
||||
|
get canEditRecipient(): boolean { |
||||
|
return this.recipientEntityType === "person"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Computed CSS classes for submit button |
||||
|
*/ |
||||
|
get submitButtonClasses(): string { |
||||
|
if (this.hasConflict) { |
||||
|
return "block w-full text-center text-md uppercase font-bold bg-gradient-to-b from-slate-300 to-slate-500 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-slate-400 px-1.5 py-2 rounded-lg cursor-not-allowed"; |
||||
|
} |
||||
|
return "block w-full text-center text-md uppercase font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-lg"; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Computed route for photo & more options |
||||
|
*/ |
||||
|
get photoOptionsRoute(): RouteLocationRaw { |
||||
|
return { |
||||
|
name: "gifted-details", |
||||
|
query: { |
||||
|
amountInput: this.localAmount.toString(), |
||||
|
description: this.localDescription, |
||||
|
giverDid: this.giverEntityType === "person" ? this.giver?.did : undefined, |
||||
|
giverName: this.giver?.name, |
||||
|
offerId: this.offerId, |
||||
|
fulfillsProjectId: this.giverEntityType === "person" && this.recipientEntityType === "project" |
||||
|
? this.toProjectId |
||||
|
: undefined, |
||||
|
providerProjectId: this.giverEntityType === "project" && this.recipientEntityType === "person" |
||||
|
? this.giver?.handleId |
||||
|
: this.fromProjectId, |
||||
|
recipientDid: this.receiver?.did, |
||||
|
recipientName: this.receiver?.name, |
||||
|
unitCode: this.localUnitCode, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle description input changes |
||||
|
*/ |
||||
|
handleDescriptionChange(): void { |
||||
|
this.$emit("update:description", this.localDescription); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle amount input changes |
||||
|
*/ |
||||
|
handleAmountChange(newAmount: number): void { |
||||
|
this.localAmount = newAmount; |
||||
|
this.$emit("update:amount", newAmount); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle unit code selection changes |
||||
|
*/ |
||||
|
handleUnitCodeChange(): void { |
||||
|
this.$emit("update:unitCode", this.localUnitCode); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle giver edit request |
||||
|
*/ |
||||
|
handleEditGiver(): void { |
||||
|
this.$emit("edit-entity", { |
||||
|
entityType: "giver", |
||||
|
currentEntity: this.giver, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle recipient edit request |
||||
|
*/ |
||||
|
handleEditRecipient(): void { |
||||
|
this.$emit("edit-entity", { |
||||
|
entityType: "recipient", |
||||
|
currentEntity: this.receiver, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle explain data info click |
||||
|
*/ |
||||
|
handleExplainData(): void { |
||||
|
this.$emit("explain-data"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle form submission |
||||
|
*/ |
||||
|
handleSubmit(): void { |
||||
|
if (!this.hasConflict) { |
||||
|
this.$emit("submit", { |
||||
|
description: this.localDescription, |
||||
|
amount: this.localAmount, |
||||
|
unitCode: this.localUnitCode, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle cancel button click |
||||
|
*/ |
||||
|
handleCancel(): void { |
||||
|
this.$emit("cancel"); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* Component-specific styles if needed */ |
||||
|
</style> |
Loading…
Reference in new issue