<template>
<div
class=""
:class="
clsx(
'group/funcruncard relative p-xs border border-transparent cursor-pointer transition-all duration-200',
themeClasses(
'hover:border-action-500 border-b-neutral-300',
'hover:border-action-300 border-b-neutral-800',
),
isRunning
? 'border-l-4 border-l-action-500 pl-xs animate-[pulse_2s_infinite]'
: 'border-l-1',
)
"
@click="$emit('click', funcRun.id)"
>
<div class="flex items-center gap-xs">
<!-- Status indicator -->
<div>
<StatusIndicatorIcon
:status="funcRunStatus(funcRun, managementFuncJobState?.state)"
type="management"
size="sm"
/>
</div>
<!-- Function info -->
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between">
<div
:class="
clsx(
'text-sm font-medium',
themeClasses(
'group-hover/funcruncard:text-action-500',
'group-hover/funcruncard:text-action-300',
),
)
"
:title="funcRun.functionDisplayName || funcRun.functionName"
>
<span
:class="
clsx(
'text-xs',
themeClasses(
'group-hover/funcruncard:text-action-500',
'group-hover/funcruncard:text-action-300',
),
)
"
>
{{ funcRun.functionDisplayName || funcRun.functionName }}
</span>
</div>
<div class="text-xs text-neutral-500 pl-xs">
{{ formatTimeAgo(funcRun.createdAt) }}
</div>
</div>
<div class="grow flex flex-row items-center text-xs gap-xs">
<!-- Kind badge -->
<span
class="px-2xs py-3xs rounded-full text-2xs inline-flex items-center justify-center flex-none"
:class="functionKindClass(funcRun.functionKind)"
>
{{ funcRun.functionKind }}
</span>
<div class="flex-1 min-w-0">
<TruncateWithTooltip
v-if="funcRun.componentId && funcRun.componentName"
>
{{ funcRun.componentName }}
</TruncateWithTooltip>
</div>
<span
v-if="funcRun.actionId"
class="flex flex-row items-center text-action-400"
>
<Icon name="bolt" size="xs" />
Action
</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import clsx from "clsx";
import { computed } from "vue";
import {
Icon,
themeClasses,
TruncateWithTooltip,
} from "@si/vue-lib/design-system";
import StatusIndicatorIcon from "@/components/StatusIndicatorIcon.vue";
import { funcRunStatus, FuncRun } from "./api_composables/func_run";
import { useManagementFuncJobState } from "./logic_composables/management";
const props = defineProps<{
funcRun: FuncRun;
}>();
defineEmits<{
(e: "click", funcRunId: string): void;
}>();
const funcRun = computed(() => props.funcRun);
const managementFuncJobStateComposable = useManagementFuncJobState(funcRun);
const managementFuncJobState = computed(
() => managementFuncJobStateComposable.value.value,
);
/**
* Determines if the function is currently in a running state
* @returns {boolean} True if function is in a running state
*/
const isRunning = computed(() => {
return ["Created", "Dispatched", "Running", "Postprocessing"].includes(
props.funcRun.state,
);
});
/**
* Formats a timestamp to a relative time string (e.g., "5m ago")
* @param {string} dateString - ISO date string to format
* @returns {string} Formatted relative time
*/
const formatTimeAgo = (dateString: string): string => {
const date = new Date(dateString);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
// Convert to seconds
const diffSec = Math.floor(diffMs / 1000);
if (diffSec < 60) {
return `${diffSec}s ago`;
}
// Convert to minutes
const diffMin = Math.floor(diffSec / 60);
if (diffMin < 60) {
return `${diffMin}m ago`;
}
// Convert to hours
const diffHour = Math.floor(diffMin / 60);
if (diffHour < 24) {
return `${diffHour}h ago`;
}
// Convert to days
const diffDay = Math.floor(diffHour / 24);
return `${diffDay}d ago`;
};
/**
* Returns Tailwind CSS classes for a function kind badge
* @param {string} kind - The function kind
* @returns {string} Tailwind CSS classes for the badge
*/
const functionKindClass = (kind: string): string => {
const classes = {
action: "bg-action-900 text-action-300",
attribute: "bg-success-900 text-success-300",
authentication: "bg-warning-900 text-warning-300",
management: "bg-neutral-800 text-neutral-300",
intrinsic: "bg-neutral-800 text-neutral-300",
codeGeneration: "bg-violet-900 text-violet-300",
};
return (
classes[kind as keyof typeof classes] || "bg-neutral-800 text-neutral-300"
);
};
</script>