Commit f79082fc authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

- Vue GANTT (beta)

parent 7baf9aef
Loading
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
<template>
  <div class="activity-gant">
    <h1>GANT</h1>
    min : <code>{{ min }}</code> ~ {{ min_date }}<br>
    max : <code>{{ max }}</code> ~ {{ max_date }}<br>

    <div class="bordered-area">
      <div class="render" style="position: relative; background: rgba(255,255,255,.7)"
           :style="{height: (50 + 80*activities.length) + 'px', width: max - min + 'px'}">
        <header style="background: white" :style="{  }" class="header-year">
          &nbsp;
        </header>

        <div v-for="year in yearheader" :style="{left: year.left+'px'}" class="header-year-div">
          {{ year.label }}
        </div>


        <div v-for="(a, i) in activities" class="activity"
             :style="{'top': 80 + (75*i)+'px', width: dayWidth*a.width+'px', left: a.left+'px'}">
          <div class="activity-label">
            <i class="icon-cube"></i>
            <strong class="acronym">
              {{ a.acronym }}
            </strong>
            <em>
              {{ a.label }}
            </em>
            <small v-if="a.type">({{ a.type }})</small>
          </div>
          <div class="milestone" v-for="m in a.milestones" :style="{left: m.left+'px', bottom: 0}">
            <i class="icon-calendar"></i>
            <strong>
              {{ m.label }}
            </strong>
            <small>
              {{ m.date | datation }}
            </small>
          </div>
        </div>
      </div>

    </div>
    <hr>
    <code>{{ url }}</code>
    <pre>{{ activities }}</pre>
  </div>
</template>
<script>
/**
 node node_modules/.bin/vue-cli-service build --name ActivityGant --dest ../public/js/oscar/dist --no-clean --formats umd,umd-min --target lib src/ActivityGant.vue
 */
const months = [
  '',
  'Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun', 'Jui', 'Aou', 'Sep', 'Oct', 'Nov', 'Déc'
]
export default {
  props: {
    url: {
      required: true
    }
  },

  data() {
    return {
      activities: [],
      dayWidth: 1,
      min: 0,
      max: 0,
      min_date: '2000-01-01',
      max_date: '2022-12-31',
    }
  },

  computed: {
    yearheader() {
      let out = [];
      let start = parseInt(this.min_date.substring(0, 4));
      let end = parseInt(this.max_date.substring(0, 4));
      let left = 0;
      for (let i = start; i < end; i++) {
        if (i != start) {
          let d = (new Date(i + '-01-01')).getTime() / 1000 / 60 / 60 / 24;
          left = d - this.min;
        }
        out.push({label: i, left: left});
      }
      return out;
    }
  },

  filters: {
    datation(dateStr) {
      let year = dateStr.substring(0, 4);
      let month = months[parseInt(dateStr.substring(5, 7))];
      return month + ' ' + year;
    }
  },

  methods: {
    fetch() {
      this.$http.get(this.url).then(ok => {
        this.min = ok.data.activities.min_time;
        this.max = ok.data.activities.max_time;
        this.min_date = ok.data.activities.min_date_str;
        this.max_date = ok.data.activities.max_date_str;

        ok.data.activities.items.forEach(activity => {

          let start = activity.start_time - this.min;
          let end = activity.end_time - this.min;

          activity.left = start;
          activity.width = (end - start);

          activity.milestones.forEach(milestone => {
            milestone.left = milestone.date_time - this.min - start;
          })

          if (!start || !end) {
            console.log("pas de debut")
          }
        });

        this.activities = ok.data.activities.items;
      });
    }
  },

  mounted() {
    this.fetch();
  }
}
</script>
 No newline at end of file
+8 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ use Oscar\Entity\SpentTypeGroup;
use Oscar\Entity\ValidationPeriod;
use Oscar\Entity\ValidationPeriodRepository;
use Oscar\Exception\OscarException;
use Oscar\Factory\ActivityGantJson;
use Oscar\Form\ActivityInfosPcruForm;
use Oscar\Form\ProjectGrantForm;
use Oscar\Formatter\ActivityPaymentFormatter;
@@ -1114,11 +1115,17 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
    public function gantAction()
    {
        $format = $this->params()->fromQuery('format', 'html');
        $ids = $this->params()->fromQuery('ids', '');

        if ($this->isAjax() || $format == 'json') {
            switch ($this->getHttpXMethod()) {
                case 'GET' :
                    die("DONNÉES");
                    $out = $this->baseJsonResponse();
                    $out['activities'] = [];
                    $activities = $this->getActivityService()->getActivitiesByIds(explode(',',$ids));
                    $format = new ActivityGantJson();
                    $out['activities'] = $format->formatAll($activities);
                    return $this->jsonOutput($out);

                default:
                    return $this->getResponseBadRequest("Méthode inconnue");
+93 −0
Original line number Diff line number Diff line
<?php


namespace Oscar\Factory;


use Oscar\Entity\Activity;
use Oscar\Entity\ActivityDate;

class ActivityGantJson
{
    private $toDayMultiplier = 60*60*24;

    public function formatOne( Activity $activity ) :array
    {
        $out = [
            'id' => $activity->getId(),
            'acronym' => $activity->getAcronym(),
            'label' => $activity->getLabel(),
            'milestones' => [],
            'start' => $activity->getDateStartStr(),
            'end' => $activity->getDateEndStr(),
            'start_time' => $activity->getDateStart() ? $this->normalizeTime($activity->getDateStart()->getTimestamp()) : null ,
            'end_time' => $activity->getDateEnd() ? $this->normalizeTime($activity->getDateEnd()->getTimestamp()) : null ,
            'type' => $activity->getActivityType() ? $activity->getActivityType()->getLabel() : ""
        ];

        /** @var ActivityDate $milestone */
        foreach ($activity->getMilestones() as $milestone) {
            $out['milestones'][] = [
              'id' => $milestone->getId(),
              'label' => $milestone->getType()->getLabel(),
              'date' => $milestone->getDateStartStr(),
              'date_time' => $this->normalizeTime($milestone->getDateStart()->getTimestamp()),
            ];
        }

        return $out;
    }

    public function formatAll( array $activities ) :array
    {
        $out = [
            'items' => []
        ];

        $minDate = $this->normalizeTime(time());
        $maxDate = $this->normalizeTime(time());
        $minDateStr = $this->normalizeTime(date("Y-m-d"));
        $maxDateStr = $this->normalizeTime(date("Y-m-d"));

        /** @var Activity $activity */
        foreach ($activities as $activity) {

            $activityStart = null;
            $activityStartTime = null;
            $activityEnd = null;
            $activityEndTime = null;

            if( $activity->getDateStart() ){
                $activityStart = $activity->getDateStartStr();
                $activityStartTime = $this->normalizeTime($activity->getDateStart()->getTimestamp());
                if( $minDate > $activityStartTime ){
                    $minDate = $activityStartTime;
                    $minDateStr = $activityStart;
                }
            }

            if( $activity->getDateEnd() ){
                $activityEnd = $activity->getDateEndStr();
                $activityEndTime = $this->normalizeTime($activity->getDateEnd()->getTimestamp());
                if( $maxDate < $activityEndTime ){
                    $maxDate = $activityEndTime;
                    $maxDateStr = $activityEnd;
                }
            }



            $out['items'][] = $this->formatOne($activity);
        }
        $out['min_time'] = $minDate;
        $out['max_time'] = $maxDate;
        $out['min_date_str'] = $minDateStr;
        $out['max_date_str'] = $maxDateStr;
        return $out;
    }

    private function normalizeTime( int $time ) :int
    {
        return intval($time / $this->toDayMultiplier);
    }
}
 No newline at end of file
+8 −0
Original line number Diff line number Diff line
@@ -536,6 +536,14 @@
                    Export JSON
                </button>
            </form>

            <form action="<?= $this->url('contract/gant') ?>" class=" export-selection-area" method="get">
                <input type="hidden" name="ids" value="<?= $exportIds ?>">
                <button type="submit" href="#" class="btn btn-primary btn-xs">
                    <i class="icon-cube"></i>
                    Vue GANT
                </button>
            </form>
            <hr />
            <div class="form-checker">
                <div class="form-checker-handler">
+39 −2
Original line number Diff line number Diff line
@@ -6,7 +6,44 @@
 * Time: 11:43
 */
?>
<style>
    .bordered-area {
        border: solid #0a3783 4px;
        padding: 1em;
        overflow: scroll;
    }
    .activity {
        min-height: 50px;
        background: rgba(124, 181, 198, .7);
        border: thin solid rgb(20, 34, 45);
        border-left-width: 4px;
        border-right-width: 4px;
        margin: .25em 0;
        position: absolute;
    }

    .milestone {
        position: absolute;
        border-left: 2px dotted rgba(20, 34, 45, .7);
        min-height: 70px;
        font-size: .9em;
        padding-left: .3em;
    }

    .header-year {
        position: absolute;
        bottom: 0;
    }
    .header-year-div {
        position: absolute;
        border-left: dashed #777777 thin;
        top: 0;
        bottom: 0;
        padding: 0 .3em;
        font-weight: bold;
        color: #BBB;
    }
</style>
<div class="container">
    <div id="app"></div>
    <script>
@@ -17,9 +54,9 @@
            new Vue({
                el: "#app",
                render(h){
                    return h(ActivityGant.default, {
                    return h(ActivityGant, {
                        props: {
                            url: 'http://localhost:8080/activites-de-recherche/api'/*,
                            url: ''/*,
                            activities: []
                            /****/
                        }
Loading