Files
DredgeTours/resources/js/components/dredgy/TourCard.vue
2025-09-15 13:40:20 +10:00

234 lines
5.7 KiB
Vue

<script setup lang="ts">
import {Tour} from "@/types";
import { defineProps } from 'vue'
import EdgyButton from "@/components/dredgy/EdgyButton.vue";
const formatPrice = (price: number) => {
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(price)
}
const props = defineProps<{
tour: Tour
}>()
</script>
<template>
<div class="tour-card" data-index="0">
<div class="tour-image">
<img :src="`/img/tours/${tour.internal_name}.jpg`" alt="">
<div class="tour-overlay"></div>
<div :class="['tour-difficulty', tour.level.toLowerCase()]">{{tour.level}}</div>
<div class="tour-price">{{formatPrice(tour.price)}}</div>
</div>
<div class="tour-content">
<div class="tour-location">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
<circle cx="12" cy="10" r="3"></circle>
</svg>
{{ tour.countries.at(0)?.name }}, {{ tour.countries.at(0)?.continent.name }}
</div>
<h3 class="tour-title">{{tour.title}}</h3>
<p class="tour-description" v-if="tour.short_description">{{tour.short_description}}</p>
<div class="tour-details">
<div class="tour-detail">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12,6 12,12 16,14"></polyline>
</svg>
{{tour.length}} Days
</div>
<div class="tour-detail">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="9" cy="7" r="4"></circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</svg>
Maximum 6
</div>
</div>
<EdgyButton classes="btn-primary btn-full">Book Now</EdgyButton>
</div>
</div>
</template>
<style scoped>
/* Tour Cards */
.tour-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 0;
clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 20px 100%, 0 calc(100% - 20px));
cursor: pointer;
transition: all 1s cubic-bezier(0.4, 0, 0.2, 1);
transform: translateY(100px) rotate(-6deg) scale(0.9);
opacity: 0;
transform-origin: center bottom;
}
.tour-card:nth-child(2n) {
transform: translateY(100px) rotate(6deg) scale(0.9);
}
.tour-card.visible {
transform: translateY(0) rotate(0deg) scale(1);
opacity: 1;
}
.tour-card:hover {
box-shadow: var(--shadow-intense);
transform: translateY(-8px) rotate(0deg) scale(1.02);
}
.tour-image {
position: relative;
height: 256px;
overflow: hidden;
}
.tour-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.tour-card:hover .tour-image img {
transform: scale(1.1);
}
.tour-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
}
.tour-difficulty {
position: absolute;
top: 1rem;
right: 1rem;
padding: 0.25rem 0.75rem;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.tour-difficulty.expert {
background: var(--destructive);
color: var(--destructive-foreground);
}
.tour-difficulty.moderate {
background: var(--secondary);
color: var(--secondary-foreground);
}
.tour-difficulty.beginner {
background: var(--accent);
color: var(--background);
}
.tour-price {
position: absolute;
bottom: 1rem;
right: 1rem;
background: var(--primary);
color: white;
padding: 0.5rem 0.75rem;
border-radius: 4px;
font-weight: bold;
}
.tour-content {
padding: 1.5rem;
}
.tour-location {
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--muted-foreground);
font-size: 0.875rem;
margin-bottom: 0.5rem;
}
.tour-title {
font-size: 1.25rem;
font-weight: bold;
color: var(--card-foreground);
margin-bottom: 0.75rem;
transition: var(--transition-smooth);
}
.tour-card:hover .tour-title {
color: var(--primary);
}
.tour-description {
color: var(--muted-foreground);
font-size: 0.875rem;
line-height: 1.6;
margin-bottom: 1rem;
}
.tour-details {
display: flex;
justify-content: space-between;
margin-bottom: 1.5rem;
}
.tour-detail {
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--muted-foreground);
font-size: 0.875rem;
}
/* Animations */
@keyframes bounce {
0%, 20%, 53%, 80%, 100% {
transform: translateX(-50%) translateY(0);
}
40%, 43% {
transform: translateX(-50%) translateY(-15px);
}
70% {
transform: translateX(-50%) translateY(-7px);
}
90% {
transform: translateX(-50%) translateY(-2px);
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-fade {
animation: fadeInUp 1s ease-out;
}
.animate-slide {
animation: fadeInUp 1s ease-out 0.2s both;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>