Files
pokemon-inventory/apps/web/src/pages/AdminSettingsPage.vue
T
2026-09-10 07:40:18 -04:00

97 lines
4.0 KiB
Vue

<script setup>
import { onMounted, reactive, ref } from 'vue';
import { getAdminSettings, updateAdminSettings } from '@/services/admin-settings-service.js';
const settings = reactive({
general: { publicDescription: '', publicContactEmail: '', publicContactPhone: '' },
inventory: { maxImagesPerItem: 10 },
pricing: { defaultStrategy: 'median' },
security: { sessionIdleMinutes: 60 },
});
const isLoading = ref(true);
const isSaving = ref(false);
const message = ref('');
const error = ref('');
function csrfToken() {
return document.cookie.split('; ').find((value) => value.startsWith('pokemon_inventory_csrf='))?.split('=')[1] ?? '';
}
async function load() {
try {
Object.assign(settings, await getAdminSettings());
} catch (requestError) {
error.value = requestError.message;
} finally {
isLoading.value = false;
}
}
async function save() {
isSaving.value = true;
message.value = '';
error.value = '';
try {
const payload = {
...settings,
general: {
...settings.general,
publicContactEmail: settings.general.publicContactEmail || undefined,
publicContactPhone: settings.general.publicContactPhone || undefined,
},
};
Object.assign(settings, await updateAdminSettings(payload, csrfToken()));
message.value = 'Settings saved.';
} catch (requestError) {
error.value = requestError.message;
} finally {
isSaving.value = false;
}
}
onMounted(load);
</script>
<template>
<section class="admin-page">
<p class="catalog-kicker">Configuration</p>
<h1>Settings</h1>
<p v-if="isLoading" class="state state--loading" role="status">Loading settings...</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="admin-workspace-grid" @submit.prevent="save">
<fieldset class="showcase-panel">
<legend>General</legend>
<label class="fixture-label" for="settings-description">Public seller description (optional)</label>
<p id="settings-description-help">Shown on public listing and product pages. Up to 2,000 characters. Clear to remove.</p>
<textarea id="settings-description" v-model="settings.general.publicDescription" class="fixture-input" rows="5" maxlength="2000" aria-describedby="settings-description-help" />
<label class="fixture-label" for="settings-email">Public contact email</label>
<input id="settings-email" v-model="settings.general.publicContactEmail" class="fixture-input" type="email" />
<label class="fixture-label" for="settings-phone">Public contact phone</label>
<input id="settings-phone" v-model="settings.general.publicContactPhone" class="fixture-input" />
</fieldset>
<fieldset class="showcase-panel">
<legend>Inventory</legend>
<label class="fixture-label" for="settings-images">Maximum images per item</label>
<input id="settings-images" v-model.number="settings.inventory.maxImagesPerItem" class="fixture-input" type="number" min="1" max="100" />
</fieldset>
<fieldset class="showcase-panel">
<legend>Pricing</legend>
<label class="fixture-label" for="settings-strategy">Default valuation strategy</label>
<select id="settings-strategy" v-model="settings.pricing.defaultStrategy" class="fixture-input">
<option value="median">Median</option>
<option value="average">Average</option>
<option value="weighted_average">Weighted average</option>
</select>
</fieldset>
<fieldset class="showcase-panel">
<legend>Security</legend>
<label class="fixture-label" for="settings-idle">Session idle minutes</label>
<input id="settings-idle" v-model.number="settings.security.sessionIdleMinutes" class="fixture-input" type="number" min="5" max="43200" />
</fieldset>
<button class="button button--primary" type="submit" :disabled="isSaving">Save settings</button>
</form>
</section>
</template>