/ src / components / StorageRequest.vue
StorageRequest.vue
  1  <script setup>
  2  import { onMounted, computed } from 'vue'
  3  import { useRouter } from 'vue-router'
  4  import { initTooltips } from 'flowbite'
  5  import { getStateColour, moderatedState, price, timestampsFor } from '@/utils/requests'
  6  import { autoPluralize } from '@/utils/strings'
  7  
  8  import CodexImage from '@/components/CodexImage.vue'
  9  import StateIndicator from '@/components/StateIndicator.vue'
 10  import RelativeTime from '@/components/RelativeTime.vue'
 11  import ShortenValue from '@/components/ShortenValue.vue'
 12  import Slots from './Slots.vue'
 13  import SkeletonLoading from './SkeletonLoading.vue'
 14  
 15  const router = useRouter()
 16  const request = defineModel()
 17  const emit = defineEmits(['updateModerated'])
 18  const props = defineProps({
 19    requestId: {
 20      type: String,
 21      required: true
 22    },
 23    enableModeration: {
 24      type: Boolean,
 25      default: false
 26    },
 27    slotsLoading: {
 28      type: Boolean,
 29      default: false
 30    },
 31    slotsFetched: {
 32      type: Boolean,
 33      default: false
 34    }
 35  })
 36  
 37  onMounted(() => {
 38    initTooltips()
 39  })
 40  
 41  const totalPrice = computed(() => price(request.value.request))
 42  const maxSlotLoss = computed(() => autoPluralize(request.value.request.ask.maxSlotLoss, 'slot'))
 43  const slots = computed(() => autoPluralize(request.value.request.ask.slots, 'slot'))
 44  const stateColour = computed(() => getStateColour(request.value.state))
 45  const timestamps = computed(() => {
 46    let { requestedAt } = request.value
 47    let { ask, expiry } = request.value.request
 48    let { endsAt, expiresAt } = timestampsFor(ask, expiry, requestedAt)
 49    return {
 50      requested: requestedAt ? new Date(requestedAt * 1000) : undefined,
 51      expires: requestedAt ? new Date(expiresAt * 1000) : undefined,
 52      ends: requestedAt ? new Date(endsAt * 1000) : undefined
 53    }
 54  })
 55  const requestDetails = computed(() => request.value.request)
 56  function updateEventModerated() {
 57    emit('updateModerated', props.requestId, request.value.moderated)
 58  }
 59  </script>
 60  
 61  <template>
 62    <div class="flex flex-wrap">
 63      <CodexImage
 64        class="flex-initial mx-auto my-4 min-w-sm max-w-md w-full rounded"
 65        :cid="requestDetails.content.cid"
 66        :moderated="enableModeration ? 'approved' : request.moderated"
 67      ></CodexImage>
 68      <div class="py-4 px-4 ml-4 max-w-2xl flex-1">
 69        <div
 70          v-if="enableModeration === true"
 71          class="flex flex-col space-between mb-4 p-5 w-full border border-gray-300 rounded-lg b-1 bg-gray-100 dark:bg-gray-800"
 72        >
 73          <label for="moderation" class="block mb-2 text-lg font-medium text-gray-900 dark:text-white"
 74            >Moderation station</label
 75          >
 76          <div class="flex items-center justify-between space-x-4">
 77            <div class="flex-1">
 78              <select
 79                id="moderation"
 80                class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
 81                v-model="request.moderated"
 82                @change="updateEventModerated"
 83              >
 84                <option v-for="(value, key) in moderatedState" :value="key" :key="key">
 85                  {{ value.text }}
 86                </option>
 87              </select>
 88            </div>
 89            <StateIndicator
 90              class="flex-none h-7 inline-block align-middle"
 91              :text="moderatedState[request.moderated].text"
 92              :color="moderatedState[request.moderated].color"
 93              size="lg"
 94            ></StateIndicator>
 95          </div>
 96          <a
 97            :href="router.resolve({ path: `/request/${requestId}` }).href"
 98            target="_blank"
 99            class="mt-4 font-medium text-blue-600 dark:text-blue-500 hover:underline"
100            @click.stop
101            >Preview</a
102          >
103        </div>
104        <div class="flex justify-between items-center mb-2">
105          <h2 class="text-xl font-semibold leading-none text-gray-900 md:text-2xl dark:text-white">
106            Request <ShortenValue :value="requestId" :chars="8"></ShortenValue>
107          </h2>
108          <StateIndicator :text="request.state" :color="stateColour" size="lg"></StateIndicator>
109        </div>
110        <p
111          class="mb-4 text-xl font-extrabold leading-none text-gray-900 md:text-2xl dark:text-white flex justify-between"
112        >
113          {{ totalPrice }} CDX
114        </p>
115        <dl>
116          <dd class="mb-4 flex justify-between font-light text-gray-500 sm:mb-5 dark:text-gray-400">
117            <div>
118              <dt class="mb-2 font-semibold leading-none text-gray-900 dark:text-white">Requested</dt>
119              <RelativeTime
120                v-if="timestamps.requested"
121                :timestamp="timestamps.requested"
122              ></RelativeTime>
123            </div>
124            <div>
125              <dt class="mb-2 font-semibold leading-none text-gray-900 dark:text-white">Expires</dt>
126              <RelativeTime v-if="timestamps.expires" :timestamp="timestamps.expires"></RelativeTime>
127            </div>
128            <div>
129              <dt class="mb-2 font-semibold leading-none text-gray-900 dark:text-white">Ends</dt>
130              <RelativeTime v-if="timestamps.ends" :timestamp="timestamps.ends"></RelativeTime>
131            </div>
132          </dd>
133        </dl>
134        <dl>
135          <dt class="mb-2 font-semibold leading-none text-gray-900 dark:text-white">Dataset CID</dt>
136          <dd class="mb-4 font-light text-gray-500 sm:mb-5 dark:text-gray-400">
137            {{ requestDetails.content.cid }}
138          </dd>
139        </dl>
140        <dl>
141          <dt class="mb-2 font-semibold leading-none text-gray-900 dark:text-white">Client</dt>
142          <dd class="mb-4 font-light text-gray-500 sm:mb-5 dark:text-gray-400">
143            {{ requestDetails.client }}
144          </dd>
145        </dl>
146        <dl>
147          <dt class="mb-2 font-semibold leading-none text-gray-900 dark:text-white">Merkle root</dt>
148          <dd class="mb-4 font-light text-gray-500 sm:mb-5 dark:text-gray-400">
149            {{ requestDetails.content.merkleRoot }}
150          </dd>
151        </dl>
152        <div class="relative overflow-x-auto overflow-y-auto max-h-screen mb-10">
153          <table
154            class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 mx-auto"
155          >
156            <tbody>
157              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
158                <td
159                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
160                >
161                  Expiry
162                </td>
163                <td class="px-6 py-2 font-light">{{ requestDetails.expiry }} seconds</td>
164              </tr>
165              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
166                <td
167                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
168                >
169                  Duration
170                </td>
171                <td class="px-6 py-2 font-light">{{ requestDetails.ask.duration }} seconds</td>
172              </tr>
173              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
174                <td
175                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
176                >
177                  Slot size
178                </td>
179                <td class="px-6 py-2 font-light">{{ requestDetails.ask.slotSize }} bytes</td>
180              </tr>
181              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
182                <td
183                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
184                >
185                  Proof probability
186                </td>
187                <td class="px-6 py-2 font-light">{{ requestDetails.ask.proofProbability }}</td>
188              </tr>
189              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
190                <td
191                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
192                >
193                  Reward
194                </td>
195                <td class="px-6 py-2 font-light">{{ requestDetails.ask.reward }} CDX</td>
196              </tr>
197              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
198                <td
199                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
200                >
201                  Collateral
202                </td>
203                <td class="px-6 py-2 font-light">{{ requestDetails.ask.collateral }} CDX</td>
204              </tr>
205              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
206                <td
207                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
208                >
209                  Slots
210                </td>
211                <td class="px-6 py-2 font-light">{{ slots }}</td>
212              </tr>
213              <tr class="hover:bg-gray-50 dark:hover:bg-gray-600 text-base">
214                <td
215                  class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white border-r"
216                >
217                  Max slot loss
218                </td>
219                <td class="px-6 py-2 font-light">
220                  {{ maxSlotLoss }}
221                </td>
222              </tr>
223            </tbody>
224          </table>
225        </div>
226  
227        <SkeletonLoading v-if="slotsLoading" type="text" />
228        <Slots v-else-if="request.slots" :slots="request.slots"></Slots>
229      </div>
230    </div>
231  </template>