<script setup lang="ts"> import draggable from "vuedraggable"; import { v4 } from "uuid"; import lmove from "lodash-move"; import { Field, FieldArray, useForm, useFieldArray } from "vee-validate"; import { ASpin } from "#components"; import { storySchema } from "@client/storyFormSchema"; import { FormStory, defaultChapter } from "@client/types/form/story"; import { autoEdit, autoSave, debouncedAutoEdit, debouncedAutoSave } from "@client/utils"; import findUser from "~/components/findUser.vue"; import singleChapter from "./singleChapter.vue"; import icon from "~/components/icon.vue"; const router = useRouter(); const props = defineProps<{ canDraft?: boolean; endpoint: string; endpointMethod: "put" | "post"; submitText?: string; }>(); let w; onMounted(() => { w = window; }); const dc = defaultChapter; // data: FormStory; const sdata = defineModel<FormStory>("data", { required: true, }); let drag = false; const expandos = ref<string[]>([]); function logSubmit(values, actions) { console.debug("VALUE"); console.debug(values); console.debug(actions); } const otherBtnInvoked = ref<boolean>(false); async function onSubmit(values, actions) { logSubmit(values, actions); if (props.canDraft) { if (otherBtnInvoked.value) { otherBtnInvoked.value = false; await autoSave(values); } else { const { data: dat } = await useApiFetch(`/story/new`, { method: "post", body: values, }); if (dat.success) { await router.push(`/story/${dat.story._id}/1`); } } } else { await autoEdit(values, props.endpoint, props.endpointMethod); } } function inval({ values, errors, results }) { logSubmit(values, undefined); } const { values, setFieldValue, handleSubmit } = useForm<FormStory>({ keepValuesOnUnmount: true, validationSchema: storySchema, initialValues: sdata.value, }); // const { push, remove, move, fields } = useFieldArray<FormChapter>("chapters"); const subCb = handleSubmit(onSubmit); const pushHOF = (push) => (e) => { if (!Array.isArray(values.chapters)) { // noinspection TypeScriptValidateTypes setFieldValue("chapters", []); } const chaps = [...toRaw(values.chapters)]; let lastIndex = chaps.length - 1; if (lastIndex < 0) lastIndex = 0; let lastChapter = chaps[lastIndex]; // log.debug('chaptrs->', chaps); // log.debug('lastIndex->', lastIndex, lastChapter); let newChapter = Object.assign({}, defaultChapter, { summary: lastChapter?.summary || "", index: (lastChapter?.index || 0) + 1, bands: lastChapter?.bands || [], characters: lastChapter?.characters || [], relationships: lastChapter?.relationships || [], uuidKey: v4(), genre: lastChapter?.genre || [], }); // console.debug("nc->", newChapter); // console.debug("pushi -> ", push); push(newChapter); sdata.value.chapters.push(newChapter); }; </script> <template> <form data-testid="storyform" @submit="subCb" @change="() => (canDraft ? debouncedAutoSave(values) : debouncedAutoEdit(values, endpoint, endpointMethod))"> <!-- <a-form v-bind:model="acData"> --> <Field name="title" v-slot="{ value, field, errorMessage }"> <a-form-item label="Title" :help="errorMessage" :validate-status="!!errorMessage ? 'error' : ''"> <a-input :data-testid="`storyform.${field.name}`" v-bind="field" :value="value" /> </a-form-item> </Field> <a-form-item label="Co-author (optional)"> <find-user data-testid="storyform.coauthor" :initial-option="sdata.coAuthor" fieldName="coAuthor" :multi="false" /> </a-form-item> <Field :unchecked-value="false" :value="true" type="checkbox" name="completed" v-slot="{ value, field, errorMessage }"> <a-checkbox :data-testid="`storyform.${field.name}`" v-bind="field"> Complete </a-checkbox> </Field> <a-divider /> <!-- <test1/> --> <field-array name="chapters" v-slot="{ fields, push, remove, move }"> <client-only :fallback="h(ASpin)"> <div> <div v-for="(element, index) in data.chapters"> <client-only> <Teleport :to="`#chapter-\\[${element.uuidKey}\\]`"> <a-collapse v-model:active-key="expandos" collapsible="icon"> <template #expandIcon="{ isActive }"> <span :data-testid="`storyform.chapters[${index}].collapse`"> <RightOutlined :rotate="isActive ? 90 : undefined" /></span> </template> <a-collapse-panel :key="`${element.uuidKey}`"> <template #header> <div :data-testid="`storyform.chapters[${index}].header`" style="display: flex; justify-content: space-between"> <span :data-testid="`storyform.chapters[${index}].titleEl`">{{ values.chapters[index]?.chapterTitle || "Untitled" }}</span> <a-button @click=" (e) => { // let localFields = toRaw(fields); // log.debug(`${index} | ${element.index}`); // log.debug('fields->', localFields); data.chapters.splice(index, 1); remove(index); // todo renumber // renumber() } " > <icon istyle="regular" name="trash" /> </a-button> </div> </template> <single-chapter :data="element" :name="`chapters[${index}]`" /> </a-collapse-panel> </a-collapse> </Teleport> </client-only> </div> </div> </client-only> <client-only> <template #fallback> <div style="display: flex; justify-content: center"> <a-spin /> </div> </template> <draggable :component-data="{ type: 'transtion-group', 'data-testid': 'storyform.chapters' }" @start="drag = true" @end="drag = false" v-model="values.chapters" tag="div" data-testid="storyform.chapters" item-key="uuidKey" @change=" (e) => { console.log(e); if (e.moved) { console.debug(e.moved); move(e.moved.oldIndex, e.moved.newIndex); data.chapters = lmove(data.chapters, e.moved.oldIndex, e.moved.newIndex); // w.tinymce.remove(); // log.debug(toRaw(acData.chapters.map((a) => toRaw(a)))); } } " > <template #item="{ element, index }"> <div :id="`chapter-[${element.uuidKey}]`"></div> </template> <template #footer> <a-button data-testid="storyform.addChapter" :onClick="pushHOF(push)"> Add chapter </a-button> </template> </draggable> </client-only> </field-array> <a-button type="primary" html-type="submit">{{ submitText || "Post" }}</a-button> <a-button html-type="submit" v-if="canDraft" @click="() => (otherBtnInvoked = true)"> Save for Later </a-button> </form> </template>