11<template >
2- <v-container >
3- <v-row >
4- <v-col cols =" 12" >
5- <h2 class =" text-h6 font-weight-medium" >State Output #{{ outputIndex }}</h2 >
6- <v-divider class =" my-4" ></v-divider >
7- </v-col >
8- </v-row >
2+ <v-container fluid >
3+ <h2 class =" text-h6 font-weight-medium" >
4+ State Output <span class =" text-grey-darken-1" >#{{ $route.params.outputIndex }}</span >
5+ </h2 >
6+ <v-divider class =" my-4" ></v-divider >
97
10- <v-row v-if =" loading" >
11- <v-col cols =" 12" class =" text-center" >
12- <v-progress-circular indeterminate color =" primary" ></v-progress-circular >
13- </v-col >
14- </v-row >
8+ <template v-if =" loading " >
9+ <v-card >
10+ <v-card-text >
11+ <v-skeleton-loader type =" list-item-three-line" ></v-skeleton-loader >
12+ <v-skeleton-loader type =" list-item-three-line" ></v-skeleton-loader >
13+ <v-skeleton-loader type =" list-item-three-line" ></v-skeleton-loader >
14+ </v-card-text >
15+ </v-card >
16+ </template >
17+ <template v-else-if =" output && ! loading " >
18+ <BaseChipGroup v-model =" selectedTab" mandatory >
19+ <v-chip label size =" small" value =" overview" >Overview</v-chip >
20+ </BaseChipGroup >
1521
16- <v-row v-else-if =" output" >
17- <v-col cols =" 12" md =" 6" >
18- <v-card >
19- <v-card-title >Output Information</v-card-title >
20- <v-card-text >
21- <v-table density =" compact" >
22- <tbody >
23- <tr >
24- <td class =" font-weight-medium" >Output Index</td >
25- <td >#{{ output.outputIndex.toLocaleString() }}</td >
26- </tr >
27- <tr >
28- <td class =" font-weight-medium" >Output Root</td >
29- <td style =" font-family : monospace ; word-break : break-all ;" >{{ output.outputRoot }}</td >
30- </tr >
31- <tr >
32- <td class =" font-weight-medium" >L2 Block</td >
33- <td v-if =" output.l2BlockNumber" >
34- <HashLink :type =" 'block'" :hash =" output.l2BlockNumber" />
35- </td >
36- <td v-else class =" text-medium-emphasis" >-</td >
37- </tr >
38- <tr >
39- <td class =" font-weight-medium" >L1 Block</td >
40- <td >{{ output.l1BlockNumber.toLocaleString() }}</td >
41- </tr >
42- <tr >
43- <td class =" font-weight-medium" >L1 Transaction</td >
44- <td style =" font-family : monospace ;" >
45- {{ output.l1TransactionHash ? `${output.l1TransactionHash.slice(0, 20)}...${output.l1TransactionHash.slice(-16)}` : '-' }}
46- </td >
47- </tr >
48- <tr >
49- <td class =" font-weight-medium" >Proposer</td >
50- <td >
51- <HashLink :type =" 'address'" :hash =" output.proposer" :withTokenName =" true" />
52- </td >
53- </tr >
54- <tr >
55- <td class =" font-weight-medium" >Proposed At</td >
56- <td >{{ $dt.shortDate(output.timestamp) }} ({{ $dt.fromNow(output.timestamp) }})</td >
57- </tr >
58- <tr >
59- <td class =" font-weight-medium" >Challenge Period Ends</td >
60- <td >
61- {{ $dt.shortDate(output.challengePeriodEnds) }}
62- <span v-if =" new Date(output.challengePeriodEnds) > new Date()" class =" text-warning" >
63- ({{ $dt.fromNow(output.challengePeriodEnds) }})
64- </span >
65- <span v-else class =" text-success" >(Ended)</span >
66- </td >
67- </tr >
68- <tr >
69- <td class =" font-weight-medium" >Status</td >
70- <td >
71- <v-chip :color =" statusColors[output.status]" >
72- {{ statusLabels[output.status] }}
73- </v-chip >
74- </td >
75- </tr >
76- </tbody >
77- </v-table >
78- </v-card-text >
79- </v-card >
80- </v-col >
81-
82- <v-col cols =" 12" md =" 6" v-if =" output.disputeGameAddress" >
83- <v-card >
84- <v-card-title >Dispute Game</v-card-title >
85- <v-card-text >
86- <v-table density =" compact" >
87- <tbody >
88- <tr >
89- <td class =" font-weight-medium" >Game Address</td >
90- <td >
91- <HashLink :type =" 'address'" :hash =" output.disputeGameAddress" />
92- </td >
93- </tr >
94- <tr v-if =" output.gameType !== null" >
95- <td class =" font-weight-medium" >Game Type</td >
96- <td >{{ output.gameType }}</td >
97- </tr >
98- </tbody >
99- </v-table >
100- </v-card-text >
101- </v-card >
102- </v-col >
103- </v-row >
22+ <OpOutputOverview
23+ v-if =" selectedTab === 'overview'"
24+ :output =" output"
25+ />
26+ </template >
27+ <template v-else >
28+ <v-card >
29+ <v-card-text >
30+ <p >Couldn't find state output #{{ $route.params.outputIndex }}</p >
31+ </v-card-text >
32+ </v-card >
33+ </template >
10434 </v-container >
10535</template >
10636
10737<script setup>
108- import { ref , onMounted , inject } from ' vue' ;
109- import HashLink from ' @/components/HashLink.vue' ;
38+ import { ref , onMounted , inject , watch } from ' vue' ;
39+ import { useRouter } from ' vue-router' ;
40+ import BaseChipGroup from ' ./base/BaseChipGroup.vue' ;
41+ import OpOutputOverview from ' ./OpOutputOverview.vue' ;
11042
11143const props = defineProps ({
11244 outputIndex: {
@@ -116,38 +48,37 @@ const props = defineProps({
11648});
11749
11850const $server = inject (' $server' );
119- const $dt = inject (' $dt' );
51+ const router = useRouter ();
52+
53+ const selectedTab = ref (' overview' );
12054
121- const loading = ref (true );
55+ // Reactive data
56+ const loading = ref (false );
12257const output = ref (null );
58+ const error = ref (null );
12359
124- const statusColors = {
125- proposed: ' info' ,
126- challenged: ' warning' ,
127- resolved: ' primary' ,
128- finalized: ' success'
129- };
60+ // Methods
61+ function loadOutput () {
62+ loading .value = true ;
63+ error .value = null ;
13064
131- const statusLabels = {
132- proposed: ' Proposed' ,
133- challenged: ' Challenged' ,
134- resolved: ' Resolved' ,
135- finalized: ' Finalized'
136- };
65+ $server .getOpOutputDetail (props .outputIndex )
66+ .then (response => output .value = response .data )
67+ .catch (console .log )
68+ .finally (() => loading .value = false );
69+ }
13770
138- async function loadOutput () {
139- loading .value = true ;
140- try {
141- const { data } = await $server .getOpOutputDetail (props .outputIndex );
142- output .value = data;
143- } catch (error) {
144- console .error (' Error loading output:' , error);
145- } finally {
146- loading .value = false ;
71+ // Watch with optimization
72+ watch (() => props .outputIndex , (outputIndex ) => {
73+ // Reset state when index changes
74+ if (outputIndex !== props .outputIndex ) {
75+ output .value = null ;
14776 }
148- }
77+
78+ loadOutput (outputIndex);
79+ }, { immediate: true });
14980
15081onMounted (() => {
151- loadOutput ();
82+ // Future: could add hash-based tab switching if more tabs are added
15283});
15384 </script >
0 commit comments