Loading front/src/ActivityGant.vue 0 → 100644 +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"> </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 module/Oscar/src/Oscar/Controller/ProjectGrantController.php +8 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"); Loading module/Oscar/src/Oscar/Factory/ActivityGantJson.php 0 → 100644 +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 module/Oscar/view/oscar/project-grant/advanced-search.phtml +8 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading module/Oscar/view/oscar/project-grant/gant.phtml +39 −2 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 Loading
front/src/ActivityGant.vue 0 → 100644 +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"> </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
module/Oscar/src/Oscar/Controller/ProjectGrantController.php +8 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"); Loading
module/Oscar/src/Oscar/Factory/ActivityGantJson.php 0 → 100644 +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
module/Oscar/view/oscar/project-grant/advanced-search.phtml +8 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading
module/Oscar/view/oscar/project-grant/gant.phtml +39 −2 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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