mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 11:37:58 +00:00
feat: better tool card
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { dynamicTime } from 'shared/helpers/timeHelper';
|
||||
|
||||
import CardLayout from 'dashboard/components-next/CardLayout.vue';
|
||||
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import Policy from 'dashboard/components/policy.vue';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
authType: {
|
||||
type: String,
|
||||
default: 'none',
|
||||
},
|
||||
paramSchema: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
updatedAt: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['action']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const [showActionsDropdown, toggleDropdown] = useToggle();
|
||||
|
||||
const menuItems = computed(() => [
|
||||
{
|
||||
label: t('CAPTAIN.CUSTOM_TOOLS.OPTIONS.EDIT_TOOL'),
|
||||
value: 'edit',
|
||||
action: 'edit',
|
||||
icon: 'i-lucide-pencil-line',
|
||||
},
|
||||
{
|
||||
label: t('CAPTAIN.CUSTOM_TOOLS.OPTIONS.DELETE_TOOL'),
|
||||
value: 'delete',
|
||||
action: 'delete',
|
||||
icon: 'i-lucide-trash',
|
||||
},
|
||||
]);
|
||||
|
||||
const timestamp = computed(() =>
|
||||
dynamicTime(props.updatedAt || props.createdAt)
|
||||
);
|
||||
|
||||
const handleAction = ({ action, value }) => {
|
||||
toggleDropdown(false);
|
||||
emit('action', { action, value, id: props.id });
|
||||
};
|
||||
|
||||
const authTypeLabel = computed(() => {
|
||||
return t(
|
||||
`CAPTAIN.CUSTOM_TOOLS.FORM.AUTH_TYPES.${props.authType.toUpperCase()}`
|
||||
);
|
||||
});
|
||||
|
||||
const parameterNames = computed(() => {
|
||||
if (!props.paramSchema || props.paramSchema.length === 0) return '';
|
||||
return props.paramSchema.map(p => p.name).join(', ');
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardLayout class="relative">
|
||||
<div class="flex relative justify-between w-full gap-1">
|
||||
<span class="text-base text-n-slate-12 line-clamp-1 font-medium">
|
||||
{{ title }}
|
||||
</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<Policy
|
||||
v-on-clickaway="() => toggleDropdown(false)"
|
||||
:permissions="['administrator']"
|
||||
class="relative flex items-center group"
|
||||
>
|
||||
<Button
|
||||
icon="i-lucide-ellipsis-vertical"
|
||||
color="slate"
|
||||
size="xs"
|
||||
class="rounded-md group-hover:bg-n-alpha-2"
|
||||
@click="toggleDropdown()"
|
||||
/>
|
||||
<DropdownMenu
|
||||
v-if="showActionsDropdown"
|
||||
:menu-items="menuItems"
|
||||
class="mt-1 ltr:right-0 rtl:right-0 top-full"
|
||||
@action="handleAction($event)"
|
||||
/>
|
||||
</Policy>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between w-full gap-4">
|
||||
<div class="flex items-center gap-3 flex-1">
|
||||
<span
|
||||
v-if="description"
|
||||
class="text-sm truncate text-n-slate-11 flex-1"
|
||||
>
|
||||
{{ description }}
|
||||
</span>
|
||||
<span
|
||||
v-if="authType !== 'none'"
|
||||
class="text-sm shrink-0 text-n-slate-11 inline-flex items-center gap-1"
|
||||
>
|
||||
<i class="i-lucide-lock text-base" />
|
||||
{{ authTypeLabel }}
|
||||
</span>
|
||||
<span
|
||||
v-if="paramSchema.length > 0"
|
||||
class="text-sm shrink-0 text-n-slate-11 inline-flex items-center gap-1"
|
||||
:title="parameterNames"
|
||||
>
|
||||
<i class="i-lucide-braces text-base" />
|
||||
{{ parameterNames }}
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-sm text-n-slate-11 line-clamp-1 shrink-0">
|
||||
{{ timestamp }}
|
||||
</span>
|
||||
</div>
|
||||
</CardLayout>
|
||||
</template>
|
||||
@@ -762,6 +762,10 @@
|
||||
}
|
||||
},
|
||||
"FORM_DESCRIPTION": "Configure your custom tool to connect with external APIs",
|
||||
"OPTIONS": {
|
||||
"EDIT_TOOL": "Edit tool",
|
||||
"DELETE_TOOL": "Delete tool"
|
||||
},
|
||||
"CREATE": {
|
||||
"TITLE": "Create Custom Tool",
|
||||
"SUCCESS_MESSAGE": "Custom tool created successfully",
|
||||
|
||||
@@ -7,6 +7,7 @@ import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
|
||||
import CaptainPaywall from 'dashboard/components-next/captain/pageComponents/Paywall.vue';
|
||||
import CustomToolsPageEmptyState from 'dashboard/components-next/captain/pageComponents/emptyStates/CustomToolsPageEmptyState.vue';
|
||||
import CreateCustomToolDialog from 'dashboard/components-next/captain/pageComponents/customTool/CreateCustomToolDialog.vue';
|
||||
import CustomToolCard from 'dashboard/components-next/captain/pageComponents/customTool/CustomToolCard.vue';
|
||||
|
||||
const store = useStore();
|
||||
|
||||
@@ -27,6 +28,12 @@ const openCreateDialog = () => {
|
||||
createDialogRef.value.dialogRef.open();
|
||||
};
|
||||
|
||||
const handleAction = ({ action, id }) => {
|
||||
// TODO: Implement edit and delete actions
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Action:', action, 'ID:', id);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchCustomTools();
|
||||
});
|
||||
@@ -56,31 +63,21 @@ onMounted(() => {
|
||||
|
||||
<template #body>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div
|
||||
<CustomToolCard
|
||||
v-for="tool in customTools"
|
||||
:id="tool.id"
|
||||
:key="tool.id"
|
||||
class="border border-slate-100 dark:border-slate-800 rounded-lg p-4"
|
||||
>
|
||||
<h3 class="text-base font-medium text-slate-900 dark:text-slate-100">
|
||||
{{ tool.title }}
|
||||
</h3>
|
||||
<p
|
||||
v-if="tool.description"
|
||||
class="text-sm text-slate-600 dark:text-slate-400 mt-1"
|
||||
>
|
||||
{{ tool.description }}
|
||||
</p>
|
||||
<div class="flex items-center gap-2 mt-2">
|
||||
<span
|
||||
class="text-xs px-2 py-1 rounded bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300"
|
||||
>
|
||||
{{ tool.http_method }}
|
||||
</span>
|
||||
<span class="text-xs text-slate-500 dark:text-slate-400 truncate">
|
||||
{{ tool.endpoint_url }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
:title="tool.title"
|
||||
:description="tool.description"
|
||||
:endpoint-url="tool.endpoint_url"
|
||||
:http-method="tool.http_method"
|
||||
:auth-type="tool.auth_type"
|
||||
:param-schema="tool.param_schema"
|
||||
:enabled="tool.enabled"
|
||||
:created-at="tool.created_at"
|
||||
:updated-at="tool.updated_at"
|
||||
@action="handleAction"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</PageLayout>
|
||||
|
||||
Reference in New Issue
Block a user