Skip to content
Snippets Groups Projects
GenericItem.vue 5.24 KiB
<template>
  <v-card :flat="flat">
    <error-alert v-if="hasError" :error-message="error"></error-alert>
    <template v-if="hasItem">
      <v-skeleton-loader
        v-if="loading"
        elevation="2"
        :type="skeletonLoaderType"
      ></v-skeleton-loader>
      <v-card v-else flat class="mt-3" :color="color">
        <v-toolbar flat :color="toolbarColor">
          <v-btn nuxt x-small text class="mr-3" @click="goToItemListLocation()"
            ><v-icon left>mdi-arrow-left</v-icon>{{ itemListLabel }}</v-btn
          >
          <v-divider vertical inset></v-divider>
          <v-toolbar-title class="text-uppercase ml-3">
            <v-btn
              v-if="item.title"
              text
              class="text-h5"
              @click="goToItemLocation()"
            >
              {{ item.title }}</v-btn
            ><small v-if="item.subtitle" class="text--secondary">
              {{ item.subtitle }}</small
            >
          </v-toolbar-title>
          <v-spacer></v-spacer>
          <template v-if="itemActions">
            <template v-if="$vuetify.breakpoint.mobile">
              <v-menu bottom left>
                <template v-slot:activator="{ on, attrs }">
                  <v-btn icon v-bind="attrs" v-on="on" @click.stop>
                    <v-icon>mdi-dots-vertical</v-icon>
                  </v-btn>
                </template>
                <v-list>
                  <template v-for="itemAction in itemActions">
                    <v-list-item
                      v-if="itemAction.can(item)"
                      :key="itemAction.title"
                      @click.stop="itemAction.action(item)"
                    >
                      <v-list-item-title
                        ><v-icon left>{{ itemAction.icon }}</v-icon>
                        {{ itemAction.title }}</v-list-item-title
                      >
                    </v-list-item>
                  </template>
                </v-list>
              </v-menu>
            </template>
            <template v-else>
              <v-tooltip
                v-for="itemAction in itemActions"
                :key="itemAction.id"
                bottom
              >
                <template v-slot:activator="{ on, attrs }">
                  <v-btn
                    v-if="itemAction.can(item)"
                    :color="$vuetify.theme.dark ? null : 'secondary'"
                    icon
                    large
                    v-bind="attrs"
                    v-on="on"
                    @click.stop="itemAction.action(item)"
                  >
                    <v-icon>{{ itemAction.icon }}</v-icon>
                  </v-btn>
                </template>
                <span>{{ itemAction.title }}</span></v-tooltip
              >
            </template>
          </template>
          <!-- chips -->
          <template v-if="!$vuetify.breakpoint.mobile">
            <v-spacer></v-spacer>
            <v-chip
              v-if="item.creation_date"
              class="ma-2"
              color="secondary"
              small
            >
              {{ item.creation_date.toDateString() }}
            </v-chip>
            <v-chip small color="accent">{{ itemType }}</v-chip>
          </template>
        </v-toolbar>

        <v-card-text>
          <v-card flat :color="color">
            <v-toolbar dense flat :color="color"
              ><v-toolbar-title>Description</v-toolbar-title>
            </v-toolbar>
            <v-card-text v-if="item.description">
              <slot name="description">
                {{ item.description }}
              </slot>
            </v-card-text>
          </v-card>
        </v-card-text>

        <slot name="content"></slot>
      </v-card>
    </template>
  </v-card>
</template>
<script>
import ErrorAlert from '@/components/ErrorAlert'

export default {
  components: {
    ErrorAlert,
  },
  props: {
    itemListLocation: { type: Object, default: () => null },
    itemLocation: { type: Object, default: () => null },
    itemType: { type: String, default: 'item' },
    itemActions: { type: Array, default: () => null },
    item: { type: Object, default: () => null },
    loading: { type: Boolean, default: false },
    skeletonLoaderType: {
      type: String,
      default:
        'card-heading, list-item-avatar-two-line@3, article, table-heading, table-thead, table-tfoot',
    },
    color: { type: String, default: null },
    flat: { type: Boolean, default: false },
  },

  data() {
    return {
      error: null,
    }
  },
  computed: {
    hasError() {
      return this.error !== null
    },
    hasItem() {
      return this.item !== null
    },
    itemListLabel() {
      return this?.itemListLocation?.label || 'items'
    },
    // TODO : choose color for toolbar
    toolbarColor() {
      return this.$vuetify.theme.dark ? null : this.color
    },
  },

  methods: {
    goToItemListLocation() {
      if (this.itemListLocation) {
        this.$router.push(this.itemListLocation)
      } else {
        this.error = `There is no ${this.itemType} list location defined`
      }
    },
    goToItemLocation() {
      if (this.itemLocation) {
        this.$router.push(this.itemLocation)
      } else {
        this.error = 'There is no location defined for the current item'
      }
    },
  },
}
</script>