Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions resources/js/admin/logs/components/Logs/BaseTable/BaseTable.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<template>
<div
class="
tw-flex-1 tw-overflow-auto tw-rounded-xl tw-border tw-border-gray-200
tw-shadow-md tw-shadow-zinc-200
tw-flex-1 tw-overflow-auto tw-rounded-xl tw-border tw-border-gray-200 tw-shadow-zinc-200
"
>
<table class="tw-w-full tw-text-left tw-text-sm">
Expand All @@ -28,7 +27,14 @@
:key="column.key"
class="tw-px-6 tw-py-4 tw-text-gray-600 tw-border-b tw-border-gray-200 tw-whitespace-nowrap"
>
{{ getItemValue(item, column) }}
<slot
:name="`cell-${column.key}`"
:value="getRawValue(item, column)"
:item="item"
:column="column"
>
{{ getItemValue(item, column) }}
</slot>
</td>
</tr>
</tbody>
Expand All @@ -43,11 +49,14 @@ export default {
data: { type: Array, required: true },
},
methods: {
getItemValue(item, column) {
// Get value - handle dot-separated keys for nested properties
const value = column.key.includes(".")
getRawValue(item, column) {
// Get raw value - handle dot-separated keys for nested properties
return column.key.includes(".")
? column.key.split(".").reduce((val, part) => (val == null ? undefined : val[part]), item)
: item[column.key];
},
getItemValue(item, column) {
const value = this.getRawValue(item, column);

// Apply format function if provided
if (typeof column.format === "function") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<div class="tw-flex tw-gap-6">
<sidebar />
<div class="tw-flex tw-gap-3 tw-px-4 tw-h-full tw-min-h-0 tw-overflow-hidden">
<sidebar class="tw-shrink-0" />

<section class="tw-flex-1 tw-overflow-hidden">
<div class="tw-flex tw-flex-col tw-rounded-xl tw-border tw-border-zinc-200 tw-p-4 tw-bg-white tw-h-screen">
<section class="tw-flex-1 tw-min-w-0 tw-min-h-0 tw-overflow-hidden">
<div class="tw-flex tw-flex-col tw-rounded-xl tw-border tw-border-zinc-200 tw-p-4 tw-bg-white tw-h-full">
<header-bar
v-model="search"
@search="onHandleSearch"
Expand Down
121 changes: 118 additions & 3 deletions resources/js/admin/logs/components/Logs/LogTable/LogTable.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,101 @@
<template>
<div class="tw-flex tw-flex-col tw-flex-1 tw-min-h-0">
<!-- Loading spinner -->
<div
v-if="loading"
class="tw-flex tw-flex-1 tw-items-center tw-justify-center tw-min-h-[200px]"
>
<div class="tw-flex tw-flex-col tw-items-center tw-gap-3">
<svg
class="tw-animate-spin tw-h-8 tw-w-8 tw-text-blue-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="tw-opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="tw-opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
<span class="tw-text-sm tw-text-gray-500">{{ $t('Loading...') }}</span>
</div>
</div>

<!-- Table content -->
<base-table
v-else
:columns="columns"
:data="data"
/>
>
<!-- Custom case_number cell with link to case -->
<template #cell-case_number="{ value }">
<a
v-if="value"
:href="`/cases/${value}`"
class="tw-text-blue-600 hover:tw-text-blue-800 hover:tw-underline tw-font-medium"
>
#{{ value }}
</a>
<span v-else class="tw-text-gray-400">-</span>
</template>

<!-- Custom status cell with colored badge -->
<template #cell-status="{ value }">
<span
class="tw-inline-flex tw-items-center tw-px-2.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium"
:class="getStatusClasses(value)"
>
{{ formatStatus(value) }}
</span>
</template>

<!-- Custom tokens_used cell with hover popover -->
<template #cell-tokens_used="{ value, item }">
<div class="tw-relative tw-group tw-inline-block">
<span class="tw-cursor-help tw-border-b tw-border-dotted tw-border-gray-400">
{{ value }}
</span>
<!-- Popover tooltip -->
<div
class="
tw-absolute tw-z-50 tw-bottom-full tw-left-1/2 tw--translate-x-1/2 tw-mb-2
tw-hidden group-hover:tw-block
tw-bg-gray-900 tw-text-white tw-text-xs tw-rounded-lg tw-py-2 tw-px-3
tw-whitespace-nowrap tw-shadow-lg
"
>
<div class="tw-flex tw-flex-col tw-gap-1">
<div class="tw-flex tw-justify-between tw-gap-4">
<span class="tw-text-gray-400">{{ $t('Input') }}:</span>
<span class="tw-font-medium">{{ item.input_tokens }}</span>
</div>
<div class="tw-flex tw-justify-between tw-gap-4">
<span class="tw-text-gray-400">{{ $t('Output') }}:</span>
<span class="tw-font-medium">{{ item.output_tokens }}</span>
</div>
</div>
<!-- Arrow -->
<div
class="
tw-absolute tw-top-full tw-left-1/2 tw--translate-x-1/2
tw-border-4 tw-border-transparent tw-border-t-gray-900
"
/>
</div>
</div>
</template>
</base-table>
<pagination
v-if="!loading"
class="tw-mt-3 tw-shrink-0"
:page="page"
:total-pages="totalPages"
Expand Down Expand Up @@ -39,6 +130,7 @@ export default {
page: 1,
totalPages: 1,
perPage: 15,
loading: false,
};
},
computed: {
Expand Down Expand Up @@ -83,19 +175,23 @@ export default {
design: [
{ key: 'flow_genie_name', label: this.$t('FlowGenie Name') },
{ key: 'user_name', label: this.$t('User') },
{ key: 'model', label: this.$t('Model') },
{ key: 'status', label: this.$t('Status') },
{ key: 'duration', label: this.$t('Execution Time') },
{ key: 'llm_calls', label: this.$t('LLM Calls') },
{ key: 'tools', label: this.$t('Tools') },
{ key: 'tokens_used', label: this.$t('Tokens Used') },
{ key: 'created_at', label: this.$t('Date'), format: dateFormatter },
],
execution: [
{ key: 'process_request_id', label: this.$t('Request ID') },
{ key: 'node_id', label: this.$t('Node ID') },
{ key: 'case_number', label: this.$t('Case #') },
{ key: 'process_name', label: this.$t('Process') },
{ key: 'node_name', label: this.$t('Node') },
{ key: 'flow_genie_name', label: this.$t('FlowGenie Name') },
{ key: 'user_name', label: this.$t('User') },
{ key: 'status', label: this.$t('Status') },
{ key: 'duration', label: this.$t('Execution Time') },
{ key: 'llm_calls', label: this.$t('LLM Calls') },
{ key: 'tools', label: this.$t('Tools') },
{ key: 'tokens_used', label: this.$t('Tokens Used') },
{ key: 'created_at', label: this.$t('Date'), format: dateFormatter },
Expand Down Expand Up @@ -141,6 +237,7 @@ export default {
this.fetchData();
},
async fetchData(params = {}) {
this.loading = true;
try {
const response = await ProcessMaker.apiClient.get(this.apiEndpoint, {
params: {
Expand All @@ -160,6 +257,8 @@ export default {
// eslint-disable-next-line no-console
console.log(error);
this.data = [];
} finally {
this.loading = false;
}
},
handlePageChange(newPage) {
Expand All @@ -170,6 +269,22 @@ export default {
this.page = 1;
this.fetchData(params);
},
getStatusClasses(status) {
const statusClasses = {
completed: 'tw-bg-green-100 tw-text-green-800',
error: 'tw-bg-red-100 tw-text-red-800',
processing: 'tw-bg-yellow-100 tw-text-yellow-800',
};
return statusClasses[status] || 'tw-bg-gray-100 tw-text-gray-800';
},
formatStatus(status) {
const statusLabels = {
completed: this.$t('Completed'),
error: this.$t('Error'),
processing: this.$t('Processing'),
};
return statusLabels[status] || status;
},
},
};
</script>
Expand Down
4 changes: 2 additions & 2 deletions resources/js/admin/logs/components/Logs/Sidebar/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<aside class="tw-w-72 tw-shrink-0">
<div class="tw-rounded-xl tw-border tw-border-zinc-200 tw-p-3 tw-bg-white tw-h-screen">
<aside class="tw-w-72 tw-shrink-0 tw-h-full">
<div class="tw-rounded-xl tw-border tw-border-zinc-200 tw-p-3 tw-bg-white tw-h-full tw-overflow-y-auto">
<div class="tw-px-2 tw-py-1 tw-text-xs tw-font-semibold tw-uppercase tw-text-zinc-500">
Logs
</div>
Expand Down
Loading