Skip to content

Commit

Permalink
Add dates to holiday summary report header
Browse files Browse the repository at this point in the history
  • Loading branch information
dmtrek14 committed Mar 12, 2024
1 parent a99a315 commit 574c8e8
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 179 deletions.
3 changes: 2 additions & 1 deletion web/holidaySummary.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
<th>Scheduled (hours)</th>
<th>Pending (hours)</th>
<th>% planned</th>
<th v-for="week in weeks" :key="week">{{week}}</th>
<th v-for="week in weeks" :key="week">{{ weeksStartDays.find(x => x.week == week)?.weekStartDate }}<br> {{week}}</th>
</thead>
<tbody>
<tr v-for="row in displayData" :key="row.user">
Expand Down Expand Up @@ -94,3 +94,4 @@
<?php
include("include/footer.php");
?>

295 changes: 151 additions & 144 deletions web/js/holidaySummary.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,153 +18,160 @@
*/

const xmlHeaders = {
method: 'GET',
mode: 'same-origin',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'text/xml'
},
referrerPolicy: 'no-referrer',
}
method: 'GET',
mode: 'same-origin',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'text/xml'
},
referrerPolicy: 'no-referrer'
};

var app = new Vue({
el: '#holidaySummaryReport',
data() {
return {
weeks: {},
displayData: {},
isLoading: true,
isLoadingProjects: true,
projectUsers: {},
projectsList: [],
allProjects: [],
autocompleteIsActive: false,
searchProject: "",
activeProject: 0,
year: new Date().getFullYear()
};
},
created() {
this.fetchSummary();
el: '#holidaySummaryReport',
data() {
return {
weeks: {},
weeksStartDays: {},
displayData: {},
isLoading: true,
isLoadingProjects: true,
projectUsers: {},
projectsList: [],
allProjects: [],
autocompleteIsActive: false,
searchProject: '',
activeProject: 0,
year: new Date().getFullYear()
};
},
created() {
this.fetchSummary();
},
computed: {
downloadUrl() {
let url = `services/getHolidaySummary.php?format=csv&users=${Object.keys(this.displayData).join(',')}`;
return this.year ? `${url}&year=${this.year}` : url;
}
},
methods: {
async fetchProjects() {
let url = 'services/getProjectsService.php?active=true&users=true';
const res = await fetch(url, xmlHeaders);
const body = await res.text();
parser = new DOMParser();
xmlDoc = parser.parseFromString(body, 'text/xml');
const projects = xmlDoc.getElementsByTagName('project');
let parsedProjects = [];
for (var i = 0; i < projects.length; i++) {
parsedProjects.push({
id: projects[i].getElementsByTagName('id')[0].innerHTML,
name: projects[i].getElementsByTagName('fullDescription')[0].innerHTML
});
const users = projects[i].getElementsByTagName('user');
let projectUsers = [];
for (var j = 0; j < users.length; j++) {
projectUsers.push(users[j].getElementsByTagName('login')[0].innerHTML);
}
this.projectUsers[parsedProjects[i].id] = projectUsers;
projectUsers.sort();
}
this.isLoadingProjects = false;
this.projectsList = parsedProjects;
this.allProjects = parsedProjects;
},
computed: {
downloadUrl() {
let url = `services/getHolidaySummary.php?format=csv&users=${Object.keys(this.displayData).join(",")}`;
return this.year ? `${url}&year=${this.year}` : url;
async fetchSummary() {
let params = new URLSearchParams(window.location.search);
let year = params.get('year');
let url = 'services/getHolidaySummary.php';
if (year) {
url += `?year=${year}`;
this.year = year;
}
const res = await fetch(url, {
method: 'GET',
mode: 'same-origin',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
referrerPolicy: 'no-referrer'
});
const body = await res.json();
this.isLoading = false;
this.weeks = Object.keys(body.weeks);
this.weeksStartDays = body.weeksStartDays;
this.displayData = body.holidays;
this.rawData = body.holidays;
await this.fetchProjects();
},
methods: {
async fetchProjects() {
let url = 'services/getProjectsService.php?active=true&users=true';
const res = await fetch(url, xmlHeaders);
const body = await res.text();
parser = new DOMParser();
xmlDoc = parser.parseFromString(body, "text/xml");
const projects = xmlDoc.getElementsByTagName("project");
let parsedProjects = [];
for (var i = 0; i < projects.length; i++) {
parsedProjects.push({
id: projects[i].getElementsByTagName("id")[0].innerHTML,
name: projects[i].getElementsByTagName("fullDescription")[0].innerHTML,
});
const users = projects[i].getElementsByTagName("user");
let projectUsers = [];
for (var j = 0; j < users.length; j++) {
projectUsers.push(users[j].getElementsByTagName("login")[0].innerHTML);
}
this.projectUsers[parsedProjects[i].id] = projectUsers;
projectUsers.sort()
}
this.isLoadingProjects = false;
this.projectsList = parsedProjects;
this.allProjects = parsedProjects;
},
async fetchSummary() {
let params = new URLSearchParams(window.location.search);
let year = params.get('year');
let url = 'services/getHolidaySummary.php';
if (year) {
url += `?year=${year}`;
this.year = year;
}
const res = await fetch(url, {
method: 'GET',
mode: 'same-origin',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
referrerPolicy: 'no-referrer',
});
const body = await res.json();
this.isLoading = false;
this.weeks = Object.keys(body.weeks);
this.displayData = body.holidays;
this.rawData = body.holidays;
await this.fetchProjects();
},
// TODO: we should implement the autocomplete as a
// reusable Single File Component, but before that we need to improve our
// static files bundling.
onSelectProject(projectIndex) {
if (!this.projectsList[projectIndex]) return;
const project = this.projectsList[projectIndex];
this.searchProject = project.name;
this.autocompleteIsActive = false;
this.projectsList = this.allProjects;
this.displayData = {};
// Diplays only users from the project
let users = this.projectUsers[project.id];
users.sort()
for (let i = 0; i < users.length; i++) {
if (this.rawData[users[i]]) {
this.displayData[users[i]] = this.rawData[users[i]];
}
}
},
showOptions() {
this.autocompleteIsActive = true;
},
hideOptions(event) {
if (!event.relatedTarget?.classList.contains('autocompleteItemBtn')) {
this.autocompleteIsActive = false;
}
this.activeProject = 0;
},
prevProject() {
if (this.activeProject > 0) {
this.activeProject--;
} else {
this.activeProject = this.projectsList.length - 1;
}
this.scrollAutocomplete();
},
nextProject() {
if (this.activeProject < this.projectsList.length - 1) {
this.activeProject++;
} else {
this.activeProject = 0;
}
this.scrollAutocomplete();
},
scrollAutocomplete() {
if (!document.getElementsByClassName('autocompleteItemBtn')[this.activeProject]) return;
const elementHeight = document.getElementsByClassName('autocompleteItemBtn')[this.activeProject].offsetHeight;
const offSet = document.getElementsByClassName('autocompleteItemBtn')[this.activeProject].offsetTop + elementHeight;
const clientHeight = document.getElementById('projectsDropdown').clientHeight;
document.getElementById('projectsDropdown').scrollTop = offSet - clientHeight;
},
filterProject(event) {
this.autocompleteIsActive = true;
if (!event.target.value) {
// reset list of users when no project is selected
this.displayData = this.rawData;
this.projectsList = this.allProjects;
} else {
this.projectsList = this.allProjects.filter(project => project.name.toLowerCase().includes(event.target.value.toLowerCase()));
}
},
// TODO: we should implement the autocomplete as a
// reusable Single File Component, but before that we need to improve our
// static files bundling.
onSelectProject(projectIndex) {
if (!this.projectsList[projectIndex]) return;
const project = this.projectsList[projectIndex];
this.searchProject = project.name;
this.autocompleteIsActive = false;
this.projectsList = this.allProjects;
this.displayData = {};
// Diplays only users from the project
let users = this.projectUsers[project.id];
users.sort();
for (let i = 0; i < users.length; i++) {
if (this.rawData[users[i]]) {
this.displayData[users[i]] = this.rawData[users[i]];
}
}
},
showOptions() {
this.autocompleteIsActive = true;
},
hideOptions(event) {
if (!event.relatedTarget?.classList.contains('autocompleteItemBtn')) {
this.autocompleteIsActive = false;
}
this.activeProject = 0;
},
prevProject() {
if (this.activeProject > 0) {
this.activeProject--;
} else {
this.activeProject = this.projectsList.length - 1;
}
this.scrollAutocomplete();
},
nextProject() {
if (this.activeProject < this.projectsList.length - 1) {
this.activeProject++;
} else {
this.activeProject = 0;
}
this.scrollAutocomplete();
},
scrollAutocomplete() {
if (!document.getElementsByClassName('autocompleteItemBtn')[this.activeProject]) return;
const elementHeight =
document.getElementsByClassName('autocompleteItemBtn')[this.activeProject].offsetHeight;
const offSet =
document.getElementsByClassName('autocompleteItemBtn')[this.activeProject].offsetTop +
elementHeight;
const clientHeight = document.getElementById('projectsDropdown').clientHeight;
document.getElementById('projectsDropdown').scrollTop = offSet - clientHeight;
},
filterProject(event) {
this.autocompleteIsActive = true;
if (!event.target.value) {
// reset list of users when no project is selected
this.displayData = this.rawData;
this.projectsList = this.allProjects;
} else {
this.projectsList = this.allProjects.filter((project) =>
project.name.toLowerCase().includes(event.target.value.toLowerCase())
);
}
}
})
}
});
Loading

0 comments on commit 574c8e8

Please sign in to comment.