Added option to add writers details in admin page

This commit is contained in:
sairaj mote 2022-01-28 18:55:43 +05:30
parent 35590cb27c
commit ff344299a8
5 changed files with 279 additions and 33 deletions

View File

@ -498,7 +498,6 @@ customElements.define('sm-input',
this._helperText = '';
this._errorText = '';
this.isRequired = false;
this.hideRequired = false;
this.validationFunction = undefined;
this.reflectedAttributes = ['value', 'required', 'disabled', 'type', 'inputmode', 'readonly', 'min', 'max', 'pattern', 'minlength', 'maxlength', 'step'];
@ -623,9 +622,6 @@ customElements.define('sm-input',
this.clearBtn.classList.remove('hide');
} else {
this.clearBtn.classList.add('hide');
if (this.isRequired && !this.hideRequired) {
this.feedbackText.textContent = '*required';
}
}
}
if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder').trim() === '') return;
@ -692,11 +688,6 @@ customElements.define('sm-input',
}
else if (name === 'required') {
this.isRequired = this.hasAttribute('required');
if (this.isRequired && !this.hideRequired) {
this.feedbackText.textContent = '';
} else {
this.feedbackText.textContent = '*required';
}
if (this.isRequired) {
this.setAttribute('aria-required', 'true');
}
@ -704,9 +695,6 @@ customElements.define('sm-input',
this.setAttribute('aria-required', 'false');
}
}
else if (name === 'hiderequired') {
this.hideRequired = this.hasAttribute('hiderequired')
}
else if (name === 'readonly') {
if (this.hasAttribute('readonly')) {
this.inputParent.classList.add('readonly');
@ -909,7 +897,7 @@ customElements.define('sm-notifications', class extends HTMLElement {
createNotification(message, options = {}) {
const { pinned = false, icon = '' } = options;
const notification = document.createElement('div')
const notification = document.createElement('output')
notification.id = this.randString(8)
notification.classList.add('notification');
let composition = ``;
@ -990,8 +978,6 @@ customElements.define('sm-notifications', class extends HTMLElement {
}
});
class Stack {
constructor() {
this.items = [];
@ -1058,7 +1044,6 @@ smPopup.innerHTML = `
right: 0;
pointer-events: none;
background: var(--backdrop-background);
backdrop-filter: blur(0.1rem);
-webkit-transition: opacity 0.3s;
-o-transition: opacity 0.3s;
transition: opacity 0.3s;
@ -1711,7 +1696,6 @@ smCopy.innerHTML = `
--padding: 0;
--background-color: inherit;
--button-background-color: rgba(var(--text-color), 0.2);
--button-border-radius: 0.3rem;
}
.copy{
display: grid;
@ -1737,7 +1721,7 @@ smCopy.innerHTML = `
border: none;
padding: 0.4rem;
background-color: inherit;
border-radius: var(--button-border-radius);
border-radius: var(--button-border-radius, 0.3rem);
}
.copy-button:active{
background-color: var(--button-background-color);

View File

@ -777,7 +777,7 @@ theme-toggle {
margin-left: 0.4rem;
}
#go_to_top {
.floating-button {
position: fixed;
z-index: 2;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.2);
@ -786,6 +786,9 @@ theme-toggle {
right: 0;
margin: 1.5rem;
padding: 1rem;
}
#go_to_top {
background-color: var(--foreground-color);
}
@ -874,11 +877,55 @@ footer {
font-size: 0.8rem;
}
#add_writer_button {
background-color: var(--accent-color);
color: rgba(var(--background-color), 1);
}
#add_writer_button .icon {
fill: rgba(var(--background-color), 1);
}
#writers_list {
margin: 1.5rem 0;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
}
.writer-card {
display: grid;
gap: 1rem;
padding: 1rem;
border-radius: 0.5rem;
background-color: var(--foreground-color);
}
.writer-card .writer-profile {
display: flex;
align-items: center;
justify-content: center;
height: 3rem;
width: 3rem;
border-radius: 2rem;
background-color: var(--accent-color);
text-align: center;
font-size: 100%;
font-weight: 700;
text-transform: uppercase;
color: var(--foreground-color);
}
.writer-card sm-copy {
margin-top: -0.5rem;
font-size: 0.8rem;
}
.hide {
display: none !important;
}
@media screen and (max-width: 40rem) {
.writer-card .writer-profile {
height: 2.5rem;
width: 2.5rem;
}
.hide-on-mobile {
display: none;
}

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -746,7 +746,7 @@ theme-toggle {
margin-left: 0.4rem;
}
#go_to_top {
.floating-button {
position: fixed;
z-index: 2;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.2);
@ -755,6 +755,8 @@ theme-toggle {
right: 0;
margin: 1.5rem;
padding: 1rem;
}
#go_to_top {
background-color: var(--foreground-color);
}
@ -836,11 +838,56 @@ footer {
}
}
#add_writer_button {
background-color: var(--accent-color);
color: rgba(var(--background-color), 1);
.icon {
fill: rgba(var(--background-color), 1);
}
}
#writers_list {
margin: 1.5rem 0;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
}
.writer-card {
display: grid;
gap: 1rem;
padding: 1rem;
border-radius: 0.5rem;
background-color: var(--foreground-color);
.writer-profile {
display: flex;
align-items: center;
justify-content: center;
height: 3rem;
width: 3rem;
border-radius: 2rem;
background-color: var(--accent-color);
text-align: center;
font-size: 100%;
font-weight: 700;
text-transform: uppercase;
color: var(--foreground-color);
}
sm-copy {
margin-top: -0.5rem;
font-size: 0.8rem;
}
}
.hide {
display: none !important;
}
@media screen and (max-width: 40rem) {
.writer-card {
.writer-profile {
height: 2.5rem;
width: 2.5rem;
}
}
.hide-on-mobile {
display: none;
}

View File

@ -45,7 +45,7 @@
</script>
</head>
<body onload="onLoadStartUp()">
<body onload="onLoadStartUp()" class="hide">
<sm-notifications id="notification_drawer"></sm-notifications>
<audio id="notification_sound">
<source src="https://rmservices.duckdns.org/files/notification-sound.mp3" type="audio/mpeg">
@ -211,7 +211,7 @@
<div id="article_contributors" class="flex"></div>
<span>created with RanchiMall Content collaboration app</span>
</section>
<button id="go_to_top" class="hide" onclick="goToTop()">
<button id="go_to_top" class="floating-button hide" onclick="goToTop()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
@ -236,10 +236,10 @@
<strip-select id="section_selector">
<strip-option value="analytic" selected>Analytics</strip-option>
<strip-option value="request">Requests</strip-option>
<strip-option value="Writers">Requests</strip-option>
<strip-option value="writers">Writers</strip-option>
</strip-select>
</div>
<section id="analytic_section" class="admin-section">
<section id="analytic_section" class="admin-section hide">
<ul id="article_analytics" class="grid gap-1-5 observe-empty-state"></ul>
<p class="empty-state">No articles</p>
</section>
@ -247,7 +247,15 @@
<ul id="publishing_requests" class="grid gap-1-5 observe-empty-state"></ul>
<p class="empty-state">No requests</p>
</section>
<section id="writers_section" class="admin-section hide">
<section id="writers_section" class="admin-section">
<button id="add_writer_button" class="floating-button" onclick="showAddWriterPopup()">
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</svg>
Add writer
</button>
<ul id="writers_list" class="grid gap-1-5 observe-empty-state"></ul>
<p class="empty-state">No writers</p>
</section>
@ -406,6 +414,37 @@
</div>
</section>
</sm-popup>
<sm-popup id="add_writer_popup">
<header slot="header" class="popup__header">
<div class="flex align-center">
<button class="popup__header__close" onclick="hidePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
</svg>
</button>
<h3 id="writer_popup_title">Add writer</h3>
</div>
</header>
<sm-form>
<div class="grid">
<h5 class="label">FLO ID</h5>
<sm-input id="get_writer_id" error-text="Incorrect FLO ID" data-flo-id required></sm-input>
</div>
<div class="grid">
<h5 class="label">Name</h5>
<sm-input id="get_writer_name" required></sm-input>
</div>
<div class="grid">
<h5 class="label">Bio</h5>
<sm-textarea id="get_writer_bio" rows="2"></sm-textarea>
</div>
<sm-button id="save_writer" variant="primary" onclick="saveWriter()">Add writer</sm-button>
</sm-form>
</sm-popup>
<sm-popup id="user_popup">
<header slot="header" class="popup__header">
<div class="flex align-center">
@ -495,6 +534,37 @@
</li>
</template>
<template id="article_writer_template">
<li class="writer-card">
<div class="flex align-start space-between">
<div class="writer-profile"></div>
<div class="flex gap-0-5">
<button class="icon-only edit-writer" title="Edit details">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M14.06 9.02l.92.92L5.92 19H5v-.92l9.06-9.06M17.66 3c-.25 0-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.2-.2-.45-.29-.71-.29zm-3.6 3.19L3 17.25V21h3.75L17.81 9.94l-3.75-3.75z" />
</svg>
</button>
<button class="icon-only delete-writer" title="Delete writer">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z" />
</svg>
</button>
</div>
</div>
<div class="grid">
<h4 class="writer-name capitalize"></h4>
<p class="writer-bio"></p>
</div>
<sm-copy class="writer-flo-id"></sm-copy>
</li>
</template>
<script>
/**
@ -699,6 +769,7 @@
window.addEventListener('hashchange', e => showPage(window.location.hash))
window.addEventListener("load", () => {
document.body.classList.remove('hide')
document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = floCrypto.validateAddr)
document.querySelectorAll('sm-input[data-private-key]').forEach(input => input.customValidation = floCrypto.getPubKeyHex)
document.addEventListener('keyup', (e) => {
if (e.code === 'Escape') {
@ -753,7 +824,7 @@
let params = {}
let searchParams
if (targetPage === '') {
pageId = 'main_page'
pageId = 'dashboard'
} else {
if (targetPage.includes('/')) {
if (targetPage.includes('?')) {
@ -771,10 +842,10 @@
pageId = targetPage
}
}
if (pageId === 'dashboard' && !floGlobals.isSubAdmin) {
pageId = 'home'
history.replaceState(null, null, '#/home')
}
// if (pageId === 'dashboard' && !floGlobals.isSubAdmin) {
// pageId = 'home'
// history.replaceState(null, null, '#/home')
// }
if (searchParams) {
const urlSearchParams = new URLSearchParams('?' + searchParams);
params = Object.fromEntries(urlSearchParams.entries());
@ -956,12 +1027,23 @@
clone.querySelector('.edit-article').dataset.articleId = articleID
return clone
},
writerCard(writerID) {
const { name, bio } = floGlobals.appObjects.articleWriters[writerID]
const clone = getRef('article_writer_template').content.cloneNode(true).firstElementChild
clone.dataset.writerId = writerID
clone.querySelector('.writer-profile').textContent = name[0]
clone.querySelector('.writer-name').textContent = name
clone.querySelector('.writer-bio').textContent = bio
clone.querySelector('.writer-flo-id').setAttribute('value', writerID)
return clone
},
}
const getArticles = async () => {
await Promise.all([
floCloudAPI.requestObjectData('articles'),
floCloudAPI.requestObjectData('articlesContent')
floCloudAPI.requestObjectData('articlesContent'),
floCloudAPI.requestObjectData('articleWriters'),
])
delete floGlobals.appObjects.articlesContent
showPage(window.location.hash, { firstLoad: true })
@ -1403,6 +1485,13 @@
}
getRef('article_analytics').innerHTML = ''
getRef('article_analytics').append(frag)
for (const articleKey in floGlobals.appObjects.articleWriters) {
const card = render.writerCard(articleKey)
frag.prepend(card)
}
getRef('writers_list').innerHTML = ''
getRef('writers_list').append(frag)
}
function publishArticle(vc) {
@ -1591,10 +1680,87 @@
}
getRef('section_selector').addEventListener('change', e => {
document.querySelectorAll('.admin-section').forEach(section => section.classList.add('hide'))
document.querySelectorAll('.admin-section').forEach(section => { section.classList.add('hide') })
getRef(`${e.target.value}_section`).classList.remove('hide')
})
function setWriterDetails(writerID) {
console.log(writerID)
const { name, bio } = floGlobals.appObjects.articleWriters[writerID]
getRef('get_writer_id').value = writerID
getRef('get_writer_name').value = name
getRef('get_writer_bio').value = bio
}
function getWriterDetails() {
return {
floID: getRef('get_writer_id').value.trim(),
name: getRef('get_writer_name').value.trim(),
bio: getRef('get_writer_bio').value.trim(),
}
}
function showAddWriterPopup() {
getRef('writer_popup_title').textContent = "Add writer"
getRef('save_writer').textContent = "ADD"
getRef('get_writer_id').removeAttribute('disabled')
getRef('get_writer_id').parentNode.classList.remove('hide')
showPopup('add_writer_popup')
}
function handleWritersClick(e) {
if (e.target.closest('.edit-writer')) {
const button = e.target.closest('.edit-writer');
const writerID = button.closest('.writer-card').dataset.writerId;
setWriterDetails(writerID)
getRef('writer_popup_title').textContent = "Edit"
getRef('save_writer').textContent = "UPDATE"
getRef('get_writer_id').setAttribute('disabled', 'true')
getRef('get_writer_id').parentNode.classList.add('hide')
showPopup('add_writer_popup')
} else if (e.target.closest('.delete-writer')) {
const button = e.target.closest('.delete-writer');
const writerID = button.closest('.writer-card').dataset.writerId;
getConfirmation('Delete this writer?').then(res => {
if (res) {
delete floGlobals.appObjects.articleWriters[writerID]
button.closest('.writer-card').remove()
floCloudAPI.updateObjectData('articleWriters').then(() => {
notify('Writer deleted', 'success')
}).catch(error => console.error(error))
}
})
}
}
function saveWriter() {
const { floID, name, bio } = getWriterDetails()
if (floGlobals.appObjects.articleWriters.hasOwnProperty(floID)) {
const { name: oldName, bio: oldBio } = floGlobals.appObjects.articleWriters[floID]
if (name !== oldName || bio !== oldBio) {
floGlobals.appObjects.articleWriters[floID].name = name
floGlobals.appObjects.articleWriters[floID].bio = bio
const writerCard = getRef('writers_list').querySelector(`.writer-card[data-writer-id="${floID}"]`)
if (writerCard) {
writerCard.querySelector('.writer-name').textContent = name
writerCard.querySelector('.writer-bio').textContent = bio
}
floCloudAPI.updateObjectData('articleWriters').then(() => {
notify('Writer details updated', 'success')
hidePopup()
}).catch(error => console.error(error))
} else {
notify('Details are not changed', 'error')
}
} else {
floGlobals.appObjects.articleWriters[floID] = { name, bio }
floCloudAPI.updateObjectData('articleWriters').then(() => {
getRef('writers_list').append(render.writerCard(floID))
notify('Added writer details', 'success')
hidePopup()
}).catch(error => console.error(error))
}
}
function getSignedIn() {
return new Promise((resolve, reject) => {
@ -1637,11 +1803,13 @@
floGlobals.subAdminData = {}
getRef('publishing_requests').addEventListener('click', handleRequestClick);
getRef('article_analytics').addEventListener('click', handleAnalyticsClick);
getRef('writers_list').addEventListener('click', handleWritersClick);
document.querySelectorAll('.admin-option').forEach(elem => elem.classList.remove('hide'));
calculateVotes()
} else {
getRef('publishing_requests').removeEventListener('click', handleRequestClick)
getRef('article_analytics').removeEventListener('click', handleAnalyticsClick);
getRef('writers_list').removeEventListener('click', handleWritersClick);
document.querySelectorAll('.admin-option').forEach(elem => elem.classList.add('hide'))
}
if (location.hash.includes('sign_in') || location.hash.includes('sign_up'))