220 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <q-page padding>
 | 
						|
    <div class="text-h4 q-mb-md">
 | 
						|
      Create New Form
 | 
						|
    </div>
 | 
						|
 | 
						|
    <q-form
 | 
						|
      @submit.prevent="createForm"
 | 
						|
      class="q-gutter-md"
 | 
						|
    >
 | 
						|
      <q-input
 | 
						|
        outlined
 | 
						|
        v-model="form.title"
 | 
						|
        label="Form Title *"
 | 
						|
        lazy-rules
 | 
						|
        :rules="[val => val && val.length > 0 || 'Please enter a title']"
 | 
						|
      />
 | 
						|
 | 
						|
      <q-input
 | 
						|
        outlined
 | 
						|
        v-model="form.description"
 | 
						|
        label="Form Description"
 | 
						|
        type="textarea"
 | 
						|
        autogrow
 | 
						|
      />
 | 
						|
 | 
						|
      <q-separator class="q-my-lg" />
 | 
						|
 | 
						|
      <div class="text-h6 q-mb-sm">
 | 
						|
        Categories & Fields
 | 
						|
      </div>
 | 
						|
 | 
						|
      <div
 | 
						|
        v-for="(category, catIndex) in form.categories"
 | 
						|
        :key="catIndex"
 | 
						|
        class="q-mb-lg q-pa-md bordered rounded-borders"
 | 
						|
      >
 | 
						|
        <div class="row items-center q-mb-sm">
 | 
						|
          <q-input
 | 
						|
            outlined
 | 
						|
            dense
 | 
						|
            v-model="category.name"
 | 
						|
            :label="`Category ${catIndex + 1} Name *`"
 | 
						|
            class="col q-mr-sm"
 | 
						|
            lazy-rules
 | 
						|
            :rules="[val => val && val.length > 0 || 'Category name required']"
 | 
						|
          />
 | 
						|
          <q-btn
 | 
						|
            flat
 | 
						|
            round
 | 
						|
            dense
 | 
						|
            icon="delete"
 | 
						|
            color="negative"
 | 
						|
            @click="removeCategory(catIndex)"
 | 
						|
            title="Remove Category"
 | 
						|
          />
 | 
						|
        </div>
 | 
						|
 | 
						|
        <div
 | 
						|
          v-for="(field, fieldIndex) in category.fields"
 | 
						|
          :key="fieldIndex"
 | 
						|
          class="q-ml-md q-mb-sm field-item"
 | 
						|
        >
 | 
						|
          <div class="row items-center q-gutter-sm">
 | 
						|
            <q-input
 | 
						|
              outlined
 | 
						|
              dense
 | 
						|
              v-model="field.label"
 | 
						|
              label="Field Label *"
 | 
						|
              class="col"
 | 
						|
              lazy-rules
 | 
						|
              :rules="[val => val && val.length > 0 || 'Field label required']"
 | 
						|
            />
 | 
						|
            <q-select
 | 
						|
              outlined
 | 
						|
              dense
 | 
						|
              v-model="field.type"
 | 
						|
              :options="fieldTypes"
 | 
						|
              label="Field Type *"
 | 
						|
              class="col-auto"
 | 
						|
              style="min-width: 150px;"
 | 
						|
              lazy-rules
 | 
						|
              :rules="[val => !!val || 'Field type required']"
 | 
						|
            />
 | 
						|
            <q-btn
 | 
						|
              flat
 | 
						|
              round
 | 
						|
              dense
 | 
						|
              icon="delete"
 | 
						|
              color="negative"
 | 
						|
              @click="removeField(catIndex, fieldIndex)"
 | 
						|
              title="Remove Field"
 | 
						|
            />
 | 
						|
          </div>
 | 
						|
          <q-input
 | 
						|
            v-model="field.description"
 | 
						|
            outlined
 | 
						|
            dense
 | 
						|
            label="Field Description (Optional)"
 | 
						|
            autogrow
 | 
						|
            class="q-mt-xs q-mb-xl"
 | 
						|
            hint="This description will appear below the field label on the form."
 | 
						|
          />
 | 
						|
        </div>
 | 
						|
        <q-btn
 | 
						|
          color="primary"
 | 
						|
          label="Add Field"
 | 
						|
          @click="addField(catIndex)"
 | 
						|
          class="q-ml-md q-mt-sm"
 | 
						|
        />
 | 
						|
      </div>
 | 
						|
 | 
						|
      <q-btn
 | 
						|
        color="secondary"
 | 
						|
        label="Add Category"
 | 
						|
        @click="addCategory"
 | 
						|
      />
 | 
						|
 | 
						|
      <q-separator class="q-my-lg" />
 | 
						|
 | 
						|
      <div>
 | 
						|
        <q-btn
 | 
						|
          label="Create Form"
 | 
						|
          type="submit"
 | 
						|
          color="primary"
 | 
						|
          :loading="submitting"
 | 
						|
        />
 | 
						|
        <q-btn
 | 
						|
          label="Cancel"
 | 
						|
          type="reset"
 | 
						|
          color="warning"
 | 
						|
          class="q-ml-sm"
 | 
						|
          :to="{ name: 'formList' }"
 | 
						|
        />
 | 
						|
      </div>
 | 
						|
    </q-form>
 | 
						|
  </q-page>
 | 
						|
</template>
 | 
						|
 | 
						|
<script setup>
 | 
						|
import { ref } from 'vue';
 | 
						|
import axios from 'boot/axios';
 | 
						|
import { useQuasar } from 'quasar';
 | 
						|
import { useRouter } from 'vue-router';
 | 
						|
 | 
						|
const $q = useQuasar();
 | 
						|
const router = useRouter();
 | 
						|
 | 
						|
const form = ref({
 | 
						|
  title: '',
 | 
						|
  description: '',
 | 
						|
  categories: [
 | 
						|
    { name: 'Category 1', fields: [{ label: '', type: null, description: '' }] }
 | 
						|
  ]
 | 
						|
});
 | 
						|
 | 
						|
const fieldTypes = ref(['text', 'number', 'date', 'textarea', 'boolean']);
 | 
						|
const submitting = ref(false);
 | 
						|
 | 
						|
function addCategory()
 | 
						|
{
 | 
						|
  form.value.categories.push({ name: `Category ${form.value.categories.length + 1}`, fields: [{ label: '', type: null, description: '' }] });
 | 
						|
}
 | 
						|
 | 
						|
function removeCategory(index)
 | 
						|
{
 | 
						|
  form.value.categories.splice(index, 1);
 | 
						|
}
 | 
						|
 | 
						|
function addField(catIndex)
 | 
						|
{
 | 
						|
  form.value.categories[catIndex].fields.push({ label: '', type: 'text', description: '' });
 | 
						|
}
 | 
						|
 | 
						|
function removeField(catIndex, fieldIndex)
 | 
						|
{
 | 
						|
  form.value.categories[catIndex].fields.splice(fieldIndex, 1);
 | 
						|
}
 | 
						|
 | 
						|
async function createForm()
 | 
						|
{
 | 
						|
  submitting.value = true;
 | 
						|
  try
 | 
						|
  {
 | 
						|
    const response = await axios.post('/api/forms', form.value);
 | 
						|
    $q.notify({
 | 
						|
      color: 'positive',
 | 
						|
      position: 'top',
 | 
						|
      message: `Form "${form.value.title}" created successfully!`,
 | 
						|
      icon: 'check_circle'
 | 
						|
    });
 | 
						|
    router.push({ name: 'formList' });
 | 
						|
  }
 | 
						|
  catch (error)
 | 
						|
  {
 | 
						|
    console.error('Error creating form:', error);
 | 
						|
    const message = error.response?.data?.error || 'Failed to create form. Please check the details and try again.';
 | 
						|
    $q.notify({
 | 
						|
      color: 'negative',
 | 
						|
      position: 'top',
 | 
						|
      message: message,
 | 
						|
      icon: 'report_problem'
 | 
						|
    });
 | 
						|
  }
 | 
						|
  finally
 | 
						|
  {
 | 
						|
    submitting.value = false;
 | 
						|
  }
 | 
						|
}
 | 
						|
</script>
 | 
						|
 | 
						|
<style scoped>
 | 
						|
.bordered {
 | 
						|
    border: 1px solid #ddd;
 | 
						|
}
 | 
						|
 | 
						|
.rounded-borders {
 | 
						|
    border-radius: 4px;
 | 
						|
}
 | 
						|
</style>
 |