Skip to content

Commit

Permalink
Fixed creating repeating events bug, added notifying if late, fixed s…
Browse files Browse the repository at this point in the history
…earch
  • Loading branch information
clr-li committed Apr 27, 2024
1 parent 70fbd70 commit d281f11
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 22 deletions.
2 changes: 1 addition & 1 deletion public/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -1567,7 +1567,7 @@ async function createNewEvent() {
const weekdays = weekdaySelector.querySelectorAll('input');
unit.onchange = e => {
interval.disabled = e.target.value === '';
weekdays.forEach(d => (d.disabled = e.target.value !== '7'));
weekdays.forEach(d => (d.disabled = e.target.value !== 'weekly'));
};
let event = null;
submit.onclick = () => {
Expand Down
46 changes: 42 additions & 4 deletions public/calendar.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { GET, PUT } from './util/Client.js';
import { requireLogin } from './util/Auth.js';
import { GET, PUT, sendGmail } from './util/Client.js';
import { requireLogin, getCurrentUser, requestGoogleCredential } from './util/Auth.js';
import { sanitizeText } from './util/util.js';
import { Popup } from './components/Popup.js';
const $ = window.$;
const user = await getCurrentUser();
await requireLogin();

$('#calendar').evoCalendar();
Expand Down Expand Up @@ -146,11 +147,48 @@ window.markAbsent = async (businessId, eventId) => {
await Popup.alert(await res.text(), 'var(--error)');
return;
}
await Popup.alert('You have been marked absent!');
await Popup.alert(
'You have been marked absent! Close this popup and click on your email account to send notifications to the event host(s).',
'var(--success)',
);

// Send email notification to business owner and admins
const credential = await requestGoogleCredential([
'https://www.googleapis.com/auth/gmail.send',
]);
let success = true;
const res1 = await GET(`/businesses/${businessId}/writemembers`);
const writeMembers = await res1.json();
console.log(writeMembers);

for (const member of writeMembers) {
const res = await sendGmail(
member.email,
'Attendance Scanner Notification of Absence',
'Hi ' +
member.name +
',\n\n' +
user.name +
' has marked themselves absent from the event.\n\n(automatically sent via Attendance Scanner QR)',
credential,
);
if (!res.ok) {
success = false;
const obj = await res.json();
const message = obj.error.message;
Popup.alert(
`Email to ${sanitizeText(member[1])} failed to send. ` + message,
'var(--error)',
);
}
}
if (success) {
Popup.alert('Emails sent successfully!', 'var(--success)');
}

This comment has been minimized.

Copy link
@SanderGi

SanderGi Apr 28, 2024

Collaborator

I have a feeling that receiving email notifications every time someone marks themselves absent could get annoying. Should we perhaps make this a setting?

This comment has been minimized.

Copy link
@SanderGi

SanderGi Apr 28, 2024

Collaborator

Also, while we wait for gmail api approval, we should use the new sendEmail function. We get 3k emails/month (1k daily limit) before I have to pay $1/1k emails. Text me if you need the env variables for testing purposes

This comment has been minimized.

Copy link
@clr-li

clr-li Apr 28, 2024

Author Owner

Yeah i think we should make it a setting


// manually change html since evo-calendar is broken when adding/removing or updating events
const badge = document.createElement('span');
badge.textContent = 'ABSENT(self-marked)';
badge.textContent = 'ABSENT(self)';
document
.querySelector(`[data-event-index="${eventId}"]`)
.getElementsByClassName('event-title')[0]
Expand Down
69 changes: 58 additions & 11 deletions public/components/DataTable.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from '../util/Component.js';
import { calcSimilarity } from '../util/util.js';

/**
* A table component with pagination, sorting and selection.
Expand Down Expand Up @@ -87,10 +88,12 @@ export class DataTable extends Component {
html += `<th>${this.formatHeader(col)}`;
} else {
html += `<th data-colIx="${i}">
${this.formatHeader(col)}&nbsp;<i class="fa-solid fa-sort"></i>`;
<p style="display: inline;" class="sort-group">${this.formatHeader(
col,
)}&nbsp;<i class="fa-solid fa-sort"></i></p>`;
}
if (this.searchableColumns.includes(col)) {
html += '&nbsp;<i class="fa-solid fa-magnifying-glass"></i></th>';
html += '&nbsp;&nbsp;<i class="search fa-solid fa-magnifying-glass"></i></th>';
} else {
html += '</th>';
}
Expand Down Expand Up @@ -306,16 +309,60 @@ export class DataTable extends Component {
search.type = 'search';
search.placeholder = 'Search NAME';
th.replaceChildren(search);
let initialRows = this.rows.filter(row =>
row[header].toLowerCase().includes(search.value.toLowerCase()),
);
search.oninput = () => {
const searchValue = search.value;
this.rows = initialRows.filter(row =>
row[header].toLowerCase().includes(searchValue.toLowerCase()),
);
this.showPage(1);
search.oninput = e => {
let switching = true;
while (switching) {
let i;
switching = false;
let shouldSwitch = false;
let rows = this.shadowRoot.getElementById('table').querySelectorAll('tr');
/* Loop through all table rows (except the
first, which contains table headers): */
for (i = 1; i < rows.length - 1; i++) {
/* Start by saying there should be no switching: */
shouldSwitch = false;
/* Get the two elements you want to compare,
one from current row and one from the next: */
let x = rows[i].querySelectorAll('td')[1];
let y = rows[i + 1].querySelectorAll('td')[1];
/* Check if the two rows should switch place,
based on the direction, asc or desc: */
if (e.target.value) {
if (
calcSimilarity(x.textContent, e.target.value) <
calcSimilarity(y.textContent, e.target.value)
) {
/* If so, mark as a switch and break the loop: */
shouldSwitch = true;
break;
}
} else {
if (x.textContent.toLowerCase() > y.textContent.toLowerCase()) {
/* If so, mark as a switch and break the loop: */
shouldSwitch = true;
break;
}
}
}
if (shouldSwitch) {
/* If a switch has been marked, make the switch
and mark the switch as done: */
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
}
}

This comment has been minimized.

Copy link
@SanderGi

SanderGi Apr 28, 2024

Collaborator

We talked about this, you should use the actual sort, not the one that only works on the visible rows on the current page.

This comment has been minimized.

Copy link
@SanderGi

SanderGi Apr 28, 2024

Collaborator

I've pushed a fix: e7121aa

};

// let initialRows = this.rows.filter(row =>
// row[header].toLowerCase().includes(search.value.toLowerCase()),
// );
// search.oninput = () => {
// const searchValue = search.value;
// this.rows = initialRows.filter(row =>
// row[header].toLowerCase().includes(searchValue.toLowerCase()),
// );
// this.showPage(1);
// };

This comment has been minimized.

Copy link
@SanderGi

SanderGi Apr 28, 2024

Collaborator

Let's not keep around commented out code. We have git in case we need to see the code again

}

/**
Expand Down
14 changes: 9 additions & 5 deletions public/payment.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ <h3 style="margin-bottom: 3px">Free Plan</h3>
<hr />
<ul style="text-align: center; max-width: 80%; margin: auto">
<li>Fixed Monthly Price: $0.00</li>
<li>Up to 50 members</li>
<li>Up to 150 members</li>
<li>Unlimited role and user privilege management</li>
<li>
<a href="https://github.com/clr-li/AttendanceScanner/issues"
Expand All @@ -67,11 +67,15 @@ <h3 style="margin-bottom: 3px">Standard Plan</h3>
<hr />
<ul style="text-align: center; max-width: 80%; margin: auto">
<li>Fixed Monthly Price: $7.00</li>
<li>Up to 100 members</li>
<li>Up to 500 members</li>
<li>Unlimited role and user privilege management</li>
<li>Email+Community support</li>
<li>API+Google Sheets integration</li>
<li>Gmail+Email invites and notifications</li>
<li>Email +
<a href="https://github.com/clr-li/AttendanceScanner/issues"
>Community support</a
>
</li>
<li>Export/import custom data via CSV</li>
<li>Gmail invites and notifications</li>

This comment has been minimized.

Copy link
@SanderGi

SanderGi Apr 28, 2024

Collaborator

You forgot to update the pricing on the nonlogged in home page

This comment has been minimized.

Copy link
@SanderGi

SanderGi Apr 28, 2024

Collaborator

Also, lets write Email instead of Gmail invites+notifications for both plans until we get Gmail api approval

</ul>
</div>
</section>
Expand Down
5 changes: 4 additions & 1 deletion public/styles/tables.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
.table-wrapper tr:hover {
background-color: rgba(220, 228, 235);
}
.table-wrapper th:hover {
.table-wrapper .sort-group:hover {
color: var(--primary);
}
.table-wrapper .search:hover {
color: var(--primary);
}
.table-wrapper tr:first-child th:first-child {
Expand Down
23 changes: 23 additions & 0 deletions server/Business.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,29 @@ router.post('/businesses/:businessId/customdata', async (request, response) => {
response.sendStatus(200);
});

/**
* Gets the name and email of members with owner, admin, or moderator roles.
*
* @pathParams businessId - id of the business to get members from
* @requiredPrivileges member of the business
* @response json object with the name and email of members with owner, admin, or moderator roles
*/
router.get('/businesses/:businessId/writemembers', async (request, response) => {
const uid = await handleAuth(request, response, request.params.businessId);
if (!uid) return;

const businessId = request.params.businessId;

const rows = await db().all(
...SQL`
SELECT Users.name, Users.email
FROM Members INNER JOIN Users on Members.user_id = Users.id
WHERE Members.business_id = ${businessId}
AND Members.role = 'owner' OR Members.role = 'admin' OR Members.role = 'moderator'`,
);
response.send(rows);
});

// ============================ USER ROUTES ============================
/**
* Gets the name of the user
Expand Down

0 comments on commit d281f11

Please sign in to comment.