<template>
<section class="page">
<GnPageHeader title="Favorites" kicker="Areas">
<template #actions>
<GnBadge variant="primary">{{ favoriteAreas.length }} favorite areas</GnBadge>
</template>
</GnPageHeader>
<AppLoadingState v-if="areasStore.isLoading" text="Loading areas" />
<AppErrorState
v-else-if="areasStore.error"
title="Areas loading failed"
:message="areasStore.error.message"
:retry="areasStore.loadAreas"
/>
<AppEmptyState
v-else-if="favoriteAreas.length === 0"
title="No favorite areas"
message="Favorite areas from the current client are preserved through localStorage."
/>
<ul v-else class="area-favorites-list">
<li
v-for="area in favoriteAreas"
:key="area.id"
class="area-favorite-item"
>
<article class="area-favorite-card" @click="goToDetail(area)">
<div class="area-favorite-icon">
<i class="ph ph-map-trifold" />
</div>
<div class="area-favorite-info">
<h2 class="area-favorite-title">{{ area.display_name }}</h2>
<p class="area-favorite-meta">
<GnBadge variant="secondary">{{ area.type }}</GnBadge>
<code>{{ area.alias }}</code>
<span v-if="parentName(area)" class="area-favorite-parent">
in <router-link
:to="{ name: 'area-detail', params: { id: String(area.parent_id) } }"
class="parent-link"
@click.stop
>
{{ parentName(area) }}
</router-link>
</span>
</p>
</div>
<div class="area-favorite-actions">
<button
class="btn-icon area-favorite-star"
:class="{ 'is-active': true }"
type="button"
:aria-label="'Unstar ' + area.display_name"
@click.stop="favoritesStore.remove(area.id)"
>
<i class="ph ph-star" />
</button>
</div>
</article>
</li>
</ul>
</section>
</template>
<script setup>
import { computed } from "vue";
import { useRouter } from "vue-router";
import { useAreasStore } from "../../../stores/areas";
import { useFavoritesStore } from "../../../stores/favorites";
import { GnPageHeader, GnBadge } from "gnexus-ui-kit/vue";
import AppLoadingState from "../../../components/feedback/AppLoadingState.vue";
import AppErrorState from "../../../components/feedback/AppErrorState.vue";
import AppEmptyState from "../../../components/feedback/AppEmptyState.vue";
const areasStore = useAreasStore();
const favoritesStore = useFavoritesStore();
const router = useRouter();
const favoriteAreas = computed(() => {
const favoriteIds = new Set(favoritesStore.ids.map(String));
return areasStore.areas.filter((area) => favoriteIds.has(String(area.id)));
});
function parentName(area) {
if (!area.parent_id) return null;
const parent = areasStore.areasById[String(area.parent_id)];
return parent?.display_name || null;
}
function goToDetail(area) {
router.push({ name: "area-detail", params: { id: String(area.id) } });
}
</script>
<style scoped>
.area-favorites-list {
display: grid;
gap: 12px;
margin: 0;
padding: 0;
list-style: none;
}
.area-favorite-card {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 14px;
padding: 14px 16px;
border: 1px solid rgba(192, 202, 245, 0.12);
background: var(--color-panel);
cursor: pointer;
transition: background 0.15s ease;
}
.area-favorite-card:hover {
background: var(--color-panel-strong);
}
.area-favorite-icon {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border: 2px solid var(--color-primary);
color: var(--color-primary);
font-size: 20px;
}
.area-favorite-info {
min-width: 0;
}
.area-favorite-title {
margin: 0 0 6px;
font-size: 18px;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.area-favorite-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 0;
color: var(--color-muted);
font-size: 13px;
}
.area-favorite-parent {
color: var(--color-muted);
}
.area-favorite-parent .parent-link {
color: var(--color-primary);
text-decoration: none;
}
.area-favorite-parent .parent-link:hover {
text-decoration: underline;
}
.area-favorite-star {
width: 40px;
height: 40px;
color: var(--color-warning);
}
.area-favorite-star:hover {
color: var(--color-danger);
}
.area-favorite-star .ph {
font-size: 22px;
}
@media (max-width: 720px) {
.area-favorite-card {
grid-template-columns: auto 1fr;
gap: 12px;
}
.area-favorite-actions {
grid-column: 1 / -1;
justify-self: end;
}
}
</style>