1
0
forked from test/crm

Merge pull request #21 from shariquerik/twilio-fix

This commit is contained in:
Shariq Ansari 2023-11-03 16:09:40 +05:30 committed by GitHub
commit a8c32cff36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 3583 additions and 1842 deletions

View File

@ -7,6 +7,7 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"section_break_malx",
"account_sid",
"api_key",
"api_secret",
@ -14,8 +15,9 @@
"auth_token",
"twiml_sid",
"section_break_ssqj",
"record_calls",
"column_break_avmt"
"enabled",
"column_break_avmt",
"record_calls"
],
"fields": [
{
@ -23,7 +25,7 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Account SID",
"reqd": 1
"mandatory_depends_on": "eval: doc.enabled"
},
{
"fieldname": "api_key",
@ -46,7 +48,7 @@
"fieldtype": "Password",
"in_list_view": 1,
"label": "Auth Token",
"reqd": 1
"mandatory_depends_on": "eval: doc.enabled"
},
{
"fieldname": "twiml_sid",
@ -67,12 +69,22 @@
{
"fieldname": "column_break_avmt",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_malx",
"fieldtype": "Section Break"
},
{
"default": "0",
"fieldname": "enabled",
"fieldtype": "Check",
"label": "Enabled"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-09-20 16:42:17.025651",
"modified": "2023-11-03 15:13:09.155818",
"modified_by": "Administrator",
"module": "FCRM",
"name": "Twilio Settings",

View File

@ -5,6 +5,10 @@ import frappe
from frappe import _
from .twilio_handler import Twilio, IncomingCall, TwilioCallDetails
@frappe.whitelist()
def is_enabled():
return frappe.db.get_single_value("Twilio Settings", "enabled")
@frappe.whitelist()
def generate_access_token():
"""Returns access token that is required to authenticate Twilio Client SDK.

View File

@ -27,8 +27,8 @@ class Twilio:
"""Make a twilio connection.
"""
settings = frappe.get_doc("Twilio Settings")
# if not (settings and settings.enabled):
# return
if not (settings and settings.enabled):
return
return Twilio(settings=settings)
def get_phone_numbers(self):
@ -115,6 +115,8 @@ class Twilio:
@classmethod
def get_twilio_client(self):
twilio_settings = frappe.get_doc("Twilio Settings")
if not twilio_settings.enabled:
frappe.throw(_("Please enable twilio settings before making a call."))
auth_token = get_decrypted_password("Twilio Settings", "Twilio Settings", 'auth_token')
client = TwilioClient(twilio_settings.account_sid, auth_token)

@ -1 +1 @@
Subproject commit 38eb500cb47d6cfc7124817e9c2acdcd560045f1
Subproject commit e0859c6165a5d54a999f94e7c1cf9647ecd2bf72

View File

@ -9,21 +9,23 @@
"serve": "vite preview"
},
"dependencies": {
"@tiptap/vue-3": "^2.0.4",
"@twilio/voice-sdk": "^2.7.1",
"@vitejs/plugin-vue": "^4.2.3",
"@vueuse/core": "^10.3.0",
"@vueuse/integrations": "^10.3.0",
"autoprefixer": "^10.4.14",
"feather-icons": "^4.28.0",
"frappe-ui": "^0.1.12",
"frappe-ui": "^0.1.14",
"pinia": "^2.0.33",
"postcss": "^8.4.5",
"socket.io-client": "^4.7.2",
"sortablejs": "^1.15.0",
"tailwindcss": "^3.3.3",
"vite": "^4.4.9",
"vue": "^3.3.4",
"vue-router": "^4.2.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.5",
"vite": "^4.4.9"
}
}

View File

@ -2,17 +2,17 @@
<div
v-show="showCallPopup"
ref="callPopup"
class="fixed select-none z-10 bg-gray-900 text-gray-300 rounded-lg shadow-2xl p-4 flex flex-col w-60 cursor-move"
class="fixed z-10 flex w-60 cursor-move select-none flex-col rounded-lg bg-gray-900 p-4 text-gray-300 shadow-2xl"
:style="style"
>
<div class="flex items-center flex-row-reverse gap-1">
<MinimizeIcon class="w-4 h-4 cursor-pointer" @click="toggleCallWindow" />
<div class="flex flex-row-reverse items-center gap-1">
<MinimizeIcon class="h-4 w-4 cursor-pointer" @click="toggleCallWindow" />
</div>
<div class="flex flex-col justify-center items-center gap-3">
<div class="flex flex-col items-center justify-center gap-3">
<Avatar
:image="contact.image"
:label="contact.full_name"
class="flex items-center justify-center [&>div]:text-[30px] !h-24 !w-24 relative"
class="relative flex !h-24 !w-24 items-center justify-center [&>div]:text-[30px]"
:class="onCall || calling ? '' : 'pulse'"
/>
<div class="flex flex-col items-center justify-center gap-1">
@ -22,11 +22,11 @@
<div class="text-sm text-gray-600">{{ contact.mobile_no }}</div>
</div>
<CountUpTimer ref="counterUp">
<div v-if="onCall" class="text-base my-1">
<div v-if="onCall" class="my-1 text-base">
{{ counterUp?.updatedTime }}
</div>
</CountUpTimer>
<div v-if="!onCall" class="text-base my-1">
<div v-if="!onCall" class="my-1 text-base">
{{
callStatus == 'ringing'
? 'Ringing...'
@ -43,13 +43,13 @@
/>
<Button class="rounded-full">
<template #icon>
<DialpadIcon class="rounded-full cursor-pointer" />
<DialpadIcon class="cursor-pointer rounded-full" />
</template>
</Button>
<Button class="rounded-full">
<template #icon>
<NoteIcon
class="text-gray-900 rounded-full cursor-pointer h-4 w-4"
class="h-4 w-4 cursor-pointer rounded-full text-gray-900"
@click="showNoteModal = true"
/>
</template>
@ -57,7 +57,7 @@
<Button class="rounded-full bg-red-600 hover:bg-red-700">
<template #icon>
<PhoneIcon
class="text-white fill-white h-4 w-4 rotate-[135deg]"
class="h-4 w-4 rotate-[135deg] fill-white text-white"
@click="hangUpCall"
/>
</template>
@ -73,7 +73,7 @@
class="rounded-lg"
>
<template #prefix>
<PhoneIcon class="fill-white h-4 w-4 rotate-[135deg]" />
<PhoneIcon class="h-4 w-4 rotate-[135deg] fill-white" />
</template>
</Button>
</div>
@ -87,7 +87,7 @@
@click="acceptIncomingCall"
>
<template #prefix>
<PhoneIcon class="fill-white h-4 w-4" />
<PhoneIcon class="h-4 w-4 fill-white" />
</template>
</Button>
<Button
@ -99,7 +99,7 @@
@click="rejectIncomingCall"
>
<template #prefix>
<PhoneIcon class="fill-white h-4 w-4 rotate-[135deg]" />
<PhoneIcon class="h-4 w-4 rotate-[135deg] fill-white" />
</template>
</Button>
</div>
@ -107,16 +107,16 @@
</div>
<div
v-show="showSmallCallWindow"
class="flex items-center justify-between gap-3 bg-gray-900 text-base text-gray-300 ml-2 px-2 py-[7px] rounded-lg cursor-pointer select-none"
class="ml-2 flex cursor-pointer select-none items-center justify-between gap-3 rounded-lg bg-gray-900 px-2 py-[7px] text-base text-gray-300"
@click="toggleCallWindow"
>
<div class="flex items-center gap-2">
<Avatar
:image="contact.image"
:label="contact.full_name"
class="flex items-center justify-center !h-5 !w-5 relative"
class="relative flex !h-5 !w-5 items-center justify-center"
/>
<div class="truncate max-w-[120px]">
<div class="max-w-[120px] truncate">
{{ contact.full_name }}
</div>
</div>
@ -124,10 +124,10 @@
<div class="my-1 min-w-[40px] text-center">
{{ counterUp?.updatedTime }}
</div>
<Button variant="solid" theme="red" class="rounded-full !h-6 !w-6">
<Button variant="solid" theme="red" class="!h-6 !w-6 rounded-full">
<template #icon>
<PhoneIcon
class="fill-white h-4 w-4 rotate-[135deg]"
class="h-4 w-4 rotate-[135deg] fill-white"
@click.stop="hangUpCall"
/>
</template>
@ -140,11 +140,11 @@
<Button
variant="solid"
theme="red"
class="rounded-full !h-6 !w-6"
class="!h-6 !w-6 rounded-full"
@click.stop="cancelCall"
>
<template #icon>
<PhoneIcon class="fill-white h-4 w-4 rotate-[135deg]" />
<PhoneIcon class="h-4 w-4 rotate-[135deg] fill-white" />
</template>
</Button>
</div>
@ -152,21 +152,21 @@
<Button
variant="solid"
theme="green"
class="rounded-full !h-6 !w-6 pulse relative"
class="pulse relative !h-6 !w-6 rounded-full"
@click.stop="acceptIncomingCall"
>
<template #icon>
<PhoneIcon class="fill-white h-4 w-4 animate-pulse" />
<PhoneIcon class="h-4 w-4 animate-pulse fill-white" />
</template>
</Button>
<Button
variant="solid"
theme="red"
class="rounded-full !h-6 !w-6"
class="!h-6 !w-6 rounded-full"
@click.stop="rejectIncomingCall"
>
<template #icon>
<PhoneIcon class="fill-white h-4 w-4 rotate-[135deg]" />
<PhoneIcon class="h-4 w-4 rotate-[135deg] fill-white" />
</template>
</Button>
</div>
@ -197,6 +197,7 @@ const contact = ref({
mobile_no: '',
})
let enabled = ref(false)
let showCallPopup = ref(false)
let showSmallCallWindow = ref(false)
let onCall = ref(false)
@ -244,6 +245,10 @@ let { style } = useDraggable(callPopup, {
preventDefault: true,
})
async function is_twilio_enabled() {
return await call('crm.twilio.api.is_enabled')
}
async function startupClient() {
log.value = 'Requesting Access Token...'
@ -469,7 +474,10 @@ function toggleCallWindow() {
}
}
onMounted(() => startupClient())
onMounted(async () => {
enabled.value = await is_twilio_enabled()
enabled.value && startupClient()
})
watch(
() => log.value,
@ -481,6 +489,7 @@ watch(
const app = getCurrentInstance()
app.appContext.config.globalProperties.makeCall = makeOutgoingCall
app.appContext.config.globalProperties.is_twilio_enabled = enabled.value
</script>
<style scoped>

View File

@ -0,0 +1,6 @@
import { getCurrentInstance } from 'vue'
export function is_twilio_enabled() {
const app = getCurrentInstance()
return app.appContext.config.globalProperties.is_twilio_enabled
}

View File

@ -5,23 +5,34 @@ import frappeui from 'frappe-ui/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [frappeui(), vue({
script: {
defineModel: true,
propsDestructure: true
}
})],
plugins: [
frappeui(),
vue({
script: {
defineModel: true,
propsDestructure: true,
},
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
build: {
outDir: `../${path.basename(path.resolve('..'))}/public/frontend`,
outDir: '../crm/public/frontend',
emptyOutDir: true,
commonjsOptions: {
include: [/tailwind.config.js/, /node_modules/],
},
sourcemap: true,
},
optimizeDeps: {
include: ['frappe-ui > feather-icons', 'showdown', 'engine.io-client'],
include: [
'feather-icons',
'showdown',
'tailwind.config.js',
'engine.io-client',
],
},
})

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"private": true,
"aworkspaces": ["frappe-ui", "frontend"],
"workspaces": ["frontend", "frappe-ui"],
"scripts": {
"postinstall": "cd frontend && yarn install",
"dev": "cd frontend && yarn dev",

3490
yarn.lock

File diff suppressed because it is too large Load Diff