diff --git a/index.html b/index.html index aa199df..b56b91e 100644 --- a/index.html +++ b/index.html @@ -55,7 +55,7 @@ "" ] } - + @@ -64,7 +64,8 @@
-

Kidnappings & Missing Persons in Uganda

+

Uganda Public Assembly Incident Wall

+

Monitoring Detensions and Releases of Participants of March To Parliament 2024

@@ -77,7 +78,6 @@

Kidnappings & Missing Persons in Uganda

-
@@ -88,6 +88,7 @@

Kidnappings & Missing Persons in Uganda

+
diff --git a/src/components/PersonCard.js b/src/components/PersonCard.js new file mode 100644 index 0000000..dd8072e --- /dev/null +++ b/src/components/PersonCard.js @@ -0,0 +1,154 @@ +const personTemplate = document.createElement('template'); + personTemplate.innerHTML = ` + +
+
+ +

+

+

Taken by

+

Time:

+

Last seen:

+

Gender:

+ +

Current Location:

+
+ +
+ `; + +class PersonCard extends HTMLElement { + constructor() { + super(); + const shadow = this.attachShadow({ mode: 'open' }); + shadow.append(personTemplate.content.cloneNode(true)); + } + + connectedCallback() { + this.render(); + } + + static get observedAttributes() { + return ['name', 'status', 'security-organ', 'time-taken', 'last-known-location', 'gender', 'twitter-handle', 'holding-location', 'photo-url', 'id']; + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + this.render(); + } + } + + render() { + // Set non-slot content + this.shadowRoot.querySelector('.card-img').src = this.getAttribute('photo-url') || ''; + this.shadowRoot.querySelector('.card-img').alt = this.getAttribute('name') || ''; + this.shadowRoot.querySelector('.card__name').textContent = this.getAttribute('name') || ''; + + const status = this.getAttribute('status') || ''; + const statusElement = this.shadowRoot.querySelector('.card-status'); + statusElement.textContent = status; + statusElement.className = `card-status ${status.toLowerCase()}`; + + const twitterHandle = this.getAttribute('twitter-handle') || '--'; + const twitterLink = this.shadowRoot.querySelector('.card__twitter a'); + twitterLink.href = `https://x.com/${twitterHandle}`; + twitterLink.textContent = twitterHandle; + + this.shadowRoot.querySelector('.share-button').addEventListener('click', this.shareCard.bind(this)); + } + + shareCard() { + const name = this.getAttribute('name'); + const status = this.getAttribute('status'); + const lastKnownLocation = this.getAttribute('last-known-location'); + + let text; + if (status.toLowerCase() === "released") { + text = `GOOD NEWS🖐!!!! ${name}, who was previously missing, has been released. They were last held at ${lastKnownLocation || 'Unknown'}. #March2Parliament`; + } else { + text = `NOTICE! This is a missing person: ${name}, status: ${status}, last seen at ${lastKnownLocation || 'Unknown'}. #March2Parliament`; + } + + const url = `https://x.com/intent/tweet?text=${encodeURIComponent(text)}`; + window.open(url, "_blank"); + } +} + +customElements.define('person-card', PersonCard); \ No newline at end of file diff --git a/src/css/styles.css b/src/css/styles.css index 488c032..0d1f091 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -5,7 +5,7 @@ --blue: #007bff; --green: #28a745; --lightergrey: #444; - --lightgrey: #333; + --lightgrey: #5e5d5d; --white: #fff; --black: #000000; --yellow: #ffb224; @@ -47,9 +47,9 @@ button:hover { } .header { - background-color: var(--lightgrey); - padding: 2rem; - text-align: center; + background-color: var(--lightgrey); + padding: .5rem; + text-align: center; } .header h1 { @@ -59,8 +59,9 @@ button:hover { } .header h2 { - font-size: 1.3rem; + font-size: 1rem; font-weight: 400; + font-style: italic; } .filters { @@ -77,7 +78,7 @@ button:hover { .content { flex-grow: 1; - max-width: 1400px; + max-width: 1440px; width: 95%; margin: 2rem auto; padding: 1rem 0; @@ -151,86 +152,10 @@ button:hover { .persons { display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 1.5rem; + grid-template-columns: repeat(auto-fill, minmax(225px, 1fr)); + gap: 1rem; justify-content: center; } - -.card { - background-color: var(--card-bg); - border-radius: 15px; - padding: 1.25rem; - text-align: center; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - transition: background-color 0.3s ease, box-shadow 0.3s ease; - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.card:hover { - background-color: var(--card-hover-bg); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); -} - -.card-img { - width: 100px; - height: 100px; - border-radius: 50%; - margin: 0 auto 1rem; - object-fit: cover; - border: 2px solid var(--white); -} - -.card__name { - font-size: 1.2rem; - margin-bottom: 0.6rem; - font-weight: 700; -} - -.card-status { - font-size: 0.8rem; - margin-bottom: 1rem; - color: var(--white); - background-color: var(--purple); - display: inline-block; - border-radius: 12px; - padding: 0.3rem 1rem; -} - -.card-status.remanded { background-color: var(--yellow); color: var(--black); } -.card-status.fallen { background-color: var(--red); } -.card-status.missing { background-color: var(--blue); } -.card-status.released { background-color: var(--green); } - -.card__info { - margin-bottom: 1rem; -} - -.card__office, .locations, .card__gender { - margin-bottom: 0.6rem; - font-size: 0.9rem; -} - -.card__twitter { - font-size: 0.9rem; - margin-bottom: 1rem; - word-break: break-all; -} - -.card__currently { - font-size: 0.9rem; - margin-bottom: 0.6rem; - margin-bottom: 1rem; - font-weight: 500; -} - -.card__currently-status { - font-weight: normal; - display: block; - margin-top: 0.3rem; -} - .share-button { background-color: var(--blue); border: none; @@ -314,66 +239,21 @@ button:hover { opacity: 0.8; } -@media screen and (max-width: 1400px) { - .persons { - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - } -} - -@media screen and (max-width: 1200px) { - .persons { - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); - } -} - @media screen and (max-width: 1080px) { .search-sort { display: flex; flex-flow: column; } -} +} @media screen and (max-width: 768px) { - .persons { - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - } - .header h1 { font-size: 2rem; } } -@media screen and (max-width: 480px) { - .persons { - grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); - } - - .card { - padding: 1rem; - } - - .card-img { - width: 80px; - height: 80px; - } - - .card__name { - font-size: 1rem; - } - - .card-status, .card__office, .locations, .card__gender, .card__twitter, .card__currently, .share-button { - font-size: 0.8rem; - } - - .search-sort input, - .content__buttons button { - font-size: 0.9rem; - padding: 0.6rem 1rem; - } -} - /* spinner */ .loading { display: flex; diff --git a/src/js/scripts.js b/src/js/scripts.js index b669cf6..0851834 100644 --- a/src/js/scripts.js +++ b/src/js/scripts.js @@ -1,27 +1,6 @@ const API_URL = `https://dashboard.missingpersonsug.org/api/victims?per_page=1000`; - -// Define the shareCard function globally - function shareCard(id) { - fetch(API_URL) - .then((response) => response.json()) - .then(responseBody => { - const card = responseBody.data.find((item) => item.id === id); - let text; - - if (card.status.toLowerCase() === "released") { - text = `GOOD NEWS🖐!!!! ${card.name}, who was previously missing, has been released. They were last held at ${card.last_known_location || 'Unknown'}. #March2Parliament`; - } else { - text = `NOTICE! This is a missing person: ${card.name}, status: ${card.status}, last seen at ${card.last_known_location || 'Unknown'}. #March2Parliament`; - } - - const url = `https://x.com/intent/tweet?text=${encodeURIComponent(text)}`; - window.open(url, "_blank"); - }) - .catch((error) => console.error("Error fetching data:", error)); -} - function parseCustomDateFormat(dateString) { const [time, date] = dateString.split(' '); const [hours, minutes] = time.split(':').map(Number); @@ -30,29 +9,6 @@ const API_URL = `https://dashboard.missingpersonsug.org/api/victims?per_page=100 return parsedDate; } - // Function to create the cards - function createCard(card) { - const takenTime = card.time_taken ? getRelativeTime(parseCustomDateFormat(card.time_taken)) : 'Unknown'; - const exactTime = card.time_taken_formatted ? parseCustomDateFormat(card.time_taken_formatted).toLocaleString('en-US', { month: 'long', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true }) : 'Unknown'; - - return ` -
-
- ${card.name} -

${card.name}

-

${card.status}

-

Taken by ${card.security_organ}

-

Time: ${takenTime}

-

Last seen: ${card.last_known_location}

-

Gender: ${card.gender}

- -

Currently: ${card.holding_location || "--"}

-
- -
- `; -} - /** * Returns a relative time string based on the time a person was taken. * @@ -76,7 +32,28 @@ const API_URL = `https://dashboard.missingpersonsug.org/api/victims?per_page=100 if (months < 12) return `${months} months ago`; return `${years} years ago`; } - + function createPersonElement(person) { + const personElement = document.createElement('person-card'); + const takenTime = person.time_taken ? getRelativeTime(parseCustomDateFormat(person.time_taken)) : 'Unknown'; + + personElement.setAttribute('name', person.name); + personElement.setAttribute('status', person.status); + personElement.setAttribute('photo-url', person.photo_url); + personElement.setAttribute('twitter-handle', person.x_handle || '--'); + personElement.setAttribute('id', person.id); + + // Set slot content + personElement.innerHTML = ` + ${person.security_organ || 'Police'} + ${takenTime} + ${person.last_known_location || 'Unknown'} + ${person.gender || 'Unknown'} + ${person.holding_location || '--'} + `; + + return personElement; + } + document.addEventListener("DOMContentLoaded", function() { // init pagination initPaginate(); @@ -246,39 +223,6 @@ async function fetchData(url){ } } -/** - * Returns a person's element object - * - * @param {person} object - data.json object - */ -function createPersonElement(person){ - const personElement = document.createElement('div'); - personElement.classList.add('person'); - - const takenTime = person.time_taken ? getRelativeTime(parseCustomDateFormat(person.time_taken)) : 'Unknown'; - const exactTime = person.time_taken_formatted ? person.time_taken_formatted : 'Unknown'; - const taken_by = person.security_organ? person.security_organ: 'Police'; - - - personElement.innerHTML = ` -
-
- ${person.name} -

${person.name}

-

${person.status}

-

Taken by ${taken_by}

-

Time: ${takenTime}

-

Last seen: ${person.last_known_location}

-

Gender: ${person.gender}

- -

Currently: ${person.holding_location || "--"}

-
- -
- `; - return personElement -} - // load persons function loadPersons(){ // if search query is empty, check if all persons are loaded, coz then we want infinte scroll otw we don't want it.