Merge pull request #852 from frappe/main-hotfix
chore: Merge hotfix to main
This commit is contained in:
commit
691bf96dd2
@ -124,6 +124,7 @@ def get_deal_activities(name):
|
|||||||
activity = {
|
activity = {
|
||||||
"activity_type": "communication",
|
"activity_type": "communication",
|
||||||
"communication_type": communication.communication_type,
|
"communication_type": communication.communication_type,
|
||||||
|
"communication_date": communication.communication_date or communication.creation,
|
||||||
"creation": communication.creation,
|
"creation": communication.creation,
|
||||||
"data": {
|
"data": {
|
||||||
"subject": communication.subject,
|
"subject": communication.subject,
|
||||||
@ -255,6 +256,7 @@ def get_lead_activities(name):
|
|||||||
activity = {
|
activity = {
|
||||||
"activity_type": "communication",
|
"activity_type": "communication",
|
||||||
"communication_type": communication.communication_type,
|
"communication_type": communication.communication_type,
|
||||||
|
"communication_date": communication.communication_date or communication.creation,
|
||||||
"creation": communication.creation,
|
"creation": communication.creation,
|
||||||
"data": {
|
"data": {
|
||||||
"subject": communication.subject,
|
"subject": communication.subject,
|
||||||
|
|||||||
@ -24,9 +24,15 @@ class FCRMSettings(Document):
|
|||||||
standard_old_items = [d.name1 for d in old_items if d.is_standard]
|
standard_old_items = [d.name1 for d in old_items if d.is_standard]
|
||||||
deleted_standard_items = set(standard_old_items) - set(standard_new_items)
|
deleted_standard_items = set(standard_old_items) - set(standard_new_items)
|
||||||
if deleted_standard_items:
|
if deleted_standard_items:
|
||||||
|
standard_dropdown_items = get_standard_dropdown_items()
|
||||||
|
if not deleted_standard_items.intersection(standard_dropdown_items):
|
||||||
|
return
|
||||||
frappe.throw(_("Cannot delete standard items {0}").format(", ".join(deleted_standard_items)))
|
frappe.throw(_("Cannot delete standard items {0}").format(", ".join(deleted_standard_items)))
|
||||||
|
|
||||||
|
|
||||||
|
def get_standard_dropdown_items():
|
||||||
|
return [item.get("name1") for item in frappe.get_hooks("standard_dropdown_items")]
|
||||||
|
|
||||||
|
|
||||||
def after_migrate():
|
def after_migrate():
|
||||||
sync_table("dropdown_items", "standard_dropdown_items")
|
sync_table("dropdown_items", "standard_dropdown_items")
|
||||||
|
|||||||
24
crm/hooks.py
24
crm/hooks.py
@ -264,22 +264,6 @@ standard_dropdown_items = [
|
|||||||
"route": "#",
|
"route": "#",
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name1": "support_link",
|
|
||||||
"label": "Support",
|
|
||||||
"type": "Route",
|
|
||||||
"icon": "life-buoy",
|
|
||||||
"route": "https://t.me/frappecrm",
|
|
||||||
"is_standard": 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name1": "docs_link",
|
|
||||||
"label": "Docs",
|
|
||||||
"type": "Route",
|
|
||||||
"icon": "book-open",
|
|
||||||
"route": "https://docs.frappe.io/crm",
|
|
||||||
"is_standard": 1,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name1": "toggle_theme",
|
"name1": "toggle_theme",
|
||||||
"label": "Toggle theme",
|
"label": "Toggle theme",
|
||||||
@ -303,6 +287,14 @@ standard_dropdown_items = [
|
|||||||
"route": "#",
|
"route": "#",
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name1": "about",
|
||||||
|
"label": "About",
|
||||||
|
"type": "Route",
|
||||||
|
"icon": "info",
|
||||||
|
"route": "#",
|
||||||
|
"is_standard": 1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name1": "separator",
|
"name1": "separator",
|
||||||
"label": "",
|
"label": "",
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# GNU GPLv3 License. See license.txt
|
# GNU GPLv3 License. See license.txt
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import safe_decode
|
||||||
from frappe.integrations.frappe_providers.frappecloud_billing import is_fc_site
|
from frappe.integrations.frappe_providers.frappecloud_billing import is_fc_site
|
||||||
from frappe.utils import cint, get_system_timezone
|
from frappe.utils import cint, get_system_timezone
|
||||||
from frappe.utils.telemetry import capture
|
from frappe.utils.telemetry import capture
|
||||||
@ -43,9 +45,41 @@ def get_boot():
|
|||||||
"user": frappe.db.get_value("User", frappe.session.user, "time_zone")
|
"user": frappe.db.get_value("User", frappe.session.user, "time_zone")
|
||||||
or get_system_timezone(),
|
or get_system_timezone(),
|
||||||
},
|
},
|
||||||
|
"app_version": get_app_version(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_default_route():
|
def get_default_route():
|
||||||
return "/crm"
|
return "/crm"
|
||||||
|
|
||||||
|
|
||||||
|
def get_app_version():
|
||||||
|
app = "crm"
|
||||||
|
branch = run_git_command(f"cd ../apps/{app} && git rev-parse --abbrev-ref HEAD")
|
||||||
|
commit = run_git_command(f"git -C ../apps/{app} rev-parse --short=7 HEAD")
|
||||||
|
tag = run_git_command(f"git -C ../apps/{app} describe --tags --abbrev=0")
|
||||||
|
dirty = run_git_command(f"git -C ../apps/{app} diff --quiet || echo 'dirty'") == "dirty"
|
||||||
|
commit_date = run_git_command(f"git -C ../apps/{app} log -1 --format=%cd")
|
||||||
|
commit_message = run_git_command(f"git -C ../apps/{app} log -1 --pretty=%B")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"branch": branch,
|
||||||
|
"commit": commit,
|
||||||
|
"commit_date": commit_date,
|
||||||
|
"commit_message": commit_message,
|
||||||
|
"tag": tag,
|
||||||
|
"dirty": dirty,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def run_git_command(command):
|
||||||
|
try:
|
||||||
|
with open(os.devnull, "wb") as null_stream:
|
||||||
|
result = subprocess.check_output(command, shell=True, stdin=null_stream, stderr=null_stream)
|
||||||
|
return safe_decode(result).strip()
|
||||||
|
except Exception:
|
||||||
|
frappe.log_error(
|
||||||
|
title="Git Command Error",
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
|||||||
4
frontend/components.d.ts
vendored
4
frontend/components.d.ts
vendored
@ -8,6 +8,7 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
AboutModal: typeof import('./src/components/Modals/AboutModal.vue')['default']
|
||||||
Activities: typeof import('./src/components/Activities/Activities.vue')['default']
|
Activities: typeof import('./src/components/Activities/Activities.vue')['default']
|
||||||
ActivityHeader: typeof import('./src/components/Activities/ActivityHeader.vue')['default']
|
ActivityHeader: typeof import('./src/components/Activities/ActivityHeader.vue')['default']
|
||||||
ActivityIcon: typeof import('./src/components/Icons/ActivityIcon.vue')['default']
|
ActivityIcon: typeof import('./src/components/Icons/ActivityIcon.vue')['default']
|
||||||
@ -151,7 +152,9 @@ declare module 'vue' {
|
|||||||
ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default']
|
ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default']
|
||||||
LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default']
|
LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default']
|
||||||
LucideCalendar: typeof import('~icons/lucide/calendar')['default']
|
LucideCalendar: typeof import('~icons/lucide/calendar')['default']
|
||||||
|
LucideInfo: typeof import('~icons/lucide/info')['default']
|
||||||
LucidePlus: typeof import('~icons/lucide/plus')['default']
|
LucidePlus: typeof import('~icons/lucide/plus')['default']
|
||||||
|
LucideSearch: typeof import('~icons/lucide/search')['default']
|
||||||
MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default']
|
MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default']
|
||||||
MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default']
|
MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default']
|
||||||
MenuIcon: typeof import('./src/components/Icons/MenuIcon.vue')['default']
|
MenuIcon: typeof import('./src/components/Icons/MenuIcon.vue')['default']
|
||||||
@ -221,6 +224,7 @@ declare module 'vue' {
|
|||||||
TaskPriorityIcon: typeof import('./src/components/Icons/TaskPriorityIcon.vue')['default']
|
TaskPriorityIcon: typeof import('./src/components/Icons/TaskPriorityIcon.vue')['default']
|
||||||
TasksListView: typeof import('./src/components/ListViews/TasksListView.vue')['default']
|
TasksListView: typeof import('./src/components/ListViews/TasksListView.vue')['default']
|
||||||
TaskStatusIcon: typeof import('./src/components/Icons/TaskStatusIcon.vue')['default']
|
TaskStatusIcon: typeof import('./src/components/Icons/TaskStatusIcon.vue')['default']
|
||||||
|
TelegramIcon: typeof import('./src/components/Icons/TelegramIcon.vue')['default']
|
||||||
TelephonySettings: typeof import('./src/components/Settings/TelephonySettings.vue')['default']
|
TelephonySettings: typeof import('./src/components/Settings/TelephonySettings.vue')['default']
|
||||||
TerritoryIcon: typeof import('./src/components/Icons/TerritoryIcon.vue')['default']
|
TerritoryIcon: typeof import('./src/components/Icons/TerritoryIcon.vue')['default']
|
||||||
TwilioCallUI: typeof import('./src/components/Telephony/TwilioCallUI.vue')['default']
|
TwilioCallUI: typeof import('./src/components/Telephony/TwilioCallUI.vue')['default']
|
||||||
|
|||||||
@ -22,9 +22,9 @@
|
|||||||
variant="subtle"
|
variant="subtle"
|
||||||
:theme="status.color"
|
:theme="status.color"
|
||||||
/>
|
/>
|
||||||
<Tooltip :text="formatDate(activity.creation)">
|
<Tooltip :text="formatDate(activity.communication_date)">
|
||||||
<div class="text-sm text-ink-gray-5">
|
<div class="text-sm text-ink-gray-5">
|
||||||
{{ __(timeAgo(activity.creation)) }}
|
{{ __(timeAgo(activity.communication_date)) }}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div class="flex gap-0.5">
|
<div class="flex gap-0.5">
|
||||||
|
|||||||
21
frontend/src/components/Icons/TelegramIcon.vue
Normal file
21
frontend/src/components/Icons/TelegramIcon.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 64 64"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
id="SVGRepo_tracerCarrier"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
></g>
|
||||||
|
<g id="SVGRepo_iconCarrier">
|
||||||
|
<path
|
||||||
|
d="M26.67,38.57l-.82,11.54A2.88,2.88,0,0,0,28.14,49l5.5-5.26,11.42,8.35c2.08,1.17,3.55.56,4.12-1.92l7.49-35.12h0c.66-3.09-1.08-4.33-3.16-3.55l-44,16.85C6.47,29.55,6.54,31.23,9,32l11.26,3.5L45.59,20.71c1.23-.83,2.36-.37,1.44.44Z"
|
||||||
|
stroke-linecap="round"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
100
frontend/src/components/Modals/AboutModal.vue
Normal file
100
frontend/src/components/Modals/AboutModal.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="show" :options="{ size: 'sm' }">
|
||||||
|
<template #body>
|
||||||
|
<div class="p-4 pt-5">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<CRMLogo class="mb-3 size-12" />
|
||||||
|
<h3 class="font-semibold text-xl text-ink-gray-9">Frappe CRM</h3>
|
||||||
|
<div class="flex items-center mt-1">
|
||||||
|
<div class="text-base text-ink-gray-6">
|
||||||
|
{{ appVersion.branch != 'main' ? appVersion.branch : '' }}
|
||||||
|
<template v-if="appVersion.branch != 'main'">
|
||||||
|
({{ appVersion.commit }})
|
||||||
|
</template>
|
||||||
|
<template v-else>{{ appVersion.tag }}</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tooltip
|
||||||
|
:text="`${appVersion.commit_message} - ${appVersion.commit_date}`"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<LucideInfo class="size-3.5 text-ink-gray-8 ml-1" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="border-t my-3 mx-2" />
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
v-for="link in links"
|
||||||
|
:key="link.label"
|
||||||
|
class="flex py-2 px-2 hover:bg-surface-gray-1 rounded cursor-pointer"
|
||||||
|
target="_blank"
|
||||||
|
:href="link.url"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
v-if="link.icon"
|
||||||
|
:is="link.icon"
|
||||||
|
class="size-4 mr-2 text-ink-gray-7"
|
||||||
|
/>
|
||||||
|
<span class="text-base text-ink-gray-8">
|
||||||
|
{{ link.label }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<hr class="border-t my-3 mx-2" />
|
||||||
|
<p class="text-sm text-ink-gray-6 px-2 mt-2">
|
||||||
|
© Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Tooltip } from 'frappe-ui'
|
||||||
|
import CRMLogo from '@/components/Icons/CRMLogo.vue'
|
||||||
|
import LucideGlobe from '~icons/lucide/globe'
|
||||||
|
import LucideGitHub from '~icons/lucide/github'
|
||||||
|
import LucideHeadset from '~icons/lucide/headset'
|
||||||
|
import LucideBug from '~icons/lucide/bug'
|
||||||
|
import LucideBookOpen from '~icons/lucide/book-open'
|
||||||
|
import TelegramIcon from '@/components/Icons/TelegramIcon.vue'
|
||||||
|
|
||||||
|
let show = defineModel()
|
||||||
|
|
||||||
|
let links = [
|
||||||
|
{
|
||||||
|
label: __('Website'),
|
||||||
|
url: 'https://frappe.io/crm',
|
||||||
|
icon: LucideGlobe,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('GitHub Repository'),
|
||||||
|
url: 'https://github.com/frappe/crm',
|
||||||
|
icon: LucideGitHub,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Documentation'),
|
||||||
|
url: 'https://docs.frappe.io/crm',
|
||||||
|
icon: LucideBookOpen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Telegram Channel'),
|
||||||
|
url: 'https://t.me/frappecrm',
|
||||||
|
icon: TelegramIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Report an Issue'),
|
||||||
|
url: 'https://github.com/frappe/crm/issues',
|
||||||
|
icon: LucideBug,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Contact Support'),
|
||||||
|
url: 'https://support.frappe.io',
|
||||||
|
icon: LucideHeadset,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
let appVersion = window.app_version
|
||||||
|
</script>
|
||||||
@ -12,16 +12,19 @@
|
|||||||
v-model="showQuickEntryModal"
|
v-model="showQuickEntryModal"
|
||||||
:doctype="quickEntryDoctype"
|
:doctype="quickEntryDoctype"
|
||||||
/>
|
/>
|
||||||
|
<AboutModal v-model="showAboutModal" />
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import CreateDocumentModal from '@/components/Modals/CreateDocumentModal.vue'
|
import CreateDocumentModal from '@/components/Modals/CreateDocumentModal.vue'
|
||||||
import QuickEntryModal from '@/components/Modals/QuickEntryModal.vue'
|
import QuickEntryModal from '@/components/Modals/QuickEntryModal.vue'
|
||||||
|
import AboutModal from '@/components/Modals/AboutModal.vue'
|
||||||
import {
|
import {
|
||||||
showCreateDocumentModal,
|
showCreateDocumentModal,
|
||||||
createDocumentDoctype,
|
createDocumentDoctype,
|
||||||
createDocumentData,
|
createDocumentData,
|
||||||
createDocumentCallback,
|
createDocumentCallback,
|
||||||
} from '@/composables/document'
|
} from '@/composables/document'
|
||||||
|
import { showAboutModal } from '@/composables/settings'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const showQuickEntryModal = ref(false)
|
const showQuickEntryModal = ref(false)
|
||||||
|
|||||||
@ -55,7 +55,11 @@ import Apps from '@/components/Apps.vue'
|
|||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { getSettings } from '@/stores/settings'
|
import { getSettings } from '@/stores/settings'
|
||||||
import { showSettings, isMobileView } from '@/composables/settings'
|
import {
|
||||||
|
showSettings,
|
||||||
|
isMobileView,
|
||||||
|
showAboutModal,
|
||||||
|
} from '@/composables/settings'
|
||||||
import { confirmLoginToFrappeCloud } from '@/composables/frappecloud'
|
import { confirmLoginToFrappeCloud } from '@/composables/frappecloud'
|
||||||
import { Dropdown } from 'frappe-ui'
|
import { Dropdown } from 'frappe-ui'
|
||||||
import { theme, toggleTheme } from '@/stores/theme'
|
import { theme, toggleTheme } from '@/stores/theme'
|
||||||
@ -131,20 +135,6 @@ function getStandardItem(item) {
|
|||||||
return {
|
return {
|
||||||
component: markRaw(Apps),
|
component: markRaw(Apps),
|
||||||
}
|
}
|
||||||
case 'support_link':
|
|
||||||
return {
|
|
||||||
icon: item.icon,
|
|
||||||
label: __(item.label),
|
|
||||||
onClick: () =>
|
|
||||||
window.open(item.route, item.open_in_new_window ? '_blank' : ''),
|
|
||||||
}
|
|
||||||
case 'docs_link':
|
|
||||||
return {
|
|
||||||
icon: item.icon,
|
|
||||||
label: __(item.label),
|
|
||||||
onClick: () =>
|
|
||||||
window.open(item.route, item.open_in_new_window ? '_blank' : ''),
|
|
||||||
}
|
|
||||||
case 'toggle_theme':
|
case 'toggle_theme':
|
||||||
return {
|
return {
|
||||||
icon: theme.value === 'dark' ? 'sun' : item.icon,
|
icon: theme.value === 'dark' ? 'sun' : item.icon,
|
||||||
@ -165,6 +155,12 @@ function getStandardItem(item) {
|
|||||||
onClick: () => confirmLoginToFrappeCloud(),
|
onClick: () => confirmLoginToFrappeCloud(),
|
||||||
condition: () => !isMobileView.value && window.is_fc_site,
|
condition: () => !isMobileView.value && window.is_fc_site,
|
||||||
}
|
}
|
||||||
|
case 'about':
|
||||||
|
return {
|
||||||
|
icon: item.icon,
|
||||||
|
label: __(item.label),
|
||||||
|
onClick: () => (showAboutModal.value = true),
|
||||||
|
}
|
||||||
case 'logout':
|
case 'logout':
|
||||||
return {
|
return {
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
|
|||||||
@ -42,3 +42,5 @@ export const isMobileView = computed(() => window.innerWidth < 768)
|
|||||||
|
|
||||||
export const showSettings = ref(false)
|
export const showSettings = ref(false)
|
||||||
export const activeSettingsPage = ref('')
|
export const activeSettingsPage = ref('')
|
||||||
|
|
||||||
|
export const showAboutModal = ref(false)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user