update sidebar layout, update theme, update client table data

This commit is contained in:
Casey 2025-12-16 06:45:50 -06:00
parent 011080e0f8
commit 1a837ffcfc
4 changed files with 109 additions and 63 deletions

View file

@ -154,67 +154,71 @@ def get_client(client_name):
@frappe.whitelist() @frappe.whitelist()
def get_clients_table_data(filters={}, sortings=[], page=1, page_size=10): def get_clients_table_data(filters={}, sortings=[], page=1, page_size=10):
"""Get paginated client table data with filtering and sorting support.""" """Get paginated client table data with filtering and sorting support."""
# try: try:
# print("DEBUG: Raw client table query received:", { print("DEBUG: Raw client table query received:", {
# "filters": filters, "filters": filters,
# "sortings": sortings, "sortings": sortings,
# "page": page, "page": page,
# "page_size": page_size "page_size": page_size
# }) })
# processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size) 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 filters:", processed_filters)
# print("DEBUG: Processed sortings:", processed_sortings) print("DEBUG: Processed sortings:", processed_sortings)
# # Handle count with proper OR filter support # Handle count with proper OR filter support
# if is_or: if is_or:
# count = frappe.db.sql(*get_count_or_filters("Address", processed_filters))[0][0] count = frappe.db.sql(*get_count_or_filters("Address", processed_filters))[0][0]
# else: else:
# count = frappe.db.count("Address", filters=processed_filters) count = frappe.db.count("Address", filters=processed_filters)
# print("DEBUG: Count of addresses matching filters:", count) print("DEBUG: Count of addresses matching filters:", count)
# address_names = frappe.db.get_all( address_names = frappe.db.get_all(
# "Address", "Address",
# fields=["name"], fields=["name"],
# filters=processed_filters if not is_or else None, filters=processed_filters if not is_or else None,
# or_filters=processed_filters if is_or else None, or_filters=processed_filters if is_or else None,
# limit=page_size, limit=page_size,
# start=(page - 1) * page_size, start=(page - 1) * page_size,
# order_by=processed_sortings order_by=processed_sortings
# ) )
# addresses = [frappe.get_doc("Address", addr["name"]).as_dict() for addr in address_names] addresses = [frappe.get_doc("Address", addr["name"]).as_dict() for addr in address_names]
# tableRows = [] tableRows = []
# for address in addresses: for address in addresses:
# tableRow = {} is_lead = False
# links = address.links tableRow = {}
# customer_links = [link for link in links if link.link_doctype == "Customer"] if links else None links = address.links
# customer_name = address.get("custom_customer_to_bill") customer_links = [link for link in links if link.link_doctype == "Customer"] if links else None
# if not customer_name and not customer_links: customer_name = address.get("custom_customer_to_bill", None)
# print("DEBUG: No customer links found and no customer to bill.") if not customer_links:
# customer_name = "N/A" customer_links = [link for link in links if link.link_doctype == "Lead"] if links else None
# elif not customer_name and customer_links: is_lead = True if customer_links else False
# print("DEBUG: No customer to bill. Customer links found:", customer_links) if not customer_name and not customer_links:
# customer_name = customer_links[0].link_name print("DEBUG: No customer links found and no customer to bill.")
# tableRow["id"] = address["name"] customer_name = "N/A"
# tableRow["customer_name"] = customer_name elif not customer_name and customer_links:
# tableRow["address"] = ( print("DEBUG: No customer to bill. Customer links found:", customer_links)
# f"{address['address_line1']}" customer_name = frappe.get_value("Lead", customer_links[0].link_name, "lead_name") if is_lead else customer_links[0].link_name
# f"{' ' + address['address_line2'] if address['address_line2'] else ''} " tableRow["id"] = address["name"]
# f"{address['city']}, {address['state']} {address['pincode']}" tableRow["customer_name"] = customer_name
# ) tableRow["address"] = (
# tableRow["appointment_scheduled_status"] = address.custom_onsite_meeting_scheduled f"{address['address_line1']}"
# tableRow["estimate_sent_status"] = address.custom_estimate_sent_status f"{' ' + address['address_line2'] if address['address_line2'] else ''} "
# tableRow["job_status"] = address.custom_job_status f"{address['city']}, {address['state']} {address['pincode']}"
# tableRow["payment_received_status"] = address.custom_payment_received_status )
# tableRows.append(tableRow) tableRow["appointment_scheduled_status"] = address.custom_onsite_meeting_scheduled
# tableDataDict = build_datatable_dict(data=tableRows, count=count, page=page, page_size=page_size) tableRow["estimate_sent_status"] = address.custom_estimate_sent_status
# return build_success_response(tableDataDict) tableRow["job_status"] = address.custom_job_status
# except frappe.ValidationError as ve: tableRow["payment_received_status"] = address.custom_payment_received_status
# return build_error_response(str(ve), 400) tableRows.append(tableRow)
# except Exception as e: tableDataDict = build_datatable_dict(data=tableRows, count=count, page=page, page_size=page_size)
# return build_error_response(str(e), 500) return build_success_response(tableDataDict)
except frappe.ValidationError as ve:
return build_error_response(str(ve), 400)
except Exception as e:
return build_error_response(str(e), 500)
@frappe.whitelist() @frappe.whitelist()

View file

@ -19,6 +19,7 @@ const notificationStore = useNotificationStore();
const toast = ref(); const toast = ref();
const companyStore = useCompanyStore(); const companyStore = useCompanyStore();
const themeStore = useThemeStore(); const themeStore = useThemeStore();
const sidebarCollapsed = ref(false);
// Connect the toast instance to the store when component mounts // Connect the toast instance to the store when component mounts
onMounted(() => { onMounted(() => {
@ -46,8 +47,8 @@ watchEffect(() => {
}" }"
> >
<div id="snw-ui"> <div id="snw-ui">
<div class="sidebar-column"> <div class="sidebar-column" :class="{ collapsed: sidebarCollapsed }">
<SideBar /> <SideBar @toggle="sidebarCollapsed = $event" />
</div> </div>
<div id="display-content"> <div id="display-content">
<ScrollPanel style="width: 100%; height: 100%"> <ScrollPanel style="width: 100%; height: 100%">
@ -82,21 +83,53 @@ watchEffect(() => {
width: 100%; width: 100%;
margin: 10px auto; margin: 10px auto;
height: 90vh; height: 90vh;
background: var(--theme-gradient); background: var(--theme-background-gradient);
} }
.sidebar-column { .sidebar-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
width: 170px;
min-width: 170px; min-width: 170px;
transition: width 0.3s ease, min-width 0.3s ease;
}
.sidebar-column.collapsed {
width: 56px;
min-width: 56px;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.sidebar-column {
width: 140px;
min-width: 140px;
}
.sidebar-column.collapsed {
width: 56px;
min-width: 56px;
}
}
@media (max-width: 480px) {
.sidebar-column {
width: 120px;
min-width: 120px;
}
.sidebar-column.collapsed {
width: 56px;
min-width: 56px;
}
} }
#display-content { #display-content {
/* flex-grow: 1; */ flex-grow: 1;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
max-width: 50vw; max-width: 100%;
min-width: 80%; min-width: 80%;
height: 100%; height: 100%;
} }

View file

@ -22,6 +22,8 @@ import {
} from "@iconoir/vue"; } from "@iconoir/vue";
import SidebarSpeedDial from "./SidebarSpeedDial.vue"; import SidebarSpeedDial from "./SidebarSpeedDial.vue";
const emit = defineEmits(['toggle']);
const router = useRouter(); const router = useRouter();
const modalStore = useModalStore(); const modalStore = useModalStore();
const notifications = useNotificationStore(); const notifications = useNotificationStore();
@ -40,6 +42,7 @@ const focusClientInput = (inputId) => {
const toggleSidebar = () => { const toggleSidebar = () => {
isCollapsed.value = !isCollapsed.value; isCollapsed.value = !isCollapsed.value;
emit('toggle', isCollapsed.value);
}; };
const openSidebarAndDial = (category) => { const openSidebarAndDial = (category) => {

View file

@ -12,6 +12,7 @@ const themeMap = {
secondaryGradientStart: "#2c9b64", secondaryGradientStart: "#2c9b64",
secondaryGradientEnd: "#38c487", secondaryGradientEnd: "#38c487",
secondaryGradient: "linear-gradient(90deg, #2c9b64 0%, #38c487 100%)", secondaryGradient: "linear-gradient(90deg, #2c9b64 0%, #38c487 100%)",
backgroundGradient: "linear-gradient(90deg, #f0f8ff 0%, #e6f3ff 100%)",
surface: "#cadcf1ff", surface: "#cadcf1ff",
surfaceAlt: "#dbfdefff", surfaceAlt: "#dbfdefff",
border: "#cfe5f7", border: "#cfe5f7",
@ -32,6 +33,7 @@ const themeMap = {
secondaryGradientStart: "#d2a106", secondaryGradientStart: "#d2a106",
secondaryGradientEnd: "#f7de6d", secondaryGradientEnd: "#f7de6d",
secondaryGradient: "linear-gradient(90deg, #d2a106 0%, #f7de6d 100%)", secondaryGradient: "linear-gradient(90deg, #d2a106 0%, #f7de6d 100%)",
backgroundGradient: "linear-gradient(90deg, #f8fff0 0%, #f0f8e6 100%)",
surface: "#f7fbe9", surface: "#f7fbe9",
surfaceAlt: "#f1f4d6", surfaceAlt: "#f1f4d6",
border: "#dfe8b5", border: "#dfe8b5",
@ -52,6 +54,7 @@ const themeMap = {
secondaryGradientStart: "#4f8ee5", secondaryGradientStart: "#4f8ee5",
secondaryGradientEnd: "#5fa4ff", secondaryGradientEnd: "#5fa4ff",
secondaryGradient: "linear-gradient(90deg, #4f8ee5 0%, #5fa4ff 100%)", secondaryGradient: "linear-gradient(90deg, #4f8ee5 0%, #5fa4ff 100%)",
backgroundGradient: "linear-gradient(90deg, #f5f7fb 0%, #e7ecf5 100%)",
surface: "#f5f7fb", surface: "#f5f7fb",
surfaceAlt: "#e7ecf5", surfaceAlt: "#e7ecf5",
border: "#ced6e5", border: "#ced6e5",
@ -72,6 +75,7 @@ const themeMap = {
secondaryGradientStart: "#b2a89c", secondaryGradientStart: "#b2a89c",
secondaryGradientEnd: "#cfc6b8", secondaryGradientEnd: "#cfc6b8",
secondaryGradient: "linear-gradient(90deg, #b2a89c 0%, #cfc6b8 100%)", secondaryGradient: "linear-gradient(90deg, #b2a89c 0%, #cfc6b8 100%)",
backgroundGradient: "linear-gradient(90deg, #f7f5f2 0%, #ebe6df 100%)",
surface: "#f7f5f2", surface: "#f7f5f2",
surfaceAlt: "#ebe6df", surfaceAlt: "#ebe6df",
border: "#d8d0c5", border: "#d8d0c5",
@ -92,6 +96,7 @@ const themeMap = {
secondaryGradientStart: "#f28c28", secondaryGradientStart: "#f28c28",
secondaryGradientEnd: "#ffc174", secondaryGradientEnd: "#ffc174",
secondaryGradient: "linear-gradient(90deg, #f28c28 0%, #ffc174 100%)", secondaryGradient: "linear-gradient(90deg, #f28c28 0%, #ffc174 100%)",
backgroundGradient: "linear-gradient(90deg, #f8fbf4 0%, #f2f1e9 100%)",
surface: "#f8fbf4", surface: "#f8fbf4",
surfaceAlt: "#f2f1e9", surfaceAlt: "#f2f1e9",
border: "#d9e5cc", border: "#d9e5cc",
@ -105,11 +110,11 @@ const themeMap = {
export const useThemeStore = defineStore("theme", { export const useThemeStore = defineStore("theme", {
state: () => ({ state: () => ({
currentTheme: themeMap.SprinklersNW, currentTheme: themeMap["Sprinklers Northwest"],
}), }),
actions: { actions: {
applyTheme(companyName) { applyTheme(companyName) {
const theme = themeMap[companyName] || themeMap.SprinklersNW; const theme = themeMap[companyName] || themeMap["Sprinklers Northwest"];
this.currentTheme = theme; this.currentTheme = theme;
if (typeof document === "undefined") return; if (typeof document === "undefined") return;
const root = document.documentElement; const root = document.documentElement;
@ -123,6 +128,7 @@ export const useThemeStore = defineStore("theme", {
root.style.setProperty("--theme-secondary-gradient-start", theme.secondaryGradientStart); root.style.setProperty("--theme-secondary-gradient-start", theme.secondaryGradientStart);
root.style.setProperty("--theme-secondary-gradient-end", theme.secondaryGradientEnd); root.style.setProperty("--theme-secondary-gradient-end", theme.secondaryGradientEnd);
root.style.setProperty("--theme-secondary-gradient", theme.secondaryGradient); root.style.setProperty("--theme-secondary-gradient", theme.secondaryGradient);
root.style.setProperty("--theme-background-gradient", theme.backgroundGradient);
root.style.setProperty("--theme-surface", theme.surface); root.style.setProperty("--theme-surface", theme.surface);
root.style.setProperty("--theme-surface-alt", theme.surfaceAlt); root.style.setProperty("--theme-surface-alt", theme.surfaceAlt);
root.style.setProperty("--theme-border", theme.border); root.style.setProperty("--theme-border", theme.border);