Infer information for the interaction you're assigning.
// ==UserScript==
// @name Assign Interaction: autofill
// @namespace https://github.com/nate-kean/
// @version 2026.5.10
// @description Infer information for the interaction you're assigning.
// @author Nate Kean
// @match https://jamesriver.fellowshiponego.com/members/view/*
// @match https://jamesriver.fellowshiponego.com/members/family/*
// @match https://jamesriver.fellowshiponego.com/members/timeline/*
// @match https://jamesriver.fellowshiponego.com/members/giving/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=fellowshiponego.com
// @require https://update.greasyfork.org/scripts/559387/1730426/getRelativeWeekday.js
// @grant none
// @license MIT
// ==/UserScript==
(function() {
const actionSpan = document
.querySelector("#aid") // TODO: this could be condensed in to one, smarter selector
.parentElement
.querySelector(".chosen-container > a > span");
const completeByDateInput = document.querySelector("#completeByDate");
const today = new Date();
const instructionsInput = document.querySelector("#instructions");
const firstName = () => document.querySelector(".pg-title").textContent.trim().split(" ")[0];
function campus() {
let campusName;
const additionalDetailsEntries = document.querySelectorAll(".other-panel > .panel-body > .info-right-column > .other-details");
for (const entry of additionalDetailsEntries) {
if (entry.textContent.includes("Campus")) {
return entry.textContent.trim().split(" ")[0];
}
}
return "__";
}
function firstVisitDate() {
let campusName;
const additionalDetailsEntries = document.querySelectorAll(".date-panel > .panel-body > .info-right-column > .date-details");
for (const entry of additionalDetailsEntries) {
if (entry.textContent.includes("First Visit Date")) {
return entry.textContent.trim();
}
}
return "__";
}
function friday() {
// This Friday on Sun, Mon, Tue, Wed, Sat; next Friday on Thu, Fri
const fridayDate = getRelativeWeekday(today, DateDir.THIS, Day.FRIDAY, false);
if ([Day.THURSDAY, Day.FRIDAY].includes(today.getDay())) {
// If it's already Thursday or Friday, make it next Friday
fridayDate.setDate(fridayDate.getDate() + 7);
}
return fridayDate;
}
function whose() {
// Determine possessive adjective to use based on gender
for (const p of document.querySelectorAll(".family-name > p")) {
switch (p.textContent) {
case "Male":
return "his";
case "Female":
return "her";
}
}
return "their";
}
function familyName() {
// Scrape or request own name and spouse's name, if applicable
const path = window.location.pathname.split("/");
const page = path.at(-2);
const uid = path.at(-1);
switch (page) {
case "view": {
const familyMembers = document.querySelectorAll(".family-panel .family-item .family-item-details");
const primary = familyMembers[0];
const primaryName = primary.querySelector("a").textContent.trim().split(" ")[0];
const spouse = familyMembers[1];
if (spouse === undefined) return primaryName;
const role = spouse.querySelector("h5").textContent.trim();
if (role === "Wife" || role === "Partner") {
const spouseName = spouse.querySelector("a").textContent.trim().split(" ")[0];
return `${primaryName} & ${spouseName}`;
}
return primaryName;
}
case "family":
// TODO: scrape another way
return "__";
//break;
default:
// TODO: request
return "__";
}
}
function connectionPoint() {
return "__";
}
function experienceType() {
// TODO: the one with the latest date
return "__";
}
function generateAutofillContent(actionName) {
switch (actionName) {
case "New Visitor Connections Follow-Up":
return {
date: today,
instructions: `${campus().toUpperCase()}/TEXT - Please send ${familyName()} a welcome text by 5pm. Thank you!`,
};
case "New Visitor Follow-Up: Cookie Visit":
return {
date: today,
instructions: `COOKIE VISIT • ${connectionPoint()} • First visited ${firstVisitDate()}`,
};
case "New Visitor Follow- Up: Phone Call":
return {
date: friday(),
instructions: `PHONE CALL • ${connectionPoint()} • First visited ${firstVisitDate()}`,
};
case "Altar Follow-Up": {
return {
date: friday(),
instructions: `Please follow up with ${firstName()} regarding ${whose()} ${experienceType()} this week. Thank you!`,
};
}
case "Discipleship":
return {
date: getRelativeWeekday(today, DateDir.THIS, Day.TUESDAY, false),
instructions: `Please follow up with ${firstName()} regarding ${whose()} baptism this week. Thank you!`,
}
}
return {
date: null,
instructions: null,
};
}
function formatDate(date) {
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const year = date.getFullYear();
return `${month}/${day}/${year}`;
}
function onMutation(records) {
const content = generateAutofillContent(actionSpan.textContent);
if (completeByDateInput.value === "" && content.date !== null) {
completeByDateInput.value = formatDate(content.date);
}
if (instructionsInput.value === "" && content.instructions !== null) {
instructionsInput.value = content.instructions;
}
}
const observer = new MutationObserver(onMutation);
const options = {
subtree: false,
childList: true,
attributes: false,
characterData: true,
characterDataOldValue: false,
};
observer.observe(actionSpan, options);
})();