forked from jsnbuchanan/crowd-funder-for-time-pwa
Fix TypeScript any types and remove deprecated Dexie code
- Replace Record<string, any> with Record<string, string> for query params - Fix error handling from catch(err: any) to catch(err: unknown) - Add EntityData interface for proper entity typing - Update EntitySelectionEvent interface with union types - Remove USE_DEXIE_DB conditionals and unused import - Clean up database service calls removing Dexie fallbacks Resolves 9 TypeScript any type warnings, improves type safety across entity selection components, and removes deprecated database migration code.
This commit is contained in:
264
src/components/EntityGrid.vue
Normal file
264
src/components/EntityGrid.vue
Normal file
@@ -0,0 +1,264 @@
|
||||
/** * EntityGrid.vue - Unified entity grid layout component * * Extracted from
|
||||
GiftedDialog.vue to provide a reusable grid layout * for displaying people,
|
||||
projects, and special entities with selection. * * @author Matthew Raymer */
|
||||
<template>
|
||||
<ul :class="gridClasses">
|
||||
<!-- Special entities (You, Unnamed) for people grids -->
|
||||
<template v-if="entityType === 'people'">
|
||||
<!-- "You" entity -->
|
||||
<SpecialEntityCard
|
||||
v-if="showYouEntity"
|
||||
entity-type="you"
|
||||
label="You"
|
||||
icon="hand"
|
||||
:selectable="youSelectable"
|
||||
:conflicted="youConflicted"
|
||||
:entity-data="youEntityData"
|
||||
@entity-selected="handleEntitySelected"
|
||||
/>
|
||||
|
||||
<!-- "Unnamed" entity -->
|
||||
<SpecialEntityCard
|
||||
entity-type="unnamed"
|
||||
label="Unnamed"
|
||||
icon="circle-question"
|
||||
:entity-data="unnamedEntityData"
|
||||
@entity-selected="handleEntitySelected"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Empty state message -->
|
||||
<li
|
||||
v-if="entities.length === 0"
|
||||
class="text-xs text-slate-500 italic col-span-full"
|
||||
>
|
||||
{{ emptyStateMessage }}
|
||||
</li>
|
||||
|
||||
<!-- Entity cards (people or projects) -->
|
||||
<template v-if="entityType === 'people'">
|
||||
<PersonCard
|
||||
v-for="person in displayedEntities"
|
||||
:key="person.did"
|
||||
:person="person"
|
||||
:conflicted="isPersonConflicted(person.did)"
|
||||
:show-time-icon="true"
|
||||
@person-selected="handlePersonSelected"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else-if="entityType === 'projects'">
|
||||
<ProjectCard
|
||||
v-for="project in displayedEntities"
|
||||
:key="project.handleId"
|
||||
:project="project"
|
||||
:active-did="activeDid"
|
||||
:all-my-dids="allMyDids"
|
||||
:all-contacts="allContacts"
|
||||
@project-selected="handleProjectSelected"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Show All navigation -->
|
||||
<ShowAllCard
|
||||
v-if="shouldShowAll"
|
||||
:entity-type="entityType"
|
||||
:route-name="showAllRoute"
|
||||
:query-params="showAllQueryParams"
|
||||
/>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Emit } from "vue-facing-decorator";
|
||||
import PersonCard from "./PersonCard.vue";
|
||||
import ProjectCard from "./ProjectCard.vue";
|
||||
import SpecialEntityCard from "./SpecialEntityCard.vue";
|
||||
import ShowAllCard from "./ShowAllCard.vue";
|
||||
import { Contact } from "../db/tables/contacts";
|
||||
import { PlanData } from "../interfaces/records";
|
||||
|
||||
/**
|
||||
* EntityGrid - Unified grid layout for displaying people or projects
|
||||
*
|
||||
* Features:
|
||||
* - Responsive grid layout for people/projects
|
||||
* - Special entity integration (You, Unnamed)
|
||||
* - Conflict detection integration
|
||||
* - Empty state messaging
|
||||
* - Show All navigation
|
||||
* - Event delegation for entity selection
|
||||
*/
|
||||
@Component({
|
||||
components: {
|
||||
PersonCard,
|
||||
ProjectCard,
|
||||
SpecialEntityCard,
|
||||
ShowAllCard,
|
||||
},
|
||||
})
|
||||
export default class EntityGrid extends Vue {
|
||||
/** Type of entities to display */
|
||||
@Prop({ required: true })
|
||||
entityType!: "people" | "projects";
|
||||
|
||||
/** Array of entities to display */
|
||||
@Prop({ required: true })
|
||||
entities!: Contact[] | PlanData[];
|
||||
|
||||
/** Maximum number of entities to display */
|
||||
@Prop({ default: 10 })
|
||||
maxItems!: number;
|
||||
|
||||
/** Active user's DID */
|
||||
@Prop({ required: true })
|
||||
activeDid!: string;
|
||||
|
||||
/** All user's DIDs */
|
||||
@Prop({ required: true })
|
||||
allMyDids!: string[];
|
||||
|
||||
/** All contacts */
|
||||
@Prop({ required: true })
|
||||
allContacts!: Contact[];
|
||||
|
||||
/** Function to check if a person DID would create a conflict */
|
||||
@Prop({ required: true })
|
||||
conflictChecker!: (did: string) => boolean;
|
||||
|
||||
/** Whether to show the "You" entity for people grids */
|
||||
@Prop({ default: true })
|
||||
showYouEntity!: boolean;
|
||||
|
||||
/** Whether the "You" entity is selectable */
|
||||
@Prop({ default: true })
|
||||
youSelectable!: boolean;
|
||||
|
||||
/** Route name for "Show All" navigation */
|
||||
@Prop({ default: "" })
|
||||
showAllRoute!: string;
|
||||
|
||||
/** Query parameters for "Show All" navigation */
|
||||
@Prop({ default: () => ({}) })
|
||||
showAllQueryParams!: Record<string, string>;
|
||||
|
||||
/**
|
||||
* Computed CSS classes for the grid layout
|
||||
*/
|
||||
get gridClasses(): string {
|
||||
const baseClasses = "grid gap-x-2 gap-y-4 text-center mb-4";
|
||||
|
||||
if (this.entityType === "projects") {
|
||||
return `${baseClasses} grid-cols-3 md:grid-cols-4`;
|
||||
} else {
|
||||
return `${baseClasses} grid-cols-4 sm:grid-cols-5 md:grid-cols-6`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed entities to display (limited by maxItems)
|
||||
*/
|
||||
get displayedEntities(): Contact[] | PlanData[] {
|
||||
const maxDisplay = this.entityType === "projects" ? 7 : this.maxItems;
|
||||
return this.entities.slice(0, maxDisplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed empty state message based on entity type
|
||||
*/
|
||||
get emptyStateMessage(): string {
|
||||
if (this.entityType === "projects") {
|
||||
return "(No projects found.)";
|
||||
} else {
|
||||
return "(Add friends to see more people worthy of recognition.)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show the "Show All" navigation
|
||||
*/
|
||||
get shouldShowAll(): boolean {
|
||||
return this.entities.length > 0 && this.showAllRoute !== "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the "You" entity is conflicted
|
||||
*/
|
||||
get youConflicted(): boolean {
|
||||
return this.conflictChecker(this.activeDid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity data for the "You" special entity
|
||||
*/
|
||||
get youEntityData(): { did: string; name: string } {
|
||||
return {
|
||||
did: this.activeDid,
|
||||
name: "You",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity data for the "Unnamed" special entity
|
||||
*/
|
||||
get unnamedEntityData(): { did: string; name: string } {
|
||||
return {
|
||||
did: "",
|
||||
name: "Unnamed",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a person DID is conflicted
|
||||
*/
|
||||
isPersonConflicted(did: string): boolean {
|
||||
return this.conflictChecker(did);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle person selection from PersonCard
|
||||
*/
|
||||
handlePersonSelected(person: Contact): void {
|
||||
this.emitEntitySelected({
|
||||
type: "person",
|
||||
data: person,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle project selection from ProjectCard
|
||||
*/
|
||||
handleProjectSelected(project: PlanData): void {
|
||||
this.emitEntitySelected({
|
||||
type: "project",
|
||||
data: project,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle special entity selection from SpecialEntityCard
|
||||
*/
|
||||
handleEntitySelected(event: {
|
||||
type: string;
|
||||
entityType: string;
|
||||
data: any;
|
||||
}): void {
|
||||
this.emitEntitySelected({
|
||||
type: "special",
|
||||
entityType: event.entityType,
|
||||
data: event.data,
|
||||
});
|
||||
}
|
||||
|
||||
// Emit methods using @Emit decorator
|
||||
|
||||
@Emit("entity-selected")
|
||||
emitEntitySelected(data: any): any {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Grid-specific styles if needed */
|
||||
</style>
|
||||
Reference in New Issue
Block a user