Files
FabledScribe/frontend/src/components/rules/RuleEditorSlideOver.vue
T

124 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
import { useRulebooksStore } from "@/stores/rulebooks";
const props = defineProps<{ ruleId: number | null; topicId: number | null }>();
const emit = defineEmits<{ close: [] }>();
const store = useRulebooksStore();
const title = ref("");
const statement = ref("");
const why = ref("");
const howToApply = ref("");
const isCreating = ref(props.ruleId === null);
async function load() {
if (props.ruleId !== null) {
await store.fetchRule(props.ruleId);
const r = store.currentRule;
if (r) {
title.value = r.title;
statement.value = r.statement;
why.value = r.why || "";
howToApply.value = r.how_to_apply || "";
}
} else {
title.value = "";
statement.value = "";
why.value = "";
howToApply.value = "";
}
}
async function save() {
if (!title.value.trim() || !statement.value.trim()) {
emit("close");
return;
}
if (isCreating.value && props.topicId !== null) {
await store.createRule(props.topicId, {
title: title.value, statement: statement.value,
why: why.value, how_to_apply: howToApply.value,
});
} else if (props.ruleId !== null) {
await store.updateRule(props.ruleId, {
title: title.value, statement: statement.value,
why: why.value, how_to_apply: howToApply.value,
});
}
emit("close");
}
async function remove() {
if (props.ruleId === null) return;
if (!confirm("Delete this rule? This cannot be undone.")) return;
await store.deleteRule(props.ruleId);
emit("close");
}
onMounted(load);
watch(() => props.ruleId, load);
</script>
<template>
<div class="backdrop" @click="save">
<aside class="slide-over" @click.stop>
<header>
<h2>{{ isCreating ? "New rule" : "Edit rule" }}</h2>
<button v-if="!isCreating" class="trash" @click="remove" aria-label="Delete">🗑</button>
<button class="close" @click="save" aria-label="Close">×</button>
</header>
<label>
Title
<input v-model="title" placeholder="e.g. dev is home" />
</label>
<label>
Statement <span class="required">*</span>
<textarea v-model="statement" rows="3" placeholder="The actionable instruction (1-2 sentences)." />
</label>
<label>
Why
<textarea v-model="why" rows="4" placeholder="Rationale — the reason this rule exists." />
</label>
<label>
How to apply
<textarea v-model="howToApply" rows="4" placeholder="When / where this kicks in." />
</label>
</aside>
</div>
</template>
<style scoped>
.backdrop {
position: fixed; inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 100;
}
.slide-over {
position: fixed; top: 0; right: 0; bottom: 0;
width: min(520px, 90vw);
background: var(--color-surface, #18181b);
border-left: 2px solid var(--color-primary, #6366f1);
padding: 1.5rem;
overflow-y: auto;
box-shadow: -8px 0 32px rgba(0, 0, 0, 0.3);
}
header { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 1rem; }
header h2 {
flex: 1; margin: 0;
font-family: Fraunces, serif; font-style: italic;
}
label { display: block; margin-bottom: 1rem; }
.required { color: var(--color-primary, #6366f1); }
input, textarea {
width: 100%; margin-top: 0.25rem;
background: var(--color-bg, #111113); color: inherit;
border: 1px solid var(--color-border, #2a2a2e); border-radius: 6px;
padding: 0.5rem; font: inherit;
font-family: inherit;
}
.trash, .close { background: none; border: none; cursor: pointer; opacity: 0.6; font-size: 1.25em; }
.trash:hover, .close:hover { opacity: 1; }
</style>