Merge pull request #136 from frappe/develop
chore: Merge develop to main
This commit is contained in:
commit
c4d4419a4b
12
crm/api/__init__.py
Normal file
12
crm/api/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
import frappe
|
||||
from frappe.translate import get_all_translations
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_translations():
|
||||
if frappe.session.user != "Guest":
|
||||
language = frappe.db.get_value("User", frappe.session.user, "language")
|
||||
else:
|
||||
language = frappe.db.get_single_value("System Settings", "language")
|
||||
|
||||
return get_all_translations(language)
|
||||
@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import get_controller
|
||||
from frappe.model import no_value_fields
|
||||
from pypika import Criterion
|
||||
@ -13,7 +14,7 @@ def sort_options(doctype: str):
|
||||
fields = [field for field in fields if field.fieldtype not in no_value_fields]
|
||||
fields = [
|
||||
{
|
||||
"label": field.label,
|
||||
"label": _(field.label),
|
||||
"value": field.fieldname,
|
||||
}
|
||||
for field in fields
|
||||
@ -29,6 +30,7 @@ def sort_options(doctype: str):
|
||||
]
|
||||
|
||||
for field in standard_fields:
|
||||
field["label"] = _(field["label"])
|
||||
fields.append(field)
|
||||
|
||||
return fields
|
||||
@ -101,6 +103,9 @@ def get_filterable_fields(doctype: str):
|
||||
field["name"] = field.get("fieldname")
|
||||
res.append(field)
|
||||
|
||||
for field in res:
|
||||
field["label"] = _(field.get("label"))
|
||||
|
||||
return res
|
||||
|
||||
def get_fields_meta(DocField, doctype, allowed_fieldtypes, restricted_fields):
|
||||
@ -184,6 +189,7 @@ def get_list_data(
|
||||
for column in columns:
|
||||
if column.get("key") not in rows:
|
||||
rows.append(column.get("key"))
|
||||
column["label"] = _(column.get("label"))
|
||||
|
||||
data = frappe.get_list(
|
||||
doctype,
|
||||
@ -197,7 +203,7 @@ def get_list_data(
|
||||
fields = [field for field in fields if field.fieldtype not in no_value_fields]
|
||||
fields = [
|
||||
{
|
||||
"label": field.label,
|
||||
"label": _(field.label),
|
||||
"type": field.fieldtype,
|
||||
"value": field.fieldname,
|
||||
"options": field.options,
|
||||
@ -224,6 +230,7 @@ def get_list_data(
|
||||
if field.get('value') not in rows:
|
||||
rows.append(field.get('value'))
|
||||
if field not in fields:
|
||||
field["label"] = _(field["label"])
|
||||
fields.append(field)
|
||||
|
||||
if not is_default and custom_view_name:
|
||||
|
||||
@ -4,9 +4,6 @@ from frappe.query_builder import Order
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_notifications():
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||
|
||||
Notification = frappe.qb.DocType("CRM Notification")
|
||||
query = (
|
||||
frappe.qb.from_(Notification)
|
||||
@ -46,9 +43,6 @@ def get_notifications():
|
||||
|
||||
@frappe.whitelist()
|
||||
def mark_as_read(user=None, comment=None):
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||
|
||||
user = user or frappe.session.user
|
||||
filters = {"to_user": user, "read": False}
|
||||
if comment:
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import frappe
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
@frappe.whitelist()
|
||||
def get_users():
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||
|
||||
users = frappe.qb.get_query(
|
||||
"User",
|
||||
fields=["name", "email", "enabled", "user_image", "full_name", "user_type"],
|
||||
@ -24,9 +21,6 @@ def get_users():
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_contacts():
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||
|
||||
contacts = frappe.get_all(
|
||||
"Contact",
|
||||
fields=[
|
||||
@ -66,9 +60,6 @@ def get_contacts():
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_lead_contacts():
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||
|
||||
lead_contacts = frappe.get_all(
|
||||
"CRM Lead",
|
||||
fields=[
|
||||
@ -88,9 +79,6 @@ def get_lead_contacts():
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_organizations():
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||
|
||||
organizations = frappe.qb.get_query(
|
||||
"CRM Organization",
|
||||
fields=['*'],
|
||||
|
||||
@ -4,9 +4,6 @@ from pypika import Criterion
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_views(doctype):
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw("Authentication failed", exc=frappe.AuthenticationError)
|
||||
|
||||
View = frappe.qb.DocType("CRM View Settings")
|
||||
query = (
|
||||
frappe.qb.from_(View)
|
||||
|
||||
@ -93,7 +93,7 @@ def get_call_log(name):
|
||||
if c:
|
||||
return [c.full_name, c.image]
|
||||
return [None, None]
|
||||
|
||||
|
||||
def get_lead_contact(number):
|
||||
l = frappe.db.get_value("CRM Lead", {"mobile_no": number, "converted": 0}, ["lead_name", "image"], as_dict=True)
|
||||
if l:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between px-10 py-5 text-lg font-medium">
|
||||
<div class="flex h-7 items-center text-xl font-semibold text-gray-800">
|
||||
{{ title }}
|
||||
{{ __(title) }}
|
||||
</div>
|
||||
<Button
|
||||
v-if="title == 'Emails'"
|
||||
@ -11,7 +11,7 @@
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4 w-4" />
|
||||
</template>
|
||||
<span>New Email</span>
|
||||
<span>{{ __('New Email') }}</span>
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="title == 'Calls'"
|
||||
@ -21,41 +21,41 @@
|
||||
<template #prefix>
|
||||
<PhoneIcon class="h-4 w-4" />
|
||||
</template>
|
||||
<span>Make a Call</span>
|
||||
<span>{{ __('Make a Call') }}</span>
|
||||
</Button>
|
||||
<Button v-else-if="title == 'Notes'" variant="solid" @click="showNote()">
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4 w-4" />
|
||||
</template>
|
||||
<span>New Note</span>
|
||||
<span>{{ __('New Note') }}</span>
|
||||
</Button>
|
||||
<Button v-else-if="title == 'Tasks'" variant="solid" @click="showTask()">
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4 w-4" />
|
||||
</template>
|
||||
<span>New Task</span>
|
||||
<span>{{ __('New Task') }}</span>
|
||||
</Button>
|
||||
<Dropdown
|
||||
v-else
|
||||
:options="[
|
||||
{
|
||||
icon: h(EmailIcon, { class: 'h-4 w-4' }),
|
||||
label: 'New Email',
|
||||
label: __('New Email'),
|
||||
onClick: () => ($refs.emailBox.show = true),
|
||||
},
|
||||
{
|
||||
icon: h(PhoneIcon, { class: 'h-4 w-4' }),
|
||||
label: 'Make a Call',
|
||||
label: __('Make a Call'),
|
||||
onClick: () => makeCall(doc.data.mobile_no),
|
||||
},
|
||||
{
|
||||
icon: h(NoteIcon, { class: 'h-4 w-4' }),
|
||||
label: 'New Note',
|
||||
label: __('New Note'),
|
||||
onClick: () => showNote(),
|
||||
},
|
||||
{
|
||||
icon: h(TaskIcon, { class: 'h-4 w-4' }),
|
||||
label: 'New Task',
|
||||
label: __('New Task'),
|
||||
onClick: () => showTask(),
|
||||
},
|
||||
]"
|
||||
@ -66,7 +66,7 @@
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4 w-4" />
|
||||
</template>
|
||||
<span>New</span>
|
||||
<span>{{ __('New') }}</span>
|
||||
<template #suffix>
|
||||
<FeatherIcon
|
||||
:name="open ? 'chevron-up' : 'chevron-down'"
|
||||
@ -82,7 +82,7 @@
|
||||
class="flex flex-1 flex-col items-center justify-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<LoadingIndicator class="h-6 w-6" />
|
||||
<span>Loading...</span>
|
||||
<span>{{ __('Loading...') }}</span>
|
||||
</div>
|
||||
<div v-else-if="activities?.length" class="activities flex-1 overflow-y-auto">
|
||||
<div
|
||||
@ -101,8 +101,8 @@
|
||||
<Dropdown
|
||||
:options="[
|
||||
{
|
||||
label: __('Delete'),
|
||||
icon: 'trash-2',
|
||||
label: 'Delete',
|
||||
onClick: () => deleteNote(note.name),
|
||||
},
|
||||
]"
|
||||
@ -135,7 +135,7 @@
|
||||
</div>
|
||||
<Tooltip :text="dateFormat(note.modified, dateTooltipFormat)">
|
||||
<div class="truncate text-sm text-gray-700">
|
||||
{{ timeAgo(note.modified) }}
|
||||
{{ __(timeAgo(note.modified)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -188,7 +188,7 @@
|
||||
:options="taskStatusOptions(updateTaskStatus, task)"
|
||||
@click.stop
|
||||
>
|
||||
<Tooltip text="Change Status">
|
||||
<Tooltip :text="__('Change Status')">
|
||||
<Button variant="ghosted" class="hover:bg-gray-300">
|
||||
<TaskStatusIcon :status="task.status" />
|
||||
</Button>
|
||||
@ -197,15 +197,15 @@
|
||||
<Dropdown
|
||||
:options="[
|
||||
{
|
||||
label: __('Delete'),
|
||||
icon: 'trash-2',
|
||||
label: 'Delete',
|
||||
onClick: () => {
|
||||
$dialog({
|
||||
title: 'Delete Task',
|
||||
message: 'Are you sure you want to delete this task?',
|
||||
title: __('Delete Task'),
|
||||
message: __('Are you sure you want to delete this task?'),
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
theme: 'red',
|
||||
variant: 'solid',
|
||||
onClick(close) {
|
||||
@ -257,12 +257,16 @@
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
{{ call.type == 'Incoming' ? 'Inbound' : 'Outbound' }} Call
|
||||
{{
|
||||
call.type == 'Incoming'
|
||||
? __('Inbound Call')
|
||||
: __('Outbound Call')
|
||||
}}
|
||||
</div>
|
||||
<div>
|
||||
<Tooltip :text="dateFormat(call.creation, dateTooltipFormat)">
|
||||
<div class="text-sm text-gray-600">
|
||||
{{ timeAgo(call.creation) }}
|
||||
{{ __(timeAgo(call.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -270,25 +274,28 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-1">
|
||||
<DurationIcon class="h-4 w-4 text-gray-600" />
|
||||
<div class="text-sm text-gray-600">Duration</div>
|
||||
<div class="text-sm text-gray-600">{{ __('Duration') }}</div>
|
||||
<div class="text-sm">
|
||||
{{ call.duration }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="call.recording_url"
|
||||
class="flex cursor-pointer select-none items-center gap-1"
|
||||
@click="call.show_recording = !call.show_recording"
|
||||
>
|
||||
<PlayIcon class="h-4 w-4 text-gray-600" />
|
||||
<div class="text-sm text-gray-600">
|
||||
{{
|
||||
call.show_recording ? 'Hide Recording' : 'Listen to Call'
|
||||
call.show_recording
|
||||
? __('Hide Recording')
|
||||
: __('Listen to Call')
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="call.show_recording"
|
||||
v-if="call.show_recording && call.recording_url"
|
||||
class="flex items-center justify-between rounded border"
|
||||
>
|
||||
<audio class="audio-control" controls :src="call.recording_url" />
|
||||
@ -302,7 +309,7 @@
|
||||
/>
|
||||
<div class="ml-1 flex flex-col gap-1">
|
||||
<div class="text-base font-medium">
|
||||
{{ call.caller.label }}
|
||||
{{ __(call.caller.label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ call.from }}
|
||||
@ -319,7 +326,7 @@
|
||||
/>
|
||||
<div class="ml-1 flex flex-col gap-1">
|
||||
<div class="text-base font-medium">
|
||||
{{ call.receiver.label }}
|
||||
{{ __(call.receiver.label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ call.to }}
|
||||
@ -378,12 +385,12 @@
|
||||
:text="dateFormat(activity.creation, dateTooltipFormat)"
|
||||
>
|
||||
<div class="text-sm text-gray-600">
|
||||
{{ timeAgo(activity.creation) }}
|
||||
{{ __(timeAgo(activity.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="flex gap-0.5">
|
||||
<Tooltip text="Reply">
|
||||
<Tooltip :text="__('Reply')">
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="text-gray-700"
|
||||
@ -392,7 +399,7 @@
|
||||
<ReplyIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip text="Reply All">
|
||||
<Tooltip :text="__('Reply All')">
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="text-gray-700"
|
||||
@ -407,14 +414,16 @@
|
||||
{{ activity.data.subject }}
|
||||
</div>
|
||||
<div class="mb-3 text-sm leading-5 text-gray-600">
|
||||
<span class="mr-1 text-2xs font-bold text-gray-500">TO:</span>
|
||||
<span class="mr-1 text-2xs font-bold text-gray-500">
|
||||
{{ __('TO') }}:
|
||||
</span>
|
||||
<span>{{ activity.data.recipients }}</span>
|
||||
<span v-if="activity.data.cc">, </span>
|
||||
<span
|
||||
v-if="activity.data.cc"
|
||||
class="mr-1 text-2xs font-bold text-gray-500"
|
||||
>
|
||||
CC:
|
||||
{{ __('CC') }}:
|
||||
</span>
|
||||
<span v-if="activity.data.cc">{{ activity.data.cc }}</span>
|
||||
<span v-if="activity.data.bcc">, </span>
|
||||
@ -422,7 +431,7 @@
|
||||
v-if="activity.data.bcc"
|
||||
class="mr-1 text-2xs font-bold text-gray-500"
|
||||
>
|
||||
BCC:
|
||||
{{ __('BCC') }}:
|
||||
</span>
|
||||
<span v-if="activity.data.bcc">{{ activity.data.bcc }}</span>
|
||||
</div>
|
||||
@ -452,15 +461,15 @@
|
||||
<span class="font-medium text-gray-800">
|
||||
{{ activity.owner_name }}
|
||||
</span>
|
||||
<span>added a</span>
|
||||
<span>{{ __('added a') }}</span>
|
||||
<span class="max-w-xs truncate font-medium text-gray-800">
|
||||
comment
|
||||
{{ __('comment') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="ml-auto whitespace-nowrap">
|
||||
<Tooltip :text="dateFormat(activity.creation, dateTooltipFormat)">
|
||||
<div class="text-gray-600">
|
||||
{{ timeAgo(activity.creation) }}
|
||||
{{ __(timeAgo(activity.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -479,12 +488,16 @@
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
{{ activity.type == 'Incoming' ? 'Inbound' : 'Outbound' }} Call
|
||||
{{
|
||||
activity.type == 'Incoming'
|
||||
? __('Inbound Call')
|
||||
: __('Outbound Call')
|
||||
}}
|
||||
</div>
|
||||
<div>
|
||||
<Tooltip :text="dateFormat(activity.creation, dateTooltipFormat)">
|
||||
<div class="text-sm text-gray-600">
|
||||
{{ timeAgo(activity.creation) }}
|
||||
{{ __(timeAgo(activity.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -492,25 +505,28 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-1">
|
||||
<DurationIcon class="h-4 w-4 text-gray-600" />
|
||||
<div class="text-sm text-gray-600">Duration</div>
|
||||
<div class="text-sm text-gray-600">{{ __('Duration') }}</div>
|
||||
<div class="text-sm">
|
||||
{{ activity.duration }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="activity.recording_url"
|
||||
class="flex cursor-pointer select-none items-center gap-1"
|
||||
@click="activity.show_recording = !activity.show_recording"
|
||||
>
|
||||
<PlayIcon class="h-4 w-4 text-gray-600" />
|
||||
<div class="text-sm text-gray-600">
|
||||
{{
|
||||
activity.show_recording ? 'Hide Recording' : 'Listen to Call'
|
||||
activity.show_recording
|
||||
? __('Hide Recording')
|
||||
: __('Listen to Call')
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="activity.show_recording"
|
||||
v-if="activity.show_recording && activity.recording_url"
|
||||
class="flex items-center justify-between rounded border"
|
||||
>
|
||||
<audio
|
||||
@ -528,7 +544,7 @@
|
||||
/>
|
||||
<div class="ml-1 flex flex-col gap-1">
|
||||
<div class="text-base font-medium">
|
||||
{{ activity.caller.label }}
|
||||
{{ __(activity.caller.label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ activity.from }}
|
||||
@ -545,7 +561,7 @@
|
||||
/>
|
||||
<div class="ml-1 flex flex-col gap-1">
|
||||
<div class="text-base font-medium">
|
||||
{{ activity.receiver.label }}
|
||||
{{ __(activity.receiver.label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ activity.to }}
|
||||
@ -557,17 +573,17 @@
|
||||
<div v-else class="mb-4 flex flex-col gap-5 py-1.5">
|
||||
<div class="flex items-start justify-stretch gap-2 text-base">
|
||||
<div class="inline-flex flex-wrap gap-1 text-gray-600">
|
||||
<span class="font-medium text-gray-800">{{
|
||||
activity.owner_name
|
||||
}}</span>
|
||||
<span v-if="activity.type">{{ activity.type }}</span>
|
||||
<span class="font-medium text-gray-800">
|
||||
{{ activity.owner_name }}
|
||||
</span>
|
||||
<span v-if="activity.type">{{ __(activity.type) }}</span>
|
||||
<span
|
||||
v-if="activity.data.field_label"
|
||||
class="max-w-xs truncate font-medium text-gray-800"
|
||||
>
|
||||
{{ activity.data.field_label }}
|
||||
{{ __(activity.data.field_label) }}
|
||||
</span>
|
||||
<span v-if="activity.value">{{ activity.value }}</span>
|
||||
<span v-if="activity.value">{{ __(activity.value) }}</span>
|
||||
<span
|
||||
v-if="activity.data.old_value"
|
||||
class="max-w-xs font-medium text-gray-800"
|
||||
@ -583,7 +599,7 @@
|
||||
{{ activity.data.old_value }}
|
||||
</div>
|
||||
</span>
|
||||
<span v-if="activity.to">to</span>
|
||||
<span v-if="activity.to">{{ __('to') }}</span>
|
||||
<span
|
||||
v-if="activity.data.value"
|
||||
class="max-w-xs font-medium text-gray-800"
|
||||
@ -604,7 +620,7 @@
|
||||
<div class="ml-auto whitespace-nowrap">
|
||||
<Tooltip :text="dateFormat(activity.creation, dateTooltipFormat)">
|
||||
<div class="text-gray-600">
|
||||
{{ timeAgo(activity.creation) }}
|
||||
{{ __(timeAgo(activity.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -620,7 +636,7 @@
|
||||
v-if="activity.data.field_label"
|
||||
class="max-w-xs truncate text-gray-600"
|
||||
>
|
||||
{{ activity.data.field_label }}
|
||||
{{ __(activity.data.field_label) }}
|
||||
</span>
|
||||
<FeatherIcon
|
||||
name="arrow-right"
|
||||
@ -628,7 +644,9 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-1">
|
||||
<span v-if="activity.type">{{ startCase(activity.type) }}</span>
|
||||
<span v-if="activity.type">{{
|
||||
startCase(__(activity.type))
|
||||
}}</span>
|
||||
<span
|
||||
v-if="activity.data.old_value"
|
||||
class="max-w-xs font-medium text-gray-800"
|
||||
@ -644,7 +662,7 @@
|
||||
{{ activity.data.old_value }}
|
||||
</div>
|
||||
</span>
|
||||
<span v-if="activity.to">to</span>
|
||||
<span v-if="activity.to">{{ __('to') }}</span>
|
||||
<span
|
||||
v-if="activity.data.value"
|
||||
class="max-w-xs font-medium text-gray-800"
|
||||
@ -666,7 +684,7 @@
|
||||
<div class="ml-auto whitespace-nowrap">
|
||||
<Tooltip :text="dateFormat(activity.creation, dateTooltipFormat)">
|
||||
<div class="text-gray-600">
|
||||
{{ timeAgo(activity.creation) }}
|
||||
{{ __(timeAgo(activity.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -674,7 +692,9 @@
|
||||
<div v-if="activity.other_versions">
|
||||
<Button
|
||||
:label="
|
||||
activity.show_others ? 'Hide all Changes' : 'Show all Changes'
|
||||
activity.show_others
|
||||
? __('Hide all Changes')
|
||||
: __('Show all Changes')
|
||||
"
|
||||
variant="outline"
|
||||
@click="activity.show_others = !activity.show_others"
|
||||
@ -696,25 +716,25 @@
|
||||
class="flex flex-1 flex-col items-center justify-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<component :is="emptyTextIcon" class="h-10 w-10" />
|
||||
<span>{{ emptyText }}</span>
|
||||
<span>{{ __(emptyText) }}</span>
|
||||
<Button
|
||||
v-if="title == 'Calls'"
|
||||
label="Make a Call"
|
||||
:label="__('Make a Call')"
|
||||
@click="makeCall(doc.data.mobile_no)"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="title == 'Notes'"
|
||||
label="Create Note"
|
||||
:label="__('Create Note')"
|
||||
@click="showNote()"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="title == 'Emails'"
|
||||
label="New Email"
|
||||
:label="__('New Email')"
|
||||
@click="$refs.emailBox.show = true"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="title == 'Tasks'"
|
||||
label="Create Task"
|
||||
:label="__('Create Task')"
|
||||
@click="showTask()"
|
||||
/>
|
||||
</div>
|
||||
@ -785,11 +805,10 @@ import {
|
||||
TextEditor,
|
||||
Avatar,
|
||||
createResource,
|
||||
createListResource,
|
||||
call,
|
||||
} from 'frappe-ui'
|
||||
import { useElementVisibility } from '@vueuse/core'
|
||||
import { ref, computed, h, markRaw, watch, nextTick, onMounted } from 'vue'
|
||||
import { ref, computed, h, markRaw, watch, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const { makeCall } = globalStore()
|
||||
|
||||
@ -32,12 +32,12 @@
|
||||
<div v-if="!onCall" class="my-1 text-base">
|
||||
{{
|
||||
callStatus == 'initiating'
|
||||
? 'Initiating call...'
|
||||
? __('Initiating call...')
|
||||
: callStatus == 'ringing'
|
||||
? 'Ringing...'
|
||||
? __('Ringing...')
|
||||
: calling
|
||||
? 'Calling...'
|
||||
: 'Incoming call...'
|
||||
? __('Calling...')
|
||||
: __('Incoming call...')
|
||||
}}
|
||||
</div>
|
||||
<div v-if="onCall" class="flex gap-2">
|
||||
@ -73,7 +73,7 @@
|
||||
size="md"
|
||||
variant="solid"
|
||||
theme="red"
|
||||
label="Cancel"
|
||||
:label="__('Cancel')"
|
||||
@click="cancelCall"
|
||||
class="rounded-lg"
|
||||
:disabled="callStatus == 'initiating'"
|
||||
@ -88,7 +88,7 @@
|
||||
size="md"
|
||||
variant="solid"
|
||||
theme="green"
|
||||
label="Accept"
|
||||
:label="__('Accept')"
|
||||
class="rounded-lg"
|
||||
@click="acceptIncomingCall"
|
||||
>
|
||||
@ -100,7 +100,7 @@
|
||||
size="md"
|
||||
variant="solid"
|
||||
theme="red"
|
||||
label="Reject"
|
||||
:label="__('Reject')"
|
||||
class="rounded-lg"
|
||||
@click="rejectIncomingCall"
|
||||
>
|
||||
@ -142,7 +142,7 @@
|
||||
</div>
|
||||
<div v-else-if="calling" class="flex items-center gap-3">
|
||||
<div class="my-1">
|
||||
{{ callStatus == 'ringing' ? 'Ringing...' : 'Calling...' }}
|
||||
{{ callStatus == 'ringing' ? __('Ringing...') : __('Calling...') }}
|
||||
</div>
|
||||
<Button
|
||||
variant="solid"
|
||||
@ -312,7 +312,7 @@ function handleIncomingCall(call) {
|
||||
|
||||
if (!contact.value) {
|
||||
contact.value = {
|
||||
full_name: 'Unknown',
|
||||
full_name: __('Unknown'),
|
||||
mobile_no: call.parameters.From,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<NestedPopover>
|
||||
<template #target>
|
||||
<Button label="Columns">
|
||||
<Button :label="__('Columns')">
|
||||
<template #prefix>
|
||||
<ColumnsIcon class="h-4" />
|
||||
</template>
|
||||
@ -24,7 +24,7 @@
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<DragIcon class="h-3.5" />
|
||||
<div>{{ element.label }}</div>
|
||||
<div>{{ __(element.label) }}</div>
|
||||
</div>
|
||||
<div class="flex cursor-pointer items-center gap-1">
|
||||
<Button
|
||||
@ -56,7 +56,7 @@
|
||||
class="w-full !justify-start !text-gray-600"
|
||||
variant="ghost"
|
||||
@click="togglePopover()"
|
||||
label="Add Column"
|
||||
:label="__('Add Column')"
|
||||
>
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4" />
|
||||
@ -69,7 +69,7 @@
|
||||
class="w-full !justify-start !text-gray-600"
|
||||
variant="ghost"
|
||||
@click="reset(close)"
|
||||
label="Reset Changes"
|
||||
:label="__('Reset Changes')"
|
||||
>
|
||||
<template #prefix>
|
||||
<ReloadIcon class="h-4" />
|
||||
@ -80,7 +80,7 @@
|
||||
class="w-full !justify-start !text-gray-600"
|
||||
variant="ghost"
|
||||
@click="resetToDefault(close)"
|
||||
label="Reset to Default"
|
||||
:label="__('Reset to Default')"
|
||||
>
|
||||
<template #prefix>
|
||||
<ReloadIcon class="h-4" />
|
||||
@ -96,32 +96,32 @@
|
||||
<FormControl
|
||||
type="text"
|
||||
size="md"
|
||||
label="Label"
|
||||
:label="__('Label')"
|
||||
v-model="column.label"
|
||||
class="w-full"
|
||||
placeholder="Column Label"
|
||||
:placeholder="__('First Name')"
|
||||
/>
|
||||
<FormControl
|
||||
type="text"
|
||||
size="md"
|
||||
label="Width"
|
||||
:label="__('Width')"
|
||||
class="w-full"
|
||||
v-model="column.width"
|
||||
placeholder="Column Width"
|
||||
description="Width can be in number, pixel or rem (eg. 3, 30px, 10rem)"
|
||||
placeholder="10rem"
|
||||
:description="__('Width can be in number, pixel or rem (eg. 3, 30px, 10rem)')"
|
||||
:debounce="500"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex w-full gap-2 border-t pt-2">
|
||||
<Button
|
||||
variant="subtle"
|
||||
label="Cancel"
|
||||
:label="__('Cancel')"
|
||||
class="w-full flex-1"
|
||||
@click="cancelUpdate"
|
||||
/>
|
||||
<Button
|
||||
variant="solid"
|
||||
label="Update"
|
||||
:label="__('Update')"
|
||||
class="w-full flex-1"
|
||||
@click="updateColumn(column)"
|
||||
/>
|
||||
|
||||
@ -64,11 +64,11 @@
|
||||
</FileUploader>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-end space-x-2 sm:mt-0">
|
||||
<Button v-bind="discardButtonProps || {}" label="Discard" />
|
||||
<Button v-bind="discardButtonProps || {}" :label="__('Discard')" />
|
||||
<Button
|
||||
variant="solid"
|
||||
v-bind="submitButtonProps || {}"
|
||||
label="Submit"
|
||||
:label="__('Comment')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
ref="sendEmailRef"
|
||||
variant="ghost"
|
||||
:class="[showEmailBox ? '!bg-gray-300 hover:!bg-gray-200' : '']"
|
||||
label="Reply"
|
||||
:label="__('Reply')"
|
||||
@click="toggleEmailBox()"
|
||||
>
|
||||
<template #prefix>
|
||||
@ -14,7 +14,7 @@
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
label="Comment"
|
||||
:label="__('Comment')"
|
||||
:class="[showCommentBox ? '!bg-gray-300 hover:!bg-gray-200' : '']"
|
||||
@click="toggleCommentBox()"
|
||||
>
|
||||
@ -25,12 +25,12 @@
|
||||
</div>
|
||||
<div v-if="showEmailBox" class="flex gap-1.5">
|
||||
<Button
|
||||
label="CC"
|
||||
:label="__('CC')"
|
||||
@click="toggleCC()"
|
||||
:class="[newEmailEditor.cc ? 'bg-gray-300 hover:bg-gray-200' : '']"
|
||||
/>
|
||||
<Button
|
||||
label="BCC"
|
||||
:label="__('BCC')"
|
||||
@click="toggleBCC()"
|
||||
:class="[newEmailEditor.bcc ? 'bg-gray-300 hover:bg-gray-200' : '']"
|
||||
/>
|
||||
@ -66,7 +66,9 @@
|
||||
v-model:attachments="attachments"
|
||||
:doctype="doctype"
|
||||
:subject="subject"
|
||||
placeholder="Add a reply..."
|
||||
:placeholder="
|
||||
__('Hi John, \n\nCan you please provide more details on this...')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div v-show="showCommentBox">
|
||||
@ -88,7 +90,7 @@
|
||||
v-model="doc.data"
|
||||
v-model:attachments="attachments"
|
||||
:doctype="doctype"
|
||||
placeholder="Add a comment..."
|
||||
:placeholder="__('@John, can you please check this?')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -40,10 +40,12 @@
|
||||
class="text-sm"
|
||||
type="text"
|
||||
:value="value"
|
||||
@change="selectDate(getDate($event.target.value)) || togglePopover()"
|
||||
@change="
|
||||
selectDate(getDate($event.target.value)) || togglePopover()
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
label="Today"
|
||||
:label="__('Today')"
|
||||
class="text-sm"
|
||||
@click="selectDate(getDate()) || togglePopover()"
|
||||
/>
|
||||
@ -57,7 +59,7 @@
|
||||
v-for="(d, i) in ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa']"
|
||||
:key="i"
|
||||
>
|
||||
{{ d }}
|
||||
{{ __(d) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -83,13 +85,13 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ date.getDate() }}
|
||||
{{ __(date.getDate()) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end p-1">
|
||||
<Button
|
||||
label="Clear"
|
||||
:label="__('Clear')"
|
||||
class="text-sm"
|
||||
@click="
|
||||
() => {
|
||||
@ -166,10 +168,12 @@ export default {
|
||||
},
|
||||
formatMonth() {
|
||||
let date = this.getDate(this.currentYear, this.currentMonth - 1, 1)
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})
|
||||
let month = __(
|
||||
date.toLocaleString('en-US', {
|
||||
month: 'long',
|
||||
})
|
||||
)
|
||||
return `${month}, ${date.getFullYear()}`
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
v-for="(d, i) in ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa']"
|
||||
:key="i"
|
||||
>
|
||||
{{ d }}
|
||||
{{ __(d) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -79,13 +79,13 @@
|
||||
</div>
|
||||
<div class="flex justify-end space-x-1 p-1">
|
||||
<Button
|
||||
label="Clear"
|
||||
:label="__('Clear')"
|
||||
@click="() => clearDates() | togglePopover()"
|
||||
:disabled="!fromDate || !toDate"
|
||||
/>
|
||||
<Button
|
||||
variant="solid"
|
||||
label="Apply"
|
||||
:label="__('Apply')"
|
||||
:disabled="!fromDate || !toDate"
|
||||
@click="() => selectDates() | togglePopover()"
|
||||
/>
|
||||
@ -161,10 +161,12 @@ export default {
|
||||
},
|
||||
formatMonth() {
|
||||
let date = this.getDate(this.currentYear, this.currentMonth - 1, 1)
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})
|
||||
let month = __(
|
||||
date.toLocaleString('en-US', {
|
||||
month: 'long',
|
||||
})
|
||||
)
|
||||
return `${month}, ${date.getFullYear()}`
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
@change="updateDate($event.target.value) || togglePopover()"
|
||||
/>
|
||||
<Button
|
||||
label="Now"
|
||||
:label="__('Now')"
|
||||
class="text-sm"
|
||||
@click="selectDate(getDate(), false, true) || togglePopover()"
|
||||
/>
|
||||
@ -55,7 +55,7 @@
|
||||
v-for="(d, i) in ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa']"
|
||||
:key="i"
|
||||
>
|
||||
{{ d }}
|
||||
{{ __(d) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -128,7 +128,7 @@
|
||||
</div>
|
||||
<div class="flex justify-end p-1">
|
||||
<Button
|
||||
label="Clear"
|
||||
:label="__('Clear')"
|
||||
class="text-sm"
|
||||
@click="
|
||||
() => {
|
||||
@ -208,10 +208,12 @@ export default {
|
||||
},
|
||||
formatMonth() {
|
||||
let date = this.getDate(this.currentYear, this.currentMonth - 1, 1)
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})
|
||||
let month = __(
|
||||
date.toLocaleString('en-US', {
|
||||
month: 'long',
|
||||
})
|
||||
)
|
||||
return `${month}, ${date.getFullYear()}`
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="space-y-1.5">
|
||||
<label class="block" :class="labelClasses" v-if="attrs.label">
|
||||
{{ attrs.label }}
|
||||
{{ __(attrs.label) }}
|
||||
</label>
|
||||
<Autocomplete
|
||||
ref="autocomplete"
|
||||
@ -33,7 +33,7 @@
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="w-full !justify-start"
|
||||
label="Create New"
|
||||
:label="__('Create New')"
|
||||
@click="attrs.onCreate(value, close)"
|
||||
>
|
||||
<template #prefix>
|
||||
@ -45,7 +45,7 @@
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="w-full !justify-start"
|
||||
label="Clear"
|
||||
:label="__('Clear')"
|
||||
@click="() => clearValue(close)"
|
||||
>
|
||||
<template #prefix>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
>
|
||||
<template #top>
|
||||
<div class="mx-10 flex items-center gap-2 border-t py-2.5">
|
||||
<span class="text-xs text-gray-500">SUBJECT:</span>
|
||||
<span class="text-xs text-gray-500">{{ __('SUBJECT') }}:</span>
|
||||
<TextInput
|
||||
class="flex-1 border-none bg-white hover:bg-white focus:border-none focus:!shadow-none focus-visible:!ring-0"
|
||||
v-model="subject"
|
||||
@ -20,12 +20,14 @@
|
||||
class="mx-10 flex items-center gap-2 border-t py-2.5"
|
||||
:class="[cc || bcc ? 'border-b' : '']"
|
||||
>
|
||||
<span class="text-xs text-gray-500">TO:</span>
|
||||
<span class="text-xs text-gray-500">{{ __('TO') }}:</span>
|
||||
<MultiselectInput
|
||||
class="flex-1"
|
||||
v-model="toEmails"
|
||||
:validate="validateEmail"
|
||||
:error-message="(value) => `${value} is an invalid email address`"
|
||||
:error-message="
|
||||
(value) => __('{0} is an invalid email address', [value])
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -33,23 +35,27 @@
|
||||
class="mx-10 flex items-center gap-2 py-2.5"
|
||||
:class="bcc ? 'border-b' : ''"
|
||||
>
|
||||
<span class="text-xs text-gray-500">CC:</span>
|
||||
<span class="text-xs text-gray-500">{{ __('CC') }}:</span>
|
||||
<MultiselectInput
|
||||
ref="ccInput"
|
||||
class="flex-1"
|
||||
v-model="ccEmails"
|
||||
:validate="validateEmail"
|
||||
:error-message="(value) => `${value} is an invalid email address`"
|
||||
:error-message="
|
||||
(value) => __('{0} is an invalid email address', [value])
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="bcc" class="mx-10 flex items-center gap-2 py-2.5">
|
||||
<span class="text-xs text-gray-500">BCC:</span>
|
||||
<span class="text-xs text-gray-500">{{ __('BCC') }}:</span>
|
||||
<MultiselectInput
|
||||
ref="bccInput"
|
||||
class="flex-1"
|
||||
v-model="bccEmails"
|
||||
:validate="validateEmail"
|
||||
:error-message="(value) => `${value} is an invalid email address`"
|
||||
:error-message="
|
||||
(value) => __('{0} is an invalid email address', [value])
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -114,11 +120,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-end space-x-2 sm:mt-0">
|
||||
<Button v-bind="discardButtonProps || {}" label="Discard" />
|
||||
<Button v-bind="discardButtonProps || {}" :label="__('Discard')" />
|
||||
<Button
|
||||
variant="solid"
|
||||
v-bind="submitButtonProps || {}"
|
||||
label="Submit"
|
||||
:label="__('Send')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -158,7 +164,7 @@ const props = defineProps({
|
||||
},
|
||||
subject: {
|
||||
type: String,
|
||||
default: 'Email from Lead',
|
||||
default: __('Email from Lead'),
|
||||
},
|
||||
editorProps: {
|
||||
type: Object,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<NestedPopover>
|
||||
<template #target>
|
||||
<Button label="Filter">
|
||||
<Button :label="__('Filter')">
|
||||
<template #prefix><FilterIcon class="h-4" /></template>
|
||||
<template v-if="filters?.size" #suffix>
|
||||
<div
|
||||
@ -24,14 +24,14 @@
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-13 pl-2 text-end text-base text-gray-600">
|
||||
{{ i == 0 ? 'Where' : 'And' }}
|
||||
{{ i == 0 ? __('Where') : __('And') }}
|
||||
</div>
|
||||
<div id="fieldname" class="!min-w-[140px]">
|
||||
<Autocomplete
|
||||
:value="f.field.fieldname"
|
||||
:options="filterableFields.data"
|
||||
@change="(e) => updateFilter(e, i)"
|
||||
placeholder="Filter by..."
|
||||
:placeholder="__('First Name')"
|
||||
/>
|
||||
</div>
|
||||
<div id="operator">
|
||||
@ -40,7 +40,7 @@
|
||||
v-model="f.operator"
|
||||
@change="(e) => updateOperator(e, f)"
|
||||
:options="getOperators(f.field.fieldtype, f.field.fieldname)"
|
||||
placeholder="Operator"
|
||||
:placeholder="__('Equals')"
|
||||
/>
|
||||
</div>
|
||||
<div id="value" class="!min-w-[140px]">
|
||||
@ -48,7 +48,7 @@
|
||||
:is="getValSelect(f)"
|
||||
v-model="f.value"
|
||||
@change="(v) => updateValue(v, f)"
|
||||
placeholder="Value"
|
||||
:placeholder="__('John Doe')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,21 +58,21 @@
|
||||
v-else
|
||||
class="mb-3 flex h-7 items-center px-3 text-sm text-gray-600"
|
||||
>
|
||||
Empty - Choose a field to filter by
|
||||
{{ __('Empty - Choose a field to filter by') }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<Autocomplete
|
||||
value=""
|
||||
:options="filterableFields.data"
|
||||
@change="(e) => setfilter(e)"
|
||||
placeholder="Filter by..."
|
||||
:placeholder="__('First name')"
|
||||
>
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="!text-gray-600"
|
||||
variant="ghost"
|
||||
@click="togglePopover()"
|
||||
label="Add Filter"
|
||||
:label="__('Add Filter')"
|
||||
>
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4" />
|
||||
@ -84,7 +84,7 @@
|
||||
v-if="filters?.size"
|
||||
class="!text-gray-600"
|
||||
variant="ghost"
|
||||
label="Clear all Filter"
|
||||
:label="__('Clear all Filter')"
|
||||
@click="clearfilter(close)"
|
||||
/>
|
||||
</div>
|
||||
@ -200,89 +200,89 @@ function getOperators(fieldtype, fieldname) {
|
||||
if (typeString.includes(fieldtype)) {
|
||||
options.push(
|
||||
...[
|
||||
{ label: 'Equals', value: 'equals' },
|
||||
{ label: 'Not Equals', value: 'not equals' },
|
||||
{ label: 'Like', value: 'like' },
|
||||
{ label: 'Not Like', value: 'not like' },
|
||||
{ label: 'In', value: 'in' },
|
||||
{ label: 'Not In', value: 'not in' },
|
||||
{ label: 'Is', value: 'is' },
|
||||
{ label: __('Equals'), value: 'equals' },
|
||||
{ label: __('Not Equals'), value: 'not equals' },
|
||||
{ label: __('Like'), value: 'like' },
|
||||
{ label: __('Not Like'), value: 'not like' },
|
||||
{ label: __('In'), value: 'in' },
|
||||
{ label: __('Not In'), value: 'not in' },
|
||||
{ label: __('Is'), value: 'is' },
|
||||
]
|
||||
)
|
||||
}
|
||||
if (fieldname === '_assign') {
|
||||
// TODO: make equals and not equals work
|
||||
options = [
|
||||
{ label: 'Like', value: 'like' },
|
||||
{ label: 'Not Like', value: 'not like' },
|
||||
{ label: 'Is', value: 'is' },
|
||||
{ label: __('Like'), value: 'like' },
|
||||
{ label: __('Not Like'), value: 'not like' },
|
||||
{ label: __('Is'), value: 'is' },
|
||||
]
|
||||
}
|
||||
if (typeNumber.includes(fieldtype)) {
|
||||
options.push(
|
||||
...[
|
||||
{ label: 'Equals', value: 'equals' },
|
||||
{ label: 'Not Equals', value: 'not equals' },
|
||||
{ label: 'Like', value: 'like' },
|
||||
{ label: 'Not Like', value: 'not like' },
|
||||
{ label: 'In', value: 'in' },
|
||||
{ label: 'Not In', value: 'not in' },
|
||||
{ label: 'Is', value: 'is' },
|
||||
{ label: '<', value: '<' },
|
||||
{ label: '>', value: '>' },
|
||||
{ label: '<=', value: '<=' },
|
||||
{ label: '>=', value: '>=' },
|
||||
{ label: __('Equals'), value: 'equals' },
|
||||
{ label: __('Not Equals'), value: 'not equals' },
|
||||
{ label: __('Like'), value: 'like' },
|
||||
{ label: __('Not Like'), value: 'not like' },
|
||||
{ label: __('In'), value: 'in' },
|
||||
{ label: __('Not In'), value: 'not in' },
|
||||
{ label: __('Is'), value: 'is' },
|
||||
{ label: __('<'), value: '<' },
|
||||
{ label: __('>'), value: '>' },
|
||||
{ label: __('<='), value: '<=' },
|
||||
{ label: __('>='), value: '>=' },
|
||||
]
|
||||
)
|
||||
}
|
||||
if (typeSelect.includes(fieldtype)) {
|
||||
options.push(
|
||||
...[
|
||||
{ label: 'Equals', value: 'equals' },
|
||||
{ label: 'Not Equals', value: 'not equals' },
|
||||
{ label: 'In', value: 'in' },
|
||||
{ label: 'Not In', value: 'not in' },
|
||||
{ label: 'Is', value: 'is' },
|
||||
{ label: __('Equals'), value: 'equals' },
|
||||
{ label: __('Not Equals'), value: 'not equals' },
|
||||
{ label: __('In'), value: 'in' },
|
||||
{ label: __('Not In'), value: 'not in' },
|
||||
{ label: __('Is'), value: 'is' },
|
||||
]
|
||||
)
|
||||
}
|
||||
if (typeLink.includes(fieldtype)) {
|
||||
options.push(
|
||||
...[
|
||||
{ label: 'Equals', value: 'equals' },
|
||||
{ label: 'Not Equals', value: 'not equals' },
|
||||
{ label: 'Like', value: 'like' },
|
||||
{ label: 'Not Like', value: 'not like' },
|
||||
{ label: 'In', value: 'in' },
|
||||
{ label: 'Not In', value: 'not in' },
|
||||
{ label: 'Is', value: 'is' },
|
||||
{ label: __('Equals'), value: 'equals' },
|
||||
{ label: __('Not Equals'), value: 'not equals' },
|
||||
{ label: __('Like'), value: 'like' },
|
||||
{ label: __('Not Like'), value: 'not like' },
|
||||
{ label: __('In'), value: 'in' },
|
||||
{ label: __('Not In'), value: 'not in' },
|
||||
{ label: __('Is'), value: 'is' },
|
||||
]
|
||||
)
|
||||
}
|
||||
if (typeCheck.includes(fieldtype)) {
|
||||
options.push(...[{ label: 'Equals', value: 'equals' }])
|
||||
options.push(...[{ label: __('Equals'), value: 'equals' }])
|
||||
}
|
||||
if (['Duration'].includes(fieldtype)) {
|
||||
options.push(
|
||||
...[
|
||||
{ label: 'Like', value: 'like' },
|
||||
{ label: 'Not Like', value: 'not like' },
|
||||
{ label: 'In', value: 'in' },
|
||||
{ label: 'Not In', value: 'not in' },
|
||||
{ label: 'Is', value: 'is' },
|
||||
{ label: __('Like'), value: 'like' },
|
||||
{ label: __('Not Like'), value: 'not like' },
|
||||
{ label: __('In'), value: 'in' },
|
||||
{ label: __('Not In'), value: 'not in' },
|
||||
{ label: __('Is'), value: 'is' },
|
||||
]
|
||||
)
|
||||
}
|
||||
if (typeDate.includes(fieldtype)) {
|
||||
options.push(
|
||||
...[
|
||||
{ label: 'Is', value: 'is' },
|
||||
{ label: '>', value: '>' },
|
||||
{ label: '<', value: '<' },
|
||||
{ label: '>=', value: '>=' },
|
||||
{ label: '<=', value: '<=' },
|
||||
{ label: 'Between', value: 'between' },
|
||||
{ label: 'Timespan', value: 'timespan' },
|
||||
{ label: __('Is'), value: 'is' },
|
||||
{ label: __('>'), value: '>' },
|
||||
{ label: __('<'), value: '<' },
|
||||
{ label: __('>='), value: '>=' },
|
||||
{ label: __('<='), value: '<=' },
|
||||
{ label: __('Between'), value: 'between' },
|
||||
{ label: __('Timespan'), value: 'timespan' },
|
||||
]
|
||||
)
|
||||
}
|
||||
@ -327,13 +327,15 @@ function getValSelect(f) {
|
||||
if (fieldtype == 'Dynamic Link') {
|
||||
return h(FormControl, { type: 'text' })
|
||||
}
|
||||
return h(Link, { class: 'form-control', doctype: options })
|
||||
return h(Link, { class: 'form-control', doctype: options, value: f.value })
|
||||
} else if (typeNumber.includes(fieldtype)) {
|
||||
return h(FormControl, { type: 'number' })
|
||||
} else if (typeDate.includes(fieldtype) && operator == 'between') {
|
||||
return h(DateRangePicker)
|
||||
return h(DateRangePicker, { value: f.value })
|
||||
} else if (typeDate.includes(fieldtype)) {
|
||||
return h(fieldtype == 'Date' ? DatePicker : DatetimePicker)
|
||||
return h(fieldtype == 'Date' ? DatePicker : DatetimePicker, {
|
||||
value: f.value,
|
||||
})
|
||||
} else {
|
||||
return h(FormControl, { type: 'text' })
|
||||
}
|
||||
@ -437,8 +439,6 @@ function updateOperator(event, filter) {
|
||||
|
||||
function isSameTypeOperator(oldOperator, newOperator) {
|
||||
let textOperators = [
|
||||
'like',
|
||||
'not like',
|
||||
'equals',
|
||||
'not equals',
|
||||
'in',
|
||||
@ -531,71 +531,71 @@ const oppositeOperatorMap = {
|
||||
|
||||
const timespanOptions = [
|
||||
{
|
||||
label: 'Last Week',
|
||||
label: __('Last Week'),
|
||||
value: 'last week',
|
||||
},
|
||||
{
|
||||
label: 'Last Month',
|
||||
label: __('Last Month'),
|
||||
value: 'last month',
|
||||
},
|
||||
{
|
||||
label: 'Last Quarter',
|
||||
label: __('Last Quarter'),
|
||||
value: 'last quarter',
|
||||
},
|
||||
{
|
||||
label: 'Last 6 Months',
|
||||
label: __('Last 6 Months'),
|
||||
value: 'last 6 months',
|
||||
},
|
||||
{
|
||||
label: 'Last Year',
|
||||
label: __('Last Year'),
|
||||
value: 'last year',
|
||||
},
|
||||
{
|
||||
label: 'Yesterday',
|
||||
label: __('Yesterday'),
|
||||
value: 'yesterday',
|
||||
},
|
||||
{
|
||||
label: 'Today',
|
||||
label: __('Today'),
|
||||
value: 'today',
|
||||
},
|
||||
{
|
||||
label: 'Tomorrow',
|
||||
label: __('Tomorrow'),
|
||||
value: 'tomorrow',
|
||||
},
|
||||
{
|
||||
label: 'This Week',
|
||||
label: __('This Week'),
|
||||
value: 'this week',
|
||||
},
|
||||
{
|
||||
label: 'This Month',
|
||||
label: __('This Month'),
|
||||
value: 'this month',
|
||||
},
|
||||
{
|
||||
label: 'This Quarter',
|
||||
label: __('This Quarter'),
|
||||
value: 'this quarter',
|
||||
},
|
||||
{
|
||||
label: 'This Year',
|
||||
label: __('This Year'),
|
||||
value: 'this year',
|
||||
},
|
||||
{
|
||||
label: 'Next Week',
|
||||
label: __('Next Week'),
|
||||
value: 'next week',
|
||||
},
|
||||
{
|
||||
label: 'Next Month',
|
||||
label: __('Next Month'),
|
||||
value: 'next month',
|
||||
},
|
||||
{
|
||||
label: 'Next Quarter',
|
||||
label: __('Next Quarter'),
|
||||
value: 'next quarter',
|
||||
},
|
||||
{
|
||||
label: 'Next 6 Months',
|
||||
label: __('Next 6 Months'),
|
||||
value: 'next 6 months',
|
||||
},
|
||||
{
|
||||
label: 'Next Year',
|
||||
label: __('Next Year'),
|
||||
value: 'next year',
|
||||
},
|
||||
]
|
||||
|
||||
@ -8,24 +8,11 @@
|
||||
>
|
||||
<path
|
||||
d="M93.7101 0H23.8141C10.7986 0 0.247559 10.5511 0.247559 23.5665V93.4626C0.247559 106.478 10.7986 117.029 23.8141 117.029H93.7101C106.726 117.029 117.277 106.478 117.277 93.4626V23.5665C117.277 10.5511 106.726 0 93.7101 0Z"
|
||||
fill="url(#paint0_angular_0_12)"
|
||||
fill="#DB4EE0"
|
||||
/>
|
||||
<path
|
||||
d="M21.2487 27.5115V38.0121H85.7749V45.8876L62.5161 68.4638V80.7495L54.9556 83.3222V68.4638C54.9556 68.4638 41.1474 55.0756 36.3171 50.3503H21.3013L42.6174 71.089C43.825 72.2441 44.5076 73.8716 44.5076 75.5517V87.9424L73.0167 88.0474V75.5517C73.0167 73.8716 73.6992 72.2441 74.9068 71.089L96.2755 50.2978V27.5115H21.2487Z"
|
||||
d="M21.2488 27.5116V38.0122H85.7749V45.8876L62.5161 68.4639V80.7496L54.9557 83.3222V68.4639C54.9557 68.4639 41.1474 55.0756 36.3171 50.3504H21.3013L42.6175 71.089C43.825 72.2441 44.5076 73.8717 44.5076 75.5518V87.9425L73.0167 88.0475V75.5518C73.0167 73.8717 73.6992 72.2441 74.9068 71.089L96.2755 50.2979V27.5116H21.2488Z"
|
||||
fill="#F1FCFF"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient
|
||||
id="paint0_angular_0_12"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(81.5001 -21.5) rotate(67.4893) scale(262.5 101.567)"
|
||||
>
|
||||
<stop offset="0.494792" stop-color="#E953E6" />
|
||||
<stop offset="1" stop-color="#BA27D1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<div class="mb-3 flex flex-col">
|
||||
<SidebarLink
|
||||
id="notifications-btn"
|
||||
label="Notifications"
|
||||
:label="__('Notifications')"
|
||||
:icon="NotificationsIcon"
|
||||
:isCollapsed="isSidebarCollapsed"
|
||||
@click="() => toggleNotificationPanel()"
|
||||
@ -45,7 +45,7 @@
|
||||
<template #header="{ opened, hide, toggle }">
|
||||
<div
|
||||
v-if="!hide"
|
||||
class="flex cursor-pointer gap-1.5 px-1 text-sm font-medium text-gray-600 transition-all duration-300 ease-in-out"
|
||||
class="flex cursor-pointer gap-1.5 px-1 text-base font-medium text-gray-600 transition-all duration-300 ease-in-out"
|
||||
:class="
|
||||
isSidebarCollapsed
|
||||
? 'ml-0 h-0 overflow-hidden opacity-0'
|
||||
@ -59,7 +59,7 @@
|
||||
:class="{ 'rotate-90': opened }"
|
||||
/>
|
||||
<span class="uppercase">
|
||||
{{ view.name }}
|
||||
{{ __(view.name) }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -67,7 +67,7 @@
|
||||
<SidebarLink
|
||||
v-for="link in view.views"
|
||||
:icon="link.icon"
|
||||
:label="link.label"
|
||||
:label="__(link.label)"
|
||||
:to="link.to"
|
||||
:isCollapsed="isSidebarCollapsed"
|
||||
class="mx-2 my-0.5"
|
||||
@ -78,19 +78,19 @@
|
||||
</div>
|
||||
<div class="m-2 flex flex-col gap-1">
|
||||
<SidebarLink
|
||||
label="Docs"
|
||||
:label="__('Docs')"
|
||||
:isCollapsed="isSidebarCollapsed"
|
||||
icon="book-open"
|
||||
@click="() => openDocs()"
|
||||
/>
|
||||
<SidebarLink
|
||||
:label="isSidebarCollapsed ? 'Expand' : 'Collapse'"
|
||||
:label="isSidebarCollapsed ? __('Expand') : __('Collapse')"
|
||||
:isCollapsed="isSidebarCollapsed"
|
||||
@click="isSidebarCollapsed = !isSidebarCollapsed"
|
||||
class=""
|
||||
>
|
||||
<template #icon>
|
||||
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
|
||||
<span class="grid h-4.5 w-4.5 flex-shrink-0 place-items-center">
|
||||
<CollapseSidebar
|
||||
class="h-4.5 w-4.5 text-gray-700 duration-300 ease-in-out"
|
||||
:class="{ '[transform:rotateY(180deg)]': isSidebarCollapsed }"
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
:variant="'subtle'"
|
||||
:theme="item.color"
|
||||
size="md"
|
||||
:label="item.label"
|
||||
:label="__(item.label)"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="column.type === 'Check'">
|
||||
|
||||
@ -172,14 +172,14 @@ function editValues(selections, unselectAll) {
|
||||
|
||||
function deleteValues(selections, unselectAll) {
|
||||
$dialog({
|
||||
title: 'Delete',
|
||||
message: `Are you sure you want to delete ${selections.size} item${
|
||||
selections.size > 1 ? 's' : ''
|
||||
}?`,
|
||||
title: __('Delete'),
|
||||
message: __('Are you sure you want to delete {0} item(s)?', [
|
||||
selections.size,
|
||||
]),
|
||||
variant: 'danger',
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => {
|
||||
@ -188,7 +188,7 @@ function deleteValues(selections, unselectAll) {
|
||||
doctype: 'Contact',
|
||||
}).then(() => {
|
||||
createToast({
|
||||
title: 'Deleted successfully',
|
||||
title: __('Deleted successfully'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -207,11 +207,11 @@ const customListActions = ref([])
|
||||
function bulkActions(selections, unselectAll) {
|
||||
let actions = [
|
||||
{
|
||||
label: 'Edit',
|
||||
label: __('Edit'),
|
||||
onClick: () => editValues(selections, unselectAll),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
onClick: () => deleteValues(selections, unselectAll),
|
||||
},
|
||||
]
|
||||
|
||||
@ -203,14 +203,12 @@ function editValues(selections, unselectAll) {
|
||||
|
||||
function deleteValues(selections, unselectAll) {
|
||||
$dialog({
|
||||
title: 'Delete',
|
||||
message: `Are you sure you want to delete ${selections.size} item${
|
||||
selections.size > 1 ? 's' : ''
|
||||
}?`,
|
||||
title: __('Delete'),
|
||||
message: __('Are you sure you want to delete {0} item(s)?', [selections.size]),
|
||||
variant: 'danger',
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => {
|
||||
@ -219,7 +217,7 @@ function deleteValues(selections, unselectAll) {
|
||||
doctype: 'CRM Deal',
|
||||
}).then(() => {
|
||||
createToast({
|
||||
title: 'Deleted successfully',
|
||||
title: __('Deleted successfully'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -239,17 +237,17 @@ const customListActions = ref([])
|
||||
function bulkActions(selections, unselectAll) {
|
||||
let actions = [
|
||||
{
|
||||
label: 'Edit',
|
||||
label: __('Edit'),
|
||||
onClick: () => editValues(selections, unselectAll),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
onClick: () => deleteValues(selections, unselectAll),
|
||||
},
|
||||
]
|
||||
customBulkActions.value.forEach((action) => {
|
||||
actions.push({
|
||||
label: action.label,
|
||||
label: __(action.label),
|
||||
onClick: () =>
|
||||
action.onClick({
|
||||
list: list.value,
|
||||
|
||||
@ -154,14 +154,14 @@ function editValues(selections, unselectAll) {
|
||||
|
||||
function deleteValues(selections, unselectAll) {
|
||||
$dialog({
|
||||
title: 'Delete',
|
||||
message: `Are you sure you want to delete ${selections.size} item${
|
||||
selections.size > 1 ? 's' : ''
|
||||
}?`,
|
||||
title: __('Delete'),
|
||||
message: __('Are you sure you want to delete {0} item(s)?', [
|
||||
selections.size,
|
||||
]),
|
||||
variant: 'danger',
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => {
|
||||
@ -170,7 +170,7 @@ function deleteValues(selections, unselectAll) {
|
||||
doctype: 'Email Template',
|
||||
}).then(() => {
|
||||
createToast({
|
||||
title: 'Deleted successfully',
|
||||
title: __('Deleted successfully'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -189,11 +189,11 @@ const customListActions = ref([])
|
||||
function bulkActions(selections, unselectAll) {
|
||||
let actions = [
|
||||
{
|
||||
label: 'Edit',
|
||||
label: __('Edit'),
|
||||
onClick: () => editValues(selections, unselectAll),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
onClick: () => deleteValues(selections, unselectAll),
|
||||
},
|
||||
]
|
||||
|
||||
@ -212,14 +212,12 @@ function editValues(selections, unselectAll) {
|
||||
|
||||
function deleteValues(selections, unselectAll) {
|
||||
$dialog({
|
||||
title: 'Delete',
|
||||
message: `Are you sure you want to delete ${selections.size} item${
|
||||
selections.size > 1 ? 's' : ''
|
||||
}?`,
|
||||
title: __('Delete'),
|
||||
message: __('Are you sure you want to delete {0} item(s)?', [selections.size]),
|
||||
variant: 'danger',
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => {
|
||||
@ -228,7 +226,7 @@ function deleteValues(selections, unselectAll) {
|
||||
doctype: 'CRM Lead',
|
||||
}).then(() => {
|
||||
createToast({
|
||||
title: 'Deleted successfully',
|
||||
title: __('Deleted successfully'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -248,17 +246,17 @@ const customListActions = ref([])
|
||||
function bulkActions(selections, unselectAll) {
|
||||
let actions = [
|
||||
{
|
||||
label: 'Edit',
|
||||
label: __('Edit'),
|
||||
onClick: () => editValues(selections, unselectAll),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
onClick: () => deleteValues(selections, unselectAll),
|
||||
},
|
||||
]
|
||||
customBulkActions.value.forEach((action) => {
|
||||
actions.push({
|
||||
label: action.label,
|
||||
label: __(action.label),
|
||||
onClick: () =>
|
||||
action.onClick({
|
||||
list: list.value,
|
||||
|
||||
@ -157,14 +157,14 @@ function editValues(selections, unselectAll) {
|
||||
|
||||
function deleteValues(selections, unselectAll) {
|
||||
$dialog({
|
||||
title: 'Delete',
|
||||
message: `Are you sure you want to delete ${selections.size} item${
|
||||
selections.size > 1 ? 's' : ''
|
||||
}?`,
|
||||
title: __('Delete'),
|
||||
message: __('Are you sure you want to delete {0} item(s)?', [
|
||||
selections.size,
|
||||
]),
|
||||
variant: 'danger',
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => {
|
||||
@ -173,7 +173,7 @@ function deleteValues(selections, unselectAll) {
|
||||
doctype: 'CRM Organization',
|
||||
}).then(() => {
|
||||
createToast({
|
||||
title: 'Deleted successfully',
|
||||
title: __('Deleted successfully'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -192,11 +192,11 @@ const customListActions = ref([])
|
||||
function bulkActions(selections, unselectAll) {
|
||||
let actions = [
|
||||
{
|
||||
label: 'Edit',
|
||||
label: __('Edit'),
|
||||
onClick: () => editValues(selections, unselectAll),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
onClick: () => deleteValues(selections, unselectAll),
|
||||
},
|
||||
]
|
||||
|
||||
@ -176,14 +176,12 @@ function editValues(selections, unselectAll) {
|
||||
|
||||
function deleteValues(selections, unselectAll) {
|
||||
$dialog({
|
||||
title: 'Delete',
|
||||
message: `Are you sure you want to delete ${selections.size} item${
|
||||
selections.size > 1 ? 's' : ''
|
||||
}?`,
|
||||
title: __('Delete'),
|
||||
message: __('Are you sure you want to delete {0} item(s)?'),
|
||||
variant: 'danger',
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => {
|
||||
@ -192,7 +190,7 @@ function deleteValues(selections, unselectAll) {
|
||||
doctype: 'CRM Task',
|
||||
}).then(() => {
|
||||
createToast({
|
||||
title: 'Deleted successfully',
|
||||
title: __('Deleted successfully'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -211,11 +209,11 @@ const customListActions = ref([])
|
||||
function bulkActions(selections, unselectAll) {
|
||||
let actions = [
|
||||
{
|
||||
label: 'Edit',
|
||||
label: __('Edit'),
|
||||
onClick: () => editValues(selections, unselectAll),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
onClick: () => deleteValues(selections, unselectAll),
|
||||
},
|
||||
]
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: 'Assign To',
|
||||
title: __('Assign To'),
|
||||
size: 'xl',
|
||||
actions: [
|
||||
{
|
||||
label: 'Cancel',
|
||||
label: __('Cancel'),
|
||||
variant: 'subtle',
|
||||
onClick: () => {
|
||||
assignees = oldAssignees
|
||||
@ -14,7 +14,7 @@
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Update',
|
||||
label: __('Update'),
|
||||
variant: 'solid',
|
||||
onClick: () => updateAssignees(),
|
||||
},
|
||||
@ -65,7 +65,7 @@
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<ErrorMessage class="mt-2" v-if="error" :message="error" />
|
||||
<ErrorMessage class="mt-2" v-if="error" :message="__(error)" />
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-semibold leading-6 text-gray-900">
|
||||
{{ dialogOptions.title || 'Untitled' }}
|
||||
{{ __(dialogOptions.title) || __('Untitled') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
@ -64,14 +64,14 @@
|
||||
v-if="field.type === 'link'"
|
||||
variant="outline"
|
||||
size="md"
|
||||
:label="field.label"
|
||||
:label="__(field.label)"
|
||||
v-model="_contact[field.name]"
|
||||
:doctype="field.doctype"
|
||||
:placeholder="field.placeholder"
|
||||
/>
|
||||
<div class="space-y-1.5" v-if="field.type === 'dropdown'">
|
||||
<label class="block text-base text-gray-600">
|
||||
{{ field.label }}
|
||||
{{ __(field.label) }}
|
||||
</label>
|
||||
<NestedPopover>
|
||||
<template #target="{ open }">
|
||||
@ -101,7 +101,7 @@
|
||||
/>
|
||||
<div v-else>
|
||||
<div class="p-1.5 px-7 text-base text-gray-500">
|
||||
No {{ field.label }} Available
|
||||
{{ __('No {0} Available', [field.label]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -109,7 +109,7 @@
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="w-full !justify-start"
|
||||
label="Create New"
|
||||
:label="__('Create New')"
|
||||
@click="field.create()"
|
||||
>
|
||||
<template #prefix>
|
||||
@ -126,7 +126,7 @@
|
||||
variant="outline"
|
||||
size="md"
|
||||
type="text"
|
||||
:label="field.label"
|
||||
:label="__(field.label)"
|
||||
:placeholder="field.placeholder"
|
||||
v-model="_contact[field.name]"
|
||||
/>
|
||||
@ -143,7 +143,7 @@
|
||||
:key="action.label"
|
||||
v-bind="action"
|
||||
>
|
||||
{{ action.label }}
|
||||
{{ __(action.label) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ title: 'Bulk Edit' }">
|
||||
<Dialog v-model="show" :options="{ title: __('Bulk Edit') }">
|
||||
<template #body-content>
|
||||
<div class="mb-4">
|
||||
<div class="mb-1.5 text-sm text-gray-600">Field</div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">{{ __('Field') }}</div>
|
||||
<Autocomplete
|
||||
:value="field.label"
|
||||
:options="fields.data"
|
||||
@change="(e) => changeField(e)"
|
||||
placeholder="Select Field..."
|
||||
:placeholder="__('Source')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">Value</div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">{{ __('Value') }}</div>
|
||||
<component
|
||||
:is="getValueComponent(field)"
|
||||
:value="newValue"
|
||||
size="md"
|
||||
@change="(v) => updateValue(v)"
|
||||
placeholder="Value"
|
||||
:placeholder="__('Contact Us')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -27,7 +27,7 @@
|
||||
variant="solid"
|
||||
@click="updateValues"
|
||||
:loading="loading"
|
||||
:label="`Update ${recordCount} Records`"
|
||||
:label="__('Update {0} Records', [recordCount])"
|
||||
/>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: editMode ? emailTemplate.name : 'Create Email Template',
|
||||
title: editMode ? __(emailTemplate.name) : __('Create Email Template'),
|
||||
size: 'xl',
|
||||
actions: [
|
||||
{
|
||||
label: editMode ? 'Update' : 'Create',
|
||||
label: editMode ? __('Update') : __('Create'),
|
||||
variant: 'solid',
|
||||
onClick: () => (editMode ? updateEmailTemplate() : callInsertDoc()),
|
||||
},
|
||||
@ -18,41 +18,41 @@
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="mb-1.5 text-sm text-gray-600">
|
||||
Name
|
||||
{{ __('Name') }}
|
||||
<span class="text-red-500">*</span>
|
||||
</div>
|
||||
<TextInput
|
||||
ref="nameRef"
|
||||
variant="outline"
|
||||
v-model="_emailTemplate.name"
|
||||
placeholder="Add name"
|
||||
:placeholder="__('Payment Reminder')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="mb-1.5 text-sm text-gray-600">Doctype</div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">{{ __('Doctype') }}</div>
|
||||
<Select
|
||||
variant="outline"
|
||||
v-model="_emailTemplate.reference_doctype"
|
||||
:options="['CRM Deal', 'CRM Lead']"
|
||||
placeholder="Select Doctype"
|
||||
:placeholder="__('CRM Deal')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">
|
||||
Subject
|
||||
{{ __('Subject') }}
|
||||
<span class="text-red-500">*</span>
|
||||
</div>
|
||||
<TextInput
|
||||
ref="subjectRef"
|
||||
variant="outline"
|
||||
v-model="_emailTemplate.subject"
|
||||
placeholder="Add subject"
|
||||
:placeholder="__('Payment Reminder from Frappé - (#{{ name }})')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">
|
||||
Content
|
||||
{{ __('Content') }}
|
||||
<span class="text-red-500">*</span>
|
||||
</div>
|
||||
<TextEditor
|
||||
@ -62,13 +62,13 @@
|
||||
:bubbleMenu="true"
|
||||
:content="_emailTemplate.response"
|
||||
@change="(val) => (_emailTemplate.response = val)"
|
||||
placeholder="Type a Content"
|
||||
:placeholder="__('Dear {{ lead_name }}, \n\nThis is a reminder for the payment of {{ grand_total }}. \n\nThanks, \nFrappé')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox v-model="_emailTemplate.enabled" label="Enabled" />
|
||||
<Checkbox v-model="_emailTemplate.enabled" :label="__('Enabled')" />
|
||||
</div>
|
||||
<ErrorMessage :message="errorMessage" />
|
||||
<ErrorMessage :message="__(errorMessage)" />
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
@ -1,16 +1,22 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ title: 'Email Templates', size: '4xl' }">
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{ title: __('Email Templates'), size: '4xl' }"
|
||||
>
|
||||
<template #body-content>
|
||||
<TextInput
|
||||
ref="searchInput"
|
||||
v-model="search"
|
||||
type="text"
|
||||
class="mb-2 w-full"
|
||||
placeholder="Search"
|
||||
/>
|
||||
:placeholder="__('Payment Reminder')"
|
||||
>
|
||||
<template #prefix>
|
||||
<FeatherIcon name="search" class="h-4 w-4 text-gray-500" />
|
||||
</template>
|
||||
</TextInput>
|
||||
<div
|
||||
v-if="filteredTemplates.length"
|
||||
class="grid max-h-[560px] grid-cols-3 gap-2 overflow-y-auto"
|
||||
class="mt-2 grid max-h-[560px] grid-cols-3 gap-2 overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
v-for="template in filteredTemplates"
|
||||
@ -22,7 +28,7 @@
|
||||
{{ template.name }}
|
||||
</div>
|
||||
<div v-if="template.subject" class="text-sm text-gray-600">
|
||||
Subject: {{ template.subject }}
|
||||
{{ __('Subject: {0}', [template.subject]) }}
|
||||
</div>
|
||||
<TextEditor
|
||||
v-if="template.response"
|
||||
@ -33,11 +39,13 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-else class="mt-2">
|
||||
<div class="flex h-56 flex-col items-center justify-center">
|
||||
<div class="text-lg text-gray-500">No templates found</div>
|
||||
<div class="text-lg text-gray-500">
|
||||
{{ __('No templates found') }}
|
||||
</div>
|
||||
<Button
|
||||
label="Create New"
|
||||
:label="__('Create New')"
|
||||
class="mt-4"
|
||||
@click="
|
||||
() => {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
size: 'xl',
|
||||
actions: [
|
||||
{
|
||||
label: editMode ? 'Update' : 'Create',
|
||||
label: editMode ? __('Update') : __('Create'),
|
||||
variant: 'solid',
|
||||
onClick: () => updateNote(),
|
||||
},
|
||||
@ -15,14 +15,16 @@
|
||||
<template #body-title>
|
||||
<div class="flex items-center gap-3">
|
||||
<h3 class="text-2xl font-semibold leading-6 text-gray-900">
|
||||
{{ editMode ? 'Edit Note' : 'Create Note' }}
|
||||
{{ editMode ? __('Edit Note') : __('Create Note') }}
|
||||
</h3>
|
||||
<Button
|
||||
v-if="_note?.reference_docname"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:label="
|
||||
_note.reference_doctype == 'CRM Deal' ? 'Open Deal' : 'Open Lead'
|
||||
_note.reference_doctype == 'CRM Deal'
|
||||
? __('Open Deal')
|
||||
: __('Open Lead')
|
||||
"
|
||||
@click="redirect()"
|
||||
>
|
||||
@ -35,16 +37,16 @@
|
||||
<template #body-content>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">Title</div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">{{ __('Title') }}</div>
|
||||
<TextInput
|
||||
ref="title"
|
||||
variant="outline"
|
||||
v-model="_note.title"
|
||||
placeholder="Add title"
|
||||
:placeholder="__('Call with John Doe')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">Content</div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">{{ __('Content') }}</div>
|
||||
<TextEditor
|
||||
variant="outline"
|
||||
ref="content"
|
||||
@ -52,7 +54,9 @@
|
||||
:bubbleMenu="true"
|
||||
:content="_note.content"
|
||||
@change="(val) => (_note.content = val)"
|
||||
placeholder="Type a Content"
|
||||
:placeholder="
|
||||
__('Took a call with John Doe and discussed the new project.')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -64,7 +68,7 @@
|
||||
import ArrowUpRightIcon from '@/components/Icons/ArrowUpRightIcon.vue'
|
||||
import { TextEditor, call } from 'frappe-ui'
|
||||
import { ref, nextTick, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const props = defineProps({
|
||||
note: {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-semibold leading-6 text-gray-900">
|
||||
{{ dialogOptions.title || 'Untitled' }}
|
||||
{{ __(dialogOptions.title) || __('Untitled') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
@ -41,67 +41,68 @@
|
||||
type="text"
|
||||
ref="title"
|
||||
size="md"
|
||||
label="Organization Name"
|
||||
:label="__('Organization Name')"
|
||||
variant="outline"
|
||||
v-model="_organization.organization_name"
|
||||
placeholder="Add Organization Name"
|
||||
placeholder="Frappé Technologies"
|
||||
/>
|
||||
<div class="flex gap-4">
|
||||
<FormControl
|
||||
class="flex-1"
|
||||
type="text"
|
||||
size="md"
|
||||
label="Website"
|
||||
:label="__('Website')"
|
||||
variant="outline"
|
||||
v-model="_organization.website"
|
||||
placeholder="Add Website"
|
||||
placeholder="https://example.com"
|
||||
/>
|
||||
<FormControl
|
||||
class="flex-1"
|
||||
type="text"
|
||||
size="md"
|
||||
label="Annual Revenue"
|
||||
:label="__('Annual Revenue')"
|
||||
variant="outline"
|
||||
v-model="_organization.annual_revenue"
|
||||
placeholder="Add Annual Revenue"
|
||||
:placeholder="__('9,999,999')"
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
class="flex-1"
|
||||
size="md"
|
||||
label="Territory"
|
||||
:label="__('Territory')"
|
||||
variant="outline"
|
||||
v-model="_organization.territory"
|
||||
doctype="CRM Territory"
|
||||
placeholder="Add Territory"
|
||||
placeholder="India"
|
||||
/>
|
||||
<div class="flex gap-4">
|
||||
<FormControl
|
||||
class="flex-1"
|
||||
type="select"
|
||||
:options="[
|
||||
'1-10',
|
||||
'11-50',
|
||||
'51-200',
|
||||
'201-500',
|
||||
'501-1000',
|
||||
'1001-5000',
|
||||
'5001-10000',
|
||||
'10001+',
|
||||
{ label: __('1-10'), value: '1-10' },
|
||||
{ label: __('11-50'), value: '11-50' },
|
||||
{ label: __('51-200'), value: '51-200' },
|
||||
{ label: __('201-500'), value: '201-500' },
|
||||
{ label: __('501-1000'), value: '501-1000' },
|
||||
{ label: __('1001-5000'), value: '1001-5000' },
|
||||
{ label: __('5001-10000'), value: '5001-10000' },
|
||||
{ label: __('10001+'), value: '10001+' },
|
||||
]"
|
||||
size="md"
|
||||
label="No. of Employees"
|
||||
:label="__('No of Employees')"
|
||||
variant="outline"
|
||||
:placeholder="__('1-10')"
|
||||
v-model="_organization.no_of_employees"
|
||||
/>
|
||||
<Link
|
||||
class="flex-1"
|
||||
size="md"
|
||||
label="Industry"
|
||||
:label="__('Industry')"
|
||||
variant="outline"
|
||||
v-model="_organization.industry"
|
||||
doctype="CRM Industry"
|
||||
placeholder="Add Industry"
|
||||
:placeholder="__('Technology')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -115,7 +116,7 @@
|
||||
v-for="action in dialogOptions.actions"
|
||||
:key="action.label"
|
||||
v-bind="action"
|
||||
:label="action.label"
|
||||
:label="__(action.label)"
|
||||
:loading="loading"
|
||||
/>
|
||||
</div>
|
||||
@ -236,14 +237,14 @@ function handleOrganizationUpdate(doc, renamed = false) {
|
||||
|
||||
const dialogOptions = computed(() => {
|
||||
let title = !editMode.value
|
||||
? 'New Organization'
|
||||
: _organization.value.organization_name
|
||||
? __('New Organization')
|
||||
: __(_organization.value.organization_name)
|
||||
let size = detailMode.value ? '' : 'xl'
|
||||
let actions = detailMode.value
|
||||
? []
|
||||
: [
|
||||
{
|
||||
label: editMode.value ? 'Save' : 'Create',
|
||||
label: editMode.value ? __('Save') : __('Create'),
|
||||
variant: 'solid',
|
||||
onClick: () =>
|
||||
editMode.value ? updateOrganization() : callInsertDoc(),
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
size: 'xl',
|
||||
actions: [
|
||||
{
|
||||
label: editMode ? 'Update' : 'Create',
|
||||
label: editMode ? __('Update') : __('Create'),
|
||||
variant: 'solid',
|
||||
onClick: () => updateTask(),
|
||||
},
|
||||
@ -15,14 +15,14 @@
|
||||
<template #body-title>
|
||||
<div class="flex items-center gap-3">
|
||||
<h3 class="text-2xl font-semibold leading-6 text-gray-900">
|
||||
{{ editMode ? 'Edit Task' : 'Create Task' }}
|
||||
{{ editMode ? __('Edit Task') : __('Create Task') }}
|
||||
</h3>
|
||||
<Button
|
||||
v-if="task?.reference_docname"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:label="
|
||||
task.reference_doctype == 'CRM Deal' ? 'Open Deal' : 'Open Lead'
|
||||
task.reference_doctype == 'CRM Deal' ? __('Open Deal') : __('Open Lead')
|
||||
"
|
||||
@click="redirect()"
|
||||
>
|
||||
@ -35,16 +35,16 @@
|
||||
<template #body-content>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">Title</div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">{{ __('Title') }}</div>
|
||||
<TextInput
|
||||
ref="title"
|
||||
variant="outline"
|
||||
v-model="_task.title"
|
||||
placeholder="Add Title"
|
||||
:placeholder="__('Call with John Doe')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">Description</div>
|
||||
<div class="mb-1.5 text-sm text-gray-600">{{ __('Description') }}</div>
|
||||
<TextEditor
|
||||
variant="outline"
|
||||
ref="description"
|
||||
@ -52,7 +52,7 @@
|
||||
:bubbleMenu="true"
|
||||
:content="_task.description"
|
||||
@change="(val) => (_task.description = val)"
|
||||
placeholder="Type a Description"
|
||||
:placeholder="__('Took a call with John Doe and discussed the new project.')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
@ -68,7 +68,7 @@
|
||||
:value="getUser(_task.assigned_to).full_name"
|
||||
doctype="User"
|
||||
@change="(option) => (_task.assigned_to = option)"
|
||||
placeholder="Assignee"
|
||||
:placeholder="__('John Doe')"
|
||||
:hideMe="true"
|
||||
>
|
||||
<template #prefix>
|
||||
@ -90,7 +90,7 @@
|
||||
icon-left="calendar"
|
||||
:value="_task.due_date"
|
||||
@change="(val) => (_task.due_date = val)"
|
||||
placeholder="Due Date"
|
||||
:placeholder="__('01/04/2024 11:30 PM')"
|
||||
input-class="border-none"
|
||||
/>
|
||||
<Dropdown :options="taskPriorityOptions(updateTaskPriority)">
|
||||
|
||||
@ -3,17 +3,17 @@
|
||||
v-model="show"
|
||||
:options="{
|
||||
title: editMode
|
||||
? 'Edit View'
|
||||
? __('Edit View')
|
||||
: duplicateMode
|
||||
? 'Duplicate View'
|
||||
: 'Create View',
|
||||
? __('Duplicate View')
|
||||
: __('Create View'),
|
||||
actions: [
|
||||
{
|
||||
label: editMode
|
||||
? 'Save Changes'
|
||||
? __('Save Changes')
|
||||
: duplicateMode
|
||||
? 'Duplicate'
|
||||
: 'Create',
|
||||
? __('Duplicate')
|
||||
: __('Create'),
|
||||
variant: 'solid',
|
||||
onClick: () => (editMode ? update() : create()),
|
||||
},
|
||||
@ -25,8 +25,8 @@
|
||||
variant="outline"
|
||||
size="md"
|
||||
type="text"
|
||||
label="View Name"
|
||||
placeholder="View Name"
|
||||
:label="__('View Name')"
|
||||
:placeholder="__('My Open Deals')"
|
||||
v-model="view.label"
|
||||
/>
|
||||
</template>
|
||||
@ -51,7 +51,6 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
const show = defineModel()
|
||||
const view = defineModel('view')
|
||||
|
||||
|
||||
@ -14,9 +14,9 @@
|
||||
<div
|
||||
class="z-20 flex items-center justify-between border-b bg-white px-5 py-2.5"
|
||||
>
|
||||
<div class="text-base font-medium">Notifications</div>
|
||||
<div class="text-base font-medium">{{ __('Notifications') }}</div>
|
||||
<div class="flex gap-1">
|
||||
<Tooltip text="Mark all as read">
|
||||
<Tooltip :text="__('Mark all as read')">
|
||||
<div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@ -28,7 +28,7 @@
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip text="Close">
|
||||
<Tooltip :text="__('Close')">
|
||||
<div>
|
||||
<Button variant="ghost" @click="() => toggleNotificationPanel()">
|
||||
<template #icon>
|
||||
@ -58,17 +58,17 @@
|
||||
<UserAvatar :user="n.from_user.name" size="lg" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-2 space-x-1 leading-5 text-gray-700">
|
||||
<div class="mb-2 space-x-1 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">
|
||||
{{ n.from_user.full_name }}
|
||||
</span>
|
||||
<span>mentioned you in {{ n.reference_doctype }}</span>
|
||||
<span>{{ __('mentioned you in {0}', [n.reference_doctype]) }}</span>
|
||||
<span class="font-medium text-gray-900">
|
||||
{{ n.reference_name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-sm text-gray-600">
|
||||
{{ timeAgo(n.creation) }}
|
||||
{{ __(timeAgo(n.creation)) }}
|
||||
</div>
|
||||
</div>
|
||||
</RouterLink>
|
||||
@ -79,7 +79,7 @@
|
||||
>
|
||||
<NotificationsIcon class="h-20 w-20 text-gray-300" />
|
||||
<div class="text-lg font-medium text-gray-500">
|
||||
No new notifications
|
||||
{{ __('No new notifications') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,7 +90,6 @@ import MarkAsDoneIcon from '@/components/Icons/MarkAsDoneIcon.vue'
|
||||
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
||||
import UserAvatar from '@/components/UserAvatar.vue'
|
||||
import { notificationsStore } from '@/stores/notifications'
|
||||
import { globalStore } from '@/stores/global'
|
||||
import { timeAgo } from '@/utils'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import { Tooltip } from 'frappe-ui'
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
:key="s.label"
|
||||
class="flex items-center gap-2 text-base leading-5"
|
||||
>
|
||||
<div class="w-[106px] text-sm text-gray-600">{{ s.label }}</div>
|
||||
<div class="w-[106px] text-sm text-gray-600">{{ __(s.label) }}</div>
|
||||
<div class="grid min-h-[28px] items-center">
|
||||
<Tooltip v-if="s.tooltipText" :text="s.tooltipText">
|
||||
<Tooltip v-if="s.tooltipText" :text="__(s.tooltipText)">
|
||||
<div class="ml-2 cursor-pointer">
|
||||
<Badge
|
||||
v-if="s.type == 'Badge'"
|
||||
@ -69,12 +69,12 @@ let slaSection = computed(() => {
|
||||
tooltipText = dateFormat(data.value.response_by, dateTooltipFormat)
|
||||
if (new Date(data.value.response_by) < new Date()) {
|
||||
color = 'red'
|
||||
if (status == 'In less than a minute') {
|
||||
if (status == __('In less than a minute')) {
|
||||
status = 'less than a minute ago'
|
||||
}
|
||||
}
|
||||
} else if (['Fulfilled', 'Failed'].includes(status)) {
|
||||
status = status + ' in ' + formatTime(data.value.first_response_time)
|
||||
status = __(status) + ' in ' + formatTime(data.value.first_response_time)
|
||||
tooltipText = dateFormat(data.value.first_responded_on, dateTooltipFormat)
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ let slaSection = computed(() => {
|
||||
{
|
||||
label: 'First Response',
|
||||
type: 'Badge',
|
||||
value: status,
|
||||
value: __(status),
|
||||
tooltipText: tooltipText,
|
||||
color: color,
|
||||
},
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
class="h-4 text-gray-900 transition-all duration-300 ease-in-out"
|
||||
:class="{ 'rotate-90': opened }"
|
||||
/>
|
||||
{{ label || 'Untitled' }}
|
||||
{{ __(label) || __('Untitled') }}
|
||||
</div>
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
class="flex items-center gap-2 px-3 leading-5 first:mt-3"
|
||||
>
|
||||
<div class="w-[106px] shrink-0 text-sm text-gray-600">
|
||||
{{ field.label }}
|
||||
{{ __(field.label) }}
|
||||
<span class="text-red-500">{{ field.reqd ? ' *' : '' }}</span>
|
||||
</div>
|
||||
<div
|
||||
@ -17,7 +17,7 @@
|
||||
v-if="field.read_only && field.type !== 'checkbox'"
|
||||
class="flex h-7 cursor-pointer items-center px-2 py-1 text-gray-600"
|
||||
>
|
||||
<Tooltip :text="field.tooltip">
|
||||
<Tooltip :text="__(field.tooltip)">
|
||||
<div>{{ data[field.name] }}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@ -1,28 +1,28 @@
|
||||
<template>
|
||||
<button
|
||||
class="flex h-7 cursor-pointer items-center rounded text-gray-800 duration-300 ease-in-out focus:outline-none focus:transition-none focus-visible:rounded focus-visible:ring-2 focus-visible:ring-gray-400"
|
||||
class="flex h-7 cursor-pointer items-center rounded text-gray-700 duration-300 ease-in-out focus:outline-none focus:transition-none focus-visible:rounded focus-visible:ring-2 focus-visible:ring-gray-400"
|
||||
:class="isActive ? 'bg-white shadow-sm' : 'hover:bg-gray-100'"
|
||||
@click="handleClick"
|
||||
>
|
||||
<div
|
||||
class="flex w-full items-center justify-between duration-300 ease-in-out"
|
||||
:class="isCollapsed ? 'p-1' : 'px-2 py-1'"
|
||||
:class="isCollapsed ? 'ml-[3px] p-1' : 'px-2 py-1'"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<Tooltip :text="label" placement="right" :disabled="!isCollapsed">
|
||||
<slot name="icon">
|
||||
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
|
||||
<span class="grid h-4.5 w-4.5 flex-shrink-0 place-items-center">
|
||||
<FeatherIcon
|
||||
v-if="typeof icon == 'string'"
|
||||
:name="icon"
|
||||
class="h-4 w-4 text-gray-700"
|
||||
class="h-4.5 w-4.5 text-gray-700"
|
||||
/>
|
||||
<component v-else :is="icon" class="h-4 w-4 text-gray-700" />
|
||||
<component v-else :is="icon" class="h-4.5 w-4.5 text-gray-700" />
|
||||
</span>
|
||||
</slot>
|
||||
</Tooltip>
|
||||
<span
|
||||
class="flex-1 flex-shrink-0 text-sm duration-300 ease-in-out"
|
||||
class="flex-1 flex-shrink-0 text-base duration-300 ease-in-out"
|
||||
:class="
|
||||
isCollapsed
|
||||
? 'ml-0 w-0 overflow-hidden opacity-0'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<NestedPopover>
|
||||
<template #target>
|
||||
<Button label="Sort" ref="sortButtonRef">
|
||||
<Button :label="__('Sort')" ref="sortButtonRef">
|
||||
<template #prefix><SortIcon class="h-4" /></template>
|
||||
<template v-if="sortValues?.size" #suffix>
|
||||
<div
|
||||
@ -33,15 +33,15 @@
|
||||
:value="sort.fieldname"
|
||||
:options="sortOptions.data"
|
||||
@change="(e) => updateSort(e, i)"
|
||||
placeholder="Sort by"
|
||||
:placeholder="__('First Name')"
|
||||
/>
|
||||
<FormControl
|
||||
class="!w-32"
|
||||
type="select"
|
||||
v-model="sort.direction"
|
||||
:options="[
|
||||
{ label: 'Ascending', value: 'asc' },
|
||||
{ label: 'Descending', value: 'desc' },
|
||||
{ label: __('Ascending'), value: 'asc' },
|
||||
{ label: __('Descending'), value: 'desc' },
|
||||
]"
|
||||
@change="
|
||||
(e) => {
|
||||
@ -49,7 +49,7 @@
|
||||
apply()
|
||||
}
|
||||
"
|
||||
placeholder="Sort by"
|
||||
:placeholder="__('Ascending')"
|
||||
/>
|
||||
<Button variant="ghost" icon="x" @click="removeSort(i)" />
|
||||
</div>
|
||||
@ -58,13 +58,13 @@
|
||||
v-else
|
||||
class="mb-3 flex h-7 items-center px-3 text-sm text-gray-600"
|
||||
>
|
||||
Empty - Choose a field to sort by
|
||||
{{ __('Empty - Choose a field to sort by') }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<Autocomplete
|
||||
:options="options"
|
||||
value=""
|
||||
placeholder="Sort by"
|
||||
:placeholder="__('First Name')"
|
||||
@change="(e) => setSort(e)"
|
||||
>
|
||||
<template #target="{ togglePopover }">
|
||||
@ -72,7 +72,7 @@
|
||||
class="!text-gray-600"
|
||||
variant="ghost"
|
||||
@click="togglePopover()"
|
||||
label="Add Sort"
|
||||
:label="__('Add Sort')"
|
||||
>
|
||||
<template #prefix>
|
||||
<FeatherIcon name="plus" class="h-4" />
|
||||
@ -84,7 +84,7 @@
|
||||
v-if="sortValues?.size"
|
||||
class="!text-gray-600"
|
||||
variant="ghost"
|
||||
label="Clear Sort"
|
||||
:label="__('Clear Sort')"
|
||||
@click="clearSort(close)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
<template>
|
||||
<Dropdown :options="userDropdownOptions">
|
||||
<Dropdown :options="dropdownOptions">
|
||||
<template v-slot="{ open }">
|
||||
<button
|
||||
class="flex h-12 py-2 items-center rounded-md duration-300 ease-in-out"
|
||||
class="flex h-12 items-center rounded-md py-2 duration-300 ease-in-out"
|
||||
:class="
|
||||
isCollapsed
|
||||
? 'px-0 w-auto'
|
||||
? 'w-auto px-0'
|
||||
: open
|
||||
? 'bg-white shadow-sm px-2 w-52'
|
||||
: 'hover:bg-gray-200 px-2 w-52'
|
||||
? 'w-52 bg-white px-2 shadow-sm'
|
||||
: 'w-52 px-2 hover:bg-gray-200'
|
||||
"
|
||||
>
|
||||
<CRMLogo class="w-8 h-8 rounded flex-shrink-0" />
|
||||
<CRMLogo class="size-8 flex-shrink-0 rounded" />
|
||||
<div
|
||||
class="flex flex-1 flex-col text-left duration-300 ease-in-out"
|
||||
:class="
|
||||
isCollapsed
|
||||
? 'opacity-0 ml-0 w-0 overflow-hidden'
|
||||
: 'opacity-100 ml-2 w-auto'
|
||||
? 'ml-0 w-0 overflow-hidden opacity-0'
|
||||
: 'ml-2 w-auto opacity-100'
|
||||
"
|
||||
>
|
||||
<div class="text-base font-medium text-gray-900 leading-none">
|
||||
CRM
|
||||
<div class="text-base font-medium leading-none text-gray-900">
|
||||
{{ __('CRM') }}
|
||||
</div>
|
||||
<div class="mt-1 text-sm text-gray-700 leading-none">
|
||||
<div class="mt-1 text-sm leading-none text-gray-700">
|
||||
{{ user.full_name }}
|
||||
</div>
|
||||
</div>
|
||||
@ -31,11 +31,15 @@
|
||||
class="duration-300 ease-in-out"
|
||||
:class="
|
||||
isCollapsed
|
||||
? 'opacity-0 ml-0 w-0 overflow-hidden'
|
||||
: 'opacity-100 ml-2 w-auto'
|
||||
? 'ml-0 w-0 overflow-hidden opacity-0'
|
||||
: 'ml-2 w-auto opacity-100'
|
||||
"
|
||||
>
|
||||
<FeatherIcon name="chevron-down" class="h-4 w-4 text-gray-600" aria-hidden="true" />
|
||||
<FeatherIcon
|
||||
name="chevron-down"
|
||||
class="size-4 text-gray-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
@ -47,7 +51,7 @@ import CRMLogo from '@/components/Icons/CRMLogo.vue'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { usersStore } from '@/stores/users'
|
||||
import { Dropdown } from 'frappe-ui'
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
isCollapsed: {
|
||||
@ -61,16 +65,16 @@ const { getUser } = usersStore()
|
||||
|
||||
const user = computed(() => getUser() || {})
|
||||
|
||||
const userDropdownOptions = [
|
||||
let dropdownOptions = ref([
|
||||
{
|
||||
icon: 'corner-up-left',
|
||||
label: 'Switch to Desk',
|
||||
label: computed(() => __('Switch to Desk')),
|
||||
onClick: () => window.location.replace('/app'),
|
||||
},
|
||||
{
|
||||
icon: 'log-out',
|
||||
label: 'Log out',
|
||||
label: computed(() => __('Log out')),
|
||||
onClick: () => logout.submit(),
|
||||
},
|
||||
]
|
||||
])
|
||||
</script>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<Dropdown :options="viewsDropdownOptions">
|
||||
<template #default="{ open }">
|
||||
<Button :label="currentView.label">
|
||||
<Button :label="__(currentView.label)">
|
||||
<template #prefix>
|
||||
<FeatherIcon :name="currentView.icon" class="h-4" />
|
||||
</template>
|
||||
@ -29,11 +29,11 @@
|
||||
v-if="viewUpdated && route.query.view && (!view.public || isManager())"
|
||||
class="flex items-center gap-2 border-r pr-2"
|
||||
>
|
||||
<Button label="Cancel" @click="cancelChanges" />
|
||||
<Button label="Save Changes" @click="saveView" />
|
||||
<Button :label="__('Cancel')" @click="cancelChanges" />
|
||||
<Button :label="__('Save Changes')" @click="saveView" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button label="Refresh" @click="reload()" :loading="isLoading">
|
||||
<Button :label="__('Refresh')" @click="reload()" :loading="isLoading">
|
||||
<template #icon>
|
||||
<RefreshIcon class="h-4 w-4" />
|
||||
</template>
|
||||
@ -55,11 +55,11 @@
|
||||
v-if="!options.hideColumnsButton"
|
||||
:options="[
|
||||
{
|
||||
group: 'Options',
|
||||
group: __('Options'),
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Export',
|
||||
label: __('Export'),
|
||||
icon: () =>
|
||||
h(FeatherIcon, { name: 'download', class: 'h-4 w-4' }),
|
||||
onClick: () => (showExportDialog = true),
|
||||
@ -96,10 +96,10 @@
|
||||
<Dialog
|
||||
v-model="showExportDialog"
|
||||
:options="{
|
||||
title: 'Export',
|
||||
title: __('Export'),
|
||||
actions: [
|
||||
{
|
||||
label: 'Download',
|
||||
label: __('Download'),
|
||||
variant: 'solid',
|
||||
onClick: () => exportRows(),
|
||||
},
|
||||
@ -109,16 +109,25 @@
|
||||
<template #body-content>
|
||||
<FormControl
|
||||
variant="outline"
|
||||
label="Export Type"
|
||||
:label="__('Export Type')"
|
||||
type="select"
|
||||
:options="['Excel', 'CSV']"
|
||||
:options="[
|
||||
{
|
||||
label: __('Excel'),
|
||||
value: 'Excel',
|
||||
},
|
||||
{
|
||||
label: __('CSV'),
|
||||
value: 'CSV',
|
||||
},
|
||||
]"
|
||||
v-model="export_type"
|
||||
placeholder="Select Export Type"
|
||||
:placeholder="__('Excel')"
|
||||
/>
|
||||
<div class="mt-3">
|
||||
<FormControl
|
||||
type="checkbox"
|
||||
:label="`Export All ${list.data.total_count} Records`"
|
||||
:label="__('Export All {0} Record(s)', [list.data.total_count])"
|
||||
v-model="export_all"
|
||||
/>
|
||||
</div>
|
||||
@ -321,7 +330,7 @@ async function exportRows() {
|
||||
|
||||
const defaultViews = [
|
||||
{
|
||||
label: props.options?.defaultViewName || 'List View',
|
||||
label: __(props.options?.defaultViewName) || __('List View'),
|
||||
icon: 'list',
|
||||
onClick() {
|
||||
viewUpdated.value = false
|
||||
@ -333,7 +342,7 @@ const defaultViews = [
|
||||
const viewsDropdownOptions = computed(() => {
|
||||
let _views = [
|
||||
{
|
||||
group: 'Default Views',
|
||||
group: __('Default Views'),
|
||||
hideLabel: true,
|
||||
items: defaultViews,
|
||||
},
|
||||
@ -341,6 +350,7 @@ const viewsDropdownOptions = computed(() => {
|
||||
|
||||
if (list.value?.data?.views) {
|
||||
list.value.data.views.forEach((view) => {
|
||||
view.label = __(view.label)
|
||||
view.icon = view.icon || 'list'
|
||||
view.filters =
|
||||
typeof view.filters == 'string'
|
||||
@ -359,18 +369,18 @@ const viewsDropdownOptions = computed(() => {
|
||||
|
||||
publicViews.length &&
|
||||
_views.push({
|
||||
group: 'Public Views',
|
||||
group: __('Public Views'),
|
||||
items: publicViews,
|
||||
})
|
||||
|
||||
savedViews.length &&
|
||||
_views.push({
|
||||
group: 'Saved Views',
|
||||
group: __('Saved Views'),
|
||||
items: savedViews,
|
||||
})
|
||||
pinnedViews.length &&
|
||||
_views.push({
|
||||
group: 'Pinned Views',
|
||||
group: __('Pinned Views'),
|
||||
items: pinnedViews,
|
||||
})
|
||||
}
|
||||
@ -489,11 +499,11 @@ function updatePageLength(value, loadMore = false) {
|
||||
const viewActions = computed(() => {
|
||||
let actions = [
|
||||
{
|
||||
group: 'Default Views',
|
||||
group: __('Default Views'),
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Duplicate',
|
||||
label: __('Duplicate'),
|
||||
icon: () => h(DuplicateIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => duplicateView(),
|
||||
},
|
||||
@ -503,14 +513,14 @@ const viewActions = computed(() => {
|
||||
|
||||
if (route.query.view && (!view.value.public || isManager())) {
|
||||
actions[0].items.push({
|
||||
label: 'Rename',
|
||||
label: __('Rename'),
|
||||
icon: () => h(EditIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => renameView(),
|
||||
})
|
||||
|
||||
if (!view.value.public) {
|
||||
actions[0].items.push({
|
||||
label: view.value.pinned ? 'Unpin View' : 'Pin View',
|
||||
label: view.value.pinned ? __('Unpin View') : __('Pin View'),
|
||||
icon: () =>
|
||||
h(view.value.pinned ? UnpinIcon : PinIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => pinView(),
|
||||
@ -519,7 +529,7 @@ const viewActions = computed(() => {
|
||||
|
||||
if (isManager()) {
|
||||
actions[0].items.push({
|
||||
label: view.value.public ? 'Make Private' : 'Make Public',
|
||||
label: view.value.public ? __('Make Private') : __('Make Public'),
|
||||
icon: () =>
|
||||
h(FeatherIcon, {
|
||||
name: view.value.public ? 'lock' : 'unlock',
|
||||
@ -530,20 +540,20 @@ const viewActions = computed(() => {
|
||||
}
|
||||
|
||||
actions.push({
|
||||
group: 'Delete View',
|
||||
group: __('Delete View'),
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
icon: 'trash-2',
|
||||
onClick: () =>
|
||||
$dialog({
|
||||
title: 'Delete View',
|
||||
message: 'Are you sure you want to delete this view?',
|
||||
title: __('Delete View'),
|
||||
message: __('Are you sure you want to delete this view?'),
|
||||
variant: 'danger',
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => deleteView(close),
|
||||
@ -558,15 +568,15 @@ const viewActions = computed(() => {
|
||||
})
|
||||
|
||||
function duplicateView() {
|
||||
let label = getView(route.query.view)?.label || 'List View'
|
||||
let label = __(getView(route.query.view)?.label) || __('List View')
|
||||
view.value.name = ''
|
||||
view.value.label = label + ' (New)'
|
||||
view.value.label = label + __(' (New)')
|
||||
showViewModal.value = true
|
||||
}
|
||||
|
||||
function renameView() {
|
||||
view.value.name = route.query.view
|
||||
view.value.label = getView(route.query.view).label
|
||||
view.value.label = __(getView(route.query.view).label)
|
||||
showViewModal.value = true
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
frappeRequest,
|
||||
FeatherIcon,
|
||||
} from 'frappe-ui'
|
||||
import translationPlugin from './translation'
|
||||
import { createDialog } from './utils/dialogs'
|
||||
import socket from './socket'
|
||||
import { getCachedListResource } from 'frappe-ui/src/resources/listResource'
|
||||
@ -45,6 +46,7 @@ setConfig('resourceFetcher', frappeRequest)
|
||||
app.use(FrappeUI)
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
app.use(translationPlugin)
|
||||
for (let key in globalComponents) {
|
||||
app.component(key, globalComponents[key])
|
||||
}
|
||||
|
||||
@ -7,16 +7,15 @@
|
||||
<Button
|
||||
v-if="callLog.data.type == 'Incoming' && !callLog.data.lead"
|
||||
variant="solid"
|
||||
label="Create lead"
|
||||
:label="__('Create lead')"
|
||||
@click="createLead"
|
||||
>
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
</LayoutHeader>
|
||||
<div class="border-b"></div>
|
||||
<div v-if="callLog.data" class="max-w-lg p-6">
|
||||
<div class="pb-3 text-base font-medium">Call details</div>
|
||||
<div class="pb-3 text-base font-medium">{{ __('Call details') }}</div>
|
||||
<div class="mb-3 flex flex-col gap-4 rounded-lg border p-4 shadow-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
@ -29,7 +28,11 @@
|
||||
class="h-4 w-4 text-gray-600"
|
||||
/>
|
||||
<div class="font-medium">
|
||||
{{ callLog.data.type == 'Incoming' ? 'Inbound' : 'Outbound' }} call
|
||||
{{
|
||||
callLog.data.type == 'Incoming'
|
||||
? __('Inbound Call')
|
||||
: __('Outbound Call')
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@ -37,7 +40,7 @@
|
||||
:variant="'subtle'"
|
||||
:theme="statusColorMap[callLog.data.status]"
|
||||
size="md"
|
||||
:label="statusLabelMap[callLog.data.status]"
|
||||
:label="__(statusLabelMap[callLog.data.status])"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,7 +53,7 @@
|
||||
/>
|
||||
<div class="ml-1 flex flex-col gap-1">
|
||||
<div class="text-base font-medium">
|
||||
{{ callLog.data.caller.label }}
|
||||
{{ __(callLog.data.caller.label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ callLog.data.from }}
|
||||
@ -64,7 +67,7 @@
|
||||
/>
|
||||
<div class="ml-1 flex flex-col gap-1">
|
||||
<div class="text-base font-medium">
|
||||
{{ callLog.data.receiver.label }}
|
||||
{{ __(callLog.data.receiver.label) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">
|
||||
{{ callLog.data.to }}
|
||||
@ -75,19 +78,19 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-1">
|
||||
<DurationIcon class="h-4 w-4 text-gray-600" />
|
||||
<div class="text-sm text-gray-600">Duration</div>
|
||||
<div class="text-sm text-gray-600">{{ __('Duration') }}</div>
|
||||
<div class="text-sm">{{ callLog.data.duration }}</div>
|
||||
</div>
|
||||
<Tooltip :text="dateFormat(callLog.data.creation, dateTooltipFormat)">
|
||||
<div class="text-sm text-gray-600">
|
||||
{{ timeAgo(callLog.data.creation) }}
|
||||
{{ __(timeAgo(callLog.data.creation)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="callLog.data.recording_url" class="mt-6">
|
||||
<div class="mb-3 text-base font-medium">Call recording</div>
|
||||
<div class="mb-3 text-base font-medium">{{ __('Call recording') }}</div>
|
||||
<div class="flex items-center justify-between rounded border shadow-sm">
|
||||
<audio
|
||||
class="audio-control"
|
||||
@ -98,7 +101,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="callLog.data.note" class="mt-6">
|
||||
<div class="mb-3 text-base font-medium">Call note</div>
|
||||
<div class="mb-3 text-base font-medium">{{ __('Call note') }}</div>
|
||||
<div
|
||||
class="flex h-56 cursor-pointer flex-col gap-3 rounded border p-4 shadow-sm"
|
||||
@click="showNoteModal = true"
|
||||
@ -117,7 +120,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="callLog.data.lead" class="mt-6">
|
||||
<div class="mb-3 text-base font-medium">Lead</div>
|
||||
<div class="mb-3 text-base font-medium">{{ __('Lead') }}</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
@ -206,7 +209,7 @@ function createLead() {
|
||||
}
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: 'Call Logs', route: { name: 'Call Logs' } }]
|
||||
let items = [{ label: __('Call Logs'), route: { name: 'Call Logs' } }]
|
||||
items.push({
|
||||
label: callLog.data?.caller.label,
|
||||
route: { name: 'Call Log', params: { callLogId: props.callLogId } },
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<PhoneIcon class="h-10 w-10" />
|
||||
<span>No Logs Found</span>
|
||||
<span>{{ __('No Logs Found') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -69,7 +69,7 @@ import { computed, ref } from 'vue'
|
||||
const { getUser } = usersStore()
|
||||
const { getContact, getLeadContact } = contactsStore()
|
||||
|
||||
const breadcrumbs = [{ label: 'Call Logs', route: { name: 'Call Logs' } }]
|
||||
const breadcrumbs = [{ label: __('Call Logs'), route: { name: 'Call Logs' } }]
|
||||
|
||||
const callLogsListView = ref(null)
|
||||
|
||||
@ -131,7 +131,7 @@ const rows = computed(() => {
|
||||
} else if (['modified', 'creation'].includes(row)) {
|
||||
_rows[row] = {
|
||||
label: dateFormat(callLog[row], dateTooltipFormat),
|
||||
timeAgo: timeAgo(callLog[row]),
|
||||
timeAgo: __(timeAgo(callLog[row])),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -24,13 +24,13 @@
|
||||
{
|
||||
icon: 'upload',
|
||||
label: contact.data.image
|
||||
? 'Change image'
|
||||
: 'Upload image',
|
||||
? __('Change image')
|
||||
: __('Upload image'),
|
||||
onClick: openFileSelector,
|
||||
},
|
||||
{
|
||||
icon: 'trash-2',
|
||||
label: 'Remove image',
|
||||
label: __('Remove image'),
|
||||
onClick: () => changeContactImage(''),
|
||||
},
|
||||
],
|
||||
@ -71,7 +71,10 @@
|
||||
>
|
||||
·
|
||||
</span>
|
||||
<Tooltip text="Make Call" v-if="contact.data.actual_mobile_no">
|
||||
<Tooltip
|
||||
:text="__('Make Call')"
|
||||
v-if="contact.data.actual_mobile_no"
|
||||
>
|
||||
<div
|
||||
class="flex cursor-pointer items-center gap-1.5"
|
||||
@click="makeCall(contact.data.actual_mobile_no)"
|
||||
@ -113,7 +116,7 @@
|
||||
contact.data.company_name
|
||||
"
|
||||
variant="ghost"
|
||||
label="More"
|
||||
:label="__('More')"
|
||||
class="-ml-1 cursor-pointer hover:text-gray-900"
|
||||
@click="
|
||||
() => {
|
||||
@ -125,7 +128,7 @@
|
||||
</div>
|
||||
<div class="mt-2 flex gap-1.5">
|
||||
<Button
|
||||
label="Edit"
|
||||
:label="__('Edit')"
|
||||
size="sm"
|
||||
@click="
|
||||
() => {
|
||||
@ -139,7 +142,7 @@
|
||||
</template>
|
||||
</Button>
|
||||
<Button
|
||||
label="Delete"
|
||||
:label="__('Delete')"
|
||||
theme="red"
|
||||
size="sm"
|
||||
@click="deleteContact"
|
||||
@ -149,7 +152,7 @@
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<ErrorMessage :message="error" />
|
||||
<ErrorMessage :message="__(error)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -161,7 +164,7 @@
|
||||
:class="{ 'text-gray-900': selected }"
|
||||
>
|
||||
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
||||
{{ tab.label }}
|
||||
{{ __(tab.label) }}
|
||||
<Badge
|
||||
class="group-hover:bg-gray-900"
|
||||
:class="[selected ? 'bg-gray-900' : 'bg-gray-600']"
|
||||
@ -187,7 +190,7 @@
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center space-y-3">
|
||||
<component :is="tab.icon" class="!h-10 !w-10" />
|
||||
<div>No {{ tab.label }} Found</div>
|
||||
<div>{{ __('No {0} Found', [__(tab.label)]) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -251,7 +254,7 @@ const showContactModal = ref(false)
|
||||
const detailMode = ref(false)
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: 'Contacts', route: { name: 'Contacts' } }]
|
||||
let items = [{ label: __('Contacts'), route: { name: 'Contacts' } }]
|
||||
items.push({
|
||||
label: contact.data?.full_name,
|
||||
route: { name: 'Contact', params: { contactId: props.contactId } },
|
||||
@ -262,7 +265,7 @@ const breadcrumbs = computed(() => {
|
||||
function validateFile(file) {
|
||||
let extn = file.name.split('.').pop().toLowerCase()
|
||||
if (!['png', 'jpg', 'jpeg'].includes(extn)) {
|
||||
return 'Only PNG and JPG images are allowed'
|
||||
return __('Only PNG and JPG images are allowed')
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,11 +281,11 @@ async function changeContactImage(file) {
|
||||
|
||||
async function deleteContact() {
|
||||
$dialog({
|
||||
title: 'Delete contact',
|
||||
message: 'Are you sure you want to delete this contact?',
|
||||
title: __('Delete contact'),
|
||||
message: __('Are you sure you want to delete this contact?'),
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
theme: 'red',
|
||||
variant: 'solid',
|
||||
async onClick(close) {
|
||||
@ -360,44 +363,44 @@ function getDealRowObject(deal) {
|
||||
},
|
||||
modified: {
|
||||
label: dateFormat(deal.modified, dateTooltipFormat),
|
||||
timeAgo: timeAgo(deal.modified),
|
||||
timeAgo: __(timeAgo(deal.modified)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const dealColumns = [
|
||||
{
|
||||
label: 'Organization',
|
||||
label: __('Organization'),
|
||||
key: 'organization',
|
||||
width: '11rem',
|
||||
},
|
||||
{
|
||||
label: 'Amount',
|
||||
label: __('Amount'),
|
||||
key: 'annual_revenue',
|
||||
width: '9rem',
|
||||
},
|
||||
{
|
||||
label: 'Status',
|
||||
label: __('Status'),
|
||||
key: 'status',
|
||||
width: '10rem',
|
||||
},
|
||||
{
|
||||
label: 'Email',
|
||||
label: __('Email'),
|
||||
key: 'email',
|
||||
width: '12rem',
|
||||
},
|
||||
{
|
||||
label: 'Mobile no',
|
||||
label: __('Mobile no'),
|
||||
key: 'mobile_no',
|
||||
width: '11rem',
|
||||
},
|
||||
{
|
||||
label: 'Deal owner',
|
||||
label: __('Deal owner'),
|
||||
key: 'deal_owner',
|
||||
width: '10rem',
|
||||
},
|
||||
{
|
||||
label: 'Last modified',
|
||||
label: __('Last modified'),
|
||||
key: 'modified',
|
||||
width: '8rem',
|
||||
},
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
v-if="contactsListView?.customListActions"
|
||||
:actions="contactsListView.customListActions"
|
||||
/>
|
||||
<Button variant="solid" label="Create" @click="showContactModal = true">
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="__('Create')"
|
||||
@click="showContactModal = true"
|
||||
>
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
@ -47,8 +51,8 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<ContactsIcon class="h-10 w-10" />
|
||||
<span>No Contacts Found</span>
|
||||
<Button label="Create" @click="showContactModal = true">
|
||||
<span>{{ __('No Contacts Found') }}</span>
|
||||
<Button :label="__('Create')" @click="showContactModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</div>
|
||||
@ -81,10 +85,10 @@ const currentContact = computed(() => {
|
||||
})
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: 'Contacts', route: { name: 'Contacts' } }]
|
||||
let items = [{ label: __('Contacts'), route: { name: 'Contacts' } }]
|
||||
if (!currentContact.value) return items
|
||||
items.push({
|
||||
label: currentContact.value.full_name,
|
||||
label: __(currentContact.value.full_name),
|
||||
route: {
|
||||
name: 'Contact',
|
||||
params: { contactId: currentContact.value.name },
|
||||
@ -123,7 +127,7 @@ const rows = computed(() => {
|
||||
} else if (['modified', 'creation'].includes(row)) {
|
||||
_rows[row] = {
|
||||
label: dateFormat(contact[row], dateTooltipFormat),
|
||||
timeAgo: timeAgo(contact[row]),
|
||||
timeAgo: __(timeAgo(contact[row])),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
<Activities
|
||||
ref="activities"
|
||||
doctype="CRM Deal"
|
||||
:title="tab.label"
|
||||
:title="tab.name"
|
||||
v-model:reload="reload"
|
||||
v-model="deal"
|
||||
/>
|
||||
@ -48,10 +48,10 @@
|
||||
<div
|
||||
class="flex h-10.5 items-center border-b px-5 py-2.5 text-lg font-semibold"
|
||||
>
|
||||
About this Deal
|
||||
{{ __('About this Deal') }}
|
||||
</div>
|
||||
<div class="flex items-center justify-start gap-5 border-b p-5">
|
||||
<Tooltip text="Organization logo">
|
||||
<Tooltip :text="__('Organization logo')">
|
||||
<div class="group relative h-[88px] w-[88px]">
|
||||
<Avatar
|
||||
size="3xl"
|
||||
@ -68,31 +68,31 @@
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div class="flex gap-1.5">
|
||||
<Tooltip text="Make a call">
|
||||
<Tooltip :text="__('Make a call')">
|
||||
<Button class="h-7 w-7" @click="triggerCall">
|
||||
<PhoneIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip text="Send an email">
|
||||
<Tooltip :text="__('Send an email')">
|
||||
<Button class="h-7 w-7">
|
||||
<EmailIcon
|
||||
class="h-4 w-4"
|
||||
@click="
|
||||
deal.data.email
|
||||
? openEmailBox()
|
||||
: errorMessage('No email set')
|
||||
: errorMessage(__('No email set'))
|
||||
"
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip text="Go to website">
|
||||
<Tooltip :text="__('Go to website')">
|
||||
<Button class="h-7 w-7">
|
||||
<LinkIcon
|
||||
class="h-4 w-4"
|
||||
@click="
|
||||
deal.data.website
|
||||
? openWebsite(deal.data.website)
|
||||
: errorMessage('No website set')
|
||||
: errorMessage(__('No website set'))
|
||||
"
|
||||
/>
|
||||
</Button>
|
||||
@ -137,7 +137,7 @@
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="h-7 px-3"
|
||||
label="Add Contact"
|
||||
:label="__('Add Contact')"
|
||||
@click="togglePopover()"
|
||||
>
|
||||
<template #prefix>
|
||||
@ -162,7 +162,7 @@
|
||||
class="flex min-h-20 flex-1 items-center justify-center gap-3 text-base text-gray-500"
|
||||
>
|
||||
<LoadingIndicator class="h-4 w-4" />
|
||||
<span>Loading...</span>
|
||||
<span>{{ __('Loading...') }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="section.contacts.length"
|
||||
@ -194,7 +194,7 @@
|
||||
v-if="contact.is_primary"
|
||||
class="ml-2"
|
||||
variant="outline"
|
||||
label="Primary"
|
||||
:label="__('Primary')"
|
||||
theme="green"
|
||||
/>
|
||||
</div>
|
||||
@ -251,7 +251,7 @@
|
||||
v-else
|
||||
class="flex h-20 items-center justify-center text-base text-gray-600"
|
||||
>
|
||||
No contacts added
|
||||
{{ __('No contacts added') }}
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
@ -392,7 +392,7 @@ function updateDeal(fieldname, value, callback) {
|
||||
deal.reload()
|
||||
reload.value = true
|
||||
createToast({
|
||||
title: 'Deal updated',
|
||||
title: __('Deal updated'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -400,8 +400,8 @@ function updateDeal(fieldname, value, callback) {
|
||||
},
|
||||
onError: (err) => {
|
||||
createToast({
|
||||
title: 'Error updating deal',
|
||||
text: err.messages?.[0],
|
||||
title: __('Error updating deal'),
|
||||
text: __(err.messages?.[0]),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
@ -413,8 +413,8 @@ function validateRequired(fieldname, value) {
|
||||
let meta = deal.data.all_fields || {}
|
||||
if (meta[fieldname]?.reqd && !value) {
|
||||
createToast({
|
||||
title: 'Error Updating Deal',
|
||||
text: `${meta[fieldname].label} is a required field`,
|
||||
title: __('Error Updating Deal'),
|
||||
text: __('{0} is a required field', [meta[fieldname].label]),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
@ -424,7 +424,7 @@ function validateRequired(fieldname, value) {
|
||||
}
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: 'Deals', route: { name: 'Deals' } }]
|
||||
let items = [{ label: __('Deals'), route: { name: 'Deals' } }]
|
||||
items.push({
|
||||
label: organization.value?.name,
|
||||
route: { name: 'Deal', params: { dealId: deal.data.name } },
|
||||
@ -435,22 +435,27 @@ const breadcrumbs = computed(() => {
|
||||
const tabIndex = ref(0)
|
||||
const tabs = [
|
||||
{
|
||||
name: 'Activity',
|
||||
label: 'Activity',
|
||||
icon: ActivityIcon,
|
||||
},
|
||||
{
|
||||
name: 'Emails',
|
||||
label: 'Emails',
|
||||
icon: EmailIcon,
|
||||
},
|
||||
{
|
||||
name: 'Calls',
|
||||
label: 'Calls',
|
||||
icon: PhoneIcon,
|
||||
},
|
||||
{
|
||||
name: 'Tasks',
|
||||
label: 'Tasks',
|
||||
icon: TaskIcon,
|
||||
},
|
||||
{
|
||||
name: 'Notes',
|
||||
label: 'Notes',
|
||||
icon: NoteIcon,
|
||||
},
|
||||
@ -504,7 +509,7 @@ const _contact = ref({})
|
||||
function contactOptions(contact) {
|
||||
let options = [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
icon: 'trash-2',
|
||||
onClick: () => removeContact(contact),
|
||||
},
|
||||
@ -512,7 +517,7 @@ function contactOptions(contact) {
|
||||
|
||||
if (!contact.is_primary) {
|
||||
options.push({
|
||||
label: 'Set as Primary Contact',
|
||||
label: __('Set as Primary Contact'),
|
||||
icon: h(SuccessIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => setPrimaryContact(contact),
|
||||
})
|
||||
@ -529,7 +534,7 @@ async function addContact(contact) {
|
||||
if (d) {
|
||||
deal_contacts.reload()
|
||||
createToast({
|
||||
title: 'Contact added',
|
||||
title: __('Contact added'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -544,7 +549,7 @@ async function removeContact(contact) {
|
||||
if (d) {
|
||||
deal_contacts.reload()
|
||||
createToast({
|
||||
title: 'Contact removed',
|
||||
title: __('Contact removed'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -559,7 +564,7 @@ async function setPrimaryContact(contact) {
|
||||
if (d) {
|
||||
deal_contacts.reload()
|
||||
createToast({
|
||||
title: 'Primary contact set',
|
||||
title: __('Primary contact set'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -578,12 +583,12 @@ function triggerCall() {
|
||||
let mobile_no = primaryContact.mobile_no || null
|
||||
|
||||
if (!primaryContact) {
|
||||
errorMessage('No primary contact set')
|
||||
errorMessage(__('No primary contact set'))
|
||||
return
|
||||
}
|
||||
|
||||
if (!mobile_no) {
|
||||
errorMessage('No mobile number set')
|
||||
errorMessage(__('No mobile number set'))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
v-if="dealsListView?.customListActions"
|
||||
:actions="dealsListView.customListActions"
|
||||
/>
|
||||
<Button variant="solid" label="Create" @click="showNewDialog = true">
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="__('Create')"
|
||||
@click="showNewDialog = true"
|
||||
>
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
@ -44,8 +48,8 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<DealsIcon class="h-10 w-10" />
|
||||
<span>No Deals Found</span>
|
||||
<Button label="Create" @click="showNewDialog = true">
|
||||
<span>{{ __('No Deals Found') }}</span>
|
||||
<Button :label="__('Create')" @click="showNewDialog = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</div>
|
||||
@ -54,8 +58,7 @@
|
||||
v-model="showNewDialog"
|
||||
:options="{
|
||||
size: '3xl',
|
||||
title: 'New Deal',
|
||||
actions: [{ label: 'Save', variant: 'solid' }],
|
||||
title: __('New Deal'),
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
@ -63,7 +66,11 @@
|
||||
</template>
|
||||
<template #actions="{ close }">
|
||||
<div class="flex flex-row-reverse gap-2">
|
||||
<Button variant="solid" label="Save" @click="createNewDeal(close)" />
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="__('Save')"
|
||||
@click="createNewDeal(close)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
@ -85,12 +92,13 @@ import {
|
||||
timeAgo,
|
||||
formatNumberIntoCurrency,
|
||||
formatTime,
|
||||
createToast,
|
||||
} from '@/utils'
|
||||
import { createResource, Breadcrumbs } from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, computed, reactive } from 'vue'
|
||||
|
||||
const breadcrumbs = [{ label: 'Deals', route: { name: 'Deals' } }]
|
||||
const breadcrumbs = [{ label: __('Deals'), route: { name: 'Deals' } }]
|
||||
|
||||
const { getUser } = usersStore()
|
||||
const { getOrganization } = organizationsStore()
|
||||
@ -139,7 +147,7 @@ const rows = computed(() => {
|
||||
? 'green'
|
||||
: 'orange'
|
||||
if (value == 'First Response Due') {
|
||||
value = timeAgo(deal.response_by)
|
||||
value = __(timeAgo(deal.response_by))
|
||||
tooltipText = dateFormat(deal.response_by, dateTooltipFormat)
|
||||
if (new Date(deal.response_by) < new Date()) {
|
||||
color = 'red'
|
||||
@ -168,7 +176,7 @@ const rows = computed(() => {
|
||||
} else if (['modified', 'creation'].includes(row)) {
|
||||
_rows[row] = {
|
||||
label: dateFormat(deal[row], dateTooltipFormat),
|
||||
timeAgo: timeAgo(deal[row]),
|
||||
timeAgo: __(timeAgo(deal[row])),
|
||||
}
|
||||
} else if (
|
||||
['first_response_time', 'first_responded_on', 'response_by'].includes(
|
||||
@ -181,7 +189,7 @@ const rows = computed(() => {
|
||||
timeAgo: deal[row]
|
||||
? row == 'first_response_time'
|
||||
? formatTime(deal[row])
|
||||
: timeAgo(deal[row])
|
||||
: __(timeAgo(deal[row]))
|
||||
: '',
|
||||
}
|
||||
}
|
||||
@ -218,7 +226,13 @@ function createNewDeal(close) {
|
||||
.submit(newDeal, {
|
||||
validate() {
|
||||
if (!newDeal.first_name) {
|
||||
return 'First name is required'
|
||||
createToast({
|
||||
title: __('Error creating deal'),
|
||||
text: __('First name is required'),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
return __('First name is required')
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
v-if="emailTemplatesListView?.customListActions"
|
||||
:actions="emailTemplatesListView.customListActions"
|
||||
/>
|
||||
<Button variant="solid" label="Create" @click="showEmailTemplateModal = true">
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="__('Create')"
|
||||
@click="showEmailTemplateModal = true"
|
||||
>
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
@ -48,7 +52,10 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<EmailIcon class="h-10 w-10" />
|
||||
<span>No Email Templates Found</span>
|
||||
<span>{{ __('No Email Templates Found') }}</span>
|
||||
<Button :label="__('Create')" @click="showEmailTemplateModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<EmailTemplateModal
|
||||
@ -70,7 +77,7 @@ import { Breadcrumbs } from 'frappe-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const breadcrumbs = [
|
||||
{ label: 'Email Templates', route: { name: 'Email Templates' } },
|
||||
{ label: __('Email Templates'), route: { name: 'Email Templates' } },
|
||||
]
|
||||
|
||||
const emailTemplatesListView = ref(null)
|
||||
@ -103,12 +110,12 @@ const rows = computed(() => {
|
||||
const showEmailTemplateModal = ref(false)
|
||||
|
||||
const emailTemplate = ref({
|
||||
subject: '',
|
||||
response: '',
|
||||
name: '',
|
||||
enabled: 1,
|
||||
owner: '',
|
||||
reference_doctype: 'CRM Deal',
|
||||
subject: '',
|
||||
response: '',
|
||||
name: '',
|
||||
enabled: 1,
|
||||
owner: '',
|
||||
reference_doctype: 'CRM Deal',
|
||||
})
|
||||
|
||||
function showEmailTemplate(name) {
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
</template>
|
||||
</Dropdown>
|
||||
<Button
|
||||
label="Convert to Deal"
|
||||
:label="__('Convert to Deal')"
|
||||
variant="solid"
|
||||
@click="showConvertToDealModal = true"
|
||||
/>
|
||||
@ -44,7 +44,7 @@
|
||||
<Activities
|
||||
ref="activities"
|
||||
doctype="CRM Lead"
|
||||
:title="tab.label"
|
||||
:title="tab.name"
|
||||
v-model:reload="reload"
|
||||
v-model="lead"
|
||||
/>
|
||||
@ -53,7 +53,7 @@
|
||||
<div
|
||||
class="flex h-10.5 items-center border-b px-5 py-2.5 text-lg font-semibold"
|
||||
>
|
||||
About this Lead
|
||||
{{ __('About this Lead') }}
|
||||
</div>
|
||||
<FileUploader
|
||||
@success="(file) => updateField('image', file.file_url)"
|
||||
@ -77,13 +77,13 @@
|
||||
{
|
||||
icon: 'upload',
|
||||
label: lead.data.image
|
||||
? 'Change image'
|
||||
: 'Upload image',
|
||||
? __('Change image')
|
||||
: __('Upload image'),
|
||||
onClick: openFileSelector,
|
||||
},
|
||||
{
|
||||
icon: 'trash-2',
|
||||
label: 'Remove image',
|
||||
label: __('Remove image'),
|
||||
onClick: () => updateField('image', ''),
|
||||
},
|
||||
],
|
||||
@ -110,45 +110,45 @@
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div class="flex gap-1.5">
|
||||
<Tooltip text="Make a call">
|
||||
<Tooltip :text="__('Make a call')">
|
||||
<Button
|
||||
class="h-7 w-7"
|
||||
@click="
|
||||
() =>
|
||||
lead.data.mobile_no
|
||||
? makeCall(lead.data.mobile_no)
|
||||
: errorMessage('No phone number set')
|
||||
: errorMessage(__('No phone number set'))
|
||||
"
|
||||
>
|
||||
<PhoneIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip text="Send an email">
|
||||
<Tooltip :text="__('Send an email')">
|
||||
<Button class="h-7 w-7">
|
||||
<EmailIcon
|
||||
class="h-4 w-4"
|
||||
@click="
|
||||
lead.data.email
|
||||
? openEmailBox()
|
||||
: errorMessage('No email set')
|
||||
: errorMessage(__('No email set'))
|
||||
"
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip text="Go to website">
|
||||
<Tooltip :text="__('Go to website')">
|
||||
<Button class="h-7 w-7">
|
||||
<LinkIcon
|
||||
class="h-4 w-4"
|
||||
@click="
|
||||
lead.data.website
|
||||
? openWebsite(lead.data.website)
|
||||
: errorMessage('No website set')
|
||||
: errorMessage(__('No website set'))
|
||||
"
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<ErrorMessage :message="error" />
|
||||
<ErrorMessage :message="__(error)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -190,11 +190,11 @@
|
||||
<Dialog
|
||||
v-model="showConvertToDealModal"
|
||||
:options="{
|
||||
title: 'Convert to Deal',
|
||||
title: __('Convert to Deal'),
|
||||
size: 'xl',
|
||||
actions: [
|
||||
{
|
||||
label: 'Convert',
|
||||
label: __('Convert'),
|
||||
variant: 'solid',
|
||||
onClick: convertToDeal,
|
||||
},
|
||||
@ -204,11 +204,11 @@
|
||||
<template #body-content>
|
||||
<div class="mb-4 flex items-center gap-2 text-gray-600">
|
||||
<OrganizationsIcon class="h-4 w-4" />
|
||||
<label class="block text-base"> Organization </label>
|
||||
<label class="block text-base">{{ __('Organization') }}</label>
|
||||
</div>
|
||||
<div class="ml-6">
|
||||
<div class="flex items-center justify-between text-base">
|
||||
<div>Choose Existing</div>
|
||||
<div>{{ __('Choose Existing') }}</div>
|
||||
<Switch v-model="existingOrganizationChecked" />
|
||||
</div>
|
||||
<Link
|
||||
@ -221,17 +221,21 @@
|
||||
@change="(data) => (existingOrganization = data)"
|
||||
/>
|
||||
<div v-else class="mt-2.5 text-base">
|
||||
New organization will be created based on the data in details section
|
||||
{{
|
||||
__(
|
||||
'New organization will be created based on the data in details section'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 mt-6 flex items-center gap-2 text-gray-600">
|
||||
<ContactsIcon class="h-4 w-4" />
|
||||
<label class="block text-base"> Contact </label>
|
||||
<label class="block text-base">{{ __('Contact') }}</label>
|
||||
</div>
|
||||
<div class="ml-6">
|
||||
<div class="flex items-center justify-between text-base">
|
||||
<div>Choose Existing</div>
|
||||
<div>{{ __('Choose Existing') }}</div>
|
||||
<Switch v-model="existingContactChecked" />
|
||||
</div>
|
||||
<Link
|
||||
@ -244,7 +248,7 @@
|
||||
@change="(data) => (existingContact = data)"
|
||||
/>
|
||||
<div v-else class="mt-2.5 text-base">
|
||||
New contact will be created based on the person's details
|
||||
{{ __("New contact will be created based on the person's details") }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -352,7 +356,7 @@ function updateLead(fieldname, value, callback) {
|
||||
lead.reload()
|
||||
reload.value = true
|
||||
createToast({
|
||||
title: 'Lead updated',
|
||||
title: __('Lead updated'),
|
||||
icon: 'check',
|
||||
iconClasses: 'text-green-600',
|
||||
})
|
||||
@ -360,8 +364,8 @@ function updateLead(fieldname, value, callback) {
|
||||
},
|
||||
onError: (err) => {
|
||||
createToast({
|
||||
title: 'Error updating lead',
|
||||
text: err.messages?.[0],
|
||||
title: __('Error updating lead'),
|
||||
text: __(err.messages?.[0]),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
@ -373,8 +377,8 @@ function validateRequired(fieldname, value) {
|
||||
let meta = lead.data.all_fields || {}
|
||||
if (meta[fieldname]?.reqd && !value) {
|
||||
createToast({
|
||||
title: 'Error Updating Lead',
|
||||
text: `${meta[fieldname].label} is a required field`,
|
||||
title: __('Error Updating Lead'),
|
||||
text: __('{0} is a required field', [meta[fieldname].label]),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
@ -384,7 +388,7 @@ function validateRequired(fieldname, value) {
|
||||
}
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: 'Leads', route: { name: 'Leads' } }]
|
||||
let items = [{ label: __('Leads'), route: { name: 'Leads' } }]
|
||||
items.push({
|
||||
label: lead.data.lead_name,
|
||||
route: { name: 'Lead', params: { leadId: lead.data.name } },
|
||||
@ -395,23 +399,28 @@ const breadcrumbs = computed(() => {
|
||||
const tabIndex = ref(0)
|
||||
const tabs = [
|
||||
{
|
||||
label: 'Activity',
|
||||
name: 'Activity',
|
||||
label: __('Activity'),
|
||||
icon: ActivityIcon,
|
||||
},
|
||||
{
|
||||
label: 'Emails',
|
||||
name: 'Emails',
|
||||
label: __('Emails'),
|
||||
icon: EmailIcon,
|
||||
},
|
||||
{
|
||||
label: 'Calls',
|
||||
name: 'Calls',
|
||||
label: __('Calls'),
|
||||
icon: PhoneIcon,
|
||||
},
|
||||
{
|
||||
label: 'Tasks',
|
||||
name: 'Tasks',
|
||||
label: __('Tasks'),
|
||||
icon: TaskIcon,
|
||||
},
|
||||
{
|
||||
label: 'Notes',
|
||||
name: 'Notes',
|
||||
label: __('Notes'),
|
||||
icon: NoteIcon,
|
||||
},
|
||||
]
|
||||
@ -419,7 +428,7 @@ const tabs = [
|
||||
function validateFile(file) {
|
||||
let extn = file.name.split('.').pop().toLowerCase()
|
||||
if (!['png', 'jpg', 'jpeg'].includes(extn)) {
|
||||
return 'Only PNG and JPG images are allowed'
|
||||
return __('Only PNG and JPG images are allowed')
|
||||
}
|
||||
}
|
||||
|
||||
@ -457,8 +466,8 @@ async function convertToDeal(updated) {
|
||||
|
||||
if (existingContactChecked.value && !existingContact.value) {
|
||||
createToast({
|
||||
title: 'Error',
|
||||
text: 'Please select an existing contact',
|
||||
title: __('Error'),
|
||||
text: __('Please select an existing contact'),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
@ -467,8 +476,8 @@ async function convertToDeal(updated) {
|
||||
|
||||
if (existingOrganizationChecked.value && !existingOrganization.value) {
|
||||
createToast({
|
||||
title: 'Error',
|
||||
text: 'Please select an existing organization',
|
||||
title: __('Error'),
|
||||
text: __('Please select an existing organization'),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
v-if="leadsListView?.customListActions"
|
||||
:actions="leadsListView.customListActions"
|
||||
/>
|
||||
<Button variant="solid" label="Create" @click="showNewDialog = true">
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="__('Create')"
|
||||
@click="showNewDialog = true"
|
||||
>
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
@ -45,8 +49,8 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<LeadsIcon class="h-10 w-10" />
|
||||
<span>No Leads Found</span>
|
||||
<Button label="Create" @click="showNewDialog = true">
|
||||
<span>{{ __('No Leads Found') }}</span>
|
||||
<Button :label="__('Create')" @click="showNewDialog = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</div>
|
||||
@ -55,8 +59,7 @@
|
||||
v-model="showNewDialog"
|
||||
:options="{
|
||||
size: '3xl',
|
||||
title: 'New Lead',
|
||||
actions: [{ label: 'Save', variant: 'solid' }],
|
||||
title: __('New Lead'),
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
@ -64,7 +67,11 @@
|
||||
</template>
|
||||
<template #actions="{ close }">
|
||||
<div class="flex flex-row-reverse gap-2">
|
||||
<Button variant="solid" label="Save" @click="createNewLead(close)" />
|
||||
<Button
|
||||
variant="solid"
|
||||
:label="__('Save')"
|
||||
@click="createNewLead(close)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
@ -80,12 +87,18 @@ import ViewControls from '@/components/ViewControls.vue'
|
||||
import { usersStore } from '@/stores/users'
|
||||
import { organizationsStore } from '@/stores/organizations'
|
||||
import { statusesStore } from '@/stores/statuses'
|
||||
import { dateFormat, dateTooltipFormat, timeAgo, formatTime } from '@/utils'
|
||||
import {
|
||||
dateFormat,
|
||||
dateTooltipFormat,
|
||||
timeAgo,
|
||||
formatTime,
|
||||
createToast,
|
||||
} from '@/utils'
|
||||
import { createResource, Breadcrumbs } from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, computed, reactive } from 'vue'
|
||||
|
||||
const breadcrumbs = [{ label: 'Leads', route: { name: 'Leads' } }]
|
||||
const breadcrumbs = [{ label: __('Leads'), route: { name: 'Leads' } }]
|
||||
|
||||
const { getUser } = usersStore()
|
||||
const { getOrganization } = organizationsStore()
|
||||
@ -136,7 +149,7 @@ const rows = computed(() => {
|
||||
? 'green'
|
||||
: 'orange'
|
||||
if (value == 'First Response Due') {
|
||||
value = timeAgo(lead.response_by)
|
||||
value = __(timeAgo(lead.response_by))
|
||||
tooltipText = dateFormat(lead.response_by, dateTooltipFormat)
|
||||
if (new Date(lead.response_by) < new Date()) {
|
||||
color = 'red'
|
||||
@ -165,7 +178,7 @@ const rows = computed(() => {
|
||||
} else if (['modified', 'creation'].includes(row)) {
|
||||
_rows[row] = {
|
||||
label: dateFormat(lead[row], dateTooltipFormat),
|
||||
timeAgo: timeAgo(lead[row]),
|
||||
timeAgo: __(timeAgo(lead[row])),
|
||||
}
|
||||
} else if (
|
||||
['first_response_time', 'first_responded_on', 'response_by'].includes(
|
||||
@ -178,7 +191,7 @@ const rows = computed(() => {
|
||||
timeAgo: lead[row]
|
||||
? row == 'first_response_time'
|
||||
? formatTime(lead[row])
|
||||
: timeAgo(lead[row])
|
||||
: __(timeAgo(lead[row]))
|
||||
: '',
|
||||
}
|
||||
}
|
||||
@ -219,7 +232,13 @@ function createNewLead(close) {
|
||||
.submit(newLead, {
|
||||
validate() {
|
||||
if (!newLead.first_name) {
|
||||
return 'First name is required'
|
||||
createToast({
|
||||
title: __('Error creating lead'),
|
||||
text: __('First name is required'),
|
||||
icon: 'x',
|
||||
iconClasses: 'text-red-600',
|
||||
})
|
||||
return __('First name is required')
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<Breadcrumbs :items="breadcrumbs" />
|
||||
</template>
|
||||
<template #right-header>
|
||||
<Button variant="solid" label="Create" @click="createNote">
|
||||
<Button variant="solid" :label="__('Create')" @click="createNote">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
@ -16,7 +16,7 @@
|
||||
doctype="FCRM Note"
|
||||
:options="{
|
||||
hideColumnsButton: true,
|
||||
defaultViewName: 'Notes View',
|
||||
defaultViewName: __('Notes View'),
|
||||
}"
|
||||
/>
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
@ -36,8 +36,8 @@
|
||||
<Dropdown
|
||||
:options="[
|
||||
{
|
||||
label: __('Delete'),
|
||||
icon: 'trash-2',
|
||||
label: 'Delete',
|
||||
onClick: () => deleteNote(note.name),
|
||||
},
|
||||
]"
|
||||
@ -66,7 +66,7 @@
|
||||
</div>
|
||||
<Tooltip :text="dateFormat(note.modified, dateTooltipFormat)">
|
||||
<div class="text-sm text-gray-700">
|
||||
{{ timeAgo(note.modified) }}
|
||||
{{ __(timeAgo(note.modified)) }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -88,8 +88,8 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<NoteIcon class="h-10 w-10" />
|
||||
<span>No Notes Found</span>
|
||||
<Button label="Create" @click="createNote">
|
||||
<span>{{ __('No Notes Found') }}</span>
|
||||
<Button :label="__('Create')" @click="createNote">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</div>
|
||||
@ -121,13 +121,7 @@ import { ref, watch } from 'vue'
|
||||
|
||||
const { getUser } = usersStore()
|
||||
|
||||
const list = {
|
||||
title: 'Notes',
|
||||
plural_label: 'Notes',
|
||||
singular_label: 'Note',
|
||||
}
|
||||
|
||||
const breadcrumbs = [{ label: list.title, route: { name: 'Notes' } }]
|
||||
const breadcrumbs = [{ label: __('Notes'), route: { name: 'Notes' } }]
|
||||
|
||||
const showNoteModal = ref(false)
|
||||
const currentNote = ref(null)
|
||||
|
||||
@ -27,13 +27,13 @@
|
||||
{
|
||||
icon: 'upload',
|
||||
label: organization.doc.organization_logo
|
||||
? 'Change image'
|
||||
: 'Upload image',
|
||||
? __('Change image')
|
||||
: __('Upload image'),
|
||||
onClick: openFileSelector,
|
||||
},
|
||||
{
|
||||
icon: 'trash-2',
|
||||
label: 'Remove image',
|
||||
label: __('Remove image'),
|
||||
onClick: () => changeOrganizationImage(''),
|
||||
},
|
||||
],
|
||||
@ -118,7 +118,7 @@
|
||||
organization.doc.annual_revenue
|
||||
"
|
||||
variant="ghost"
|
||||
label="More"
|
||||
:label="__('More')"
|
||||
class="-ml-1 cursor-pointer hover:text-gray-900"
|
||||
@click="
|
||||
() => {
|
||||
@ -130,7 +130,7 @@
|
||||
</div>
|
||||
<div class="mt-2 flex gap-1.5">
|
||||
<Button
|
||||
label="Edit"
|
||||
:label="__('Edit')"
|
||||
size="sm"
|
||||
@click="
|
||||
() => {
|
||||
@ -144,7 +144,7 @@
|
||||
</template>
|
||||
</Button>
|
||||
<Button
|
||||
label="Delete"
|
||||
:label="__('Delete')"
|
||||
theme="red"
|
||||
size="sm"
|
||||
@click="deleteOrganization"
|
||||
@ -154,7 +154,7 @@
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<ErrorMessage class="mt-2" :message="error" />
|
||||
<ErrorMessage class="mt-2" :message="__(error)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -166,7 +166,7 @@
|
||||
:class="{ 'text-gray-900': selected }"
|
||||
>
|
||||
<component v-if="tab.icon" :is="tab.icon" class="h-5" />
|
||||
{{ tab.label }}
|
||||
{{ __(tab.label) }}
|
||||
<Badge
|
||||
class="group-hover:bg-gray-900"
|
||||
:class="[selected ? 'bg-gray-900' : 'bg-gray-600']"
|
||||
@ -199,7 +199,7 @@
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center space-y-3">
|
||||
<component :is="tab.icon" class="!h-10 !w-10" />
|
||||
<div>No {{ tab.label }} Found</div>
|
||||
<div>{{ __('No {0} Found', [__(tab.label)]) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -268,7 +268,7 @@ const organization = createDocumentResource({
|
||||
})
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: 'Organizations', route: { name: 'Organizations' } }]
|
||||
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
|
||||
items.push({
|
||||
label: props.organizationId,
|
||||
route: {
|
||||
@ -282,7 +282,7 @@ const breadcrumbs = computed(() => {
|
||||
function validateFile(file) {
|
||||
let extn = file.name.split('.').pop().toLowerCase()
|
||||
if (!['png', 'jpg', 'jpeg'].includes(extn)) {
|
||||
return 'Only PNG and JPG images are allowed'
|
||||
return __('Only PNG and JPG images are allowed')
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,11 +298,11 @@ async function changeOrganizationImage(file) {
|
||||
|
||||
async function deleteOrganization() {
|
||||
$dialog({
|
||||
title: 'Delete organization',
|
||||
message: 'Are you sure you want to delete this organization?',
|
||||
title: __('Delete organization'),
|
||||
message: __('Are you sure you want to delete this organization?'),
|
||||
actions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
label: __('Delete'),
|
||||
theme: 'red',
|
||||
variant: 'solid',
|
||||
async onClick(close) {
|
||||
@ -416,7 +416,7 @@ function getDealRowObject(deal) {
|
||||
},
|
||||
modified: {
|
||||
label: dateFormat(deal.modified, dateTooltipFormat),
|
||||
timeAgo: timeAgo(deal.modified),
|
||||
timeAgo: __(timeAgo(deal.modified)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -437,44 +437,44 @@ function getContactRowObject(contact) {
|
||||
},
|
||||
modified: {
|
||||
label: dateFormat(contact.modified, dateTooltipFormat),
|
||||
timeAgo: timeAgo(contact.modified),
|
||||
timeAgo: __(timeAgo(contact.modified)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const dealColumns = [
|
||||
{
|
||||
label: 'Organization',
|
||||
label: __('Organization'),
|
||||
key: 'organization',
|
||||
width: '11rem',
|
||||
},
|
||||
{
|
||||
label: 'Amount',
|
||||
label: __('Amount'),
|
||||
key: 'annual_revenue',
|
||||
width: '9rem',
|
||||
},
|
||||
{
|
||||
label: 'Status',
|
||||
label: __('Status'),
|
||||
key: 'status',
|
||||
width: '10rem',
|
||||
},
|
||||
{
|
||||
label: 'Email',
|
||||
label: __('Email'),
|
||||
key: 'email',
|
||||
width: '12rem',
|
||||
},
|
||||
{
|
||||
label: 'Mobile no',
|
||||
label: __('Mobile no'),
|
||||
key: 'mobile_no',
|
||||
width: '11rem',
|
||||
},
|
||||
{
|
||||
label: 'Deal owner',
|
||||
label: __('Deal owner'),
|
||||
key: 'deal_owner',
|
||||
width: '10rem',
|
||||
},
|
||||
{
|
||||
label: 'Last modified',
|
||||
label: __('Last modified'),
|
||||
key: 'modified',
|
||||
width: '8rem',
|
||||
},
|
||||
@ -482,27 +482,27 @@ const dealColumns = [
|
||||
|
||||
const contactColumns = [
|
||||
{
|
||||
label: 'Name',
|
||||
label: __('Name'),
|
||||
key: 'full_name',
|
||||
width: '17rem',
|
||||
},
|
||||
{
|
||||
label: 'Email',
|
||||
label: __('Email'),
|
||||
key: 'email',
|
||||
width: '12rem',
|
||||
},
|
||||
{
|
||||
label: 'Phone',
|
||||
label: __('Phone'),
|
||||
key: 'mobile_no',
|
||||
width: '12rem',
|
||||
},
|
||||
{
|
||||
label: 'Organization',
|
||||
label: __('Organization'),
|
||||
key: 'company_name',
|
||||
width: '12rem',
|
||||
},
|
||||
{
|
||||
label: 'Last modified',
|
||||
label: __('Last modified'),
|
||||
key: 'modified',
|
||||
width: '8rem',
|
||||
},
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
/>
|
||||
<Button
|
||||
variant="solid"
|
||||
label="Create"
|
||||
:label="__('Create')"
|
||||
@click="showOrganizationModal = true"
|
||||
>
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
@ -51,8 +51,8 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<OrganizationsIcon class="h-10 w-10" />
|
||||
<span>No Organizations Found</span>
|
||||
<Button label="Create" @click="showOrganizationModal = true">
|
||||
<span>{{ __('No Organizations Found') }}</span>
|
||||
<Button :label="__('Create')" @click="showOrganizationModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</div>
|
||||
@ -88,10 +88,10 @@ const currentOrganization = computed(() => {
|
||||
})
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: 'Organizations', route: { name: 'Organizations' } }]
|
||||
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
|
||||
if (!currentOrganization.value) return items
|
||||
items.push({
|
||||
label: currentOrganization.value.name,
|
||||
label: __(currentOrganization.value.name),
|
||||
route: {
|
||||
name: 'Organization',
|
||||
params: { organizationId: currentOrganization.value.name },
|
||||
@ -126,7 +126,7 @@ const rows = computed(() => {
|
||||
} else if (['modified', 'creation'].includes(row)) {
|
||||
_rows[row] = {
|
||||
label: dateFormat(organization[row], dateTooltipFormat),
|
||||
timeAgo: timeAgo(organization[row]),
|
||||
timeAgo: __(timeAgo(organization[row])),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
v-if="tasksListView?.customListActions"
|
||||
:actions="tasksListView.customListActions"
|
||||
/>
|
||||
<Button variant="solid" label="Create" @click="createTask">
|
||||
<Button variant="solid" :label="__('Create')" @click="createTask">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</template>
|
||||
@ -45,7 +45,10 @@
|
||||
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||
>
|
||||
<EmailIcon class="h-10 w-10" />
|
||||
<span>No Tasks Found</span>
|
||||
<span>{{ __('No Tasks Found') }}</span>
|
||||
<Button :label="__('Create')" @click="showTaskModal = true">
|
||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<TaskModal v-model="showTaskModal" v-model:reloadTasks="tasks" :task="task" />
|
||||
@ -63,7 +66,7 @@ import { dateFormat, dateTooltipFormat, timeAgo } from '@/utils'
|
||||
import { Breadcrumbs } from 'frappe-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const breadcrumbs = [{ label: 'Tasks', route: { name: 'Tasks' } }]
|
||||
const breadcrumbs = [{ label: __('Tasks'), route: { name: 'Tasks' } }]
|
||||
|
||||
const { getUser } = usersStore()
|
||||
|
||||
@ -86,7 +89,7 @@ const rows = computed(() => {
|
||||
if (['modified', 'creation'].includes(row)) {
|
||||
_rows[row] = {
|
||||
label: dateFormat(task[row], dateTooltipFormat),
|
||||
timeAgo: timeAgo(task[row]),
|
||||
timeAgo: __(timeAgo(task[row])),
|
||||
}
|
||||
} else if (row == 'assigned_to') {
|
||||
_rows[row] = {
|
||||
|
||||
47
frontend/src/translation.js
Normal file
47
frontend/src/translation.js
Normal file
@ -0,0 +1,47 @@
|
||||
import { createResource } from 'frappe-ui'
|
||||
|
||||
export default function translationPlugin(app) {
|
||||
app.config.globalProperties.__ = translate
|
||||
window.__ = translate
|
||||
if (!window.translatedMessages) fetchTranslations()
|
||||
}
|
||||
|
||||
function format(message, replace) {
|
||||
return message.replace(/{(\d+)}/g, function (match, number) {
|
||||
return typeof replace[number] != 'undefined' ? replace[number] : match
|
||||
})
|
||||
}
|
||||
|
||||
function translate(message, replace, context = null) {
|
||||
let translatedMessages = window.translatedMessages || {}
|
||||
let translatedMessage = ''
|
||||
|
||||
if (context) {
|
||||
let key = `${message}:${context}`
|
||||
if (translatedMessages[key]) {
|
||||
translatedMessage = translatedMessages[key]
|
||||
}
|
||||
}
|
||||
|
||||
if (!translatedMessage) {
|
||||
translatedMessage = translatedMessages[message] || message
|
||||
}
|
||||
|
||||
const hasPlaceholders = /{\d+}/.test(message)
|
||||
if (!hasPlaceholders) {
|
||||
return translatedMessage
|
||||
}
|
||||
|
||||
return format(translatedMessage, replace)
|
||||
}
|
||||
|
||||
function fetchTranslations(lang) {
|
||||
createResource({
|
||||
url: 'crm.api.get_translations',
|
||||
cache: 'translations',
|
||||
auto: true,
|
||||
transform: (data) => {
|
||||
window.translatedMessages = data
|
||||
},
|
||||
})
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user