feat: init

This commit is contained in:
2026-02-13 22:02:30 +01:00
commit 8f9ff830fb
16711 changed files with 3307340 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
<script setup lang="ts">
const { rates } = useVatRates()
const selectedCode = ref('DE')
const response = ref('')
const statusCode = ref<number | null>(null)
const isLoading = ref(false)
const showAllRates = ref(false)
const endpointUrl = computed(() =>
showAllRates.value
? 'https://vat-api.eu/api/v1/rates'
: `https://vat-api.eu/api/v1/rates/${selectedCode.value}`,
)
function syntaxHighlight(json: string): string {
return json
.replace(/("(?:\\.|[^"\\])*")\s*:/g, '<span style="color:#79c0ff">$1</span>:')
.replace(/:\s*("(?:\\.|[^"\\])*")/g, ': <span style="color:#a5d6ff">$1</span>')
.replace(/:\s*(\d+\.?\d*)/g, ': <span style="color:#79c0ff">$1</span>')
.replace(/(\[|\])/g, '<span style="color:#ff7b72">$1</span>')
.replace(/^(\{|})/gm, '<span style="color:#ff7b72">$1</span>')
}
async function sendRequest() {
isLoading.value = true
response.value = ''
statusCode.value = null
try {
const url = showAllRates.value
? '/api/v1/rates'
: `/api/v1/rates/${selectedCode.value}`
const data = await $fetch(url)
statusCode.value = 200
response.value = syntaxHighlight(JSON.stringify(data, null, 2))
} catch (err: any) {
statusCode.value = err?.statusCode ?? 500
const errorBody = err?.data ?? { error: err?.statusMessage ?? 'Request failed' }
response.value = syntaxHighlight(JSON.stringify(errorBody, null, 2))
} finally {
isLoading.value = false
}
}
// Auto-send on mount
onMounted(() => sendRequest())
</script>
<template>
<section id="playground" class="section-padding bg-surface-soft">
<div class="section-container">
<div class="text-center mb-10 animate-on-scroll">
<h2 class="text-title md:text-display-sm text-ink">
Try it out
</h2>
<p class="mt-3 text-ink-muted max-w-lg mx-auto">
Build your request, send it, and see the response.
</p>
</div>
<div class="animate-on-scroll max-w-3xl mx-auto">
<div class="bg-white rounded-2xl border border-surface-border shadow-card overflow-hidden">
<!-- Controls -->
<div class="p-5 border-b border-surface-border">
<div class="flex flex-wrap gap-3 items-end">
<!-- Endpoint toggle -->
<div class="flex-shrink-0">
<label class="block text-xs font-medium text-ink-muted mb-1.5">Endpoint</label>
<div class="flex rounded-lg border border-surface-border overflow-hidden text-sm">
<button
class="px-3 py-2 font-medium transition-colors"
:class="!showAllRates ? 'bg-eu-blue text-white' : 'bg-white text-ink-secondary hover:bg-surface-soft'"
@click="showAllRates = false"
>
Single
</button>
<button
class="px-3 py-2 font-medium transition-colors"
:class="showAllRates ? 'bg-eu-blue text-white' : 'bg-white text-ink-secondary hover:bg-surface-soft'"
@click="showAllRates = true"
>
All Rates
</button>
</div>
</div>
<!-- Country picker -->
<div v-if="!showAllRates" class="flex-shrink-0">
<label class="block text-xs font-medium text-ink-muted mb-1.5">Country</label>
<select
v-model="selectedCode"
class="px-3 py-2 rounded-lg border border-surface-border bg-white text-sm text-ink focus:outline-none focus:ring-2 focus:ring-eu-blue/20 focus:border-eu-blue/40 transition-all"
>
<option v-for="rate in rates" :key="rate.code" :value="rate.code">
{{ rate.flag }} {{ rate.country }} ({{ rate.code }})
</option>
</select>
</div>
<!-- Send button -->
<button
class="px-5 py-2 rounded-lg bg-eu-gold text-eu-blue-dark font-semibold text-sm hover:bg-eu-gold-dark transition-colors flex items-center gap-2"
@click="sendRequest"
:disabled="isLoading"
>
<svg v-if="isLoading" class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
<span>{{ isLoading ? 'Sending...' : 'Send Request' }}</span>
</button>
</div>
<!-- URL display -->
<div class="mt-3.5 flex items-center gap-2 px-3.5 py-2.5 rounded-lg bg-surface-muted font-mono text-sm overflow-x-auto">
<span class="flex-shrink-0 text-xs font-semibold px-1.5 py-0.5 rounded bg-green-100 text-green-700">GET</span>
<span class="text-ink-secondary">{{ endpointUrl }}</span>
</div>
</div>
<!-- Response -->
<div class="relative">
<div class="flex items-center justify-between px-5 py-3 bg-[#0d1117] border-b border-white/5">
<span class="text-xs text-[#8b949e] font-mono">Response</span>
<span v-if="statusCode === 200" class="text-xs font-mono px-2 py-0.5 rounded bg-green-500/20 text-green-400">200 OK</span>
<span v-else-if="statusCode" class="text-xs font-mono px-2 py-0.5 rounded bg-red-500/20 text-red-400">{{ statusCode }} Error</span>
</div>
<div class="bg-[#0d1117] text-[#e6edf3] font-mono text-sm leading-relaxed p-5 max-h-80 overflow-y-auto">
<pre v-if="response" class="text-[13px] leading-6" v-html="response" />
<p v-else-if="isLoading" class="text-[#8b949e] text-sm">Loading...</p>
<p v-else class="text-[#8b949e] text-sm">Click "Send Request" to see a response.</p>
</div>
</div>
</div>
</div>
</div>
</section>
</template>