diff --git a/crm/api/report.py b/crm/api/report.py new file mode 100644 index 000000000..07413b515 --- /dev/null +++ b/crm/api/report.py @@ -0,0 +1,20 @@ +import frappe +from frappe.desk.query_report import run + +@frappe.whitelist(allow_guest=True) +def get_report(report_name, filters=None): + print("Fetching report data...") + if isinstance(filters, str): + import json + filters = json.loads(filters) + + # Run the report + try: + report_output = run(report_name, filters or {}) + return { + "columns": report_output.get("columns"), + "data": report_output.get("result") + } + except Exception as e: + frappe.log_error(frappe.get_traceback(), "Error fetching report data") + return {"error": str(e)} diff --git a/crm/fcrm/api.py b/crm/fcrm/api.py new file mode 100644 index 000000000..71727c0bc --- /dev/null +++ b/crm/fcrm/api.py @@ -0,0 +1,19 @@ + +import frappe +from frappe import _ + +@frappe.whitelist(allow_guest=True) +def get_fcrm_reports(): + """Fetch all standard and custom reports for FCRM module (Redsoft CRM).""" + reports = frappe.get_all( + "Report", + filters={"module": "Redsoft CRM"}, + fields=["name", "ref_doctype", "report_type", "is_standard", "modified"] + ) + + return { + "status": "success", + "module": "Redsoft CRM", + "count": len(reports), + "data": reports + } diff --git a/crm/fcrm/doctype/crm_lead/crm_lead.json b/crm/fcrm/doctype/crm_lead/crm_lead.json index e39af407f..c5e84c820 100644 --- a/crm/fcrm/doctype/crm_lead/crm_lead.json +++ b/crm/fcrm/doctype/crm_lead/crm_lead.json @@ -290,7 +290,7 @@ "image_field": "image", "index_web_pages_for_search": 1, "links": [], - "modified": "2025-01-02 22:14:01.991054", + "modified": "2025-04-21 11:56:49.339554", "modified_by": "Administrator", "module": "FCRM", "name": "CRM Lead", @@ -329,8 +329,18 @@ "report": 1, "role": "All", "share": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Guest", + "share": 1 } ], + "row_format": "Dynamic", "sender_field": "email", "sender_name_field": "first_name", "show_title_field_in_link": 1, diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 82afdb191..06b1915a3 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -188,6 +188,8 @@ declare module 'vue' { ReloadIcon: typeof import('./src/components/Icons/ReloadIcon.vue')['default'] ReplyAllIcon: typeof import('./src/components/Icons/ReplyAllIcon.vue')['default'] ReplyIcon: typeof import('./src/components/Icons/ReplyIcon.vue')['default'] + ReportDetailModal: typeof import('./src/components/Modals/ReportDetailModal.vue')['default'] + ReportsListView: typeof import('./src/components/ListViews/ReportsListView.vue')['default'] Resizer: typeof import('./src/components/Resizer.vue')['default'] RightSideLayoutIcon: typeof import('./src/components/Icons/RightSideLayoutIcon.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] diff --git a/frontend/src/components/Layouts/AppSidebar.vue b/frontend/src/components/Layouts/AppSidebar.vue index 05d5bbb0e..3b2cf2e95 100644 --- a/frontend/src/components/Layouts/AppSidebar.vue +++ b/frontend/src/components/Layouts/AppSidebar.vue @@ -164,6 +164,7 @@ import SidebarLink from '@/components/SidebarLink.vue' import Notifications from '@/components/Notifications.vue' import Settings from '@/components/Settings/Settings.vue' import { viewsStore } from '@/stores/views' +import DashboardIcon from '@/components/Icons/DashboardIcon.vue' import { unreadNotificationsCount, notificationsStore, @@ -195,14 +196,14 @@ const isDemoSite = ref(window.is_demo_site) const links = [ { - label: 'Leads', + label: 'Patients', icon: LeadsIcon, - to: 'Leads', + to: 'Patients', }, { - label: 'Deals', + label: 'Bookings', icon: DealsIcon, - to: 'Deals', + to: 'Bookings', }, { label: 'Contacts', @@ -234,6 +235,11 @@ const links = [ icon: Email2Icon, to: 'Email Templates', }, + { + label: 'Reports', + icon: DashboardIcon, + to: 'Reports', + }, ] const allViews = computed(() => { @@ -281,9 +287,9 @@ function getIcon(routeName, icon) { if (icon) return h('div', { class: 'size-auto' }, icon) switch (routeName) { - case 'Leads': + case 'Patients': return LeadsIcon - case 'Deals': + case 'Bookings': return DealsIcon case 'Contacts': return ContactsIcon @@ -292,7 +298,9 @@ function getIcon(routeName, icon) { case 'Notes': return NoteIcon case 'Call Logs': - return PhoneIcon + return + case 'Dashboard': + return DashboardIcon default: return PinIcon } @@ -324,7 +332,7 @@ const steps = reactive([ completed: false, onClick: () => { minimize.value = true - router.push({ name: 'Leads' }) + router.push({ name: 'Patients' }) }, }, { @@ -358,7 +366,7 @@ const steps = reactive([ if (lead) { router.push({ name: 'Lead', params: { leadId: lead } }) } else { - router.push({ name: 'Leads' }) + router.push({ name: 'Patients' }) } }, } @@ -421,7 +429,7 @@ const steps = reactive([ hash: '#comments', }) } else { - router.push({ name: 'Leads' }) + router.push({ name: 'Patients' }) } }, }, @@ -441,7 +449,7 @@ const steps = reactive([ hash: '#emails', }) } else { - router.push({ name: 'Leads' }) + router.push({ name: 'Patients' }) } }, }, @@ -469,7 +477,7 @@ const steps = reactive([ hash: '#activity', }) } else { - router.push({ name: 'Leads' }) + router.push({ name: 'Patients' }) } }, } diff --git a/frontend/src/components/ListViews/ReportsListView.vue b/frontend/src/components/ListViews/ReportsListView.vue new file mode 100644 index 000000000..67f1066ff --- /dev/null +++ b/frontend/src/components/ListViews/ReportsListView.vue @@ -0,0 +1,207 @@ + + + diff --git a/frontend/src/components/Mobile/MobileSidebar.vue b/frontend/src/components/Mobile/MobileSidebar.vue index af313d635..eebe8a768 100644 --- a/frontend/src/components/Mobile/MobileSidebar.vue +++ b/frontend/src/components/Mobile/MobileSidebar.vue @@ -108,19 +108,20 @@ import { createResource } from 'frappe-ui' import { TrialBanner } from 'frappe-ui/frappe' import { computed, h, provide } from 'vue' import { mobileSidebarOpened as sidebarOpened } from '@/composables/settings' +import DashboardIcon from '@/components/Icons/DashboardIcon.vue' const { getPinnedViews, getPublicViews } = viewsStore() const links = [ { - label: 'Leads', + label: 'Patients', icon: LeadsIcon, - to: 'Leads', + to: 'Patients', }, { - label: 'Deals', + label: 'Bookings', icon: DealsIcon, - to: 'Deals', + to: 'Bookings', }, { label: 'Contacts', @@ -152,6 +153,11 @@ const links = [ icon: Email2Icon, to: 'Email Templates', }, + { + label: 'Reports', + icon: DashboardIcon, + to: 'Reports', + }, ] const allViews = computed(() => { @@ -199,9 +205,9 @@ function getIcon(routeName, icon) { if (icon) return h('div', { class: 'size-auto' }, icon) switch (routeName) { - case 'Leads': + case 'Patients': return LeadsIcon - case 'Deals': + case 'Patients': return DealsIcon case 'Contacts': return ContactsIcon @@ -211,6 +217,8 @@ function getIcon(routeName, icon) { return NoteIcon case 'Call Logs': return PhoneIcon + case 'Dashboard': + return DashboardIcon default: return PinIcon } diff --git a/frontend/src/components/Modals/DealModal.vue b/frontend/src/components/Modals/DealModal.vue index 7bd0b592b..4ad3dc04c 100644 --- a/frontend/src/components/Modals/DealModal.vue +++ b/frontend/src/components/Modals/DealModal.vue @@ -5,7 +5,7 @@

- {{ __('Create Deal') }} + {{ __('Create Booking') }}

diff --git a/frontend/src/components/Modals/LeadModal.vue b/frontend/src/components/Modals/LeadModal.vue index 95e0add20..09f1dc2aa 100644 --- a/frontend/src/components/Modals/LeadModal.vue +++ b/frontend/src/components/Modals/LeadModal.vue @@ -5,7 +5,7 @@

- {{ __('Create Lead') }} + {{ __('Create Patient') }}

diff --git a/frontend/src/components/Modals/ReportDetailModal.vue b/frontend/src/components/Modals/ReportDetailModal.vue new file mode 100644 index 000000000..840fa14fc --- /dev/null +++ b/frontend/src/components/Modals/ReportDetailModal.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/frontend/src/components/Modals/ViewModal.vue b/frontend/src/components/Modals/ViewModal.vue index a3ba26f7f..3b4d6f0bc 100644 --- a/frontend/src/components/Modals/ViewModal.vue +++ b/frontend/src/components/Modals/ViewModal.vue @@ -37,7 +37,7 @@ class="flex-1" size="md" type="text" - :placeholder="__('My Open Deals')" + :placeholder="__('My Open Bookings')" v-model="view.label" />
diff --git a/frontend/src/pages/Contact.vue b/frontend/src/pages/Contact.vue index bfe7ea76f..60f739c98 100644 --- a/frontend/src/pages/Contact.vue +++ b/frontend/src/pages/Contact.vue @@ -150,7 +150,7 @@