Compare commits

...

2 commits

2 changed files with 262 additions and 43 deletions

View file

@ -42,4 +42,8 @@ ul {
list-style-type: none;
padding: 0;
margin: 0;
}
[title] {
cursor: help;
}

View file

@ -37,6 +37,7 @@
}
table#view_table thead th {
text-align: center;
vertical-align: bottom;
}
table#view_table thead th:first-child {
font-weight: bold;
@ -44,6 +45,14 @@
table#view_table thead th.me {
font-weight: bold;
}
table#view_table thead th img {
display: block;
margin: auto;
margin-bottom: 1em;
border-radius: 50%;
max-width: 4em;
max-height: 4em;
}
table#view_table tbody tr {
border-bottom: 1px solid #dddddd;
@ -55,6 +64,8 @@
table#view_table tbody th span {
font-size: 0.8em;
font-weight: normal;
display: inline-block;
margin-left: 0.33em;
}
table#view_table tbody tr:nth-of-type(odd) {
background-color: var(--colour_lightgrey);
@ -113,68 +124,272 @@
<body>
<table id="view_table">
<thead>
<tr>
<th>
<!-- Group name -->
Les abeilles en test
</th>
<tr id="view_table_thead_headers">
<th id="view_table_thead_name"></th>
<!-- For-each group member -->
<th>Alice</th>
<th class="me">Moi (Loulou)</th>
<th>Bob</th>
</tr>
</thead>
<tbody>
<!-- For-each date -->
<tr>
<th>
Date #1
<br>
<span title="1🏠 + 1🚅">🇸🇪 2</span>
<span title="1🏠">🗼 1</span>
</th>
<!-- For-each group member -->
<td>
<span class="where">
🗼
</span>
</td>
<td class="me">
<span class="where">
🇸🇪
<i class="modifier modifier-bottom modifier-cross">🚅</i>
</span>
</td>
<td>
<span class="where">
🥨
<i class="modifier modifier-top modifier-cross">🏠</i>
<i class="modifier modifier-bottom modifier-cross">🚅</i>
</span>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>
<!-- Total -->
3 group members
</th>
<th id="view_table_tfoot_total"></th>
<!-- Colspan = number of members -->
<th colspan="3"></th>
<th id="view_table_tfoot_filler"></th>
</tr>
</tfoot>
</table>
<p>
So far, this group has planned between 2022-08-01 and 2024-11-25.
So far, this group has planned between
<span id="view_first_filled_date"></span>
and
<span id="view_last_filled_date"></span>.
</p>
<p>
<a href="index.html">Take me home!</a>
</p>
<template id="row_date">
<tr>
<th></th>
<!-- For-each group member = availability -->
</tr>
</template>
<template id="cell_availability">
<td>
<span class="where"></span>
</td>
</template>
<template id="modifier_cannot_host">
<i class="modifier modifier-top modifier-cross" title="Cannot host">🏠</i>
</template>
<template id="modifier_cannot_travel">
<i class="modifier modifier-bottom modifier-cross" title="Cannot travel">🚅</i>
</template>
<script>
var userUid = 1;
var viewData = {
view: {
name: "Le groupe des abeilles",
numberOfGroupMembers: 3,
firstFilledDate: "2023-05-06",
lastFilledDate: "2025-01-31"
},
members: [
{
uid: 1,
displayName: "Louis",
avatar: "https://static.david-david-studio.com/image/24511.jpg"
},
{
uid: 2,
displayName: "Mathilde",
avatar: "https://tinder.com/static/tinder.png"
},
{
uid: 3,
displayName: "Louis",
avatar: "https://www.aprifel.com/wp-content/uploads/2019/02/potiron.jpg"
}
],
dates: [
{
date: "2024-08-01",
membersAvailability: [
{
memberUid: 1,
location: "🇸🇪",
canHost: true,
canTravel: true
},
{
memberUid: 2,
location: "🗼",
canHost: true,
canTravel: true
},
{
memberUid: 3,
location: "🥨",
canHost: true,
canTravel: true
}
]
},
{
date: "2024-08-02",
membersAvailability: [
{
memberUid: 1,
location: "🗼",
canHost: false,
canTravel: true
},
{
memberUid: 2,
location: "🗼",
canHost: true,
canTravel: true
},
{
memberUid: 3,
location: "🥨",
canHost: true,
canTravel: false
}
]
}
]
};
function data2dom(data) {
// Retrieve common DOM elements
var headerRow = document.getElementById("view_table_thead_headers");
var tbody = document.getElementById("view_table").querySelector("tbody");
// Prepared data
var numberOfDisplayedMembers = data.members.length;
// Static: update header
document.getElementById("view_table_thead_name").textContent = data.view.name;
// Static: update footer
let strViewMembersCount = data.view.numberOfGroupMembers + " group member";
if(data.view.numberOfGroupMembers > 1) strViewMembersCount += "s";
document.getElementById("view_table_tfoot_total").textContent = strViewMembersCount;
document.getElementById("view_table_tfoot_filler").setAttribute("colspan", numberOfDisplayedMembers);
// Static: update below table
document.getElementById("view_first_filled_date").textContent = data.view.firstFilledDate;
document.getElementById("view_last_filled_date").textContent = data.view.lastFilledDate;
// For each member
data.members.forEach(element => {
let newCell = document.createElement("th");
let displayName;
if(element.uid == userUid) {
displayName = "Me (" + element.displayName + ")";
newCell.classList.add("me");
}
else {
displayName = element.displayName;
}
if(element.avatar) {
let newAvatar = document.createElement("img");
newAvatar.setAttribute("src", element.avatar);
newAvatar.setAttribute("alt", "Photo for " + element.displayName);
newCell.appendChild(newAvatar);
}
let newTextData = document.createTextNode(displayName);
newCell.appendChild(newTextData);
headerRow.appendChild(newCell);
});
// For each date
const rowDateTemplate = document.getElementById("row_date");
const cellAvailabilityTemplate = document.getElementById("cell_availability");
const modifierCannotHostTemplate = document.getElementById("modifier_cannot_host");
const modifierCannotTravelTemplate = document.getElementById("modifier_cannot_travel");
data.dates.forEach(element => {
let clonedRow = rowDateTemplate.content.cloneNode(true);
let header = clonedRow.querySelector("th");
let headerName = document.createTextNode(element.date);
header.appendChild(headerName);
let locations = [];
element.membersAvailability.forEach(availability => {
// Creating the DOM
let clonedCell = cellAvailabilityTemplate.content.cloneNode(true);
if(userUid == availability.memberUid) {
clonedCell.children[0].classList.add("me");
}
let whereSpan = clonedCell.querySelector("span.where");
let locationText = document.createTextNode(availability.location);
whereSpan.appendChild(locationText);
if(!availability.canHost) {
let clonedModifier = modifierCannotHostTemplate.content.cloneNode(true);
whereSpan.appendChild(clonedModifier);
}
if(!availability.canTravel) {
let clonedModifier = modifierCannotTravelTemplate.content.cloneNode(true);
whereSpan.appendChild(clonedModifier);
}
clonedRow.children[0].appendChild(clonedCell);
// Aggregating data per location, for totals
let existingLocations = locations.filter(e => e.location === availability.location);
if(existingLocations.length < 1) {
locations.push({
location: availability.location,
membersAvailable: 0,
hostsAvailable: 0,
travellersAvailable: 0
});
}
});
// Update other locations if member can travel
element.membersAvailability.forEach(availability => {
locations.forEach(existingLocation => {
if(existingLocation.location == availability.location) {
existingLocation.membersAvailable++;
if(availability.canHost) {
existingLocation.hostsAvailable++;
}
}
else if(availability.canTravel) {
existingLocation.membersAvailable++;
existingLocation.travellersAvailable++;
}
});
});
if(locations.length > 0) {
header.appendChild(document.createElement("br"));
}
locations.sort((a, b) => {
if(a.membersAvailable > b.membersAvailable) {
return -1;
}
else if(a.membersAvailable < b.membersAvailable) {
return 1;
}
return 0;
}).forEach(location => {
let spanLocation = document.createElement("span");
spanLocation.textContent = location.location + location.membersAvailable;
let title = [location.location + ": "];
if(location.hostsAvailable > 0) {
title.push(location.hostsAvailable + "🏠");
}
if(location.travellersAvailable > 0) {
title.push(location.travellersAvailable + "🚅");
}
let neitherHostNorTraveller = location.membersAvailable - location.hostsAvailable - location.travellersAvailable;
if(neitherHostNorTraveller > 0) {
title.push(neitherHostNorTraveller);
}
spanLocation.setAttribute("title", title.join(" "));
header.appendChild(spanLocation);
});
tbody.appendChild(clonedRow);
});
}
data2dom(viewData);
</script>
</body>
</html>