Working log in/route guard

This commit is contained in:
2025-09-04 08:03:24 -04:00
parent 992a1a217d
commit dc1ee95827
37 changed files with 1283 additions and 1191 deletions

View File

@@ -1,53 +1,100 @@
<!-- SearchResults.vue -->
<!-- components/SearchResults.vue -->
<template>
<div class="fixed top-16 left-1/2 -translate-x-1/2 w-full max-w-2xl px-4">
<div class="overflow-x-auto bg-base-100 rounded-lg shadow-2xl border border-gray-700">
<table class="table w-full">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<!-- It now reads `searchResults` directly from the store -->
<tr v-for="user in searchStore.searchResults" :key="user.id" class="hover cursor-pointer" @click="viewProfile(user.id)">
<td>
<div class="font-bold">{{ user.customer_first_name }} {{ user.customer_last_name }}</div>
</td>
<td>
<div>{{ user.customer_address }}</div>
<div class="text-sm opacity-70">{{ user.customer_town }}, {{ user.customer_state }}</div>
</td>
<td>{{ user.customer_phone_number }}</td>
</tr>
</tbody>
</table>
<div
ref="searchContainer"
class="absolute top-16 left-1/2 -translate-x-1/2 z-50 w-96 max-w-[90vw] max-h-[70vh] bg-base-100 rounded-lg shadow-xl border border-base-300 flex flex-col"
>
<!-- Header of the search results box -->
<div class="p-4 border-b border-base-300 flex-shrink-0">
<h3 class="font-bold">Search Results</h3>
</div>
<!--
THE FIX IS HERE:
- Removed the `menu` class from the `<ul>`.
- Added `flex flex-col` to explicitly force a single vertical column.
- Added `space-y-1` to add a small gap between each list item.
-->
<ul class="flex flex-col space-y-1 p-2 flex-grow overflow-y-auto">
<li v-if="isLoading" class="p-4 text-center">
<span class="loading loading-spinner"></span>
</li>
<li v-else-if="searchResults.length === 0 && searchTerm.length > 1" class="px-4 py-2 text-sm text-gray-500">
No results found for "{{ searchTerm }}"
</li>
<!-- The v-for now loops through simple <li> elements -->
<li v-for="result in searchResults" :key="result.id">
<!--
We add styling directly to the router-link to make it look like a clickable list item.
- `block`: Makes the entire area clickable, not just the text.
-->
<router-link
:to="{ name: 'customerProfile', params: { id: result.id } }"
@click="clearSearch"
class="block p-2 rounded-lg hover:bg-base-200 focus:bg-primary focus:text-primary-content"
>
<div>
<div class="font-bold">{{ result.customer_first_name }} {{ result.customer_last_name }}</div>
<div class="text-sm opacity-70">
{{ result.customer_address }}
</div>
<div class="text-sm opacity-70">
{{ result.customer_town }}, {{ getStateName(result.customer_state) }}
</div>
<div class="text-xs opacity-60 mt-1">
{{ result.customer_phone_number }}
</div>
</div>
</router-link>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
// 1. Import the store
import { defineComponent } from 'vue';
import { mapState, mapActions } from 'pinia';
import { useSearchStore } from '../stores/search'; // Adjust path if needed
export default defineComponent({
name: "SearchResults",
setup() {
// 2. Make the store available
const searchStore = useSearchStore();
return { searchStore };
name: 'SearchResults',
data() {
return {
stateMap: {
0: 'MA', 1: 'RI', 2: 'NH', 3: 'ME', 4: 'VT', 5: 'CT', 6: 'NY',
} as Record<number, string>,
};
},
computed: {
...mapState(useSearchStore, ['searchTerm', 'searchResults', 'isLoading']),
},
methods: {
viewProfile(customerId: number) {
// When a user is clicked, navigate to their profile...
this.$router.push({ name: 'customerProfile', params: { id: customerId } });
// ...and clear the search so the overlay disappears.
this.searchStore.clearSearch();
}
}
...mapActions(useSearchStore, ['clearSearch']),
getStateName(stateValue: number | string): string {
const stateNumber = Number(stateValue);
return this.stateMap[stateNumber] || 'N/A';
},
handleClickOutside(event: MouseEvent) {
const searchContainer = this.$refs.searchContainer as HTMLElement;
const searchInput = document.getElementById('customer-search-input');
if (searchContainer && !searchContainer.contains(event.target as Node) && searchInput && !searchInput.contains(event.target as Node)) {
this.clearSearch();
}
},
},
mounted() {
document.addEventListener('mousedown', this.handleClickOutside);
},
beforeUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
},
});
</script>c
</script>