diff --git a/custom_ui/api/db/clients.py b/custom_ui/api/db/clients.py index d6b6c71..f21b811 100644 --- a/custom_ui/api/db/clients.py +++ b/custom_ui/api/db/clients.py @@ -194,7 +194,8 @@ def get_clients_table_data(filters={}, sortings=[], page=1, page_size=10): }) processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size) - + print("DEBUG: Processed filters:", processed_filters) + print("DEBUG: Processed sortings:", processed_sortings) # Handle count with proper OR filter support if is_or: count = frappe.db.sql(*get_count_or_filters("Address", processed_filters))[0][0] diff --git a/custom_ui/api/proxy.py b/custom_ui/api/proxy.py index d84c4eb..7c45473 100644 --- a/custom_ui/api/proxy.py +++ b/custom_ui/api/proxy.py @@ -1,8 +1,9 @@ import frappe import requests from urllib.parse import urlparse +import logging -allowed_hosts = ["api.zippopotam.us"] # Update this list with trusted domains as needed +allowed_hosts = ["api.zippopotam.us", "nominatim.openstreetmap.org"] # Update this list with trusted domains as needed @frappe.whitelist(allow_guest=True) def request(url, method="GET", data=None, headers=None): diff --git a/custom_ui/install.py b/custom_ui/install.py index 314cffd..69bdc72 100644 --- a/custom_ui/install.py +++ b/custom_ui/install.py @@ -5,11 +5,21 @@ from .utils import create_module def after_install(): create_module() + add_custom_fields() build_frontend() def after_migrate(): + add_custom_fields() + frappe.db.commit() + + # Proper way to refresh metadata + frappe.clear_cache(doctype="Address") + frappe.reload_doctype("Address") + + update_address_fields() build_frontend() + def build_frontend(): app_package_path = frappe.get_app_path("custom_ui") app_root = os.path.dirname(app_package_path) @@ -40,4 +50,231 @@ def build_frontend(): print("\nā Frontend build completed successfully.\n") except subprocess.CalledProcessError as e: frappe.log_error(message=str(e), title="Frontend Build Failed") - print(f"\nā Frontend build failed: {e}\n") \ No newline at end of file + print(f"\nā Frontend build failed: {e}\n") + +def add_custom_fields(): + from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + + print("\nš§ Adding custom fields to Address doctype...") + + custom_fields = { + "Address": [ + dict( + fieldname="full_address", + label="Full Address", + fieldtype="Data", + insert_after="country" + ), + dict( + fieldname="latitude", + label="Latitude", + fieldtype="Float", + precision=8, + insert_after="full_address" + ), + dict( + fieldname="longitude", + label="Longitude", + fieldtype="Float", + precision=8, + insert_after="latitude" + ), + dict( + fieldname="onsite_meeting_scheduled", + label="On-Site Meeting Scheduled", + fieldtype="Select", + options="Not Started\nIn Progress\nCompleted", + default="Not Started", + insert_after="longitude" + ), + dict( + fieldname="estimate_sent_status", + label="Estimate Sent Status", + fieldtype="Select", + options="Not Started\nIn Progress\nCompleted", + default="Not Started", + insert_after="onsite_meeting_scheduled" + ), + dict( + fieldname="job_status", + label="Job Status", + fieldtype="Select", + options="Not Started\nIn Progress\nCompleted", + default="Not Started", + insert_after="estimate_sent_status" + ), + dict( + fieldname="payment_received_status", + label="Payment Received Status", + fieldtype="Select", + options="Not Started\nIn Progress\nCompleted", + default="Not Started", + insert_after="job_status" + ) + ] + } + + field_count = len(custom_fields["Address"]) + print(f"š Creating {field_count} custom fields for Address doctype...") + + try: + create_custom_fields(custom_fields) + print("ā Custom fields added successfully!") + print(" ⢠full_address (Data)") + print(" ⢠latitude (Float)") + print(" ⢠longitude (Float)") + print(" ⢠onsite_meeting_scheduled (Select)") + print(" ⢠estimate_sent_status (Select)") + print(" ⢠job_status (Select)") + print(" ⢠payment_received_status (Select)") + print("š§ Custom fields installation complete.\n") + except Exception as e: + print(f"ā Error creating custom fields: {str(e)}") + frappe.log_error(message=str(e), title="Custom Fields Creation Failed") + raise + +def update_address_fields(): + addresses = frappe.get_all("Address", pluck="name") + total_addresses = len(addresses) + + if total_addresses == 0: + print("š No addresses found to update.") + return + + print(f"\nš Updating fields for {total_addresses} addresses...") + + # Verify custom fields exist by checking the meta + address_meta = frappe.get_meta("Address") + required_fields = ['full_address', 'custom_onsite_meeting_scheduled', + 'custom_estimate_sent_status', 'custom_job_status', + 'custom_payment_received_status'] + + missing_fields = [] + for field in required_fields: + if not address_meta.has_field(field): + missing_fields.append(field) + + if missing_fields: + print(f"\nā Missing custom fields: {', '.join(missing_fields)}") + print(" Custom fields creation may have failed. Skipping address updates.") + return + + print("ā All custom fields verified. Proceeding with address updates...") + + # Field update counters + field_counters = { + 'full_address': 0, + 'latitude': 0, + 'longitude': 0, + 'custom_onsite_meeting_scheduled': 0, + 'custom_estimate_sent_status': 0, + 'custom_job_status': 0, + 'custom_payment_received_status': 0 + } + total_field_updates = 0 + addresses_updated = 0 + + for index, name in enumerate(addresses, 1): + # Calculate progress + progress_percentage = int((index / total_addresses) * 100) + bar_length = 30 + filled_length = int(bar_length * index // total_addresses) + bar = 'ā' * filled_length + 'ā' * (bar_length - filled_length) + + # Print progress bar with field update count + print(f"\rš Progress: [{bar}] {progress_percentage:3d}% ({index}/{total_addresses}) | Fields Updated: {total_field_updates} - Processing: {name[:25]}...", end='', flush=True) + + should_update = False + address = frappe.get_doc("Address", name) + current_address_updates = 0 + current_address_updates = 0 + + # Use getattr with default values instead of direct attribute access + if not getattr(address, 'full_address', None): + address_parts_1 = [ + address.address_line1 or "", + address.address_line2 or "", + address.city or "", + ] + address_parts_2 = [ + address.state or "", + address.pincode or "", + ] + + full_address = ", ".join([ + " ".join(filter(None, address_parts_1)), + " ".join(filter(None, address_parts_2)) + ]).strip() + address.full_address = full_address + field_counters['full_address'] += 1 + current_address_updates += 1 + should_update = True + onsite_meeting = "Not Started" + estimate_sent = "Not Started" + job_status = "Not Started" + payment_received = "Not Started" + + onsite_meetings = frappe.get_all("On-Site Meeting", filters={"address": address.address_title}) + if onsite_meetings and onsite_meetings[0]: + onsite_meeting = "Completed" + + estimates = frappe.get_all("Quotation", fields=["custom_sent"], filters={"custom_installation_address": address.address_title}) + if estimates and estimates[0] and estimates[0]["custom_sent"] == 1: + estimate_sent = "Completed" + elif estimates and estimates[0]: + estimate_sent = "In Progress" + + jobs = frappe.get_all("Project", fields=["status"], filters={"custom_installation_address": address.address_title, "project_template": "SNW Install"}) + if jobs and jobs[0] and jobs[0]["status"] == "Completed": + job_status = "Completed" + elif jobs and jobs[0]: + job_status = "In Progress" + + sales_invoices = frappe.get_all("Sales Invoice", fields=["outstanding_amount"], filters={"custom_installation_address": address.address_title}) + # payments = frappe.get_all("Payment Entry", filters={"custom_installation_address": address.address_title}) + if sales_invoices and sales_invoices[0] and sales_invoices[0]["outstanding_amount"] == 0: + payment_received = "Completed" + elif sales_invoices and sales_invoices[0]: + payment_received = "In Progress" + + if getattr(address, 'custom_onsite_meeting_scheduled', None) != onsite_meeting: + address.custom_onsite_meeting_scheduled = onsite_meeting + field_counters['custom_onsite_meeting_scheduled'] += 1 + current_address_updates += 1 + should_update = True + if getattr(address, 'custom_estimate_sent_status', None) != estimate_sent: + address.custom_estimate_sent_status = estimate_sent + field_counters['custom_estimate_sent_status'] += 1 + current_address_updates += 1 + should_update = True + if getattr(address, 'custom_job_status', None) != job_status: + address.custom_job_status = job_status + field_counters['custom_job_status'] += 1 + current_address_updates += 1 + should_update = True + if getattr(address, 'custom_payment_received_status', None) != payment_received: + address.custom_payment_received_status = payment_received + field_counters['custom_payment_received_status'] += 1 + current_address_updates += 1 + should_update = True + + if should_update: + address.save(ignore_permissions=True) + addresses_updated += 1 + total_field_updates += current_address_updates + + # Print completion summary + print(f"\n\nā Address field update completed!") + print(f"š Summary:") + print(f" ⢠Total addresses processed: {total_addresses:,}") + print(f" ⢠Addresses updated: {addresses_updated:,}") + print(f" ⢠Total field updates: {total_field_updates:,}") + print(f"\nš Field-specific updates:") + print(f" ⢠Full Address: {field_counters['full_address']:,}") + print(f" ⢠Latitude: {field_counters['latitude']:,}") + print(f" ⢠Longitude: {field_counters['longitude']:,}") + print(f" ⢠On-Site Meeting Status: {field_counters['custom_onsite_meeting_scheduled']:,}") + print(f" ⢠Estimate Sent Status: {field_counters['custom_estimate_sent_status']:,}") + print(f" ⢠Job Status: {field_counters['custom_job_status']:,}") + print(f" ⢠Payment Received Status: {field_counters['custom_payment_received_status']:,}") + print("š Address field updates complete.\n") \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 24122ff..c6c74c2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,10 +14,12 @@ "axios": "^1.12.2", "chart.js": "^4.5.1", "frappe-ui": "^0.1.205", + "leaflet": "^1.9.4", "pinia": "^3.0.3", "primevue": "^4.4.1", "vue": "^3.5.22", "vue-chartjs": "^5.3.3", + "vue-leaflet": "^0.1.0", "vue-router": "^4.6.3", "vuetify": "^3.10.7" }, @@ -3214,6 +3216,12 @@ "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", "license": "MIT" }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -4626,6 +4634,12 @@ "vue": "^3.0.0-0 || ^2.7.0" } }, + "node_modules/vue-leaflet": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/vue-leaflet/-/vue-leaflet-0.1.0.tgz", + "integrity": "sha512-J2QxmQSbmnpM/Ng+C8vxowXcWp/IEe99r87psHyWYpBz2nbxkQAeYXW7WFcgzV4O7d7Vm4a1GcqKzrU9DeDpBA==", + "license": "MIT" + }, "node_modules/vue-router": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 68ce6bb..ce9bf05 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,10 +15,12 @@ "axios": "^1.12.2", "chart.js": "^4.5.1", "frappe-ui": "^0.1.205", + "leaflet": "^1.9.4", "pinia": "^3.0.3", "primevue": "^4.4.1", "vue": "^3.5.22", "vue-chartjs": "^5.3.3", + "vue-leaflet": "^0.1.0", "vue-router": "^4.6.3", "vuetify": "^3.10.7" }, diff --git a/frontend/src/api.js b/frontend/src/api.js index ac7629e..c03e35c 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -250,6 +250,10 @@ class Api { return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { type }); } + static async getClientNames(type) { + return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { type }); + } + static async getCompanyNames() { const companies = await this.getDocsList("Company", ["name"]); const companyNames = companies.map((company) => company.name); @@ -311,6 +315,18 @@ class Api { } return places; } + + static async getGeocode(address) { + const urlSafeAddress = encodeURIComponent(address); + const url = `https://nominatim.openstreetmap.org/search?format=jsonv2&q=${urlSafeAddress}`; + const response = await this.request(FRAPPE_PROXY_METHOD, { + url, + method: "GET", + headers: { "User-Agent": "FrappeApp/1.0 (+https://yourappdomain.com)" }, + }); + const { lat, lon } = response[0] || {}; + return { latitude: parseFloat(lat), longitude: parseFloat(lon) }; + } } export default Api; diff --git a/frontend/src/components/SideBar.vue b/frontend/src/components/SideBar.vue index d592855..29fa250 100644 --- a/frontend/src/components/SideBar.vue +++ b/frontend/src/components/SideBar.vue @@ -31,7 +31,7 @@ const createButtons = ref([ { label: "Client", command: () => { - router.push("/clients/new"); + router.push("/client?new=true"); }, }, { diff --git a/frontend/src/components/clientSubPages/Overview.vue b/frontend/src/components/clientSubPages/Overview.vue new file mode 100644 index 0000000..99ca138 --- /dev/null +++ b/frontend/src/components/clientSubPages/Overview.vue @@ -0,0 +1,826 @@ + + + + + + Client Information + + + + + Customer Name: + + {{ clientData?.customerName || "N/A" }} + + + Customer Type: + + {{ clientData?.customerType || "N/A" }} + + + Customer Group: + {{ clientData?.customerGroup || "N/A" }} + + + Territory: + {{ clientData?.territory || "N/A" }} + + + + + + + Address Information + + + Address Line 1 *: + + + + Address Line 2: + + + + Zip Code *: + + + + City *: + + + + State *: + + + + + Full Address: + {{ fullAddress }} + + + City: + {{ selectedAddressData.city || "N/A" }} + + + State: + {{ selectedAddressData.state || "N/A" }} + + + Zip Code: + {{ selectedAddressData.pincode || "N/A" }} + + + + + + + Contact Information + + + Contact Name *: + + + + Phone Number: + + + + Email: + + + + + + + + + On-Site Meeting + + + + Estimate Sent + + + + Job Status + + + + Payment Received + + + + + + + + + + + + + Location + + + + Coordinates: + {{ parseFloat(latitude).toFixed(6) }}, {{ parseFloat(longitude).toFixed(6) }} + + + + + + + + Are you sure you want to edit this client information? This will enable editing + mode. + + + + + + + + + + + + diff --git a/frontend/src/components/clientSubPages/ProjectStatus.vue b/frontend/src/components/clientSubPages/ProjectStatus.vue new file mode 100644 index 0000000..ec3ee18 --- /dev/null +++ b/frontend/src/components/clientSubPages/ProjectStatus.vue @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/components/common/DataTable.vue b/frontend/src/components/common/DataTable.vue index 12c5387..1c86910 100644 --- a/frontend/src/components/common/DataTable.vue +++ b/frontend/src/components/common/DataTable.vue @@ -1052,7 +1052,7 @@ const getBadgeColor = (status) => { return "success"; case "in progress": case "pending": - return "warning"; + return "warn"; case "not started": case "closed": case "cancelled": diff --git a/frontend/src/components/common/LeafletMap.vue b/frontend/src/components/common/LeafletMap.vue new file mode 100644 index 0000000..330b0f7 --- /dev/null +++ b/frontend/src/components/common/LeafletMap.vue @@ -0,0 +1,219 @@ + + + + + + + No coordinates available + + + + + + + + diff --git a/frontend/src/components/pages/Client.vue b/frontend/src/components/pages/Client.vue index d4da95d..44cbb86 100644 --- a/frontend/src/components/pages/Client.vue +++ b/frontend/src/components/pages/Client.vue @@ -1,44 +1,89 @@ + + + + {{ client.customerName }} + + Address: + + {{ addresses[0] }} + + + + Overview - Projects 1 + Projects 1 Financials History - Overview + - Project Status + Project Status - Accounting + Accounting - History + History diff --git a/frontend/src/components/pages/Clients.vue b/frontend/src/components/pages/Clients.vue index bfb7faf..43c6391 100644 --- a/frontend/src/components/pages/Clients.vue +++ b/frontend/src/components/pages/Clients.vue @@ -165,7 +165,7 @@ const tableActions = [ { label: "View Details", action: (rowData) => { - router.push(`/clients/${rowData.customerName}`); + router.push(`/client?client=${rowData.customerName}&address=${rowData.address}`); }, type: "button", style: "info", @@ -191,36 +191,36 @@ const tableActions = [ // variant: "filled", // }, // }, - { - label: "Edit", - action: (rowData) => { - console.log("Editing client:", rowData); - // Implementation would open edit modal - }, - type: "button", - style: "secondary", - icon: "pi pi-pencil", - rowAction: true, // Row action - appears in each row's actions column - layout: { - priority: "primary", - variant: "outlined", - }, - }, - { - label: "Quick View", - action: (rowData) => { - console.log("Quick view for:", rowData.addressTitle); - // Implementation would show quick preview - }, - type: "button", - style: "info", - icon: "pi pi-search", - rowAction: true, // Row action - appears in each row's actions column - layout: { - priority: "secondary", - variant: "compact", - }, - }, + // { + // label: "Edit", + // action: (rowData) => { + // console.log("Editing client:", rowData); + // // Implementation would open edit modal + // }, + // type: "button", + // style: "secondary", + // icon: "pi pi-pencil", + // rowAction: true, // Row action - appears in each row's actions column + // layout: { + // priority: "primary", + // variant: "outlined", + // }, + // }, + // { + // label: "Quick View", + // action: (rowData) => { + // console.log("Quick view for:", rowData.addressTitle); + // // Implementation would show quick preview + // }, + // type: "button", + // style: "info", + // icon: "pi pi-search", + // rowAction: true, // Row action - appears in each row's actions column + // layout: { + // priority: "secondary", + // variant: "compact", + // }, + // }, ]; // Handle lazy loading events from DataTable const handleLazyLoad = async (event) => { diff --git a/frontend/src/router.js b/frontend/src/router.js index f0e189b..afbdcea 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -19,7 +19,7 @@ const routes = [ }, { path: "/calendar", component: Calendar }, { path: "/clients", component: Clients }, - { path: "/clients/:clientName", component: Client, props: true }, + { path: "/client", component: Client }, { path: "/jobs", component: Jobs }, { path: "/routes", component: Routes }, { path: "/create", component: Create }, diff --git a/frontend/src/utils.js b/frontend/src/utils.js index 711c271..422e9e9 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -13,1635 +13,6 @@ class DataUtils { return `${completedSteps}/${steps.length}`; } - static dummyClientData = [ - { - id: 1, - fullName: "John Doe 123 Lane Dr Cityville, MN", - appointmentScheduled: "completed", - estimateSent: "in progress", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 2, - fullName: "Jane Smith 456 Oak St Townsville, CA", - appointmentScheduled: "in progress", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 3, - fullName: "Mike Johnson 789 Pine Rd Villagetown, TX", - appointmentScheduled: "completed", - estimateSent: "completed", - paymentReceived: "in progress", - jobStatus: "in progress", - }, - { - id: 4, - fullName: "Emily Davis 321 Maple Ave Hamlet, FL", - appointmentScheduled: "not started", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 5, - fullName: "David Wilson 654 Cedar Blvd Borough, NY", - appointmentScheduled: "completed", - estimateSent: "completed", - paymentReceived: "completed", - jobStatus: "completed", - }, - // create 30 more dummy entries - { - id: 6, - fullName: "Sarah Brown 987 Birch Ln Metropolis, IL", - appointmentScheduled: "in progress", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 7, - fullName: "Chris Taylor 159 Spruce Ct Capital City, WA", - appointmentScheduled: "completed", - estimateSent: "completed", - paymentReceived: "in progress", - jobStatus: "in progress", - }, - { - id: 8, - fullName: "Amanda Martinez 753 Willow Dr Smalltown, OH", - appointmentScheduled: "not started", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 9, - fullName: "Joshua Anderson 852 Aspen Rd Bigcity, NJ", - appointmentScheduled: "completed", - estimateSent: "in progress", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 10, - fullName: "Olivia Thomas 147 Cypress St Uptown, GA", - appointmentScheduled: "completed", - estimateSent: "completed", - paymentReceived: "completed", - jobStatus: "completed", - }, - { - id: 11, - fullName: "Daniel Jackson 369 Fir Ave Downtown, MI", - appointmentScheduled: "in progress", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 12, - fullName: "Sophia White 258 Palm Blvd Riverside, AZ", - appointmentScheduled: "completed", - estimateSent: "completed", - paymentReceived: "in progress", - jobStatus: "in progress", - }, - { - id: 13, - fullName: "Matthew Harris 951 Olive Ln Seaside, OR", - appointmentScheduled: "not started", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 14, - fullName: "Isabella Clark 753 Magnolia Ct Hilltown, SC", - appointmentScheduled: "completed", - estimateSent: "in progress", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 15, - fullName: "Andrew Lewis 357 Dogwood Dr Lakeview, VT", - appointmentScheduled: "completed", - estimateSent: "completed", - paymentReceived: "completed", - jobStatus: "completed", - }, - { - id: 16, - fullName: "Mia Robinson 159 Cherry St Forestville, KY", - appointmentScheduled: "in progress", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - { - id: 17, - fullName: "Ethan Walker 456 Walnut Rd Meadowbrook, ND", - appointmentScheduled: "completed", - estimateSent: "completed", - paymentReceived: "in progress", - jobStatus: "in progress", - }, - { - id: 18, - fullName: "Ava Hall 123 Poplar Ave Greenfield, MS", - appointmentScheduled: "not started", - estimateSent: "not started", - paymentReceived: "not started", - jobStatus: "not started", - }, - ]; - - static dummyJobData = [ - { - jobId: "JOB001", - foreman: "Mike Thompson", - crew: ["Worker A", "Worker B"], - address: "123 Lane Dr, Cityville, MN", - customer: "John Doe", - overAllStatus: "in progress", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "in progress" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB002", - foreman: null, - crew: [], - address: "456 Oak St, Townsville, CA", - customer: "Jane Smith", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "not started" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB003", - foreman: "Sarah Johnson", - crew: ["Worker C", "Worker D", "Worker E"], - address: "789 Pine Rd, Villagetown, TX", - customer: "Mike Johnson", - overAllStatus: "in progress", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "completed" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "in progress" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB004", - foreman: null, - crew: [], - address: "321 Maple Ave, Hamlet, FL", - customer: "Emily Davis", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "not started" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB005", - foreman: "David Martinez", - crew: ["Worker F", "Worker G"], - address: "654 Cedar Blvd, Borough, NY", - customer: "David Wilson", - overAllStatus: "completed", - completionDate: "2024-10-15", - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "completed" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "completed" }, - { stepName: "Job Completed", status: "completed" }, - ], - }, - { - jobId: "JOB006", - foreman: null, - crew: [], - address: "987 Birch Ln, Metropolis, IL", - customer: "Sarah Brown", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "in progress" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB007", - foreman: "Chris Wilson", - crew: ["Worker H", "Worker I"], - address: "159 Spruce Ct, Capital City, WA", - customer: "Chris Taylor", - overAllStatus: "in progress", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "completed" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "in progress" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB008", - foreman: null, - crew: [], - address: "753 Willow Dr, Smalltown, OH", - customer: "Amanda Martinez", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "not started" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB009", - foreman: "Lisa Anderson", - crew: ["Worker J"], - address: "852 Aspen Rd, Bigcity, NJ", - customer: "Joshua Anderson", - overAllStatus: "in progress", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "in progress" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB010", - foreman: "Robert Thomas", - crew: ["Worker K", "Worker L", "Worker M"], - address: "147 Cypress St, Uptown, GA", - customer: "Olivia Thomas", - overAllStatus: "completed", - completionDate: "2024-10-20", - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "completed" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "completed" }, - { stepName: "Job Completed", status: "completed" }, - ], - }, - { - jobId: "JOB011", - foreman: null, - crew: [], - address: "369 Fir Ave, Downtown, MI", - customer: "Daniel Jackson", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "in progress" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB012", - foreman: "Maria White", - crew: ["Worker N", "Worker O"], - address: "258 Palm Blvd, Riverside, AZ", - customer: "Sophia White", - overAllStatus: "in progress", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "completed" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "in progress" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB013", - foreman: null, - crew: [], - address: "951 Olive Ln, Seaside, OR", - customer: "Matthew Harris", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "not started" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB014", - foreman: "James Clark", - crew: ["Worker P"], - address: "753 Magnolia Ct, Hilltown, SC", - customer: "Isabella Clark", - overAllStatus: "in progress", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "in progress" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB015", - foreman: "Patricia Lewis", - crew: ["Worker Q", "Worker R", "Worker S"], - address: "357 Dogwood Dr, Lakeview, VT", - customer: "Andrew Lewis", - overAllStatus: "completed", - completionDate: "2024-10-18", - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "completed" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "completed" }, - { stepName: "Job Completed", status: "completed" }, - ], - }, - { - jobId: "JOB016", - foreman: null, - crew: [], - address: "159 Cherry St, Forestville, KY", - customer: "Mia Robinson", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "in progress" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB017", - foreman: "Kevin Walker", - crew: ["Worker T", "Worker U"], - address: "456 Walnut Rd, Meadowbrook, ND", - customer: "Ethan Walker", - overAllStatus: "in progress", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "completed" }, - { stepName: "Materials Received", status: "completed" }, - { stepName: "Permits", status: "completed" }, - { stepName: "Locate Utilities", status: "completed" }, - { stepName: "Foreman Assigned", status: "completed" }, - { stepName: "Crew Assigned", status: "completed" }, - { stepName: "Job Started", status: "in progress" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - { - jobId: "JOB018", - foreman: null, - crew: [], - address: "123 Poplar Ave, Greenfield, MS", - customer: "Ava Hall", - overAllStatus: "not started", - completionDate: null, - steps: [ - { stepName: "Materials Ordered", status: "not started" }, - { stepName: "Materials Received", status: "not started" }, - { stepName: "Permits", status: "not started" }, - { stepName: "Locate Utilities", status: "not started" }, - { stepName: "Foreman Assigned", status: "not started" }, - { stepName: "Crew Assigned", status: "not started" }, - { stepName: "Job Started", status: "not started" }, - { stepName: "Job Completed", status: "not started" }, - ], - }, - ]; - - static dummyServiceData = [ - // Scheduled services - { - id: "service_1", - title: "Sprinkler System Installation - John Doe", - customer: "John Doe", - address: "123 Lane Dr, Cityville, MN", - serviceType: "Sprinkler System Installation", - foreman: "Mike Thompson", - crew: ["Technician A", "Technician B"], - scheduledDate: "2025-10-25", - scheduledTime: "08:00", - duration: 480, // minutes (8 hours) - status: "scheduled", - priority: "high", - estimatedCost: 4500, - notes: "Residential front and back yard coverage, 12 zones", - }, - { - id: "service_2", - title: "Sprinkler Winterization - Jane Smith", - customer: "Jane Smith", - address: "456 Oak St, Townsville, CA", - serviceType: "Sprinkler Winterization", - foreman: "Sarah Johnson", - crew: ["Technician C"], - scheduledDate: "2025-10-25", - scheduledTime: "14:00", - duration: 90, // minutes (1.5 hours) - status: "scheduled", - priority: "medium", - estimatedCost: 150, - notes: "Blow out all lines before first freeze", - }, - { - id: "service_3", - title: "Drip Line Repair - Mike Johnson", - customer: "Mike Johnson", - address: "789 Pine Rd, Villagetown, TX", - serviceType: "Drip Line Repair", - foreman: "David Martinez", - crew: ["Technician D"], - scheduledDate: "2025-10-26", - scheduledTime: "09:00", - duration: 120, // minutes (2 hours) - status: "scheduled", - priority: "high", - estimatedCost: 280, - notes: "Multiple leaks in flower bed drip system", - }, - { - id: "service_4", - title: "Controller Programming - Emily Davis", - customer: "Emily Davis", - address: "321 Maple Ave, Hamlet, FL", - serviceType: "Controller Programming", - foreman: "Chris Wilson", - crew: ["Technician E"], - scheduledDate: "2025-10-27", - scheduledTime: "10:00", - duration: 60, // minutes (1 hour) - status: "scheduled", - priority: "medium", - estimatedCost: 125, - notes: "Seasonal schedule update and zone testing", - }, - { - id: "service_5", - title: "Spring Startup - David Wilson", - customer: "David Wilson", - address: "654 Cedar Blvd, Borough, NY", - serviceType: "Spring Startup", - foreman: "Lisa Anderson", - crew: ["Technician F"], - scheduledDate: "2025-10-28", - scheduledTime: "11:00", - duration: 75, // minutes (1.25 hours) - status: "scheduled", - priority: "low", - estimatedCost: 95, - notes: "Annual system startup and inspection", - }, - { - id: "service_6", - title: "Zone Expansion - Sarah Brown", - customer: "Sarah Brown", - address: "987 Birch Ln, Metropolis, IL", - serviceType: "Zone Expansion", - foreman: "Robert Thomas", - crew: ["Technician G", "Technician H"], - scheduledDate: "2025-10-29", - scheduledTime: "08:30", - duration: 300, // minutes (5 hours) - status: "scheduled", - priority: "high", - estimatedCost: 1800, - notes: "Add 4 new zones to existing system for landscaped areas", - }, - { - id: "service_13", - title: "Backflow Testing - Robert Green", - customer: "Robert Green", - address: "445 Elm St, Riverside, CA", - serviceType: "Backflow Testing", - foreman: "Mike Thompson", - crew: ["Technician I"], - scheduledDate: "2025-10-27", - scheduledTime: "14:30", - duration: 60, // minutes (1 hour) - status: "scheduled", - priority: "medium", - estimatedCost: 120, - notes: "Annual backflow preventer inspection", - }, - { - id: "service_14", - title: "Smart Controller Install - Lisa Park", - customer: "Lisa Park", - address: "892 Maple Dr, Springfield, TX", - serviceType: "Smart Controller Install", - foreman: "Sarah Johnson", - crew: ["Technician J"], - scheduledDate: "2025-10-28", - scheduledTime: "09:30", - duration: 120, // minutes (2 hours) - status: "scheduled", - priority: "low", - estimatedCost: 450, - notes: "Upgrade to WiFi-enabled smart controller", - }, - { - id: "service_15", - title: "Pump Station Repair - Mark Wilson", - customer: "Mark Wilson", - address: "123 Oak Ave, Laketown, FL", - serviceType: "Pump Station Repair", - foreman: "David Martinez", - crew: ["Technician K", "Technician L"], - scheduledDate: "2025-10-29", - scheduledTime: "13:00", - duration: 180, // minutes (3 hours) - status: "scheduled", - priority: "urgent", - estimatedCost: 680, - notes: "Pump not priming, possible impeller issue", - }, - - // Unscheduled services - { - id: "service_7", - title: "Emergency Leak Repair - Amanda Martinez", - customer: "Amanda Martinez", - address: "753 Willow Dr, Smalltown, OH", - serviceType: "Emergency Leak Repair", - foreman: null, - crew: [], - scheduledDate: null, - scheduledTime: null, - duration: 90, // minutes (1.5 hours) - status: "unscheduled", - priority: "urgent", - estimatedCost: 200, - notes: "Main line leak flooding driveway", - }, - { - id: "service_8", - title: "Broken Sprinkler Head - Joshua Anderson", - customer: "Joshua Anderson", - address: "852 Aspen Rd, Bigcity, NJ", - serviceType: "Sprinkler Head Replacement", - foreman: null, - crew: [], - scheduledDate: null, - scheduledTime: null, - duration: 45, // minutes - status: "unscheduled", - priority: "medium", - estimatedCost: 75, - notes: "Multiple heads damaged by mower", - }, - { - id: "service_9", - title: "System Inspection - Olivia Thomas", - customer: "Olivia Thomas", - address: "147 Cypress St, Uptown, GA", - serviceType: "System Inspection", - foreman: null, - crew: [], - scheduledDate: null, - scheduledTime: null, - duration: 60, // minutes (1 hour) - status: "unscheduled", - priority: "low", - estimatedCost: 85, - notes: "Annual maintenance check requested", - }, - { - id: "service_10", - title: "New Installation Quote - Daniel Jackson", - customer: "Daniel Jackson", - address: "369 Fir Ave, Downtown, MI", - serviceType: "Installation Quote", - foreman: null, - crew: [], - scheduledDate: null, - scheduledTime: null, - duration: 45, // minutes - status: "unscheduled", - priority: "medium", - estimatedCost: 0, - notes: "Estimate for new construction sprinkler system", - }, - { - id: "service_11", - title: "Valve Replacement - Sophia White", - customer: "Sophia White", - address: "258 Palm Blvd, Riverside, AZ", - serviceType: "Valve Replacement", - foreman: null, - crew: [], - scheduledDate: null, - scheduledTime: null, - duration: 120, // minutes (2 hours) - status: "unscheduled", - priority: "medium", - estimatedCost: 350, - notes: "Zone 3 valve not closing properly", - }, - { - id: "service_12", - title: "Controller Repair - Matthew Harris", - customer: "Matthew Harris", - address: "951 Olive Ln, Seaside, OR", - serviceType: "Controller Repair", - foreman: null, - crew: [], - scheduledDate: null, - scheduledTime: null, - duration: 90, // minutes (1.5 hours) - status: "unscheduled", - priority: "urgent", - estimatedCost: 250, - notes: "Controller display not working, possible storm damage", - }, - ]; - - static dummyRouteData = [ - { - routeId: "TECH-001", - routeName: "North Valley Tech Route", - routeType: "Tech Route", - technician: "Mike Thompson", - date: "2025-10-25", - status: "in progress", - totalStops: 6, - completedStops: 2, - estimatedDuration: "8 hours", - startTime: "07:00", - vehicleId: "TECH-101", - totalMileage: 52.3, - stops: [ - { - stopId: 1, - customer: "John Doe", - address: "123 Lane Dr, Cityville, MN", - serviceType: "Complete Sprinkler System Install", - estimatedTime: "07:30", - duration: 240, - status: "completed", - coordinates: { lat: 44.9778, lng: -93.265 }, - }, - { - stopId: 2, - customer: "Sarah Johnson", - address: "456 Oak St, Northfield, MN", - serviceType: "Zone Expansion Install", - estimatedTime: "11:45", - duration: 180, - status: "completed", - coordinates: { lat: 44.4583, lng: -93.1614 }, - }, - { - stopId: 3, - customer: "Mike Wilson", - address: "789 Pine Rd, Faribault, MN", - serviceType: "Drip System Installation", - estimatedTime: "15:00", - duration: 120, - status: "in progress", - coordinates: { lat: 44.295, lng: -93.2688 }, - }, - { - stopId: 4, - customer: "Emily Davis", - address: "321 Maple Ave, Owatonna, MN", - serviceType: "Smart Controller Install", - estimatedTime: "17:30", - duration: 90, - status: "not started", - coordinates: { lat: 44.0839, lng: -93.2261 }, - }, - { - stopId: 5, - customer: "David Brown", - address: "654 Cedar Blvd, Austin, MN", - serviceType: "Backflow Preventer Install", - estimatedTime: "19:15", - duration: 75, - status: "not started", - coordinates: { lat: 43.6667, lng: -92.9746 }, - }, - { - stopId: 6, - customer: "Lisa Garcia", - address: "987 Elm St, Albert Lea, MN", - serviceType: "Pump Station Installation", - estimatedTime: "20:45", - duration: 150, - status: "not started", - coordinates: { lat: 43.6481, lng: -93.3687 }, - }, - ], - }, - { - routeId: "MAT-002", - routeName: "South Metro Materials Route", - routeType: "Materials Route", - technician: "Sarah Johnson", - date: "2025-10-25", - status: "completed", - totalStops: 5, - completedStops: 5, - estimatedDuration: "4 hours", - startTime: "06:00", - vehicleId: "MAT-102", - totalMileage: 42.1, - stops: [ - { - stopId: 1, - customer: "Jennifer White - Job #2245", - address: "555 Main St, Burnsville, MN", - serviceType: "Deliver Pipes & Fittings", - estimatedTime: "06:30", - duration: 30, - status: "completed", - coordinates: { lat: 44.7677, lng: -93.2777 }, - }, - { - stopId: 2, - customer: "Mark Anderson - Job #2248", - address: "777 First Ave, Eagan, MN", - serviceType: "Deliver Sprinkler Heads & Valves", - estimatedTime: "07:15", - duration: 25, - status: "completed", - coordinates: { lat: 44.8041, lng: -93.1668 }, - }, - { - stopId: 3, - customer: "Carol Thompson - Job #2250", - address: "999 Second St, Apple Valley, MN", - serviceType: "Deliver Controller & Wire", - estimatedTime: "08:00", - duration: 20, - status: "completed", - coordinates: { lat: 44.7319, lng: -93.2177 }, - }, - { - stopId: 4, - customer: "Steven Clark - Job #2253", - address: "111 Third Ave, Rosemount, MN", - serviceType: "Deliver Backflow Preventer", - estimatedTime: "08:45", - duration: 25, - status: "completed", - coordinates: { lat: 44.7391, lng: -93.0658 }, - }, - { - stopId: 5, - customer: "Nancy Rodriguez - Job #2256", - address: "222 Fourth St, Lakeville, MN", - serviceType: "Deliver Pump & Pressure Tank", - estimatedTime: "09:30", - duration: 35, - status: "completed", - coordinates: { lat: 44.6497, lng: -93.2427 }, - }, - ], - }, - { - routeId: "WAR-003", - routeName: "West Metro Warranty Route", - routeType: "Warranty Route", - technician: "David Martinez", - date: "2025-10-26", - status: "not started", - totalStops: 6, - completedStops: 0, - estimatedDuration: "5.5 hours", - startTime: "08:00", - vehicleId: "SRV-103", - totalMileage: 48.7, - stops: [ - { - stopId: 1, - customer: "Rebecca Johnson - Warranty #W-445", - address: "444 West St, Minnetonka, MN", - serviceType: "Warranty: Valve Not Closing", - estimatedTime: "08:30", - duration: 60, - status: "not started", - coordinates: { lat: 44.9211, lng: -93.4687 }, - }, - { - stopId: 2, - customer: "Kevin Brown - Warranty #W-448", - address: "666 Lake Rd, Wayzata, MN", - serviceType: "Warranty: Controller Malfunction", - estimatedTime: "10:00", - duration: 75, - status: "not started", - coordinates: { lat: 44.9744, lng: -93.5066 }, - }, - { - stopId: 3, - customer: "Michelle Davis - Warranty #W-451", - address: "888 Shore Dr, Orono, MN", - serviceType: "Warranty: Sprinkler Head Broken", - estimatedTime: "11:30", - duration: 45, - status: "not started", - coordinates: { lat: 45.0541, lng: -93.6091 }, - }, - { - stopId: 4, - customer: "Daniel Wilson - Warranty #W-453", - address: "101 Bay Ln, Mound, MN", - serviceType: "Warranty: Pipe Leak Repair", - estimatedTime: "12:30", - duration: 90, - status: "not started", - coordinates: { lat: 44.9364, lng: -93.6719 }, - }, - { - stopId: 5, - customer: "Laura Smith - Warranty #W-456", - address: "202 Hill St, Chanhassen, MN", - serviceType: "Warranty: Low Water Pressure", - estimatedTime: "14:15", - duration: 60, - status: "not started", - coordinates: { lat: 44.8619, lng: -93.5272 }, - }, - { - stopId: 6, - customer: "James Lee - Warranty #W-458", - address: "303 Valley Rd, Chaska, MN", - serviceType: "Warranty: Zone Not Working", - estimatedTime: "15:30", - duration: 75, - status: "not started", - coordinates: { lat: 44.7886, lng: -93.6022 }, - }, - ], - }, - { - routeId: "SOD-004", - routeName: "East Metro Sod Route", - routeType: "Sod Route", - technician: "Chris Wilson", - date: "2025-10-26", - status: "in progress", - totalStops: 4, - completedStops: 1, - estimatedDuration: "6 hours", - startTime: "07:30", - vehicleId: "SOD-104", - totalMileage: 35.8, - stops: [ - { - stopId: 1, - customer: "Thomas Anderson - Job #2301", - address: "505 East St, Woodbury, MN", - serviceType: "Install 2,500 sq ft Sod", - estimatedTime: "08:00", - duration: 120, - status: "completed", - coordinates: { lat: 44.9237, lng: -92.9594 }, - }, - { - stopId: 2, - customer: "Patricia Miller - Job #2305", - address: "707 North Ave, Oakdale, MN", - serviceType: "Install 1,800 sq ft Sod", - estimatedTime: "10:30", - duration: 90, - status: "in progress", - coordinates: { lat: 44.9633, lng: -92.9652 }, - }, - { - stopId: 3, - customer: "Michael Taylor - Job #2308", - address: "909 South Blvd, Maplewood, MN", - serviceType: "Install 3,200 sq ft Sod", - estimatedTime: "12:15", - duration: 150, - status: "not started", - coordinates: { lat: 44.953, lng: -92.9952 }, - }, - { - stopId: 4, - customer: "Lisa Roberts - Job #2312", - address: "121 Center St, North St. Paul, MN", - serviceType: "Install 1,200 sq ft Sod", - estimatedTime: "15:00", - duration: 75, - status: "not started", - coordinates: { lat: 45.0119, lng: -93.0074 }, - }, - ], - }, - { - routeId: "MACH-005", - routeName: "Northwest Machine Route", - routeType: "Machine Route", - technician: "Lisa Anderson", - date: "2025-10-27", - status: "not started", - totalStops: 3, - completedStops: 0, - estimatedDuration: "5 hours", - startTime: "07:00", - vehicleId: "MACH-105", - totalMileage: 45.2, - stops: [ - { - stopId: 1, - customer: "Ashley White - Job #2320", - address: "141 Northwest Dr, Plymouth, MN", - serviceType: "Deliver Trenching Machine", - estimatedTime: "07:30", - duration: 90, - status: "not started", - coordinates: { lat: 45.0105, lng: -93.4555 }, - }, - { - stopId: 2, - customer: "Brian Johnson - Job #2325", - address: "151 Maple St, Maple Grove, MN", - serviceType: "Deliver Boring Machine", - estimatedTime: "09:15", - duration: 75, - status: "not started", - coordinates: { lat: 45.0724, lng: -93.4557 }, - }, - { - stopId: 3, - customer: "Christina Davis - Job #2328", - address: "161 Oak Ave, Osseo, MN", - serviceType: "Deliver Compactor & Tools", - estimatedTime: "10:45", - duration: 60, - status: "not started", - coordinates: { lat: 45.1188, lng: -93.4025 }, - }, - ], - }, - { - routeId: "TRK-006", - routeName: "Regional Truck Route", - routeType: "Truck Route", - technician: "Mark Thompson", - date: "2025-10-27", - status: "not started", - totalStops: 4, - completedStops: 0, - estimatedDuration: "6 hours", - startTime: "06:30", - vehicleId: "TRUCK-106", - totalMileage: 78.5, - stops: [ - { - stopId: 1, - customer: "Metro Landscaping - Pickup #445", - address: "2250 Highway 55, Plymouth, MN", - serviceType: "Equipment Pickup", - estimatedTime: "07:00", - duration: 30, - status: "not started", - coordinates: { lat: 45.0153, lng: -93.4233 }, - }, - { - stopId: 2, - customer: "Irrigation Supply Co - Order #889", - address: "1875 Industrial Blvd, Maple Grove, MN", - serviceType: "Material Pickup", - estimatedTime: "08:00", - duration: 45, - status: "not started", - coordinates: { lat: 45.0845, lng: -93.4321 }, - }, - { - stopId: 3, - customer: "Johnson Properties - Delivery", - address: "3456 Elm Street, Brooklyn Park, MN", - serviceType: "Equipment Drop-off", - estimatedTime: "09:15", - duration: 60, - status: "not started", - coordinates: { lat: 45.0941, lng: -93.3563 }, - }, - { - stopId: 4, - customer: "Storage Facility - Return", - address: "5200 Bass Lake Rd, Crystal, MN", - serviceType: "Equipment Return", - estimatedTime: "11:00", - duration: 45, - status: "not started", - coordinates: { lat: 45.0341, lng: -93.3678 }, - }, - ], - }, - ]; - - static dummyWarrantyData = [ - { - id: 1, - warrantyId: "W-001", - customer: "John Doe", - address: "123 Lane Dr, Cityville, MN", - originalJobId: "JOB001", - issueDescription: "Sprinkler head not rotating properly", - dateReported: "2024-10-20", - priority: "medium", - status: "open", - assignedTechnician: "Mike Johnson", - estimatedCompletionDate: "2024-10-25", - warrantyType: "Parts & Labor", - systemType: "Residential Irrigation", - }, - { - id: 2, - warrantyId: "W-002", - customer: "Jane Smith", - address: "456 Oak St, Townsville, CA", - originalJobId: "JOB002", - issueDescription: "Controller not responding to schedule", - dateReported: "2024-10-18", - priority: "high", - status: "in progress", - assignedTechnician: "Sarah Johnson", - estimatedCompletionDate: "2024-10-23", - warrantyType: "Parts Only", - systemType: "Commercial Irrigation", - }, - { - id: 3, - warrantyId: "W-003", - customer: "Mike Johnson", - address: "789 Pine Rd, Villagetown, TX", - originalJobId: "JOB003", - issueDescription: "Multiple zones not activating", - dateReported: "2024-10-15", - priority: "high", - status: "completed", - assignedTechnician: "David Martinez", - estimatedCompletionDate: "2024-10-20", - warrantyType: "Labor Only", - systemType: "Residential Irrigation", - }, - { - id: 4, - warrantyId: "W-004", - customer: "Emily Davis", - address: "321 Maple Ave, Hamlet, FL", - originalJobId: "JOB004", - issueDescription: "Low water pressure in all zones", - dateReported: "2024-10-22", - priority: "medium", - status: "open", - assignedTechnician: null, - estimatedCompletionDate: "2024-10-28", - warrantyType: "Parts & Labor", - systemType: "Residential Irrigation", - }, - { - id: 5, - warrantyId: "W-005", - customer: "David Wilson", - address: "654 Cedar Blvd, Borough, NY", - originalJobId: "JOB005", - issueDescription: "Valve stuck in open position", - dateReported: "2024-10-19", - priority: "high", - status: "in progress", - assignedTechnician: "Chris Wilson", - estimatedCompletionDate: "2024-10-24", - warrantyType: "Parts & Labor", - systemType: "Commercial Irrigation", - }, - { - id: 6, - warrantyId: "W-006", - customer: "Sarah Brown", - address: "987 Birch Ln, Metropolis, IL", - originalJobId: "JOB006", - issueDescription: "Broken sprinkler head causing water waste", - dateReported: "2024-10-21", - priority: "low", - status: "open", - assignedTechnician: null, - estimatedCompletionDate: "2024-10-30", - warrantyType: "Parts Only", - systemType: "Residential Irrigation", - }, - { - id: 7, - warrantyId: "W-007", - customer: "Chris Taylor", - address: "159 Spruce Ct, Capital City, WA", - originalJobId: "JOB007", - issueDescription: "Timer programming lost after power outage", - dateReported: "2024-10-17", - priority: "medium", - status: "completed", - assignedTechnician: "Mike Johnson", - estimatedCompletionDate: "2024-10-22", - warrantyType: "Labor Only", - systemType: "Commercial Irrigation", - }, - { - id: 8, - warrantyId: "W-008", - customer: "Amanda Martinez", - address: "753 Willow Dr, Smalltown, OH", - originalJobId: "JOB008", - issueDescription: "Pipe leak causing flooding in lawn", - dateReported: "2024-10-16", - priority: "high", - status: "in progress", - assignedTechnician: "Sarah Johnson", - estimatedCompletionDate: "2024-10-23", - warrantyType: "Parts & Labor", - systemType: "Residential Irrigation", - }, - { - id: 9, - warrantyId: "W-009", - customer: "Joshua Anderson", - address: "852 Aspen Rd, Bigcity, NJ", - originalJobId: "JOB009", - issueDescription: "Sprinkler coverage uneven after installation", - dateReported: "2024-10-14", - priority: "low", - status: "open", - assignedTechnician: "David Martinez", - estimatedCompletionDate: "2024-10-29", - warrantyType: "Labor Only", - systemType: "Residential Irrigation", - }, - { - id: 10, - warrantyId: "W-010", - customer: "Olivia Thomas", - address: "147 Cypress St, Uptown, GA", - originalJobId: "JOB010", - issueDescription: "Rain sensor not functioning properly", - dateReported: "2024-10-13", - priority: "medium", - status: "completed", - assignedTechnician: "Chris Wilson", - estimatedCompletionDate: "2024-10-18", - warrantyType: "Parts Only", - systemType: "Commercial Irrigation", - }, - ]; - - static dummyTimesheetData = [ - // Current week entries (Oct 25-31, 2025) - { - id: 1, - timesheetId: "TS-2025-001", - employee: "Mike Thompson", - employeeId: "EMP001", - date: "2025-10-25", - weekOf: "2025-10-21", - jobId: "JOB001", - customer: "John Doe", - address: "123 Lane Dr, Cityville, MN", - taskDescription: "Sprinkler System Installation - Zone 1-4", - clockIn: "07:30", - clockOut: "17:00", - breakTime: 30, // minutes - regularHours: 9.0, - overtimeHours: 0.5, - totalHours: 9.5, - status: "submitted", - approved: false, - approvedBy: null, - approvedDate: null, - hourlyRate: 28.5, - overtimeRate: 42.75, - totalPay: 292.88, - notes: "Completed main line installation and zone 1-4 heads", - equipment: ["Trencher", "Compactor", "Hand tools"], - mileage: 45, - materials: ["PVC Pipe - 200ft", "Sprinkler Heads - 15", "Valves - 4"], - }, - { - id: 2, - timesheetId: "TS-2025-002", - employee: "Sarah Johnson", - employeeId: "EMP002", - date: "2025-10-25", - weekOf: "2025-10-21", - jobId: "JOB007", - customer: "Chris Taylor", - address: "159 Spruce Ct, Capital City, WA", - taskDescription: "Zone Expansion Install", - clockIn: "08:00", - clockOut: "16:30", - breakTime: 30, - regularHours: 8.0, - overtimeHours: 0, - totalHours: 8.0, - status: "approved", - approved: true, - approvedBy: "Lisa Anderson", - approvedDate: "2025-10-26", - hourlyRate: 26.0, - overtimeRate: 39.0, - totalPay: 208.0, - notes: "Added 3 new zones, tested all systems", - equipment: ["Boring machine", "Hand tools"], - mileage: 32, - materials: [ - "Irrigation tubing - 150ft", - "Sprinkler Heads - 8", - "Control wire - 300ft", - ], - }, - { - id: 3, - timesheetId: "TS-2025-003", - employee: "David Martinez", - employeeId: "EMP003", - date: "2025-10-25", - weekOf: "2025-10-21", - jobId: "JOB012", - customer: "Sophia White", - address: "258 Palm Blvd, Riverside, AZ", - taskDescription: "Drip System Repair & Maintenance", - clockIn: "07:00", - clockOut: "15:30", - breakTime: 30, - regularHours: 8.0, - overtimeHours: 0, - totalHours: 8.0, - status: "draft", - approved: false, - approvedBy: null, - approvedDate: null, - hourlyRate: 24.5, - overtimeRate: 36.75, - totalPay: 196.0, - notes: "Repaired multiple leaks in drip lines, replaced emitters", - equipment: ["Hand tools", "Pressure gauge"], - mileage: 28, - materials: ["Drip tubing - 50ft", "Emitters - 25", "Fittings - misc"], - }, - { - id: 4, - timesheetId: "TS-2025-004", - employee: "Chris Wilson", - employeeId: "EMP004", - date: "2025-10-24", - weekOf: "2025-10-21", - jobId: "JOB003", - customer: "Mike Johnson", - address: "789 Pine Rd, Villagetown, TX", - taskDescription: "Controller Programming & System Testing", - clockIn: "09:00", - clockOut: "17:30", - breakTime: 60, - regularHours: 7.5, - overtimeHours: 0, - totalHours: 7.5, - status: "submitted", - approved: false, - approvedBy: null, - approvedDate: null, - hourlyRate: 30.0, - overtimeRate: 45.0, - totalPay: 225.0, - notes: "Programmed new smart controller, tested all 8 zones", - equipment: ["Laptop", "Multimeter", "Hand tools"], - mileage: 42, - materials: ["Smart Controller - 1", "Wire nuts - 10"], - }, - { - id: 5, - timesheetId: "TS-2025-005", - employee: "Lisa Anderson", - employeeId: "EMP005", - date: "2025-10-24", - weekOf: "2025-10-21", - jobId: "JOB017", - customer: "Ethan Walker", - address: "456 Walnut Rd, Meadowbrook, ND", - taskDescription: "Backflow Prevention Installation", - clockIn: "08:30", - clockOut: "16:00", - breakTime: 30, - regularHours: 7.0, - overtimeHours: 0, - totalHours: 7.0, - status: "approved", - approved: true, - approvedBy: "Mike Thompson", - approvedDate: "2025-10-25", - hourlyRate: 27.5, - overtimeRate: 41.25, - totalPay: 192.5, - notes: "Installed backflow preventer, pressure testing completed", - equipment: ["Pipe cutter", "Threading machine", "Pressure tester"], - mileage: 38, - materials: ["Backflow Preventer - 1", "Copper fittings - misc", "Solder"], - }, - { - id: 6, - timesheetId: "TS-2025-006", - employee: "Mike Thompson", - employeeId: "EMP001", - date: "2025-10-23", - weekOf: "2025-10-21", - jobId: "JOB015", - customer: "Andrew Lewis", - address: "357 Dogwood Dr, Lakeview, VT", - taskDescription: "System Winterization", - clockIn: "07:30", - clockOut: "15:00", - breakTime: 30, - regularHours: 7.0, - overtimeHours: 0, - totalHours: 7.0, - status: "approved", - approved: true, - approvedBy: "Lisa Anderson", - approvedDate: "2025-10-24", - hourlyRate: 28.5, - overtimeRate: 42.75, - totalPay: 199.5, - notes: "Winterized entire system, drained all lines and components", - equipment: ["Air compressor", "Hand tools"], - mileage: 55, - materials: ["Compressed air", "Antifreeze - 2 gallons"], - }, - // Previous week entries (Oct 14-20, 2025) - { - id: 7, - timesheetId: "TS-2025-007", - employee: "Sarah Johnson", - employeeId: "EMP002", - date: "2025-10-20", - weekOf: "2025-10-14", - jobId: "JOB010", - customer: "Olivia Thomas", - address: "147 Cypress St, Uptown, GA", - taskDescription: "Emergency Repair - Main Line Leak", - clockIn: "07:00", - clockOut: "18:30", - breakTime: 30, - regularHours: 8.0, - overtimeHours: 3.0, - totalHours: 11.0, - status: "approved", - approved: true, - approvedBy: "Mike Thompson", - approvedDate: "2025-10-21", - hourlyRate: 26.0, - overtimeRate: 39.0, - totalPay: 325.0, - notes: "Emergency call - repaired major main line break", - equipment: ["Excavator", "Pipe cutter", "Hand tools"], - mileage: 67, - materials: ["PVC Pipe - 20ft", "Couplings - 4", "Pipe cement"], - }, - { - id: 8, - timesheetId: "TS-2025-008", - employee: "David Martinez", - employeeId: "EMP003", - date: "2025-10-19", - weekOf: "2025-10-14", - jobId: "JOB005", - customer: "David Wilson", - address: "654 Cedar Blvd, Borough, NY", - taskDescription: "Final System Testing & Cleanup", - clockIn: "08:00", - clockOut: "16:30", - breakTime: 30, - regularHours: 8.0, - overtimeHours: 0, - totalHours: 8.0, - status: "approved", - approved: true, - approvedBy: "Lisa Anderson", - approvedDate: "2025-10-20", - hourlyRate: 24.5, - overtimeRate: 36.75, - totalPay: 196.0, - notes: "Final system testing, site cleanup, customer walkthrough", - equipment: ["Hand tools", "Cleanup supplies"], - mileage: 25, - materials: ["Grass seed - 5 lbs", "Topsoil - 2 bags"], - }, - { - id: 9, - timesheetId: "TS-2025-009", - employee: "Chris Wilson", - employeeId: "EMP004", - date: "2025-10-18", - weekOf: "2025-10-14", - jobId: "JOB009", - customer: "Joshua Anderson", - address: "852 Aspen Rd, Bigcity, NJ", - taskDescription: "Valve Replacement & Zone Testing", - clockIn: "08:30", - clockOut: "17:00", - breakTime: 30, - regularHours: 8.0, - overtimeHours: 0, - totalHours: 8.0, - status: "approved", - approved: true, - approvedBy: "Mike Thompson", - approvedDate: "2025-10-19", - hourlyRate: 30.0, - overtimeRate: 45.0, - totalPay: 240.0, - notes: "Replaced faulty zone 3 valve, tested all zones for proper operation", - equipment: ["Trenching shovel", "Hand tools", "Multimeter"], - mileage: 48, - materials: ["Zone Valve - 1", "Wire nuts - 5", "Electrical tape"], - }, - { - id: 10, - timesheetId: "TS-2025-010", - employee: "Lisa Anderson", - employeeId: "EMP005", - date: "2025-10-17", - weekOf: "2025-10-14", - jobId: "JOB014", - customer: "Isabella Clark", - address: "753 Magnolia Ct, Hilltown, SC", - taskDescription: "Sprinkler Head Adjustments & Coverage Testing", - clockIn: "09:00", - clockOut: "16:30", - breakTime: 30, - regularHours: 7.0, - overtimeHours: 0, - totalHours: 7.0, - status: "approved", - approved: true, - approvedBy: "Sarah Johnson", - approvedDate: "2025-10-18", - hourlyRate: 27.5, - overtimeRate: 41.25, - totalPay: 192.5, - notes: "Adjusted all sprinkler heads for optimal coverage, marked areas needing attention", - equipment: ["Hand tools", "Measuring tape", "Spray paint"], - mileage: 33, - materials: ["Sprinkler Head Adjustment Tools", "Marking flags - 20"], - }, - // Older entries for reporting - { - id: 11, - timesheetId: "TS-2025-011", - employee: "Mike Thompson", - employeeId: "EMP001", - date: "2025-10-16", - weekOf: "2025-10-14", - jobId: "JOB006", - customer: "Sarah Brown", - address: "987 Birch Ln, Metropolis, IL", - taskDescription: "Site Survey & Design Planning", - clockIn: "08:00", - clockOut: "15:30", - breakTime: 30, - regularHours: 7.0, - overtimeHours: 0, - totalHours: 7.0, - status: "approved", - approved: true, - approvedBy: "Lisa Anderson", - approvedDate: "2025-10-17", - hourlyRate: 28.5, - overtimeRate: 42.75, - totalPay: 199.5, - notes: "Completed site survey, created design plan for 4-zone expansion", - equipment: ["Measuring wheel", "Stake flags", "Camera"], - mileage: 40, - materials: ["Survey stakes - 50", "Flagging tape - 1 roll"], - }, - { - id: 12, - timesheetId: "TS-2025-012", - employee: "Sarah Johnson", - employeeId: "EMP002", - date: "2025-10-15", - weekOf: "2025-10-14", - jobId: "MAINT-001", - customer: "Multiple Properties", - address: "Various - See route sheet", - taskDescription: "Routine Maintenance Route - North Valley", - clockIn: "07:00", - clockOut: "16:00", - breakTime: 30, - regularHours: 8.5, - overtimeHours: 0, - totalHours: 8.5, - status: "approved", - approved: true, - approvedBy: "Mike Thompson", - approvedDate: "2025-10-16", - hourlyRate: 26.0, - overtimeRate: 39.0, - totalPay: 221.0, - notes: "Completed maintenance on 8 properties - routine checks and minor adjustments", - equipment: ["Service truck", "Hand tools", "Pressure gauge"], - mileage: 85, - materials: ["Sprinkler heads - 3", "Nozzles - 5", "Wire nuts - 10"], - }, - ]; - static US_STATES = [ "AL", "AK", @@ -1694,6 +65,33 @@ class DataUtils { "WI", "WY", ]; + + static calculateFullAddress(address) { + if (!address) return ""; + + // Build address parts with spaces, except comma between city and state + const addressParts = []; + + // Add address lines with spaces + if (address.addressLine1?.trim()) addressParts.push(address.addressLine1.trim()); + if (address.addressLine2?.trim()) addressParts.push(address.addressLine2.trim()); + + // Add city, state with comma, and zip + const cityStateParts = []; + if (address.city?.trim()) cityStateParts.push(address.city.trim()); + if (address.state?.trim()) cityStateParts.push(address.state.trim()); + + // Join city and state with comma, then add to address + if (cityStateParts.length > 0) { + addressParts.push(cityStateParts.join(", ")); + } + + // Add zip code + if (address.pincode?.trim()) addressParts.push(address.pincode.trim()); + + // Join all parts with spaces + return addressParts.join(" ").trim(); + } } export default DataUtils;
+ Are you sure you want to edit this client information? This will enable editing + mode. +
No coordinates available