196 lines
5.4 KiB
Vue
196 lines
5.4 KiB
Vue
<script setup lang="ts">
|
|
import OrderStatusBadge from '~/components/orders/OrderStatusBadge.vue';
|
|
import {
|
|
getOrderStatusBadgePresentation,
|
|
getOrderStatusPresentation,
|
|
type OrderStatusTone,
|
|
} from '~/composables/useOrderStatusPresentation';
|
|
|
|
const props = defineProps<{
|
|
status: string;
|
|
createdAt: string | Date;
|
|
audience?: 'client' | 'manager';
|
|
}>();
|
|
|
|
const isExpanded = ref(false);
|
|
|
|
const presentation = computed(() => getOrderStatusPresentation(props.status, props.createdAt, props.audience ?? 'client'));
|
|
const currentBadge = computed(() => getOrderStatusBadgePresentation(props.status));
|
|
|
|
function currentToneClass(tone: OrderStatusTone) {
|
|
if (tone === 'success') {
|
|
return {
|
|
marker: 'bg-[#139957] ring-4 ring-[#dff4e8]',
|
|
panel: 'bg-[#eef8f2]',
|
|
title: 'text-[#123824]',
|
|
note: 'text-[#355947]',
|
|
date: 'text-[#139957]',
|
|
connector: 'bg-[#bfe0cb]',
|
|
};
|
|
}
|
|
if (tone === 'danger') {
|
|
return {
|
|
marker: 'bg-[#d94b55] ring-4 ring-[#fbe5e7]',
|
|
panel: 'bg-[#fff1f2]',
|
|
title: 'text-[#7e2130]',
|
|
note: 'text-[#9b4150]',
|
|
date: 'text-[#d94b55]',
|
|
connector: 'bg-[#f1c6cb]',
|
|
};
|
|
}
|
|
if (tone === 'warning') {
|
|
return {
|
|
marker: 'bg-[#f1a43a] ring-4 ring-[#fff0d9]',
|
|
panel: 'bg-[#fff7eb]',
|
|
title: 'text-[#6c4303]',
|
|
note: 'text-[#8f6420]',
|
|
date: 'text-[#c67d11]',
|
|
connector: 'bg-[#efd1a2]',
|
|
};
|
|
}
|
|
|
|
return {
|
|
marker: 'bg-[#2e8de4] ring-4 ring-[#e3effb]',
|
|
panel: 'bg-[#eef5fc]',
|
|
title: 'text-[#174b7e]',
|
|
note: 'text-[#436b92]',
|
|
date: 'text-[#2e8de4]',
|
|
connector: 'bg-[#c7dbef]',
|
|
};
|
|
}
|
|
|
|
function markerClass(state: 'done' | 'current' | 'upcoming') {
|
|
if (state === 'current') {
|
|
return currentToneClass(currentBadge.value.tone).marker;
|
|
}
|
|
if (state === 'done') {
|
|
return 'bg-[#9dcfb0]';
|
|
}
|
|
return 'bg-[#d8e4dd]';
|
|
}
|
|
|
|
function connectorClass(state: 'done' | 'current' | 'upcoming') {
|
|
if (state === 'done' || state === 'current') {
|
|
return state === 'current'
|
|
? currentToneClass(currentBadge.value.tone).connector
|
|
: 'bg-[#cfe5d7]';
|
|
}
|
|
|
|
return 'bg-[#e4ece7]';
|
|
}
|
|
|
|
function titleClass(state: 'done' | 'current' | 'upcoming') {
|
|
if (state === 'current') {
|
|
return currentToneClass(currentBadge.value.tone).title;
|
|
}
|
|
if (state === 'done') {
|
|
return 'text-[#355947]';
|
|
}
|
|
|
|
return 'text-[#6a8a76]';
|
|
}
|
|
|
|
function noteClass(state: 'done' | 'current' | 'upcoming') {
|
|
if (state === 'current') {
|
|
return currentToneClass(currentBadge.value.tone).note;
|
|
}
|
|
if (state === 'done') {
|
|
return 'text-[#557562]';
|
|
}
|
|
|
|
return 'text-[#7d9688]';
|
|
}
|
|
|
|
function stagePanelClass(state: 'done' | 'current' | 'upcoming') {
|
|
if (state === 'current') {
|
|
return currentToneClass(currentBadge.value.tone).panel;
|
|
}
|
|
if (state === 'done') {
|
|
return 'bg-[#f3f7f4]';
|
|
}
|
|
|
|
return 'bg-[#f7faf8]';
|
|
}
|
|
|
|
function dateClass(state: 'done' | 'current' | 'upcoming') {
|
|
if (state === 'current') {
|
|
return currentToneClass(currentBadge.value.tone).date;
|
|
}
|
|
if (state === 'done') {
|
|
return 'text-[#5c7b69]';
|
|
}
|
|
|
|
return 'text-[#86a091]';
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="surface-card rounded-3xl p-5">
|
|
<button
|
|
type="button"
|
|
class="flex w-full items-start justify-between gap-4 text-left"
|
|
@click="isExpanded = !isExpanded"
|
|
>
|
|
<div class="space-y-3">
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<h2 class="text-2xl font-black leading-tight text-[#123824]">
|
|
{{ presentation.title }}
|
|
</h2>
|
|
<OrderStatusBadge :status="status" />
|
|
</div>
|
|
<p class="max-w-2xl text-sm leading-6 text-[#355947]">
|
|
{{ presentation.summary }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3 pt-1">
|
|
<span
|
|
class="flex h-10 w-10 items-center justify-center rounded-full bg-[#f2f5f3] text-[#123824] transition-transform"
|
|
:class="{ 'rotate-180': isExpanded }"
|
|
>
|
|
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="none">
|
|
<path d="M5 7.5L10 12.5L15 7.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
</button>
|
|
|
|
<div v-if="isExpanded" class="mt-5 pt-1">
|
|
<div
|
|
v-for="(stage, index) in presentation.stages"
|
|
:key="stage.code"
|
|
class="flex gap-4"
|
|
>
|
|
<div class="flex w-4 shrink-0 flex-col items-center">
|
|
<span class="mt-1 h-3 w-3 rounded-full" :class="markerClass(stage.state)" />
|
|
<span
|
|
v-if="index < presentation.stages.length - 1"
|
|
class="mt-2 w-px flex-1"
|
|
:class="connectorClass(stage.state)"
|
|
/>
|
|
</div>
|
|
|
|
<div class="min-w-0 flex-1 pb-5">
|
|
<div
|
|
class="rounded-[22px] px-4 py-4 transition-colors"
|
|
:class="stagePanelClass(stage.state)"
|
|
>
|
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
<p class="text-sm font-semibold" :class="titleClass(stage.state)">
|
|
{{ stage.label }}
|
|
</p>
|
|
<p class="text-xs font-semibold uppercase tracking-[0.12em]" :class="dateClass(stage.state)">
|
|
{{ stage.dateLabel }}
|
|
</p>
|
|
</div>
|
|
|
|
<p class="mt-2 text-sm leading-6" :class="noteClass(stage.state)">
|
|
{{ stage.note }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|