Quick Start

After completing the Installation section, configure your environments and review the examples by running the demo application. You can open a support request for sections you do not understand from the documentation, or browse previously asked questions on the Github Discussions page.

Introduction to CRUD Pages

Step 1 is to identify the backend resources you want to manage.

Go to src/resources/index.js.

export default [
  {
    name: "companies",
    icon: "mdi-city",
    label: "name",
  },
  {
    name: "users",
    icon: "mdi-account",
    permissions: ["admin"],
  },
  {
    name: "roles",
    icon: "mdi-account-key",
    label: "name",
    permissions: [
      { name: "admin", actions: ["show","list","edit","delete", "create"] },
    ],
    actions: ["show","list", "edit", "delete", "create"],
  }
];

Step 2 is to define CRUD pages for each resource, following this naming convention:

src/resources/{Resource}/{action}.vue.

Actions

  • List.vue
  • Create.vue
  • Edit.vue
  • Form.vue
  • Show.vue

For example, for the List request, create a file named List.vue under a src/resources/Users/ folder.

List.vue

src/resources/Users/List.vue

Users List

Users Settings

<template>
  <div>
    <v-card>
      <v-card-text>        
        <va-list 
            :fields="fields"
            :filters="filters"
            :disable-global-search="false"
            :disable-actions="false"
          >
          <va-data-table-server>
          </va-data-table-server>
        </va-list>
      </v-card-text>
    </v-card>
  </div>
</template>
<script>
export default {
  props: ["resource"],
  data() {
    return {
      filters: [
        { source: "firstname", label: this.$t("users.firstname"), col: 2 },
        { source: "lastname", label: this.$t("users.lastname") },
        { source: "email", label: this.$t("users.email") },
        { source: "active", label: this.$t("users.active"), type: "boolean" },
      ],
      fields: [
        {
          source: "firstname",
          label: this.$t("users.firstname"),
          sortable: true,
          width: "5%"
        },
        {
          source: "lastname",
          label: this.$t("users.lastname"),
          sortable: true,
        },
        {
          source: "email",
          label: this.$t("users.email"),
          sortable: true,
        },
        {
          source: "active",
          label: this.$t("users.active"),
          sortable: true,
        }
      ],
    };
  },
};
</script>

Show.vue

src/resources/Users/Show.vue

Users Show

<template>
  <v-card>
    <v-card-text>
      <va-show-layout :title="title">
        <va-show :item="item">
          <v-row justify="left">
            <v-col cols="4">
              <v-table density="compact" class="va-show-table">
                <tbody>
                  <tr>
                    <td><b>{{ $t('users.firstname') }}</b></td>
                    <td>
                      <va-field
                        source="firstname"
                      ></va-field>
                    </td>
                  </tr>
                  <tr>
                    <td><b>{{ $t('users.lastname') }}</b></td>
                    <td>
                      <va-field
                        source="lastname"
                        :label="$t('users.lastname')"
                      ></va-field>
                    </td>
                  </tr>
                  <tr>
                    <td><b>{{ $t('users.email') }}</b></td>
                    <td>
                      <va-field
                        source="email"
                        :label="$t('users.email')"
                      ></va-field>
                    </td>
                  </tr>
                  <tr>
                    <td><b>{{ $t('users.active') }}</b></td>
                    <td>
                      <va-field
                        source="active"
                        type="boolean"
                      ></va-field>
                    </td>
                  </tr>
                  <tr>
                    <td><b>{{ $t('users.createdAt') }}</b></td>
                    <td>
                      <va-field
                        source="createdAt"
                        :label="$t('users.createdAt')"
                      ></va-field>
                    </td>
                  </tr>
                </tbody>
              </v-table>
            </v-col>
          </v-row>
        </va-show>
      </va-show-layout>
    </v-card-text>
  </v-card>
</template>
<script>
export default {
  props: ["title", "item"],
};
</script>

Create.vue

src/resources/Users/Create.vue

Users Create

<template>
  <va-create-layout :title="title">
    <users-form :item="item"></users-form>
  </va-create-layout>
</template>
<script>
export default {
  props: ["title", "item"],
};
</script>

Edit.vue

src/resources/Users/Edit.vue

Users Create

<template>
  <va-edit-layout :title="title">
    <users-form :id="id" :item="item"></users-form>
  </va-edit-layout>
</template>
<script>
export default {
  props: ["id", "title", "item"],
};
</script>

Form.vue

src/resources/Users/Form.vue

Users Form

<template>
  <va-form :id="id" :item="item" v-model="model">
    <v-row no-gutters>
      <v-col lg="3" md="3" sm="8">
        <va-avatar-input
          source="avatar.image"
        >
        </va-avatar-input>

        <va-text-input
          source="firstname"
          :error-messages="firstnameErrors"
        ></va-text-input>

        <va-text-input
          source="lastname"
          :error-messages="lastnameErrors"
        ></va-text-input>

        <va-text-input
          source="email"
          :error-messages="emailErrors"
        ></va-text-input>

        <va-text-input
          source="password"
          :error-messages="passwordErrors"
        ></va-text-input>

        <va-select-input
          source="userRoles"
          reference="roles"
          :error-messages="userRoleErrors"
          multiple
          clearable
        ></va-select-input>

        <va-boolean-input
          source="active"
          hide-details
          class="mb-2"
        ></va-boolean-input>

        <va-boolean-input 
          source="emailActivation"
          class="mb-1"
        >  
        </va-boolean-input>
      </v-col>
    </v-row>
    <va-save-button class="mr-2"></va-save-button>
  </va-form>
</template>
<script>
import { provide } from 'vue';
import { useVuelidate } from "@vuelidate/core";
import { required, email, minLength, maxLength } from "@vuelidate/validators";
import Utils from "olobase-admin/src/mixins/utils";

export default {
  props: ["id", "item"],
  mixins: [Utils],
  setup() {
    let vuelidate = useVuelidate();
    provide('v$', vuelidate)
    return { v$: vuelidate }
  },
  validations: {
    model: {
      firstname: {
        required,
        minLength: minLength(2),
      },
      lastname: {
        required,
        minLength: minLength(2),
        maxLength: maxLength(120),
      },
      userRoles: {
        required,
      },
      email: {
        required,
        email,
      },
      password: {
        minLength: minLength(8),
        maxLength: maxLength(16),
      },
    }
  },
  data() {
    return {
      model: {
        id: null,
        firstname: null,
        lastname: null,
        email: null,
        password: null,
        active: 0,
        emailActivation: 0,
        userRoles: null,
        avatar: {
          image: null
        }
      },
    };
  },
  created() {
    this.model.id = this.generateId(this);
    if (!this.id) {
      this.model.password = this.generatePassword(8);
    }
  },
  computed: {
    firstnameErrors() {
      const errors = [];
      if (!this.v$['model'].firstname.$dirty) return errors;
      this.v$['model'].firstname.required.$invalid &&
        errors.push(this.$t("v.text.required"));
      this.v$['model'].firstname.minLength.$invalid &&
        errors.push(this.$t("v.string.minLength", { min: "2" }));
      return errors;
    },
    lastnameErrors() {
      const errors = [];
      if (!this.v$['model'].lastname.$dirty) return errors;
      this.v$['model'].lastname.required.$invalid &&
        errors.push(this.$t("v.text.required"));
      this.v$['model'].lastname.minLength.$invalid &&
        errors.push(this.$t("v.string.minLength", { min: "2" }));
      this.v$['model'].lastname.maxLength.$invalid &&
        errors.push(this.$t("v.string.maxLength", { max: "120" }));
      return errors;
    },
    emailErrors() {
      const errors = [];
      if (!this.v$['model'].email.$dirty) return errors;
      this.v$['model'].email.required.$invalid &&
        errors.push(this.$t("v.email.required"));
      this.v$["model"].email.email.$invalid && 
        errors.push(this.$t("v.email.invalid"));
      return errors;
    },
    passwordErrors() {
      const errors = [];
      if (!this.v$["model"].password.$dirty) return errors;
      this.v$["model"].password.minLength.$invalid &&
        errors.push(this.$t("v.string.minLength", { min: "8" }));
      this.v$["model"].password.maxLength.$invalid &&
        errors.push(this.$t("v.string.maxLength", { max: "16" }));
      return errors;
    },
    userRoleErrors() {
      const errors = [];
      if (!this.v$["model"].userRoles.$dirty) return errors;
      this.v$["model"].userRoles.required.$invalid &&
        errors.push(this.$t("v.text.required"));
      return errors;
    },
  },
}
</script>