mirror of
https://github.com/davegallant/rfd-fyi.git
synced 2026-03-03 09:36:35 +00:00
Add refresh button
This commit is contained in:
55
src/App.vue
55
src/App.vue
@@ -17,6 +17,7 @@ export default {
|
|||||||
currentTheme: "auto",
|
currentTheme: "auto",
|
||||||
darkModeQuery: null,
|
darkModeQuery: null,
|
||||||
themeChangeHandler: null,
|
themeChangeHandler: null,
|
||||||
|
isLoading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -176,13 +177,21 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
fetchDeals() {
|
fetchDeals() {
|
||||||
axios
|
this.isLoading = true;
|
||||||
.get("api/v1/topics")
|
const minLoadingTime = new Promise(resolve => setTimeout(resolve, 500));
|
||||||
.then((response) => {
|
|
||||||
|
Promise.all([
|
||||||
|
axios.get("api/v1/topics"),
|
||||||
|
minLoadingTime
|
||||||
|
])
|
||||||
|
.then(([response]) => {
|
||||||
this.topics = response.data;
|
this.topics = response.data;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("Failed to fetch deals:", err.response || err);
|
console.error("Failed to fetch deals:", err.response || err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -216,6 +225,9 @@ export default {
|
|||||||
@keyup.enter="createFilterRoute(filter.toString())"
|
@keyup.enter="createFilterRoute(filter.toString())"
|
||||||
@keyup.esc="$refs.filter.blur()"
|
@keyup.esc="$refs.filter.blur()"
|
||||||
/>
|
/>
|
||||||
|
<button class="icon-button" title="Refresh deals" @click="fetchDeals" :disabled="isLoading">
|
||||||
|
<span class="material-symbols-outlined" :class="{ 'spinning': isLoading }">refresh</span>
|
||||||
|
</button>
|
||||||
<button class="icon-button" :title="sortTitle" @click="toggleSort">
|
<button class="icon-button" :title="sortTitle" @click="toggleSort">
|
||||||
<span class="material-symbols-outlined">{{ sortIcon }}</span>
|
<span class="material-symbols-outlined">{{ sortIcon }}</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -225,7 +237,16 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cards-grid">
|
<div v-if="isLoading && topics.length === 0" class="loading-container">
|
||||||
|
<span class="material-symbols-outlined spinning loading-spinner">refresh</span>
|
||||||
|
<p>Loading deals...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cards-wrapper" v-else>
|
||||||
|
<div v-if="isLoading" class="loading-overlay">
|
||||||
|
<span class="material-symbols-outlined spinning loading-spinner">refresh</span>
|
||||||
|
</div>
|
||||||
|
<div class="cards-grid">
|
||||||
<div v-for="topic in filteredTopics" :key="topic.topic_id" class="deal-card">
|
<div v-for="topic in filteredTopics" :key="topic.topic_id" class="deal-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="title-with-link">
|
<div class="title-with-link">
|
||||||
@@ -277,7 +298,33 @@ export default {
|
|||||||
<div class="card-timestamp">Last post: {{ formatDate(topic.last_post_time) }}</div>
|
<div class="card-timestamp">Last post: {{ formatDate(topic.last_post_time) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.cards-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(128, 128, 128, 0.3);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-overlay .loading-spinner {
|
||||||
|
font-size: 48px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -437,4 +437,49 @@ mark {
|
|||||||
.search-input {
|
.search-input {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
Loading & Spinner
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinning {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container p {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button:disabled:hover {
|
||||||
|
background-color: var(--bg-input);
|
||||||
|
border-color: var(--border-color-light);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user