Browse Source

data deleter and delete button

Vincent GUFFON 4 years ago
parent
commit
afcf7c7485

+ 77 - 0
components/Ui/Button/Delete.vue

@@ -0,0 +1,77 @@
+<template>
+  <main>
+    <v-btn icon @click="alertDeleteItem()">
+      <v-icon>mdi-delete</v-icon>
+    </v-btn>
+
+    <lazy-LayoutDialog
+      :show="showDialog"
+    >
+      <template v-slot:dialogText>
+        <v-card-title class="text-h5 grey lighten-2">
+          {{$t('attention')}}
+        </v-card-title>
+        <v-card-text>
+          <br>
+          <p>{{$t('confirm_to_delete')}}</p>
+        </v-card-text>
+      </template>
+      <template v-slot:dialogBtn>
+        <v-btn class="mr-4 submitBtn ot_grey ot_white--text" @click="closeDialog">
+          {{$t('cancel')}}
+        </v-btn>
+        <v-btn class="mr-4 submitBtn ot_danger ot_white--text" @click="deleteItem">
+          {{$t('delete')}}
+        </v-btn>
+      </template>
+    </lazy-LayoutDialog>
+  </main>
+</template>
+
+<script lang="ts">
+  import {defineComponent, useContext, ref} from '@nuxtjs/composition-api'
+  import {DataDeleterArgs} from "~/types/interfaces";
+  import {TYPE_ALERT} from "~/types/enums";
+  import {alert} from "~/types/interfaces";
+
+  export default defineComponent({
+    props: {
+      deleteArgs: {
+        type: Object as () => DataDeleterArgs,
+        required: true
+      }
+    },
+    setup(props){
+      const {$dataDeleter, store, app: {i18n}} = useContext()
+      const showDialog = ref(false)
+
+      const deleteItem = async () =>{
+        try{
+          await $dataDeleter.invoke(props.deleteArgs)
+          const alert:alert = {
+            type: TYPE_ALERT.SUCCESS,
+            message: i18n.t('deleteSuccess') as string
+          }
+          store.commit('page/setAlert', alert)
+        }catch (error) {
+          const alert:alert = {
+            type: TYPE_ALERT.ALERT,
+            message: error.message
+          }
+          store.commit('page/setAlert', alert)
+        }
+        showDialog.value = false
+      }
+
+      return {
+        alertDeleteItem : () => showDialog.value = true,
+        closeDialog : () => showDialog.value = false,
+        deleteItem,
+        showDialog
+      }
+    }
+  })
+</script>
+
+<style scoped>
+</style>

+ 38 - 0
components/Ui/Card.vue

@@ -13,10 +13,48 @@
     </v-card-text>
     <v-card-actions>
       <v-spacer></v-spacer>
+      <v-btn icon>
+        <NuxtLink :to="link" class="no-decoration"><v-icon>mdi-pencil</v-icon></NuxtLink>
+      </v-btn>
+      <UiButtonDelete :deleteArgs="args"></UiButtonDelete>
       <slot name="card.action"></slot>
     </v-card-actions>
   </v-card>
 </template>
 
+<script lang="ts">
+  import {defineComponent} from '@nuxtjs/composition-api'
+  import {QUERY_TYPE} from "~/types/enums";
+  import {DataDeleterArgs} from "~/types/interfaces";
+
+  export default defineComponent({
+    props: {
+      link:{
+        type: String,
+        required: true
+      },
+      model:{
+        type: Function,
+        required: true
+      },
+      id:{
+        type: Number,
+        required: true
+      }
+    },
+    setup(props) {
+      const args: DataDeleterArgs = {
+        type: QUERY_TYPE.MODEL,
+        model: props.model,
+        id: props.id
+      }
+      return {
+        args
+      }
+    }
+  })
+</script>
+
+
 <style scoped>
 </style>

+ 0 - 1
components/Ui/DataTable.vue

@@ -43,7 +43,6 @@
   import {AnyJson} from "~/types/interfaces";
   import {queryHelper} from "~/services/store/query";
   import {QUERY_TYPE} from "~/types/enums";
-  import {Organization} from "~/models/Organization/Organization";
 
   export default defineComponent({
     props: {

+ 2 - 6
components/Ui/SubList.vue

@@ -13,7 +13,7 @@
 </template>
 
 <script lang="ts">
-  import {defineComponent, ref, useContext, useFetch, toRefs, onUnmounted} from '@nuxtjs/composition-api'
+  import {defineComponent, ref, computed, useContext, useFetch, toRefs, onUnmounted} from '@nuxtjs/composition-api'
   import {Model, Query} from "@vuex-orm/core";
   import {queryHelper} from "~/services/store/query";
   import {QUERY_TYPE} from "~/types/enums";
@@ -39,21 +39,17 @@
     },
     setup(props) {
       const {rootModel, rootId, model, query} = toRefs(props);
-      const items = ref([] as Array<Model>)
-
       const {$dataProvider} = useContext()
       const {fetch, fetchState} = useFetch(async ()=>{
-
         await $dataProvider.invoke({
           type: QUERY_TYPE.MODEL,
           model: model.value,
           root_model: rootModel.value,
           root_id: rootId.value
         })
-
-        items.value = queryHelper.getCollection(query.value);
       })
 
+      const items  = computed(() => queryHelper.getCollection(query.value))
       // onUnmounted( useRepositoryHelper.cleanRepository(repository.value) )
 
       return {

+ 1 - 0
config/nuxtConfig/plugins.js

@@ -8,5 +8,6 @@ export default {
     '~/plugins/Data/axios',
     '~/plugins/Data/dataPersister',
     '~/plugins/Data/dataProvider',
+    '~/plugins/Data/dataDeleter',
   ]
 }

+ 4 - 0
lang/enum/fr-FR.js

@@ -57,5 +57,9 @@ export default (context, locale) => {
     ADDRESS_CONTACT: "Adresse de contact",
     ADDRESS_BILL: "Adresse de facturation",
     ADDRESS_OTHER: "Autre adresse",
+    PRINCIPAL: "Contact principal",
+    BILL: "Contact de facturation",
+    OTHER: "Autre contact",
+    CONTACT: "Contact",
   })
 }

+ 3 - 0
lang/field/fr-FR.js

@@ -35,8 +35,11 @@ export default (context, locale) => {
     icomNumber: "N° ICOM",
     urssafNumber: "N° URSSAF",
     email: "E-mail",
+    emailInvalid: "E-mail invalide",
     telphone: "Téléphone",
+    telphoneInvalid: "Téléphone invalide",
     mobilPhone: "Portable",
+    mobilPhoneInvalid: "Portable invalide",
     actions: "Actions",
     twitter: "Lien Twitter",
     facebook: "Lien Facebook",

+ 4 - 0
lang/form/fr-FR.js

@@ -2,7 +2,11 @@ export default (context, locale) => {
   return ({
     save: 'Enregistrer',
     back: 'Retour',
+    cancel: 'Annuler',
+    delete: 'Supprimer',
+    confirm_to_delete: 'Vous êtes sur le point de supprimer un élément.',
     saveSuccess: 'Sauvegarde effectuée',
+    deleteSuccess: 'Suppression effectuée',
     quit_form: 'Quitter le formulaire',
     save_and_quit: 'Sauvegarder et quitter le formulaire',
     back_to_form: 'Retourner au formulaire',

+ 15 - 0
models/Core/ContactPoint.ts

@@ -6,15 +6,30 @@ export class ContactPoint extends Model{
   @Attr(null)
   id!: number | null
 
+  @Str('PRINCIPAL', {nullable: false})
+  contactType!: string
+
   @Str('', {nullable: true})
   email!: string
 
+  @Str('', {nullable: true})
+  emailInvalid!: string
+
   @Str('', {nullable: true})
   telphone!: string
 
+  @Str('', {nullable: true})
+  telphoneInvalid!: string
+
   @Str('', {nullable: true})
   mobilPhone!: string
 
+  @Str('', {nullable: true})
+  mobilPhoneInvalid!: string
+
   @Str('', {nullable: true})
   faxNumber!: string
+
+  @Str('', {nullable: true})
+  faxNumberInvalid!: string
 }

+ 12 - 0
plugins/Data/dataDeleter.ts

@@ -0,0 +1,12 @@
+import {Plugin} from '@nuxt/types'
+import DataDeleter from "~/services/dataDeleter/dataDeleter";
+
+const dataDeleterPlugin: Plugin = (ctx) => {
+  const dataDeleter = new DataDeleter();
+  dataDeleter.initCtx(ctx)
+
+  //Déclare un nouvel accesseur de service via le context Nuxt
+  ctx.$dataDeleter = dataDeleter
+}
+
+export default dataDeleterPlugin

+ 17 - 0
services/connection/connection.ts

@@ -41,6 +41,9 @@ class Connection{
 
           return this.put(url, args.id, args.data)
         }
+
+      case HTTP_METHOD.DELETE:
+        return this.deleteItem(url, args.id)
     }
 
     throw new Error('Method unknown')
@@ -89,6 +92,20 @@ class Connection{
     return this.request(config)
   }
 
+  /**
+   * DELETE Item : préparation de la config pour la suppression d'un item
+   * @param {string} url
+   * @param {number} id
+   * @return {Promise<any>}
+   */
+  private deleteItem(url: string, id: number): Promise<any>{
+    const config:AxiosRequestConfig = {
+      url: `${url}/${id}`,
+      method: HTTP_METHOD.DELETE,
+    }
+    return this.request(config)
+  }
+
   /**
    * Exécution de la requete
    * @param {AxiosRequestConfig} config

+ 69 - 0
services/dataDeleter/dataDeleter.ts

@@ -0,0 +1,69 @@
+import {hooks} from "~/services/dataDeleter/hook/_import";
+import {DataDeleterArgs} from "~/types/interfaces";
+import {Context} from "@nuxt/types/app";
+import Connection from "~/services/connection/connection";
+import ApiError from "~/services/utils/apiError";
+import {HTTP_METHOD} from "~/types/enums";
+import ConstructUrl from "~/services/connection/constructUrl";
+import {repositoryHelper} from "~/services/store/repository";
+
+class DataDeleter{
+  private ctx !: Context
+  private arguments!: DataDeleterArgs
+
+  constructor() {
+    this.sort()
+  }
+
+  initCtx(ctx:Context){
+    Connection.initConnector(ctx.$axios)
+    this.ctx = ctx
+  }
+
+  async invoke(args:DataDeleterArgs): Promise<any>{
+    this.arguments = args
+    try{
+      this.preHook()
+
+      const url = this.constructUrl()
+      const response = await this.connection(url)
+
+      if(this.arguments.model)
+        repositoryHelper.deleteItem(this.arguments.model, this.arguments.id)
+    }catch(error){
+     throw new ApiError(error.response.status, error.response.data.detail)
+    }
+  }
+
+  async preHook(){
+    for(const hook of hooks){
+      if(hook.support(this.arguments)){
+        await new hook().invoke(this.arguments)
+      }
+    }
+  }
+
+  constructUrl(): string{
+    const constructUrl = new ConstructUrl()
+    return constructUrl.invoke(this.arguments)
+  }
+
+  connection(url: string): Promise<any>{
+    const connection = new Connection()
+    return connection.invoke(HTTP_METHOD.DELETE, url, this.arguments)
+  }
+
+  sort(){
+    hooks.sort(function(a, b) {
+      if (a.priority > b.priority) {
+        return 1
+      }
+      if (a.priority < b.priority) {
+        return -1
+      }
+      return 0
+    });
+  }
+}
+
+export default DataDeleter

+ 5 - 0
services/dataDeleter/hook/_import.ts

@@ -0,0 +1,5 @@
+import HookExample from "~/services/dataPersister/hook/hookExample";
+
+export const hooks = [
+  HookExample
+]

+ 11 - 0
services/dataDeleter/hook/baseHook.ts

@@ -0,0 +1,11 @@
+import {DataDeleterArgs} from "~/types/interfaces";
+
+class BaseHook{
+  static priority = 255
+
+  static support(args:DataDeleterArgs): boolean{
+    throw new Error('Need to be implement into static method')
+  }
+}
+
+export default BaseHook

+ 19 - 0
services/dataDeleter/hook/hookExample.ts

@@ -0,0 +1,19 @@
+import {DataDeleterArgs, HookDeleter} from "~/types/interfaces";
+import BaseHook from "~/services/dataDeleter/hook/baseHook";
+
+class HookExample extends BaseHook implements HookDeleter{
+  static priority = 10
+
+  constructor() {
+    super()
+  }
+
+  async invoke(args: DataDeleterArgs): Promise<any>{
+  }
+
+  static support(args:DataDeleterArgs): boolean{
+    return false
+  }
+}
+
+export default HookExample

+ 10 - 0
services/store/repository.ts

@@ -90,6 +90,16 @@ class Repository{
     return repository.all()
   }
 
+  /**
+   * Supprime l'Item du repository
+   * @param {Model} model
+   * @param {number} id
+   */
+  public deleteItem(model: typeof Model, id: number){
+    const repository = this.getRepository(model)
+    repository.destroy(id)
+  }
+
   /**
    * Supprime tous les Items du repository
    * @param {VuexRepository} repository

+ 14 - 0
test/services/connection/connection.spec.ts

@@ -60,6 +60,20 @@ describe('invoke()', () => {
       await $connection.invoke(HTTP_METHOD.PUT, 'users', {type: QUERY_TYPE.MODEL, id: 1, data:{}})
       expect($connection.put).toHaveBeenCalled();
     })
+  })
+
+  describe('deleteItem()',  () => {
+    it('should delete item', async()=>{
+      Connection.connector.$request = jest.fn().mockReturnValue({})
+      const response = await $connection.invoke(HTTP_METHOD.DELETE, 'users', {type: QUERY_TYPE.MODEL, id: 1})
+      expect(response).toStrictEqual({})
+    })
 
+    it('should call deleteItem', async()=>{
+      $connection.deleteItem =  jest.fn().mockReturnValue({})
+      await $connection.invoke(HTTP_METHOD.DELETE, 'users', {type: QUERY_TYPE.MODEL, id: 1})
+      expect($connection.deleteItem).toHaveBeenCalled();
+    })
   })
+
 })

+ 14 - 0
test/services/datadeleter/hook/baseHook.spec.ts

@@ -0,0 +1,14 @@
+import {QUERY_TYPE} from "~/types/enums";
+import BaseHook from "~/services/dataDeleter/hook/baseHook";
+import {DataDeleterArgs} from "~/types/interfaces";
+
+class DummyHook extends BaseHook{}
+
+describe('support()', () =>{
+  it('should trow an error if support doesnt defined in DummyHook', () =>{
+    const args:DataDeleterArgs = {
+       type: QUERY_TYPE.DEFAULT,
+    }
+    expect(() => DummyHook.support(args)).toThrow()
+  })
+})

+ 10 - 0
types/interfaces.d.ts

@@ -2,6 +2,7 @@ import {Model} from "@vuex-orm/core";
 import {Ability} from "@casl/ability";
 import DataPersister from "~/services/dataPersister/dataPersister";
 import DataProvider from "~/services/dataProvider/dataProvider";
+import DataDeleter from "~/services/dataDeleter/dataDeleter";
 import {Store} from "vuex";
 import {ABILITIES, QUERY_TYPE, TYPE_ALERT} from "~/types/enums";
 import {Context} from "@nuxt/types/app";
@@ -14,6 +15,7 @@ declare module '@nuxt/types' {
     $ability: Ability,
     $dataPersister: DataPersister,
     $dataProvider: DataProvider,
+    $dataDeleter: DataDeleter,
   }
 }
 
@@ -121,10 +123,18 @@ interface DataPersisterArgs extends UrlArgs{
   readonly hook?:string
 }
 
+interface DataDeleterArgs extends UrlArgs{
+  readonly hook?:string
+}
+
 interface HookPersister{
   invoke(args:DataPersisterArgs): Promise<any>,
 }
 
+interface HookDeleter{
+  invoke(args:DataDeleterArgs): Promise<any>,
+}
+
 interface DataProviderArgs extends UrlArgs{
   readonly hook?:string
 }