The Add RSS modal is now opened from a button directly above the feed list in AdminFeeds.vue instead of the hamburger menu, so feed creation lives alongside the rest of feed management. AppNav.vue no longer renders it. AddUrl.vue emits an `added` event on a successful save; AdminFeeds.vue listens for it and reloads its feed list, so a newly added feed shows up immediately without leaving the admin page. Also fixes a bug this move surfaced: the modal's "Close" button had no `type`, so inside the form it defaulted to type="submit" — clicking Close on a filled-in form silently created the feed.
163 lines
3.5 KiB
Vue
163 lines
3.5 KiB
Vue
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import axios from 'axios'
|
|
import Modal from './modal/AddUrl.vue'
|
|
|
|
const showModal = ref(false)
|
|
const feeds = ref([])
|
|
const error = ref('')
|
|
|
|
function authHeaders() {
|
|
return {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'user-token': localStorage.getItem('user-token'),
|
|
},
|
|
}
|
|
}
|
|
|
|
async function loadFeeds() {
|
|
const userId = localStorage.getItem('user-id')
|
|
try {
|
|
const response = await axios.get(`/api/v1/article/feeds/${userId}`, authHeaders())
|
|
feeds.value = response.data.feeds
|
|
} catch (e) {
|
|
error.value = 'Failed to load feeds.'
|
|
}
|
|
}
|
|
|
|
async function deleteFeed(feedId) {
|
|
if (!window.confirm('Delete this feed and all its articles?')) return
|
|
try {
|
|
await axios.delete(`/api/v1/article/feed/${feedId}`, authHeaders())
|
|
feeds.value = feeds.value.filter(f => f.id !== feedId)
|
|
} catch (e) {
|
|
error.value = 'Failed to delete feed.'
|
|
}
|
|
}
|
|
|
|
onMounted(loadFeeds)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="admin">
|
|
<h1 class="admin__heading">Admin</h1>
|
|
<button class="admin__add" type="button" @click="showModal = true">Add RSS</button>
|
|
<p v-if="error" class="admin__error">{{ error }}</p>
|
|
<p v-else-if="feeds.length === 0" class="admin__empty">No feeds added yet.</p>
|
|
<ul v-else class="admin__list">
|
|
<li v-for="feed in feeds" :key="feed.id" class="admin__item">
|
|
<div class="admin__item-info">
|
|
<span class="admin__item-title">{{ feed.title }}</span>
|
|
<a :href="feed.url" class="admin__item-url" target="_blank" rel="noopener">{{ feed.url }}</a>
|
|
</div>
|
|
<button class="admin__delete" type="button" @click="deleteFeed(feed.id)">Delete</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<Teleport to="body">
|
|
<Modal :show="showModal" @close="showModal = false" @added="loadFeeds">
|
|
<template #header>
|
|
<h3>Add RSS Feed</h3>
|
|
</template>
|
|
</Modal>
|
|
</Teleport>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.admin {
|
|
padding: 1.5rem 1rem;
|
|
max-width: 720px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.admin__heading {
|
|
font-size: 1.25rem;
|
|
font-weight: bold;
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.admin__add {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 44px;
|
|
margin-bottom: 1.25rem;
|
|
padding: 0.5rem 1.1rem;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
color: var(--color-text);
|
|
font: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.admin__add:hover {
|
|
border-color: var(--color-border-hover);
|
|
}
|
|
|
|
.admin__error,
|
|
.admin__empty {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.admin__list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.admin__item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
padding: 0.75rem 1rem;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 6px;
|
|
background: var(--color-background-soft);
|
|
}
|
|
|
|
.admin__item-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.2rem;
|
|
min-width: 0;
|
|
}
|
|
|
|
.admin__item-title {
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.admin__item-url {
|
|
font-size: 0.8rem;
|
|
opacity: 0.6;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
color: inherit;
|
|
}
|
|
|
|
.admin__delete {
|
|
flex-shrink: 0;
|
|
min-height: 36px;
|
|
padding: 0.3rem 0.9rem;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
color: var(--color-text);
|
|
font: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.admin__delete:hover {
|
|
border-color: var(--color-border-hover);
|
|
}
|
|
</style>
|