assets_js_controllers_member-card-profile-controller.js
import { Controller } from "@hotwired/stimulus"
/**
* MemberCardProfile Stimulus Controller
*
* Manages multi-card member profile displays with dynamic layout and content organization.
* Automatically loads member data via AJAX and creates additional cards when content
* overflow occurs, ensuring optimal readability and presentation.
*
* Features:
* - Dynamic card creation with overflow management
* - AJAX-based member data loading
* - Plugin content organization and display
* - Membership status tracking and expiration handling
* - Background check status display
* - Responsive card layout with space calculation
* - Multi-section content organization
*
* Values:
* - url: String - API endpoint for member data
*
* Targets:
* - cardSet: Container for all profile cards
* - firstCard: Initial card element
* - name: Member name display element
* - scaName: SCA name display element
* - branchName: Branch name display element
* - membershipInfo: Membership information display
* - backgroundCheck: Background check status display
* - lastUpdate: Last update timestamp display
* - loading: Loading indicator element
* - memberDetails: Member details container
*
* Usage:
* <div data-controller="member-card-profile" data-member-card-profile-url-value="/api/member/123">
* <div data-member-card-profile-target="cardSet">
* <div data-member-card-profile-target="firstCard" class="auth_card">
* <div data-member-card-profile-target="loading">Loading...</div>
* <div data-member-card-profile-target="memberDetails" hidden>
* <div data-member-card-profile-target="name"></div>
* <div data-member-card-profile-target="scaName"></div>
* <!-- Additional profile elements -->
* </div>
* </div>
* </div>
* </div>
*/
class MemberCardProfile extends Controller {
static targets = ["cardSet",
"firstCard",
"name",
"scaName",
"branchName",
"membershipInfo",
"backgroundCheck",
"lastUpdate",
"loading",
"memberDetails"];
static values = {
url: String,
}
/**
* Initialize controller state
* Sets up card management variables
*/
initialize() {
this.currentCard = null;
this.cardCount = 1;
this.maxCardLength = 0;
}
/**
* Calculate used space in current card
* Measures total height of all child elements
*
* @returns {Number} Total height of card content in pixels
*/
usedSpaceInCard() {
var cardChildren = this.currentCard.children;
var runningTotal = 0;
for (var i = 0; i < cardChildren.length; i++) {
runningTotal += cardChildren[i].offsetHeight;
}
return runningTotal;
}
/**
* Append element to card with overflow handling
* Creates new card if content would exceed available space
*
* @param {HTMLElement} element - Element to append to card
* @param {Number|null} minSpace - Minimum space percentage to maintain
*/
appendToCard(element, minSpace) {
this.currentCard.appendChild(element);
if (minSpace === null) {
minSpace = 2;
}
minSpace = this.maxCardLength * (minSpace / 100);
if (this.usedSpaceInCard() > (this.maxCardLength - minSpace)) {
this.currentCard.removeChild(element);
this.startCard();
this.currentCard.appendChild(element);
}
}
/**
* Create and initialize new card
* Sets up new card structure and updates current card reference
*/
startCard() {
this.cardCount++;
var card = document.createElement("div");
card.classList.add("auth_card");
card.id = "card_" + this.cardCount;
var cardDetails = document.createElement("div");
cardDetails.classList.add("cardbox");
cardDetails.id = "cardDetails_" + this.cardCount;
cardDetails.dataset.section = "auth-card";
card.appendChild(cardDetails);
this.cardSetTarget.appendChild(card);
this.currentCard = cardDetails;
}
/**
* Configure fetch options for AJAX requests
* Sets up headers for JSON API communication
*
* @returns {Object} Fetch options object
*/
optionsForFetch() {
return {
headers: {
"X-Requested-With": "XMLHttpRequest",
"Accept": "application/json"
}
}
}
/**
* Load member card data from API
* Fetches member information and organizes plugin content into cards
*/
loadCard() {
this.currentCard = this.firstCardTarget;
this.maxCardLength = this.firstCardTarget.offsetHeight;
this.cardCount = 1;
fetch(this.urlValue, this.optionsForFetch())
.then(response => response.json())
.then(data => {
this.nameTarget.textContent = data.member.first_name + ' ' + data.member.last_name;
this.scaNameTarget.textContent = data.member.sca_name;
this.branchNameTarget.textContent = data.member.branch.name;
if (data.member.membership_number && data.member.membership_number.length > 0) {
var memberExpDate = new Date(data.member.membership_expires_on);
if (memberExpDate < new Date()) {
memberExpDate = "Expired";
} else {
memberExpDate = " - " + memberExpDate.toLocaleDateString();
}
this.membershipInfoTarget.textContent = data.member.membership_number + ' ' + memberExpDate;
} else {
this.membershipInfoTarget.innerHtml = "";
this.membershipInfoTarget.textContent = "No Membership Info";
}
if (data.member.background_check_expires_on) {
var backgroundCheckExpDate = new Date(data.member.background_check_expires_on);
if (backgroundCheckExpDate < new Date()) {
backgroundCheckExpDate = "Expired";
} else {
backgroundCheckExpDate = " - " + backgroundCheckExpDate.toLocaleDateString();
}
var strong = document.createElement("strong");
strong.textContent = backgroundCheckExpDate;
this.backgroundCheckTarget.innerHtml = "";
this.backgroundCheckTarget.appendChild(strong);
} else {
this.backgroundCheckTarget.innerHtml = "";
this.backgroundCheckTarget.textContent = "No Background Check";
}
var today = new Date();
this.lastUpdateTarget.textContent = today.toLocaleDateString();
this.loadingTarget.hidden = true;
this.memberDetailsTarget.hidden = false;
for (let key in data) {
if (key === 'member') {
continue;
}
var pluginData = data[key];
for (let sectionKey in pluginData) {
var sectionData = pluginData[sectionKey];
var groupCount = sectionData.length;
if (groupCount === 0) {
continue;
}
var sectionHeader = document.createElement("h3");
sectionHeader.textContent = sectionKey;
this.appendToCard(sectionHeader, 20);
for (let groupKey in sectionData) {
var groupData = sectionData[groupKey];
var groupHeader = document.createElement("h5");
groupHeader.textContent = groupKey;
var groupDiv = document.createElement("div");
groupDiv.classList.add("cardGroup");
groupDiv.appendChild(groupHeader);
var groupList = document.createElement("ul");
for (let i = 0; i < groupData.length; i++) {
var itemValue = groupData[i];
var listItem = document.createElement("li");
listItem.textContent = itemValue;
groupList.appendChild(listItem);
}
groupDiv.appendChild(groupList);
this.appendToCard(groupDiv, 10);
}
}
}
});
}
/**
* Connect controller to DOM
* Initiates card loading process
*/
connect() {
this.loadCard();
}
}
if (!window.Controllers) {
window.Controllers = {}
}
window.Controllers["member-card-profile"] = MemberCardProfile;