cleanup rules
This commit is contained in:
+2
-2
@@ -367,12 +367,12 @@ components:
|
|||||||
RuleResponse:
|
RuleResponse:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
rules:
|
clauses:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/RuleClauseResponse"
|
$ref: "#/components/schemas/RuleClauseResponse"
|
||||||
required:
|
required:
|
||||||
- rules
|
- clauses
|
||||||
UpdateRuleBookRequest:
|
UpdateRuleBookRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@@ -1,22 +1,37 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline';
|
import {ChevronDownIcon, ChevronUpIcon} from '@heroicons/vue/24/outline';
|
||||||
import { vOnClickOutside } from '@vueuse/components';
|
import {vOnClickOutside} from '@vueuse/components';
|
||||||
import { useEventListener } from '@vueuse/core';
|
import {useEventListener} from '@vueuse/core';
|
||||||
import { ref } from 'vue';
|
import {ref} from 'vue';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
inline?: boolean;
|
||||||
|
autoClose: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
inline: false,
|
||||||
|
autoClose: true
|
||||||
|
})
|
||||||
|
|
||||||
const isOpen = ref(false);
|
const isOpen = ref(false);
|
||||||
|
|
||||||
|
const doAutoClose = () => {
|
||||||
|
if (props.autoClose) {
|
||||||
|
isOpen.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEventListener('keyup', e => {
|
useEventListener('keyup', e => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
isOpen.value = false;
|
doAutoClose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="dropdown" :class="{'dropdown-open': isOpen, 'dropdown-close': !isOpen}" v-on-click-outside="() => isOpen = false">
|
<div class="dropdown" :class="{'dropdown-open': isOpen, 'dropdown-close': !isOpen}" v-on-click-outside="doAutoClose">
|
||||||
<button @click="isOpen = !isOpen">
|
<button @click="isOpen = !isOpen">
|
||||||
<slot name="button" />
|
|
||||||
<Transition
|
<Transition
|
||||||
enter-active-class="transition-transform"
|
enter-active-class="transition-transform"
|
||||||
enter-from-class="rotate-180"
|
enter-from-class="rotate-180"
|
||||||
@@ -25,6 +40,7 @@ useEventListener('keyup', e => {
|
|||||||
<ChevronDownIcon v-if="!isOpen" class="chevron" />
|
<ChevronDownIcon v-if="!isOpen" class="chevron" />
|
||||||
<ChevronUpIcon v-else class="chevron" />
|
<ChevronUpIcon v-else class="chevron" />
|
||||||
</Transition>
|
</Transition>
|
||||||
|
<slot name="button" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
@@ -32,7 +48,10 @@ useEventListener('keyup', e => {
|
|||||||
enter-from-class="opacity-0"
|
enter-from-class="opacity-0"
|
||||||
leave-from-class="transition-opacity"
|
leave-from-class="transition-opacity"
|
||||||
leave-to-class="opacity-0">
|
leave-to-class="opacity-0">
|
||||||
<div v-if="isOpen" class="relative">
|
<div v-if="inline && isOpen">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="isOpen" class="relative">
|
||||||
<div class="z-10 divide-y rounded-b-md absolute">
|
<div class="z-10 divide-y rounded-b-md absolute">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
@@ -45,6 +64,6 @@ useEventListener('keyup', e => {
|
|||||||
@reference "tailwindcss";
|
@reference "tailwindcss";
|
||||||
|
|
||||||
.chevron {
|
.chevron {
|
||||||
@apply w-4 h-4 ms-1;
|
@apply w-4 h-4 me-1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const RuleClauseResponseRateEnum = {
|
|||||||
export type RuleClauseResponseRateEnum = typeof RuleClauseResponseRateEnum[keyof typeof RuleClauseResponseRateEnum];
|
export type RuleClauseResponseRateEnum = typeof RuleClauseResponseRateEnum[keyof typeof RuleClauseResponseRateEnum];
|
||||||
|
|
||||||
export interface RuleResponse {
|
export interface RuleResponse {
|
||||||
'rules': Array<RuleClauseResponse>;
|
'clauses': Array<RuleClauseResponse>;
|
||||||
}
|
}
|
||||||
export interface SetCharacterRuleBookRequest {
|
export interface SetCharacterRuleBookRequest {
|
||||||
'ruleBookId': string;
|
'ruleBookId': string;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import log from "loglevel";
|
|||||||
import {activityTypes, RuleInput, Rules, useRuleBooksStore} from "@/rules";
|
import {activityTypes, RuleInput, Rules, useRuleBooksStore} from "@/rules";
|
||||||
import {PlusIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
import {PlusIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
||||||
import {routeNames} from "@/routes";
|
import {routeNames} from "@/routes";
|
||||||
|
import {Dropdown} from "@/components";
|
||||||
|
|
||||||
const ruleBookId = ref<string>();
|
const ruleBookId = ref<string>();
|
||||||
const name = ref<string>('');
|
const name = ref<string>('');
|
||||||
@@ -89,8 +90,13 @@ watch(useRoute(), async route => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col grow border-b-1" v-for="activityType in activityTypes" :key="activityType.key">
|
<div class="flex flex-col grow border-b-1" v-for="activityType in activityTypes" :key="activityType.key">
|
||||||
|
<Dropdown :inline="true" :autoClose="false" class="rule-dropdown">
|
||||||
|
<template #button>
|
||||||
<span>{{ activityType.name }}</span>
|
<span>{{ activityType.name }}</span>
|
||||||
|
</template>
|
||||||
<RuleInput :ledgerRefs="ledgerRefs" v-model="rules[activityType.key]" />
|
<RuleInput :ledgerRefs="ledgerRefs" v-model="rules[activityType.key]" />
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 justify-end flex">
|
<div class="mt-2 justify-end flex">
|
||||||
@@ -100,3 +106,15 @@ watch(useRoute(), async route => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@reference "tailwindcss";
|
||||||
|
|
||||||
|
.rule-dropdown :deep(>button) {
|
||||||
|
@apply bg-slate-800 hover:bg-slate-800 border-none flex items-center w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-dropdown.dropdown-open :deep(>button) {
|
||||||
|
@apply bg-slate-800 rounded-b-none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+22
-17
@@ -5,6 +5,7 @@ import RuleClauseInput from "@/rules/RuleClauseInput.vue";
|
|||||||
import {computed, useTemplateRef} from "vue";
|
import {computed, useTemplateRef} from "vue";
|
||||||
import {Bars4Icon, PlusIcon, TrashIcon} from '@heroicons/vue/24/outline';
|
import {Bars4Icon, PlusIcon, TrashIcon} from '@heroicons/vue/24/outline';
|
||||||
import {useSortable} from "@vueuse/integrations/useSortable";
|
import {useSortable} from "@vueuse/integrations/useSortable";
|
||||||
|
import {systemLedgerRef} from "@/ledger";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
ledgerRefs: string[];
|
ledgerRefs: string[];
|
||||||
@@ -12,49 +13,54 @@ interface Props {
|
|||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const rule = defineModel<RuleResponse>({default: {rules:{}}});
|
const rule = defineModel<RuleResponse>({default: {clauses:[]}});
|
||||||
const rules = computed<RuleClauseResponse[]>({
|
const clauses = computed<RuleClauseResponse[]>({
|
||||||
get: () => rule.value && rule.value.rules ? rule.value.rules : [],
|
get: () => rule.value && rule.value.clauses ? rule.value.clauses : [],
|
||||||
set: value => rule.value = {rules: value}
|
set: value => rule.value = {clauses: value}
|
||||||
})
|
})
|
||||||
|
|
||||||
const addRule = () => {
|
const addClause = () => {
|
||||||
rules.value = [...rules.value, {rate: RuleClauseResponseRateEnum.None}]
|
clauses.value = [...clauses.value, {
|
||||||
|
rate: RuleClauseResponseRateEnum.None,
|
||||||
|
fromLedgerRef: systemLedgerRef,
|
||||||
|
toLedgerRef: systemLedgerRef
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
const setRule = (index: number, rule?: RuleClauseResponse) => {
|
const setClause = (index: number, clause?: RuleClauseResponse) => {
|
||||||
if (!rule) {
|
if (!clause) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rules.value = rules.value.with(index, rule)
|
clauses.value = clauses.value.with(index, clause)
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeRule = (index: number) => {
|
const removeClause = (index: number) => {
|
||||||
rules.value = rules.value.toSpliced(index, 1)
|
clauses.value = clauses.value.toSpliced(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortableContainer = useTemplateRef('sortable-container')
|
const sortableContainer = useTemplateRef('sortable-container')
|
||||||
useSortable(sortableContainer, rules, { handle: '.sortable-handle'});
|
useSortable(sortableContainer, clauses, { handle: '.sortable-handle'});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div ref="sortable-container" class="flex-col">
|
<div ref="sortable-container" class="flex-col">
|
||||||
<div class="flex items-end gap-2 mt-2" v-for="(rule, index) in rules" :key="index">
|
<div class="flex items-end gap-2 mt-2" v-for="(clause, index) in clauses" :key="index">
|
||||||
<span class="sortable-handle flex">
|
<span class="sortable-handle flex">
|
||||||
<Bars4Icon class="w-6"/>
|
<Bars4Icon class="w-6"/>
|
||||||
</span>
|
</span>
|
||||||
<RuleClauseInput :ledgerRefs="ledgerRefs" :modelValue="rule" @update:modelValue="v => setRule(index, v)" />
|
<RuleClauseInput :ledgerRefs="ledgerRefs" :modelValue="clause" @update:modelValue="v => setClause(index, v)" />
|
||||||
<button class="btn-icon" @click="removeRule(index)"><TrashIcon /></button>
|
<button class="btn-icon" @click="removeClause(index)"><TrashIcon /></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end mb-2 mt-2">
|
<div class="flex justify-end mb-2 mt-2">
|
||||||
<button class="btn-icon" @click="addRule"><PlusIcon /></button>
|
<button class="btn-icon" @click="addClause"><PlusIcon /></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@reference "tailwindcss";
|
||||||
|
|
||||||
.sortable-handle {
|
.sortable-handle {
|
||||||
@apply cursor-grab;
|
@apply cursor-grab;
|
||||||
@@ -67,5 +73,4 @@ useSortable(sortableContainer, rules, { handle: '.sortable-handle'});
|
|||||||
.sortable-chosen .sortable-handle {
|
.sortable-chosen .sortable-handle {
|
||||||
@apply cursor-grabbing;
|
@apply cursor-grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
import {characterRuleBookApi, ledgerApi, ruleBookApi} from "@/mammon";
|
import {characterRuleBookApi, ruleBookApi} from "@/mammon";
|
||||||
import {
|
import {
|
||||||
CharacterRuleBookResponse,
|
CharacterRuleBookResponse,
|
||||||
CreateRuleBookRequest,
|
CreateRuleBookRequest,
|
||||||
@@ -13,6 +13,8 @@ import {ref, triggerRef} from "vue";
|
|||||||
export const activityTypes = {
|
export const activityTypes = {
|
||||||
itemBought: {key: "ITEM_BOUGHT", name: "Item Bought"},
|
itemBought: {key: "ITEM_BOUGHT", name: "Item Bought"},
|
||||||
itemSold: {key: "ITEM_SOLD", name: "Item Sold"},
|
itemSold: {key: "ITEM_SOLD", name: "Item Sold"},
|
||||||
|
itemAcquiredManually: {key: "ITEM_ACQUIRED_MANUALLY", name: "Item Acquired Manually"},
|
||||||
|
itemConsumedManually: {key: "ITEM_CONSUME_MANUALLY", name: "Item Consumed Manually"},
|
||||||
// bountyEarned: {id: "BOUNTY_EARNED", name: "Bounty Earned"},
|
// bountyEarned: {id: "BOUNTY_EARNED", name: "Bounty Earned"},
|
||||||
// itemManufactured: {id: "ITEM_MANUFACTURED", name: "Item Manufactured"}
|
// itemManufactured: {id: "ITEM_MANUFACTURED", name: "Item Manufactured"}
|
||||||
} as const;
|
} as const;
|
||||||
@@ -53,7 +55,7 @@ export const useRuleBooksStore = defineStore('rule-books', () => {
|
|||||||
|
|
||||||
const findById = (ruleBookId: string): RuleBook | undefined => ruleBooks.value.find(rb => rb.ruleBookId === ruleBookId);
|
const findById = (ruleBookId: string): RuleBook | undefined => ruleBooks.value.find(rb => rb.ruleBookId === ruleBookId);
|
||||||
const create = (ruleBook: CreateRuleBookRequest) => ruleBookApi.createRuleBook(ruleBook).then(response => addRuleBook(response.data));
|
const create = (ruleBook: CreateRuleBookRequest) => ruleBookApi.createRuleBook(ruleBook).then(response => addRuleBook(response.data));
|
||||||
const update = (ruleBookId: string, ruleBook: CreateRuleBookRequest) => ledgerApi.updateMainLedger(ruleBookId, ruleBook).then(response => replaceRuleBook(response.data));
|
const update = (ruleBookId: string, ruleBook: CreateRuleBookRequest) => ruleBookApi.updateRuleBook(ruleBookId, ruleBook).then(response => replaceRuleBook(response.data));
|
||||||
|
|
||||||
const refresh = () => ruleBookApi.findAllRuleBooks().then(response => ruleBooks.value = response.data as RuleBook[]);
|
const refresh = () => ruleBookApi.findAllRuleBooks().then(response => ruleBooks.value = response.data as RuleBook[]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user