This commit is contained in:
2026-05-09 01:18:51 +02:00
parent 7116ee4619
commit 959970c150
132 changed files with 21310 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
<template>
<div class="space-y-3">
<p class="text-[11px] text-muted leading-relaxed">
saga compensations · external signals · long-running waits · retry policies
</p>
<div class="flex items-center gap-3 text-[10px] text-muted/60">
<span class="font-mono">app/Temporal/OrderFulfillment/</span>
<a href="https://docs.temporal.io/develop/php/message-passing#signals" target="_blank" class="text-accent/50 hover:text-accent transition-colors">signals</a>
<a href="https://github.com/temporalio/samples-php" target="_blank" class="text-accent/50 hover:text-accent transition-colors">saga pattern</a>
<a href="https://docs.temporal.io/develop/php/failure-detection" target="_blank" class="text-accent/50 hover:text-accent transition-colors">failure detection</a>
</div>
<SimulationControls v-model="simulation" />
<template v-if="localOrders.length > 0">
<table class="w-full text-[11px]">
<thead>
<tr class="text-left text-muted border-b border-border">
<th class="py-1 font-normal">Order</th>
<th class="font-normal">Customer</th>
<th class="font-normal text-right pr-4">Amount</th>
<th class="font-normal pl-4">Status</th>
<th class="font-normal text-right">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="order in localOrders" :key="order.id"
class="border-t border-border/30 text-value hover:bg-section/30 transition-colors">
<td class="py-1.5 font-medium">{{ order.order_number }}</td>
<td class="text-muted">{{ order.customer_name }}</td>
<td class="text-right pr-4">${{ Number(order.total_amount).toFixed(2) }}</td>
<td class="pl-4">
<span class="inline-flex items-center gap-1">
<span class="w-1 h-1 rounded-full" :class="orderStatusDot(order.displayStatus || order.status)"></span>
{{ order.displayStatus || order.status }}
</span>
</td>
<td class="text-right">
<div class="inline-flex gap-1">
<button @click="processOrder(order)"
class="px-1.5 py-0.5 text-[10px] text-accent border border-accent/20 hover:bg-accent/10 transition-all rounded-sm cursor-pointer">
process
</button>
<button @click="shipOrder(order)"
class="px-1.5 py-0.5 text-[10px] text-value border border-border hover:bg-border/30 transition-all rounded-sm cursor-pointer">
ship
</button>
<button @click="checkStatus(order)"
class="px-1.5 py-0.5 text-[10px] text-muted border border-border/50 hover:bg-border/20 transition-all rounded-sm cursor-pointer">
status
</button>
</div>
</td>
</tr>
</tbody>
</table>
</template>
<p v-else class="text-[11px] text-muted">
No orders found. Run <code class="bg-deep px-1 py-0.5 rounded-sm text-label">TemporalDemoSeeder</code> first.
</p>
<div v-if="resultJson" class="bg-deep border border-border rounded-sm overflow-hidden">
<div class="px-2 py-1 border-b border-border/50 text-[10px] text-muted uppercase tracking-wider">Response</div>
<pre class="p-2 text-[10px] text-label leading-relaxed overflow-x-auto">{{ resultJson }}</pre>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import axios from 'axios';
import { usePolling } from '../composables/usePolling.js';
import SimulationControls from './SimulationControls.vue';
const props = defineProps({
orders: { type: Array, default: () => [] },
});
const simulation = ref({
failureRate: 0,
latencyMs: 0,
rateLimiting: { enabled: false, hitChance: 25, retryAfterMs: 2000 },
});
const localOrders = reactive([...props.orders]);
const resultJson = ref('');
let currentPollingOrder = null;
const { start: startPolling, stop: stopPolling } = usePolling(async () => {
if (!currentPollingOrder) return true;
const { data } = await axios.get(`/temporal/order/${currentPollingOrder.id}/status`);
if (data.error && !data.status) {
currentPollingOrder.displayStatus = 'completed';
resultJson.value = JSON.stringify(data, null, 2);
return true;
}
currentPollingOrder.displayStatus = data.status || currentPollingOrder.displayStatus;
resultJson.value = JSON.stringify(data, null, 2);
return ['completed', 'failed'].includes(data.status);
});
async function processOrder(order) {
const { data } = await axios.post(`/temporal/order/${order.id}/process`, { simulation: simulation.value });
resultJson.value = JSON.stringify(data, null, 2);
stopPolling();
currentPollingOrder = order;
startPolling();
}
async function shipOrder(order) {
const { data } = await axios.post(`/temporal/order/${order.id}/ship`);
resultJson.value = JSON.stringify(data, null, 2);
}
async function checkStatus(order) {
const { data } = await axios.get(`/temporal/order/${order.id}/status`);
order.displayStatus = data.status || order.displayStatus;
resultJson.value = JSON.stringify(data, null, 2);
}
function orderStatusDot(status) {
if (status === 'completed' || status === 'delivered') return 'bg-accent';
if (status === 'failed' || status === 'cancelled') return 'bg-danger';
if (status === 'processing' || status === 'shipping') return 'bg-warn';
return 'bg-muted';
}
</script>