diff --git a/src/components/EntityGrid.vue b/src/components/EntityGrid.vue index 795d0639..30de3cc9 100644 --- a/src/components/EntityGrid.vue +++ b/src/components/EntityGrid.vue @@ -14,6 +14,8 @@ projects, and special entities with selection. * * @author Matthew Raymer */ :selectable="youSelectable" :conflicted="youConflicted" :entity-data="youEntityData" + :notify="notify" + :conflict-context="conflictContext" @entity-selected="handleEntitySelected" /> @@ -23,6 +25,8 @@ projects, and special entities with selection. * * @author Matthew Raymer */ label="Unnamed" icon="circle-question" :entity-data="unnamedEntityData" + :notify="notify" + :conflict-context="conflictContext" @entity-selected="handleEntitySelected" /> @@ -38,23 +42,27 @@ projects, and special entities with selection. * * @author Matthew Raymer */ @@ -77,6 +85,7 @@ import SpecialEntityCard from "./SpecialEntityCard.vue"; import ShowAllCard from "./ShowAllCard.vue"; import { Contact } from "../db/tables/contacts"; import { PlanData } from "../interfaces/records"; +import { NotificationIface } from "../constants/app"; /** * EntityGrid - Unified grid layout for displaying people or projects @@ -88,6 +97,7 @@ import { PlanData } from "../interfaces/records"; * - Empty state messaging * - Show All navigation * - Event delegation for entity selection + * - Warning notifications for conflicted entities */ @Component({ components: { @@ -142,6 +152,14 @@ export default class EntityGrid extends Vue { @Prop({ default: () => ({}) }) showAllQueryParams!: Record; + /** Notification function from parent component */ + @Prop() + notify?: (notification: NotificationIface, timeout?: number) => void; + + /** Context for conflict messages (e.g., "giver", "recipient") */ + @Prop({ default: "other party" }) + conflictContext!: string; + /** * Computed CSS classes for the grid layout */ @@ -241,7 +259,7 @@ export default class EntityGrid extends Vue { handleEntitySelected(event: { type: string; entityType: string; - data: any; + data: { did?: string; name: string }; }): void { this.emitEntitySelected({ type: "special", @@ -253,7 +271,15 @@ export default class EntityGrid extends Vue { // Emit methods using @Emit decorator @Emit("entity-selected") - emitEntitySelected(data: any): any { + emitEntitySelected(data: { + type: "person" | "project" | "special"; + entityType?: string; + data: Contact | PlanData | { did?: string; name: string }; + }): { + type: "person" | "project" | "special"; + entityType?: string; + data: Contact | PlanData | { did?: string; name: string }; + } { return data; } } diff --git a/src/components/EntitySelectionStep.vue b/src/components/EntitySelectionStep.vue index 9798f42c..3c415ce2 100644 --- a/src/components/EntitySelectionStep.vue +++ b/src/components/EntitySelectionStep.vue @@ -19,6 +19,8 @@ with dynamic labeling and grid display. * * @author Matthew Raymer */ :you-selectable="youSelectable" :show-all-route="showAllRoute" :show-all-query-params="showAllQueryParams" + :notify="notify" + :conflict-context="conflictContext" @entity-selected="handleEntitySelected" /> @@ -36,6 +38,7 @@ import { Component, Prop, Vue, Emit } from "vue-facing-decorator"; import EntityGrid from "./EntityGrid.vue"; import { Contact } from "../db/tables/contacts"; import { PlanData } from "../interfaces/records"; +import { NotificationIface } from "../constants/app"; /** * Entity data interface for giver/receiver @@ -67,6 +70,7 @@ interface EntitySelectionEvent { * - Show All navigation with context preservation * - Cancel functionality * - Event delegation for entity selection + * - Warning notifications for conflicted entities */ @Component({ components: { @@ -130,6 +134,10 @@ export default class EntitySelectionStep extends Vue { @Prop() receiver?: EntityData | null; + /** Notification function from parent component */ + @Prop() + notify?: (notification: NotificationIface, timeout?: number) => void; + /** * Computed step label based on context */ @@ -143,6 +151,17 @@ export default class EntitySelectionStep extends Vue { } } + /** + * Computed conflict context for better error messages + */ + get conflictContext(): string { + if (this.stepType === "giver") { + return "recipient"; + } else { + return "giver"; + } + } + /** * Whether to show projects in the grid */ diff --git a/src/components/GiftedDialog.vue b/src/components/GiftedDialog.vue index 4748414f..8237bbe8 100644 --- a/src/components/GiftedDialog.vue +++ b/src/components/GiftedDialog.vue @@ -18,6 +18,7 @@ :to-project-id="toProjectId" :giver="giver" :receiver="receiver" + :notify="$notify" @entity-selected="handleEntitySelected" @cancel="cancel" /> diff --git a/src/components/PersonCard.vue b/src/components/PersonCard.vue index e391cfff..1f3bf595 100644 --- a/src/components/PersonCard.vue +++ b/src/components/PersonCard.vue @@ -34,6 +34,7 @@ conflict detection. * * @author Matthew Raymer */ import { Component, Prop, Vue, Emit } from "vue-facing-decorator"; import EntityIcon from "./EntityIcon.vue"; import { Contact } from "../db/tables/contacts"; +import { NotificationIface } from "../constants/app"; /** * PersonCard - Individual person display with selection capability @@ -44,6 +45,7 @@ import { Contact } from "../db/tables/contacts"; * - Time icon overlay for contacts * - Click event handling * - Emits click events for parent handling + * - Warning notifications for conflicted entities */ @Component({ components: { @@ -67,6 +69,14 @@ export default class PersonCard extends Vue { @Prop({ default: false }) showTimeIcon!: boolean; + /** Notification function from parent component */ + @Prop() + notify?: (notification: NotificationIface, timeout?: number) => void; + + /** Context for conflict messages (e.g., "giver", "recipient") */ + @Prop({ default: "other party" }) + conflictContext!: string; + /** * Computed CSS classes for the card */ @@ -92,11 +102,22 @@ export default class PersonCard extends Vue { } /** - * Handle card click - only emit if selectable and not conflicted + * Handle card click - emit if selectable and not conflicted, show warning if conflicted */ handleClick(): void { if (this.selectable && !this.conflicted) { this.emitPersonSelected(this.person); + } else if (this.conflicted && this.notify) { + // Show warning notification for conflicted entity + this.notify( + { + group: "alert", + type: "warning", + title: "Cannot Select", + text: `You cannot select "${this.person.name || this.person.did || "Unnamed"}" because they are already selected as the ${this.conflictContext}.`, + }, + 3000, + ); } } diff --git a/src/components/SpecialEntityCard.vue b/src/components/SpecialEntityCard.vue index 79bc2bc0..1d475229 100644 --- a/src/components/SpecialEntityCard.vue +++ b/src/components/SpecialEntityCard.vue @@ -13,6 +13,7 @@ conflict detection and selection capability. * * @author Matthew Raymer */