|
|
|
@@ -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>
|