Assign Interaction: autofill

Infer information for the interaction you're assigning.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==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);
})();