all repos — stealth-developers @ 09081644ff2bf482c3c68319af758a28e1626a15

apps/web/src/components/Tickets/AttachmentComponent.vue (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
<script setup lang="ts">
import type { SanitisedTicketAttachment } from "@stealth-developers/api";

import { computed } from "vue";

const props = defineProps<{
	attachment: SanitisedTicketAttachment;
}>();

const fileType = computed(() => props.attachment.fileType || "");
const fileSize = computed(() => props.attachment.fileSize || 0);
const fileName = computed(() => props.attachment.fileName || "Unknown");

const url = computed(() => `https://api.media.vt3e.cat/sd-prod/${props.attachment.key}`);

const formatSize = (bytes: number) => {
	if (bytes === 0) return "0 B";
	const k = 1024;
	const sizes = ["B", "KB", "MB", "GB", "TB"];
	const i = Math.floor(Math.log(bytes) / Math.log(k));
	return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]!;
};
</script>

<template>
	<div v-if="fileType.startsWith('video/')" class="attachment-card video">
		<video :src="url" controls class="attachment-media"></video>
	</div>
	<a v-else :href="url" target="_blank" class="attachment-card link">
		<img v-if="fileType.startsWith('image/')" :src="url" :alt="fileName" class="attachment-media" />
		<div class="attachment-file" v-else>
			<div class="file-info">
				<span class="filename" :title="fileName">{{ fileName }}</span>
				<span class="size">{{ formatSize(fileSize) }}</span>
			</div>
		</div>
	</a>
</template>

<style scoped>
.attachment-card {
	display: flex;
	flex-direction: column;
	border-radius: 0.75rem;
	overflow: hidden;
	background-color: hsla(var(--surface0) / 0.3);
	border: 1px solid hsla(var(--surface0) / 0.5);
	max-width: 100%;

	&.link {
		text-decoration: none;
		transition: border-color var(--transition);

		&:hover {
			border-color: hsla(var(--accent) / 0.5);
		}
	}

	.attachment-media {
		display: block;
		max-width: 100%;
		max-height: 160px;
		object-fit: cover;
	}

	img.attachment-media {
		min-width: 160px;
	}

	video.attachment-media {
		max-width: 300px;
	}

	.attachment-file {
		display: flex;
		align-items: center;
		gap: 0.75rem;
		padding: 0.75rem 1rem;
		color: hsl(var(--text));

		.file-info {
			display: flex;
			flex-direction: column;
			min-width: 0;

			.filename {
				font-weight: 600;
				font-size: 0.9rem;
				overflow: hidden;
				text-overflow: ellipsis;
				white-space: nowrap;
				max-width: 180px;
			}

			.size {
				color: hsl(var(--subtext0));
				font-size: 0.75rem;
			}
		}
	}
}
</style>