Commit 77d5820f authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

Système de filtre pour les jalons en cours

parent fdf8a03d
Loading
Loading
Loading
Loading
Loading
+55 −3
Original line number Diff line number Diff line
@@ -515,24 +515,50 @@ class ActivityDateController extends AbstractOscarController implements UseServi
        return $view;
    }

    /**
     * Liste des jalons de la personnes.
     *
     * @return array|JsonModel
     * @throws OscarException
     */
    public function mineAction(){

        if( $this->isAjax() || $this->getRequest()->getQuery('f') === 'json' ){

            $currentPerson = $this->getOscarUserContextService()->getCurrentPerson();

            if( $currentPerson ){

                $dateBounds = $this->getRequest()->getQuery("b", null);
                $page = $this->getRequest()->getQuery("p", 1);
                $size = $this->getRequest()->getQuery("s", 50);
                $orgs = $this->getRequest()->getQuery("o", null);
                $hideMine = $this->getRequest()->getQuery("m", null) === '0';
                $filters = $this->getRequest()->getQuery("r", '');

                $filterOrganizations = null;
                if( $orgs ){
                    $filterOrganizations = explode(',', $orgs);
                }

                $filtersBounds = [];
                if( $filters ){
                    $filtersBounds = explode(',', $filters);
                }

                $bounds = null;
                if( $dateBounds == '3months' ){
                $todoOnly = false;
                if( in_array('3months', $filtersBounds )){
                    $first = (new \DateTime())->add(\DateInterval::createFromDateString('-2 month'));
                    $last = (new \DateTime())->add(\DateInterval::createFromDateString('+2 month'));
                    $last = (new \DateTime())->add(\DateInterval::createFromDateString('+1 month'));
                    $bounds = [
                        'from' => $first->format('Y-m-d'),
                        'to' => $last->format('Y-m-d')
                    ];
                }
                if( in_array('todo', $filtersBounds )){
                    $todoOnly = true;
                }

                // Récupération des IDS pour les activités "Directes"
                $idsActivityDirect = $this->getProjectGrantService()->getActivityRepository()
@@ -545,11 +571,16 @@ class ActivityDateController extends AbstractOscarController implements UseServi
                $organizationsNamed = [];
                $idsInOrganizations = [];
                foreach ($organizations as $organization){
                    $hiddenOrg = false;
                    if( is_array($filterOrganizations) && in_array($organization->getId(), $filterOrganizations)) {
                        $hiddenOrg = true;
                    }
                    $organizationsNamedItem = [
                        'id' => $organization->getId(),
                        'code' => $organization->getCode(),
                        'shortname' => $organization->getShortName(),
                        'fullname' => $organization->getFullName(),
                        'hidden' => $hiddenOrg,
                        'activitiesIds' => [],
                    ];
                    $milestonesIdsOrganizations = $this->getProjectGrantService()->getActivityRepository()
@@ -557,13 +588,25 @@ class ActivityDateController extends AbstractOscarController implements UseServi

                    $organizationsNamedItem['activitiesIds'] = $milestonesIdsOrganizations;
                    $organizationsNamed[] = $organizationsNamedItem;
                    if( !$hiddenOrg ){
                        $idsInOrganizations = array_merge($idsInOrganizations, $milestonesIdsOrganizations);
                    }
                }

                // Activités
                if( $hideMine ){
                    $idsActivities = $idsInOrganizations;
                } else {
                    $idsActivities = array_merge($idsActivityDirect, $idsInOrganizations);
                }

                // Jalons
                $milestonesCount = $this->getProjectGrantService()->getActivityDateRepository()
                    ->getMilestonesByIdsActivityCount($idsActivities, $bounds, $todoOnly);

                // Pagination
                $milestones = $this->getProjectGrantService()->getActivityDateRepository()
                    ->getMilestonesByIdsActivity($idsActivities, $bounds);
                    ->getMilestonesByIdsActivity($idsActivities, $bounds, $todoOnly, $size, $page);

                $types = $this->getProjectGrantService()->getActivityDateRepository()
                    ->getMilestonesTypes();
@@ -573,7 +616,16 @@ class ActivityDateController extends AbstractOscarController implements UseServi
                $formatter->setOscarUserContext($this->getOscarUserContextService());
                $formatter->setUrlPlugin($this->url());
                $out = $formatter->formatAllWithTypes($milestones, true);

                $out['pagination'] = [
                    'page' => $page,
                    'size' => $size,
                    'totalPages' => ceil($milestonesCount / $size),
                ];
                $out['filterBounds'] = $filtersBounds;
                $out['total'] = $milestonesCount;
                $out['organizationsDetails'] = $organizationsNamed;
                $out['organizationsSelected'] = [];
                $out['direct'] = $idsActivityDirect;
                $out['bounds'] = $bounds;
                return new JsonModel($out);
+50 −11
Original line number Diff line number Diff line
@@ -173,32 +173,55 @@ class ActivityDateRepository extends EntityRepository
    }

    /**
     * Retourne la liste des jalons pour les activités données.
     *
     * @param array $idsActivities
     * @return ActivityDate[]
     * @param array|null $datesBounds
     * @return \Doctrine\ORM\QueryBuilder
     */
    public function getMilestonesByIdsActivity(array $idsActivities, ?array $datesBounds = null) :array
    protected function getMilestonesByIdsActivityQuery(array $idsActivities, ?array $bounds = null, ?bool $todoOnly = false)
    {
        $query = $this->createQueryBuilder('m')
            ->select('m')
            ->innerJoin('m.activity', 'a')
            ->innerJoin('m.type', 't')
            ->where('a.id IN(:idsActivities)')
            ->orderBy('m.dateStart', 'desc')
            ->setParameter('idsActivities', $idsActivities);

        if( $datesBounds ){
            if( $datesBounds['from'] ){
        if( $todoOnly ){
            $query->andWhere('t.finishable = TRUE');
            $query->andWhere('m.finished  = 0 OR m.finished IS NULL');
        }
        if( $bounds ){
            if( $bounds['from'] ){
                $query->andWhere('m.dateStart >= :from')
                    ->setParameter('from', $datesBounds['from']);
                    ->setParameter('from', $bounds['from']);
            }
            if( $datesBounds['to'] ){
            if( $bounds['to'] ){
                $query->andWhere('m.dateStart <= :to')
                    ->setParameter('to', $datesBounds['to']);
                    ->setParameter('to', $bounds['to']);
            }
        }

        return $query;
    }

    /**
     * Retourne la liste des jalons pour les activités données.
     *
     * @param array $idsActivities
     * @return ActivityDate[]
     */
    public function getMilestonesByIdsActivity(
        array $idsActivities,
        ?array $bounds = null,
        ?bool $todoOnly = false,
        ?int $size = 50,
        ?int $page = 1) :array
    {
        $query = $this->getMilestonesByIdsActivityQuery($idsActivities, $bounds, $todoOnly);
        $query->select('m')
            ->setMaxResults($size)
            ->setFirstResult($size*($page-1))
            ->orderBy('m.dateStart', 'desc');

        return $query->getQuery()->getResult();
    }

@@ -206,4 +229,20 @@ class ActivityDateRepository extends EntityRepository
    {
        return $this->getEntityManager()->getRepository(DateType::class)->findAll();
    }

    /**
     * Retourne le nombre de jalons total.
     *
     * @param array $idsActivities
     * @param array|null $bounds
     * @return bool|float|int|string|null
     * @throws \Doctrine\ORM\NoResultException
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function getMilestonesByIdsActivityCount(array $idsActivities, ?array $bounds, bool $todoOnly = false)
    {
        $query = $this->getMilestonesByIdsActivityQuery($idsActivities, $bounds, $todoOnly);
        $query->select('COUNT(m.id) as total');
        return $query->getQuery()->getSingleScalarResult();
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -393,6 +393,15 @@ a.usage {
  .oscar-tag-icon {
    color: darken($brand-info, 15%);
  }
  &.--selected {
    background-color: darken($brand-info, 45%);
  }
  &.--selectable {
    cursor: pointer;
    &:hover {
      background-color: darken($brand-info, 50%);
    }
  }
  &.secondary {
    //text-shadow: 1px -1px 1px rgba(255,255,255,.3);
    background-color: lighten($brand-secondary1, 30%);
@@ -400,6 +409,16 @@ a.usage {
    .oscar-tag-icon {
      color: darken($brand-secondary1, 10%);
    }
    &.--selected {
      background-color: darken($brand-secondary1, 5%);
      color: lighten($brand-secondary1, 25%);
    }
    &.--selectable {
      &:hover {
        background-color: darken($brand-secondary1, 15%);
        color: lighten($brand-secondary1, 25%);
      }
    }
  }
  &.thridary {
    //text-shadow: 1px -1px 1px rgba(255,255,255,.3);
+221 −44
Original line number Diff line number Diff line
<template>
  <h1>
    <i class="icon-calendar"></i>
    {{ title }}</h1>
<!--  <nav>-->
<!--    {{ organizationsDetails }}-->
<!--  </nav>-->
  <section class="milstones">
    <article v-for="milestone in milestones" class="milestone card"
    {{ title }}
    <em> ({{ total + ' ' + $filters.oscarText('jalons') }})</em>
    <button class="btn btn-default" @click="fetch(2000)">
      FETCH
    </button>
  </h1>

  <div class="row">
    <section class="results col-md-8 milestones">
      <div class="pagination btn-group">
        <div v-for="i in pagination.totalPages"
             class="pagination-item btn btn-default"
             @click.prevent="handlerPage(i)"
             :class="{'btn-primary': pagination.page == i}">
          {{ i }}
        </div>
      </div>
      <nav>
        Filtres :
        <div class="btn btn-lg"
             :class="filterBounds.includes('3months') ? 'btn-primary' : 'btn-default'"
             @click="handlerProche()">
          <i class="icon-calendar"></i>
          Jalons proches
        </div>
        <div class="btn btn-lg"
             :class="filterBounds.includes('todo') ? 'btn-primary' : 'btn-default'"
             @click="handlerTodo()">
          <i class="icon-valid"></i>
          Jalons à faire uniquement
        </div>
      </nav>
      <hr>

        <section class="by-year" v-for="(yearData, year) in groupedMilestones">
          <div class="dateMarker">
            {{ year }}
          </div>
          <section class="by-month" v-for="(monthData, month) in yearData.months">
            <div class="dateMarker monthMarker">
              {{ months[month] }}
            </div>
            <article v-for="milestone in monthData.milestones" class="milestone card"
                     :class="{
                'has-problem': milestone.late,
                'past': milestone.past,
@@ -18,13 +55,6 @@
                'progression-refused': milestone.finished == 400,
             }"
            >
      <div v-if="yearMarkerChange(milestone)" class="dateMarker">
        {{ markerYear }}
      </div>
      <div v-if="monthMarkerChange(milestone)" class="dateMarker monthMarker">
        {{ months[markerMonth] }}
      </div>

              <h2 class="card-title">
                <strong>
          <span class="problem-info" v-if="milestone.late">
@@ -111,12 +141,60 @@
              </div>
            </article>
          </section>
        </section>
    </section>
    <section class="results col-md-4">
      <nav class="filters">
        <section class="filters-select">
          <h3>Nombre de résultats</h3>

          <select name="" id="" v-model="size">
            <option value="50">50</option>
            <option value="25">25</option>
            <option value="10">10</option>
          </select>
        </section>
        <section>
          <h3>{{ $filters.oscarText('Mes activités') }}</h3>
          <div class="oscar-tag --selectable secondary"
               :class="{'--selected': filterMine}"
               @click.prevent="handlerFilterMine($event)">
            >
            <i class="icon-eye-off" v-if="!filterMine"></i>
            <i class="icon-eye" v-else></i>
            {{ $filters.oscarText('Mes activités') }}
            ( {{ direct.length}} {{ $filters.oscarText('Activités') }} )
          </div>
        </section>
        <section class="filters-select filters-organizations" v-if="organizationsDetails && organizationsDetails.length">
          <h3>{{ $filters.oscarText('Mes structures') }}</h3>
          <article v-for="elem in organizationsDetails" class="oscar-tag --selectable secondary"
                   :class="{'--selected': !elem.hidden}"
                   @click.prevent="handlerFilterOrganization(elem, $event)">
            <div class="select-label">
              <i class="icon-eye-off" v-if="elem.hidden"></i>
              <i class="icon-eye" v-else></i>
              <code v-show="elem.code">{{ elem.code }}</code>
              <strong v-show="elem.shortname">{{ elem.shortname }}</strong>
              <em>{{ elem.fullname }}</em>
              <span class="total-rounded">
                (<strong>{{ elem.activitiesIds.length }}</strong>
                {{ $filters.oscarText('Activités') }})
              </span>
            </div>
          </article>
        </section>
      </nav>
    </section>
  </div>
</template>

<script>

import AxiosOscar from "../utils/AxiosOscar.js";

let tempo = null;

export default {
  props: {
    title: {
@@ -132,44 +210,92 @@ export default {
      types: [],
      milestones: [],
      progressions: [],
      pagination: {},
      filterBounds: [],
      organizationsDetails: null,
      filterOrganizations: [],
      filterMine: true,

      // Nombre de résultat
      size: 50,

      direct: [],
      markerYear: "",
      total: 0,
      markerMonth: "",
      months:{
        "01": "Janvier",
        "02": "Février",
        "03": "Mars",
        "04": "Avril",
        "05": "Mai",
        "06": "Juin",
        "07": "Juillet",
        "08": "Aout",
        "09": "Septembre",
        "10": "Octobre",
        "11": "Novembre",
        "12": "Décembre",
      months: [ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aout", "Septembre", "Octobre", "Novembre", "Décembre"],
    }
  },

  computed: {
    groupedMilestones(){
      let grouped = {};

      for( let i=0; i<this.milestones.length; i++) {
        let milestone = this.milestones[i];
        let date = new Date(milestone.dateStart);
        let year = date.getFullYear();
        let month = date.getMonth();
        if( !grouped.hasOwnProperty(year) ){
          grouped[year] = {
            months: {}
          };
        }
        if( !grouped[year].months.hasOwnProperty(month) ){
          grouped[year].months[month] = {
            milestones: []
          }
        }
        grouped[year].months[month].milestones.push(milestone);
      }

      return grouped;
    }
  },

  methods: {
    //      {{ milestone.dateStart.substring(0, 4) }}
    yearMarkerChange(milestone) {
      let year = milestone.dateStart.substr(0, 4);
      if( year != this.markerYear ){
        this.markerYear = year;
        return true;
      }
      return false;
    handlerProche(){
      if(this.filterBounds.includes('3months')){
        this.filterBounds.splice(this.filterBounds.indexOf('3months'),1);
      } else {
        this.filterBounds.push('3months')
      }
      this.fetch(1000);
    },
    handlerTodo(){
      if(this.filterBounds.includes('todo')){
        this.filterBounds.splice(this.filterBounds.indexOf('todo'),1);
      } else {
        this.filterBounds.push('todo')
      }
      this.fetch(1000);
    },
    monthMarkerChange(milestone) {
      let month = milestone.dateStart.substr(5, 2);
      if( month != this.markerMonth ){
        this.markerMonth = month;
        return true;
    handlerPage(page){
      this.pagination.page = page;
      this.fetch(0, page);
    },
    handlerFilterOrganization( organization, event ){
      if(event.ctrlKey){
        this.organizationsDetails.forEach(org => {
          org.hidden = true;
        });
        organization.hidden = false;
      } else {
        organization.hidden = !organization.hidden;
      }
      return false;
      this.fetch(2000);
    },

    handlerFilterMine(event){
      if(event.ctrlKey){
        this.organizationsDetails.forEach(org => {
          org.hidden = true;
        });
        this.filterMine = true;
      } else {
        this.filterMine = !this.filterMine;
      }
      this.fetch(2000);
    },

    handlerProgression(milestone, action) {
@@ -202,7 +328,7 @@ export default {
      if( this.organizationsDetails ){
        Object.keys(this.organizationsDetails).forEach((org,key) => {
          let detail = this.organizationsDetails[key];
          if( detail.activitiesIds && detail.activitiesIds.includes(activityMilestoneInfo.id) ){
          if( detail && detail.activitiesIds && detail.activitiesIds.includes(activityMilestoneInfo.id) ){
            info.orgs.push(detail);
          }
        });
@@ -212,13 +338,58 @@ export default {
      info.activity = activityMilestoneInfo;
      return info;
    },
    fetch(){
      AxiosOscar.get(this.url).then((response) => {

    fetch(delay=1000, page=1){
      if( this.pagination && this.pagination.page ){
        this.pagination.page = page;
        console.log("page", this.pagination.page);
      }
      if( tempo ){
        clearTimeout(tempo);
      }
      tempo = setTimeout(()=>{
        this.performFetch();
        clearTimeout(tempo);
      }, delay);

    },

    performFetch(){
      // Filtre organisations
      let orgs = null;
      let url = this.url +'?f=json';
      if( this.organizationsDetails ){
        orgs = [];
        for(let org in this.organizationsDetails){
          let organizationInfos = this.organizationsDetails[org];
          if( organizationInfos.hidden ){
            orgs.push(organizationInfos.id);
          }
        }
        url += '&o=' +orgs.join(',');
      }
      if( this.filterMine === false ){
        url += "&m=0"
      }

      if( this.pagination && this.pagination.page ){
        url += "&p=" + this.pagination.page;
      }

      url += "&r=" + this.filterBounds.join(',');



      console.log(url);
      AxiosOscar.get(url).then((response) => {
        this.types = response.data.types;
        this.pagination = response.data.pagination;
        this.filterBounds = response.data.filterBounds;
        this.milestones = response.data.milestones;
        this.progressions = response.data.progressions;
        this.organizationsDetails = response.data.organizationsDetails;
        this.direct = response.data.direct;
        this.total = response.data.total;
      })
    },
    handlerActivityShow(milestone){
@@ -238,10 +409,16 @@ export default {
  padding-top: 1.5em;
  padding-left: 100px;
}
.milestone {
.milestones, .by-year, .by-month {
  position: relative;
}

.filters-organizations {
  article {
    margin: .5em;
  }
}

.dateMarker {
  border-radius: 8px 0 0 0;
  background: #484855;