Files
nimbus/resources/js/components/domain/Client/Response/ResponseStatus/ResponseStatus.vue
Mazen Touati 2895a0ddc6 feat(export): add shareable links (#41)
* feat(export): add shareable links

* chore: reconfigure PW

* test: fix namespace

* style: apply prettier

* chore: reduce workers count in CI for PW

tests are running slower (to the point some time out) and flaky

* fix: initialize pending request from store immediately

* chore: apply rector
2026-01-24 03:01:32 +01:00

138 lines
4.4 KiB
Vue

<script setup lang="ts">
import { AppButton } from '@/components/base/button';
import RequestHistory from '@/components/domain/Client/Response/ResponseStatus/History/RequestHistory.vue';
import ResponseStatusCode from '@/components/domain/Client/Response/ResponseStatus/ResponseStatusCode.vue';
import { PendingRequest, STATUS } from '@/interfaces/http';
import { useRequestsHistoryStore, useRequestStore } from '@/stores';
import { cn } from '@/utils/ui';
import { Import, RefreshCwOffIcon } from 'lucide-vue-next';
import prettyBytes from 'pretty-bytes';
import prettyMs from 'pretty-ms';
import { PrimitiveProps } from 'reka-ui';
import { computed, ComputedRef, HTMLAttributes } from 'vue';
import AppTooltipWrapper from '../../../../base/tooltip/AppTooltipWrapper.vue';
/*
* Types & interfaces.
*/
interface ResponseStatusProps extends PrimitiveProps {
class?: HTMLAttributes['class'];
}
/*
* Props.
*/
const props = defineProps<ResponseStatusProps>();
/*
* Stores.
*/
const requestStore = useRequestStore();
const historyStore = useRequestsHistoryStore();
/*
* Computed.
*/
const pendingRequestData: ComputedRef<PendingRequest | null> = computed(
() => requestStore.pendingRequestData,
);
const lastLog = computed(() => historyStore.lastLog);
const status = computed(() => {
if (pendingRequestData.value?.isProcessing) {
return STATUS.PENDING;
}
if (!lastLog.value || !lastLog.value.response) {
return STATUS.EMPTY;
}
return lastLog.value.response.status ?? STATUS.EMPTY;
});
const size = computed(() =>
prettyBytes(
pendingRequestData.value?.wasExecuted
? (lastLog.value?.response?.sizeInBytes ?? 0)
: 0, // <- When a new endpoint is initialized, we reset the size as well.
{ space: false },
),
);
const duration = computed(() => {
return prettyMs(
// If there's a pending request that's processing, use its duration
// Otherwise, use the last completed request's duration
pendingRequestData.value?.durationInMs ?? lastLog.value?.durationInMs ?? 0,
{
verbose: false,
secondsDecimalDigits: 2,
keepDecimalsOnWholeSeconds: true,
},
);
});
/*
* Actions.
*/
const cancelRequest = () => {
requestStore.cancelCurrentRequest();
};
const isImportedFromShare = computed(() => lastLog.value?.importedFromShare === true);
</script>
<template>
<div :class="cn('h-toolbar flex', props.class)">
<div class="relative flex h-full flex-1 items-center justify-between p-2">
<div class="flex w-full items-center justify-between gap-1">
<div class="flex items-center space-x-2">
<ResponseStatusCode
:status="status"
:response="
!pendingRequestData?.isProcessing
? lastLog?.response
: undefined
"
/>
<div class="w-8 border-b border-zinc-200"></div>
<span class="text-xs">
<span data-testid="response-status-duration">{{ duration }}</span>
<template v-if="!pendingRequestData?.isProcessing">
<span class="text-color-muted mx-1 text-xs">/</span>
<span data-testid="response-status-size">{{ size }}</span>
</template>
</span>
</div>
<div v-if="!pendingRequestData?.isProcessing" class="flex items-center">
<RequestHistory />
</div>
</div>
<div v-if="pendingRequestData?.isProcessing">
<AppButton variant="outline" size="xs" @click="cancelRequest">
<RefreshCwOffIcon />
Cancel
</AppButton>
</div>
</div>
<AppTooltipWrapper
v-if="isImportedFromShare"
value="This response was imported from a shareable link"
>
<div
class="flex h-full items-center gap-1 rounded-none border-indigo-500/50 bg-indigo-500/5 p-3.5 text-xs text-indigo-600 dark:text-indigo-400"
data-testid="imported-badge"
>
<Import class="size-4" />
</div>
</AppTooltipWrapper>
</div>
</template>