Refined Discover and Transport UI, added shuttle categorization and descriptions

This commit is contained in:
2026-03-03 10:08:57 -05:00
parent 767667b1b6
commit 9d8a535929
8 changed files with 788 additions and 1504 deletions

View File

@ -177,6 +177,9 @@
"noShuttles": "No tourist routes available at the moment.", "noShuttles": "No tourist routes available at the moment.",
"filterRoute": "Filter by route", "filterRoute": "Filter by route",
"allRoutes": "All routes", "allRoutes": "All routes",
"category": "Category",
"local": "Local",
"interprovincial": "Interprovincial",
"tripType": "Trip type", "tripType": "Trip type",
"oneWay": "One way", "oneWay": "One way",
"roundTrip": "Round trip", "roundTrip": "Round trip",

View File

@ -179,6 +179,9 @@
"noShuttles": "No hay rutas turísticas disponibles en este momento.", "noShuttles": "No hay rutas turísticas disponibles en este momento.",
"filterRoute": "Filtrar por ruta", "filterRoute": "Filtrar por ruta",
"allRoutes": "Todas las rutas", "allRoutes": "Todas las rutas",
"category": "Categoría",
"local": "Local",
"interprovincial": "Interprovincial",
"tripType": "Tipo de viaje", "tripType": "Tipo de viaje",
"oneWay": "Ida", "oneWay": "Ida",
"roundTrip": "Vuelta", "roundTrip": "Vuelta",

View File

@ -175,6 +175,7 @@ export interface Shuttle {
phone_number?: string phone_number?: string
english_speaking?: boolean english_speaking?: boolean
image_url?: string image_url?: string
category?: 'local' | 'interprovincial' | 'all'
is_active: boolean is_active: boolean
created_at?: string created_at?: string
updated_at?: string updated_at?: string

View File

@ -16,6 +16,8 @@ const shuttleForm = ref({
origin: 'Boquete', origin: 'Boquete',
destination: 'Santa Catalina', destination: 'Santa Catalina',
vehicle_type: 'Mini Van Compartida', vehicle_type: 'Mini Van Compartida',
category: 'local',
description: '',
price_per_person: 35, price_per_person: 35,
price_private_trip: 180, price_private_trip: 180,
estimated_duration: '4.5 horas', estimated_duration: '4.5 horas',
@ -66,6 +68,8 @@ async function saveShuttle() {
origin: shuttleForm.value.origin, origin: shuttleForm.value.origin,
destination: shuttleForm.value.destination, destination: shuttleForm.value.destination,
vehicle_type: shuttleForm.value.vehicle_type, vehicle_type: shuttleForm.value.vehicle_type,
category: shuttleForm.value.category,
description: shuttleForm.value.description,
company_name: shuttleForm.value.company_name, company_name: shuttleForm.value.company_name,
price_per_person: shuttleForm.value.price_per_person, price_per_person: shuttleForm.value.price_per_person,
price_private_trip: shuttleForm.value.price_private_trip, price_private_trip: shuttleForm.value.price_private_trip,
@ -144,9 +148,28 @@ async function saveShuttle() {
</div> </div>
</div> </div>
<div class="form-group grid-row">
<div class="input-box">
<label>Tipo de Vehículo</label>
<input v-model="shuttleForm.vehicle_type" type="text" placeholder="Mini Van Compartida">
</div>
<div class="input-box">
<label>Categoría</label>
<select v-model="shuttleForm.category" class="nexus-select">
<option value="local">Evento Local / Interno</option>
<option value="interprovincial">Interprovincial (Fuera de provincia)</option>
</select>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label>Tipo de Vehículo</label> <label>Descripción del Viaje</label>
<input v-model="shuttleForm.vehicle_type" type="text" placeholder="Mini Van Compartida"> <textarea
v-model="shuttleForm.description"
placeholder="Añade detalles sobre el itinerario, paradas, o qué incluye el viaje..."
rows="3"
class="nexus-textarea"
></textarea>
</div> </div>
<div class="form-group grid-row"> <div class="form-group grid-row">
@ -276,6 +299,11 @@ async function saveShuttle() {
</div> </div>
</div> </div>
<!-- NUEVA DESCRIPCIÓN EN PREVIEW -->
<div class="shuttle-description-preview" v-if="shuttleForm.description">
<p>{{ shuttleForm.description }}</p>
</div>
<!-- Precios prominentes --> <!-- Precios prominentes -->
<div class="price-block"> <div class="price-block">
<div class="price-row-main"> <div class="price-row-main">
@ -339,6 +367,7 @@ async function saveShuttle() {
font-weight: 800; font-weight: 800;
margin: 0; margin: 0;
background: linear-gradient(135deg, #fff 0%, #94a3b8 100%); background: linear-gradient(135deg, #fff 0%, #94a3b8 100%);
background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
@ -409,7 +438,7 @@ async function saveShuttle() {
color: #94a3b8; color: #94a3b8;
} }
.form-group input { .form-group input, .nexus-select, .nexus-textarea {
background: rgba(15, 23, 42, 0.5); background: rgba(15, 23, 42, 0.5);
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.1);
padding: 14px 16px; padding: 14px 16px;
@ -420,11 +449,34 @@ async function saveShuttle() {
transition: all 0.3s; transition: all 0.3s;
} }
.form-group input:focus { .nexus-select {
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='white'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1rem center;
background-size: 1.5em;
padding-right: 3rem;
}
.nexus-textarea {
resize: vertical;
min-height: 100px;
}
.form-group input:focus, .nexus-select:focus, .nexus-textarea:focus {
border-color: #fee715; border-color: #fee715;
box-shadow: 0 0 0 4px rgba(254, 231, 21, 0.1); box-shadow: 0 0 0 4px rgba(254, 231, 21, 0.1);
} }
.shuttle-description-preview {
padding: 0 20px 14px;
color: rgba(255,255,255,0.7);
font-size: 0.85rem;
line-height: 1.5;
white-space: pre-wrap;
}
.file-upload-wrapper { .file-upload-wrapper {
position: relative; position: relative;
width: 100%; width: 100%;

View File

@ -142,7 +142,7 @@ function resetFilters() {
</header> </header>
<!-- CHIPS DE CATEGORÍA --> <!-- CHIPS DE CATEGORÍA -->
<div class="cat-chips-wrap"> <div class="cat-chips-wrap glass-effect">
<div class="cat-chips-scroll"> <div class="cat-chips-scroll">
<button <button
v-for="cat in categories" v-for="cat in categories"
@ -151,7 +151,10 @@ function resetFilters() {
:class="{ 'cat-chip--active': selectedCategory === cat }" :class="{ 'cat-chip--active': selectedCategory === cat }"
@click="selectedCategory = cat" @click="selectedCategory = cat"
> >
{{ catEmoji(cat) }} {{ catName(cat) }} <span class="cat-chip-inner">
<span class="cat-emoji">{{ catEmoji(cat) }}</span>
<span class="cat-text">{{ catName(cat) }}</span>
</span>
</button> </button>
</div> </div>
</div> </div>
@ -408,8 +411,9 @@ function resetFilters() {
background: var(--bg-primary); background: var(--bg-primary);
border: 1.5px solid var(--border-color); border: 1.5px solid var(--border-color);
border-radius: 0.875rem; border-radius: 0.875rem;
padding: 0.75rem 1rem; padding: 0.75rem 1.125rem;
transition: border-color 0.2s; transition: all 0.3s ease;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
} }
.search-wrap:focus-within { border-color: var(--active-color); } .search-wrap:focus-within { border-color: var(--active-color); }
@ -426,7 +430,12 @@ function resetFilters() {
outline: none; outline: none;
} }
.search-input::placeholder { color: var(--text-secondary); } .search-input::placeholder { color: var(--text-secondary); opacity: 0.7; }
.search-wrap:focus-within .search-icon {
transform: scale(1.1);
color: var(--active-color);
}
.search-clear { .search-clear {
background: none; background: none;
@ -445,11 +454,13 @@ function resetFilters() {
═══════════════════════════════════════════ */ ═══════════════════════════════════════════ */
.cat-chips-wrap { .cat-chips-wrap {
background: var(--bg-secondary); background: var(--bg-secondary);
padding: 0.75rem 0; padding: 0.875rem 0;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 10; z-index: 100;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
} }
.cat-chips-scroll { .cat-chips-scroll {
@ -465,27 +476,61 @@ function resetFilters() {
.cat-chip { .cat-chip {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 0.25rem; padding: 0.125rem;
padding: 0.5rem 0.875rem;
border-radius: 99px; border-radius: 99px;
border: 1.5px solid var(--border-color); border: 1px solid var(--border-color);
background: var(--bg-primary); background: var(--bg-primary);
color: var(--text-secondary); color: var(--text-secondary);
font-size: 0.8125rem;
font-weight: 700;
font-family: inherit; font-family: inherit;
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
flex-shrink: 0; flex-shrink: 0;
transition: all 0.18s; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
} }
.cat-chip:hover { border-color: var(--active-color); color: var(--text-primary); } .cat-chip-inner {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem 0.5rem 0.625rem;
}
.cat-emoji {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
background: var(--bg-secondary);
border-radius: 50%;
font-size: 1rem;
transition: transform 0.3s ease;
}
.cat-text {
font-size: 0.875rem;
font-weight: 700;
}
.cat-chip:hover {
border-color: var(--active-color);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.cat-chip:hover .cat-emoji {
transform: scale(1.1) rotate(5deg);
}
.cat-chip--active { .cat-chip--active {
background: var(--active-color); background: var(--active-color);
border-color: var(--active-color); border-color: var(--active-color);
color: #101820; color: #101820;
box-shadow: 0 4px 15px rgba(254, 231, 21, 0.3);
}
.cat-chip--active .cat-emoji {
background: rgba(0, 0, 0, 0.1);
} }
/* ═══════════════════════════════════════════ /* ═══════════════════════════════════════════
@ -654,8 +699,9 @@ function resetFilters() {
} }
.biz-card:hover { .biz-card:hover {
transform: translateY(-4px); transform: translateY(-6px) scale(1.02);
border-color: var(--active-color); border-color: var(--active-color);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
} }
.biz-img-wrap { .biz-img-wrap {

View File

@ -100,6 +100,8 @@ onMounted(async () => {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
width: 100%; width: 100%;
max-width: 400px; max-width: 400px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(8px);
} }
.hub-tab { .hub-tab {
@ -118,6 +120,30 @@ onMounted(async () => {
justify-content: center; justify-content: center;
gap: 8px; gap: 8px;
text-decoration: none; text-decoration: none;
overflow: hidden;
position: relative;
}
.hub-tab::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
background: white;
border-radius: 50%;
opacity: 0;
transform: translate(-50%, -50%) scale(0);
transition: all 0.5s ease;
}
.hub-tab:active::after {
width: 200%;
height: 200%;
opacity: 0.1;
transform: translate(-50%, -50%) scale(1);
transition: 0s;
} }
.hub-tab.active { .hub-tab.active {
@ -135,9 +161,9 @@ onMounted(async () => {
width: calc(50% - 6px); width: calc(50% - 6px);
background: #FEE715; background: #FEE715;
border-radius: 12px; border-radius: 12px;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
z-index: 1; z-index: 1;
box-shadow: 0 4px 15px rgba(254, 231, 21, 0.4); box-shadow: 0 4px 20px rgba(254, 231, 21, 0.4);
} }
.fade-enter-active, .fade-enter-active,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff