feat: add seller saved views
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-10 01:36:15 -04:00
parent ec322df25c
commit ffd239d27c
19 changed files with 364 additions and 0 deletions
@@ -0,0 +1,5 @@
import AdminSavedViewsPage from './AdminSavedViewsPage.vue';
export default { title: 'Pages/AdminSavedViewsPage', component: AdminSavedViewsPage };
export const SavedViews = {};
@@ -0,0 +1,86 @@
<script setup>
import { onMounted, ref } from 'vue';
import { createAdminSavedView, deleteAdminSavedView, listAdminSavedViews } from '@/services/admin-saved-view-service.js';
const views = ref([]);
const name = ref('');
const filters = ref('{}');
const isLoading = ref(true);
const isSaving = ref(false);
const error = ref('');
const message = ref('');
function csrfToken() {
return document.cookie.split('; ').find((value) => value.startsWith('pokemon_inventory_csrf='))?.split('=')[1] ?? '';
}
async function load() {
try {
views.value = await listAdminSavedViews();
} catch (requestError) {
error.value = requestError.message;
} finally {
isLoading.value = false;
}
}
async function createView() {
isSaving.value = true;
error.value = '';
message.value = '';
try {
const view = await createAdminSavedView({ name: name.value, filters: JSON.parse(filters.value) }, csrfToken());
views.value.push(view);
name.value = '';
filters.value = '{}';
message.value = 'Saved view created.';
} catch (requestError) {
error.value = requestError instanceof SyntaxError ? 'Filters must be valid JSON.' : requestError.message;
} finally {
isSaving.value = false;
}
}
async function removeView(id) {
isSaving.value = true;
try {
await deleteAdminSavedView(id, csrfToken());
views.value = views.value.filter((view) => view.id !== id);
} catch (requestError) {
error.value = requestError.message;
} finally {
isSaving.value = false;
}
}
onMounted(load);
</script>
<template>
<section class="admin-page">
<p class="catalog-kicker">Inventory management</p>
<h1>Saved views</h1>
<p v-if="isLoading" class="state state--loading" role="status">Loading saved views...</p>
<p v-if="message" class="state" role="status">{{ message }}</p>
<p v-if="error" class="state state--error" role="alert">{{ error }}</p>
<form v-if="!isLoading" class="showcase-panel" @submit.prevent="createView">
<h2>New saved view</h2>
<label class="fixture-label" for="saved-view-name">Name</label>
<input id="saved-view-name" v-model="name" class="fixture-input" required maxlength="100" />
<label class="fixture-label" for="saved-view-filters">Filters (JSON)</label>
<textarea id="saved-view-filters" v-model="filters" class="fixture-input" rows="4" required />
<button class="button button--primary" type="submit" :disabled="isSaving">Save view</button>
</form>
<section class="admin-list" aria-labelledby="saved-views-title">
<h2 id="saved-views-title">Your saved views</h2>
<p v-if="!views.length && !isLoading" class="state state--empty">No saved views yet.</p>
<ul v-else>
<li v-for="view in views" :key="view.id">
<strong>{{ view.name }}</strong> · {{ JSON.stringify(view.filters) }}
<button class="button button--secondary" type="button" :disabled="isSaving" @click="removeView(view.id)">Delete</button>
</li>
</ul>
</section>
</section>
</template>
+1
View File
@@ -20,6 +20,7 @@ async function logout() {
<nav aria-label="Admin navigation">
<a href="/admin">Overview</a>
<RouterLink :to="{ name: 'admin-settings' }">Settings</RouterLink>
<RouterLink :to="{ name: 'admin-saved-views' }">Saved views</RouterLink>
</nav>
<div class="admin-sidebar__footer">
<span>{{ principal?.email }}</span>