fix: update scemantic classes fro dark mode manually
This commit is contained in:
parent
e9afb11407
commit
7f62371837
@ -5,6 +5,7 @@ from frappe import _
|
||||
from bs4 import BeautifulSoup
|
||||
from crm.fcrm.doctype.crm_notification.crm_notification import notify_user
|
||||
|
||||
|
||||
def on_update(self, method):
|
||||
notify_mentions(self)
|
||||
|
||||
@ -24,25 +25,31 @@ def notify_mentions(doc):
|
||||
doctype = doc.reference_doctype
|
||||
if doctype.startswith("CRM "):
|
||||
doctype = doctype[4:].lower()
|
||||
name = reference_doc.lead_name or name if doctype == "lead" else reference_doc.organization or reference_doc.lead_name or name
|
||||
name = (
|
||||
reference_doc.lead_name
|
||||
if doctype == "lead"
|
||||
else reference_doc.organization or reference_doc.lead_name
|
||||
)
|
||||
notification_text = f"""
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">{ owner }</span>
|
||||
<div class="mb-2 leading-5 text-ink-gray-5">
|
||||
<span class="font-medium text-ink-gray-9">{ owner }</span>
|
||||
<span>{ _('mentioned you in {0}').format(doctype) }</span>
|
||||
<span class="font-medium text-gray-900">{ name }</span>
|
||||
<span class="font-medium text-ink-gray-9">{ name }</span>
|
||||
</div>
|
||||
"""
|
||||
notify_user({
|
||||
"owner": doc.owner,
|
||||
"assigned_to": mention.email,
|
||||
"notification_type": "Mention",
|
||||
"message": doc.content,
|
||||
"notification_text": notification_text,
|
||||
"reference_doctype": "Comment",
|
||||
"reference_docname": doc.name,
|
||||
"redirect_to_doctype": doc.reference_doctype,
|
||||
"redirect_to_docname": doc.reference_name,
|
||||
})
|
||||
notify_user(
|
||||
{
|
||||
"owner": doc.owner,
|
||||
"assigned_to": mention.email,
|
||||
"notification_type": "Mention",
|
||||
"message": doc.content,
|
||||
"notification_text": notification_text,
|
||||
"reference_doctype": "Comment",
|
||||
"reference_docname": doc.name,
|
||||
"redirect_to_doctype": doc.reference_doctype,
|
||||
"redirect_to_docname": doc.reference_name,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def extract_mentions(html):
|
||||
@ -56,39 +63,42 @@ def extract_mentions(html):
|
||||
)
|
||||
return mentions
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_attachments(name: str, attachments: Iterable[str | dict]) -> None:
|
||||
"""Add attachments to the given Comment
|
||||
"""Add attachments to the given Comment
|
||||
|
||||
:param name: Comment name
|
||||
:param attachments: File names or dicts with keys "fname" and "fcontent"
|
||||
"""
|
||||
# loop through attachments
|
||||
for a in attachments:
|
||||
if isinstance(a, str):
|
||||
attach = frappe.db.get_value("File", {"name": a}, ["file_url", "is_private"], as_dict=1)
|
||||
file_args = {
|
||||
"file_url": attach.file_url,
|
||||
"is_private": attach.is_private,
|
||||
}
|
||||
elif isinstance(a, dict) and "fcontent" in a and "fname" in a:
|
||||
# dict returned by frappe.attach_print()
|
||||
file_args = {
|
||||
"file_name": a["fname"],
|
||||
"content": a["fcontent"],
|
||||
"is_private": 1,
|
||||
}
|
||||
else:
|
||||
continue
|
||||
:param name: Comment name
|
||||
:param attachments: File names or dicts with keys "fname" and "fcontent"
|
||||
"""
|
||||
# loop through attachments
|
||||
for a in attachments:
|
||||
if isinstance(a, str):
|
||||
attach = frappe.db.get_value(
|
||||
"File", {"name": a}, ["file_url", "is_private"], as_dict=1
|
||||
)
|
||||
file_args = {
|
||||
"file_url": attach.file_url,
|
||||
"is_private": attach.is_private,
|
||||
}
|
||||
elif isinstance(a, dict) and "fcontent" in a and "fname" in a:
|
||||
# dict returned by frappe.attach_print()
|
||||
file_args = {
|
||||
"file_name": a["fname"],
|
||||
"content": a["fcontent"],
|
||||
"is_private": 1,
|
||||
}
|
||||
else:
|
||||
continue
|
||||
|
||||
file_args.update(
|
||||
{
|
||||
"attached_to_doctype": "Comment",
|
||||
"attached_to_name": name,
|
||||
"folder": "Home/Attachments",
|
||||
}
|
||||
)
|
||||
file_args.update(
|
||||
{
|
||||
"attached_to_doctype": "Comment",
|
||||
"attached_to_name": name,
|
||||
"folder": "Home/Attachments",
|
||||
}
|
||||
)
|
||||
|
||||
_file = frappe.new_doc("File")
|
||||
_file.update(file_args)
|
||||
_file.save(ignore_permissions=True)
|
||||
_file = frappe.new_doc("File")
|
||||
_file.update(file_args)
|
||||
_file.save(ignore_permissions=True)
|
||||
|
||||
191
crm/api/todo.py
191
crm/api/todo.py
@ -2,102 +2,131 @@ import frappe
|
||||
from frappe import _
|
||||
from crm.fcrm.doctype.crm_notification.crm_notification import notify_user
|
||||
|
||||
def after_insert(doc, method):
|
||||
if doc.reference_type in ["CRM Lead", "CRM Deal"] and doc.reference_name and doc.allocated_to:
|
||||
fieldname = "lead_owner" if doc.reference_type == "CRM Lead" else "deal_owner"
|
||||
lead_owner = frappe.db.get_value(doc.reference_type, doc.reference_name, fieldname)
|
||||
if not lead_owner:
|
||||
frappe.db.set_value(doc.reference_type, doc.reference_name, fieldname, doc.allocated_to)
|
||||
|
||||
if doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"] and doc.reference_name and doc.allocated_to:
|
||||
notify_assigned_user(doc)
|
||||
def after_insert(doc, method):
|
||||
if (
|
||||
doc.reference_type in ["CRM Lead", "CRM Deal"]
|
||||
and doc.reference_name
|
||||
and doc.allocated_to
|
||||
):
|
||||
fieldname = "lead_owner" if doc.reference_type == "CRM Lead" else "deal_owner"
|
||||
lead_owner = frappe.db.get_value(
|
||||
doc.reference_type, doc.reference_name, fieldname
|
||||
)
|
||||
if not lead_owner:
|
||||
frappe.db.set_value(
|
||||
doc.reference_type, doc.reference_name, fieldname, doc.allocated_to
|
||||
)
|
||||
|
||||
if (
|
||||
doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"]
|
||||
and doc.reference_name
|
||||
and doc.allocated_to
|
||||
):
|
||||
notify_assigned_user(doc)
|
||||
|
||||
|
||||
def on_update(doc, method):
|
||||
if doc.has_value_changed("status") and doc.status == "Cancelled" and doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"] and doc.reference_name and doc.allocated_to:
|
||||
notify_assigned_user(doc, is_cancelled=True)
|
||||
if (
|
||||
doc.has_value_changed("status")
|
||||
and doc.status == "Cancelled"
|
||||
and doc.reference_type in ["CRM Lead", "CRM Deal", "CRM Task"]
|
||||
and doc.reference_name
|
||||
and doc.allocated_to
|
||||
):
|
||||
notify_assigned_user(doc, is_cancelled=True)
|
||||
|
||||
|
||||
def notify_assigned_user(doc, is_cancelled=False):
|
||||
_doc = frappe.get_doc(doc.reference_type, doc.reference_name)
|
||||
owner = frappe.get_cached_value("User", frappe.session.user, "full_name")
|
||||
notification_text = get_notification_text(owner, doc, _doc, is_cancelled)
|
||||
_doc = frappe.get_doc(doc.reference_type, doc.reference_name)
|
||||
owner = frappe.get_cached_value("User", frappe.session.user, "full_name")
|
||||
notification_text = get_notification_text(owner, doc, _doc, is_cancelled)
|
||||
|
||||
message = _("Your assignment on {0} {1} has been removed by {2}").format(
|
||||
doc.reference_type,
|
||||
doc.reference_name,
|
||||
owner
|
||||
) if is_cancelled else _("{0} assigned a {1} {2} to you").format(
|
||||
owner,
|
||||
doc.reference_type,
|
||||
doc.reference_name
|
||||
)
|
||||
message = (
|
||||
_("Your assignment on {0} {1} has been removed by {2}").format(
|
||||
doc.reference_type, doc.reference_name, owner
|
||||
)
|
||||
if is_cancelled
|
||||
else _("{0} assigned a {1} {2} to you").format(
|
||||
owner, doc.reference_type, doc.reference_name
|
||||
)
|
||||
)
|
||||
|
||||
redirect_to_doctype, redirect_to_name = get_redirect_to_doc(doc)
|
||||
redirect_to_doctype, redirect_to_name = get_redirect_to_doc(doc)
|
||||
|
||||
notify_user(
|
||||
{
|
||||
"owner": frappe.session.user,
|
||||
"assigned_to": doc.allocated_to,
|
||||
"notification_type": "Assignment",
|
||||
"message": message,
|
||||
"notification_text": notification_text,
|
||||
"reference_doctype": doc.reference_type,
|
||||
"reference_docname": doc.reference_name,
|
||||
"redirect_to_doctype": redirect_to_doctype,
|
||||
"redirect_to_docname": redirect_to_name,
|
||||
}
|
||||
)
|
||||
|
||||
notify_user({
|
||||
"owner": frappe.session.user,
|
||||
"assigned_to": doc.allocated_to,
|
||||
"notification_type": "Assignment",
|
||||
"message": message,
|
||||
"notification_text": notification_text,
|
||||
"reference_doctype": doc.reference_type,
|
||||
"reference_docname": doc.reference_name,
|
||||
"redirect_to_doctype": redirect_to_doctype,
|
||||
"redirect_to_docname": redirect_to_name,
|
||||
})
|
||||
|
||||
def get_notification_text(owner, doc, reference_doc, is_cancelled=False):
|
||||
name = doc.reference_name
|
||||
doctype = doc.reference_type
|
||||
name = doc.reference_name
|
||||
doctype = doc.reference_type
|
||||
|
||||
if doctype.startswith("CRM "):
|
||||
doctype = doctype[4:].lower()
|
||||
if doctype.startswith("CRM "):
|
||||
doctype = doctype[4:].lower()
|
||||
|
||||
if doctype in ["lead", "deal"]:
|
||||
name = reference_doc.lead_name or name if doctype == "lead" else reference_doc.organization or reference_doc.lead_name or name
|
||||
if doctype in ["lead", "deal"]:
|
||||
name = (
|
||||
reference_doc.lead_name or name
|
||||
if doctype == "lead"
|
||||
else reference_doc.organization or reference_doc.lead_name or name
|
||||
)
|
||||
|
||||
if is_cancelled:
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span>{ _('Your assignment on {0} {1} has been removed by {2}').format(
|
||||
doctype,
|
||||
f'<span class="font-medium text-gray-900">{ name }</span>',
|
||||
f'<span class="font-medium text-gray-900">{ owner }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
if is_cancelled:
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-ink-gray-5">
|
||||
<span>{ _('Your assignment on {0} {1} has been removed by {2}').format(
|
||||
doctype,
|
||||
f'<span class="font-medium text-ink-gray-9">{ name }</span>',
|
||||
f'<span class="font-medium text-ink-gray-9">{ owner }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">{ owner }</span>
|
||||
<span>{ _('assigned a {0} {1} to you').format(
|
||||
doctype,
|
||||
f'<span class="font-medium text-gray-900">{ name }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-ink-gray-5">
|
||||
<span class="font-medium text-ink-gray-9">{ owner }</span>
|
||||
<span>{ _('assigned a {0} {1} to you').format(
|
||||
doctype,
|
||||
f'<span class="font-medium text-ink-gray-9">{ name }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
|
||||
if doctype == "task":
|
||||
if is_cancelled:
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-ink-gray-5">
|
||||
<span>{ _('Your assignment on task {0} has been removed by {1}').format(
|
||||
f'<span class="font-medium text-ink-gray-9">{ reference_doc.title }</span>',
|
||||
f'<span class="font-medium text-ink-gray-9">{ owner }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-ink-gray-5">
|
||||
<span class="font-medium text-ink-gray-9">{ owner }</span>
|
||||
<span>{ _('assigned a new task {0} to you').format(
|
||||
f'<span class="font-medium text-ink-gray-9">{ reference_doc.title }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
|
||||
if doctype == "task":
|
||||
if is_cancelled:
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span>{ _('Your assignment on task {0} has been removed by {1}').format(
|
||||
f'<span class="font-medium text-gray-900">{ reference_doc.title }</span>',
|
||||
f'<span class="font-medium text-gray-900">{ owner }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
return f"""
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">{ owner }</span>
|
||||
<span>{ _('assigned a new task {0} to you').format(
|
||||
f'<span class="font-medium text-gray-900">{ reference_doc.title }</span>'
|
||||
) }</span>
|
||||
</div>
|
||||
"""
|
||||
|
||||
def get_redirect_to_doc(doc):
|
||||
if doc.reference_type == "CRM Task":
|
||||
reference_doc = frappe.get_doc(doc.reference_type, doc.reference_name)
|
||||
return reference_doc.reference_doctype, reference_doc.reference_docname
|
||||
if doc.reference_type == "CRM Task":
|
||||
reference_doc = frappe.get_doc(doc.reference_type, doc.reference_name)
|
||||
return reference_doc.reference_doctype, reference_doc.reference_docname
|
||||
|
||||
return doc.reference_type, doc.reference_name
|
||||
return doc.reference_type, doc.reference_name
|
||||
|
||||
@ -30,25 +30,27 @@ def notify_agent(doc):
|
||||
if doctype.startswith("CRM "):
|
||||
doctype = doctype[4:].lower()
|
||||
notification_text = f"""
|
||||
<div class="mb-2 leading-5 text-gray-600">
|
||||
<span class="font-medium text-gray-900">{ _('You') }</span>
|
||||
<div class="mb-2 leading-5 text-ink-gray-5">
|
||||
<span class="font-medium text-ink-gray-9">{ _('You') }</span>
|
||||
<span>{ _('received a whatsapp message in {0}').format(doctype) }</span>
|
||||
<span class="font-medium text-gray-900">{ doc.reference_name }</span>
|
||||
<span class="font-medium text-ink-gray-9">{ doc.reference_name }</span>
|
||||
</div>
|
||||
"""
|
||||
assigned_users = get_assigned_users(doc.reference_doctype, doc.reference_name)
|
||||
for user in assigned_users:
|
||||
notify_user({
|
||||
"owner": doc.owner,
|
||||
"assigned_to": user,
|
||||
"notification_type": "WhatsApp",
|
||||
"message": doc.message,
|
||||
"notification_text": notification_text,
|
||||
"reference_doctype": "WhatsApp Message",
|
||||
"reference_docname": doc.name,
|
||||
"redirect_to_doctype": doc.reference_doctype,
|
||||
"redirect_to_docname": doc.reference_name,
|
||||
})
|
||||
notify_user(
|
||||
{
|
||||
"owner": doc.owner,
|
||||
"assigned_to": user,
|
||||
"notification_type": "WhatsApp",
|
||||
"message": doc.message,
|
||||
"notification_text": notification_text,
|
||||
"reference_doctype": "WhatsApp Message",
|
||||
"reference_docname": doc.name,
|
||||
"redirect_to_doctype": doc.reference_doctype,
|
||||
"redirect_to_docname": doc.reference_name,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_lead_or_deal_from_number(number):
|
||||
@ -92,6 +94,7 @@ def is_whatsapp_enabled():
|
||||
return False
|
||||
return frappe.get_cached_value("WhatsApp Settings", "WhatsApp Settings", "enabled")
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def is_whatsapp_installed():
|
||||
if not frappe.db.exists("DocType", "WhatsApp Settings"):
|
||||
@ -105,8 +108,8 @@ def get_whatsapp_messages(reference_doctype, reference_name):
|
||||
return []
|
||||
messages = []
|
||||
|
||||
if reference_doctype == 'CRM Deal':
|
||||
lead = frappe.db.get_value(reference_doctype, reference_name, 'lead')
|
||||
if reference_doctype == "CRM Deal":
|
||||
lead = frappe.db.get_value(reference_doctype, reference_name, "lead")
|
||||
if lead:
|
||||
messages = frappe.get_all(
|
||||
"WhatsApp Message",
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 59415754663693989ae5144b43370f4b0426aa50
|
||||
Subproject commit 82150c9591a36abc5dc8667ae5873651d4b2cc0d
|
||||
@ -18,7 +18,7 @@
|
||||
/>
|
||||
<component
|
||||
v-else
|
||||
class="size-4"
|
||||
class="size-4 text-ink-gray-7"
|
||||
:is="fileIcon(attachment.file_type)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col gap-2 border border-outline-gray-modals rounded-md bg-surface-white px-3 py-2.5"
|
||||
class="flex flex-col gap-2 border border-outline-gray-modals rounded-md bg-surface-white px-3 py-2.5 text-ink-gray-9"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="inline-flex gap-2 items-center text-base font-medium">
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="cursor-pointer rounded bg-surface-menu-bar px-3 py-[7.5px] text-base leading-6 transition-all duration-300 ease-in-out"
|
||||
class="cursor-pointer rounded bg-surface-gray-1 px-3 py-[7.5px] text-base leading-6 transition-all duration-300 ease-in-out"
|
||||
>
|
||||
<div class="prose-f" v-html="activity.content" />
|
||||
<div v-if="activity.attachments.length" class="mt-2 flex flex-wrap gap-2">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div
|
||||
class="cursor-pointer flex flex-col rounded-md shadow bg-surface-white px-3 py-1.5 text-base transition-all duration-300 ease-in-out"
|
||||
>
|
||||
<div class="-mb-0.5 flex items-center justify-between gap-2 truncate">
|
||||
<div class="-mb-0.5 flex items-center justify-between gap-2 truncate text-ink-gray-9">
|
||||
<div class="flex items-center gap-2 truncate">
|
||||
<span>{{ activity.data.sender_full_name }}</span>
|
||||
<span class="sm:flex hidden text-sm text-ink-gray-5">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div v-if="tasks.length">
|
||||
<div v-for="(task, i) in tasks" :key="task.name">
|
||||
<div
|
||||
class="activity flex cursor-pointer gap-6 rounded p-2.5 duration-300 ease-in-out hover:bg-surface-menu-bar"
|
||||
class="activity flex cursor-pointer gap-6 rounded p-2.5 duration-300 ease-in-out hover:bg-surface-gray-1"
|
||||
@click="modalRef.showTask(task)"
|
||||
>
|
||||
<div class="flex flex-1 flex-col gap-1.5 text-base truncate">
|
||||
@ -76,7 +76,7 @@
|
||||
<Button
|
||||
icon="more-horizontal"
|
||||
variant="ghosted"
|
||||
class="hover:bg-surface-gray-4"
|
||||
class="hover:bg-surface-gray-4 text-ink-gray-9"
|
||||
/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
>
|
||||
<div
|
||||
:id="whatsapp.name"
|
||||
class="group/message relative max-w-[90%] rounded-md bg-surface-menu-bar p-1.5 pl-2 text-base shadow-sm"
|
||||
class="group/message relative max-w-[90%] rounded-md bg-surface-gray-1 text-ink-gray-9 p-1.5 pl-2 text-base shadow-sm"
|
||||
>
|
||||
<div
|
||||
v-if="whatsapp.is_reply"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<template #target="{ togglePopover }">
|
||||
<button
|
||||
:class="[
|
||||
active ? 'bg-surface-gray-2' : 'text-ink-gray-8',
|
||||
active ? 'bg-surface-gray-3' : 'text-ink-gray-6',
|
||||
'group w-full flex h-7 items-center justify-between rounded px-2 text-base hover:bg-surface-gray-2',
|
||||
]"
|
||||
@click.prevent="togglePopover()"
|
||||
@ -19,7 +19,7 @@
|
||||
</template>
|
||||
<template #body>
|
||||
<div
|
||||
class="grid grid-cols-3 justify-between mx-3 p-2 rounded-lg border border-gray-100 bg-surface-white shadow-xl"
|
||||
class="grid grid-cols-3 justify-between mx-3 p-2 min-w-40 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div v-for="app in apps.data" :key="app.name">
|
||||
<a
|
||||
@ -39,8 +39,8 @@
|
||||
<script setup>
|
||||
import AppsIcon from '@/components/Icons/AppsIcon.vue'
|
||||
import { Popover, createResource } from 'frappe-ui'
|
||||
import { onUnmounted } from 'vue';
|
||||
import { stopRecording } from '@/telemetry';
|
||||
import { onUnmounted } from 'vue'
|
||||
import { stopRecording } from '@/telemetry'
|
||||
|
||||
const props = defineProps({
|
||||
active: Boolean,
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
</template>
|
||||
<template #body="{ close }">
|
||||
<div
|
||||
class="my-2 rounded-lg border border-gray-100 bg-surface-white p-1.5 shadow-xl"
|
||||
class="my-2 p-1.5 min-w-40 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div v-if="!edit">
|
||||
<Draggable
|
||||
@ -49,7 +49,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
<div class="mt-1.5 flex flex-col gap-1 border-t pt-1.5">
|
||||
<div class="mt-1.5 flex flex-col gap-1 border-t border-outline-gray-modals pt-1.5">
|
||||
<Autocomplete
|
||||
value=""
|
||||
:options="fields"
|
||||
@ -114,7 +114,7 @@
|
||||
placeholder="10rem"
|
||||
:description="
|
||||
__(
|
||||
'Width can be in number, pixel or rem (eg. 3, 30px, 10rem)'
|
||||
'Width can be in number, pixel or rem (eg. 3, 30px, 10rem)',
|
||||
)
|
||||
"
|
||||
:debounce="500"
|
||||
@ -295,6 +295,6 @@ watchOnce(
|
||||
oldValues.value.columns = JSON.parse(JSON.stringify(val.columns))
|
||||
oldValues.value.rows = JSON.parse(JSON.stringify(val.rows))
|
||||
oldValues.value.isDefault = val.is_default
|
||||
}
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="flex flex-wrap gap-1 min-h-20 p-1.5 cursor-text rounded h-7 text-base border border-outline-gray-2 bg-surface-white hover:border-outline-gray-3 focus:border-outline-gray-4 focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors w-full"
|
||||
class="group flex flex-wrap gap-1 min-h-20 p-1.5 cursor-text rounded h-7 text-base bg-surface-gray-2 hover:bg-surface-gray-3 focus:border-outline-gray-4 focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors w-full"
|
||||
@click="setFocus"
|
||||
>
|
||||
<Button
|
||||
@ -11,7 +11,7 @@
|
||||
:label="value"
|
||||
theme="gray"
|
||||
variant="subtle"
|
||||
class="rounded"
|
||||
class="rounded bg-surface-gray-3 group-hover:bg-surface-gray-4 focus-visible:ring-outline-gray-4"
|
||||
@keydown.delete.capture.stop="removeLastValue"
|
||||
>
|
||||
<template #suffix>
|
||||
@ -23,9 +23,9 @@
|
||||
</template>
|
||||
</Button>
|
||||
<div class="flex-1">
|
||||
<TextInput
|
||||
<input
|
||||
ref="search"
|
||||
class="w-full border-none bg-surface-white hover:bg-surface-white focus:border-none focus:!shadow-none focus-visible:!ring-0"
|
||||
class="w-full border-none h-7 text-base bg-surface-gray-2 group-hover:bg-surface-gray-3 focus:border-none focus:!shadow-none focus-visible:!ring-0 transition-colors"
|
||||
type="text"
|
||||
v-model="query"
|
||||
placeholder="example@email.com"
|
||||
@ -113,7 +113,7 @@ const removeLastValue = () => {
|
||||
}
|
||||
|
||||
function setFocus() {
|
||||
search.value.el.focus()
|
||||
search.value.focus()
|
||||
}
|
||||
|
||||
defineExpose({ setFocus })
|
||||
|
||||
@ -41,9 +41,11 @@
|
||||
</template>
|
||||
<template #body="{ isOpen }">
|
||||
<div v-show="isOpen">
|
||||
<div class="mt-1 rounded-lg bg-surface-white py-1 text-base shadow-2xl">
|
||||
<div
|
||||
class="mt-1 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<ComboboxOptions
|
||||
class="my-1 max-h-[12rem] overflow-y-auto px-1.5"
|
||||
class="p-1.5 max-h-[12rem] overflow-y-auto"
|
||||
static
|
||||
>
|
||||
<ComboboxOption
|
||||
@ -55,7 +57,7 @@
|
||||
<li
|
||||
:class="[
|
||||
'flex cursor-pointer items-center rounded px-2 py-1 text-base',
|
||||
{ 'bg-surface-gray-2': active },
|
||||
{ 'bg-surface-gray-3': active },
|
||||
]"
|
||||
>
|
||||
<UserAvatar
|
||||
@ -146,16 +148,15 @@ const filterOptions = createResource({
|
||||
cache: [text.value, 'Contact'],
|
||||
params: { txt: text.value },
|
||||
transform: (data) => {
|
||||
let allData = data
|
||||
.map((option) => {
|
||||
let fullName = option[0]
|
||||
let email = option[1]
|
||||
let name = option[2]
|
||||
return {
|
||||
label: fullName || name || email,
|
||||
value: email,
|
||||
}
|
||||
})
|
||||
let allData = data.map((option) => {
|
||||
let fullName = option[0]
|
||||
let email = option[1]
|
||||
let name = option[2]
|
||||
return {
|
||||
label: fullName || name || email,
|
||||
value: email,
|
||||
}
|
||||
})
|
||||
return allData
|
||||
},
|
||||
})
|
||||
|
||||
@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="option.selected">
|
||||
<FeatherIcon name="check" class="text-primary-500 h-4 w-6" size="sm" />
|
||||
<FeatherIcon name="check" class="text-ink-gray-5 h-4 w-6" size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -34,7 +34,9 @@
|
||||
variant="ghost"
|
||||
@click="toggleCC()"
|
||||
:class="[
|
||||
cc ? '!bg-surface-gray-4 hover:bg-surface-gray-3' : '!text-ink-gray-4',
|
||||
cc
|
||||
? '!bg-surface-gray-4 hover:bg-surface-gray-3'
|
||||
: '!text-ink-gray-4',
|
||||
]"
|
||||
/>
|
||||
<Button
|
||||
@ -42,7 +44,9 @@
|
||||
variant="ghost"
|
||||
@click="toggleBCC()"
|
||||
:class="[
|
||||
bcc ? '!bg-surface-gray-4 hover:bg-surface-gray-3' : '!text-ink-gray-4',
|
||||
bcc
|
||||
? '!bg-surface-gray-4 hover:bg-surface-gray-3'
|
||||
: '!text-ink-gray-4',
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
@ -73,8 +77,8 @@
|
||||
</div>
|
||||
<div class="sm:mx-10 mx-4 flex items-center gap-2 pb-2.5">
|
||||
<span class="text-xs text-ink-gray-4">{{ __('SUBJECT') }}:</span>
|
||||
<TextInput
|
||||
class="flex-1 border-none bg-surface-white hover:bg-surface-white focus:border-none focus:!shadow-none focus-visible:!ring-0"
|
||||
<input
|
||||
class="flex-1 border-none text-ink-gray-9 text-base bg-surface-white hover:bg-surface-white focus:border-none focus:!shadow-none focus-visible:!ring-0"
|
||||
v-model="subject"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div
|
||||
v-for="section in sections"
|
||||
:key="section.label"
|
||||
class="section first:border-t-0 first:pt-0"
|
||||
class="section first:border-t-0 border-outline-gray-modals first:pt-0"
|
||||
:class="section.hideBorder ? '' : 'border-t pt-4'"
|
||||
>
|
||||
<div
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<template #prefix><FilterIcon class="h-4" /></template>
|
||||
<template v-if="filters?.size" #suffix>
|
||||
<div
|
||||
class="flex h-5 w-5 items-center justify-center rounded bg-surface-gray-7 pt-[1px] text-2xs font-medium text-ink-white"
|
||||
class="flex h-5 w-5 items-center justify-center rounded-[5px] bg-surface-white pt-px text-xs font-medium text-ink-gray-8 shadow-sm"
|
||||
>
|
||||
{{ filters.size }}
|
||||
</div>
|
||||
@ -27,7 +27,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ close }">
|
||||
<div class="my-2 rounded-lg border border-gray-100 bg-surface-white shadow-xl">
|
||||
<div
|
||||
class="my-2 min-w-40 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div class="min-w-72 p-2 sm:min-w-[400px]">
|
||||
<div
|
||||
v-if="filters?.size"
|
||||
|
||||
@ -8,10 +8,10 @@
|
||||
<template #body="{ togglePopover }">
|
||||
<div
|
||||
v-if="reaction"
|
||||
class="flex items-center justify-center gap-2 rounded-full bg-surface-white px-2 py-1 shadow-sm"
|
||||
class="px-2 py-1 flex items-center justify-center gap-2 rounded-full bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div
|
||||
class="size-5 cursor-pointer rounded-full bg-surface-white text-xl"
|
||||
class="size-5 cursor-pointer rounded-full bg-surface-transparent text-xl"
|
||||
v-for="r in reactionEmojis"
|
||||
:key="r"
|
||||
@click="() => (emoji = r) && togglePopover()"
|
||||
@ -26,9 +26,12 @@
|
||||
@click.stop="() => (reaction = false)"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="my-3 max-w-max transform bg-surface-white px-4 sm:px-0">
|
||||
<div
|
||||
v-else
|
||||
class="my-3 max-w-max transform bg-surface-white px-4 sm:px-0"
|
||||
>
|
||||
<div
|
||||
class="relative max-h-96 overflow-y-auto rounded-lg pb-3 shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||
class="relative max-h-96 pb-3 overflow-y-auto min-w-40 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div class="flex gap-2 px-3 pb-1 pt-3">
|
||||
<div class="flex-1">
|
||||
@ -43,7 +46,9 @@
|
||||
</div>
|
||||
<div class="w-96"></div>
|
||||
<div class="px-3" v-for="(emojis, group) in emojiGroups" :key="group">
|
||||
<div class="sticky top-0 bg-surface-white pb-2 pt-3 text-sm text-ink-gray-7">
|
||||
<div
|
||||
class="sticky top-0 bg-surface-modal pb-2 pt-3 text-sm text-ink-gray-7"
|
||||
>
|
||||
{{ group }}
|
||||
</div>
|
||||
<div class="grid w-96 grid-cols-12 place-items-center">
|
||||
|
||||
@ -1,31 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2 4.5H14"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4 8.5H12"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M6.5 12.5H9.5"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M2 4.16675C1.72386 4.16675 1.5 4.39061 1.5 4.66675C1.5 4.94289 1.72386 5.16675 2 5.16675H14C14.2761 5.16675 14.5 4.94289 14.5 4.66675C14.5 4.39061 14.2761 4.16675 14 4.16675H2ZM3.49996 7.99761C3.49996 7.72147 3.72382 7.49761 3.99996 7.49761H12C12.2761 7.49761 12.5 7.72147 12.5 7.99761C12.5 8.27375 12.2761 8.49761 12 8.49761H3.99996C3.72382 8.49761 3.49996 8.27375 3.49996 7.99761ZM5.9 11.3289C5.9 11.0527 6.12386 10.8289 6.4 10.8289H9.6C9.87614 10.8289 10.1 11.0527 10.1 11.3289C10.1 11.605 9.87614 11.8289 9.6 11.8289H6.4C6.12386 11.8289 5.9 11.605 5.9 11.3289Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@ -1,45 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1.75 3.75H10.75"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1.75 7.75H7.75"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1.75 11.75H5.75"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14.25 8.25L12.25 6.25L10.25 8.25"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.25 12.25L12.25 6.25"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M5.5 3.5C5.5 3.22386 5.27614 3 5 3C4.72386 3 4.5 3.22386 4.5 3.5V11.2931L2.87846 9.67159C2.68319 9.47633 2.36661 9.47633 2.17135 9.67159C1.97609 9.86686 1.97609 10.1834 2.17135 10.3787L4.64622 12.8536C4.75097 12.9583 4.89063 13.0069 5.02774 12.9992C5.15903 12.9921 5.27684 12.9342 5.36195 12.845L7.8282 10.3787C8.02347 10.1834 8.02347 9.86686 7.8282 9.67159C7.63294 9.47633 7.31636 9.47633 7.1211 9.67159L5.5 11.2927V3.5ZM11.3698 3.16295C11.2784 3.06282 11.1468 3 11.0005 3C10.9947 3 10.989 3.0001 10.9832 3.00029C10.8608 3.00432 10.7396 3.05304 10.6462 3.14647L8.17135 5.62134C7.97609 5.8166 7.97609 6.13319 8.17135 6.32845C8.36661 6.52371 8.68319 6.52371 8.87846 6.32845L10.5005 4.70641V12.5C10.5005 12.7761 10.7243 13 11.0005 13C11.2766 13 11.5005 12.7761 11.5005 12.5V4.70784L13.1211 6.32845C13.3164 6.52371 13.6329 6.52371 13.8282 6.32845C14.0235 6.13319 14.0235 5.8166 13.8282 5.62134L11.3698 3.16295Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
rx="1"
|
||||
transform="rotate(-45 0.792893 0)"
|
||||
fill="currentColor"
|
||||
stroke="white"
|
||||
class="stroke stroke-ink-white"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@ -11,7 +11,9 @@
|
||||
<Dialog v-model="showDialog" :options="{ title: __('Kanban Settings') }">
|
||||
<template #body-content>
|
||||
<div>
|
||||
<div class="text-base text-ink-gray-8 mb-2">{{ __('Column Field') }}</div>
|
||||
<div class="text-base text-ink-gray-8 mb-2">
|
||||
{{ __('Column Field') }}
|
||||
</div>
|
||||
<Autocomplete
|
||||
v-if="columnFields"
|
||||
value=""
|
||||
@ -21,7 +23,6 @@
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="w-full !justify-start"
|
||||
variant="subtle"
|
||||
@click="togglePopover()"
|
||||
:label="columnField.label"
|
||||
/>
|
||||
@ -39,7 +40,6 @@
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="w-full !justify-start"
|
||||
variant="subtle"
|
||||
@click="togglePopover()"
|
||||
:label="titleField.label"
|
||||
/>
|
||||
@ -47,7 +47,9 @@
|
||||
</Autocomplete>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="text-base text-ink-gray-8 mb-2">{{ __('Fields Order') }}</div>
|
||||
<div class="text-base text-ink-gray-8 mb-2">
|
||||
{{ __('Fields Order') }}
|
||||
</div>
|
||||
<Draggable
|
||||
:list="allFields"
|
||||
@end="reorder"
|
||||
@ -57,7 +59,7 @@
|
||||
>
|
||||
<template #item="{ element: field }">
|
||||
<div
|
||||
class="px-1 py-0.5 border rounded text-base text-ink-gray-8 flex items-center justify-between gap-2"
|
||||
class="px-1 py-0.5 border border-outline-gray-modals rounded text-base text-ink-gray-8 flex items-center justify-between gap-2"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<DragVerticalIcon class="h-3.5 cursor-grab" />
|
||||
@ -78,7 +80,6 @@
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="w-full mt-2"
|
||||
variant="outline"
|
||||
@click="togglePopover()"
|
||||
:label="__('Add Field')"
|
||||
>
|
||||
|
||||
@ -17,7 +17,11 @@
|
||||
<div class="flex items-center text-base">
|
||||
<NestedPopover>
|
||||
<template #target>
|
||||
<Button variant="ghost" size="sm" class="hover:!bg-surface-gray-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="hover:!bg-surface-gray-2"
|
||||
>
|
||||
<IndicatorIcon
|
||||
:class="colorClasses(column.column.color, true)"
|
||||
/>
|
||||
@ -25,7 +29,7 @@
|
||||
</template>
|
||||
<template #body="{ close }">
|
||||
<div
|
||||
class="flex flex-col gap-3 px-3 py-2.5 rounded-lg border border-gray-100 bg-surface-white shadow-xl"
|
||||
class="flex flex-col gap-3 px-3 py-2.5 min-w-40 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
@ -48,7 +52,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</NestedPopover>
|
||||
<div>{{ column.column.name }}</div>
|
||||
<div class="text-ink-gray-9">{{ column.column.name }}</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<Dropdown :options="actions(column)">
|
||||
@ -80,7 +84,7 @@
|
||||
<template #item="{ element: fields }">
|
||||
<component
|
||||
:is="options.getRoute ? 'router-link' : 'div'"
|
||||
class="pt-3 px-3.5 pb-2.5 rounded-lg border bg-surface-white text-base flex flex-col"
|
||||
class="pt-3 px-3.5 pb-2.5 rounded-lg border bg-surface-white text-base flex flex-col text-ink-gray-9"
|
||||
:data-name="fields.name"
|
||||
v-bind="{
|
||||
to: options.getRoute ? options.getRoute(fields) : undefined,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex h-screen w-screen">
|
||||
<MobileSidebar />
|
||||
<div class="flex h-full flex-1 flex-col overflow-auto">
|
||||
<div class="flex h-full flex-1 flex-col overflow-auto bg-surface-white">
|
||||
<MobileAppHeader />
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
variant="ghosted"
|
||||
@click="sidebarOpened = !sidebarOpened"
|
||||
>
|
||||
<MenuIcon class="h-4" />
|
||||
<MenuIcon class="h-4 text-ink-gray-9" />
|
||||
</Button>
|
||||
</div>
|
||||
<div id="app-header" class="flex-1" />
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="dialogOptions">
|
||||
<template #body>
|
||||
<div class="bg-surface-white px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
</template>
|
||||
<template #item-label="{ option }">
|
||||
<Tooltip :text="option.value">
|
||||
<div class="cursor-pointer">
|
||||
<div class="cursor-pointer text-ink-gray-9">
|
||||
{{ getUser(option.value).full_name }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
@ -55,7 +55,6 @@
|
||||
<Button
|
||||
:label="getUser(assignee.name).full_name"
|
||||
theme="gray"
|
||||
variant="outline"
|
||||
>
|
||||
<template #prefix>
|
||||
<UserAvatar :user="assignee.name" size="sm" />
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="dialogOptions">
|
||||
<template #body>
|
||||
<div class="bg-surface-white px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ size: '3xl' }">
|
||||
<template #body>
|
||||
<div class="bg-surface-white px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||
|
||||
@ -17,60 +17,49 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex sm:flex-row flex-col gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">
|
||||
{{ __('Name') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
<TextInput
|
||||
<FormControl
|
||||
ref="nameRef"
|
||||
variant="outline"
|
||||
v-model="_emailTemplate.name"
|
||||
:placeholder="__('Payment Reminder')"
|
||||
:label="__('Name')"
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">{{ __('Doctype') }}</div>
|
||||
<Select
|
||||
variant="outline"
|
||||
<FormControl
|
||||
type="select"
|
||||
v-model="_emailTemplate.reference_doctype"
|
||||
:label="__('Doctype')"
|
||||
:options="['CRM Deal', 'CRM Lead']"
|
||||
:placeholder="__('CRM Deal')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">
|
||||
{{ __('Subject') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
<TextInput
|
||||
<FormControl
|
||||
ref="subjectRef"
|
||||
variant="outline"
|
||||
v-model="_emailTemplate.subject"
|
||||
:label="__('Subject')"
|
||||
:placeholder="__('Payment Reminder from Frappé - (#{{ name }})')"
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">
|
||||
{{ __('Content Type') }}
|
||||
</div>
|
||||
<Select
|
||||
variant="outline"
|
||||
<FormControl
|
||||
type="select"
|
||||
v-model="_emailTemplate.content_type"
|
||||
:label="__('Content Type')"
|
||||
default="Rich Text"
|
||||
:options="['Rich Text', 'HTML']"
|
||||
:placeholder="__('Rich Text')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">
|
||||
{{ __('Content') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
<FormControl
|
||||
v-if="_emailTemplate.content_type === 'HTML'"
|
||||
type="textarea"
|
||||
variant="outline"
|
||||
:label="__('Content')"
|
||||
:required="true"
|
||||
ref="content"
|
||||
:rows="10"
|
||||
v-model="_emailTemplate.response_html"
|
||||
@ -80,20 +69,24 @@
|
||||
)
|
||||
"
|
||||
/>
|
||||
<TextEditor
|
||||
v-else
|
||||
variant="outline"
|
||||
ref="content"
|
||||
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-outline-gray-2 bg-surface-white hover:border-outline-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||
:bubbleMenu="true"
|
||||
:content="_emailTemplate.response"
|
||||
@change="(val) => (_emailTemplate.response = val)"
|
||||
:placeholder="
|
||||
__(
|
||||
'Dear {{ lead_name }}, \n\nThis is a reminder for the payment of {{ grand_total }}. \n\nThanks, \nFrappé',
|
||||
)
|
||||
"
|
||||
/>
|
||||
<div v-else>
|
||||
<div class="mb-1.5 text-xs text-ink-gray-5">
|
||||
{{ __('Content') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
<TextEditor
|
||||
ref="content"
|
||||
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||
:bubbleMenu="true"
|
||||
:content="_emailTemplate.response"
|
||||
@change="(val) => (_emailTemplate.response = val)"
|
||||
:placeholder="
|
||||
__(
|
||||
'Dear {{ lead_name }}, \n\nThis is a reminder for the payment of {{ grand_total }}. \n\nThanks, \nFrappé',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox v-model="_emailTemplate.enabled" :label="__('Enabled')" />
|
||||
@ -106,7 +99,7 @@
|
||||
|
||||
<script setup>
|
||||
import { capture } from '@/telemetry'
|
||||
import { Checkbox, Select, TextEditor, call } from 'frappe-ui'
|
||||
import { Checkbox, TextEditor, call } from 'frappe-ui'
|
||||
import { ref, nextTick, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
@ -229,9 +222,9 @@ watch(
|
||||
errorMessage.value = ''
|
||||
nextTick(() => {
|
||||
if (_emailTemplate.value.name) {
|
||||
subjectRef.value.el.focus()
|
||||
subjectRef.value?.el?.focus()
|
||||
} else {
|
||||
nameRef.value.el.focus()
|
||||
nameRef.value?.el?.focus()
|
||||
}
|
||||
_emailTemplate.value = { ...props.emailTemplate }
|
||||
_emailTemplate.value.content_type = _emailTemplate.value.use_html
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ size: '3xl' }">
|
||||
<template #body>
|
||||
<div class="bg-surface-white px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||
|
||||
@ -19,7 +19,6 @@
|
||||
</h3>
|
||||
<Button
|
||||
v-if="_note?.reference_docname"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:label="
|
||||
_note.reference_doctype == 'CRM Deal'
|
||||
@ -37,20 +36,19 @@
|
||||
<template #body-content>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">{{ __('Title') }}</div>
|
||||
<TextInput
|
||||
<FormControl
|
||||
ref="title"
|
||||
variant="outline"
|
||||
:label="__('Title')"
|
||||
v-model="_note.title"
|
||||
:placeholder="__('Call with John Doe')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">{{ __('Content') }}</div>
|
||||
<div class="mb-1.5 text-xs text-ink-gray-5">{{ __('Content') }}</div>
|
||||
<TextEditor
|
||||
variant="outline"
|
||||
ref="content"
|
||||
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-outline-gray-2 bg-surface-white hover:border-outline-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||
:bubbleMenu="true"
|
||||
:content="_note.content"
|
||||
@change="(val) => (_note.content = val)"
|
||||
@ -149,12 +147,12 @@ watch(
|
||||
if (!value) return
|
||||
editMode.value = false
|
||||
nextTick(() => {
|
||||
title.value.el.focus()
|
||||
title.value?.el?.focus()
|
||||
_note.value = { ...props.note }
|
||||
if (_note.value.title || _note.value.content) {
|
||||
editMode.value = true
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="dialogOptions">
|
||||
<template #body>
|
||||
<div class="bg-surface-white px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl font-semibold leading-6 text-ink-gray-9">
|
||||
|
||||
@ -19,7 +19,6 @@
|
||||
</h3>
|
||||
<Button
|
||||
v-if="task?.reference_docname"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:label="
|
||||
task.reference_doctype == 'CRM Deal'
|
||||
@ -37,22 +36,21 @@
|
||||
<template #body-content>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">{{ __('Title') }}</div>
|
||||
<TextInput
|
||||
<FormControl
|
||||
ref="title"
|
||||
variant="outline"
|
||||
:label="__('Title')"
|
||||
v-model="_task.title"
|
||||
:placeholder="__('Call with John Doe')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm text-ink-gray-5">
|
||||
<div class="mb-1.5 text-xs text-ink-gray-5">
|
||||
{{ __('Description') }}
|
||||
</div>
|
||||
<TextEditor
|
||||
variant="outline"
|
||||
ref="description"
|
||||
editor-class="!prose-sm overflow-auto min-h-[80px] max-h-80 py-1.5 px-2 rounded border border-outline-gray-2 bg-surface-white hover:border-outline-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||
:bubbleMenu="true"
|
||||
:content="_task.description"
|
||||
@change="(val) => (_task.description = val)"
|
||||
|
||||
@ -5,15 +5,15 @@
|
||||
title: editMode
|
||||
? __('Edit View')
|
||||
: duplicateMode
|
||||
? __('Duplicate View')
|
||||
: __('Create View'),
|
||||
? __('Duplicate View')
|
||||
: __('Create View'),
|
||||
actions: [
|
||||
{
|
||||
label: editMode
|
||||
? __('Save Changes')
|
||||
: duplicateMode
|
||||
? __('Duplicate')
|
||||
: __('Create'),
|
||||
? __('Duplicate')
|
||||
: __('Create'),
|
||||
variant: 'solid',
|
||||
onClick: () => (editMode ? update() : create()),
|
||||
},
|
||||
@ -27,16 +27,14 @@
|
||||
<div class="flex gap-2">
|
||||
<IconPicker v-model="view.icon" v-slot="{ togglePopover }">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="md"
|
||||
class="flex size-8 text-2xl leading-none"
|
||||
:label="view.icon"
|
||||
@click="togglePopover"
|
||||
/>
|
||||
</IconPicker>
|
||||
<TextInput
|
||||
<FormControl
|
||||
class="flex-1"
|
||||
variant="outline"
|
||||
size="md"
|
||||
type="text"
|
||||
:placeholder="__('My Open Deals')"
|
||||
@ -49,7 +47,7 @@
|
||||
|
||||
<script setup>
|
||||
import IconPicker from '@/components/IconPicker.vue'
|
||||
import { call, TextInput } from 'frappe-ui'
|
||||
import { call } from 'frappe-ui'
|
||||
import { ref, watch, nextTick } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
@ -87,7 +85,7 @@ async function create() {
|
||||
view.value.doctype = props.doctype
|
||||
let v = await call(
|
||||
'crm.fcrm.doctype.crm_view_settings.crm_view_settings.create',
|
||||
{ view: view.value }
|
||||
{ view: view.value },
|
||||
)
|
||||
show.value = false
|
||||
props.options.afterCreate?.(v)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="avatars?.length"
|
||||
class="mr-1.5 flex cursor-pointer items-center"
|
||||
class="mr-1.5 flex cursor-pointer items-center "
|
||||
:class="[
|
||||
avatars?.length > 1 ? 'flex-row-reverse' : 'truncate [&>div]:truncate',
|
||||
]"
|
||||
@ -24,7 +24,7 @@
|
||||
:key="avatar.name"
|
||||
>
|
||||
<Avatar
|
||||
class="user-avatar -mr-1.5 transform ring-2 ring-white transition hover:z-10 hover:scale-110"
|
||||
class="user-avatar -mr-1.5 transform ring-2 ring-outline-white transition hover:z-10 hover:scale-110"
|
||||
shape="circle"
|
||||
:image="avatar.image"
|
||||
:label="avatar.label"
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
left: 'calc(100% + 1px)',
|
||||
}"
|
||||
>
|
||||
<div class="flex h-screen flex-col">
|
||||
<div class="flex h-screen flex-col text-ink-gray-9">
|
||||
<div
|
||||
class="z-20 flex items-center justify-between border-b bg-surface-white px-5 py-2.5"
|
||||
>
|
||||
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="notifications.data?.length"
|
||||
class="divide-y overflow-auto text-base"
|
||||
class="divide-y divide-outline-gray-modals overflow-auto text-base"
|
||||
>
|
||||
<RouterLink
|
||||
v-for="n in notifications.data"
|
||||
|
||||
@ -2,14 +2,14 @@
|
||||
<div>
|
||||
<Draggable :list="sections" item-key="label" class="flex flex-col gap-5.5">
|
||||
<template #item="{ element: section }">
|
||||
<div class="flex flex-col gap-1.5 p-2.5 bg-surface-menu-bar rounded">
|
||||
<div class="flex flex-col gap-1.5 p-2.5 bg-surface-gray-2 rounded">
|
||||
<div class="flex items-center justify-between">
|
||||
<div
|
||||
class="flex h-7 max-w-fit cursor-pointer items-center gap-2 text-base font-medium leading-4"
|
||||
class="flex h-7 max-w-fit cursor-pointer items-center gap-2 text-base font-medium leading-4 text-ink-gray-9"
|
||||
>
|
||||
<div
|
||||
v-if="!section.editingLabel"
|
||||
:class="section.hideLabel ? 'text-ink-gray-3' : ''"
|
||||
:class="{ 'text-ink-gray-3': section.hideLabel }"
|
||||
>
|
||||
{{ __(section.label) || __('Untitled') }}
|
||||
</div>
|
||||
@ -48,7 +48,7 @@
|
||||
>
|
||||
<template #item="{ element: field }">
|
||||
<div
|
||||
class="px-2.5 py-2 border rounded text-base bg-surface-white text-ink-gray-8 flex items-center leading-4 justify-between gap-2"
|
||||
class="px-2.5 py-2 border border-outline-gray-2 rounded text-base bg-surface-modal text-ink-gray-8 flex items-center leading-4 justify-between gap-2"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<DragVerticalIcon class="h-3.5 cursor-grab" />
|
||||
@ -74,7 +74,7 @@
|
||||
<template #target="{ togglePopover }">
|
||||
<div class="gap-2 w-full">
|
||||
<Button
|
||||
class="w-full !h-8 !border-outline-gray-modals hover:!border-outline-gray-2"
|
||||
class="w-full !h-8 !bg-surface-modal"
|
||||
variant="outline"
|
||||
@click="togglePopover()"
|
||||
:label="__('Add Field')"
|
||||
@ -86,7 +86,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #item-label="{ option }">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex flex-col gap-1 text-ink-gray-9">
|
||||
<div>{{ option.label }}</div>
|
||||
<div class="text-ink-gray-4 text-sm">
|
||||
{{ `${option.fieldname} - ${option.fieldtype}` }}
|
||||
|
||||
@ -112,6 +112,6 @@ let slaSection = computed(() => {
|
||||
<style scoped>
|
||||
:deep(.form-control button) {
|
||||
border-color: transparent;
|
||||
background: white;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
<slot name="header" v-bind="{ opened, hide, open, close, toggle }">
|
||||
<div v-if="!hide" class="flex items-center justify-between">
|
||||
<div
|
||||
class="flex h-7 max-w-fit cursor-pointer items-center gap-2 pl-2 pr-3 text-base font-semibold leading-5"
|
||||
class="flex h-7 text-ink-gray-9 max-w-fit cursor-pointer items-center gap-2 pl-2 pr-3 text-base font-semibold leading-5"
|
||||
@click="toggle()"
|
||||
>
|
||||
<FeatherIcon
|
||||
name="chevron-right"
|
||||
class="h-4 text-ink-gray-9 transition-all duration-300 ease-in-out"
|
||||
class="h-4 transition-all duration-300 ease-in-out"
|
||||
:class="{ 'rotate-90': opened }"
|
||||
/>
|
||||
{{ __(label) || __('Untitled') }}
|
||||
|
||||
@ -39,7 +39,10 @@
|
||||
<div v-if="data[field.name]" class="truncate">
|
||||
{{ data[field.name] }}
|
||||
</div>
|
||||
<div v-else class="text-base leading-5 text-ink-gray-4 truncate">
|
||||
<div
|
||||
v-else
|
||||
class="text-base leading-5 text-ink-gray-4 truncate"
|
||||
>
|
||||
{{ field.placeholder }}
|
||||
</div>
|
||||
<template #suffix>
|
||||
@ -52,7 +55,7 @@
|
||||
</template>
|
||||
<template #body>
|
||||
<div
|
||||
class="my-2 space-y-1.5 divide-y rounded-lg border border-gray-100 bg-surface-white p-1.5 shadow-xl"
|
||||
class="my-2 p-1.5 min-w-40 space-y-1.5 divide-y divide-outline-gray-1 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div>
|
||||
<DropdownItem
|
||||
@ -161,16 +164,18 @@
|
||||
@change.stop="emit('update', field.name, $event.target.value)"
|
||||
/>
|
||||
</div>
|
||||
<ArrowUpRightIcon
|
||||
v-if="field.type === 'link' && field.link && data[field.name]"
|
||||
class="h-4 w-4 shrink-0 cursor-pointer text-ink-gray-5 hover:text-ink-gray-8"
|
||||
@click="field.link(data[field.name])"
|
||||
/>
|
||||
<EditIcon
|
||||
v-if="field.type === 'link' && field.edit && data[field.name]"
|
||||
class="size-3.5 shrink-0 cursor-pointer text-ink-gray-5 hover:text-ink-gray-8"
|
||||
@click="field.edit(data[field.name])"
|
||||
/>
|
||||
<div class="ml-1">
|
||||
<ArrowUpRightIcon
|
||||
v-if="field.type === 'link' && field.link && data[field.name]"
|
||||
class="h-4 w-4 shrink-0 cursor-pointer text-ink-gray-5 hover:text-ink-gray-8"
|
||||
@click.stop="field.link(data[field.name])"
|
||||
/>
|
||||
<EditIcon
|
||||
v-if="field.type === 'link' && field.edit && data[field.name]"
|
||||
class="size-3.5 shrink-0 cursor-pointer text-ink-gray-5 hover:text-ink-gray-8"
|
||||
@click.stop="field.edit(data[field.name])"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FadedScrollableDiv>
|
||||
@ -257,7 +262,7 @@ function evaluate(code, context = {}) {
|
||||
:deep(.form-control button),
|
||||
.dropdown-button {
|
||||
border-color: transparent;
|
||||
background: white;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.form-control button) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full flex-col gap-8 p-8">
|
||||
<div class="flex h-full flex-col gap-8 p-8 text-ink-gray-9">
|
||||
<h2 class="flex gap-2 text-xl font-semibold leading-none h-5">
|
||||
{{ __('Send Invites To') }}
|
||||
</h2>
|
||||
@ -18,7 +18,6 @@
|
||||
type="select"
|
||||
class="mt-4"
|
||||
v-model="role"
|
||||
variant="outline"
|
||||
:label="__('Invite as')"
|
||||
:options="[
|
||||
{ label: __('Regular Access'), value: 'Sales User' },
|
||||
@ -35,7 +34,7 @@
|
||||
</div>
|
||||
<ul class="flex flex-col gap-1">
|
||||
<li
|
||||
class="flex items-center justify-between px-2 py-1 rounded-lg bg-surface-menu-bar"
|
||||
class="flex items-center justify-between px-2 py-1 rounded-lg bg-surface-gray-2"
|
||||
v-for="user in pendingInvitations.data"
|
||||
:key="user.name"
|
||||
>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
:label="profile.full_name"
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-2xl font-semibold">{{ profile.full_name }}</span>
|
||||
<span class="text-2xl font-semibold text-ink-gray-9">{{ profile.full_name }}</span>
|
||||
<span class="text-base text-ink-gray-7">{{ profile.email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
>
|
||||
<template #body>
|
||||
<div class="flex h-[calc(100vh_-_8rem)]">
|
||||
<div class="flex w-52 shrink-0 flex-col bg-surface-menu-bar p-2">
|
||||
<h1 class="mb-3 px-2 pt-2 text-lg font-semibold">
|
||||
<div class="flex w-52 shrink-0 flex-col bg-surface-gray-2 p-2">
|
||||
<h1 class="mb-3 px-2 pt-2 text-lg font-semibold text-ink-gray-9">
|
||||
{{ __('Settings') }}
|
||||
</h1>
|
||||
<div v-for="tab in tabs">
|
||||
@ -25,15 +25,15 @@
|
||||
class="w-full"
|
||||
:class="
|
||||
activeTab?.label == i.label
|
||||
? 'bg-surface-white shadow-sm'
|
||||
: 'hover:bg-surface-gray-2'
|
||||
? 'bg-surface-selected shadow-sm hover:bg-surface-selected'
|
||||
: 'hover:bg-surface-gray-3'
|
||||
"
|
||||
@click="activeTab = i"
|
||||
/>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col overflow-y-auto">
|
||||
<div class="flex flex-1 flex-col overflow-y-auto bg-surface-modal">
|
||||
<component :is="activeTab.component" v-if="activeTab" />
|
||||
</div>
|
||||
</div>
|
||||
@ -41,7 +41,6 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
||||
import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
||||
import ERPNextIcon from '@/components/Icons/ERPNextIcon.vue'
|
||||
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
||||
@ -57,10 +56,12 @@ import {
|
||||
showSettings,
|
||||
activeSettingsPage,
|
||||
} from '@/composables/settings'
|
||||
import { Dialog } from 'frappe-ui'
|
||||
import { ref, markRaw, computed, watch } from 'vue'
|
||||
import { Dialog, Avatar } from 'frappe-ui'
|
||||
import { ref, markRaw, computed, watch, h } from 'vue'
|
||||
|
||||
const { isManager } = usersStore()
|
||||
const { isManager, getUser } = usersStore()
|
||||
|
||||
const user = computed(() => getUser() || {})
|
||||
|
||||
const tabs = computed(() => {
|
||||
let _tabs = [
|
||||
@ -70,7 +71,12 @@ const tabs = computed(() => {
|
||||
items: [
|
||||
{
|
||||
label: __('Profile'),
|
||||
icon: ContactsIcon,
|
||||
icon: () =>
|
||||
h(Avatar, {
|
||||
size: 'xs',
|
||||
label: user.value.full_name,
|
||||
image: user.value.user_image,
|
||||
}),
|
||||
component: markRaw(ProfileSettings),
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex h-full flex-col gap-8">
|
||||
<h2 class="flex gap-2 text-xl font-semibold leading-none h-5">
|
||||
<h2 class="flex gap-2 text-xl font-semibold leading-none h-5 text-ink-gray-9">
|
||||
<div>{{ title || __(doctype) }}</div>
|
||||
<Badge
|
||||
v-if="data.isDirty"
|
||||
|
||||
@ -4,15 +4,15 @@
|
||||
<template #item="{ element: section }">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
class="flex items-center justify-between rounded px-2.5 py-2 bg-surface-menu-bar"
|
||||
class="flex items-center justify-between rounded px-2.5 py-2 bg-surface-gray-2"
|
||||
>
|
||||
<div
|
||||
class="flex max-w-fit cursor-pointer items-center gap-2 text-base leading-4"
|
||||
class="flex max-w-fit cursor-pointer items-center gap-2 text-base leading-4 text-ink-gray-9"
|
||||
@click="section.opened = !section.opened"
|
||||
>
|
||||
<FeatherIcon
|
||||
name="chevron-right"
|
||||
class="h-4 text-ink-gray-9 transition-all duration-300 ease-in-out"
|
||||
class="h-4 transition-all duration-300 ease-in-out"
|
||||
:class="{ 'rotate-90': section.opened }"
|
||||
/>
|
||||
<div v-if="!section.editingLabel">
|
||||
@ -62,7 +62,7 @@
|
||||
>
|
||||
<template #item="{ element: field }">
|
||||
<div
|
||||
class="px-2.5 py-2 border rounded text-base leading-4 text-ink-gray-8 flex items-center justify-between gap-2"
|
||||
class="px-2.5 py-2 border border-outline-gray-modals rounded text-base leading-4 text-ink-gray-8 flex items-center justify-between gap-2"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<DragVerticalIcon class="h-3.5 cursor-grab" />
|
||||
@ -87,7 +87,7 @@
|
||||
>
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="w-full h-8 mt-1.5 !border-outline-gray-modals hover:!border-outline-gray-2"
|
||||
class="w-full h-8 mt-1.5 !bg-surface-gray-1"
|
||||
variant="outline"
|
||||
@click="togglePopover()"
|
||||
:label="__('Add Field')"
|
||||
@ -98,7 +98,7 @@
|
||||
</Button>
|
||||
</template>
|
||||
<template #item-label="{ option }">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex flex-col gap-1 text-ink-gray-9">
|
||||
<div>{{ option.label }}</div>
|
||||
<div class="text-ink-gray-4 text-sm">
|
||||
{{ `${option.fieldname} - ${option.fieldtype}` }}
|
||||
@ -108,7 +108,7 @@
|
||||
</Autocomplete>
|
||||
<div
|
||||
v-else
|
||||
class="flex justify-center items-center border rounded border-dashed p-3"
|
||||
class="flex justify-center items-center border rounded border-dashed border-outline-gray-modals p-3"
|
||||
>
|
||||
<div class="text-sm text-ink-gray-4">
|
||||
{{ __('This section is not editable') }}
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="flex flex-1 justify-center items-center text-ink-gray-5 bg-surface-menu-bar rounded border border-gray-50"
|
||||
class="flex flex-1 justify-center items-center text-ink-gray-5 bg-surface-gray-2 rounded"
|
||||
>
|
||||
{{ __('Toggle on for preview') }}
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<button
|
||||
class="flex h-7 cursor-pointer items-center rounded text-ink-gray-7 duration-300 ease-in-out focus:outline-none focus:transition-none focus-visible:rounded focus-visible:ring-2 focus-visible:ring-outline-gray-3"
|
||||
:class="isActive ? 'bg-surface-white shadow-sm' : 'hover:bg-surface-gray-2'"
|
||||
:class="isActive ? 'bg-surface-selected shadow-sm' : 'hover:bg-surface-gray-2'"
|
||||
@click="handleClick"
|
||||
>
|
||||
<div
|
||||
@ -55,7 +55,7 @@ const route = useRoute()
|
||||
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
type: [Object, String],
|
||||
type: [Object, String, Function],
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
<template v-if="!hideLabel" #prefix><SortIcon class="h-4" /></template>
|
||||
<template v-if="sortValues?.size" #suffix>
|
||||
<div
|
||||
class="flex h-5 w-5 items-center justify-center rounded bg-surface-gray-7 pt-[1px] text-2xs font-medium text-ink-white"
|
||||
class="flex h-5 w-5 items-center justify-center rounded-[5px] bg-surface-white pt-px text-xs font-medium text-ink-gray-8 shadow-sm"
|
||||
>
|
||||
{{ sortValues.size }}
|
||||
</div>
|
||||
@ -52,6 +52,7 @@
|
||||
</Button>
|
||||
<Button
|
||||
:label="getSortLabel()"
|
||||
class="shrink-0"
|
||||
:class="sortValues.size ? 'rounded-l-none' : ''"
|
||||
>
|
||||
<template v-if="!hideLabel && !sortValues?.size" #prefix>
|
||||
@ -67,7 +68,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ close }">
|
||||
<div class="my-2 rounded-lg border border-gray-100 bg-surface-white shadow-xl">
|
||||
<div
|
||||
class="my-2 min-w-40 rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
>
|
||||
<div class="min-w-60 p-2">
|
||||
<div
|
||||
v-if="sortValues?.size"
|
||||
@ -82,7 +85,7 @@
|
||||
<div class="handle flex h-7 w-7 items-center justify-center">
|
||||
<DragIcon class="h-4 w-4 cursor-grab text-ink-gray-5" />
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="flex flex-1 [&>_div]:w-full">
|
||||
<Button
|
||||
size="md"
|
||||
class="rounded-r-none border-r"
|
||||
@ -97,7 +100,6 @@
|
||||
<DesendingIcon v-else class="h-4" />
|
||||
</Button>
|
||||
<Autocomplete
|
||||
class="!w-32"
|
||||
:value="sort.fieldname"
|
||||
:options="sortOptions.data"
|
||||
@change="(e) => updateSort(e, i)"
|
||||
@ -240,7 +242,7 @@ function getSortLabel() {
|
||||
if (!sortValues.value.size) return __('Sort')
|
||||
let values = Array.from(sortValues.value)
|
||||
let label = sortOptions.data?.find(
|
||||
(option) => option.value === values[0].fieldname
|
||||
(option) => option.value === values[0].fieldname,
|
||||
)?.label
|
||||
|
||||
return label || sort.fieldname
|
||||
|
||||
@ -27,10 +27,8 @@
|
||||
</template>
|
||||
<template #item="{ item, active }">
|
||||
<button
|
||||
:class="[
|
||||
active ? 'bg-surface-gray-2' : 'text-ink-gray-8',
|
||||
'group flex gap-4 h-7 w-full justify-between items-center rounded px-2 text-base',
|
||||
]"
|
||||
class="group flex text-ink-gray-6 gap-4 h-7 w-full justify-between items-center rounded px-2 text-base"
|
||||
:class="{ 'bg-surface-gray-3': active }"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
>
|
||||
<div class="w-full">
|
||||
<button
|
||||
class="flex w-full items-center justify-between focus:outline-none"
|
||||
class="flex h-7 w-full items-center justify-between gap-2 rounded bg-surface-gray-2 px-2 py-1 transition-colors hover:bg-surface-gray-3 border border-transparent focus:border-outline-gray-4 focus:outline-none focus:ring-2 focus:ring-outline-gray-3"
|
||||
:class="inputClasses"
|
||||
@click="() => togglePopover()"
|
||||
>
|
||||
@ -41,11 +41,13 @@
|
||||
</template>
|
||||
<template #body="{ isOpen }">
|
||||
<div v-show="isOpen">
|
||||
<div class="mt-1 rounded-lg bg-surface-white py-1 text-base shadow-2xl">
|
||||
<div class="relative px-1.5 pt-0.5">
|
||||
<div
|
||||
class="relative mt-1 rounded-lg bg-surface-modal text-base shadow-2xl"
|
||||
>
|
||||
<div class="relative px-1.5 pt-1.5">
|
||||
<ComboboxInput
|
||||
ref="search"
|
||||
class="form-input w-full"
|
||||
class="form-input w-full focus:bg-surface-gray-3 hover:bg-surface-gray-4 text-ink-gray-8"
|
||||
type="text"
|
||||
@change="
|
||||
(e) => {
|
||||
@ -60,7 +62,7 @@
|
||||
class="absolute right-1.5 inline-flex h-7 w-7 items-center justify-center"
|
||||
@click="selectedValue = null"
|
||||
>
|
||||
<FeatherIcon name="x" class="w-4" />
|
||||
<FeatherIcon name="x" class="w-4 text-ink-gray-8" />
|
||||
</button>
|
||||
</div>
|
||||
<ComboboxOptions
|
||||
@ -75,7 +77,7 @@
|
||||
>
|
||||
<div
|
||||
v-if="group.group && !group.hideLabel"
|
||||
class="px-2.5 py-1.5 text-sm font-medium text-ink-gray-4"
|
||||
class="truncate bg-surface-modal px-2.5 py-1.5 text-sm font-medium text-ink-gray-5"
|
||||
>
|
||||
{{ group.group }}
|
||||
</div>
|
||||
@ -88,8 +90,8 @@
|
||||
>
|
||||
<li
|
||||
:class="[
|
||||
'flex items-center rounded px-2.5 py-1.5 text-base',
|
||||
{ 'bg-surface-gray-2': active },
|
||||
'flex cursor-pointer items-center rounded px-2.5 py-1.5 text-base',
|
||||
{ 'bg-surface-gray-3': active },
|
||||
]"
|
||||
>
|
||||
<slot
|
||||
@ -100,19 +102,24 @@
|
||||
name="item-label"
|
||||
v-bind="{ active, selected, option }"
|
||||
>
|
||||
{{ option.label }}
|
||||
<div class="flex-1 truncate text-ink-gray-7">
|
||||
{{ option.label }}
|
||||
</div>
|
||||
</slot>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
</div>
|
||||
<li
|
||||
v-if="groups.length == 0"
|
||||
class="mt-1.5 rounded-md px-2.5 py-1.5 text-base text-ink-gray-5"
|
||||
class="my-1.5 rounded-md px-2.5 py-1.5 text-base text-ink-gray-5"
|
||||
>
|
||||
No results found
|
||||
</li>
|
||||
</ComboboxOptions>
|
||||
<div v-if="slots.footer" class="border-t p-1.5 pb-0.5">
|
||||
<div
|
||||
v-if="slots.footer"
|
||||
class="border-t border-outline-gray-modals p-1.5"
|
||||
>
|
||||
<slot
|
||||
name="footer"
|
||||
v-bind="{ value: search?.el._value, close }"
|
||||
@ -219,7 +226,7 @@ function filterOptions(options) {
|
||||
return options.filter((option) => {
|
||||
let searchTexts = [option.label, option.value]
|
||||
return searchTexts.some((text) =>
|
||||
(text || '').toString().toLowerCase().includes(query.value.toLowerCase())
|
||||
(text || '').toString().toLowerCase().includes(query.value.toLowerCase()),
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -272,7 +279,9 @@ const inputClasses = computed(() => {
|
||||
'border border-outline-gray-2 bg-surface-white placeholder-ink-gray-4 hover:border-outline-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3',
|
||||
disabled: [
|
||||
'border bg-surface-menu-bar placeholder-ink-gray-3',
|
||||
props.variant === 'outline' ? 'border-outline-gray-2' : 'border-transparent',
|
||||
props.variant === 'outline'
|
||||
? 'border-outline-gray-2'
|
||||
: 'border-transparent',
|
||||
],
|
||||
}[variant]
|
||||
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
|
||||
<template #body>
|
||||
<div
|
||||
class="rounded-lg bg-surface-white shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
class="mt-2 min-w-40 divide-y divide-outline-gray-modals rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
:class="{
|
||||
'mt-2': ['bottom', 'left', 'right'].includes(placement),
|
||||
'ml-2': placement == 'right-start',
|
||||
}"
|
||||
>
|
||||
<MenuItems
|
||||
class="min-w-40 divide-y divide-gray-100"
|
||||
class="min-w-40 divide-y divide-outline-gray-modals"
|
||||
:class="{
|
||||
'left-0 origin-top-left': placement == 'left',
|
||||
'right-0 origin-top-right': placement == 'right',
|
||||
@ -52,7 +52,7 @@
|
||||
<button
|
||||
v-else
|
||||
:class="[
|
||||
active ? 'bg-surface-gray-2' : 'text-ink-gray-8',
|
||||
active ? 'bg-surface-gray-3' : 'text-ink-gray-6',
|
||||
'group flex h-7 w-full items-center rounded px-2 text-base',
|
||||
]"
|
||||
@click="item.onClick"
|
||||
@ -68,7 +68,7 @@
|
||||
v-else-if="item.icon"
|
||||
:is="item.icon"
|
||||
/>
|
||||
<span class="whitespace-nowrap">
|
||||
<span class="whitespace-nowrap text-ink-gray-7">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@ -60,11 +60,13 @@
|
||||
clip-path: inset(22px 0 0 0);
|
||||
"
|
||||
>
|
||||
<CameraIcon class="h-6 w-6 cursor-pointer text-ink-white" />
|
||||
<CameraIcon
|
||||
class="h-6 w-6 cursor-pointer text-white"
|
||||
/>
|
||||
</div>
|
||||
</component>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 truncate">
|
||||
<div class="flex flex-col gap-2 truncate text-ink-gray-9">
|
||||
<div class="truncate text-2xl font-medium">
|
||||
<span v-if="contact.data.salutation">
|
||||
{{ contact.data.salutation + '. ' }}
|
||||
@ -150,7 +152,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</Resizer>
|
||||
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
|
||||
<Tabs class="!h-full" v-model="tabIndex" :tabs="tabs">
|
||||
<template #tab="{ tab, selected }">
|
||||
<button
|
||||
class="group flex items-center gap-2 border-b border-transparent py-2.5 text-base text-ink-gray-5 duration-300 ease-in-out hover:border-outline-gray-3 hover:text-ink-gray-9"
|
||||
@ -669,4 +671,4 @@ const dealColumns = [
|
||||
width: '8rem',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
</Tabs>
|
||||
<Resizer side="right" class="flex flex-col justify-between border-l">
|
||||
<div
|
||||
class="flex h-10.5 cursor-copy items-center border-b px-5 py-2.5 text-lg font-medium"
|
||||
class="flex h-10.5 cursor-copy items-center border-b px-5 py-2.5 text-lg font-medium text-ink-gray-9"
|
||||
@click="copyToClipboard(deal.data.name)"
|
||||
>
|
||||
{{ __(deal.data.name) }}
|
||||
@ -64,7 +64,7 @@
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div class="flex flex-col gap-2.5 truncate">
|
||||
<div class="flex flex-col gap-2.5 truncate text-ink-gray-9">
|
||||
<Tooltip :text="organization.data?.name || __('Set an organization')">
|
||||
<div class="truncate text-2xl font-medium">
|
||||
{{ organization.data?.name || __('Untitled') }}
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
</Tabs>
|
||||
<Resizer class="flex flex-col justify-between border-l" side="right">
|
||||
<div
|
||||
class="flex h-10.5 cursor-copy items-center border-b px-5 py-2.5 text-lg font-medium"
|
||||
class="flex h-10.5 cursor-copy items-center border-b px-5 py-2.5 text-lg font-medium text-ink-gray-9"
|
||||
@click="copyToClipboard(lead.data.name)"
|
||||
>
|
||||
{{ __(lead.data.name) }}
|
||||
@ -102,13 +102,13 @@
|
||||
clip-path: inset(12px 0 0 0);
|
||||
"
|
||||
>
|
||||
<CameraIcon class="size-4 cursor-pointer text-ink-white" />
|
||||
<CameraIcon class="size-4 cursor-pointer text-white" />
|
||||
</div>
|
||||
</component>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2.5 truncate">
|
||||
<Tooltip :text="lead.data.lead_name || __('Set first name')">
|
||||
<div class="truncate text-2xl font-medium">
|
||||
<div class="truncate text-2xl font-medium text-ink-gray-9">
|
||||
{{ lead.data.lead_name || __('Untitled') }}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@ -53,12 +53,12 @@
|
||||
clip-path: inset(22px 0 0 0);
|
||||
"
|
||||
>
|
||||
<CameraIcon class="h-6 w-6 cursor-pointer text-ink-white" />
|
||||
<CameraIcon class="h-6 w-6 cursor-pointer text-white" />
|
||||
</div>
|
||||
</component>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 truncate">
|
||||
<div class="truncate text-lg font-medium">
|
||||
<div class="truncate text-lg font-medium text-ink-gray-9">
|
||||
<span v-if="contact.data.salutation">
|
||||
{{ contact.data.salutation + '. ' }}
|
||||
</span>
|
||||
|
||||
@ -22,10 +22,10 @@
|
||||
</Tooltip>
|
||||
</template>
|
||||
</LayoutHeader>
|
||||
<div class="flex flex-col overflow-hidden">
|
||||
<div class="flex flex-col overflow-hidden text-ink-gray-9">
|
||||
<div
|
||||
v-if="notifications.data?.length"
|
||||
class="divide-y overflow-y-auto text-base"
|
||||
class="divide-y divide-outline-gray-1 overflow-y-auto text-base"
|
||||
>
|
||||
<RouterLink
|
||||
v-for="n in notifications.data"
|
||||
|
||||
@ -56,12 +56,12 @@
|
||||
clip-path: inset(22px 0 0 0);
|
||||
"
|
||||
>
|
||||
<CameraIcon class="h-6 w-6 cursor-pointer text-ink-white" />
|
||||
<CameraIcon class="h-6 w-6 cursor-pointer text-white" />
|
||||
</div>
|
||||
</component>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 truncate">
|
||||
<div class="truncate text-lg font-medium">
|
||||
<div class="truncate text-lg font-medium text-ink-gray-9">
|
||||
{{ organization.doc.name }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
@click="editNote(note)"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="truncate text-lg font-medium">
|
||||
<div class="truncate text-lg font-medium text-ink-gray-9">
|
||||
{{ note.title }}
|
||||
</div>
|
||||
<Dropdown
|
||||
|
||||
@ -60,12 +60,12 @@
|
||||
clip-path: inset(22px 0 0 0);
|
||||
"
|
||||
>
|
||||
<CameraIcon class="h-6 w-6 cursor-pointer text-ink-white" />
|
||||
<CameraIcon class="h-6 w-6 cursor-pointer text-white" />
|
||||
</div>
|
||||
</component>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 truncate">
|
||||
<div class="truncate text-2xl font-medium">
|
||||
<div class="truncate text-2xl font-medium text-ink-gray-9">
|
||||
<span>{{ organization.doc.name }}</span>
|
||||
</div>
|
||||
<div
|
||||
@ -135,7 +135,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</Resizer>
|
||||
<Tabs class="overflow-hidden" v-model="tabIndex" :tabs="tabs">
|
||||
<Tabs class="!h-full" v-model="tabIndex" :tabs="tabs">
|
||||
<template #tab="{ tab, selected }">
|
||||
<button
|
||||
class="group flex items-center gap-2 border-b border-transparent py-2.5 text-base text-ink-gray-5 duration-300 ease-in-out hover:border-outline-gray-3 hover:text-ink-gray-9"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user