Added Property Detail page and routing from Clients list.
This commit is contained in:
parent
54280ac78f
commit
016aa08b95
3 changed files with 190 additions and 1 deletions
|
|
@ -310,7 +310,7 @@ const handlePropertyClick = (link, rowData) => {
|
||||||
console.log("DEBUG: Property Link Clicked.");
|
console.log("DEBUG: Property Link Clicked.");
|
||||||
const client = encodeURIComponent(rowData.customerName);
|
const client = encodeURIComponent(rowData.customerName);
|
||||||
const address = encodeURIComponent(rowData.address);
|
const address = encodeURIComponent(rowData.address);
|
||||||
router.push(`/client?client=${client}&address=${address}`);
|
router.push(`/property?client=${client}&address=${address}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleEstimateClick = (status, rowData) => {
|
const handleEstimateClick = (status, rowData) => {
|
||||||
|
|
|
||||||
187
frontend/src/components/pages/Property.vue
Normal file
187
frontend/src/components/pages/Property.vue
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
<template>
|
||||||
|
<div class="property-page">
|
||||||
|
<!-- Client Header -->
|
||||||
|
<TopBar :selectedAddressIdx="selectedAddressIdx" :client="client" :nextVisitDate="nextVisitDate" v-if="client.customerName" @update:selectedAddressIdx="selectedAddressIdx = $event" />
|
||||||
|
<AdditionalInfoBar :address="client.addresses[selectedAddressIdx]" v-if="client.customerName" />
|
||||||
|
|
||||||
|
<Tabs value="0">
|
||||||
|
<TabList>
|
||||||
|
<Tab value="0">Overview</Tab>
|
||||||
|
<Tab value="1">Projects <span class="tab-info-alert">1</span></Tab>
|
||||||
|
<Tab value="2">Financials</Tab>
|
||||||
|
</TabList>
|
||||||
|
<TabPanels>
|
||||||
|
<TabPanel value="0">
|
||||||
|
<Overview
|
||||||
|
:client-data="client"
|
||||||
|
:selected-address="selectedAddress"
|
||||||
|
:is-new="isNew"
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value="1">
|
||||||
|
<div id="projects-tab"><h3>Project Status</h3></div>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value="2">
|
||||||
|
<div id="financials-tab"><h3>Accounting</h3></div>
|
||||||
|
</TabPanel>
|
||||||
|
</TabPanels>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, ref, watch } from "vue";
|
||||||
|
import Tabs from "primevue/tabs";
|
||||||
|
import TabList from "primevue/tablist";
|
||||||
|
import Tab from "primevue/tab";
|
||||||
|
import TabPanels from "primevue/tabpanels";
|
||||||
|
import TabPanel from "primevue/tabpanel";
|
||||||
|
import Api from "../../api";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { useLoadingStore } from "../../stores/loading";
|
||||||
|
import { useNotificationStore } from "../../stores/notifications-primevue";
|
||||||
|
import DataUtils from "../../utils";
|
||||||
|
import Overview from "../clientSubPages/Overview.vue";
|
||||||
|
import ProjectStatus from "../clientSubPages/ProjectStatus.vue";
|
||||||
|
import TopBar from "../clientView/TopBar.vue";
|
||||||
|
import AdditionalInfoBar from "../clientView/AdditionalInfoBar.vue";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const loadingStore = useLoadingStore();
|
||||||
|
const notificationStore = useNotificationStore();
|
||||||
|
|
||||||
|
const address = route.query.address || null;
|
||||||
|
const clientName = route.query.client || null;
|
||||||
|
const isNew = computed(() => route.query.new === "true" || false);
|
||||||
|
|
||||||
|
const clientNames = ref([]);
|
||||||
|
const client = ref({});
|
||||||
|
const geocode = ref({});
|
||||||
|
|
||||||
|
const selectedAddress = ref(address);
|
||||||
|
|
||||||
|
const selectedAddressObject = computed(() =>
|
||||||
|
client.value.addresses?.find(
|
||||||
|
(addr) => DataUtils.calculateFullAddress(addr) === selectedAddress.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const addresses = computed(() => {
|
||||||
|
if (client.value && client.value.addresses) {
|
||||||
|
return client.value.addresses.map((addr) => DataUtils.calculateFullAddress(addr));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const nextVisitDate = ref(null); // Placeholder, update as needed
|
||||||
|
|
||||||
|
const selectedAddressIdx = computed({
|
||||||
|
get: () => addresses.value.indexOf(selectedAddress.value),
|
||||||
|
set: (idx) => {
|
||||||
|
if (idx >= 0 && idx < addresses.value.length) {
|
||||||
|
selectedAddress.value = addresses.value[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const getClientNames = async (type) => {
|
||||||
|
loadingStore.setLoading(true);
|
||||||
|
try {
|
||||||
|
const names = await Api.getClientNames(type);
|
||||||
|
clientNames.value = names;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching client names in Client.vue: ", error.message || error);
|
||||||
|
} finally {
|
||||||
|
loadingStore.setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getClient = async (name) => {
|
||||||
|
loadingStore.setLoading(true);
|
||||||
|
try {
|
||||||
|
const clientData = await Api.getClient(name);
|
||||||
|
client.value = clientData || {};
|
||||||
|
// Set initial selected address if provided in route or use first address
|
||||||
|
if (address && client.value.addresses) {
|
||||||
|
const fullAddresses = client.value.addresses.map((addr) =>
|
||||||
|
DataUtils.calculateFullAddress(addr),
|
||||||
|
);
|
||||||
|
if (fullAddresses.includes(address)) {
|
||||||
|
selectedAddress.value = address;
|
||||||
|
} else if (fullAddresses.length > 0) {
|
||||||
|
selectedAddress.value = fullAddresses[0];
|
||||||
|
}
|
||||||
|
} else if (client.value.addresses && client.value.addresses.length > 0) {
|
||||||
|
selectedAddress.value = DataUtils.calculateFullAddress(client.value.addresses[0]);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
selectedAddressObject.value?.customLongitude &&
|
||||||
|
selectedAddressObject.value?.customLatitude
|
||||||
|
) {
|
||||||
|
geocode.value = {
|
||||||
|
longitude: selectedAddressObject.value.customLongitude || selectedAddressObject.value.longitude,
|
||||||
|
latitude: selectedAddressObject.value.customLatitude || selectedAddressObject.value.latitude,
|
||||||
|
};
|
||||||
|
} else if (selectedAddress.value) {
|
||||||
|
// geocode.value = await Api.getGeocode(selectedAddress.value);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching client data in Client.vue: ", error.message || error);
|
||||||
|
} finally {
|
||||||
|
loadingStore.setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (clientName) {
|
||||||
|
await getClient(clientName);
|
||||||
|
console.log("Displaying existing client data");
|
||||||
|
}
|
||||||
|
console.debug(
|
||||||
|
"DEBUG: Client.vue mounted with clientName:",
|
||||||
|
clientName,
|
||||||
|
"isNew:",
|
||||||
|
isNew.value,
|
||||||
|
"address:",
|
||||||
|
address,
|
||||||
|
"addresses:",
|
||||||
|
addresses.value,
|
||||||
|
"selectedAddress:",
|
||||||
|
selectedAddress.value,
|
||||||
|
"Does selected address match an address in addresses?:",
|
||||||
|
selectedAddress.value && addresses.value.includes(selectedAddress.value),
|
||||||
|
"geocode:",
|
||||||
|
geocode.value,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query,
|
||||||
|
async (newQuery, oldQuery) => {
|
||||||
|
const clientName = newQuery.client || null;
|
||||||
|
const isNewClient = newQuery.new === "true" || false;
|
||||||
|
const address = newQuery.address || null;
|
||||||
|
|
||||||
|
// Clear client data if switching to new client mode
|
||||||
|
if (isNewClient) {
|
||||||
|
client.value = {};
|
||||||
|
selectedAddress.value = null;
|
||||||
|
geocode.value = {};
|
||||||
|
console.log("Switched to new client mode - cleared client data");
|
||||||
|
} else if (clientName && clientName !== oldQuery.client) {
|
||||||
|
// Load client data if switching to existing client
|
||||||
|
await getClient(clientName);
|
||||||
|
console.log("Route query changed - displaying existing client data");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<style lang="css">
|
||||||
|
.tab-info-alert {
|
||||||
|
background-color: #a95e46;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: white;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -10,6 +10,7 @@ import TimeSheets from "./components/pages/TimeSheets.vue";
|
||||||
import Warranties from "./components/pages/Warranties.vue";
|
import Warranties from "./components/pages/Warranties.vue";
|
||||||
import Home from "./components/pages/Home.vue";
|
import Home from "./components/pages/Home.vue";
|
||||||
import Client from "./components/pages/Client.vue";
|
import Client from "./components/pages/Client.vue";
|
||||||
|
import Property from "./components/pages/Property.vue";
|
||||||
import Estimate from "./components/pages/Estimate.vue";
|
import Estimate from "./components/pages/Estimate.vue";
|
||||||
import Job from "./components/pages/Job.vue";
|
import Job from "./components/pages/Job.vue";
|
||||||
|
|
||||||
|
|
@ -21,6 +22,7 @@ const routes = [
|
||||||
{ path: "/calendar", component: Calendar },
|
{ path: "/calendar", component: Calendar },
|
||||||
{ path: "/clients", component: Clients },
|
{ path: "/clients", component: Clients },
|
||||||
{ path: "/client", component: Client },
|
{ path: "/client", component: Client },
|
||||||
|
{ path: "/property", component: Property },
|
||||||
{ path: "/jobs", component: Jobs },
|
{ path: "/jobs", component: Jobs },
|
||||||
{ path: "/job", component: Job },
|
{ path: "/job", component: Job },
|
||||||
{ path: "/invoices", component: Invoices },
|
{ path: "/invoices", component: Invoices },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue