Vincent GUFFON 4 vuotta sitten
vanhempi
commit
fbcd6d052d

BIN
.output/public/images/Artist-Square.jpg


BIN
.output/public/images/School-Square.jpg


BIN
.output/public/images/nom-de-domaine.jpg


BIN
.output/public/images/sms_big.png


+ 0 - 0
.output/public/robots.txt


+ 122 - 0
components/Layout/AlertBar/Cotisation.vue

@@ -0,0 +1,122 @@
+<!--
+Cotisation bar : Barre qui s'affiche pour donner l'état de la cotisation
+-->
+
+<template>
+  <main>
+    <UiSystemBar color="ot_info" v-if="show_cotisation_access">
+      <template #bar.text>
+        <a @click="goOn('AFFILIATION')" class="ot_white--text">
+          <v-icon small>fas fa-exclamation-triangle</v-icon> {{$t('cotisation_access')}}
+        </a>
+      </template>
+    </UiSystemBar>
+
+    <UiSystemBar color="ot_info" v-else-if="show_upload_invoice">
+      <template #bar.text>
+        <a @click="goOn('INVOICE')" class="ot_white--text">
+          <v-icon small>fas fa-exclamation-triangle</v-icon> {{$t('upload_cotisation_invoice')}}
+        </a>
+      </template>
+    </UiSystemBar>
+
+    <UiSystemBar color="ot_info" v-else-if="show_renew_insurance">
+      <template #bar.text>
+        <a @click="goOn('INSURANCE')" class="ot_white--text">
+          <v-icon small>fas fa-exclamation-triangle</v-icon> {{$t('renew_insurance_cmf')}}
+        </a>
+      </template>
+    </UiSystemBar>
+
+    <UiSystemBar color="ot_info" v-else-if="show_insurance_subscription">
+      <template #bar.text>
+        <a @click="goOn('ADVERTISINGINSURANCE')" class="ot_white--text">
+          <v-icon small>fas fa-exclamation-triangle</v-icon> {{$t('insurance_cmf_subscription')}}
+        </a>
+      </template>
+    </UiSystemBar>
+  </main>
+</template>
+
+<script lang="ts">
+import { defineComponent, useFetch, useContext, Ref, ref} from '@nuxtjs/composition-api'
+import {ALERT_STATE_COTISATION, QUERY_TYPE} from "~/types/enums";
+import {organizationState} from "~/types/interfaces";
+
+export default defineComponent({
+  setup () {
+    const {$dataProvider, store, $config} = useContext()
+    const profileOrganization:organizationState = store.state.profile.organization
+
+    const baseLegacyUrl:string = $config.baseURL_adminLegacy
+    const show_cotisation_access: Ref<Boolean> = ref(false)
+    const show_upload_invoice: Ref<Boolean> = ref(false)
+    const show_renew_insurance: Ref<Boolean> = ref(false)
+    const show_insurance_subscription: Ref<Boolean> = ref(false)
+    const cotisation_year: Ref<Number> = ref(0)
+
+    /**
+     * On récupère l'état des cotisations via l'API
+     */
+    useFetch(async () => {
+      const response = await $dataProvider.invoke({
+        type: QUERY_TYPE.DEFAULT,
+        id: profileOrganization.id,
+        url: 'cotisations'
+      })
+      cotisation_year.value = response.data.cotisationYear
+      handleShow(response.data.alertState)
+    })
+
+    /**
+     * Suivant l'état de l'alerte on affiche tel ou tel message
+     * @param alertState
+     */
+    const handleShow = (alertState: ALERT_STATE_COTISATION) =>{
+      switch(alertState){
+        case ALERT_STATE_COTISATION.AFFILIATION :
+          show_cotisation_access.value = true
+          break;
+        case ALERT_STATE_COTISATION.INVOICE :
+          show_upload_invoice.value = true
+          break;
+        case ALERT_STATE_COTISATION.INSURANCE :
+          show_renew_insurance.value = true
+          break;
+        case ALERT_STATE_COTISATION.ADVERTISINGINSURANCE :
+          show_insurance_subscription.value = true
+          break;
+      }
+    }
+
+    /**
+     * Suivant le bandeau, une action différente est réalisée
+     * @param type
+     */
+    const goOn = (type: ALERT_STATE_COTISATION) => {
+      switch(type){
+        case ALERT_STATE_COTISATION.AFFILIATION :
+          window.location.href = `${baseLegacyUrl}/cotisation/cotisation_steps/${profileOrganization.id}/steps/1`
+          break;
+        case ALERT_STATE_COTISATION.INVOICE :
+          window.open(`${baseLegacyUrl}/cotisation/invoice/${cotisation_year.value}`, '_blank')
+          break;
+        case ALERT_STATE_COTISATION.INSURANCE :
+          window.location.href = `${baseLegacyUrl}/cotisation/insuranceedit`
+          break;
+        case ALERT_STATE_COTISATION.ADVERTISINGINSURANCE :
+          window.open('https://www.cmf-musique.org/services/assurances/assurance-de-groupe/', '_blank')
+          break;
+      }
+    }
+
+    return{
+      show_cotisation_access,
+      show_upload_invoice,
+      show_renew_insurance,
+      show_insurance_subscription,
+      goOn
+    }
+  }
+})
+</script>

+ 24 - 0
components/Layout/AlertBar/Env.vue

@@ -0,0 +1,24 @@
+<!--
+Switch year bar : Barre qui s'affiche lorsque l'utilisateur n'est pas dans un environnement de production
+-->
+
+<template>
+  <UiSystemBar color="ot_warning" v-if="show">
+    <template #bar.text>
+      <v-icon small>fas fa-exclamation-triangle</v-icon> {{$t('not_production_environment', { env })}}
+    </template>
+  </UiSystemBar>
+</template>
+
+<script lang="ts">
+import { defineComponent} from '@nuxtjs/composition-api'
+
+export default defineComponent({
+  setup () {
+    return{
+      show: process.env.NODE_ENV !== 'production',
+      env: process.env.NODE_ENV
+    }
+  }
+})
+</script>

+ 36 - 0
components/Layout/AlertBar/SwitchUser.vue

@@ -0,0 +1,36 @@
+<!--
+Switch year bar : Barre qui s'affiche lorsque l'utilisateur possède un compte multi user
+-->
+
+<template>
+  <UiSystemBar color="ot_info" v-if="show">
+    <template #bar.text>
+      <v-icon small>fas fa-exclamation-triangle</v-icon>
+      <span v-html="$t('multi_account_alert', { fullname })"></span>
+       <v-icon class="ml-1" small>fa fa-users</v-icon>
+      {{$t('multi_account_alert_next')}}
+    </template>
+  </UiSystemBar>
+</template>
+
+<script lang="ts">
+import { defineComponent, useStore} from '@nuxtjs/composition-api'
+import {accessState, AnyStore} from "~/types/interfaces";
+import {State} from "@vuex-orm/core";
+
+export default defineComponent({
+  setup () {
+    const store:AnyStore = useStore<State>()
+    const profileAccess:accessState = store.state.profile.access
+
+    return {
+      show: profileAccess.hasFamilyMenu,
+      fullname: `${profileAccess.givenName} ${profileAccess.name}`
+    }
+  }
+})
+</script>
+
+<style scoped>
+
+</style>

+ 57 - 0
components/Layout/AlertBar/SwitchYear.vue

@@ -0,0 +1,57 @@
+<!--
+Switch year bar : Barre qui s'affiche lorsque l'utilisateur n'est pas sur l'année courante.
+-->
+
+<template>
+  <UiSystemBar color="ot_warning" v-if="isShow">
+    <template #bar.text>
+      {{$t('not_current_year')}}
+      <a @click="resetYear"><strong class="ot_black--text">{{$t('not_current_year_reset')}}</strong></a>
+    </template>
+  </UiSystemBar>
+</template>
+
+<script lang="ts">
+import { defineComponent, useContext, computed} from '@nuxtjs/composition-api'
+import {accessState, organizationState} from "~/types/interfaces";
+import {$useDirtyForm} from "~/use/form/useDirtyForm";
+import {$useMyProfileUpdater} from "~/use/updater/useMyProfileUpdater";
+
+export default defineComponent({
+  setup () {
+    const { store, $dataPersister } = useContext()
+    const { markFormAsNotDirty } = $useDirtyForm(store)
+    const { updateMyProfile, setHistorical, setActivityYear } = $useMyProfileUpdater(store, $dataPersister)
+
+    const profileAccess:accessState = store.state.profile.access
+    const profileOrganization:organizationState = store.state.profile.organization
+
+    const isShow = computed(() => {
+      return (
+        profileAccess.historical.past || profileAccess.historical.future
+        ||
+        profileAccess.historical.dateStart || profileAccess.historical.dateEnd
+        ||
+        profileAccess.activityYear !== profileOrganization.currentActivityYear
+      )
+    })
+
+    const resetYear = async () =>{
+      setHistorical(['present'])
+      setActivityYear(profileOrganization.currentActivityYear)
+      markFormAsNotDirty()
+      await updateMyProfile()
+      window.location.reload()
+    }
+
+    return {
+      isShow,
+      resetYear
+    }
+  }
+})
+</script>
+
+<style scoped>
+
+</style>

+ 32 - 0
components/Layout/Alertbar.vue

@@ -0,0 +1,32 @@
+<!--
+Alert bars
+Contient les différentes barre d'alertes qui s'affichent selon certains cas...
+-->
+
+<template>
+  <main>
+    <client-only><LayoutAlertBarEnv></LayoutAlertBarEnv></client-only>
+    <LayoutAlertBarSwitchUser></LayoutAlertBarSwitchUser>
+    <LayoutAlertBarCotisation v-if="isCmf && $can('manage', 'cotisation')"></LayoutAlertBarCotisation>
+    <LayoutAlertBarSwitchYear></LayoutAlertBarSwitchYear>
+  </main>
+</template>
+
+<script lang="ts">
+import { defineComponent, useContext } from '@nuxtjs/composition-api'
+import {$organizationProfile} from "~/services/profile/organizationProfile";
+
+export default defineComponent({
+  setup () {
+    const { store } = useContext()
+    const organizationProfile = $organizationProfile(store)
+    return {
+      isCmf: organizationProfile.isCmf(),
+    }
+  }
+})
+</script>
+
+<style scoped>
+
+</style>

+ 11 - 3
components/Layout/Header/Notification.vue

@@ -81,12 +81,15 @@ import {AnyStore, ApiResponse, HydraMetadata} from "~/types/interfaces";
 import {queryHelper} from "~/services/store/query";
 import {NotificationUsers} from "~/models/Core/NotificationUsers";
 import {State} from "@vuex-orm/core";
+import {$accessProfile} from "~/services/profile/accessProfile";
 
 export default defineComponent({
   setup: function () {
     const {$dataProvider, $dataPersister, $config, app: { i18n }} = useContext()
     const store:AnyStore = useStore<State>()
     const profileAccess = store.state.profile.access
+    const currentAccessId = $accessProfile(store).getCurrentAccessId()
+
     const loading: Ref<Boolean> = ref(true)
     const isOpen: Ref<Boolean> = ref(false)
     const page: Ref<number> = ref(1)
@@ -162,6 +165,11 @@ export default defineComponent({
            return `${i18n.t('your_message')} ${notification.message?.about} ${i18n.t('has_been_sent')} `
            break;
 
+        case NOTIFICATION_TYPE.SYSTEM :
+          if(notification.message?.about)
+            return `${i18n.t(notification.message.about)}`
+          break;
+
         default:
           return i18n.t(notification.name)
       }
@@ -194,7 +202,7 @@ export default defineComponent({
     const createNewNotificationUsers = (notification: Notification) =>{
       const newNotificationUsers = repositoryHelper.persist(NotificationUsers, new NotificationUsers(
         {
-          access:`/api/accesses/${profileAccess.id}`,
+          access:`/api/accesses/${currentAccessId}`,
           notification:`/api/notifications/${notification.id}`,
           isRead: true
         }
@@ -215,9 +223,9 @@ export default defineComponent({
     const download = (link: string) => {
       const url_parts: Array<string> = link.split('/api');
       if(profileAccess.originalAccess)
-        url_parts[0] = `api/${profileAccess.originalAccess}/${profileAccess.id}`
+        url_parts[0] = `api/${profileAccess.originalAccess}/${currentAccessId}`
       else
-        url_parts[0] = `api/${profileAccess.id}`
+        url_parts[0] = `api/${currentAccessId}`
 
       window.open(`${$config.baseURL_Legacy}/${url_parts.join('')}`);
     }

+ 13 - 10
components/Ui/Image.vue

@@ -32,7 +32,7 @@ export default defineComponent({
   props: {
     id: {
       type: Number,
-      required: true
+      required: false
     },
     imageByDefault: {
       type: String,
@@ -57,15 +57,18 @@ export default defineComponent({
 
     useFetch(async () => {
         try{
-          imageLoaded.value = await $dataProvider.invoke({
-            type: QUERY_TYPE.IMAGE,
-            baseUrl: $config.baseURL_Legacy,
-            imgArgs: {
-              id: props.id,
-              height: props.height,
-              width: props.width
-            }
-          })
+          if(props.id){
+            imageLoaded.value = await $dataProvider.invoke({
+              type: QUERY_TYPE.IMAGE,
+              baseUrl: $config.baseURL_Legacy,
+              imgArgs: {
+                id: props.id,
+                height: props.height,
+                width: props.width
+              }
+            })
+          }else
+            throw new Error('id is null')
         }catch (e){
           imageLoaded.value = require(`/assets/images/byDefault/${props.imageByDefault}`)
         }

+ 31 - 0
components/Ui/SystemBar.vue

@@ -0,0 +1,31 @@
+<!--
+System bars
+-->
+
+<template>
+    <v-system-bar
+      dark
+      height="40"
+      :color="color"
+    >
+        <div class="flex text-center ot_white--text">
+          <slot name="bar.text"></slot>
+        </div>
+    </v-system-bar>
+</template>
+
+<script lang="ts">
+import { defineComponent} from '@nuxtjs/composition-api'
+
+export default defineComponent({
+  props: {
+    color: {
+      type: String,
+      required: true
+    }
+  }
+})
+</script>
+
+<style scoped>
+</style>

+ 2 - 1
config/nuxtConfig/vuetify.js

@@ -38,7 +38,8 @@ export default {
           ot_info: '#3c8dbc',
           ot_menu_color: '#b8c7ce',
           ot_content_color: '#ecf0f4',
-          ot_white: '#ffffff'
+          ot_white: '#ffffff',
+          ot_black: '#000000'
         },
       }
     }

+ 10 - 0
lang/layout/fr-FR.js

@@ -1,5 +1,15 @@
 export default (context, locale) => {
   return ({
+    insurance_cmf_subscription: 'Souscrire un contrat assurance CMF',
+    renew_insurance_cmf: 'Accéder au renouvellement de votre assurance CMF',
+    upload_cotisation_invoice: 'Télécharger la facture de votre appel de cotisation',
+    cotisation_access: 'Accéder au renouvellement de cotisation à ma fédération',
+    information_new_online_registration: 'Nouvelle préinscription',
+    not_production_environment: 'ATTENTION ! Vous êtes sur un environnement {env}.',
+    multi_account_alert: 'Vous êtes connecté, en tant que <strong>{fullname}</strong>, avec un accès famille. Utilisez l\'icône',
+    multi_account_alert_next: 'en haut à droite pour changer les informations des autres membres de votre famille.',
+    not_current_year: 'Votre logiciel est actuellement placé dans une autre année que celle actuelle, et/ou affiche des données passées/futures.',
+    not_current_year_reset: 'Cliquez ici pour afficher les données de l\'année actuelle.',
     welcome: 'Accueil',
     address_book: 'Répertoire',
     person: 'Personnes',

+ 2 - 0
layouts/default.vue

@@ -11,6 +11,8 @@
       <v-main class="ot_content_color">
         <LayoutSubheader v-if="displayedSubHeader" />
 
+        <LayoutAlertbar class="mt-1"></LayoutAlertbar>
+
         <!-- Page will be rendered here-->
         <nuxt />
       </v-main>

+ 0 - 1
services/data/processor/modelProcessor.ts

@@ -25,7 +25,6 @@ class ModelProcessor extends BaseProcessor implements Processor {
     if(payload.metadata.type !== METADATA_TYPE.COLLECTION){
       payload.data.originalState = _.cloneDeep(payload)
     }
-    // console.log(payload.data)
     await repositoryHelper.persist(this.arguments.model, payload.data)
 
     return payload

+ 18 - 3
services/profile/accessProfile.ts

@@ -19,12 +19,19 @@ class AccessProfile {
    * @param {AccessStore} store State Access du Store contenant les informations de l'utilisateur
    * @param {Ability} ability Plugin $ability
    */
-  constructor (store: AccessStore, ability: Ability) {
+  constructor (store: AccessStore) {
     this.accessProfile = store.state.profile.access
-    this.$ability = ability
     this.store = store
   }
 
+  /**
+   * Permet de setter le service d'abilités
+   * @param ability
+   */
+  setAbility(ability: Ability){
+    this.$ability = ability
+  }
+
   /**
    * Est-ce que l'utilisateur possède le rôle donné?
    *
@@ -112,6 +119,14 @@ class AccessProfile {
     return this.accessProfile.isAdminAccess
   }
 
+  /**
+   * Retourne l'id de la session en cours
+   * @return {boolean}
+   */
+  getCurrentAccessId(): number{
+    return this.accessProfile.switchId ?? this.accessProfile.id
+  }
+
   /**
    * Factory
    *
@@ -127,4 +142,4 @@ class AccessProfile {
   }
 }
 
-export const $accessProfile = (store:AccessStore, ability:Ability) => new AccessProfile(store, ability)
+export const $accessProfile = (store:AccessStore) => new AccessProfile(store)

+ 9 - 1
services/rights/abilitiesUtils.ts

@@ -37,11 +37,18 @@ class AbilitiesUtils {
    */
   initFactory () {
     this.factory = {
-      access: $accessProfile(this.$store, this.$ability),
+      access: $accessProfile(this.$store),
       organization: $organizationProfile(this.$store)
     }
   }
 
+  /**
+   * Initialise les Abilities pour le service AccessProfile
+   */
+  initAbilities(){
+    this.factory.access.setAbility(this.$ability)
+  }
+
   /**
    * Définit les abilities de l'utilisateur à chaque fois qu'on met à jour son profile
    */
@@ -81,6 +88,7 @@ class AbilitiesUtils {
     const abilitiesByRoles: Array<AbilitiesType> = this.getAbilitiesByRoles(this.$store.state.profile.access.roles)
     this.$ability.update(abilitiesByRoles)
     this.initFactory()
+    this.initAbilities()
     return abilitiesByRoles.concat(this.getAbilitiesByConfig('./config/abilities/config.yaml'))
   }
 

+ 0 - 2
services/store/repository.ts

@@ -61,8 +61,6 @@ class Repository {
    * @param {AnyJson} entry
    */
   public persist (model: typeof Model, entry: AnyJson): Model {
-    if (_.isEmpty(entry)) { throw new Error('entry is empty') }
-
     return this.getRepository(model).save(entry)
   }
 

+ 5 - 0
store/profile/organization.ts

@@ -5,6 +5,7 @@ export const state = () => ({
   id: null,
   name: '',
   product: '',
+  currentActivityYear: '',
   modules: [],
   hasChildren: false,
   showAdherentList: false,
@@ -24,6 +25,9 @@ export const mutations = {
   setProduct (state: organizationState, product: string) {
     state.product = product
   },
+  setCurrentActivityYear (state: organizationState, currentActivityYear: number) {
+    state.currentActivityYear = currentActivityYear
+  },
   setModules (state: organizationState, modules: Array<string>) {
     state.modules = modules
   },
@@ -55,6 +59,7 @@ export const actions = {
     context.commit('setId', profile.id)
     context.commit('setName', profile.name)
     context.commit('setProduct', profile.product)
+    context.commit('setCurrentActivityYear', profile.currentYear)
     context.commit('setWebsite', profile.website)
     context.commit('setSubDomain', profile.subDomain)
     context.commit('setModules', profile.modules)

+ 9 - 1
types/enums.ts

@@ -41,6 +41,14 @@ export const enum METADATA_TYPE {
 
 export const enum NOTIFICATION_TYPE {
   MESSAGE= 'MESSAGE',
-  FILE= 'FILE'
+  FILE= 'FILE',
+  SYSTEM= 'SYSTEM'
+}
+
+export const enum ALERT_STATE_COTISATION {
+  AFFILIATION= 'AFFILIATION',
+  INVOICE= 'INVOICE',
+  INSURANCE= 'INSURANCE',
+  ADVERTISINGINSURANCE= 'ADVERTISINGINSURANCE'
 }
 

+ 1 - 0
types/interfaces.d.ts

@@ -120,6 +120,7 @@ interface organizationState extends baseOrganizationState {
   id: number,
   name: string,
   product?: string,
+  currentActivityYear?: number,
   modules?: Array<string>,
   hasChildren?: boolean,
   showAdherentList?: boolean,

+ 8 - 2
use/updater/useMyProfileUpdater.ts

@@ -2,9 +2,11 @@ import { computed, ComputedRef } from '@nuxtjs/composition-api'
 import { Item, Model } from '@vuex-orm/core'
 import { repositoryHelper } from '~/services/store/repository'
 import { QUERY_TYPE } from '~/types/enums'
-import { accessState, AnyJson, AnyStore, Historical } from '~/types/interfaces'
+import { AnyJson, AnyStore, Historical } from '~/types/interfaces'
 import DataPersister from '~/services/data/dataPersister'
 import { MyProfile } from '~/models/Access/MyProfile'
+import { $accessProfile } from '@/services/profile/accessProfile'
+import { useContext } from '@nuxtjs/composition-api'
 
 /**
  * @category Use/updater
@@ -25,7 +27,11 @@ export class UseMyProfileUpdater {
    * Composition function
    */
   public invoke (): AnyJson {
-    this.myProfile = this.getMyProfileInstance(this.store.state.profile.access.id) as MyProfile
+
+    const {$ability} = useContext()
+    const currentAccessId = $accessProfile(this.store).getCurrentAccessId()
+
+    this.myProfile = this.getMyProfileInstance(currentAccessId) as MyProfile
     const activityYear:ComputedRef<number> = computed(() => this.myProfile.activityYear)
     const historical:ComputedRef<Historical> = computed(() => this.myProfile.historical)