Merge pull request #3502 from RitvikSardana/fix/bump-frappe-ui

fix: bump frappe UI
This commit is contained in:
RitvikSardana
2026-06-28 18:22:17 +05:30
committed by GitHub
203 changed files with 2398 additions and 1786 deletions
+7 -7
View File
@@ -11,19 +11,19 @@
"dependencies": {
"@headlessui/vue": "^1.7.22",
"@tailwindcss/typography": "^0.5.9",
"@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",
"@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",
"@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.3",
"frappe-ui": "1.0.0-beta.14",
"gemoji": "^8.1.0",
"mime": "^3.0.0",
"pinia": "^2.0.33",
+2 -2
View File
@@ -32,14 +32,14 @@ onMounted(() => {
window.addEventListener("online", () => {
toast.create({
message: __("You are now online."),
icon: h(Wifi, { class: "text-ink-white" }),
icon: h(Wifi, { class: "text-ink-base" }),
});
});
window.addEventListener("offline", () => {
toast.create({
message: __("You are now offline."),
icon: h(WifiOff, { class: "text-ink-white" }),
icon: h(WifiOff, { class: "text-ink-base" }),
});
});
!isCustomerPortal.value && setConfig("localTimezone", window.timezone?.user);
-72
View File
@@ -1,72 +0,0 @@
<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>
+10 -11
View File
@@ -25,9 +25,8 @@
{{ placeholder || "" }}
</span>
</div>
<FeatherIcon
name="chevron-down"
class="h-4 w-4 text-ink-gray-7"
<span
class="lucide-chevron-down h-4 w-4 text-ink-gray-7"
aria-hidden="true"
/>
</button>
@@ -37,7 +36,7 @@
<template #body="{ isOpen }">
<div v-show="isOpen">
<div
class="mt-1 rounded-lg bg-surface-white py-1 text-base shadow-2xl"
class="mt-1 rounded-lg bg-surface-base py-1 text-base shadow-2xl"
>
<div class="relative px-1.5 pt-0.5">
<ComboboxInput
@@ -57,7 +56,7 @@
class="absolute inset-y-0 end-3 top-px flex items-center"
@click="selectedValue = null"
>
<FeatherIcon name="x" class="size-4" />
<span class="lucide-x size-4" aria-hidden="true" />
</button>
</div>
<ComboboxOptions
@@ -72,7 +71,7 @@
>
<div
v-if="group.group && !group.hideLabel"
class="px-2.5 py-1.5 text-sm font-medium text-ink-gray-4"
class="px-2.5 py-1.5 text-sm-medium text-ink-gray-4"
>
{{ group.group }}
</div>
@@ -129,7 +128,7 @@ import {
ComboboxOption,
ComboboxOptions,
} from "@headlessui/vue";
import { FeatherIcon, Popover } from "frappe-ui";
import { Popover } from "frappe-ui";
import { computed, nextTick, ref, useAttrs, useSlots, watch } from "vue";
const props = defineProps({
@@ -250,7 +249,7 @@ const inputClasses = computed(() => {
let sizeClasses = {
sm: "text-base rounded h-7",
md: "text-base rounded h-8",
lg: "text-lg rounded-md h-10",
lg: "text-md rounded-md h-10",
xl: "text-xl rounded-md h-10",
}[props.size];
@@ -264,11 +263,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-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",
"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",
outline:
"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",
"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",
disabled: [
"border bg-surface-menu-bar placeholder-ink-gray-3",
"border bg-surface-sidebar 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-white shadow-xl min-w-[140px]"
class="flex flex-col mx-3 p-1.5 rounded-lg border border-outline-gray-1 bg-surface-base shadow-xl min-w-[140px]"
>
<button
v-for="option in agentStatusStore.statusOptions"
+2 -2
View File
@@ -26,10 +26,10 @@
</div>
<div
@click="showCallLogDetailModal = true"
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"
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"
>
<div class="flex items-center justify-between">
<div class="inline-flex gap-2 items-center text-base font-medium">
<div class="inline-flex gap-2 items-center text-base-medium">
<div>
{{
activity.call_type == "Incoming"
+11 -11
View File
@@ -15,7 +15,7 @@
<div class="flex items-end w-full gap-2">
<slot name="text">
<div
class="text-2xl font-medium text-center text-ink-gray-8 whitespace-nowrap"
class="text-2xl-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>
<FeatherIcon name="chevron-down" class="size-4" />
<span class="lucide-chevron-down size-4" aria-hidden="true" />
</div>
<template #item-label="{ item }">
<div
@@ -44,10 +44,10 @@
</div>
</template>
<template #item-suffix="{ item }">
<FeatherIcon
<span
v-if="item.label == __(currentDuration)"
name="check"
class="size-4"
class="lucide-check size-4"
aria-hidden="true"
/>
</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 font-medium text-center text-ink-gray-8 whitespace-nowrap"
class="text-2xl-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() }}
<FeatherIcon name="chevron-down" class="size-4" />
<span class="lucide-chevron-down size-4" aria-hidden="true" />
</div>
<template #item-label="{ item }">
<div
@@ -96,10 +96,10 @@
</div>
</template>
<template #item-suffix="{ item }">
<FeatherIcon
<span
v-if="item.label == __(currentDuration)"
name="check"
class="size-4"
class="lucide-check size-4"
aria-hidden="true"
/>
</template>
</Dropdown>
@@ -111,7 +111,7 @@
<script setup lang="ts">
import { __ } from "@/translation";
import { EChartsOption } from "echarts";
import { Dropdown, ECharts, FeatherIcon } from "frappe-ui";
import { Dropdown, ECharts } from "frappe-ui";
import { computed, type PropType } from "vue";
const props = defineProps({
+36 -22
View File
@@ -39,21 +39,19 @@
:id="`comment-${name}`"
class="rounded-md bg-surface-gray-1 transition-colors px-3 py-1.5"
>
<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">
<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">
<div>
<Button
:label="
@@ -70,7 +68,7 @@
<Button label="Discard" @click="handleDiscard" />
</div>
</template>
</TextEditor>
</Editor>
<div
class="flex flex-wrap gap-2 mb-2"
v-if="!editable && Boolean(attachments.length)"
@@ -97,13 +95,13 @@
</template>
<template #body>
<div
class="bg-surface-white rounded-lg shadow-lg p-2 border border-outline-gray-2"
class="bg-surface-base 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-lg transition-colors"
class="size-6 flex items-center justify-center rounded hover:bg-surface-gray-2 text-md transition-colors"
@click="handleReaction(emoji)"
>
{{ emoji }}
@@ -117,7 +115,7 @@
<Tooltip>
<template #body>
<div
class="bg-surface-gray-7 px-2 py-1 text-center text-p-xs text-ink-white shadow-xl rounded"
class="bg-surface-gray-10 px-2 py-1 text-center text-p-xs text-ink-base shadow-xl rounded"
>
<span v-for="(user, idx) in reaction.users" :key="user.user"
>{{ user.full_name
@@ -129,7 +127,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-3 hover:bg-surface-blue-3'
? 'bg-surface-blue-2 text-ink-blue-6 hover:bg-surface-blue-3'
: 'bg-surface-gray-3 text-ink-gray-6 hover:bg-surface-gray-4'
"
v-if="reaction.count !== 0"
@@ -157,24 +155,34 @@ 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();
@@ -198,6 +206,12 @@ 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);
+37 -26
View File
@@ -1,23 +1,23 @@
<template>
<TextEditor
<Editor
v-if="agentsList.data"
ref="editorRef"
: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] } }"
v-model="newComment"
:extensions="extensions"
:placeholder="placeholder"
:editable="editable"
:mentions="dropdown"
@change="editable ? (newComment = $event) : null"
:extensions="[ComponentUtils, HandleExcelPaste, CleanStyles]"
:uploadFunction="(file:any)=>uploadFunction(file, doctype, ticketId)"
:upload-function="(file:any)=>uploadFunction(file, doctype, ticketId)"
>
<template #bottom>
<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),
]"
/>
<!-- 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>
<FeatherIcon
class="h-3.5"
name="x"
<span
class="lucide-x h-3.5"
aria-hidden="true"
@click.stop="removeAttachment(a)"
/>
</template>
@@ -63,9 +63,9 @@
</button>
</template>
</FileUploader>
<div class="h-4 w-[2px] border-s" />
<div class="ml-0.5 mr-1 h-5 w-px bg-surface-gray-3" />
</div>
<TextEditorFixedMenu :buttons="textEditorMenuButtons" />
<EditorFixedMenu :editor="editor" :items="textEditorMenuItems" />
</div>
<div class="flex items-center justify-end gap-x-2 w-[40%]">
<Button
@@ -96,21 +96,23 @@
</div>
</div>
</template>
</TextEditor>
</Editor>
</template>
<script setup lang="ts">
import { FileUploader, createResource } from "frappe-ui";
import {
FileUploader,
TextEditor,
TextEditorFixedMenu,
createResource,
} from "frappe-ui";
Editor,
EditorContent,
EditorFixedMenu,
RichTextKit,
} from "frappe-ui/editor";
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 {
@@ -122,7 +124,6 @@ import {
getFontFamily,
isContentEmpty,
removeAttachmentFromServer,
textEditorMenuButtons,
uploadFunction,
} from "@/utils";
import { useStorage } from "@vueuse/core";
@@ -223,6 +224,16 @@ 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 ||
+25 -24
View File
@@ -1,16 +1,13 @@
<template>
<div class="flex flex-col">
<TextEditor
<Editor
ref="editorRef"
:editor-class="editorClass"
:bubble-menu="false"
:content="internalContent"
:extensions="extensions"
v-bind="uploadFn ? { uploadFunction: uploadFn } : {}"
v-model="internalContent"
:extensions="editorExtensions"
:upload-function="uploadFn"
:placeholder="placeholder"
@change="(val: string) => (internalContent = val)"
>
<template #top>
<template #default="{ editor }">
<div
class="flex items-center overflow-x-auto rounded-t border border-b-0 border-[--surface-gray-2] px-2 py-1"
>
@@ -38,10 +35,11 @@
</FileUploader>
<div class="h-4 w-[2px] border-s" />
</div>
<TextEditorFixedMenu :buttons="(menuButtons as any)" />
<EditorFixedMenu :editor="editor" :items="savedReplyMenuItems" />
</div>
<EditorContent :editor="editor" :class="editorClass" />
</template>
</TextEditor>
</Editor>
<div
v-if="showAttachments && attachments?.length"
class="flex flex-wrap gap-2 mt-2"
@@ -53,9 +51,9 @@
:url="attachment.file_url"
>
<template #suffix>
<FeatherIcon
class="h-3.5 cursor-pointer"
name="x"
<span
class="lucide-x h-3.5 cursor-pointer"
aria-hidden="true"
@click.self.stop="removeAttachment(attachment)"
/>
</template>
@@ -67,21 +65,21 @@
<script setup lang="ts">
import { AttachmentItem } from "@/components";
import { AttachmentIcon } from "@/components/icons";
import { menuButtons } from "@/components/Settings/SavedReplies/savedReplies";
import { savedReplyMenuItems } from "@/editor-menu";
import { getUserEmailInfo } from "@/composables/useUserEmailInfo";
import {
CleanStyles,
ComponentUtils,
HandleExcelPaste,
} from "@/tiptap-extensions";
import { isContentEmpty } from "@/utils";
import { isContentEmpty, uploadFunction } from "@/utils";
import { FileUploader, type UploadedFile } from "frappe-ui";
import {
FeatherIcon,
FileUploader,
TextEditor,
TextEditorFixedMenu,
type UploadedFile,
} from "frappe-ui";
Editor,
EditorContent,
EditorFixedMenu,
RichTextKit,
} from "frappe-ui/editor";
import { computed, nextTick, onMounted, ref, watch } from "vue";
type UploadFunction = (file: File) => Promise<UploadedFile>;
@@ -105,6 +103,7 @@ const props = withDefaults(
showSignature: false,
type: "Saved Reply",
showAttachments: false,
uploadFn: (file: File) => uploadFunction(file),
}
);
@@ -120,15 +119,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-gray-modals hover:shadow-sm",
"focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0",
"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",
];
const emailClass = [
"!prose-sm max-w-full overflow-auto py-1.5 px-2",
"rounded-b border border-[--surface-gray-2] bg-surface-white",
"rounded-b border border-[--surface-gray-2] bg-surface-base",
"placeholder-ink-gray-4",
"text-ink-gray-8 transition-colors -mt-0.5",
];
@@ -139,6 +138,8 @@ const editorClass = computed(() => [
props.maxHeight,
]);
const editorExtensions = computed(() => [RichTextKit, ...props.extensions]);
const userResource = getUserEmailInfo();
function getDefaultContent(signature: string): string {
+11 -4
View File
@@ -8,7 +8,10 @@
v-bind="action"
>
<template v-if="action.icon" #prefix>
<FeatherIcon :name="action.icon" class="h-4 w-4" />
<span
:class="['h-4 w-4', lucideClass(action.icon)]"
aria-hidden="true"
/>
</template>
</Button>
</div>
@@ -17,9 +20,12 @@
<Dropdown v-slot="{ open }" :options="g.action">
<Button :label="g.label">
<template #suffix>
<FeatherIcon
:name="open ? 'chevron-up' : 'chevron-down'"
class="h-4"
<span
:class="[
'h-4',
open ? 'lucide-chevron-up' : 'lucide-chevron-down',
]"
aria-hidden="true"
/>
</template>
</Button>
@@ -34,6 +40,7 @@
<script setup>
import { Dropdown } from "frappe-ui";
import { computed } from "vue";
import { lucideClass } from "@/utils";
const props = defineProps({
actions: {
+2 -2
View File
@@ -2,7 +2,7 @@
<div
:id="`communication-${name}`"
v-bind="$attrs"
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"
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"
>
<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-gray-modals !-mx-3" />
<div class="border-0 border-t my-3 border-outline-elevation-2 !-mx-3" />
<EmailContent :content="content" />
<div class="flex flex-wrap gap-2">
<AttachmentItem
+43 -34
View File
@@ -1,21 +1,13 @@
<template>
<TextEditor
<Editor
ref="editorRef"
: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] } }"
v-model="newEmail"
:extensions="extensions"
:placeholder="placeholder"
:editable="editable"
@change="editable ? (newEmail = $event) : null"
:extensions="[ComponentUtils, HandleExcelPaste, CleanStyles]"
:uploadFunction="(file:any)=>uploadFunction(file, doctype, ticketId)"
@keydown.capture="handleKeydown"
:upload-function="(file:any)=>uploadFunction(file, doctype, ticketId)"
>
<template #top>
<template #default="{ editor }">
<div
v-if="hasMultipleSenders"
class="mx-6 md:mx-5 flex items-center gap-2 border-t py-2.5 h-12.5"
@@ -101,12 +93,22 @@
:custom-email-label="__('Add to recipients')"
/>
</div>
</template>
<template #editor>
<div class="overflow-y-auto min-h-[7rem] max-h-[30vh] flex flex-col">
<EditorBubbleMenu :editor="editor" :items="articleToolbar" />
<div
class="overflow-y-auto min-h-[7rem] max-h-[30vh] flex flex-col"
@keydown.capture="handleKeydown"
>
<div class="flex-1">
<EditorContent :editor="editor" />
<EditorContent
:editor="editor"
:class="[
'prose-sm max-w-full mx-6 md:mx-5 py-3',
getFontFamily(newEmail),
'[&_p.reply-to-content]:hidden',
]"
/>
</div>
<div
v-if="quotedContent"
@@ -127,8 +129,7 @@
/>
</div>
</div>
</template>
<template #bottom>
<!-- Attachments -->
<div class="flex flex-wrap gap-2 px-5 my-2">
<AttachmentItem
@@ -138,15 +139,15 @@
:url="!['MOV', 'MP4'].includes(a.file_type) ? a.file_url : null"
>
<template #suffix>
<FeatherIcon
class="h-3.5"
name="x"
<span
class="lucide-x h-3.5"
aria-hidden="true"
@click.self.stop="removeAttachment(a)"
/>
</template>
</AttachmentItem>
</div>
<!-- TextEditor Fixed Menu -->
<!-- Editor Fixed Menu -->
<div
class="flex justify-between overflow-scroll px-4 py-2.5 items-center border-t"
>
@@ -184,9 +185,9 @@
>
<SavedReplyIcon class="h-4 w-4" />
</button>
<div class="h-4 w-[2px] border-s" />
<div class="ml-0.5 mr-1 h-5 w-px bg-surface-gray-3" />
</div>
<TextEditorFixedMenu :buttons="textEditorMenuButtons" />
<EditorFixedMenu :editor="editor" :items="textEditorMenuItems" />
</div>
<div class="flex items-center justify-end gap-x-2 sm:mt-0 w-[40%]">
<Button label="Discard" @click="handleDiscard" />
@@ -204,7 +205,7 @@
</div>
</div>
</template>
</TextEditor>
</Editor>
<SavedRepliesSelectorModal
v-model="showSavedRepliesSelectorModal"
:doctype="doctype"
@@ -219,6 +220,7 @@ 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,
@@ -230,19 +232,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 {
FileUploader,
TextEditor,
TextEditorFixedMenu,
createResource,
toast,
} from "frappe-ui";
Editor,
EditorBubbleMenu,
EditorContent,
EditorFixedMenu,
RichTextKit,
articleToolbar,
} from "frappe-ui/editor";
import { useOnboarding } from "frappe-ui/frappe";
import {
computed,
@@ -299,6 +301,13 @@ 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");
+9 -9
View File
@@ -11,7 +11,7 @@
:tooltip="copyOnClick ? __('Click to copy') : undefined"
:class="[
{
'rounded bg-surface-white hover:!bg-surface-gray-1 focus-visible:ring-outline-gray-4':
'rounded border !bg-surface-base 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>
<FeatherIcon
class="h-3.5"
name="x"
<span
class="lucide-x h-3.5"
aria-hidden="true"
@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-white hover:bg-surface-white'
? 'bg-surface-base hover:bg-surface-base'
: '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-modal 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-elevation-2 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"
>
<FeatherIcon name="search" class="h-4" />
<span class="lucide-search h-4" aria-hidden="true" />
{{ __(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 font-medium">
<div class="text-base-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-3 mt-2 pl-2"
class="whitespace-pre-line text-sm text-ink-blue-6 mt-2 pl-2"
>
{{ info }}
</div>
+4 -4
View File
@@ -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 font-medium text-ink-gray-6">
<div class="text-base-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 font-medium text-ink-gray-4 w-9/12 md:w-4/12"
class="flex flex-col items-center gap-2 text-xl-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-white) 10%, color-mix(in srgb, var(--surface-white) 90%, transparent) 25%, transparent 70%)',
'radial-gradient(ellipse at center, var(--surface-base) 10%, color-mix(in srgb, var(--surface-base) 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-lg font-medium text-ink-gray-8': text === 'lg',
'text-md font-medium text-ink-gray-8': text === 'lg',
}"
>
{{ __(title) }}
+2 -2
View File
@@ -7,9 +7,9 @@
:class="icon"
v-bind="$attrs"
/>
<FeatherIcon
<span
v-else-if="typeof icon == 'string'"
:name="icon"
:class="`lucide-${icon}`"
v-bind="$attrs"
/>
<component v-else :is="icon" v-bind="$attrs" />
+5 -8
View File
@@ -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-modal 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-elevation-2 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,12 +26,9 @@
@click.stop="() => (reaction = false)"
/>
</div>
<div
v-else
class="my-3 max-w-max transform bg-surface-white px-4 sm:px-0"
>
<div v-else class="my-3 max-w-max transform bg-surface-base px-4 sm:px-0">
<div
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"
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"
>
<div class="flex gap-2 px-3 pb-1 pt-3">
<div class="flex-1">
@@ -47,13 +44,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-modal pb-2 pt-3 text-sm text-ink-gray-7"
class="sticky top-0 bg-surface-elevation-2 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-blue-200"
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"
v-for="_emoji in emojis"
:key="_emoji.description"
@click="() => (emoji = _emoji.emoji) && togglePopover()"
+3 -3
View File
@@ -12,7 +12,7 @@
/>
<button
type="button"
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="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="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-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"
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"
: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 font-medium text-ink-gray-8">
<span v-if="label" class="text-p-sm-medium text-ink-gray-8">
{{ __(label) }}
</span>
<span v-if="description" class="text-p-sm text-ink-gray-5">
+1 -1
View File
@@ -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 font-medium text-ink-gray-8 justify-between w-full me-1"
class="my-2 flex items-center gap-2 text-base-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" />
+7 -5
View File
@@ -148,7 +148,6 @@ import { useStorage } from "@vueuse/core";
import {
createResource,
Dropdown,
FeatherIcon,
frappeRequest,
ListFooter,
ListHeader,
@@ -446,9 +445,9 @@ function getGroupedByRows(listRows, groupByField) {
group: option || " ",
collapsed: false,
rows: filteredRows,
icon: h(FeatherIcon, {
name: "folder",
class: "h-4 w-4 flex-shrink-0 text-ink-gray-6",
icon: h("span", {
class: ["lucide-folder", "h-4 w-4 flex-shrink-0 text-ink-gray-6"],
"aria-hidden": "true",
}),
};
groupedRows.push(groupDetail);
@@ -817,7 +816,10 @@ function handleScrollPosition() {
}, 200);
}
function handleColumnResize() {
function handleColumnResize({ key, width, save } = {}) {
const column = columns.value.find((c) => c.key === key);
if (column) column.width = width;
if (!save) return;
isViewUpdated.value = true;
defaultParams.columns = columns.value;
if (!defaultParams.is_default) return;
+1 -1
View File
@@ -21,7 +21,7 @@
:text="avatar.name"
>
<Avatar
class="user-avatar -mr-1.5 ring-2 ring-[var(--surface-white)] transition hover:z-10 hover:scale-110"
class="user-avatar -mr-1.5 ring-2 ring-[var(--surface-base)] transition hover:z-10 hover:scale-110"
shape="circle"
:image="avatar.image"
:label="avatar.label"
+9 -2
View File
@@ -10,10 +10,12 @@
/>
<div
class="flex flex-col h-full gap-1.5"
:class="[isMobileView && 'flex-1 items-center justify-center']"
:class="[
(isMobileView || !hasDocInfo) && 'flex-1 items-center justify-center',
]"
>
<div class="flex gap-2 items-center">
<p class="font-medium text-ink-gray-8 text-xl">
<p class="text-ink-gray-8 text-xl-medium">
{{ avatar.label }}
</p>
<Tooltip v-if="badge" :text="badge.tooltip ?? ''">
@@ -56,6 +58,7 @@
<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;
@@ -83,4 +86,8 @@ const props = withDefaults(
);
const { isMobileView } = useScreenSize();
const hasDocInfo = computed(() => {
return props.docInfo?.some((item) => item.value) ?? false;
});
</script>
+1 -1
View File
@@ -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 font-medium text-ink-gray-9">
<div v-if="title" class="text-lg-medium text-ink-gray-9">
{{ title }}
</div>
</slot>
+1 -1
View File
@@ -13,7 +13,7 @@
<Tooltip>
<template #body>
<div
class="rounded bg-surface-gray-7 py-1.5 px-2 text-xs text-ink-white shadow-xl"
class="rounded bg-surface-gray-10 py-1.5 px-2 text-xs text-ink-base 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 font-semibold">{{ __("Saved Replies") }}</div>
<div class="text-2xl-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-white pt-px text-xs font-medium text-ink-gray-8 shadow-sm"
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"
v-if="savedReplyListResource?.data?.length"
>
{{ savedReplyListResource?.data?.length }}
@@ -80,22 +80,28 @@
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 font-semibold truncate border-b pb-2">
<div class="text-base-semibold truncate border-b pb-2">
{{ template.title }}
</div>
<TextEditor
<Editor
v-if="template.message"
:content="template.message"
:model-value="template.message"
:editable="false"
editor-class="!prose-sm max-w-none !text-sm text-ink-gray-5 focus:outline-none"
class="flex-1 overflow-hidden pointer-events-none"
/>
: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>
<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-7/20 rounded-lg"
class="flex items-center justify-center absolute top-0 start-0 w-full h-full bg-surface-gray-10/20 rounded-lg"
>
<LoadingIndicator class="size-4" />
</div>
@@ -133,9 +139,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 {
@@ -154,6 +160,8 @@ const props = defineProps({
},
});
const extensions = [RichTextKit];
const show = defineModel();
const activeFilter = useStorage("saved-replies-filter", "Personal");
const { disableGlobalScopeForSavedReplies, teamRestrictionApplied } =
@@ -297,8 +305,7 @@ watch(
(newValue) => {
if (newValue) {
nextTick(() => {
const inputEl = searchInput.value?.$el?.querySelector("input");
inputEl?.focus();
searchInput.value?.el?.focus();
});
}
},
+4 -4
View File
@@ -23,7 +23,7 @@
v-if="option.image"
:image="option.image"
:label="option.label"
class="border-2 border-[var(--surface-white)] flex-shrink-0"
class="border-2 border-[var(--surface-base)] 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-gray-modals rounded-lg bg-surface-modal 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-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"
>
<!-- Header -->
<div class="py-1.5 px-1.5">
<div
class="flex h-7 items-center text-sm font-medium text-ink-gray-6 justify-between"
class="flex h-7 items-center text-sm-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-white items-center px-2 text-sm font-medium text-ink-gray-6"
class="flex h-7 pt-1.5 sticky top-0 bg-surface-base items-center px-2 text-sm-medium text-ink-gray-6"
>
{{ group.group }}
</div>
+4 -6
View File
@@ -11,19 +11,17 @@
:class="labelClass"
@click="collapsible && toggle()"
>
<FeatherIcon
<span
v-if="collapsible && collapseIconPosition === 'left'"
name="chevron-right"
class="h-4 transition-all duration-300 ease-in-out"
class="lucide-chevron-right h-4 transition-all duration-300 ease-in-out"
:class="{ 'rotate-90': opened }"
/>
<span>
{{ label || "Untitled" }}
</span>
<FeatherIcon
<span
v-if="collapsible && collapseIconPosition === 'right'"
name="chevron-right"
class="h-4 transition-all duration-300 ease-in-out"
class="lucide-chevron-right h-4 transition-all duration-300 ease-in-out"
:class="{ 'rotate-90': opened }"
/>
</div>
+4 -5
View File
@@ -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-white shadow-2xl rounded"
class="p-1 text-ink-gray-6 top-1 absolute w-[--reka-popper-anchor-width] bg-surface-base shadow-2xl rounded"
:class="bodyClass"
>
<div class="max-h-52 overflow-y-auto">
@@ -34,10 +34,9 @@
<div class="w-full truncate">
{{ option.label }}
</div>
<FeatherIcon
<span
v-if="model == option.value"
name="check"
class="size-4 ms-2"
class="lucide-check size-4 ms-2"
/>
</div>
</div>
@@ -55,7 +54,7 @@
</template>
<script setup lang="ts">
import { Button, FeatherIcon, Popover } from "frappe-ui";
import { Button, Popover } from "frappe-ui";
const model = defineModel();
+13 -10
View File
@@ -45,9 +45,12 @@
class="flex items-center justify-between w-fit p-4"
>
<template #suffix>
<FeatherIcon
:name="open ? 'chevron-up' : 'chevron-down'"
class="h-4"
<span
:class="[
'h-4',
open ? 'lucide-chevron-up' : 'lucide-chevron-down',
]"
aria-hidden="true"
/>
</template>
</Button>
@@ -61,10 +64,10 @@
<span class="whitespace-nowrap">
{{ item.label }}
</span>
<FeatherIcon
<span
v-if="activeFilter === item.label"
name="check"
class="size-4 text-ink-gray-7"
class="lucide-check size-4 text-ink-gray-7"
aria-hidden="true"
/>
</div>
</button>
@@ -142,12 +145,12 @@
:label="getUserRole(agent.name)"
:button="{
label: getUserRole(agent.name),
iconRight: 'chevron-down',
iconRight: 'lucide-chevron-down',
iconLeft:
getUserRole(agent.name) === 'Agent'
? 'user'
? 'lucide-user'
: getUserRole(agent.name) === 'Manager'
? 'briefcase'
? 'lucide-briefcase'
: null,
}"
placement="right"
@@ -184,7 +187,7 @@
<script setup lang="ts">
import { useAuthStore } from "@/stores/auth";
import { useUserStore } from "@/stores/user";
import { Avatar, Button, call, Dropdown, FeatherIcon, toast } from "frappe-ui";
import { Avatar, Button, call, Dropdown, 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-lg font-semibold text-ink-gray-8">{{
<span class="text-md-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 font-medium text-ink-gray-8">
<div class="text-base-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-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"
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"
@click="togglePopover()"
>
<div>
@@ -37,12 +37,12 @@
)?.label
}}
</div>
<FeatherIcon name="chevron-down" class="size-4" />
<span class="lucide-chevron-down size-4" />
</div>
</template>
<template #body="{ togglePopover }">
<div
class="p-1 text-ink-gray-7 mt-1 bg-surface-white shadow-xl rounded w-[--reka-popper-anchor-width]"
class="p-1 text-ink-gray-7 mt-1 bg-surface-base shadow-xl rounded w-[--reka-popper-anchor-width]"
>
<div
v-for="option in ticketRoutingOptions"
@@ -58,10 +58,9 @@
<span>
{{ option.label }}
</span>
<FeatherIcon
<span
v-if="assignmentRuleData.rule == option.value"
name="check"
class="size-4"
class="lucide-check size-4"
/>
</div>
</div>
@@ -71,7 +70,7 @@
</div>
<div class="mt-7 flex items-center justify-between gap-2">
<div>
<div class="text-base font-medium text-ink-gray-8">
<div class="text-base-medium text-ink-gray-8">
{{ __("Assignees") }}
</div>
<div class="text-p-sm text-ink-gray-6 mt-1">
@@ -97,7 +96,7 @@
:placement="'top'"
>
<div
class="text-xs rounded-full select-none bg-surface-blue-3 text-ink-white p-0.5 px-2"
class="text-xs rounded-full select-none bg-surface-blue-3 text-ink-base p-0.5 px-2"
>
{{ __("Last") }}
</div>
@@ -12,7 +12,7 @@
</template>
<template #body="{ togglePopover }">
<div
class="mt-1 rounded-lg bg-surface-white py-1 text-base shadow-2xl w-60"
class="mt-1 rounded-lg bg-surface-base 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 = ''"
>
<FeatherIcon name="x" class="w-4" />
<span class="lucide-x 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-menu-bar rounded"
class="grid grid-cols-12 items-center gap-4 cursor-pointer hover:bg-surface-sidebar 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 text-ink-gray-7 font-medium">{{ data.name }}</div>
<div class="text-base-medium text-ink-gray-7">{{ 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-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"
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"
@click="togglePopover()"
>
<div>
@@ -70,27 +70,26 @@
)?.label
}}
</div>
<FeatherIcon name="chevron-down" class="size-4" />
<span class="lucide-chevron-down size-4" />
</div>
</template>
<template #body="{ togglePopover }">
<div
class="p-1 text-ink-gray-6 top-1 absolute bg-surface-white shadow-2xl rounded w-[--reka-popper-anchor-width]"
class="p-1 text-ink-gray-6 top-1 absolute bg-surface-base 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-menu-bar text-base flex items-center justify-between rounded"
class="p-2 cursor-pointer hover:bg-surface-sidebar text-base flex items-center justify-between rounded"
@click="
assignmentRuleData.priority = option.value;
togglePopover();
"
>
{{ option.label }}
<FeatherIcon
<span
v-if="assignmentRuleData.priority == option.value"
name="check"
class="size-4"
class="lucide-check size-4"
/>
</div>
</div>
@@ -118,7 +117,7 @@
<hr class="my-8" />
<div>
<div class="flex flex-col gap-1">
<span class="text-lg font-semibold text-ink-gray-8">{{
<span class="text-md-semibold text-ink-gray-8">{{
__("Assignment Condition")
}}</span>
<div class="flex items-center justify-between gap-6">
@@ -144,12 +143,12 @@
class="text-sm text-ink-gray-6 flex gap-1 cursor-default text-nowrap flex items-center"
>
<span>{{ __("Old Condition") }}</span>
<FeatherIcon name="info" class="size-4" />
<span class="lucide-info size-4" />
</div>
</template>
<template #body-main>
<div
class="text-sm text-ink-gray-6 p-2 bg-surface-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
class="text-sm text-ink-gray-6 p-2 bg-surface-base rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
>
<code>{{ assignmentRuleData.assignCondition }}</code>
</div>
@@ -195,7 +194,7 @@
<hr class="my-8" />
<div>
<div class="flex flex-col gap-1">
<span class="text-lg font-semibold text-ink-gray-8">{{
<span class="text-md-semibold text-ink-gray-8">{{
__("Unassignment Condition")
}}</span>
<div class="flex items-center justify-between gap-6">
@@ -225,12 +224,12 @@
class="text-sm text-ink-gray-6 flex gap-1 cursor-default text-nowrap flex items-center"
>
<span> {{ __("Old Condition") }} </span>
<FeatherIcon name="info" class="size-4" />
<span class="lucide-info size-4" />
</div>
</template>
<template #body-main>
<div
class="text-sm text-ink-gray-6 p-2 bg-surface-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
class="text-sm text-ink-gray-6 p-2 bg-surface-base rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
>
<code>{{ assignmentRuleData.unassignCondition }}</code>
</div>
@@ -272,7 +271,7 @@
<hr class="my-8" />
<div>
<div class="flex flex-col gap-1">
<span class="text-lg font-semibold text-ink-gray-8">{{
<span class="text-md-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-lg font-semibold text-ink-gray-8">
<h1 class="text-md-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 font-medium text-ink-gray-6">
<div class="text-base-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);
"
>
<FeatherIcon name="plus" class="h-4" />
<span class="lucide-plus 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, FeatherIcon } from "frappe-ui";
import { Button, Dropdown, ErrorMessage } 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-gray-modals p-2 cursor-pointer hover:bg-surface-menu-bar rounded h-14"
class="flex justify-between items-center border-outline-elevation-2 p-2 cursor-pointer hover:bg-surface-sidebar 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 text-ink-gray-7 font-medium">
<p class="text-p-base-medium text-ink-gray-7">
{{ __(emailAccount.email_account_name) }}
</p>
<div class="text-p-sm w-full text-ink-gray-5 mt-1">
@@ -35,7 +35,12 @@
:emailAccount="emailAccount"
@click="emit('update:step', 'email-edit', emailAccount)"
/>
<hr class="mx-2" />
<hr
class="mx-2"
v-if="
emailAccount !== emailAccounts.data[emailAccounts.data.length - 1]
"
/>
</div>
</div>
<!-- fallback if no email accounts -->
@@ -51,11 +56,11 @@
</template>
<script setup lang="ts">
import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
import { EmailAccount } from "@/types";
import { createListResource } from "frappe-ui";
import EmailAccountCard from "./EmailAccountCard.vue";
import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
import { EmailIcon } from "../icons";
import EmailAccountCard from "./EmailAccountCard.vue";
const emit = defineEmits(["update:step"]);
+3 -3
View File
@@ -27,17 +27,17 @@
<!-- email service provider info -->
<div>
<div
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-gray-modals"
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-elevation-2"
>
<CircleAlert
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-2"
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-5"
/>
<div class="text-wrap text-xs text-ink-gray-7">
{{ selectedService.info }}
<a
:href="selectedService.link"
target="_blank"
class="text-ink-blue-2 underline"
class="text-ink-blue-5 underline"
>here</a
>.
</div>
+6 -6
View File
@@ -13,10 +13,10 @@
/>
</div>
<div
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-gray-modals"
class="flex items-center gap-2 rounded-md p-2 ring-1 ring-outline-elevation-2"
>
<CircleAlert
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-2"
class="h-6 w-5 w-min-5 w-max-5 min-h-5 max-w-5 text-ink-blue-5"
/>
<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-2 underline"
class="text-ink-blue-5 underline"
>here</a
>.
</span>
@@ -32,7 +32,7 @@
<a
:href="deskEditUrl"
target="_blank"
class="text-ink-blue-2 underline"
class="text-ink-blue-5 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-2" }),
icon: h(CircleAlert, { class: "text-ink-blue-5" }),
});
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-2" }),
icon: h(CircleAlert, { class: "text-ink-blue-5" }),
});
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="font-semibold text-xl">
<h1 class="text-xl-semibold">
{{ 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 font-medium text-ink-gray-7 relative z-10 pointer-events-none"
class="text-base-medium text-ink-gray-7 relative z-10 pointer-events-none"
>
{{ __(notification.label) }}
</h2>
@@ -26,12 +26,12 @@
{{ __(notification.description) }}
</p>
</div>
<FeatherIcon
name="chevron-right"
class="text-ink-gray-7 size-4 relative z-10 pointer-events-none rtl:rotate-180"
<span
class="lucide-chevron-right text-ink-gray-7 size-4 relative z-10 pointer-events-none rtl:rotate-180"
aria-hidden="true"
/>
<div
class="w-full h-full absolute top-0 start-0 hover:bg-surface-menu-bar rounded-[inherit]"
class="w-full h-full absolute top-0 start-0 hover:bg-surface-sidebar 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-3': selected }"
:class="{ 'ring-2 ring-outline-blue-4': 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-3 select-none">*</span>
<span class="text-ink-red-6 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-3 select-none">*</span>
<span class="text-ink-red-6 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-lg font-semibold text-ink-gray-8">
<h1 class="text-md-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-menu-bar rounded h-12.5"
class="grid grid-cols-11 items-center gap-4 cursor-pointer hover:bg-surface-sidebar 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 text-ink-gray-8 font-medium">
<span class="text-base-medium text-ink-gray-8">
{{ 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-lg font-semibold text-ink-gray-8">
<h1 class="text-md-semibold text-ink-gray-8">
{{ __("General") }}
</h1>
<UnsavedBadge :show="isDirty" />
@@ -38,12 +38,12 @@
<WorkflowKnowledgebaseSettings />
<hr class="my-8" />
<div>
<div class="text-base font-semibold text-ink-gray-9">
<div class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-semibold text-ink-gray-9">
<div class="text-base-semibold text-ink-gray-9">
{{ __("Branding") }}
</div>
<FormControl
@@ -12,10 +12,14 @@
:image="props.image"
:label="props.title"
/>
<FeatherIcon v-else name="image" class="size-6 text-ink-gray-4" />
<span
v-else
class="lucide-image size-6 text-ink-gray-4"
aria-hidden="true"
/>
</div>
<div class="flex flex-col gap-1 max-w-sm items-start">
<span class="text-base font-medium text-ink-gray-8">{{ title }}</span>
<span class="text-base-medium text-ink-gray-8">{{ title }}</span>
<span class="text-p-sm text-ink-gray-6">{{ description }}</span>
</div>
</div>
@@ -57,7 +61,7 @@
</template>
<script setup lang="ts">
import { Avatar, Button, FeatherIcon, FileUploader } from "frappe-ui";
import { Avatar, Button, 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 font-semibold text-ink-gray-9">
<div class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-semibold text-ink-gray-9">
<div class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium text-ink-gray-8">{{
<span class="text-base-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 font-medium mb-2 text-ink-gray-8 ms-2.5">
<div class="text-base-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-orange-700 bg-yellow-100 items-center justify-center rounded hover:bg-yellow-100 select-none m-[1px]"
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="{
'!text-ink-gray-4 !bg-surface-gray-2': isWeekOff(date),
}"
@@ -46,10 +46,10 @@
"
>
<div class="w-[5%]">
<div class="size-3.5 bg-orange-500 rounded-sm mt-1" />
<div class="size-3.5 bg-surface-orange-5 rounded-sm mt-1" />
</div>
<div class="grow">
<div class="text-sm font-semibold">
<div class="text-sm-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-7 text-ink-white hover:!bg-surface-gray-7/80 hover:text-ink-white':
'bg-surface-gray-10 text-ink-base hover:!bg-surface-gray-10/80 hover:text-ink-base':
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 font-medium text-ink-gray-6">
<div class="text-base-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-menu-bar rounded"
class="flex items-center cursor-pointer hover:bg-surface-sidebar rounded"
>
<div
class="w-full ps-2 flex flex-col justify-center h-14"
@click="holidayListActiveScreen = { screen: 'view', data: data }"
>
<div class="text-base text-ink-gray-7 font-medium">{{ data.name }}</div>
<div class="text-base-medium text-ink-gray-7">{{ 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-lg font-semibold text-ink-gray-8">{{
<span class="text-md-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-lg font-semibold text-ink-gray-8">
<div class="text-md-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-lg font-semibold text-ink-gray-8">
<span class="text-md-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-yellow-100 size-4 rounded-sm" />
<span class="bg-surface-yellow-2 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 font-semibold text-xl cursor-pointer select-none"
class="flex items-center gap-2 text-xl-semibold cursor-pointer select-none"
variant="ghost"
@click="togglePopover"
:label="currentYear + ''"
@@ -23,10 +23,10 @@
@click="onYearChange(togglePopover, year)"
>
{{ year }}
<FeatherIcon
name="check"
class="size-4"
<span
class="lucide-check 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 font-semibold text-xl select-none"
class="flex items-center gap-2 px-2 text-xl-semibold select-none"
>
{{ startYear }}
</div>
@@ -85,7 +85,7 @@
:class="[
'size-1.5 rounded-full cursor-pointer',
{
'bg-surface-gray-7': visibleMonths === 'first-half',
'bg-surface-gray-10': 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-7': visibleMonths === 'second-half',
'bg-surface-gray-10': 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-white w-full text-base px-0 focus:!ring-0 border-none hover:bg-surface-white outline-none no-underline focus:!outline-none"
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"
/>
<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-3 select-none" aria-hidden="true">*</span>
<span class="text-ink-red-6 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 font-semibold">
<h2 class="mt-8 text-base-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-lg font-semibold text-ink-gray-8">
<h1 class="text-md-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 font-semibold text-ink-gray-9">
<div class="text-base-semibold text-ink-gray-9">
{{ __("Appearance") }}
</div>
<ThemeSwitcher
@@ -32,7 +32,7 @@
</div>
<hr class="my-8" />
<div>
<div class="text-base font-semibold text-ink-gray-9">
<div class="text-base-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 font-medium text-ink-gray-8">
<span class="text-base-medium text-ink-gray-8">
{{ __("Language") }}
</span>
<span class="text-p-sm text-ink-gray-6">
@@ -12,24 +12,22 @@
<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 font-medium text-ink-gray-8">
<span class="text-base-medium text-ink-gray-8">
{{ __("Timezone") }}
</span>
<span class="text-p-sm text-ink-gray-6">
{{ __("Change timezone of the application.") }}
</span>
</div>
<Autocomplete
<TimezoneControl
:model-value="user.doc?.time_zone"
:options="timezoneOptions"
:placeholder="__('Select Timezone')"
size="sm"
class="w-40"
@update:model-value="updateTimezone"
/>
@@ -38,9 +36,9 @@
</template>
<script setup lang="ts">
import { ref } from "vue";
import { createResource } from "frappe-ui";
import TimezoneControl from "@/components/TimezoneControl.vue";
import { __ } from "@/translation";
import { Link } from "frappe-ui/frappe";
const props = defineProps<{ user: any }>();
@@ -49,20 +47,8 @@ function updateLanguage(value: string | null) {
props.user.doc.language = value || props.user.originalDoc?.language;
}
function updateTimezone(value: { label: string; value: string } | null) {
function updateTimezone(value: string | null) {
if (!props.user.doc) return;
props.user.doc.time_zone = value?.value || props.user.originalDoc?.time_zone;
props.user.doc.time_zone = 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,64 +18,113 @@
<div
v-for="option in themeOptions"
:key="option.value"
class="flex-1 rounded-lg border cursor-pointer min-h-[42px]"
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="
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"
>
<div :class="{ flex: option.panes.length > 1 }">
<!-- 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
v-for="(pane, index) in option.panes"
v-for="(frame, index) in option.frames"
:key="index"
:class="pane.containerClass"
:class="frame.frameClass"
>
<div :class="pane.screenClass">
<div
class="flex gap-[3px] py-[3px] px-1 border-b"
:class="
pane.tone === 'light'
? 'border-gray-100'
: 'border-gray-800'
"
<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 }"
>
<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>
<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="flex items-start justify-between gap-2 p-2.5 pr-0 pb-1 min-h-[41px]"
class="absolute flex items-center gap-1 text-xs text-ink-gray-5 font-semibold top-[24px] left-[10px]"
>
<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>
<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>
</div>
</div>
@@ -105,10 +154,13 @@ import { __ } from "@/translation";
import { useTheme, type Theme } from "frappe-ui";
import { computed, type Component } from "vue";
type Pane = {
tone: "light" | "dark";
containerClass: string;
screenClass: string;
type Tone = "light" | "dark";
type Frame = {
tone: Tone;
/** Stretch to fill the frame (light/dark) or natural width (system). */
fill: boolean;
frameClass: string;
};
const props = withDefaults(
@@ -127,21 +179,27 @@ 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,
panes: [
frames: [
{
tone: "light",
containerClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
screenClass: "bg-white rounded-tl-sm",
fill: true,
frameClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
},
],
},
@@ -149,11 +207,11 @@ const themeOptions: {
value: "dark",
label: "Dark",
bars: true,
panes: [
frames: [
{
tone: "dark",
containerClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
screenClass: "bg-gray-900 rounded-tl-sm",
fill: true,
frameClass: "pl-5 pt-3.5 bg-surface-gray-2 rounded-t-[10.5px]",
},
],
},
@@ -161,18 +219,18 @@ const themeOptions: {
value: "system",
label: "System",
bars: false,
panes: [
frames: [
{
tone: "light",
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",
fill: false,
frameClass:
"flex flex-1 overflow-hidden pl-5 pt-3.5 bg-surface-gray-2 rounded-tl-[10.5px] max-h-[78px]",
},
{
tone: "dark",
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",
fill: false,
frameClass:
"flex flex-1 overflow-hidden pl-5 pt-3.5 bg-surface-gray-3 rounded-tr-[10.5px] max-h-[78px]",
},
],
},
@@ -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-white 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-base 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"
>
<FeatherIcon
name="x"
class="size-3.5 cursor-pointer text-ink-gray-4"
<span
class="lucide-x size-3.5 cursor-pointer text-ink-gray-4"
aria-hidden="true"
/>
</div>
</Tooltip>
<div
v-if="uploading"
class="w-full h-full top-0 left-0 absolute bg-surface-gray-7 bg-opacity-20 rounded-full flex items-center justify-center"
class="w-full h-full top-0 left-0 absolute bg-surface-gray-10 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-lg sm:text-xl !font-semibold text-ink-gray-8"
class="text-md 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 font-semibold text-ink-gray-9">
<span class="text-base-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 font-medium text-ink-gray-8">
<span class="text-base-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 font-medium text-ink-gray-8">
<span class="text-base-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 font-medium text-ink-gray-8">
<span class="text-base-medium text-ink-gray-8">
{{ __("Password") }}
</span>
<span class="text-p-sm text-ink-gray-6">{{
@@ -18,25 +18,42 @@
<template #content>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-1">
<span class="text-base font-medium text-ink-gray-8">
<span class="text-base-medium text-ink-gray-8">
{{ __("Signature") }}
</span>
<span class="text-p-sm text-ink-gray-6">
{{ __("Manage your email signature.") }}
</span>
</div>
<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"
<Editor
:model-value="user?.doc?.email_signature"
:placeholder="__('Write your email signature here.')"
:bubbleMenu="true"
:fixed-menu="true"
:extensions="extensions"
:upload-function="(file) => uploadFunction(file)"
@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 font-medium text-ink-gray-8">
<span class="text-base-medium text-ink-gray-8">
{{ __("Emails") }}
</span>
<span class="text-p-sm text-ink-gray-6">
@@ -50,10 +67,10 @@
<div>
<div
v-if="user.doc.user_emails?.length"
class="w-full border rounded-md mb-2 border-outline-gray-modals"
class="w-full border rounded-md mb-2 border-outline-elevation-2"
>
<div
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"
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"
>
<span>{{ __("Email Account") }}</span>
<span>{{ __("Email") }}</span>
@@ -62,7 +79,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-gray-modals 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-elevation-2 last:border-b-0"
>
<span class="text-ink-gray-8 font-medium truncate">
{{ e.email_account }}
@@ -86,7 +103,7 @@
>
<template #target="{ togglePopover }">
<Button
class="!bg-surface-modal"
class="!bg-surface-elevation-2"
variant="outline"
:label="__('Add Email')"
iconLeft="lucide-plus"
@@ -129,10 +146,26 @@ import SettingsLayoutBase from "@/components/layouts/SettingsLayoutBase.vue";
import { getUserEmailInfo } from "@/composables/useUserEmailInfo";
import { useAuthStore } from "@/stores/auth";
import { __ } from "@/translation";
import { normalize } from "@/utils";
import { Button, createDocumentResource, TextEditor, toast } from "frappe-ui";
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 { 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-3'
: 'text-ink-red-3'
? 'text-ink-green-6'
: 'text-ink-red-6'
"
>
{{ confirmPasswordMessage }}
@@ -25,7 +25,7 @@
@update:model-value="savedRepliesSearchQuery = $event"
:placeholder="__('Search')"
type="text"
class="bg-surface-white hover:bg-surface-white focus:ring-0 border-outline-gray-2"
class="bg-surface-base hover:bg-surface-base focus:ring-0 border-outline-gray-2"
:debounce="300"
>
<template #prefix>
@@ -47,9 +47,12 @@
class="flex items-center justify-between w-fit p-4"
>
<template #suffix>
<FeatherIcon
:name="open ? 'chevron-up' : 'chevron-down'"
class="h-4"
<span
:class="[
'h-4',
open ? 'lucide-chevron-up' : 'lucide-chevron-down',
]"
aria-hidden="true"
/>
</template>
</Button>
@@ -63,10 +66,10 @@
<span class="whitespace-nowrap">
{{ item.label }}
</span>
<FeatherIcon
<span
v-if="activeFilter === item.value"
name="check"
class="size-4 text-ink-gray-7"
class="lucide-check size-4 text-ink-gray-7"
aria-hidden="true"
/>
</div>
</button>
@@ -111,7 +114,7 @@
:key="savedReply.name"
>
<div
class="grid grid-cols-12 items-center gap-4 cursor-pointer hover:bg-surface-menu-bar rounded"
class="grid grid-cols-12 items-center gap-4 cursor-pointer hover:bg-surface-sidebar rounded"
>
<div
@click="
@@ -122,9 +125,7 @@
"
class="w-full px-2 flex flex-col justify-center h-12.5 col-span-7 min-w-0"
>
<div
class="text-base text-ink-gray-7 font-medium w-full truncate"
>
<div class="text-base-medium text-ink-gray-7 w-full truncate">
{{ savedReply.title }}
</div>
</div>
@@ -206,7 +207,6 @@ 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-white 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-base p-1 text-base shadow-lg pointer-events-auto hide-scrollbar"
>
<button
v-for="(item, index) in props.items"
@@ -25,16 +25,21 @@
<div class="space-y-1.5">
<FormLabel :label="__('Preview')" />
<div class="relative">
<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"
<Editor
:model-value="dialogModel.preview"
:editable="false"
class="pointer-events-none"
/>
: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>
<div
v-if="getResponsePreviewResource.loading"
class="absolute top-0 end-0 flex items-center justify-center size-full rounded-md bg-surface-gray-7/20"
class="absolute top-0 end-0 flex items-center justify-center size-full rounded-md bg-surface-gray-10/20"
>
<LoadingIndicator class="size-4" />
</div>
@@ -51,15 +56,16 @@ import {
createResource,
Dialog,
FormLabel,
TextEditor,
toast,
} from "frappe-ui";
import { LoadingIndicator } from "frappe-ui";
import { menuButtons } from "../savedReplies";
import { Editor, EditorContent, RichTextKit } from "frappe-ui/editor";
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-lg font-semibold text-ink-gray-8">
<h1 class="text-md-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-menu-bar overflow-y-auto hide-scrollbar"
class="flex-col rounded-l-lg w-56 shrink-0 ps-1 py-1 bg-surface-sidebar overflow-y-auto hide-scrollbar"
>
<h1
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"
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"
>
{{ __("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 font-medium text-ink-gray-5 transition-all duration-300 ease-in-out sticky top-0 z-10 bg-surface-menu-bar"
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"
>
<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-selected shadow-sm'
? 'bg-surface-elevation-3 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-modal max-w-[816px] overflow-hidden relative"
class="flex flex-1 flex-col bg-surface-elevation-2 max-w-[816px] overflow-hidden relative"
>
<component
:is="activeTab.component"
@@ -27,7 +27,7 @@
</div>
</template>
<template #body>
<div class="absolute bg-surface-white top-2">
<div class="absolute bg-surface-base top-2">
<DurationPicker
v-model="priorityData.response_time"
:options="{ seconds: false }"
@@ -51,7 +51,7 @@
</div>
</template>
<template #body>
<div class="absolute bg-surface-white top-2">
<div class="absolute bg-surface-base top-2">
<DurationPicker
v-model="priorityData.resolution_time"
:options="{ seconds: false }"
@@ -44,7 +44,7 @@
value: 'Sunday',
},
]"
:class="{ 'border-outline-red-3': errors.workday }"
:class="{ 'border-outline-red-4': 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-3': errors.start_time }"
:class="{ 'border-outline-red-4': 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-3': errors.end_time }"
:class="{ 'border-outline-red-4': 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(['', '', ''])"
>
<FeatherIcon name="plus" class="h-4" />
<span class="lucide-plus h-4" aria-hidden="true" />
{{ __("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, FeatherIcon } from "frappe-ui";
import { Button, Dropdown, ErrorMessage } 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-lg font-semibold text-ink-gray-8">
<div class="text-md-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-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
class="my-2 min-w-40 rounded-lg bg-surface-elevation-2 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-gray-modals pt-1.5 p-1"
class="mt-1.5 flex flex-col gap-1 border-t border-outline-elevation-2 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-lg font-semibold text-ink-gray-8">
<h1 class="text-md-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 font-medium text-ink-gray-6">
<div class="text-base-medium text-ink-gray-6">
{{ __("No SLA found") }}
</div>
<div class="text-p-sm text-ink-gray-5 max-w-60 text-center">
@@ -1,14 +1,12 @@
<template>
<div
class="grid grid-cols-6 items-center gap-4 cursor-pointer hover:bg-surface-menu-bar rounded"
class="grid grid-cols-6 items-center gap-4 cursor-pointer hover:bg-surface-sidebar 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 text-ink-gray-7 font-medium flex items-center gap-2"
>
<div class="text-base-medium text-ink-gray-7 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 text-ink-gray-7 font-medium">
<span class="text-sm-medium text-ink-gray-7">
{{ __("Enabled") }}
</span>
</div>
@@ -62,7 +62,7 @@
<hr class="my-8" />
<div>
<div class="flex flex-col gap-1">
<span class="text-lg font-semibold text-ink-gray-8">{{
<span class="text-md-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 font-medium"
class="text-ink-gray-6 text-base-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") }}
<FeatherIcon name="info" class="size-4" />
<span class="lucide-info size-4" aria-hidden="true" />
</div>
</template>
<template #body-main>
<div
class="text-sm text-ink-gray-6 p-2 bg-surface-white rounded-md max-w-96 text-wrap whitespace-pre-wrap leading-5"
class="text-sm text-ink-gray-6 p-2 bg-surface-base 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-lg font-semibold text-ink-gray-8">
<span class="text-md-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-lg font-semibold text-ink-gray-8">
<span class="text-md-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 font-medium">
<div class="select-none text-ink-gray-6 text-sm-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 font-medium">
<div class="select-none text-ink-gray-6 text-sm-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-lg font-semibold text-ink-gray-8">
<span class="text-md-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-3">*</span>
<span v-if="column.isRequired" class="text-ink-red-6">*</span>
</div>
</div>
<hr v-if="slaData.priorities?.length !== 0" />
@@ -31,7 +31,7 @@
</div>
</template>
<template #body>
<div class="absolute bg-surface-white top-2">
<div class="absolute bg-surface-base 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-3">*</span>
<span v-if="column.isRequired" class="text-ink-red-6">*</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 text-ink-gray-7 font-medium">
<span class="text-sm-medium text-ink-gray-7">
{{ __("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 text-ink-gray-7 font-medium">
<span class="text-sm-medium text-ink-gray-7">
{{ __("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 font-medium text-ink-gray-6">
<div class="text-base-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-menu-bar rounded h-12.5"
class="flex items-center cursor-pointer hover:bg-surface-sidebar 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 text-ink-gray-7 font-medium">
<p class="text-base-medium text-ink-gray-7">
{{ team.name }}
</p>
<Badge :label="__('Disabled')" v-if="team.disabled" />
@@ -16,7 +16,11 @@
<UserAvatar :name="value" size="xs" />
</template>
<template #suffix>
<FeatherIcon class="h-3.5" name="x" @click.stop="removeValue(value)" />
<span
class="lucide-x h-3.5"
aria-hidden="true"
@click.stop="removeValue(value)"
/>
</template>
</Button>
<div class="flex-1">
@@ -45,7 +49,7 @@
</ComboboxAnchor>
<ComboboxPortal>
<ComboboxContent
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"
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"
position="popper"
:align="'start'"
@openAutoFocus.prevent
@@ -65,7 +69,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 font-medium">
<div class="text-base-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-lg font-semibold text-ink-gray-8">
<h1 class="text-md-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-menu-bar rounded px-2"
class="flex items-center justify-between gap-8 py-3 hover:bg-surface-sidebar rounded px-2"
>
<div class="flex flex-col">
<div class="text-p-base font-medium text-ink-gray-7 truncate">
<div class="text-p-base-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-gray-modals" />
<div class="h-px border-t mx-2 border-outline-elevation-2" />
<div
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-menu-bar px-2"
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-sidebar px-2"
@click="emit('updateStep', 'twilio-settings')"
>
<div class="flex flex-col">
<div class="text-p-base font-medium text-ink-gray-7 truncate">
<div class="text-p-base-medium text-ink-gray-7 truncate">
{{ __("Twilio") }}
</div>
<div class="text-p-sm text-ink-gray-5 truncate">
@@ -79,20 +79,20 @@
}}
</div>
</div>
<FeatherIcon
name="chevron-right"
class="size-4 text-ink-gray-5 rtl:rotate-180"
<span
class="lucide-chevron-right size-4 text-ink-gray-5 rtl:rotate-180"
aria-hidden="true"
/>
</div>
<div class="h-px border-t mx-2 border-outline-gray-modals" />
<div class="h-px border-t mx-2 border-outline-elevation-2" />
<div
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-menu-bar px-2"
class="flex items-center justify-between py-3 cursor-pointer rounded hover:bg-surface-sidebar px-2"
@click="emit('updateStep', 'exotel-settings')"
>
<div class="flex flex-col">
<div class="text-p-base font-medium text-ink-gray-7 truncate">
<div class="text-p-base-medium text-ink-gray-7 truncate">
{{ __("Exotel") }}
</div>
<div class="text-p-sm text-ink-gray-5 truncate">
@@ -103,9 +103,9 @@
}}
</div>
</div>
<FeatherIcon
name="chevron-right"
class="size-4 text-ink-gray-5 rtl:rotate-180"
<span
class="lucide-chevron-right size-4 text-ink-gray-5 rtl:rotate-180"
aria-hidden="true"
/>
</div>
</div>
+1 -1
View File
@@ -67,7 +67,7 @@ const props = withDefaults(defineProps<P>(), {
isActive: false,
onClick: () => () => true,
to: "",
bgColor: "bg-surface-selected",
bgColor: "bg-surface-elevation-3",
hvColor: "hover:bg-surface-gray-2",
});
const router = useRouter();
+2 -2
View File
@@ -103,12 +103,12 @@
v-if="showVariant('empty-state')"
:style="{
backgroundImage:
'radial-gradient(ellipse at center, var(--surface-white) 10%, color-mix(in srgb, var(--surface-white) 90%, transparent) 25%, transparent 70%)',
'radial-gradient(ellipse at center, var(--surface-base) 10%, color-mix(in srgb, var(--surface-base) 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 font-medium text-center text-p-base leading-[1.15]"
class="relative z-10 text-ink-gray-7 text-center text-p-base-medium leading-[1.15]"
>
{{ __(getEmptyState(i).title) }}
</div>
+4 -4
View File
@@ -3,12 +3,12 @@
<svg
v-for="i in [1, 0.8, 0.6, 0.4, 0.2]"
:key="i"
class="-all peer fill-gray-300"
class="-all peer fill-ink-gray-3"
:class="{
['fill-yellow-500']: i <= rating,
['fill-ink-yellow-5']: i <= rating,
'cursor-pointer': !static,
'hover:fill-yellow-200': i > rating && !static,
'peer-hover:fill-yellow-200': i > rating && !static,
'hover:fill-ink-yellow-2': i > rating && !static,
'peer-hover:fill-ink-yellow-2': i > rating && !static,
}"
height="16px"
width="16px"
+44 -46
View File
@@ -1,18 +1,15 @@
<template>
<div class="rounded p-3 shadow w-full">
<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)"
<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)"
>
<template #top>
<template #default="{ editor }">
<span class="text-base">
<span class="flex items-center justify-between">
<UserAvatar
@@ -25,18 +22,24 @@
</span>
<slot name="top-bottom" />
</span>
</template>
<template #bottom>
<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" />
<div class="flex flex-col gap-2">
<slot name="bottom-top" />
<div
class="flex flex-col space-y-1.5 overflow-auto sm:flex-row sm:justify-between"
class="flex flex-col space-y-1.5 sm:flex-row sm:items-center sm:justify-between sm:gap-2"
>
<div class="flex items-center">
<div class="flex items-center min-w-0 overflow-x-auto max-w-[70%]">
<slot name="bottom-left" />
<TextEditorFixedMenu :buttons="fixedMenu" />
<EditorFixedMenu :editor="editor" :items="textEditorMenuItems" />
</div>
<div class="flex items-center gap-2">
<div class="flex items-center gap-2 shrink-0">
<Button
:label="__('Discard')"
theme="gray"
@@ -54,63 +57,58 @@
</div>
</div>
</template>
</FTextEditor>
</Editor>
</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 { ClearFormattingUtility, getFontFamily, isContentEmpty } from "@/utils";
import { TextEditor as FTextEditor, TextEditorFixedMenu } from "frappe-ui";
import { computed, nextTick, ref } from "vue";
import { getFontFamily, isContentEmpty, uploadFunction } from "@/utils";
import {
Editor,
EditorBubbleMenu,
EditorContent,
EditorFixedMenu,
RichTextKit,
articleToolbar,
} from "frappe-ui/editor";
import { computed, ref } from "vue";
interface P {
modelValue: string;
placeholder?: string;
autofocus?: boolean;
}
interface E {
(event: "clear"): void;
(event: "update:modelValue", any): string;
(event: "update:modelValue", value: string): void;
}
const props = withDefaults(defineProps<P>(), {
withDefaults(defineProps<P>(), {
autofocus: false,
});
defineEmits<E>();
const e = ref(null);
const editor = computed(() => e.value.editor);
const authStore = useAuthStore();
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,
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,
];
defineExpose({
editor,
});
if (props.autofocus) {
nextTick(() => e.value.editor.commands.focus());
}
</script>
+3 -3
View File
@@ -4,7 +4,7 @@
<Tooltip :text="__(field.label)">
<span>{{ __(field.label) }}</span>
</Tooltip>
<span v-if="field.required" class="text-ink-red-3"> * </span>
<span v-if="field.required" class="text-ink-red-6"> * </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-white);
background: var(--surface-base);
}
:deep(.form-control button) {
@@ -175,7 +175,7 @@ function emitUpdate(fieldname: Field["fieldname"], value: FieldValue) {
}
:deep(.form-control button svg) {
color: var(--ink-white);
color: var(--ink-base);
width: 0;
}
</style>
+4 -4
View File
@@ -1,17 +1,17 @@
<template>
<Autocomplete
<Combobox
:label="label ? __(label) : undefined"
:options="options"
:placeholder="__('Select timezone')"
:loading="timezoneResource.loading"
:model-value="modelValue"
@update:model-value="(v: { label: string; value: string } | string) => (modelValue = typeof v === 'string' ? v : v.value)"
v-model="modelValue"
class=""
/>
</template>
<script setup lang="ts">
import { __ } from "@/translation";
import { Autocomplete, createResource } from "frappe-ui";
import { Combobox, createResource } from "frappe-ui";
import { computed } from "vue";
const props = defineProps<{
+1 -1
View File
@@ -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-3">
<span v-if="field.required" class="place-self-center text-ink-red-6">
*
</span>
</span>
+1 -1
View File
@@ -8,7 +8,7 @@
/>
<span
v-if="expand"
class="truncate capitalize text-base text-ink-gray-9 font-medium"
class="truncate capitalize text-base-medium text-ink-gray-9"
:class="{
'font-medium': strong,
}"
+4 -7
View File
@@ -7,7 +7,7 @@
!sidebarStore.isExpanded
? 'w-auto px-0'
: open
? 'w-full px-2 bg-surface-white shadow-sm'
? 'w-full px-2 bg-surface-base shadow-sm'
: 'w-full px-2 hover:bg-surface-gray-3'
"
>
@@ -20,9 +20,7 @@
: 'ms-2 w-auto opacity-100'
"
>
<div
class="text-base font-medium leading-none text-ink-gray-9 truncate"
>
<div class="text-base-medium leading-none text-ink-gray-9 truncate">
{{ config.brandName || "Helpdesk" }}
</div>
<div class="mt-1 text-sm text-ink-gray-7">
@@ -37,9 +35,8 @@
: 'ms-2 relative start-[-2.5px] w-auto opacity-100'
"
>
<FeatherIcon
name="chevron-down"
class="h-4 w-4 text-ink-gray-5"
<span
class="lucide-chevron-down h-4 w-4 text-ink-gray-5"
aria-hidden="true"
/>
</div>
+24 -23
View File
@@ -2,31 +2,32 @@
<div class="flex items-center">
<router-link
:to="{ name: routeName }"
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"
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"
>
{{ 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
variant="ghost"
class="text-lg font-medium text-nowrap truncate max-w-[200px] sm:max-w-none"
:label="currentView.label"
<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"
>
<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>
<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>
<template #item-prefix="{ item }">
@@ -54,10 +55,10 @@
v-if="item.name"
class="flex items-center justify-end gap-2 min-w-11"
>
<FeatherIcon
<span
v-if="isCurrentView(item)"
name="check"
class="size-4 text-ink-gray-7"
class="lucide-check size-4 text-ink-gray-7"
aria-hidden="true"
/>
<Dropdown align="end" :options="dropdownActions(item)">
<template #default="{ open }">
@@ -79,7 +80,7 @@
<script setup>
import Icon from "@/components/Icon.vue";
import { useScreenSize } from "@/composables/screen";
import { Badge, Dropdown, FeatherIcon } from "frappe-ui";
import { Badge, Dropdown } from "frappe-ui";
import { useRoute } from "vue-router";
const props = defineProps({
@@ -138,6 +139,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-modal;
@apply sticky -top-[6px] z-10 bg-surface-elevation-2;
}
</style>
@@ -1,6 +1,6 @@
<template>
<div
class="flex w-full min-w-0 items-center rounded p-2 text-base font-medium text-ink-gray-8"
class="flex w-full min-w-0 items-center rounded p-2 text-base-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 font-medium text-ink-gray-8"
class="flex w-full min-w-0 items-center rounded p-2 text-base-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-white)] transition hover:z-10 hover:scale-110"
class="-mr-1.5 cursor-pointer ring-2 ring-[var(--surface-base)] 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="h-7.5 w-7.5 text-ink-gray-4" />
<LucideStar class="size-7.5 text-ink-gray-4" />
<div class="flex flex-col gap-1 max-w-[282px] m-auto">
<p class="text-base font-medium text-ink-gray-7">
<p class="text-base-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-2 stroke-ink-amber-2" />
<LucideStar class="size-4 fill-ink-amber-6 stroke-ink-amber-6" />
</template>
</Button>
</Tooltip>

Some files were not shown because too many files have changed in this diff Show More