Revert "fix: bump frappe UI"
This commit is contained in:
+7
-7
@@ -11,19 +11,19 @@
|
||||
"dependencies": {
|
||||
"@headlessui/vue": "^1.7.22",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@tiptap/core": "^3.26.0",
|
||||
"@tiptap/extension-table": "^3.26.0",
|
||||
"@tiptap/extension-text-align": "^3.26.0",
|
||||
"@tiptap/extension-text-style": "^3.26.0",
|
||||
"@tiptap/pm": "^3.26.0",
|
||||
"@tiptap/starter-kit": "^3.26.0",
|
||||
"@tiptap/core": "^3.11.0",
|
||||
"@tiptap/extension-table": "^3.11.0",
|
||||
"@tiptap/extension-text-align": "^3.11.0",
|
||||
"@tiptap/extension-text-style": "^3.11.0",
|
||||
"@tiptap/pm": "^3.11.0",
|
||||
"@tiptap/starter-kit": "^3.11.0",
|
||||
"@twilio/voice-sdk": "^2.15.0",
|
||||
"@vee-validate/zod": "^4.8.2",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vueuse/core": "^13.8.0",
|
||||
"@vueuse/integrations": "^13.8.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"frappe-ui": "1.0.0-beta.14",
|
||||
"frappe-ui": "1.0.0-beta.3",
|
||||
"gemoji": "^8.1.0",
|
||||
"mime": "^3.0.0",
|
||||
"pinia": "^2.0.33",
|
||||
|
||||
+2
-2
@@ -32,14 +32,14 @@ onMounted(() => {
|
||||
window.addEventListener("online", () => {
|
||||
toast.create({
|
||||
message: __("You are now online."),
|
||||
icon: h(Wifi, { class: "text-ink-base" }),
|
||||
icon: h(Wifi, { class: "text-ink-white" }),
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("offline", () => {
|
||||
toast.create({
|
||||
message: __("You are now offline."),
|
||||
icon: h(WifiOff, { class: "text-ink-base" }),
|
||||
icon: h(WifiOff, { class: "text-ink-white" }),
|
||||
});
|
||||
});
|
||||
!isCustomerPortal.value && setConfig("localTimezone", window.timezone?.user);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<Popover
|
||||
placement="right-start"
|
||||
class="flex w-full"
|
||||
trigger="hover"
|
||||
:hoverDelay="0.1"
|
||||
:leaveDelay="0.1"
|
||||
>
|
||||
<template #target="{ togglePopover }">
|
||||
<button
|
||||
:class="[
|
||||
'group w-full flex h-7 items-center justify-between rounded px-2 text-base text-ink-gray-8 hover:bg-surface-gray-2',
|
||||
]"
|
||||
@click.prevent="togglePopover()"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<AppsIcon />
|
||||
<span class="whitespace-nowrap">Apps</span>
|
||||
</div>
|
||||
<ChevronRight class="h-4 w-4 stroke-1.5" />
|
||||
</button>
|
||||
</template>
|
||||
<template #body>
|
||||
<div
|
||||
class="flex flex-col justify-between mx-3 p-1.5 rounded-lg border border-outline-gray-1 bg-surface-white shadow-xl"
|
||||
>
|
||||
<div v-for="app in apps.data" key="name">
|
||||
<a
|
||||
:href="app.route"
|
||||
class="flex gap-2 rounded items-center hover:bg-surface-gray-2 p-1.5"
|
||||
>
|
||||
<img class="size-6" :src="app.logo" />
|
||||
<div class="text-sm" @click="app.onClick">
|
||||
{{ app.title }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Popover, createResource } from "frappe-ui";
|
||||
import ChevronRight from "~icons/lucide/chevron-right";
|
||||
import AppsIcon from "./icons/AppsIcon.vue";
|
||||
|
||||
const apps = createResource({
|
||||
url: "frappe.apps.get_apps",
|
||||
cache: "apps",
|
||||
auto: true,
|
||||
transform: (data) => {
|
||||
let _apps = [
|
||||
{
|
||||
name: "frappe",
|
||||
logo: "/assets/helpdesk/desk/desk.png",
|
||||
title: "Desk",
|
||||
route: "/desk/helpdesk",
|
||||
},
|
||||
];
|
||||
data.map((app) => {
|
||||
if (app.name === "helpdesk") return;
|
||||
_apps.push({
|
||||
name: app.name,
|
||||
logo: app.logo,
|
||||
title: app.title,
|
||||
route: app.route,
|
||||
});
|
||||
});
|
||||
return _apps;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -25,8 +25,9 @@
|
||||
{{ placeholder || "" }}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="lucide-chevron-down h-4 w-4 text-ink-gray-7"
|
||||
<FeatherIcon
|
||||
name="chevron-down"
|
||||
class="h-4 w-4 text-ink-gray-7"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
@@ -36,7 +37,7 @@
|
||||
<template #body="{ isOpen }">
|
||||
<div v-show="isOpen">
|
||||
<div
|
||||
class="mt-1 rounded-lg bg-surface-base py-1 text-base shadow-2xl"
|
||||
class="mt-1 rounded-lg bg-surface-white py-1 text-base shadow-2xl"
|
||||
>
|
||||
<div class="relative px-1.5 pt-0.5">
|
||||
<ComboboxInput
|
||||
@@ -56,7 +57,7 @@
|
||||
class="absolute inset-y-0 end-3 top-px flex items-center"
|
||||
@click="selectedValue = null"
|
||||
>
|
||||
<span class="lucide-x size-4" aria-hidden="true" />
|
||||
<FeatherIcon name="x" class="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
<ComboboxOptions
|
||||
@@ -71,7 +72,7 @@
|
||||
>
|
||||
<div
|
||||
v-if="group.group && !group.hideLabel"
|
||||
class="px-2.5 py-1.5 text-sm-medium text-ink-gray-4"
|
||||
class="px-2.5 py-1.5 text-sm font-medium text-ink-gray-4"
|
||||
>
|
||||
{{ group.group }}
|
||||
</div>
|
||||
@@ -128,7 +129,7 @@ import {
|
||||
ComboboxOption,
|
||||
ComboboxOptions,
|
||||
} from "@headlessui/vue";
|
||||
import { Popover } from "frappe-ui";
|
||||
import { FeatherIcon, Popover } from "frappe-ui";
|
||||
import { computed, nextTick, ref, useAttrs, useSlots, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -249,7 +250,7 @@ const inputClasses = computed(() => {
|
||||
let sizeClasses = {
|
||||
sm: "text-base rounded h-7",
|
||||
md: "text-base rounded h-8",
|
||||
lg: "text-md rounded-md h-10",
|
||||
lg: "text-lg rounded-md h-10",
|
||||
xl: "text-xl rounded-md h-10",
|
||||
}[props.size];
|
||||
|
||||
@@ -263,11 +264,11 @@ const inputClasses = computed(() => {
|
||||
let variant = props.disabled ? "disabled" : props.variant;
|
||||
let variantClasses = {
|
||||
subtle:
|
||||
"border border-outline-gray-1 bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-elevation-2 hover:bg-surface-gray-3 focus:bg-surface-base focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3",
|
||||
"border border-outline-gray-1 bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 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",
|
||||
outline:
|
||||
"border border-outline-gray-2 bg-surface-base placeholder-ink-gray-4 hover:border-outline-gray-3 hover:shadow-sm focus:bg-surface-base focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3",
|
||||
"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-sidebar placeholder-ink-gray-3",
|
||||
"border bg-surface-menu-bar placeholder-ink-gray-3",
|
||||
props.variant === "outline"
|
||||
? "border-outline-gray-2"
|
||||
: "border-transparent",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</template>
|
||||
<template #body="{ close }">
|
||||
<div
|
||||
class="flex flex-col mx-3 p-1.5 rounded-lg border border-outline-gray-1 bg-surface-base shadow-xl min-w-[140px]"
|
||||
class="flex flex-col mx-3 p-1.5 rounded-lg border border-outline-gray-1 bg-surface-white shadow-xl min-w-[140px]"
|
||||
>
|
||||
<button
|
||||
v-for="option in agentStatusStore.statusOptions"
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
</div>
|
||||
<div
|
||||
@click="showCallLogDetailModal = true"
|
||||
class="flex flex-col gap-2 cursor-pointer border-transparent shadow rounded-md bg-surface-elevation-1 px-3 py-2.5 text-ink-gray-9"
|
||||
class="flex flex-col gap-2 cursor-pointer border-transparent shadow rounded-md bg-surface-cards 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-medium">
|
||||
<div class="inline-flex gap-2 items-center text-base font-medium">
|
||||
<div>
|
||||
{{
|
||||
activity.call_type == "Incoming"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="flex items-end w-full gap-2">
|
||||
<slot name="text">
|
||||
<div
|
||||
class="text-2xl-medium text-center text-ink-gray-8 whitespace-nowrap"
|
||||
class="text-2xl font-medium text-center text-ink-gray-8 whitespace-nowrap"
|
||||
>
|
||||
{{ text }}
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
<div class="rtl:flex rtl:gap-1">
|
||||
<span>vs</span> <span>{{ currentDuration.toLowerCase() }}</span>
|
||||
</div>
|
||||
<span class="lucide-chevron-down size-4" aria-hidden="true" />
|
||||
<FeatherIcon name="chevron-down" class="size-4" />
|
||||
</div>
|
||||
<template #item-label="{ item }">
|
||||
<div
|
||||
@@ -44,10 +44,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #item-suffix="{ item }">
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="item.label == __(currentDuration)"
|
||||
class="lucide-check size-4"
|
||||
aria-hidden="true"
|
||||
name="check"
|
||||
class="size-4"
|
||||
/>
|
||||
</template>
|
||||
</Dropdown>
|
||||
@@ -63,7 +63,7 @@
|
||||
<div class="flex items-end w-full gap-2 justify-between flex-1">
|
||||
<slot name="text">
|
||||
<span
|
||||
class="text-2xl-medium text-center text-ink-gray-8 whitespace-nowrap"
|
||||
class="text-2xl font-medium text-center text-ink-gray-8 whitespace-nowrap"
|
||||
>
|
||||
{{ text }}
|
||||
</span>
|
||||
@@ -84,7 +84,7 @@
|
||||
class="flex items-center gap-0.5 text-ink-gray-5 hover:text-ink-gray-6 cursor-pointer shrink-0"
|
||||
>
|
||||
vs {{ currentDuration.toLowerCase() }}
|
||||
<span class="lucide-chevron-down size-4" aria-hidden="true" />
|
||||
<FeatherIcon name="chevron-down" class="size-4" />
|
||||
</div>
|
||||
<template #item-label="{ item }">
|
||||
<div
|
||||
@@ -96,10 +96,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #item-suffix="{ item }">
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="item.label == __(currentDuration)"
|
||||
class="lucide-check size-4"
|
||||
aria-hidden="true"
|
||||
name="check"
|
||||
class="size-4"
|
||||
/>
|
||||
</template>
|
||||
</Dropdown>
|
||||
@@ -111,7 +111,7 @@
|
||||
<script setup lang="ts">
|
||||
import { __ } from "@/translation";
|
||||
import { EChartsOption } from "echarts";
|
||||
import { Dropdown, ECharts } from "frappe-ui";
|
||||
import { Dropdown, ECharts, FeatherIcon } from "frappe-ui";
|
||||
import { computed, type PropType } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@@ -39,19 +39,21 @@
|
||||
:id="`comment-${name}`"
|
||||
class="rounded-md bg-surface-gray-1 transition-colors px-3 py-1.5"
|
||||
>
|
||||
<Editor v-model="_content" :extensions="extensions" :editable="editable">
|
||||
<template #default="{ editor }">
|
||||
<EditorBubbleMenu :editor="editor" :items="textEditorMenuItems" />
|
||||
<EditorContent
|
||||
:editor="editor"
|
||||
:class="[
|
||||
'prose-f shrink text-p-sm transition-all duration-300 ease-in-out block w-full content',
|
||||
getFontFamily(_content),
|
||||
]"
|
||||
@keydown.ctrl.enter.capture.stop="handleSaveComment"
|
||||
@keydown.meta.enter.capture.stop="handleSaveComment"
|
||||
/>
|
||||
<div v-if="editable" class="flex flex-row-reverse gap-2">
|
||||
<TextEditor
|
||||
:editor-class="[
|
||||
'prose-f shrink text-p-sm transition-all duration-300 ease-in-out block w-full content',
|
||||
getFontFamily(_content),
|
||||
]"
|
||||
:content="_content"
|
||||
:editable="editable"
|
||||
:bubble-menu="textEditorMenuButtons"
|
||||
:mentions="userMentions"
|
||||
@change="(event:string) => {_content = event}"
|
||||
@keydown.ctrl.enter.capture.stop="handleSaveComment"
|
||||
@keydown.meta.enter.capture.stop="handleSaveComment"
|
||||
>
|
||||
<template #bottom v-if="editable">
|
||||
<div class="flex flex-row-reverse gap-2">
|
||||
<div>
|
||||
<Button
|
||||
:label="
|
||||
@@ -68,7 +70,7 @@
|
||||
<Button label="Discard" @click="handleDiscard" />
|
||||
</div>
|
||||
</template>
|
||||
</Editor>
|
||||
</TextEditor>
|
||||
<div
|
||||
class="flex flex-wrap gap-2 mb-2"
|
||||
v-if="!editable && Boolean(attachments.length)"
|
||||
@@ -95,13 +97,13 @@
|
||||
</template>
|
||||
<template #body>
|
||||
<div
|
||||
class="bg-surface-base rounded-lg shadow-lg p-2 border border-outline-gray-2"
|
||||
class="bg-surface-white rounded-lg shadow-lg p-2 border border-outline-gray-2"
|
||||
>
|
||||
<div class="grid grid-cols-6 gap-2">
|
||||
<button
|
||||
v-for="emoji in emojiList"
|
||||
:key="emoji"
|
||||
class="size-6 flex items-center justify-center rounded hover:bg-surface-gray-2 text-md transition-colors"
|
||||
class="size-6 flex items-center justify-center rounded hover:bg-surface-gray-2 text-lg transition-colors"
|
||||
@click="handleReaction(emoji)"
|
||||
>
|
||||
{{ emoji }}
|
||||
@@ -115,7 +117,7 @@
|
||||
<Tooltip>
|
||||
<template #body>
|
||||
<div
|
||||
class="bg-surface-gray-10 px-2 py-1 text-center text-p-xs text-ink-base shadow-xl rounded"
|
||||
class="bg-surface-gray-7 px-2 py-1 text-center text-p-xs text-ink-white shadow-xl rounded"
|
||||
>
|
||||
<span v-for="(user, idx) in reaction.users" :key="user.user"
|
||||
>{{ user.full_name
|
||||
@@ -127,7 +129,7 @@
|
||||
class="flex items-center gap-1 px-2 py-1 rounded-full text-sm transition-colors"
|
||||
:class="
|
||||
reaction.current_user_reacted
|
||||
? 'bg-surface-blue-2 text-ink-blue-6 hover:bg-surface-blue-3'
|
||||
? 'bg-surface-blue-2 text-ink-blue-3 hover:bg-surface-blue-3'
|
||||
: 'bg-surface-gray-3 text-ink-gray-6 hover:bg-surface-gray-4'
|
||||
"
|
||||
v-if="reaction.count !== 0"
|
||||
@@ -155,34 +157,24 @@ import { updateRes as updateComment } from "@/stores/knowledgeBase";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { __ } from "@/translation";
|
||||
import { CommentActivity } from "@/types";
|
||||
import { textEditorMenuItems } from "@/editor-menu";
|
||||
import {
|
||||
CleanStyles,
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
} from "@/tiptap-extensions";
|
||||
import {
|
||||
ConfirmDelete,
|
||||
dateFormat,
|
||||
dateTooltipFormat,
|
||||
getFontFamily,
|
||||
isContentEmpty,
|
||||
textEditorMenuButtons,
|
||||
timeAgo,
|
||||
} from "@/utils";
|
||||
import {
|
||||
Avatar,
|
||||
Dropdown,
|
||||
Popover,
|
||||
TextEditor,
|
||||
Tooltip,
|
||||
createResource,
|
||||
toast,
|
||||
} from "frappe-ui";
|
||||
import {
|
||||
Editor,
|
||||
EditorBubbleMenu,
|
||||
EditorContent,
|
||||
RichTextKit,
|
||||
} from "frappe-ui/editor";
|
||||
import { PropType, computed, onMounted, ref } from "vue";
|
||||
|
||||
const authStore = useAuthStore();
|
||||
@@ -206,12 +198,6 @@ const isTicketMergedComment = computed(() => {
|
||||
});
|
||||
const agentStore = useAgentStore();
|
||||
const userMentions = computed(() => agentStore.dropdown ?? []);
|
||||
const extensions = [
|
||||
RichTextKit.configure({ mention: { items: userMentions } }),
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
CleanStyles,
|
||||
];
|
||||
|
||||
const emit = defineEmits(["update"]);
|
||||
const isConfirmingDelete = ref(false);
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<Editor
|
||||
<TextEditor
|
||||
v-if="agentsList.data"
|
||||
ref="editorRef"
|
||||
v-model="newComment"
|
||||
:extensions="extensions"
|
||||
:editor-class="[
|
||||
'prose-sm max-w-none',
|
||||
editable &&
|
||||
'min-h-[7rem] mx-5 max-h-[44vh] overflow-y-auto border-t py-3',
|
||||
getFontFamily(newComment),
|
||||
]"
|
||||
:content="newComment"
|
||||
:starterkit-options="{ heading: { levels: [2, 3, 4, 5, 6] } }"
|
||||
:placeholder="placeholder"
|
||||
:editable="editable"
|
||||
:upload-function="(file:any)=>uploadFunction(file, doctype, ticketId)"
|
||||
:mentions="dropdown"
|
||||
@change="editable ? (newComment = $event) : null"
|
||||
:extensions="[ComponentUtils, HandleExcelPaste, CleanStyles]"
|
||||
:uploadFunction="(file:any)=>uploadFunction(file, doctype, ticketId)"
|
||||
>
|
||||
<template #default="{ editor }">
|
||||
<EditorContent
|
||||
:editor="editor"
|
||||
:class="[
|
||||
'prose-sm max-w-none',
|
||||
editable &&
|
||||
'min-h-[7rem] mx-5 max-h-[44vh] overflow-y-auto border-t py-3',
|
||||
getFontFamily(newComment),
|
||||
]"
|
||||
/>
|
||||
<template #bottom>
|
||||
<!-- Attachments -->
|
||||
<div class="flex flex-wrap gap-2 my-2 ms-5">
|
||||
<AttachmentItem
|
||||
@@ -27,9 +27,9 @@
|
||||
:url="!['MOV', 'MP4'].includes(a.file_type) ? a.file_url : null"
|
||||
>
|
||||
<template #suffix>
|
||||
<span
|
||||
class="lucide-x h-3.5"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
class="h-3.5"
|
||||
name="x"
|
||||
@click.stop="removeAttachment(a)"
|
||||
/>
|
||||
</template>
|
||||
@@ -63,9 +63,9 @@
|
||||
</button>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div class="ml-0.5 mr-1 h-5 w-px bg-surface-gray-3" />
|
||||
<div class="h-4 w-[2px] border-s" />
|
||||
</div>
|
||||
<EditorFixedMenu :editor="editor" :items="textEditorMenuItems" />
|
||||
<TextEditorFixedMenu :buttons="textEditorMenuButtons" />
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-x-2 w-[40%]">
|
||||
<Button
|
||||
@@ -96,23 +96,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Editor>
|
||||
</TextEditor>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { FileUploader, createResource } from "frappe-ui";
|
||||
import {
|
||||
Editor,
|
||||
EditorContent,
|
||||
EditorFixedMenu,
|
||||
RichTextKit,
|
||||
} from "frappe-ui/editor";
|
||||
FileUploader,
|
||||
TextEditor,
|
||||
TextEditorFixedMenu,
|
||||
createResource,
|
||||
} from "frappe-ui";
|
||||
import { useOnboarding } from "frappe-ui/frappe";
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
|
||||
import { AttachmentItem } from "@/components/";
|
||||
import { AttachmentIcon } from "@/components/icons/";
|
||||
import { useTyping } from "@/composables/realtime";
|
||||
import { textEditorMenuItems } from "@/editor-menu";
|
||||
import { useAgentStore } from "@/stores/agent";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import {
|
||||
@@ -124,6 +122,7 @@ import {
|
||||
getFontFamily,
|
||||
isContentEmpty,
|
||||
removeAttachmentFromServer,
|
||||
textEditorMenuButtons,
|
||||
uploadFunction,
|
||||
} from "@/utils";
|
||||
import { useStorage } from "@vueuse/core";
|
||||
@@ -224,16 +223,6 @@ async function submitComment() {
|
||||
const editorRef = ref(null);
|
||||
const editor = computed(() => editorRef.value?.editor);
|
||||
|
||||
const extensions = [
|
||||
RichTextKit.configure({
|
||||
heading: { levels: [2, 3, 4, 5, 6] },
|
||||
mention: { items: dropdown },
|
||||
}),
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
CleanStyles,
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
if (
|
||||
agentsList.value.loading ||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<Editor
|
||||
<TextEditor
|
||||
ref="editorRef"
|
||||
v-model="internalContent"
|
||||
:extensions="editorExtensions"
|
||||
:upload-function="uploadFn"
|
||||
:editor-class="editorClass"
|
||||
:bubble-menu="false"
|
||||
:content="internalContent"
|
||||
:extensions="extensions"
|
||||
v-bind="uploadFn ? { uploadFunction: uploadFn } : {}"
|
||||
:placeholder="placeholder"
|
||||
@change="(val: string) => (internalContent = val)"
|
||||
>
|
||||
<template #default="{ editor }">
|
||||
<template #top>
|
||||
<div
|
||||
class="flex items-center overflow-x-auto rounded-t border border-b-0 border-[--surface-gray-2] px-2 py-1"
|
||||
>
|
||||
@@ -35,11 +38,10 @@
|
||||
</FileUploader>
|
||||
<div class="h-4 w-[2px] border-s" />
|
||||
</div>
|
||||
<EditorFixedMenu :editor="editor" :items="savedReplyMenuItems" />
|
||||
<TextEditorFixedMenu :buttons="(menuButtons as any)" />
|
||||
</div>
|
||||
<EditorContent :editor="editor" :class="editorClass" />
|
||||
</template>
|
||||
</Editor>
|
||||
</TextEditor>
|
||||
<div
|
||||
v-if="showAttachments && attachments?.length"
|
||||
class="flex flex-wrap gap-2 mt-2"
|
||||
@@ -51,9 +53,9 @@
|
||||
:url="attachment.file_url"
|
||||
>
|
||||
<template #suffix>
|
||||
<span
|
||||
class="lucide-x h-3.5 cursor-pointer"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
class="h-3.5 cursor-pointer"
|
||||
name="x"
|
||||
@click.self.stop="removeAttachment(attachment)"
|
||||
/>
|
||||
</template>
|
||||
@@ -65,21 +67,21 @@
|
||||
<script setup lang="ts">
|
||||
import { AttachmentItem } from "@/components";
|
||||
import { AttachmentIcon } from "@/components/icons";
|
||||
import { savedReplyMenuItems } from "@/editor-menu";
|
||||
import { menuButtons } from "@/components/Settings/SavedReplies/savedReplies";
|
||||
import { getUserEmailInfo } from "@/composables/useUserEmailInfo";
|
||||
import {
|
||||
CleanStyles,
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
} from "@/tiptap-extensions";
|
||||
import { isContentEmpty, uploadFunction } from "@/utils";
|
||||
import { FileUploader, type UploadedFile } from "frappe-ui";
|
||||
import { isContentEmpty } from "@/utils";
|
||||
import {
|
||||
Editor,
|
||||
EditorContent,
|
||||
EditorFixedMenu,
|
||||
RichTextKit,
|
||||
} from "frappe-ui/editor";
|
||||
FeatherIcon,
|
||||
FileUploader,
|
||||
TextEditor,
|
||||
TextEditorFixedMenu,
|
||||
type UploadedFile,
|
||||
} from "frappe-ui";
|
||||
import { computed, nextTick, onMounted, ref, watch } from "vue";
|
||||
|
||||
type UploadFunction = (file: File) => Promise<UploadedFile>;
|
||||
@@ -103,7 +105,6 @@ const props = withDefaults(
|
||||
showSignature: false,
|
||||
type: "Saved Reply",
|
||||
showAttachments: false,
|
||||
uploadFn: (file: File) => uploadFunction(file),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -119,15 +120,15 @@ const savedReplyClass = [
|
||||
"!prose-sm max-w-full overflow-auto py-1.5 px-2",
|
||||
"rounded-b border border-[--surface-gray-2] bg-surface-gray-2",
|
||||
"placeholder-ink-gray-4",
|
||||
"hover:border-outline-elevation-2 hover:shadow-sm",
|
||||
"focus:bg-surface-base focus:border-outline-gray-4 focus:shadow-sm focus:ring-0",
|
||||
"hover:border-outline-gray-modals 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 -mt-0.5",
|
||||
];
|
||||
|
||||
const emailClass = [
|
||||
"!prose-sm max-w-full overflow-auto py-1.5 px-2",
|
||||
"rounded-b border border-[--surface-gray-2] bg-surface-base",
|
||||
"rounded-b border border-[--surface-gray-2] bg-surface-white",
|
||||
"placeholder-ink-gray-4",
|
||||
"text-ink-gray-8 transition-colors -mt-0.5",
|
||||
];
|
||||
@@ -138,8 +139,6 @@ const editorClass = computed(() => [
|
||||
props.maxHeight,
|
||||
]);
|
||||
|
||||
const editorExtensions = computed(() => [RichTextKit, ...props.extensions]);
|
||||
|
||||
const userResource = getUserEmailInfo();
|
||||
|
||||
function getDefaultContent(signature: string): string {
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
v-bind="action"
|
||||
>
|
||||
<template v-if="action.icon" #prefix>
|
||||
<span
|
||||
:class="['h-4 w-4', lucideClass(action.icon)]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<FeatherIcon :name="action.icon" class="h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -20,12 +17,9 @@
|
||||
<Dropdown v-slot="{ open }" :options="g.action">
|
||||
<Button :label="g.label">
|
||||
<template #suffix>
|
||||
<span
|
||||
:class="[
|
||||
'h-4',
|
||||
open ? 'lucide-chevron-up' : 'lucide-chevron-down',
|
||||
]"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
:name="open ? 'chevron-up' : 'chevron-down'"
|
||||
class="h-4"
|
||||
/>
|
||||
</template>
|
||||
</Button>
|
||||
@@ -40,7 +34,6 @@
|
||||
<script setup>
|
||||
import { Dropdown } from "frappe-ui";
|
||||
import { computed } from "vue";
|
||||
import { lucideClass } from "@/utils";
|
||||
|
||||
const props = defineProps({
|
||||
actions: {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
:id="`communication-${name}`"
|
||||
v-bind="$attrs"
|
||||
class="grow cursor-pointer bg-surface-base rounded-md text-base leading-6 transition-all duration-300 ease-in-out border border-outline-gray-2"
|
||||
class="grow cursor-pointer bg-surface-white rounded-md text-base leading-6 transition-all duration-300 ease-in-out border border-outline-gray-2"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between gap-2"
|
||||
@@ -95,7 +95,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="border-0 border-t my-3 border-outline-elevation-2 !-mx-3" />
|
||||
<div class="border-0 border-t my-3 border-outline-gray-modals !-mx-3" />
|
||||
<EmailContent :content="content" />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<AttachmentItem
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<template>
|
||||
<Editor
|
||||
<TextEditor
|
||||
ref="editorRef"
|
||||
v-model="newEmail"
|
||||
:extensions="extensions"
|
||||
:editor-class="[
|
||||
'prose-sm max-w-full mx-6 md:mx-5 py-3',
|
||||
getFontFamily(newEmail),
|
||||
'[&_p.reply-to-content]:hidden',
|
||||
]"
|
||||
:content="newEmail"
|
||||
:starterkit-options="{ heading: { levels: [2, 3, 4, 5, 6] } }"
|
||||
:placeholder="placeholder"
|
||||
:editable="editable"
|
||||
:upload-function="(file:any)=>uploadFunction(file, doctype, ticketId)"
|
||||
@change="editable ? (newEmail = $event) : null"
|
||||
:extensions="[ComponentUtils, HandleExcelPaste, CleanStyles]"
|
||||
:uploadFunction="(file:any)=>uploadFunction(file, doctype, ticketId)"
|
||||
@keydown.capture="handleKeydown"
|
||||
>
|
||||
<template #default="{ editor }">
|
||||
<template #top>
|
||||
<div
|
||||
v-if="hasMultipleSenders"
|
||||
class="mx-6 md:mx-5 flex items-center gap-2 border-t py-2.5 h-12.5"
|
||||
@@ -93,22 +101,12 @@
|
||||
:custom-email-label="__('Add to recipients')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<EditorBubbleMenu :editor="editor" :items="articleToolbar" />
|
||||
|
||||
<div
|
||||
class="overflow-y-auto min-h-[7rem] max-h-[30vh] flex flex-col"
|
||||
@keydown.capture="handleKeydown"
|
||||
>
|
||||
<template #editor>
|
||||
<div class="overflow-y-auto min-h-[7rem] max-h-[30vh] flex flex-col">
|
||||
<div class="flex-1">
|
||||
<EditorContent
|
||||
:editor="editor"
|
||||
:class="[
|
||||
'prose-sm max-w-full mx-6 md:mx-5 py-3',
|
||||
getFontFamily(newEmail),
|
||||
'[&_p.reply-to-content]:hidden',
|
||||
]"
|
||||
/>
|
||||
<EditorContent :editor="editor" />
|
||||
</div>
|
||||
<div
|
||||
v-if="quotedContent"
|
||||
@@ -129,7 +127,8 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #bottom>
|
||||
<!-- Attachments -->
|
||||
<div class="flex flex-wrap gap-2 px-5 my-2">
|
||||
<AttachmentItem
|
||||
@@ -139,15 +138,15 @@
|
||||
:url="!['MOV', 'MP4'].includes(a.file_type) ? a.file_url : null"
|
||||
>
|
||||
<template #suffix>
|
||||
<span
|
||||
class="lucide-x h-3.5"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
class="h-3.5"
|
||||
name="x"
|
||||
@click.self.stop="removeAttachment(a)"
|
||||
/>
|
||||
</template>
|
||||
</AttachmentItem>
|
||||
</div>
|
||||
<!-- Editor Fixed Menu -->
|
||||
<!-- TextEditor Fixed Menu -->
|
||||
<div
|
||||
class="flex justify-between overflow-scroll px-4 py-2.5 items-center border-t"
|
||||
>
|
||||
@@ -185,9 +184,9 @@
|
||||
>
|
||||
<SavedReplyIcon class="h-4 w-4" />
|
||||
</button>
|
||||
<div class="ml-0.5 mr-1 h-5 w-px bg-surface-gray-3" />
|
||||
<div class="h-4 w-[2px] border-s" />
|
||||
</div>
|
||||
<EditorFixedMenu :editor="editor" :items="textEditorMenuItems" />
|
||||
<TextEditorFixedMenu :buttons="textEditorMenuButtons" />
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-x-2 sm:mt-0 w-[40%]">
|
||||
<Button label="Discard" @click="handleDiscard" />
|
||||
@@ -205,7 +204,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Editor>
|
||||
</TextEditor>
|
||||
<SavedRepliesSelectorModal
|
||||
v-model="showSavedRepliesSelectorModal"
|
||||
:doctype="doctype"
|
||||
@@ -220,7 +219,6 @@ import EmailMultiSelect from "@/components/EmailMultiSelect.vue";
|
||||
import { AttachmentIcon } from "@/components/icons";
|
||||
import { useTyping } from "@/composables/realtime";
|
||||
import { getUserEmailInfo } from "@/composables/useUserEmailInfo";
|
||||
import { textEditorMenuItems } from "@/editor-menu";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import {
|
||||
CleanStyles,
|
||||
@@ -232,19 +230,19 @@ import {
|
||||
htmlToText,
|
||||
isContentEmpty,
|
||||
removeAttachmentFromServer,
|
||||
textEditorMenuButtons,
|
||||
uploadFunction,
|
||||
validateEmailWithZod,
|
||||
} from "@/utils";
|
||||
import { EditorContent } from "@tiptap/vue-3";
|
||||
import { useStorage } from "@vueuse/core";
|
||||
import { FileUploader, createResource, toast } from "frappe-ui";
|
||||
import {
|
||||
Editor,
|
||||
EditorBubbleMenu,
|
||||
EditorContent,
|
||||
EditorFixedMenu,
|
||||
RichTextKit,
|
||||
articleToolbar,
|
||||
} from "frappe-ui/editor";
|
||||
FileUploader,
|
||||
TextEditor,
|
||||
TextEditorFixedMenu,
|
||||
createResource,
|
||||
toast,
|
||||
} from "frappe-ui";
|
||||
import { useOnboarding } from "frappe-ui/frappe";
|
||||
import {
|
||||
computed,
|
||||
@@ -301,13 +299,6 @@ const { onUserType, cleanup } = useTyping(props.ticketId);
|
||||
const editorRef = ref(null);
|
||||
const editor = computed(() => editorRef.value?.editor);
|
||||
|
||||
const extensions = [
|
||||
RichTextKit.configure({ heading: { levels: [2, 3, 4, 5, 6] } }),
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
CleanStyles,
|
||||
];
|
||||
|
||||
function focusEditorAtStart() {
|
||||
setTimeout(() => {
|
||||
editorRef.value?.editor?.commands?.focus("start");
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
:tooltip="copyOnClick ? __('Click to copy') : undefined"
|
||||
:class="[
|
||||
{
|
||||
'rounded border !bg-surface-base hover:!bg-surface-gray-1 focus-visible:ring-outline-gray-4':
|
||||
'rounded bg-surface-white hover:!bg-surface-gray-1 focus-visible:ring-outline-gray-4':
|
||||
variant === 'subtle',
|
||||
},
|
||||
copyOnClick
|
||||
@@ -22,9 +22,9 @@
|
||||
@keydown.delete.capture.stop="removeLastValue"
|
||||
>
|
||||
<template #suffix>
|
||||
<span
|
||||
class="lucide-x h-3.5"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
class="h-3.5"
|
||||
name="x"
|
||||
@click.stop="removeValue(value)"
|
||||
/>
|
||||
</template>
|
||||
@@ -42,7 +42,7 @@
|
||||
class="flex h-7 max-w-full w-auto items-center gap-2 rounded px-2 py-1 border border-transparent"
|
||||
:class="[
|
||||
variant == 'ghost'
|
||||
? 'bg-surface-base hover:bg-surface-base'
|
||||
? 'bg-surface-white hover:bg-surface-white'
|
||||
: 'bg-surface-gray-2 hover:bg-surface-gray-3',
|
||||
inputClass,
|
||||
]"
|
||||
@@ -62,7 +62,7 @@
|
||||
</ComboboxAnchor>
|
||||
<ComboboxPortal>
|
||||
<ComboboxContent
|
||||
class="z-10 mt-1 min-w-48 w-auto max-w-96 bg-surface-elevation-2 overflow-hidden rounded-lg shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||
class="z-10 mt-1 min-w-48 w-auto max-w-96 bg-surface-modal overflow-hidden rounded-lg shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||
position="popper"
|
||||
:align="'start'"
|
||||
@openAutoFocus.prevent
|
||||
@@ -72,7 +72,7 @@
|
||||
<ComboboxEmpty
|
||||
class="flex gap-2 rounded px-2 py-1 text-base text-ink-gray-5"
|
||||
>
|
||||
<span class="lucide-search h-4" aria-hidden="true" />
|
||||
<FeatherIcon name="search" class="h-4" />
|
||||
{{ __(emptyPlaceholder) }}
|
||||
</ComboboxEmpty>
|
||||
<ComboboxItem
|
||||
@@ -88,7 +88,7 @@
|
||||
size="lg"
|
||||
/>
|
||||
<div class="flex flex-col gap-1 p-1 text-ink-gray-8">
|
||||
<div class="text-base-medium">
|
||||
<div class="text-base font-medium">
|
||||
{{ getUsernameLabel(option.label) }}
|
||||
</div>
|
||||
<div class="text-sm text-ink-gray-5">
|
||||
@@ -105,7 +105,7 @@
|
||||
<ErrorMessage class="mt-2 pl-2" v-if="error" :message="error" />
|
||||
<div
|
||||
v-if="info"
|
||||
class="whitespace-pre-line text-sm text-ink-blue-6 mt-2 pl-2"
|
||||
class="whitespace-pre-line text-sm text-ink-blue-3 mt-2 pl-2"
|
||||
>
|
||||
{{ info }}
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<component v-if="icon" :is="icon" class="size-6 text-ink-gray-6" />
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<div class="text-base-medium text-ink-gray-6">
|
||||
<div class="text-base font-medium text-ink-gray-6">
|
||||
{{ __(title) }}
|
||||
</div>
|
||||
<div
|
||||
@@ -25,7 +25,7 @@
|
||||
class="flex h-full items-center justify-center absolute inset-x-0 top-0 pointer-events-none"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col items-center gap-2 text-xl-medium text-ink-gray-4 w-9/12 md:w-4/12"
|
||||
class="flex flex-col items-center gap-2 text-xl font-medium text-ink-gray-4 w-9/12 md:w-4/12"
|
||||
>
|
||||
<!-- overlay variant (for charts) -->
|
||||
<div
|
||||
@@ -33,7 +33,7 @@
|
||||
class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none -z-10"
|
||||
:style="{
|
||||
backgroundImage:
|
||||
'radial-gradient(ellipse at center, var(--surface-base) 10%, color-mix(in srgb, var(--surface-base) 90%, transparent) 25%, transparent 70%)',
|
||||
'radial-gradient(ellipse at center, var(--surface-white) 10%, color-mix(in srgb, var(--surface-white) 90%, transparent) 25%, transparent 70%)',
|
||||
}"
|
||||
/>
|
||||
<!-- Icon -->
|
||||
@@ -44,7 +44,7 @@
|
||||
:class="{
|
||||
'text-sm font-medium text-ink-gray-8': text === 'sm',
|
||||
'text-base font-medium text-ink-gray-8': text === 'md' || !text,
|
||||
'text-md font-medium text-ink-gray-8': text === 'lg',
|
||||
'text-lg font-medium text-ink-gray-8': text === 'lg',
|
||||
}"
|
||||
>
|
||||
{{ __(title) }}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
:class="icon"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-else-if="typeof icon == 'string'"
|
||||
:class="`lucide-${icon}`"
|
||||
:name="icon"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<component v-else :is="icon" v-bind="$attrs" />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<template #body="{ togglePopover }">
|
||||
<div
|
||||
v-if="reaction"
|
||||
class="px-2 py-1 flex items-center justify-center gap-2 rounded-full bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
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-transparent text-xl"
|
||||
@@ -26,9 +26,12 @@
|
||||
@click.stop="() => (reaction = false)"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="my-3 max-w-max transform bg-surface-base 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 pb-3 overflow-y-auto min-w-40 rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
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">
|
||||
@@ -44,13 +47,13 @@
|
||||
<div class="w-96"></div>
|
||||
<div class="px-3" v-for="(emojis, group) in emojiGroups" :key="group">
|
||||
<div
|
||||
class="sticky top-0 bg-surface-elevation-2 pb-2 pt-3 text-sm text-ink-gray-7"
|
||||
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">
|
||||
<button
|
||||
class="h-8 w-8 rounded-md p-1 text-2xl hover:bg-surface-gray-2 focus:outline-none focus:ring focus:ring-outline-blue-2"
|
||||
class="h-8 w-8 rounded-md p-1 text-2xl hover:bg-surface-gray-2 focus:outline-none focus:ring focus:ring-blue-200"
|
||||
v-for="_emoji in emojis"
|
||||
:key="_emoji.description"
|
||||
@click="() => (emoji = _emoji.emoji) && togglePopover()"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute inset-0 flex cursor-pointer items-center justify-center bg-black/40 text-ink-base opacity-0 outline-none transition focus:outline-none focus-visible:outline-none group-hover:opacity-100"
|
||||
class="absolute inset-0 flex cursor-pointer items-center justify-center bg-black/40 text-white opacity-0 outline-none transition focus:outline-none focus-visible:outline-none group-hover:opacity-100"
|
||||
:class="roundedClass"
|
||||
:aria-label="__('Replace image')"
|
||||
@click.prevent="openFileSelector()"
|
||||
@@ -21,7 +21,7 @@
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute -right-2.5 -top-2.5 flex size-4.5 cursor-pointer items-center justify-center rounded-full bg-surface-base text-ink-gray-4 opacity-0 outline outline-black-overlay-50 duration-300 ease-in-out focus:outline-none focus-visible:outline-none hover:bg-surface-gray-2 group-hover:opacity-100"
|
||||
class="absolute -right-2.5 -top-2.5 flex size-4.5 cursor-pointer items-center justify-center rounded-full bg-surface-white text-ink-gray-4 opacity-0 outline outline-black-overlay-50 duration-300 ease-in-out focus:outline-none focus-visible:outline-none hover:bg-surface-gray-2 group-hover:opacity-100"
|
||||
:aria-label="__('Remove image')"
|
||||
@click.prevent="image = ''"
|
||||
>
|
||||
@@ -45,7 +45,7 @@
|
||||
</FileUploader>
|
||||
|
||||
<div v-if="label || description" class="flex flex-col gap-0.5">
|
||||
<span v-if="label" class="text-p-sm-medium text-ink-gray-8">
|
||||
<span v-if="label" class="text-p-sm font-medium text-ink-gray-8">
|
||||
{{ __(label) }}
|
||||
</span>
|
||||
<span v-if="description" class="text-p-sm text-ink-gray-5">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div v-for="group in groupedRows" :key="group.group">
|
||||
<ListGroupHeader :group="group">
|
||||
<div
|
||||
class="my-2 flex items-center gap-2 text-base-medium text-ink-gray-8 justify-between w-full me-1"
|
||||
class="my-2 flex items-center gap-2 text-base font-medium text-ink-gray-8 justify-between w-full me-1"
|
||||
>
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<component v-if="group.icon" :is="group.icon" />
|
||||
|
||||
@@ -148,6 +148,7 @@ import { useStorage } from "@vueuse/core";
|
||||
import {
|
||||
createResource,
|
||||
Dropdown,
|
||||
FeatherIcon,
|
||||
frappeRequest,
|
||||
ListFooter,
|
||||
ListHeader,
|
||||
@@ -445,9 +446,9 @@ function getGroupedByRows(listRows, groupByField) {
|
||||
group: option || " ",
|
||||
collapsed: false,
|
||||
rows: filteredRows,
|
||||
icon: h("span", {
|
||||
class: ["lucide-folder", "h-4 w-4 flex-shrink-0 text-ink-gray-6"],
|
||||
"aria-hidden": "true",
|
||||
icon: h(FeatherIcon, {
|
||||
name: "folder",
|
||||
class: "h-4 w-4 flex-shrink-0 text-ink-gray-6",
|
||||
}),
|
||||
};
|
||||
groupedRows.push(groupDetail);
|
||||
@@ -816,10 +817,7 @@ function handleScrollPosition() {
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function handleColumnResize({ key, width, save } = {}) {
|
||||
const column = columns.value.find((c) => c.key === key);
|
||||
if (column) column.width = width;
|
||||
if (!save) return;
|
||||
function handleColumnResize() {
|
||||
isViewUpdated.value = true;
|
||||
defaultParams.columns = columns.value;
|
||||
if (!defaultParams.is_default) return;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
:text="avatar.name"
|
||||
>
|
||||
<Avatar
|
||||
class="user-avatar -mr-1.5 ring-2 ring-[var(--surface-base)] transition hover:z-10 hover:scale-110"
|
||||
class="user-avatar -mr-1.5 ring-2 ring-[var(--surface-white)] transition hover:z-10 hover:scale-110"
|
||||
shape="circle"
|
||||
:image="avatar.image"
|
||||
:label="avatar.label"
|
||||
|
||||
@@ -10,12 +10,10 @@
|
||||
/>
|
||||
<div
|
||||
class="flex flex-col h-full gap-1.5"
|
||||
:class="[
|
||||
(isMobileView || !hasDocInfo) && 'flex-1 items-center justify-center',
|
||||
]"
|
||||
:class="[isMobileView && 'flex-1 items-center justify-center']"
|
||||
>
|
||||
<div class="flex gap-2 items-center">
|
||||
<p class="text-ink-gray-8 text-xl-medium">
|
||||
<p class="font-medium text-ink-gray-8 text-xl">
|
||||
{{ avatar.label }}
|
||||
</p>
|
||||
<Tooltip v-if="badge" :text="badge.tooltip ?? ''">
|
||||
@@ -58,7 +56,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useScreenSize } from "@/composables/screen";
|
||||
import { Avatar, Badge, Tooltip, type AvatarProps } from "frappe-ui";
|
||||
import { computed } from "vue";
|
||||
|
||||
interface DocInfoItem {
|
||||
icon?: any;
|
||||
@@ -86,8 +83,4 @@ const props = withDefaults(
|
||||
);
|
||||
|
||||
const { isMobileView } = useScreenSize();
|
||||
|
||||
const hasDocInfo = computed(() => {
|
||||
return props.docInfo?.some((item) => item.value) ?? false;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<header class="border-b px-5 py-2.5">
|
||||
<div class="flex items-center justify-between">
|
||||
<slot name="title">
|
||||
<div v-if="title" class="text-lg-medium text-ink-gray-9">
|
||||
<div v-if="title" class="text-lg font-medium text-ink-gray-9">
|
||||
{{ title }}
|
||||
</div>
|
||||
</slot>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<Tooltip>
|
||||
<template #body>
|
||||
<div
|
||||
class="rounded bg-surface-gray-10 py-1.5 px-2 text-xs text-ink-base shadow-xl"
|
||||
class="rounded bg-surface-gray-7 py-1.5 px-2 text-xs text-ink-white shadow-xl"
|
||||
>
|
||||
<span class="flex items-center gap-1">
|
||||
{{ show ? "Hide Password" : "Show Password" }}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<template #default>
|
||||
<div class="max-h-[575px]" :style="{ height: 'calc(100vh - 8rem)' }">
|
||||
<div class="flex items-center justify-between w-full p-4 pb-2">
|
||||
<div class="text-2xl-semibold">{{ __("Saved Replies") }}</div>
|
||||
<div class="text-2xl font-semibold">{{ __("Saved Replies") }}</div>
|
||||
<Button
|
||||
variant="solid"
|
||||
icon-left="lucide-plus"
|
||||
@@ -50,7 +50,7 @@
|
||||
>
|
||||
<template #suffix>
|
||||
<p
|
||||
class="flex h-5 w-5 items-center justify-center rounded-[5px] bg-surface-base pt-px text-xs-medium text-ink-gray-8 shadow-sm"
|
||||
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"
|
||||
v-if="savedReplyListResource?.data?.length"
|
||||
>
|
||||
{{ savedReplyListResource?.data?.length }}
|
||||
@@ -80,28 +80,22 @@
|
||||
class="flex h-56 cursor-pointer flex-col gap-2 rounded-lg border p-3 hover:bg-surface-gray-2 relative"
|
||||
@click="onTemplateSelect(template)"
|
||||
>
|
||||
<div class="text-base-semibold truncate border-b pb-2">
|
||||
<div class="text-base font-semibold truncate border-b pb-2">
|
||||
{{ template.title }}
|
||||
</div>
|
||||
<Editor
|
||||
<TextEditor
|
||||
v-if="template.message"
|
||||
:model-value="template.message"
|
||||
:content="template.message"
|
||||
:editable="false"
|
||||
:extensions="extensions"
|
||||
>
|
||||
<template #default="{ editor }">
|
||||
<EditorContent
|
||||
:editor="editor"
|
||||
class="flex-1 overflow-hidden pointer-events-none !prose-sm max-w-none !text-sm text-ink-gray-5 focus:outline-none"
|
||||
/>
|
||||
</template>
|
||||
</Editor>
|
||||
editor-class="!prose-sm max-w-none !text-sm text-ink-gray-5 focus:outline-none"
|
||||
class="flex-1 overflow-hidden pointer-events-none"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
selectedTemplate.name === template.name &&
|
||||
selectedTemplate.isLoading
|
||||
"
|
||||
class="flex items-center justify-center absolute top-0 start-0 w-full h-full bg-surface-gray-10/20 rounded-lg"
|
||||
class="flex items-center justify-center absolute top-0 start-0 w-full h-full bg-surface-gray-7/20 rounded-lg"
|
||||
>
|
||||
<LoadingIndicator class="size-4" />
|
||||
</div>
|
||||
@@ -139,9 +133,9 @@ import {
|
||||
Dialog,
|
||||
Dropdown,
|
||||
LoadingIndicator,
|
||||
TextEditor,
|
||||
TextInput,
|
||||
} from "frappe-ui";
|
||||
import { Editor, EditorContent, RichTextKit } from "frappe-ui/editor";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, nextTick, ref, watch } from "vue";
|
||||
import {
|
||||
@@ -160,8 +154,6 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const extensions = [RichTextKit];
|
||||
|
||||
const show = defineModel();
|
||||
const activeFilter = useStorage("saved-replies-filter", "Personal");
|
||||
const { disableGlobalScopeForSavedReplies, teamRestrictionApplied } =
|
||||
@@ -305,7 +297,8 @@ watch(
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
nextTick(() => {
|
||||
searchInput.value?.el?.focus();
|
||||
const inputEl = searchInput.value?.$el?.querySelector("input");
|
||||
inputEl?.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
v-if="option.image"
|
||||
:image="option.image"
|
||||
:label="option.label"
|
||||
class="border-2 border-[var(--surface-base)] flex-shrink-0"
|
||||
class="border-2 border-[var(--surface-white)] flex-shrink-0"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
@@ -50,12 +50,12 @@
|
||||
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="absolute z-50 mt-2 w-64 divide-y divide-outline-elevation-2 rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none start-0 origin-top-left"
|
||||
class="absolute z-50 mt-2 w-64 divide-y divide-outline-gray-modals rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none start-0 origin-top-left"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="py-1.5 px-1.5">
|
||||
<div
|
||||
class="flex h-7 items-center text-sm-medium text-ink-gray-6 justify-between"
|
||||
class="flex h-7 items-center text-sm font-medium text-ink-gray-6 justify-between"
|
||||
>
|
||||
<input
|
||||
ref="inputRef"
|
||||
@@ -121,7 +121,7 @@
|
||||
<div v-for="group in filteredGroups" :key="`group-${group.group}`">
|
||||
<!-- Group Header -->
|
||||
<div
|
||||
class="flex h-7 pt-1.5 sticky top-0 bg-surface-base items-center px-2 text-sm-medium text-ink-gray-6"
|
||||
class="flex h-7 pt-1.5 sticky top-0 bg-surface-white items-center px-2 text-sm font-medium text-ink-gray-6"
|
||||
>
|
||||
{{ group.group }}
|
||||
</div>
|
||||
|
||||
@@ -11,17 +11,19 @@
|
||||
:class="labelClass"
|
||||
@click="collapsible && toggle()"
|
||||
>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="collapsible && collapseIconPosition === 'left'"
|
||||
class="lucide-chevron-right h-4 transition-all duration-300 ease-in-out"
|
||||
name="chevron-right"
|
||||
class="h-4 transition-all duration-300 ease-in-out"
|
||||
:class="{ 'rotate-90': opened }"
|
||||
/>
|
||||
<span>
|
||||
{{ label || "Untitled" }}
|
||||
</span>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="collapsible && collapseIconPosition === 'right'"
|
||||
class="lucide-chevron-right h-4 transition-all duration-300 ease-in-out"
|
||||
name="chevron-right"
|
||||
class="h-4 transition-all duration-300 ease-in-out"
|
||||
:class="{ 'rotate-90': opened }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</template>
|
||||
<template #body="{ togglePopover }">
|
||||
<div
|
||||
class="p-1 text-ink-gray-6 top-1 absolute w-[--reka-popper-anchor-width] bg-surface-base shadow-2xl rounded"
|
||||
class="p-1 text-ink-gray-6 top-1 absolute w-[--reka-popper-anchor-width] bg-surface-white shadow-2xl rounded"
|
||||
:class="bodyClass"
|
||||
>
|
||||
<div class="max-h-52 overflow-y-auto">
|
||||
@@ -34,9 +34,10 @@
|
||||
<div class="w-full truncate">
|
||||
{{ option.label }}
|
||||
</div>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="model == option.value"
|
||||
class="lucide-check size-4 ms-2"
|
||||
name="check"
|
||||
class="size-4 ms-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,7 +55,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, Popover } from "frappe-ui";
|
||||
import { Button, FeatherIcon, Popover } from "frappe-ui";
|
||||
|
||||
const model = defineModel();
|
||||
|
||||
|
||||
@@ -45,12 +45,9 @@
|
||||
class="flex items-center justify-between w-fit p-4"
|
||||
>
|
||||
<template #suffix>
|
||||
<span
|
||||
:class="[
|
||||
'h-4',
|
||||
open ? 'lucide-chevron-up' : 'lucide-chevron-down',
|
||||
]"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
:name="open ? 'chevron-up' : 'chevron-down'"
|
||||
class="h-4"
|
||||
/>
|
||||
</template>
|
||||
</Button>
|
||||
@@ -64,10 +61,10 @@
|
||||
<span class="whitespace-nowrap">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="activeFilter === item.label"
|
||||
class="lucide-check size-4 text-ink-gray-7"
|
||||
aria-hidden="true"
|
||||
name="check"
|
||||
class="size-4 text-ink-gray-7"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
@@ -145,12 +142,12 @@
|
||||
:label="getUserRole(agent.name)"
|
||||
:button="{
|
||||
label: getUserRole(agent.name),
|
||||
iconRight: 'lucide-chevron-down',
|
||||
iconRight: 'chevron-down',
|
||||
iconLeft:
|
||||
getUserRole(agent.name) === 'Agent'
|
||||
? 'lucide-user'
|
||||
? 'user'
|
||||
: getUserRole(agent.name) === 'Manager'
|
||||
? 'lucide-briefcase'
|
||||
? 'briefcase'
|
||||
: null,
|
||||
}"
|
||||
placement="right"
|
||||
@@ -187,7 +184,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { Avatar, Button, call, Dropdown, toast } from "frappe-ui";
|
||||
import { Avatar, Button, call, Dropdown, FeatherIcon, toast } from "frappe-ui";
|
||||
import { h, onUnmounted } from "vue";
|
||||
import LucideCheck from "~icons/lucide/check";
|
||||
import { activeFilter, useAgents } from "./agents";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">{{
|
||||
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||
__("Assignee Rules")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -14,7 +14,7 @@
|
||||
</div>
|
||||
<div class="mt-8 flex items-center justify-between gap-2">
|
||||
<div>
|
||||
<div class="text-base-medium text-ink-gray-8">
|
||||
<div class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Ticket Routing") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-6 mt-1">
|
||||
@@ -27,7 +27,7 @@
|
||||
<Popover placement="bottom-end">
|
||||
<template #target="{ togglePopover }">
|
||||
<div
|
||||
class="flex items-center justify-between text-base rounded h-7 py-1.5 ps-2 pe-2 border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-elevation-2 hover:bg-surface-gray-3 focus:bg-surface-base 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 w-full dark:[color-scheme:dark] select-none min-w-44"
|
||||
class="flex items-center justify-between text-base rounded h-7 py-1.5 ps-2 pe-2 border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 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 w-full dark:[color-scheme:dark] select-none min-w-44"
|
||||
@click="togglePopover()"
|
||||
>
|
||||
<div>
|
||||
@@ -37,12 +37,12 @@
|
||||
)?.label
|
||||
}}
|
||||
</div>
|
||||
<span class="lucide-chevron-down size-4" />
|
||||
<FeatherIcon name="chevron-down" class="size-4" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ togglePopover }">
|
||||
<div
|
||||
class="p-1 text-ink-gray-7 mt-1 bg-surface-base shadow-xl rounded w-[--reka-popper-anchor-width]"
|
||||
class="p-1 text-ink-gray-7 mt-1 bg-surface-white shadow-xl rounded w-[--reka-popper-anchor-width]"
|
||||
>
|
||||
<div
|
||||
v-for="option in ticketRoutingOptions"
|
||||
@@ -58,9 +58,10 @@
|
||||
<span>
|
||||
{{ option.label }}
|
||||
</span>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="assignmentRuleData.rule == option.value"
|
||||
class="lucide-check size-4"
|
||||
name="check"
|
||||
class="size-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,7 +71,7 @@
|
||||
</div>
|
||||
<div class="mt-7 flex items-center justify-between gap-2">
|
||||
<div>
|
||||
<div class="text-base-medium text-ink-gray-8">
|
||||
<div class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Assignees") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-6 mt-1">
|
||||
@@ -96,7 +97,7 @@
|
||||
:placement="'top'"
|
||||
>
|
||||
<div
|
||||
class="text-xs rounded-full select-none bg-surface-blue-3 text-ink-base p-0.5 px-2"
|
||||
class="text-xs rounded-full select-none bg-surface-blue-3 text-ink-white p-0.5 px-2"
|
||||
>
|
||||
{{ __("Last") }}
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</template>
|
||||
<template #body="{ togglePopover }">
|
||||
<div
|
||||
class="mt-1 rounded-lg bg-surface-base py-1 text-base shadow-2xl w-60"
|
||||
class="mt-1 rounded-lg bg-surface-white py-1 text-base shadow-2xl w-60"
|
||||
>
|
||||
<div class="relative px-1.5 pt-0.5">
|
||||
<ComboboxInput
|
||||
@@ -32,7 +32,7 @@
|
||||
class="absolute end-1.5 inline-flex h-7 w-7 items-center justify-center"
|
||||
@click="query = ''"
|
||||
>
|
||||
<span class="lucide-x w-4" />
|
||||
<FeatherIcon name="x" class="w-4" />
|
||||
</button>
|
||||
</div>
|
||||
<ComboboxOptions class="my-2 max-h-64 overflow-y-auto px-1.5" static>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div
|
||||
class="grid grid-cols-12 items-center gap-4 cursor-pointer hover:bg-surface-sidebar rounded"
|
||||
class="grid grid-cols-12 items-center gap-4 cursor-pointer hover:bg-surface-menu-bar rounded"
|
||||
>
|
||||
<div
|
||||
@click="assignmentRulesActiveScreen = { screen: 'view', data: data }"
|
||||
class="w-full ps-2 col-span-7 h-14 flex flex-col justify-center"
|
||||
>
|
||||
<div class="text-base-medium text-ink-gray-7">{{ data.name }}</div>
|
||||
<div class="text-base text-ink-gray-7 font-medium">{{ data.name }}</div>
|
||||
<div
|
||||
v-if="data.description && data.description.length > 0"
|
||||
class="text-sm w-full text-ink-gray-5 mt-1 truncate"
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<Popover>
|
||||
<template #target="{ togglePopover }">
|
||||
<div
|
||||
class="flex items-center justify-between text-base rounded h-7 py-1.5 ps-2 pe-2 border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-elevation-2 hover:bg-surface-gray-3 focus:bg-surface-base 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 w-full dark:[color-scheme:dark] cursor-default"
|
||||
class="flex items-center justify-between text-base rounded h-7 py-1.5 ps-2 pe-2 border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 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 w-full dark:[color-scheme:dark] cursor-default"
|
||||
@click="togglePopover()"
|
||||
>
|
||||
<div>
|
||||
@@ -70,26 +70,27 @@
|
||||
)?.label
|
||||
}}
|
||||
</div>
|
||||
<span class="lucide-chevron-down size-4" />
|
||||
<FeatherIcon name="chevron-down" class="size-4" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ togglePopover }">
|
||||
<div
|
||||
class="p-1 text-ink-gray-6 top-1 absolute bg-surface-base shadow-2xl rounded w-[--reka-popper-anchor-width]"
|
||||
class="p-1 text-ink-gray-6 top-1 absolute bg-surface-white shadow-2xl rounded w-[--reka-popper-anchor-width]"
|
||||
>
|
||||
<div
|
||||
v-for="option in priorityOptions"
|
||||
:key="option.value"
|
||||
class="p-2 cursor-pointer hover:bg-surface-sidebar text-base flex items-center justify-between rounded"
|
||||
class="p-2 cursor-pointer hover:bg-surface-menu-bar text-base flex items-center justify-between rounded"
|
||||
@click="
|
||||
assignmentRuleData.priority = option.value;
|
||||
togglePopover();
|
||||
"
|
||||
>
|
||||
{{ option.label }}
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="assignmentRuleData.priority == option.value"
|
||||
class="lucide-check size-4"
|
||||
name="check"
|
||||
class="size-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -117,7 +118,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">{{
|
||||
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||
__("Assignment Condition")
|
||||
}}</span>
|
||||
<div class="flex items-center justify-between gap-6">
|
||||
@@ -143,12 +144,12 @@
|
||||
class="text-sm text-ink-gray-6 flex gap-1 cursor-default text-nowrap flex items-center"
|
||||
>
|
||||
<span>{{ __("Old Condition") }}</span>
|
||||
<span class="lucide-info size-4" />
|
||||
<FeatherIcon name="info" class="size-4" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body-main>
|
||||
<div
|
||||
class="text-sm text-ink-gray-6 p-2 bg-surface-base rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||
class="text-sm text-ink-gray-6 p-2 bg-surface-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||
>
|
||||
<code>{{ assignmentRuleData.assignCondition }}</code>
|
||||
</div>
|
||||
@@ -194,7 +195,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">{{
|
||||
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||
__("Unassignment Condition")
|
||||
}}</span>
|
||||
<div class="flex items-center justify-between gap-6">
|
||||
@@ -224,12 +225,12 @@
|
||||
class="text-sm text-ink-gray-6 flex gap-1 cursor-default text-nowrap flex items-center"
|
||||
>
|
||||
<span> {{ __("Old Condition") }} </span>
|
||||
<span class="lucide-info size-4" />
|
||||
<FeatherIcon name="info" class="size-4" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body-main>
|
||||
<div
|
||||
class="text-sm text-ink-gray-6 p-2 bg-surface-base rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||
class="text-sm text-ink-gray-6 p-2 bg-surface-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||
>
|
||||
<code>{{ assignmentRuleData.unassignCondition }}</code>
|
||||
</div>
|
||||
@@ -271,7 +272,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">{{
|
||||
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||
__("Assignment Schedule")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<SettingsLayoutBase>
|
||||
<template #title>
|
||||
<h1 class="text-md-semibold text-ink-gray-8">
|
||||
<h1 class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Assignment Rules") }}
|
||||
</h1>
|
||||
</template>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<Settings class="size-6 text-ink-gray-6 rotate-90" />
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<div class="text-base-medium text-ink-gray-6">
|
||||
<div class="text-base font-medium text-ink-gray-6">
|
||||
{{ __("No assignment rule found") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-5 max-w-60 text-center">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
validateAssignmentRule(props.name);
|
||||
"
|
||||
>
|
||||
<span class="lucide-plus h-4" />
|
||||
<FeatherIcon name="plus" class="h-4" />
|
||||
{{ __("Add a condition") }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-2">
|
||||
@@ -41,7 +41,7 @@
|
||||
import CFConditions from "@/components/conditions-filter/CFConditions.vue";
|
||||
import { validateConditions } from "@/utils";
|
||||
import { watchDebounced } from "@vueuse/core";
|
||||
import { Button, Dropdown, ErrorMessage } from "frappe-ui";
|
||||
import { Button, Dropdown, ErrorMessage, FeatherIcon } from "frappe-ui";
|
||||
import { validateAssignmentRule } from "@/stores/assignmentRules";
|
||||
import { __ } from "@/translation";
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex justify-between items-center border-outline-elevation-2 p-2 cursor-pointer hover:bg-surface-sidebar rounded h-14"
|
||||
class="flex justify-between items-center border-outline-gray-modals p-2 cursor-pointer hover:bg-surface-menu-bar rounded h-14"
|
||||
>
|
||||
<!-- avatar and name -->
|
||||
<div class="flex justify-between items-center gap-2">
|
||||
<EmailProviderIcon :logo="emailIcon[emailAccount.service]" />
|
||||
<div class="rtl:text-right">
|
||||
<p class="text-p-base-medium text-ink-gray-7">
|
||||
<p class="text-p-base text-ink-gray-7 font-medium">
|
||||
{{ __(emailAccount.email_account_name) }}
|
||||
</p>
|
||||
<div class="text-p-sm w-full text-ink-gray-5 mt-1">
|
||||
|
||||
@@ -35,12 +35,7 @@
|
||||
:emailAccount="emailAccount"
|
||||
@click="emit('update:step', 'email-edit', emailAccount)"
|
||||
/>
|
||||
<hr
|
||||
class="mx-2"
|
||||
v-if="
|
||||
emailAccount !== emailAccounts.data[emailAccounts.data.length - 1]
|
||||
"
|
||||
/>
|
||||
<hr class="mx-2" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- fallback if no email accounts -->
|
||||
@@ -56,11 +51,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
|
||||
import { EmailAccount } from "@/types";
|
||||
import { createListResource } from "frappe-ui";
|
||||
import { EmailIcon } from "../icons";
|
||||
import EmailAccountCard from "./EmailAccountCard.vue";
|
||||
import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
|
||||
import { EmailIcon } from "../icons";
|
||||
|
||||
const emit = defineEmits(["update:step"]);
|
||||
|
||||
|
||||
@@ -27,17 +27,17 @@
|
||||
<!-- email service provider info -->
|
||||
<div>
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-elevation-2"
|
||||
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-gray-modals"
|
||||
>
|
||||
<CircleAlert
|
||||
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-5"
|
||||
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-2"
|
||||
/>
|
||||
<div class="text-wrap text-xs text-ink-gray-7">
|
||||
{{ selectedService.info }}
|
||||
<a
|
||||
:href="selectedService.link"
|
||||
target="_blank"
|
||||
class="text-ink-blue-5 underline"
|
||||
class="text-ink-blue-2 underline"
|
||||
>here</a
|
||||
>.
|
||||
</div>
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-elevation-2"
|
||||
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-gray-modals"
|
||||
>
|
||||
<CircleAlert
|
||||
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-5"
|
||||
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-2"
|
||||
/>
|
||||
<div class="text-wrap text-xs text-ink-gray-7 flex flex-col gap-1">
|
||||
<span>
|
||||
@@ -24,7 +24,7 @@
|
||||
<a
|
||||
:href="info.link"
|
||||
target="_blank"
|
||||
class="text-ink-blue-5 underline"
|
||||
class="text-ink-blue-2 underline"
|
||||
>here</a
|
||||
>.
|
||||
</span>
|
||||
@@ -32,7 +32,7 @@
|
||||
<a
|
||||
:href="deskEditUrl"
|
||||
target="_blank"
|
||||
class="text-ink-blue-5 underline"
|
||||
class="text-ink-blue-2 underline"
|
||||
>
|
||||
{{ __("Open in Desk") }}
|
||||
</a>
|
||||
@@ -342,7 +342,7 @@ async function updateAccount() {
|
||||
if (!nameChanged && !otherFieldsChanged) {
|
||||
toast.create({
|
||||
message: __("No changes made"),
|
||||
icon: h(CircleAlert, { class: "text-ink-blue-5" }),
|
||||
icon: h(CircleAlert, { class: "text-ink-blue-2" }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -405,7 +405,7 @@ function pullEmails() {
|
||||
|
||||
toast.create({
|
||||
message: __("Pulling emails, this may take a few minutes."),
|
||||
icon: h(CircleAlert, { class: "text-ink-blue-5" }),
|
||||
icon: h(CircleAlert, { class: "text-ink-blue-2" }),
|
||||
});
|
||||
|
||||
call("frappe.email.doctype.email_account.email_account.pull_emails", {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<span class="sr-only">{{ __("back to email event list") }}</span>
|
||||
<LucideChevronLeft class="w-4.5 h-4.5 rtl:rotate-180" />
|
||||
</button>
|
||||
<h1 class="text-xl-semibold">
|
||||
<h1 class="font-semibold text-xl">
|
||||
{{ props.title }}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<li class="flex items-center justify-between p-3 rounded relative">
|
||||
<div class="flex flex-col gap-1">
|
||||
<h2
|
||||
class="text-base-medium text-ink-gray-7 relative z-10 pointer-events-none"
|
||||
class="text-base font-medium text-ink-gray-7 relative z-10 pointer-events-none"
|
||||
>
|
||||
{{ __(notification.label) }}
|
||||
</h2>
|
||||
@@ -26,12 +26,12 @@
|
||||
{{ __(notification.description) }}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
class="lucide-chevron-right text-ink-gray-7 size-4 relative z-10 pointer-events-none rtl:rotate-180"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
name="chevron-right"
|
||||
class="text-ink-gray-7 size-4 relative z-10 pointer-events-none rtl:rotate-180"
|
||||
/>
|
||||
<div
|
||||
class="w-full h-full absolute top-0 start-0 hover:bg-surface-sidebar rounded-[inherit]"
|
||||
class="w-full h-full absolute top-0 start-0 hover:bg-surface-menu-bar rounded-[inherit]"
|
||||
@click="
|
||||
() => {
|
||||
props.onSelect(notification);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex size-8 cursor-pointer items-center justify-center rounded-full bg-surface-gray-2 hover:bg-surface-gray-3"
|
||||
:class="{ 'ring-2 ring-outline-blue-4': selected }"
|
||||
:class="{ 'ring-2 ring-outline-blue-3': selected }"
|
||||
>
|
||||
<img v-if="logoValue" :src="logoValue" class="size-4.5" />
|
||||
<LucideMail v-else class="size-4.5 text-ink-gray-7" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="flex-1 flex flex-col gap-1.5">
|
||||
<span class="block text-xs text-ink-gray-5"
|
||||
>{{ __("Parent field") }}
|
||||
<span class="text-ink-red-6 select-none">*</span>
|
||||
<span class="text-ink-red-3 select-none">*</span>
|
||||
</span>
|
||||
<Combobox
|
||||
v-model="state.selectedParentField"
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="flex-1 flex flex-col gap-1.5">
|
||||
<span class="block text-xs text-ink-gray-5"
|
||||
>{{ __("Child field") }}
|
||||
<span class="text-ink-red-6 select-none">*</span>
|
||||
<span class="text-ink-red-3 select-none">*</span>
|
||||
</span>
|
||||
<Combobox
|
||||
v-model="state.selectedChildField"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<SettingsLayoutBase>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1 class="text-md-semibold text-ink-gray-8">
|
||||
<h1 class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Field Dependencies") }}
|
||||
</h1>
|
||||
</div>
|
||||
@@ -75,7 +75,7 @@
|
||||
:key="row.name"
|
||||
>
|
||||
<div
|
||||
class="grid grid-cols-11 items-center gap-4 cursor-pointer hover:bg-surface-sidebar rounded h-12.5"
|
||||
class="grid grid-cols-11 items-center gap-4 cursor-pointer hover:bg-surface-menu-bar rounded h-12.5"
|
||||
>
|
||||
<div
|
||||
@click.stop="$emit('update:step', 'fd', row.name)"
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
:model-value="toggleAllChildValues"
|
||||
class="me-2"
|
||||
/>
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base text-ink-gray-8 font-medium">
|
||||
{{ toggleCheckboxLabel }}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<SettingsLayoutBase :description="__('Manage general settings of your app.')">
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1 class="text-md-semibold text-ink-gray-8">
|
||||
<h1 class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("General") }}
|
||||
</h1>
|
||||
<UnsavedBadge :show="isDirty" />
|
||||
@@ -38,12 +38,12 @@
|
||||
<WorkflowKnowledgebaseSettings />
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="text-base-semibold text-ink-gray-9">
|
||||
<div class="text-base font-semibold text-ink-gray-9">
|
||||
{{ __("User Signup") }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-6">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Disable signup")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div v-if="settingsData">
|
||||
<div class="text-base-semibold text-ink-gray-9">
|
||||
<div class="text-base font-semibold text-ink-gray-9">
|
||||
{{ __("Branding") }}
|
||||
</div>
|
||||
<FormControl
|
||||
|
||||
@@ -12,14 +12,10 @@
|
||||
:image="props.image"
|
||||
:label="props.title"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="lucide-image size-6 text-ink-gray-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<FeatherIcon v-else name="image" class="size-6 text-ink-gray-4" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1 max-w-sm items-start">
|
||||
<span class="text-base-medium text-ink-gray-8">{{ title }}</span>
|
||||
<span class="text-base font-medium text-ink-gray-8">{{ title }}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{ description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,7 +57,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Avatar, Button, FileUploader } from "frappe-ui";
|
||||
import { Avatar, Button, FeatherIcon, FileUploader } from "frappe-ui";
|
||||
import ImageUpIcon from "~icons/lucide/image-up";
|
||||
|
||||
const emit = defineEmits(["onUpload", "onRemove"]);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="text-base-semibold text-ink-gray-9">
|
||||
<div class="text-base font-semibold text-ink-gray-9">
|
||||
{{ __("Ticket Settings") }}
|
||||
</div>
|
||||
<div class="mt-6 flex flex-col gap-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Make feedback mandatory")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Enable comment reactions")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -31,7 +31,7 @@
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Restrict tickets by team")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -63,7 +63,7 @@
|
||||
v-if="settingsData.restrictTicketsByAgentGroup"
|
||||
>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Disable global saved replies")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -76,7 +76,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Auto update status")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -105,7 +105,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Allow anyone to create tickets")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6"
|
||||
@@ -125,7 +125,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Default ticket type")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -141,7 +141,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Automatically close stale tickets")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -195,7 +195,7 @@
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Outside working hours notice")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="text-base-semibold text-ink-gray-9">
|
||||
<div class="text-base font-semibold text-ink-gray-9">
|
||||
{{ __("Workflow & Knowledge Base Settings") }}
|
||||
</div>
|
||||
<div class="mt-6 flex flex-col gap-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Prefer knowledge base")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">{{
|
||||
<span class="text-base font-medium text-ink-gray-8">{{
|
||||
__("Skip email workflow")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="w-max mx-auto">
|
||||
<div class="text-base-medium mb-2 text-ink-gray-8 ms-2.5">
|
||||
<div class="text-base font-medium mb-2 text-ink-gray-8 ms-2.5">
|
||||
{{ formattedMonth }}
|
||||
</div>
|
||||
<div class="rounded-md text-sm">
|
||||
@@ -18,7 +18,7 @@
|
||||
<Popover v-if="isHoliday(date)">
|
||||
<template #target="{ open, close }">
|
||||
<div
|
||||
class="flex size-7 cursor-pointer text-ink-orange-6 bg-surface-yellow-2 items-center justify-center rounded hover:bg-surface-yellow-2 select-none m-[1px]"
|
||||
class="flex size-7 cursor-pointer text-orange-700 bg-yellow-100 items-center justify-center rounded hover:bg-yellow-100 select-none m-[1px]"
|
||||
:class="{
|
||||
'!text-ink-gray-4 !bg-surface-gray-2': isWeekOff(date),
|
||||
}"
|
||||
@@ -46,10 +46,10 @@
|
||||
"
|
||||
>
|
||||
<div class="w-[5%]">
|
||||
<div class="size-3.5 bg-surface-orange-5 rounded-sm mt-1" />
|
||||
<div class="size-3.5 bg-orange-500 rounded-sm mt-1" />
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-sm-semibold">
|
||||
<div class="text-sm font-semibold">
|
||||
{{ getHolidayDescription(date) }}
|
||||
</div>
|
||||
<div class="text-xs mt-1">
|
||||
@@ -137,7 +137,7 @@
|
||||
'text-ink-gray-3':
|
||||
// @ts-ignore
|
||||
date.getMonth() !== currentMonth - 1 || !isDateInRange(date),
|
||||
'bg-surface-gray-10 text-ink-base hover:!bg-surface-gray-10/80 hover:text-ink-base':
|
||||
'bg-surface-gray-7 text-ink-white hover:!bg-surface-gray-7/80 hover:text-ink-white':
|
||||
getFormattedDate(date) === dateValue && isDateInRange(date),
|
||||
'opacity-50 cursor-not-allowed': !isDateInRange(date),
|
||||
}"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<Briefcase class="size-6 text-ink-gray-6" />
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<div class="text-base-medium text-ink-gray-6">
|
||||
<div class="text-base font-medium text-ink-gray-6">
|
||||
{{ __("No Holiday list found") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-5 max-w-60 text-center">
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center cursor-pointer hover:bg-surface-sidebar rounded"
|
||||
class="flex items-center cursor-pointer hover:bg-surface-menu-bar rounded"
|
||||
>
|
||||
<div
|
||||
class="w-full ps-2 flex flex-col justify-center h-14"
|
||||
@click="holidayListActiveScreen = { screen: 'view', data: data }"
|
||||
>
|
||||
<div class="text-base-medium text-ink-gray-7">{{ data.name }}</div>
|
||||
<div class="text-base text-ink-gray-7 font-medium">{{ data.name }}</div>
|
||||
<div
|
||||
v-if="data.description && data.description.length > 0"
|
||||
class="text-sm text-ink-gray-5 mt-1 truncate"
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">{{
|
||||
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||
__("Valid From")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -110,7 +110,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="text-md-semibold text-ink-gray-8">
|
||||
<div class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Recurring Holidays") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-6">
|
||||
@@ -128,7 +128,7 @@
|
||||
<div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex justify-between flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">
|
||||
<span class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Holidays") }}
|
||||
</span>
|
||||
<div class="text-p-sm text-ink-gray-6">
|
||||
@@ -167,7 +167,7 @@
|
||||
<!-- Indicators -->
|
||||
<div class="flex gap-4" v-if="holidayListView === 'calendar'">
|
||||
<div class="gap-1 flex items-center">
|
||||
<span class="bg-surface-yellow-2 size-4 rounded-sm" />
|
||||
<span class="bg-yellow-100 size-4 rounded-sm" />
|
||||
<span class="text-sm text-ink-gray-6">{{
|
||||
__("Holidays")
|
||||
}}</span>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Popover v-if="startYear !== endYear">
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="flex items-center gap-2 text-xl-semibold cursor-pointer select-none"
|
||||
class="flex items-center gap-2 font-semibold text-xl cursor-pointer select-none"
|
||||
variant="ghost"
|
||||
@click="togglePopover"
|
||||
:label="currentYear + ''"
|
||||
@@ -23,10 +23,10 @@
|
||||
@click="onYearChange(togglePopover, year)"
|
||||
>
|
||||
{{ year }}
|
||||
<span
|
||||
class="lucide-check size-4"
|
||||
<FeatherIcon
|
||||
name="check"
|
||||
class="size-4"
|
||||
v-if="year === currentYear"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,7 +35,7 @@
|
||||
</Popover>
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center gap-2 px-2 text-xl-semibold select-none"
|
||||
class="flex items-center gap-2 px-2 font-semibold text-xl select-none"
|
||||
>
|
||||
{{ startYear }}
|
||||
</div>
|
||||
@@ -85,7 +85,7 @@
|
||||
:class="[
|
||||
'size-1.5 rounded-full cursor-pointer',
|
||||
{
|
||||
'bg-surface-gray-10': visibleMonths === 'first-half',
|
||||
'bg-surface-gray-7': visibleMonths === 'first-half',
|
||||
'bg-surface-gray-4': visibleMonths === 'second-half',
|
||||
},
|
||||
]"
|
||||
@@ -95,7 +95,7 @@
|
||||
:class="[
|
||||
'size-1.5 rounded-full cursor-pointer',
|
||||
{
|
||||
'bg-surface-gray-10': visibleMonths === 'second-half',
|
||||
'bg-surface-gray-7': visibleMonths === 'second-half',
|
||||
'bg-surface-gray-4': visibleMonths === 'first-half',
|
||||
},
|
||||
]"
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
:type="'text'"
|
||||
placeholder="Description"
|
||||
v-model="holiday[column.key]"
|
||||
class="!bg-surface-base w-full text-base px-0 focus:!ring-0 border-none hover:bg-surface-base outline-none no-underline focus:!outline-none"
|
||||
class="!bg-surface-white w-full text-base px-0 focus:!ring-0 border-none hover:bg-surface-white outline-none no-underline focus:!outline-none"
|
||||
/>
|
||||
<div v-else>
|
||||
{{ dayjs(holiday[column.key]).format("DD MMM YYYY") }}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="space-y-1.5">
|
||||
<label class="block text-xs text-ink-gray-5">
|
||||
{{ __("Role") }}
|
||||
<span class="text-ink-red-6 select-none" aria-hidden="true">*</span>
|
||||
<span class="text-ink-red-3 select-none" aria-hidden="true">*</span>
|
||||
</label>
|
||||
<Select :options="roleOptions" v-model="role" required class="w-full">
|
||||
<template #suffix>
|
||||
@@ -38,7 +38,7 @@
|
||||
>
|
||||
</form>
|
||||
<template v-if="pendingInvitesResource.data?.length">
|
||||
<h2 class="mt-8 text-base-semibold">
|
||||
<h2 class="mt-8 text-base font-semibold">
|
||||
{{ __("Pending Invites") }}
|
||||
</h2>
|
||||
<ul class="flex flex-col gap-[0.375rem] mt-3">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<SettingsLayoutBase :description="__('Manage your personal preferences.')">
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1 class="text-md-semibold text-ink-gray-8">
|
||||
<h1 class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Preferences") }}
|
||||
</h1>
|
||||
<UnsavedBadge :show="isDirty" />
|
||||
@@ -22,7 +22,7 @@
|
||||
<template #content>
|
||||
<div class="flex flex-col">
|
||||
<div>
|
||||
<div class="text-base-semibold text-ink-gray-9">
|
||||
<div class="text-base font-semibold text-ink-gray-9">
|
||||
{{ __("Appearance") }}
|
||||
</div>
|
||||
<ThemeSwitcher
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="text-base-semibold text-ink-gray-9">
|
||||
<div class="text-base font-semibold text-ink-gray-9">
|
||||
{{ __("Language & Time") }}
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Language") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -12,22 +12,24 @@
|
||||
<Link
|
||||
:model-value="user.doc?.language"
|
||||
doctype="Language"
|
||||
:placeholder="__('Select Language')"
|
||||
class="w-40"
|
||||
@update:model-value="updateLanguage"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Timezone") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
{{ __("Change timezone of the application.") }}
|
||||
</span>
|
||||
</div>
|
||||
<TimezoneControl
|
||||
<Autocomplete
|
||||
:model-value="user.doc?.time_zone"
|
||||
:options="timezoneOptions"
|
||||
:placeholder="__('Select Timezone')"
|
||||
size="sm"
|
||||
class="w-40"
|
||||
@update:model-value="updateTimezone"
|
||||
/>
|
||||
@@ -36,9 +38,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TimezoneControl from "@/components/TimezoneControl.vue";
|
||||
import { ref } from "vue";
|
||||
import { createResource } from "frappe-ui";
|
||||
import { __ } from "@/translation";
|
||||
import { Link } from "frappe-ui/frappe";
|
||||
|
||||
const props = defineProps<{ user: any }>();
|
||||
|
||||
@@ -47,8 +49,20 @@ function updateLanguage(value: string | null) {
|
||||
props.user.doc.language = value || props.user.originalDoc?.language;
|
||||
}
|
||||
|
||||
function updateTimezone(value: string | null) {
|
||||
function updateTimezone(value: { label: string; value: string } | null) {
|
||||
if (!props.user.doc) return;
|
||||
props.user.doc.time_zone = value || props.user.originalDoc?.time_zone;
|
||||
props.user.doc.time_zone = value?.value || props.user.originalDoc?.time_zone;
|
||||
}
|
||||
|
||||
const timezoneOptions = ref<{ label: string; value: string }[]>([]);
|
||||
createResource({
|
||||
url: "frappe.core.doctype.user.user.get_timezones",
|
||||
auto: true,
|
||||
onSuccess(data: { timezones: string[] }) {
|
||||
timezoneOptions.value = data.timezones.map((tz) => ({
|
||||
label: tz,
|
||||
value: tz,
|
||||
}));
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -18,113 +18,64 @@
|
||||
<div
|
||||
v-for="option in themeOptions"
|
||||
:key="option.value"
|
||||
class="flex-1 max-w-[227px] min-h-[42px] overflow-hidden rounded-lg border cursor-pointer outline-none transition-colors focus-visible:ring-2 focus-visible:ring-outline-gray-3"
|
||||
class="flex-1 rounded-lg border cursor-pointer min-h-[42px]"
|
||||
:class="
|
||||
theme === option.value
|
||||
? 'border-outline-gray-5'
|
||||
: 'border-outline-gray-modals'
|
||||
"
|
||||
role="radio"
|
||||
:aria-checked="theme === option.value"
|
||||
tabindex="0"
|
||||
@click="theme = option.value"
|
||||
@keydown.enter.prevent="theme = option.value"
|
||||
@keydown.space.prevent="theme = option.value"
|
||||
>
|
||||
<!-- Theme-independent window mock: each frame is a fixed-tone SVG so
|
||||
the Light/Dark/System previews always render the same regardless
|
||||
of the app's active theme. -->
|
||||
<div :class="{ flex: option.frames.length > 1 }">
|
||||
<div :class="{ flex: option.panes.length > 1 }">
|
||||
<div
|
||||
v-for="(frame, index) in option.frames"
|
||||
v-for="(pane, index) in option.panes"
|
||||
:key="index"
|
||||
:class="frame.frameClass"
|
||||
:class="pane.containerClass"
|
||||
>
|
||||
<div
|
||||
class="relative"
|
||||
:class="frame.fill ? 'w-full' : 'w-fit shrink-0'"
|
||||
>
|
||||
<svg
|
||||
width="207"
|
||||
height="68"
|
||||
viewBox="0 0 207 68"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="block h-auto rounded-tl-sm"
|
||||
:class="{ 'w-full': frame.fill }"
|
||||
>
|
||||
<path
|
||||
d="M0 4C0 1.79086 1.79086 0 4 0H207V83H0V4Z"
|
||||
:fill="tones[frame.tone].background"
|
||||
/>
|
||||
<template v-if="option.bars">
|
||||
<rect
|
||||
x="104"
|
||||
y="28"
|
||||
width="103"
|
||||
height="6"
|
||||
:fill="tones[frame.tone].bar"
|
||||
/>
|
||||
<rect
|
||||
x="104"
|
||||
y="39"
|
||||
width="103"
|
||||
height="6"
|
||||
:fill="tones[frame.tone].bar"
|
||||
/>
|
||||
<rect
|
||||
x="104"
|
||||
y="50"
|
||||
width="103"
|
||||
height="6"
|
||||
:fill="tones[frame.tone].bar"
|
||||
/>
|
||||
</template>
|
||||
<rect
|
||||
x="4"
|
||||
y="3"
|
||||
width="6"
|
||||
height="6"
|
||||
rx="3"
|
||||
fill="#FF5F57"
|
||||
/>
|
||||
<rect
|
||||
x="13"
|
||||
y="3"
|
||||
width="6"
|
||||
height="6"
|
||||
rx="3"
|
||||
fill="#FEBC2D"
|
||||
/>
|
||||
<rect
|
||||
x="22"
|
||||
y="3"
|
||||
width="6"
|
||||
height="6"
|
||||
rx="3"
|
||||
fill="#28C840"
|
||||
/>
|
||||
<line
|
||||
y1="13.5"
|
||||
x2="207"
|
||||
y2="13.5"
|
||||
:stroke="tones[frame.tone].line"
|
||||
/>
|
||||
</svg>
|
||||
<div :class="pane.screenClass">
|
||||
<div
|
||||
class="absolute flex items-center gap-1 text-xs text-ink-gray-5 font-semibold top-[24px] left-[10px]"
|
||||
class="flex gap-[3px] py-[3px] px-1 border-b"
|
||||
:class="
|
||||
pane.tone === 'light'
|
||||
? 'border-gray-100'
|
||||
: 'border-gray-800'
|
||||
"
|
||||
>
|
||||
<img
|
||||
v-if="logoIsImage"
|
||||
:src="logo as string"
|
||||
class="size-5 object-cover"
|
||||
/>
|
||||
<component
|
||||
:is="logo"
|
||||
v-else-if="logo"
|
||||
class="size-5 shrink-0 rounded-[5px]"
|
||||
/>
|
||||
<div v-if="name">{{ __(name) }}</div>
|
||||
<div class="size-1.5 bg-[#FF5F57] rounded-full" />
|
||||
<div class="size-1.5 bg-[#FEBC2D] rounded-full" />
|
||||
<div class="size-1.5 bg-[#28C840] rounded-full" />
|
||||
</div>
|
||||
<div
|
||||
class="flex items-start justify-between gap-2 p-2.5 pr-0 pb-1 min-h-[41px]"
|
||||
>
|
||||
<div
|
||||
class="flex items-center flex-1 gap-1 text-xs text-ink-gray-5 font-semibold"
|
||||
>
|
||||
<img
|
||||
v-if="logoIsImage"
|
||||
:src="logo as string"
|
||||
class="size-5 object-cover"
|
||||
/>
|
||||
<component
|
||||
:is="logo"
|
||||
v-else-if="logo"
|
||||
class="size-5 shrink-0 rounded"
|
||||
/>
|
||||
<div>{{ __(name) }}</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="option.bars"
|
||||
class="flex flex-col flex-1 gap-[5px]"
|
||||
>
|
||||
<div
|
||||
v-for="bar in 3"
|
||||
:key="bar"
|
||||
class="w-full h-1.5"
|
||||
:class="
|
||||
pane.tone === 'light' ? 'bg-gray-100' : 'bg-gray-800'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,13 +105,10 @@ import { __ } from "@/translation";
|
||||
import { useTheme, type Theme } from "frappe-ui";
|
||||
import { computed, type Component } from "vue";
|
||||
|
||||
type Tone = "light" | "dark";
|
||||
|
||||
type Frame = {
|
||||
tone: Tone;
|
||||
/** Stretch to fill the frame (light/dark) or natural width (system). */
|
||||
fill: boolean;
|
||||
frameClass: string;
|
||||
type Pane = {
|
||||
tone: "light" | "dark";
|
||||
containerClass: string;
|
||||
screenClass: string;
|
||||
};
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -179,27 +127,21 @@ const emit = defineEmits<{ "update:modelValue": [theme: Theme] }>();
|
||||
|
||||
const { currentTheme, setTheme } = useTheme();
|
||||
|
||||
// Fixed window colors per tone — independent of the active app theme.
|
||||
const tones: Record<Tone, { background: string; bar: string; line: string }> = {
|
||||
light: { background: "#FFFFFF", bar: "#F3F3F3", line: "#EDEDED" },
|
||||
dark: { background: "#171717", bar: "#383838", line: "#383838" },
|
||||
};
|
||||
|
||||
const themeOptions: {
|
||||
value: Theme;
|
||||
label: string;
|
||||
panes: Pane[];
|
||||
bars: boolean;
|
||||
frames: Frame[];
|
||||
}[] = [
|
||||
{
|
||||
value: "light",
|
||||
label: "Light",
|
||||
bars: true,
|
||||
frames: [
|
||||
panes: [
|
||||
{
|
||||
tone: "light",
|
||||
fill: true,
|
||||
frameClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
|
||||
containerClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
|
||||
screenClass: "bg-white rounded-tl-sm",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -207,11 +149,11 @@ const themeOptions: {
|
||||
value: "dark",
|
||||
label: "Dark",
|
||||
bars: true,
|
||||
frames: [
|
||||
panes: [
|
||||
{
|
||||
tone: "dark",
|
||||
fill: true,
|
||||
frameClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
|
||||
containerClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
|
||||
screenClass: "bg-gray-900 rounded-tl-sm",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -219,18 +161,18 @@ const themeOptions: {
|
||||
value: "system",
|
||||
label: "System",
|
||||
bars: false,
|
||||
frames: [
|
||||
panes: [
|
||||
{
|
||||
tone: "light",
|
||||
fill: false,
|
||||
frameClass:
|
||||
"flex flex-1 overflow-hidden pl-5 pt-3.5 bg-surface-gray-2 rounded-tl-[10.5px] max-h-[78px]",
|
||||
containerClass:
|
||||
"flex flex-1 pl-5 pt-3.5 bg-surface-gray-2 rounded-tl-[10.5px]",
|
||||
screenClass: "bg-white rounded-tl-sm w-full",
|
||||
},
|
||||
{
|
||||
tone: "dark",
|
||||
fill: false,
|
||||
frameClass:
|
||||
"flex flex-1 overflow-hidden pl-5 pt-3.5 bg-surface-gray-3 rounded-tr-[10.5px] max-h-[78px]",
|
||||
containerClass:
|
||||
"flex flex-1 pl-5 pt-3.5 bg-surface-gray-3 rounded-tr-[10.5px]",
|
||||
screenClass: "bg-gray-900 rounded-tl-sm w-full",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -39,20 +39,20 @@
|
||||
/>
|
||||
<div
|
||||
v-if="user.doc?.user_image"
|
||||
class="z-1 size-4 absolute -top-1 -right-1 flex cursor-pointer items-center justify-center rounded-full bg-surface-base opacity-0 duration-300 ease-in-out group-hover:opacity-100 hover:bg-surface-gray-2 outline outline-black-overlay-50"
|
||||
class="z-1 size-4 absolute -top-1 -right-1 flex cursor-pointer items-center justify-center rounded-full bg-surface-white opacity-0 duration-300 ease-in-out group-hover:opacity-100 hover:bg-surface-gray-2 outline outline-black-overlay-50"
|
||||
@click.stop="updateImage()"
|
||||
@mouseenter="isHoveringRemove = true"
|
||||
@mouseleave="isHoveringRemove = false"
|
||||
>
|
||||
<span
|
||||
class="lucide-x size-3.5 cursor-pointer text-ink-gray-4"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
name="x"
|
||||
class="size-3.5 cursor-pointer text-ink-gray-4"
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div
|
||||
v-if="uploading"
|
||||
class="w-full h-full top-0 left-0 absolute bg-surface-gray-10 bg-opacity-20 rounded-full flex items-center justify-center"
|
||||
class="w-full h-full top-0 left-0 absolute bg-surface-gray-7 bg-opacity-20 rounded-full flex items-center justify-center"
|
||||
>
|
||||
<LoadingIndicator class="size-4" />
|
||||
</div>
|
||||
@@ -61,7 +61,7 @@
|
||||
<div class="flex flex-col gap-1">
|
||||
<div v-if="!editName" class="flex items-end gap-1">
|
||||
<span
|
||||
class="text-md sm:text-xl !font-semibold text-ink-gray-8"
|
||||
class="text-lg sm:text-xl !font-semibold text-ink-gray-8"
|
||||
>
|
||||
{{ user?.doc?.full_name }}
|
||||
</span>
|
||||
@@ -100,7 +100,7 @@
|
||||
<div>
|
||||
<div class="flex items-center justify-between h-7">
|
||||
<div class="flex gap-2 items-center">
|
||||
<span class="text-base-semibold text-ink-gray-9">
|
||||
<span class="text-base font-semibold text-ink-gray-9">
|
||||
{{ __("Account Info & Security") }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
<div v-if="hasAgentRecord" class="flex items-center justify-between mt-6">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Availability") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -124,7 +124,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-6">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Emails & Signature") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -142,7 +142,7 @@
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-6">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Password") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">{{
|
||||
|
||||
@@ -18,42 +18,25 @@
|
||||
<template #content>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Signature") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
{{ __("Manage your email signature.") }}
|
||||
</span>
|
||||
</div>
|
||||
<Editor
|
||||
:model-value="user?.doc?.email_signature"
|
||||
<TextEditor
|
||||
editor-class="!prose-sm max-w-full overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded-b border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals 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 -mt-0.5 rtl:text-end"
|
||||
:content="user?.doc?.email_signature"
|
||||
:placeholder="__('Write your email signature here.')"
|
||||
:extensions="extensions"
|
||||
:upload-function="(file) => uploadFunction(file)"
|
||||
:bubbleMenu="true"
|
||||
:fixed-menu="true"
|
||||
@change="(val) => (user.doc.email_signature = val)"
|
||||
>
|
||||
<template #default="{ editor }">
|
||||
<div class="flex flex-col">
|
||||
<div
|
||||
class="overflow-x-auto hide-scrollbar rounded-t border border-b-0 border-[--surface-gray-2] bg-surface-gray-2 px-2 py-1"
|
||||
>
|
||||
<EditorFixedMenu
|
||||
:editor="editor"
|
||||
:items="textEditorMenuItems"
|
||||
/>
|
||||
</div>
|
||||
<EditorBubbleMenu :editor="editor" :items="articleToolbar" />
|
||||
<EditorContent
|
||||
:editor="editor"
|
||||
class="!prose-sm max-w-full overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded-b border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-elevation-2 hover:shadow-sm focus:bg-surface-base 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 -mt-0.5 rtl:text-end"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Editor>
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4 mt-6">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-base-medium text-ink-gray-8">
|
||||
<span class="text-base font-medium text-ink-gray-8">
|
||||
{{ __("Emails") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -67,10 +50,10 @@
|
||||
<div>
|
||||
<div
|
||||
v-if="user.doc.user_emails?.length"
|
||||
class="w-full border rounded-md mb-2 border-outline-elevation-2"
|
||||
class="w-full border rounded-md mb-2 border-outline-gray-modals"
|
||||
>
|
||||
<div
|
||||
class="grid grid-cols-[4fr_4fr_0.3fr] gap-2 px-4 py-3 text-sm-medium text-ink-gray-5 border-b border-outline-elevation-2"
|
||||
class="grid grid-cols-[4fr_4fr_0.3fr] gap-2 px-4 py-3 text-sm font-medium text-ink-gray-5 border-b border-outline-gray-modals"
|
||||
>
|
||||
<span>{{ __("Email Account") }}</span>
|
||||
<span>{{ __("Email") }}</span>
|
||||
@@ -79,7 +62,7 @@
|
||||
<div
|
||||
v-for="e in user.doc.user_emails"
|
||||
:key="e.name"
|
||||
class="grid grid-cols-[4fr_4fr_0.3fr] gap-2 group items-center px-4 py-2.5 text-base border-b border-outline-elevation-2 last:border-b-0"
|
||||
class="grid grid-cols-[4fr_4fr_0.3fr] gap-2 group items-center px-4 py-2.5 text-base border-b border-outline-gray-modals last:border-b-0"
|
||||
>
|
||||
<span class="text-ink-gray-8 font-medium truncate">
|
||||
{{ e.email_account }}
|
||||
@@ -103,7 +86,7 @@
|
||||
>
|
||||
<template #target="{ togglePopover }">
|
||||
<Button
|
||||
class="!bg-surface-elevation-2"
|
||||
class="!bg-surface-modal"
|
||||
variant="outline"
|
||||
:label="__('Add Email')"
|
||||
iconLeft="lucide-plus"
|
||||
@@ -146,26 +129,10 @@ import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
|
||||
import { getUserEmailInfo } from "@/composables/useUserEmailInfo";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { __ } from "@/translation";
|
||||
import { textEditorMenuItems } from "@/editor-menu";
|
||||
import {
|
||||
CleanStyles,
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
} from "@/tiptap-extensions";
|
||||
import { normalize, uploadFunction } from "@/utils";
|
||||
import { Button, createDocumentResource, toast } from "frappe-ui";
|
||||
import {
|
||||
Editor,
|
||||
EditorBubbleMenu,
|
||||
EditorContent,
|
||||
EditorFixedMenu,
|
||||
RichTextKit,
|
||||
articleToolbar,
|
||||
} from "frappe-ui/editor";
|
||||
import { normalize } from "@/utils";
|
||||
import { Button, createDocumentResource, TextEditor, toast } from "frappe-ui";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { disableSettingModalOutsideClick } from "../settingsModal";
|
||||
|
||||
const extensions = [RichTextKit, ComponentUtils, HandleExcelPaste, CleanStyles];
|
||||
const { userId } = useAuthStore();
|
||||
const user = createDocumentResource({ doctype: "User", name: userId });
|
||||
const emit = defineEmits(["updateStep"]);
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
class="text-sm text-ink-gray-5"
|
||||
:class="
|
||||
confirmPasswordMessage === __('Passwords match')
|
||||
? 'text-ink-green-6'
|
||||
: 'text-ink-red-6'
|
||||
? 'text-ink-green-3'
|
||||
: 'text-ink-red-3'
|
||||
"
|
||||
>
|
||||
{{ confirmPasswordMessage }}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
@update:model-value="savedRepliesSearchQuery = $event"
|
||||
:placeholder="__('Search')"
|
||||
type="text"
|
||||
class="bg-surface-base hover:bg-surface-base focus:ring-0 border-outline-gray-2"
|
||||
class="bg-surface-white hover:bg-surface-white focus:ring-0 border-outline-gray-2"
|
||||
:debounce="300"
|
||||
>
|
||||
<template #prefix>
|
||||
@@ -47,12 +47,9 @@
|
||||
class="flex items-center justify-between w-fit p-4"
|
||||
>
|
||||
<template #suffix>
|
||||
<span
|
||||
:class="[
|
||||
'h-4',
|
||||
open ? 'lucide-chevron-up' : 'lucide-chevron-down',
|
||||
]"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
:name="open ? 'chevron-up' : 'chevron-down'"
|
||||
class="h-4"
|
||||
/>
|
||||
</template>
|
||||
</Button>
|
||||
@@ -66,10 +63,10 @@
|
||||
<span class="whitespace-nowrap">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="activeFilter === item.value"
|
||||
class="lucide-check size-4 text-ink-gray-7"
|
||||
aria-hidden="true"
|
||||
name="check"
|
||||
class="size-4 text-ink-gray-7"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
@@ -114,7 +111,7 @@
|
||||
:key="savedReply.name"
|
||||
>
|
||||
<div
|
||||
class="grid grid-cols-12 items-center gap-4 cursor-pointer hover:bg-surface-sidebar rounded"
|
||||
class="grid grid-cols-12 items-center gap-4 cursor-pointer hover:bg-surface-menu-bar rounded"
|
||||
>
|
||||
<div
|
||||
@click="
|
||||
@@ -125,7 +122,9 @@
|
||||
"
|
||||
class="w-full px-2 flex flex-col justify-center h-12.5 col-span-7 min-w-0"
|
||||
>
|
||||
<div class="text-base-medium text-ink-gray-7 w-full truncate">
|
||||
<div
|
||||
class="text-base text-ink-gray-7 font-medium w-full truncate"
|
||||
>
|
||||
{{ savedReply.title }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,6 +206,7 @@ import {
|
||||
Button,
|
||||
call,
|
||||
Dropdown,
|
||||
FeatherIcon,
|
||||
LoadingIndicator,
|
||||
TextInput,
|
||||
toast,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="props.items.length > 0"
|
||||
class="max-h-[300px] min-w-40 overflow-y-auto rounded-lg bg-surface-base p-1 text-base shadow-lg pointer-events-auto hide-scrollbar"
|
||||
class="max-h-[300px] min-w-40 overflow-y-auto rounded-lg bg-surface-white p-1 text-base shadow-lg pointer-events-auto hide-scrollbar"
|
||||
>
|
||||
<button
|
||||
v-for="(item, index) in props.items"
|
||||
|
||||
@@ -25,21 +25,16 @@
|
||||
<div class="space-y-1.5">
|
||||
<FormLabel :label="__('Preview')" />
|
||||
<div class="relative">
|
||||
<Editor
|
||||
:model-value="dialogModel.preview"
|
||||
<TextEditor
|
||||
editor-class="!prose-sm max-w-full 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 flex"
|
||||
:bubble-menu="menuButtons"
|
||||
:content="dialogModel.preview"
|
||||
:editable="false"
|
||||
:extensions="extensions"
|
||||
>
|
||||
<template #default="{ editor }">
|
||||
<EditorContent
|
||||
:editor="editor"
|
||||
class="pointer-events-none !prose-sm max-w-full 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-elevation-2 hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-base 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 flex"
|
||||
/>
|
||||
</template>
|
||||
</Editor>
|
||||
class="pointer-events-none"
|
||||
/>
|
||||
<div
|
||||
v-if="getResponsePreviewResource.loading"
|
||||
class="absolute top-0 end-0 flex items-center justify-center size-full rounded-md bg-surface-gray-10/20"
|
||||
class="absolute top-0 end-0 flex items-center justify-center size-full rounded-md bg-surface-gray-7/20"
|
||||
>
|
||||
<LoadingIndicator class="size-4" />
|
||||
</div>
|
||||
@@ -56,16 +51,15 @@ import {
|
||||
createResource,
|
||||
Dialog,
|
||||
FormLabel,
|
||||
TextEditor,
|
||||
toast,
|
||||
} from "frappe-ui";
|
||||
import { LoadingIndicator } from "frappe-ui";
|
||||
import { Editor, EditorContent, RichTextKit } from "frappe-ui/editor";
|
||||
import { menuButtons } from "../savedReplies";
|
||||
import { Link } from "@/components";
|
||||
import { watch } from "vue";
|
||||
import { __ } from "@/translation";
|
||||
|
||||
const extensions = [RichTextKit];
|
||||
|
||||
const dialogModel = defineModel<{
|
||||
show: boolean;
|
||||
ticketId: string;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<slot name="title">
|
||||
<h1 class="text-md-semibold text-ink-gray-8">
|
||||
<h1 class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ title }}
|
||||
</h1>
|
||||
</slot>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
:style="{ height: 'calc(100vh - 8rem)' }"
|
||||
>
|
||||
<div
|
||||
class="flex-col rounded-l-lg w-56 shrink-0 ps-1 py-1 bg-surface-sidebar overflow-y-auto hide-scrollbar"
|
||||
class="flex-col rounded-l-lg w-56 shrink-0 ps-1 py-1 bg-surface-menu-bar overflow-y-auto hide-scrollbar"
|
||||
>
|
||||
<h1
|
||||
class="h-7.5 px-2 py-[7px] my-[3px] flex cursor-pointer gap-1.5 text-xs-medium text-ink-gray-5 transition-all duration-300 ease-in-out sticky top-0 z-10 bg-surface-sidebar"
|
||||
class="h-7.5 px-2 py-[7px] my-[3px] flex cursor-pointer gap-1.5 text-xs font-medium text-ink-gray-5 transition-all duration-300 ease-in-out sticky top-0 z-10 bg-surface-menu-bar"
|
||||
>
|
||||
{{ __("Account") }}
|
||||
</h1>
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<div
|
||||
v-if="!tab.hideLabel"
|
||||
class="h-7.5 px-2 py-[7px] my-[3px] flex cursor-pointer gap-1.5 text-xs-medium text-ink-gray-5 transition-all duration-300 ease-in-out sticky top-0 z-10 bg-surface-sidebar"
|
||||
class="h-7.5 px-2 py-[7px] my-[3px] flex cursor-pointer gap-1.5 text-xs font-medium text-ink-gray-5 transition-all duration-300 ease-in-out sticky top-0 z-10 bg-surface-menu-bar"
|
||||
>
|
||||
<Tooltip :text="__(tab.label)" placement="right">
|
||||
<span class="truncate">{{ __(tab.label) }}</span>
|
||||
@@ -37,7 +37,7 @@
|
||||
class="flex h-7 w-full items-center gap-2 rounded px-2 py-[7px]"
|
||||
:class="[
|
||||
activeTab?.label == item.label
|
||||
? 'bg-surface-elevation-3 shadow-sm'
|
||||
? 'bg-surface-selected shadow-sm'
|
||||
: 'hover:bg-surface-gray-2',
|
||||
]"
|
||||
@click="() => onTabChange(item)"
|
||||
@@ -56,7 +56,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-1 flex-col bg-surface-elevation-2 max-w-[816px] overflow-hidden relative"
|
||||
class="flex flex-1 flex-col bg-surface-modal max-w-[816px] overflow-hidden relative"
|
||||
>
|
||||
<component
|
||||
:is="activeTab.component"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="absolute bg-surface-base top-2">
|
||||
<div class="absolute bg-surface-white top-2">
|
||||
<DurationPicker
|
||||
v-model="priorityData.response_time"
|
||||
:options="{ seconds: false }"
|
||||
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="absolute bg-surface-base top-2">
|
||||
<div class="absolute bg-surface-white top-2">
|
||||
<DurationPicker
|
||||
v-model="priorityData.resolution_time"
|
||||
:options="{ seconds: false }"
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
value: 'Sunday',
|
||||
},
|
||||
]"
|
||||
:class="{ 'border-outline-red-4': errors.workday }"
|
||||
:class="{ 'border-outline-red-3': errors.workday }"
|
||||
@blur="validateField('workday')"
|
||||
/>
|
||||
<ErrorMessage :message="errors.workday" class="mt-2" />
|
||||
@@ -58,7 +58,7 @@
|
||||
placeholder="Start Time"
|
||||
label="Start Time"
|
||||
v-model="workDayData.start_time"
|
||||
:class="{ 'border-outline-red-4': errors.start_time }"
|
||||
:class="{ 'border-outline-red-3': errors.start_time }"
|
||||
@blur="validateField('start_time')"
|
||||
/>
|
||||
<ErrorMessage :message="errors.start_time" class="mt-2" />
|
||||
@@ -72,7 +72,7 @@
|
||||
placeholder="End Time"
|
||||
label="End Time"
|
||||
v-model="workDayData.end_time"
|
||||
:class="{ 'border-outline-red-4': errors.end_time }"
|
||||
:class="{ 'border-outline-red-3': errors.end_time }"
|
||||
@blur="validateTimeRange"
|
||||
/>
|
||||
<ErrorMessage :message="errors.end_time" class="mt-2" />
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
class="flex p-4 items-center cursor-pointer justify-center gap-2 text-sm border border-outline-gray-2 text-ink-gray-5 rounded-md"
|
||||
@click="props.conditions.push(['', '', ''])"
|
||||
>
|
||||
<span class="lucide-plus h-4" aria-hidden="true" />
|
||||
<FeatherIcon name="plus" class="h-4" />
|
||||
{{ __("Add a custom condition") }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-2">
|
||||
@@ -30,7 +30,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, Dropdown, ErrorMessage } from "frappe-ui";
|
||||
import { Button, Dropdown, ErrorMessage, FeatherIcon } from "frappe-ui";
|
||||
import { slaDataErrors, validateSlaData } from "@/stores/sla";
|
||||
import { watchDebounced } from "@vueuse/core";
|
||||
import CFConditions from "@/components/conditions-filter/CFConditions.vue";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="text-md-semibold text-ink-gray-8">
|
||||
<div class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Work Schedule and Holidays") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-6 max-w-lg">
|
||||
@@ -22,7 +22,7 @@
|
||||
</template>
|
||||
<template #body>
|
||||
<div
|
||||
class="my-2 min-w-40 rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
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="max-h-52 overflow-y-auto p-1">
|
||||
<div
|
||||
@@ -49,7 +49,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mt-1.5 flex flex-col gap-1 border-t border-outline-elevation-2 pt-1.5 p-1"
|
||||
class="mt-1.5 flex flex-col gap-1 border-t border-outline-gray-modals pt-1.5 p-1"
|
||||
>
|
||||
<Button
|
||||
class="w-full !justify-start !text-ink-gray-5"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<SettingsLayoutBase>
|
||||
<template #title>
|
||||
<h1 class="text-md-semibold text-ink-gray-8">
|
||||
<h1 class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Service Level Agreements (SLAs)") }}
|
||||
</h1>
|
||||
</template>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<ShieldCheck class="size-6 text-ink-gray-6" />
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<div class="text-base-medium text-ink-gray-6">
|
||||
<div class="text-base font-medium text-ink-gray-6">
|
||||
{{ __("No SLA found") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-5 max-w-60 text-center">
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div
|
||||
class="grid grid-cols-6 items-center gap-4 cursor-pointer hover:bg-surface-sidebar rounded"
|
||||
class="grid grid-cols-6 items-center gap-4 cursor-pointer hover:bg-surface-menu-bar rounded"
|
||||
>
|
||||
<div
|
||||
@click="slaActiveScreen = { screen: 'view', data: data, fetchData: true }"
|
||||
class="w-full ps-2 col-span-5 flex flex-col justify-center h-14"
|
||||
>
|
||||
<div class="text-base-medium text-ink-gray-7 flex items-center gap-2">
|
||||
<div
|
||||
class="text-base text-ink-gray-7 font-medium flex items-center gap-2"
|
||||
>
|
||||
{{ data.name }}
|
||||
<Badge v-if="data.default_sla" color="gray" size="sm">Default</Badge>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@click="toggleEnabled"
|
||||
>
|
||||
<Switch size="sm" v-model="slaData.enabled" />
|
||||
<span class="text-sm-medium text-ink-gray-7">
|
||||
<span class="text-sm text-ink-gray-7 font-medium">
|
||||
{{ __("Enabled") }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -62,7 +62,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">{{
|
||||
<span class="text-lg font-semibold text-ink-gray-8">{{
|
||||
__("Assignment Conditions")
|
||||
}}</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -75,7 +75,7 @@
|
||||
:label="__('Set as default SLA')"
|
||||
:model-value="slaData.default_sla"
|
||||
@update:model-value="toggleDefaultSla"
|
||||
class="text-ink-gray-6 text-base-medium"
|
||||
class="text-ink-gray-6 text-base font-medium"
|
||||
/>
|
||||
<div
|
||||
v-if="isOldSla && slaActiveScreen.data && !slaData.default_sla"
|
||||
@@ -86,12 +86,12 @@
|
||||
class="text-sm text-ink-gray-6 flex gap-1 cursor-default"
|
||||
>
|
||||
{{ __("Old Conditions") }}
|
||||
<span class="lucide-info size-4" aria-hidden="true" />
|
||||
<FeatherIcon name="info" class="size-4" />
|
||||
</div>
|
||||
</template>
|
||||
<template #body-main>
|
||||
<div
|
||||
class="text-sm text-ink-gray-6 p-2 bg-surface-base rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||
class="text-sm text-ink-gray-6 p-2 bg-surface-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
|
||||
>
|
||||
<code>{{ slaData.condition }}</code>
|
||||
</div>
|
||||
@@ -128,7 +128,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">
|
||||
<span class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Valid From") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -175,7 +175,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">
|
||||
<span class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Response and Resolution") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
@@ -197,7 +197,7 @@
|
||||
:checked="!slaData.apply_sla_for_resolution"
|
||||
type="radio"
|
||||
/>
|
||||
<div class="select-none text-ink-gray-6 text-sm-medium">
|
||||
<div class="select-none text-ink-gray-6 text-sm font-medium">
|
||||
Apply SLA for response time
|
||||
</div>
|
||||
</div>
|
||||
@@ -210,7 +210,7 @@
|
||||
:checked="slaData.apply_sla_for_resolution"
|
||||
type="radio"
|
||||
/>
|
||||
<div class="select-none text-ink-gray-6 text-sm-medium">
|
||||
<div class="select-none text-ink-gray-6 text-sm font-medium">
|
||||
Apply SLA for response time and resolution time
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,7 +223,7 @@
|
||||
<hr class="my-8" />
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-md-semibold text-ink-gray-8">
|
||||
<span class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Status Details") }}
|
||||
</span>
|
||||
<span class="text-p-sm text-ink-gray-6">
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
}"
|
||||
>
|
||||
{{ column.label }}
|
||||
<span v-if="column.isRequired" class="text-ink-red-6">*</span>
|
||||
<span v-if="column.isRequired" class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr v-if="slaData.priorities?.length !== 0" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="absolute bg-surface-base top-2">
|
||||
<div class="absolute bg-surface-white top-2">
|
||||
<DurationPicker v-model="props.row[column.key]" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
}"
|
||||
>
|
||||
{{ column.label }}
|
||||
<span v-if="column.isRequired" class="text-ink-red-6">*</span>
|
||||
<span v-if="column.isRequired" class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr v-if="slaData.support_and_resolution?.length !== 0" />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2 cursor-pointer">
|
||||
<Switch v-model="teamData.enabled" />
|
||||
<span class="text-sm-medium text-ink-gray-7">
|
||||
<span class="text-sm text-ink-gray-7 font-medium">
|
||||
{{ __("Enabled") }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center justify-between gap-2 cursor-pointer">
|
||||
<Switch v-model="teamEnabled" v-if="!team.loading" />
|
||||
<span class="text-sm-medium text-ink-gray-7">
|
||||
<span class="text-sm text-ink-gray-7 font-medium">
|
||||
{{ __("Enabled") }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -90,7 +90,7 @@
|
||||
<UserIcon class="size-6 text-ink-gray-6" />
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<div class="text-base-medium text-ink-gray-6">
|
||||
<div class="text-base font-medium text-ink-gray-6">
|
||||
{{ __("No members found") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-5 max-w-60 text-center">
|
||||
|
||||
@@ -53,13 +53,13 @@
|
||||
<hr class="mx-2 mt-2" />
|
||||
<div v-for="(team, index) in teams.data" :key="team.name">
|
||||
<div
|
||||
class="flex items-center cursor-pointer hover:bg-surface-sidebar rounded h-12.5"
|
||||
class="flex items-center cursor-pointer hover:bg-surface-menu-bar rounded h-12.5"
|
||||
>
|
||||
<div
|
||||
class="w-full py-3 ps-2 flex gap-1 items-center"
|
||||
@click="() => emit('update:step', 'team-edit', team.name)"
|
||||
>
|
||||
<p class="text-base-medium text-ink-gray-7">
|
||||
<p class="text-base text-ink-gray-7 font-medium">
|
||||
{{ team.name }}
|
||||
</p>
|
||||
<Badge :label="__('Disabled')" v-if="team.disabled" />
|
||||
|
||||
@@ -16,11 +16,7 @@
|
||||
<UserAvatar :name="value" size="xs" />
|
||||
</template>
|
||||
<template #suffix>
|
||||
<span
|
||||
class="lucide-x h-3.5"
|
||||
aria-hidden="true"
|
||||
@click.stop="removeValue(value)"
|
||||
/>
|
||||
<FeatherIcon class="h-3.5" name="x" @click.stop="removeValue(value)" />
|
||||
</template>
|
||||
</Button>
|
||||
<div class="flex-1">
|
||||
@@ -49,7 +45,7 @@
|
||||
</ComboboxAnchor>
|
||||
<ComboboxPortal>
|
||||
<ComboboxContent
|
||||
class="z-10 mt-1 min-w-48 w-auto max-w-96 bg-surface-elevation-2 overflow-hidden rounded-lg shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||
class="z-10 mt-1 min-w-48 w-auto max-w-96 bg-surface-modal overflow-hidden rounded-lg shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||
position="popper"
|
||||
:align="'start'"
|
||||
@openAutoFocus.prevent
|
||||
@@ -69,7 +65,7 @@
|
||||
>
|
||||
<UserAvatar class="me-1" :name="agent.agent_name" size="lg" />
|
||||
<div class="flex flex-col gap-1 p-1 text-ink-gray-8">
|
||||
<div class="text-base-medium">
|
||||
<div class="text-base font-medium">
|
||||
{{ agent.agent_name }}
|
||||
</div>
|
||||
<div class="text-sm text-ink-gray-5">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<SettingsLayoutBase :description="__('Configure your telephony settings.')">
|
||||
<template #title>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1 class="text-md-semibold text-ink-gray-8">
|
||||
<h1 class="text-lg font-semibold text-ink-gray-8">
|
||||
{{ __("Telephony") }}
|
||||
</h1>
|
||||
<UnsavedBadge
|
||||
@@ -35,10 +35,10 @@
|
||||
<div class="flex-1 flex flex-col">
|
||||
<!-- General -->
|
||||
<div
|
||||
class="flex items-center justify-between gap-8 py-3 hover:bg-surface-sidebar rounded px-2"
|
||||
class="flex items-center justify-between gap-8 py-3 hover:bg-surface-menu-bar rounded px-2"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<div class="text-p-base-medium text-ink-gray-7 truncate">
|
||||
<div class="text-p-base font-medium text-ink-gray-7 truncate">
|
||||
{{ __("Default medium") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-5">
|
||||
@@ -61,14 +61,14 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-px border-t mx-2 border-outline-elevation-2" />
|
||||
<div class="h-px border-t mx-2 border-outline-gray-modals" />
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-sidebar px-2"
|
||||
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-menu-bar px-2"
|
||||
@click="emit('updateStep', 'twilio-settings')"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<div class="text-p-base-medium text-ink-gray-7 truncate">
|
||||
<div class="text-p-base font-medium text-ink-gray-7 truncate">
|
||||
{{ __("Twilio") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-5 truncate">
|
||||
@@ -79,20 +79,20 @@
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="lucide-chevron-right size-4 text-ink-gray-5 rtl:rotate-180"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
name="chevron-right"
|
||||
class="size-4 text-ink-gray-5 rtl:rotate-180"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="h-px border-t mx-2 border-outline-elevation-2" />
|
||||
<div class="h-px border-t mx-2 border-outline-gray-modals" />
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-sidebar px-2"
|
||||
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-menu-bar px-2"
|
||||
@click="emit('updateStep', 'exotel-settings')"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<div class="text-p-base-medium text-ink-gray-7 truncate">
|
||||
<div class="text-p-base font-medium text-ink-gray-7 truncate">
|
||||
{{ __("Exotel") }}
|
||||
</div>
|
||||
<div class="text-p-sm text-ink-gray-5 truncate">
|
||||
@@ -103,9 +103,9 @@
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="lucide-chevron-right size-4 text-ink-gray-5 rtl:rotate-180"
|
||||
aria-hidden="true"
|
||||
<FeatherIcon
|
||||
name="chevron-right"
|
||||
class="size-4 text-ink-gray-5 rtl:rotate-180"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -67,7 +67,7 @@ const props = withDefaults(defineProps<P>(), {
|
||||
isActive: false,
|
||||
onClick: () => () => true,
|
||||
to: "",
|
||||
bgColor: "bg-surface-elevation-3",
|
||||
bgColor: "bg-surface-selected",
|
||||
hvColor: "hover:bg-surface-gray-2",
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
@@ -103,12 +103,12 @@
|
||||
v-if="showVariant('empty-state')"
|
||||
:style="{
|
||||
backgroundImage:
|
||||
'radial-gradient(ellipse at center, var(--surface-base) 10%, color-mix(in srgb, var(--surface-base) 90%, transparent) 25%, transparent 70%)',
|
||||
'radial-gradient(ellipse at center, var(--surface-white) 10%, color-mix(in srgb, var(--surface-white) 90%, transparent) 25%, transparent 70%)',
|
||||
}"
|
||||
class="rounded-xl p-6 w-2/3 text-center pointer-events-auto space-y-0.5 relative z-10 bottom-4.5"
|
||||
>
|
||||
<div
|
||||
class="relative z-10 text-ink-gray-7 text-center text-p-base-medium leading-[1.15]"
|
||||
class="relative z-10 text-ink-gray-7 font-medium text-center text-p-base leading-[1.15]"
|
||||
>
|
||||
{{ __(getEmptyState(i).title) }}
|
||||
</div>
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<svg
|
||||
v-for="i in [1, 0.8, 0.6, 0.4, 0.2]"
|
||||
:key="i"
|
||||
class="-all peer fill-ink-gray-3"
|
||||
class="-all peer fill-gray-300"
|
||||
:class="{
|
||||
['fill-ink-yellow-5']: i <= rating,
|
||||
['fill-yellow-500']: i <= rating,
|
||||
'cursor-pointer': !static,
|
||||
'hover:fill-ink-yellow-2': i > rating && !static,
|
||||
'peer-hover:fill-ink-yellow-2': i > rating && !static,
|
||||
'hover:fill-yellow-200': i > rating && !static,
|
||||
'peer-hover:fill-yellow-200': i > rating && !static,
|
||||
}"
|
||||
height="16px"
|
||||
width="16px"
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<template>
|
||||
<div class="rounded p-3 shadow w-full">
|
||||
<Editor
|
||||
ref="editorComponent"
|
||||
:extensions="extensions"
|
||||
:model-value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
:autofocus="autofocus"
|
||||
:upload-function="(file: File) => uploadFunction(file)"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
<FTextEditor
|
||||
ref="e"
|
||||
:extensions="[ComponentUtils, HandleExcelPaste, CleanStyles]"
|
||||
v-bind="$attrs"
|
||||
:editor-class="[
|
||||
'prose-f max-h-64 max-w-none overflow-auto my-4 min-h-[5rem]',
|
||||
getFontFamily(modelValue),
|
||||
]"
|
||||
bubble-menu
|
||||
:content="modelValue"
|
||||
@change="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<template #default="{ editor }">
|
||||
<template #top>
|
||||
<span class="text-base">
|
||||
<span class="flex items-center justify-between">
|
||||
<UserAvatar
|
||||
@@ -22,24 +25,18 @@
|
||||
</span>
|
||||
<slot name="top-bottom" />
|
||||
</span>
|
||||
<EditorContent
|
||||
:editor="editor"
|
||||
:class="[
|
||||
'prose-f max-h-64 max-w-none overflow-auto my-4 min-h-[5rem]',
|
||||
getFontFamily(modelValue),
|
||||
]"
|
||||
/>
|
||||
<EditorBubbleMenu :editor="editor" :items="articleToolbar" />
|
||||
</template>
|
||||
<template #bottom>
|
||||
<div class="flex flex-col gap-2">
|
||||
<slot name="bottom-top" />
|
||||
<div
|
||||
class="flex flex-col space-y-1.5 sm:flex-row sm:items-center sm:justify-between sm:gap-2"
|
||||
class="flex flex-col space-y-1.5 overflow-auto sm:flex-row sm:justify-between"
|
||||
>
|
||||
<div class="flex items-center min-w-0 overflow-x-auto max-w-[70%]">
|
||||
<div class="flex items-center">
|
||||
<slot name="bottom-left" />
|
||||
<EditorFixedMenu :editor="editor" :items="textEditorMenuItems" />
|
||||
<TextEditorFixedMenu :buttons="fixedMenu" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
:label="__('Discard')"
|
||||
theme="gray"
|
||||
@@ -57,58 +54,63 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Editor>
|
||||
</FTextEditor>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { UserAvatar } from "@/components";
|
||||
import { textEditorMenuItems } from "@/editor-menu";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import {
|
||||
CleanStyles,
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
} from "@/tiptap-extensions";
|
||||
import { getFontFamily, isContentEmpty, uploadFunction } from "@/utils";
|
||||
import {
|
||||
Editor,
|
||||
EditorBubbleMenu,
|
||||
EditorContent,
|
||||
EditorFixedMenu,
|
||||
RichTextKit,
|
||||
articleToolbar,
|
||||
} from "frappe-ui/editor";
|
||||
import { computed, ref } from "vue";
|
||||
import { ClearFormattingUtility, getFontFamily, isContentEmpty } from "@/utils";
|
||||
import { TextEditor as FTextEditor, TextEditorFixedMenu } from "frappe-ui";
|
||||
import { computed, nextTick, ref } from "vue";
|
||||
|
||||
interface P {
|
||||
modelValue: string;
|
||||
placeholder?: string;
|
||||
autofocus?: boolean;
|
||||
}
|
||||
|
||||
interface E {
|
||||
(event: "clear"): void;
|
||||
(event: "update:modelValue", value: string): void;
|
||||
(event: "update:modelValue", any): string;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<P>(), {
|
||||
const props = withDefaults(defineProps<P>(), {
|
||||
autofocus: false,
|
||||
});
|
||||
|
||||
defineEmits<E>();
|
||||
|
||||
const e = ref(null);
|
||||
const editor = computed(() => e.value.editor);
|
||||
const authStore = useAuthStore();
|
||||
const editorComponent = ref(null);
|
||||
const editor = computed(() => editorComponent.value?.editor);
|
||||
|
||||
const extensions = [
|
||||
RichTextKit.configure({ heading: { levels: [1, 2, 3, 4, 5, 6] } }),
|
||||
ComponentUtils,
|
||||
HandleExcelPaste,
|
||||
CleanStyles,
|
||||
const fixedMenu = [
|
||||
"Paragraph",
|
||||
["Heading 2", "Heading 3", "Heading 4", "Heading 5"],
|
||||
"Separator",
|
||||
"Bold",
|
||||
"Italic",
|
||||
"Separator",
|
||||
"Bullet List",
|
||||
"Numbered List",
|
||||
"Separator",
|
||||
"Image",
|
||||
"Video",
|
||||
"Link",
|
||||
"Blockquote",
|
||||
"Code",
|
||||
ClearFormattingUtility,
|
||||
];
|
||||
|
||||
defineExpose({
|
||||
editor,
|
||||
});
|
||||
|
||||
if (props.autofocus) {
|
||||
nextTick(() => e.value.editor.commands.focus());
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<Tooltip :text="__(field.label)">
|
||||
<span>{{ __(field.label) }}</span>
|
||||
</Tooltip>
|
||||
<span v-if="field.required" class="text-ink-red-6"> * </span>
|
||||
<span v-if="field.required" class="text-ink-red-3"> * </span>
|
||||
</div>
|
||||
<div
|
||||
class="-m-0.5 min-h-[28px] flex-1 items-center overflow-hidden p-0.5 text-base"
|
||||
@@ -157,7 +157,7 @@ function emitUpdate(fieldname: Field["fieldname"], value: FieldValue) {
|
||||
:deep(.form-control textarea),
|
||||
:deep(.form-control button) {
|
||||
border-color: transparent;
|
||||
background: var(--surface-base);
|
||||
background: var(--surface-white);
|
||||
}
|
||||
|
||||
:deep(.form-control button) {
|
||||
@@ -175,7 +175,7 @@ function emitUpdate(fieldname: Field["fieldname"], value: FieldValue) {
|
||||
}
|
||||
|
||||
:deep(.form-control button svg) {
|
||||
color: var(--ink-base);
|
||||
color: var(--ink-white);
|
||||
width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<Combobox
|
||||
<Autocomplete
|
||||
:label="label ? __(label) : undefined"
|
||||
:options="options"
|
||||
:placeholder="__('Select timezone')"
|
||||
:loading="timezoneResource.loading"
|
||||
v-model="modelValue"
|
||||
class=""
|
||||
:model-value="modelValue"
|
||||
@update:model-value="(v: { label: string; value: string } | string) => (modelValue = typeof v === 'string' ? v : v.value)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { __ } from "@/translation";
|
||||
import { Combobox, createResource } from "frappe-ui";
|
||||
import { Autocomplete, createResource } from "frappe-ui";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="space-y-1.5" v-if="field.display_via_depends_on">
|
||||
<span class="block text-sm text-ink-gray-7">
|
||||
{{ __(field.label) }}
|
||||
<span v-if="field.required" class="place-self-center text-ink-red-6">
|
||||
<span v-if="field.required" class="place-self-center text-ink-red-3">
|
||||
*
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/>
|
||||
<span
|
||||
v-if="expand"
|
||||
class="truncate capitalize text-base-medium text-ink-gray-9"
|
||||
class="truncate capitalize text-base text-ink-gray-9 font-medium"
|
||||
:class="{
|
||||
'font-medium': strong,
|
||||
}"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
!sidebarStore.isExpanded
|
||||
? 'w-auto px-0'
|
||||
: open
|
||||
? 'w-full px-2 bg-surface-base shadow-sm'
|
||||
? 'w-full px-2 bg-surface-white shadow-sm'
|
||||
: 'w-full px-2 hover:bg-surface-gray-3'
|
||||
"
|
||||
>
|
||||
@@ -20,7 +20,9 @@
|
||||
: 'ms-2 w-auto opacity-100'
|
||||
"
|
||||
>
|
||||
<div class="text-base-medium leading-none text-ink-gray-9 truncate">
|
||||
<div
|
||||
class="text-base font-medium leading-none text-ink-gray-9 truncate"
|
||||
>
|
||||
{{ config.brandName || "Helpdesk" }}
|
||||
</div>
|
||||
<div class="mt-1 text-sm text-ink-gray-7">
|
||||
@@ -35,8 +37,9 @@
|
||||
: 'ms-2 relative start-[-2.5px] w-auto opacity-100'
|
||||
"
|
||||
>
|
||||
<span
|
||||
class="lucide-chevron-down h-4 w-4 text-ink-gray-5"
|
||||
<FeatherIcon
|
||||
name="chevron-down"
|
||||
class="h-4 w-4 text-ink-gray-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,32 +2,31 @@
|
||||
<div class="flex items-center">
|
||||
<router-link
|
||||
:to="{ name: routeName }"
|
||||
class="ps-0 pe-0.5 py-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-5 hover:text-ink-gray-7 flex items-center justify-center text-lg-medium"
|
||||
class="ps-0 pe-0.5 py-1 text-lg font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-5 hover:text-ink-gray-7 flex items-center justify-center"
|
||||
>
|
||||
{{ isMobileView ? "..." : label }}
|
||||
</router-link>
|
||||
<span class="mx-0.5 text-base text-ink-gray-4" aria-hidden="true"> / </span>
|
||||
<Dropdown :options="options">
|
||||
<template #default="{ open }">
|
||||
<button
|
||||
type="button"
|
||||
class="group flex items-center rounded px-0.5 py-1 text-lg-medium text-ink-gray-8 focus:outline-none focus-visible:ring-2 focus-visible:ring-outline-gray-3 max-w-[200px] sm:max-w-none"
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="text-lg font-medium text-nowrap truncate max-w-[200px] sm:max-w-none"
|
||||
:label="currentView.label"
|
||||
>
|
||||
<component
|
||||
:is="currentView.icon"
|
||||
class="me-1 h-4 flex items-center justify-center self-center"
|
||||
/>
|
||||
<span class="truncate group-hover:underline">
|
||||
{{ currentView.label }}
|
||||
</span>
|
||||
<span
|
||||
:class="[
|
||||
'ms-0.5 h-4 text-ink-gray-5',
|
||||
open ? 'lucide-chevron-up' : 'lucide-chevron-down',
|
||||
]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<template #prefix>
|
||||
<component
|
||||
:is="currentView.icon"
|
||||
class="h-4 flex items-center justify-center"
|
||||
/>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<FeatherIcon
|
||||
:name="open ? 'chevron-up' : 'chevron-down'"
|
||||
class="h-4 text-ink-gray-8"
|
||||
/>
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template #item-prefix="{ item }">
|
||||
@@ -55,10 +54,10 @@
|
||||
v-if="item.name"
|
||||
class="flex items-center justify-end gap-2 min-w-11"
|
||||
>
|
||||
<span
|
||||
<FeatherIcon
|
||||
v-if="isCurrentView(item)"
|
||||
class="lucide-check size-4 text-ink-gray-7"
|
||||
aria-hidden="true"
|
||||
name="check"
|
||||
class="size-4 text-ink-gray-7"
|
||||
/>
|
||||
<Dropdown align="end" :options="dropdownActions(item)">
|
||||
<template #default="{ open }">
|
||||
@@ -80,7 +79,7 @@
|
||||
<script setup>
|
||||
import Icon from "@/components/Icon.vue";
|
||||
import { useScreenSize } from "@/composables/screen";
|
||||
import { Badge, Dropdown } from "frappe-ui";
|
||||
import { Badge, Dropdown, FeatherIcon } from "frappe-ui";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -139,6 +138,6 @@ const isCurrentView = (item) => {
|
||||
|
||||
/* keep the group label pinned while its items scroll */
|
||||
[data-slot="group"]:has(.kebab-btn) [data-slot="group-label"] {
|
||||
@apply sticky -top-[6px] z-10 bg-surface-elevation-2;
|
||||
@apply sticky -top-[6px] z-10 bg-surface-modal;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex w-full min-w-0 items-center rounded p-2 text-base-medium text-ink-gray-8"
|
||||
class="flex w-full min-w-0 items-center rounded p-2 text-base font-medium text-ink-gray-8"
|
||||
:class="{ 'bg-surface-gray-3': active }"
|
||||
>
|
||||
<component
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex w-full min-w-0 items-center rounded p-2 text-base-medium text-ink-gray-8"
|
||||
class="flex w-full min-w-0 items-center rounded p-2 text-base font-medium text-ink-gray-8"
|
||||
:class="{ 'bg-surface-gray-3': active }"
|
||||
>
|
||||
<component
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:text="customer.name"
|
||||
>
|
||||
<Avatar
|
||||
class="-mr-1.5 cursor-pointer ring-2 ring-[var(--surface-base)] transition hover:z-10 hover:scale-110"
|
||||
class="-mr-1.5 cursor-pointer ring-2 ring-[var(--surface-white)] transition hover:z-10 hover:scale-110"
|
||||
shape="circle"
|
||||
size="sm"
|
||||
:image="customer.image"
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
v-else-if="!feedbackCount.data"
|
||||
class="flex flex-col items-center justify-center gap-3 py-16 text-center"
|
||||
>
|
||||
<LucideStar class="size-7.5 text-ink-gray-4" />
|
||||
<LucideStar class="h-7.5 w-7.5 text-ink-gray-4" />
|
||||
<div class="flex flex-col gap-1 max-w-[282px] m-auto">
|
||||
<p class="text-base-medium text-ink-gray-7">
|
||||
<p class="text-base font-medium text-ink-gray-7">
|
||||
{{ __("No feedback yet") }}
|
||||
</p>
|
||||
<p class="text-ink-gray-6 text-base">
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
@click="$emit('setPrimary')"
|
||||
>
|
||||
<template #icon>
|
||||
<LucideStar class="size-4 fill-ink-amber-6 stroke-ink-amber-6" />
|
||||
<LucideStar class="size-4 fill-ink-amber-2 stroke-ink-amber-2" />
|
||||
</template>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user