sync: auto-sync from ACG-TECH03L at 2026-04-19 12:50:13

Author: Howard Enos
Machine: ACG-TECH03L
Timestamp: 2026-04-19 12:50:13
This commit is contained in:
2026-04-19 12:50:15 -07:00
parent c44a01f5dd
commit c4fdb5a233
13 changed files with 2238 additions and 47 deletions

View File

@@ -0,0 +1,42 @@
# Cover email — sending the HTML staff editor
**To:** Meredith Kuhn, John Trozzi (cc: Ashley Jensen)
**From:** Howard Enos — Computer Guru
**Date:** 2026-04-18
**Subject:** Optional tool to edit the staff list — try this if the Word doc feels heavy
---
Meredith / John,
I had some free time and I made this HTML file that may or may not help.
It's an alternative to marking up the Word questionnaire — same data, same questions, just in a format that might be faster to work through on a computer. Double-click the file and it opens in Edge, Chrome, Safari, whatever you have. Nothing to install, no login, no account to make.
**What you get:**
- Every staff member already grouped by department, pulled from what's in Active Directory today
- Drag the `⋮⋮` grip next to a name to move them to a different department (instead of retyping names in another table)
- Click any name or title to correct the spelling or title inline — no form fields, just click and type
- A notes box under each name for freeform comments — spelling concerns, "this person is leaving in June," "they actually belong in Memory Care," whatever you want me to know about that person
- Access-type buttons for each person: **D** = desktop only, **P** = phone only, **D+P** = both, **—** = leave blank if you're not sure and we'll decide together
- **Outside** (building) and **ALIS** are separate checkboxes — tick them independently for whoever needs them
- **+ Add** row at the bottom of every department if I've missed someone
A few people already have notes pre-filled where I had open questions — Matt Brooks' department, Christine Nyanzunda's one-or-two-accounts question, Kyla Quick Tiffany's name spelling, Patricia Sandoval-Beck's hyphen, and a few caregiver names I wasn't confident on. Answer those in the notes box next to each name and I'll have everything I need.
**How to send it back:**
Your edits autosave to the browser as you type. When you're done, click the green **Save to File** button at the top — that downloads a new copy of the HTML file with everything baked in. Email that copy back to me and I'll see every change. If you'd rather, **Export JSON** or **Export CSV** buttons also work. Any of the three is fine — whichever is easiest on your end.
If you switch computers partway through, use Save to File and open the downloaded copy on the other machine — your edits travel with the file.
**If the Word doc is easier, use the Word doc.** This is not a replacement, just another option. Whichever format gets us accurate answers is the right tool.
Thank you —
Howard
---
*Draft — prepared 2026-04-18 as a cover email for the cascades-staff-editor.html attachment.*

View File

@@ -0,0 +1,773 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Cascades — Staff &amp; Department Editor</title>
<style>
:root {
--bg:#f4f6fa; --panel:#fff; --line:#d7dce4; --ink:#1a2030;
--muted:#5c6678; --accent:#2b6cb0; --accent-ink:#fff;
--caregiver:#fff6e5; --caregiver-line:#f1c27d;
--staff:#eef3fb; --staff-line:#c5d5ec;
--warn:#b42318; --ok:#15803d; --drop:#fff8c5;
}
* { box-sizing:border-box; }
html,body { margin:0; background:var(--bg); color:var(--ink);
font:14px/1.4 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
header {
position:sticky; top:0; z-index:5; background:#1a2030; color:#fff;
padding:10px 16px; display:flex; gap:10px; align-items:center; flex-wrap:wrap;
box-shadow:0 1px 4px rgba(0,0,0,.15);
}
header h1 { font-size:16px; margin:0; flex:1; font-weight:600; }
header .sub { font-size:12px; color:#9aa4b8; margin-left:6px; }
button {
background:#2b6cb0; color:#fff; border:0; border-radius:5px;
padding:7px 12px; font:inherit; cursor:pointer;
}
button:hover { background:#224f84; }
button.ghost { background:transparent; color:#cfd6e4; border:1px solid #3a4458; }
button.ghost:hover { background:#2a3346; }
button.danger { background:#b42318; }
button.danger:hover { background:#8a1a11; }
main { padding:14px 16px 80px; max-width:1300px; margin:0 auto; }
.hint {
background:#fffbe6; border:1px solid #f1d982; color:#5c4a00;
padding:8px 12px; border-radius:6px; margin:0 0 14px; font-size:13px;
}
.hint b { color:#3d3200; }
.dept {
background:var(--panel); border:1px solid var(--line); border-radius:8px;
margin:10px 0; padding:0; overflow:hidden;
box-shadow:0 1px 3px rgba(0,0,0,.06);
}
.dept-header {
padding:10px 14px; background:#eef2f7; border-bottom:1px solid var(--line);
display:flex; gap:10px; align-items:center;
}
.dept-name { font-weight:600; font-size:15px; flex:1; outline:none; }
.dept-name:focus { background:#fff; padding:2px 6px; border-radius:4px; }
.count {
background:#fff; border:1px solid var(--line); color:var(--muted);
padding:2px 8px; border-radius:10px; font-size:12px;
}
.dept-actions { display:flex; gap:6px; }
.dept-actions button { padding:4px 8px; font-size:12px; }
.people { padding:6px 8px 10px; }
.person {
display:grid;
grid-template-columns: 18px 1.4fr 1.6fr auto auto auto auto auto auto;
gap:6px; align-items:center;
padding:6px 8px; margin:4px 0;
border:1px solid transparent; border-radius:5px;
background:var(--staff); border-color:var(--staff-line);
}
.person.caregiver { background:var(--caregiver); border-color:var(--caregiver-line); }
.person.dragging { opacity:.35; }
.person.drop-over { outline:2px dashed var(--accent); outline-offset:-2px; }
.grip {
cursor:grab; color:var(--muted); user-select:none; text-align:center;
font-size:16px; line-height:1;
}
.grip:active { cursor:grabbing; }
.field {
border:1px solid transparent; padding:4px 6px; border-radius:4px;
min-width:40px; outline:none;
}
.field:hover { border-color:#cfd6e4; background:#fff; }
.field:focus { border-color:var(--accent); background:#fff; }
.name { font-weight:600; }
.title { color:var(--muted); font-size:13px; }
.seg {
display:inline-flex; border:1px solid #c5cddb; border-radius:4px;
background:#fff; overflow:hidden; font-size:12px;
}
.seg label {
padding:3px 7px; cursor:pointer; user-select:none;
border-right:1px solid #e5e9f0;
}
.seg label:last-child { border-right:0; }
.seg input { display:none; }
.seg input:checked + span {
background:var(--accent); color:var(--accent-ink);
margin:-3px -7px; padding:3px 7px; display:inline-block;
}
.chk {
display:inline-flex; align-items:center; gap:4px;
font-size:12px; color:var(--muted); cursor:pointer; user-select:none;
padding:3px 6px; border:1px solid #c5cddb; border-radius:4px; background:#fff;
}
.chk input { accent-color:var(--accent); margin:0; }
.chk.on { color:var(--ok); border-color:#9ad6a4; background:#eefaf0; }
.dropdown-wrap { position:relative; }
select.dept-select {
font:inherit; font-size:12px; padding:4px 6px;
border:1px solid #c5cddb; border-radius:4px; background:#fff; max-width:150px;
}
.delete {
background:transparent; color:var(--warn); padding:3px 8px; font-size:16px;
line-height:1; border:1px solid transparent; border-radius:4px;
}
.delete:hover { background:#fde8e6; border-color:#f4b4ae; }
.notes {
grid-column: 2 / -1;
display:block; cursor:text;
margin-top:4px; padding:6px 8px;
border:1px dashed #cfd6e4; border-radius:4px;
background:rgba(255,255,255,.7);
font-size:12px; color:#2a3140; min-height:20px;
outline:none; white-space:pre-wrap; word-wrap:break-word;
}
.notes:hover { border-color:#9aa4b8; background:#fff; }
.notes:focus {
border-style:solid; border-color:var(--accent); background:#fff;
box-shadow:0 0 0 2px rgba(43,108,176,.15);
}
.notes:empty::before {
content: attr(data-placeholder);
color:#9aa4b8; font-style:italic;
}
.add-row {
padding:6px 8px; display:flex; gap:6px; align-items:center;
border-top:1px dashed #dbe0ea; margin-top:4px;
}
.add-row input {
flex:1; padding:5px 8px; border:1px solid #c5cddb; border-radius:4px;
font:inherit;
}
.add-row button { padding:5px 10px; font-size:13px; }
.drop-zone { min-height:24px; }
.drop-zone.drop-over {
background:var(--drop); border:2px dashed #caa300; border-radius:6px;
}
footer {
position:fixed; bottom:0; left:0; right:0;
background:#fff; border-top:1px solid var(--line);
padding:10px 16px; display:flex; gap:8px; align-items:center; flex-wrap:wrap;
box-shadow:0 -1px 3px rgba(0,0,0,.06);
}
footer .status { color:var(--muted); font-size:12px; flex:1; }
.legend { color:var(--muted); font-size:12px; }
.legend span { display:inline-block; width:10px; height:10px; border-radius:2px; vertical-align:middle; margin:0 4px 0 10px; }
.legend .l-staff { background:var(--staff); border:1px solid var(--staff-line); }
.legend .l-cg { background:var(--caregiver); border:1px solid var(--caregiver-line); }
@media (max-width: 900px) {
.person {
grid-template-columns: 18px 1fr auto auto;
}
.person .title, .person .seg, .person .dropdown-wrap { grid-column: 2 / -1; }
}
@media print {
header, footer, .dept-actions, .add-row, .delete, .grip { display:none !important; }
.person { break-inside:avoid; background:#fff !important; border-color:#bbb !important; }
.dept { break-inside:avoid; page-break-inside:avoid; }
.dept-header { background:#eee !important; }
}
</style>
</head>
<body>
<header>
<h1>Cascades — Staff &amp; Department Editor</h1>
<span class="sub" id="savedTag">loaded</span>
<button onclick="saveToFile()" title="Download an updated copy of this HTML file with all your edits baked in. Works on any machine — just open the downloaded file." style="background:#15803d;">Save to File</button>
<button onclick="exportJSON()" title="Download everything as JSON">Export JSON</button>
<button onclick="exportCSV()" title="Download as CSV for spreadsheet">Export CSV</button>
<button class="ghost" onclick="importJSON()">Import JSON</button>
<button class="ghost" onclick="window.print()">Print</button>
<button class="danger" onclick="resetAll()" title="Revert to the original roster">Reset</button>
</header>
<main>
<div class="hint">
<b>How to use:</b>
<ul style="margin:6px 0 0; padding-left:22px;">
<li><b>Drag</b> the <b>⋮⋮</b> grip on any name to move them to another department — drop on the department header or anywhere in its list.</li>
<li><b>Click</b> any name or title to edit it inline.</li>
<li><b>Notes box</b> under each name — click it to add spelling concerns, department conflicts, schedule notes, or anything else we should know about that person.</li>
<li>
<b>Access type</b> (pick one):
<b>D</b> = desktop / PC only,
<b>P</b> = phone only,
<b>D+P</b> = both a desk and a shared phone,
<b></b> = not set / leave blank if you're not sure and we'll decide together.
</li>
<li><b>Outside</b> is a <em>separate</em> checkbox — tick it if the person is allowed to sign in from outside the building (home, personal cell, travel). Leave it unchecked to lock them to Cascades. Works with any access type.</li>
<li><b>ALIS</b> is a <em>separate</em> checkbox — tick it for anyone who logs into ALIS.</li>
<li>Use the <b>+ Add</b> row at the bottom of a department to add a person we've missed.</li>
</ul>
</div>
</p>
<div id="board"></div>
<div style="margin-top:18px; display:flex; gap:8px; align-items:center;">
<input id="newDept" placeholder="New department name (e.g. Security, Activities)" style="flex:1; padding:8px 10px; border:1px solid var(--line); border-radius:5px; font:inherit;">
<button onclick="addDept()">+ Add department</button>
</div>
</main>
<footer>
<span class="status" id="status">&nbsp;</span>
<span class="legend">
Legend: <span class="l-staff"></span>Staff <span class="l-cg"></span>Caregiver
</span>
</footer>
<script>
// -----------------------------------------------------------------------------
// Initial roster — mirrors clients/cascades-tucson/scripts/generate-user-questionnaire.py
// -----------------------------------------------------------------------------
const INITIAL = {
departments: [
"Administrative","Marketing / Sales",
"Care, Assisted Living (Nursing / Clinical)","Care, Memory Care",
"Resident Services","Life Enrichment","Culinary","Maintenance",
"Housekeeping","Transportation","Caregivers (shift staff)"
],
people: [
// Staff
["Meredith Kuhn","Executive Director","Administrative","D+P",true,true,""],
["Ashley Jensen","Assistant Executive Director","Administrative","D+P",true,true,""],
["Lauren Hasselman","Business Office Director","Administrative","D",true,false,""],
["Allison Reibschied","Accounting Assistant","Administrative","D",false,false,""],
["Megan Hiatt","Sales Director","Marketing / Sales","D+P",true,true,"Handles resident intake (PHI)"],
["Crystal Rodriguez","Sales Associate","Marketing / Sales","D+P",true,true,"Handles resident intake (PHI)"],
["Tamra Matthews","Move-In Coordinator","Marketing / Sales","D+P",true,true,"Leaving June 2026 — confirm"],
["Lois Lane","Health Services Director","Care, Assisted Living (Nursing / Clinical)","D+P",true,true,""],
["Karen Rossini","Health Services Manager","Care, Assisted Living (Nursing / Clinical)","D+P",true,true,""],
["Britney Thompson","Memory Care Nurse","Care, Assisted Living (Nursing / Clinical)","D+P",true,true,"Title says Memory Care — which department?"],
["Veronica Feller","Care, Assisted Living Aide","Care, Assisted Living (Nursing / Clinical)","P",false,true,""],
["Shelby Trozzi","Memory Care Director","Care, Memory Care","D+P",true,true,""],
["Christine Nyanzunda","Memory Care Admin Assistant","Care, Memory Care","D+P",true,true,"Also on caregiver list — same person?"],
["Christina DuPras","Resident Services Director","Resident Services","D+P",true,true,""],
["Cathy Kingston","Receptionist","Resident Services","D",false,false,"Front desk shared PC"],
["Shontiel Nunn","Receptionist","Resident Services","D",false,false,"Front desk shared PC"],
["Kyla Quick Tiffany","Receptionist","Resident Services","D",false,false,"Is the spelling correct? Three separate names, or is it 'Quick-Tiffany' with a hyphen?"],
["Michelle Shestko","MC Receptionist","Resident Services","D",false,false,"MC front desk shared PC"],
["Sebastian Leon","Courtesy Patrol","Resident Services","P",false,false,""],
["Sheldon Gardfrey","Courtesy Patrol","Resident Services","P",false,false,""],
["Ray Rai","Courtesy Patrol","Resident Services","P",false,false,""],
["Susan Hicks","Life Enrichment Director","Life Enrichment","D",true,false,""],
["Sharon Edwards","Life Enrichment Assistant","Life Enrichment","D",false,false,""],
["JD Martin","Culinary Director","Culinary","D+P",false,false,""],
["Ramon Castaneda","Kitchen Manager","Culinary","D+P",false,false,""],
["Alyssa Brooks","Dining Manager","Culinary","D+P",false,false,""],
["John Trozzi","Maintenance Director","Maintenance","D+P",true,false,""],
["Matt Brooks","Memory Care Receptionist","Maintenance","D+P",false,true,"HR says Maintenance — which is correct?"],
["Lupe Sanchez","Housekeeping Director","Housekeeping","D+P",false,false,"AKA Guadalupe Sanchez"],
["Richard Adams","Driver","Transportation","P",false,false,""],
["Julian Crim","Driver","Transportation","P",false,false,""],
["Christopher Holick","Driver","Transportation","P",false,false,""],
// Caregivers (default P, no outside, no ALIS — flag ones that differ)
["Thelma Abainza","Caregiver — Tower (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Niel Castro","MedTech / CCG — Tower (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Espe Esperance","MedTech — Tower (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Barbara Johnson","Caregiver — Tower (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Kasey Flores","Caregiver — Memory Care (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Richard Flores","Caregiver — Memory Care (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Marie Kastner","Caregiver — Memory Care (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Bella Mendoza","Caregiver — Memory Care (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Rosa Morales","MedTech — Memory Care (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Sandra Padilla","MedTech / CCG — Tower (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Polett Pinazavala","MedTech — Memory Care (TueSat)","Caregivers (shift staff)","P",false,false,"Confirm spelling"],
["Whisper Reed","MedTech — Tower overnight (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Patricia Sandoval-Beck","MedTech — Tower (TueSat)","Caregivers (shift staff)","P",false,false,"Hyphenated last name — correct?"],
["Charity Sika","Caregiver — Memory Care (TueSat)","Caregivers (shift staff)","P",false,false,""],
["Ederick Yuzon","Caregiver — Tower (TueSat)","Caregivers (shift staff)","P",false,false,"Confirm spelling"],
["Juan Andrade","Caregiver — Memory Care (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Jahmeka Clarke","MedTech — Memory Care (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Karina Aziakpo","MedTech / CCG — MC overnight (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Jinnelle Dittbenner","Caregiver — Tower (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Agnes McFerren","Caregiver — Tower (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Samuel Ramirez","Caregiver — Tower (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Erica Sanchez","Caregiver — Memory Care (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Katrina Wyzykowski","MedTech — Memory Care (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Corey Tate","Caregiver — Tower NOC (SunThu)","Caregivers (shift staff)","P",false,false,""],
["Ashli Atwood","MedTech / CCG — MC overnight (FriMon)","Caregivers (shift staff)","P",false,false,""],
["Cole Johnson","MedTech — Tower (FriMon)","Caregivers (shift staff)","P",false,false,""],
["Roseline Cooper","Caregiver — MC overnight (FriMon)","Caregivers (shift staff)","P",false,false,""],
["Monique Lopez","Caregiver — Tower Fri+Sat doubles","Caregivers (shift staff)","P",false,false,""],
["Gloria Williford","MedTech — MC Fri+Sat doubles","Caregivers (shift staff)","P",false,false,""],
["Sarah Carroll","Caregiver — Tower (ThuMon)","Caregivers (shift staff)","P",false,false,""],
["Luke Hogan","Caregiver — Tower (ThuMon)","Caregivers (shift staff)","P",false,false,""],
["Gina Williams","Caregiver — Tower (ThuMon)","Caregivers (shift staff)","P",false,false,""],
["Jen Higdon","Caregiver — Tower M/W/F AM","Caregivers (shift staff)","P",false,false,""],
["Mary Kariuki","Caregiver — Tower SatMon + Wed PM","Caregivers (shift staff)","P",false,false,""],
["CeCe Lassey","Caregiver — Tower Sun/Mon doubles + Tue PM","Caregivers (shift staff)","P",false,false,""],
["Paty Doran","MedTech / CCG — Tower Sun/Mon only","Caregivers (shift staff)","P",false,false,"Paty, Patti, or Patricia?"],
["Ezekiel Huerta","Caregiver PRN — Tower","Caregivers (shift staff)","P",false,false,""],
["Maia Baker","MedTech PRN — Memory Care","Caregivers (shift staff)","P",false,false,"Is she still employed?"],
]
};
const CAREGIVER_DEPT = "Caregivers (shift staff)";
const STORAGE_KEY = "cascades-staff-editor-v2";
// -----------------------------------------------------------------------------
// State
// -----------------------------------------------------------------------------
let state = pickInitialState();
function pickInitialState() {
// Priority: baked-in state from a previous "Save to File" (if it's the newest),
// then this-browser localStorage, then the original roster.
const embedded = (typeof window !== "undefined" && window.__SAVED_STATE__) || null;
const local = load();
if (embedded && local) {
const emT = new Date(embedded.savedAt || 0).getTime();
const loT = new Date(local.savedAt || 0).getTime();
return emT >= loT ? embedded : local;
}
return embedded || local || rebuild(INITIAL);
}
function rebuild(src) {
let pid = 1;
return {
departments: [...src.departments],
people: src.people.map(p => ({
id: pid++, name: p[0], title: p[1], dept: p[2],
access: p[3], outside: !!p[4], alis: !!p[5], notes: p[6] || ""
})),
savedAt: new Date().toISOString()
};
}
function save() {
state.savedAt = new Date().toISOString();
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
document.getElementById("savedTag").textContent = "saved " + new Date().toLocaleTimeString();
} catch(e) { document.getElementById("savedTag").textContent = "save failed"; }
}
function load() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
const obj = JSON.parse(raw);
if (!obj.departments || !obj.people) return null;
return obj;
} catch(e) { return null; }
}
function resetAll() {
if (!confirm("Reset everything back to the original Howard-provided roster? Your edits will be lost.")) return;
state = rebuild(INITIAL);
save(); render();
}
// -----------------------------------------------------------------------------
// Rendering
// -----------------------------------------------------------------------------
function render() {
const board = document.getElementById("board");
board.innerHTML = "";
// Ensure every person's dept still exists; if not, put them in an Unsorted dept
const deptSet = new Set(state.departments);
for (const p of state.people) {
if (!deptSet.has(p.dept)) {
if (!deptSet.has("Unsorted")) { state.departments.push("Unsorted"); deptSet.add("Unsorted"); }
p.dept = "Unsorted";
}
}
for (const dept of state.departments) {
const peopleInDept = state.people.filter(p => p.dept === dept);
const det = document.createElement("div");
det.className = "dept"; det.dataset.dept = dept;
const sum = document.createElement("div");
sum.className = "dept-header";
sum.innerHTML = `
<span class="dept-name" contenteditable spellcheck="false"
onblur="renameDept(this, '${escapeAttr(dept)}')"
onkeydown="if(event.key==='Enter'){event.preventDefault();this.blur();}">${escapeHTML(dept)}</span>
<span class="count">${peopleInDept.length}</span>
<span class="dept-actions">
<button onclick="moveDeptUp('${escapeAttr(dept)}')" title="Move up">↑</button>
<button onclick="moveDeptDown('${escapeAttr(dept)}')" title="Move down">↓</button>
<button class="danger" onclick="deleteDept('${escapeAttr(dept)}')" title="Delete department (moves members to Unsorted)">✕</button>
</span>`;
det.appendChild(sum);
const list = document.createElement("div");
list.className = "people drop-zone";
list.dataset.dept = dept;
list.addEventListener("dragover", onDragOver);
list.addEventListener("dragleave", onDragLeave);
list.addEventListener("drop", onDrop);
sum.addEventListener("dragover", onDragOver);
sum.addEventListener("dragleave", onDragLeave);
sum.addEventListener("drop", onDrop);
for (const p of peopleInDept) list.appendChild(personRow(p));
// Add-person row
const add = document.createElement("div");
add.className = "add-row";
add.innerHTML = `
<input placeholder="New person — name" data-field="name">
<input placeholder="Title / role" data-field="title" style="flex:0.8;">
<button>+ Add</button>`;
const btn = add.querySelector("button");
btn.onclick = () => {
const name = add.querySelector("[data-field=name]").value.trim();
const title = add.querySelector("[data-field=title]").value.trim();
if (!name) return;
const isCg = dept === CAREGIVER_DEPT;
state.people.push({
id: nextId(), name, title, dept,
access: isCg ? "P" : "D+P",
outside: !isCg, alis: false, notes: ""
});
save(); render();
};
list.appendChild(add);
det.appendChild(list);
board.appendChild(det);
}
updateStatus();
}
function personRow(p) {
const row = document.createElement("div");
row.className = "person" + (p.dept === CAREGIVER_DEPT ? " caregiver" : "");
row.dataset.id = p.id;
// Only the grip handle is draggable — a draggable row would steal mousedown
// from the contenteditable fields and break clicking inside the notes.
row.innerHTML = `
<span class="grip" draggable="true" title="Drag to move this person to another department">⋮⋮</span>
<span class="field name" contenteditable spellcheck="false"
data-id="${p.id}" data-k="name">${escapeHTML(p.name)}</span>
<span class="field title" contenteditable spellcheck="false"
data-id="${p.id}" data-k="title">${escapeHTML(p.title)}</span>
<span class="seg" title="Access type">
${accessSeg(p)}
</span>
<label class="chk ${p.outside ? "on" : ""}" title="Allowed to sign in outside the building">
<input type="checkbox" ${p.outside ? "checked" : ""} data-id="${p.id}" data-k="outside">Outside
</label>
<label class="chk ${p.alis ? "on" : ""}" title="Uses ALIS">
<input type="checkbox" ${p.alis ? "checked" : ""} data-id="${p.id}" data-k="alis">ALIS
</label>
<span class="dropdown-wrap">
<select class="dept-select" data-id="${p.id}" title="Move to another department">
${state.departments.map(d => `<option ${d===p.dept?"selected":""}>${escapeHTML(d)}</option>`).join("")}
</select>
</span>
<button class="delete" title="Remove this person" data-id="${p.id}">✕</button>
<span class="notes" contenteditable spellcheck="true"
data-id="${p.id}" data-k="notes"
data-placeholder="Click to add notes about this person — spelling, department conflicts, schedule, etc.">${escapeHTML(p.notes)}</span>
`;
// Wire handlers (delegation would work too but this keeps it local)
row.querySelectorAll("[contenteditable]").forEach(el => {
el.addEventListener("blur", onFieldBlur);
el.addEventListener("keydown", e => {
// Enter blurs name/title, but in notes we allow multi-line
if (e.key === "Enter" && !el.classList.contains("notes")) {
e.preventDefault(); el.blur();
}
});
});
row.querySelectorAll(".notes").forEach(el => {
el.addEventListener("mousedown", onNotesMouseDown);
});
row.querySelectorAll('input[type="radio"]').forEach(el => el.addEventListener("change", onAccessChange));
row.querySelectorAll('input[type="checkbox"]').forEach(el => el.addEventListener("change", onToggle));
row.querySelector("select.dept-select").addEventListener("change", onDeptSelect);
row.querySelector("button.delete").addEventListener("click", onDelete);
// Grip is the only drag initiator
const grip = row.querySelector(".grip");
grip.addEventListener("dragstart", onDragStart);
grip.addEventListener("dragend", onDragEnd);
return row;
}
function accessSeg(p) {
// D / P / D+P are the real values. "—" is a clear / not-yet-decided option.
// Users can also click a selected button again to deselect it.
const opts = [
{v:"D", label:"D", title:"Desktop / PC only"},
{v:"P", label:"P", title:"Phone only"},
{v:"D+P", label:"D+P", title:"Both desktop and phone"},
{v:"", label:"—", title:"Not set / unknown"}
];
return opts.map(o => `
<label title="${o.title}">
<input type="radio" name="acc-${p.id}" value="${o.v}" ${p.access===o.v?"checked":""} data-id="${p.id}" data-k="access">
<span>${o.label}</span>
</label>`).join("");
}
function updateStatus() {
const total = state.people.length;
const staff = state.people.filter(p => p.dept !== CAREGIVER_DEPT).length;
const cg = total - staff;
const outside = state.people.filter(p => p.outside).length;
const alis = state.people.filter(p => p.alis).length;
document.getElementById("status").textContent =
`${total} people (${staff} staff + ${cg} caregiver) · ${outside} outside-access · ${alis} ALIS · ${state.departments.length} departments`;
}
// -----------------------------------------------------------------------------
// Field handlers
// -----------------------------------------------------------------------------
function onFieldBlur(e) {
const id = +e.target.dataset.id, k = e.target.dataset.k;
const p = state.people.find(x => x.id === id); if (!p) return;
const v = e.target.textContent.trim();
if (p[k] !== v) { p[k] = v; save(); updateStatus(); }
}
function onAccessChange(e) {
const id = +e.target.dataset.id;
const p = state.people.find(x => x.id === id); if (!p) return;
p.access = e.target.value; save();
}
function onToggle(e) {
const id = +e.target.dataset.id, k = e.target.dataset.k;
const p = state.people.find(x => x.id === id); if (!p) return;
p[k] = e.target.checked;
e.target.closest("label").classList.toggle("on", e.target.checked);
save(); updateStatus();
}
function onDeptSelect(e) {
const id = +e.target.dataset.id;
const p = state.people.find(x => x.id === id); if (!p) return;
p.dept = e.target.value; save(); render();
}
function onDelete(e) {
const id = +e.currentTarget.dataset.id;
const p = state.people.find(x => x.id === id); if (!p) return;
if (!confirm(`Remove ${p.name} from the list? (You can add them back later.)`)) return;
state.people = state.people.filter(x => x.id !== id);
save(); render();
}
function renameDept(el, oldName) {
const newName = el.textContent.trim();
if (!newName || newName === oldName) { el.textContent = oldName; return; }
if (state.departments.includes(newName)) {
alert("A department with that name already exists."); el.textContent = oldName; return;
}
const i = state.departments.indexOf(oldName);
if (i >= 0) state.departments[i] = newName;
for (const p of state.people) if (p.dept === oldName) p.dept = newName;
save(); render();
}
function moveDeptUp(name) {
const i = state.departments.indexOf(name); if (i <= 0) return;
[state.departments[i-1], state.departments[i]] = [state.departments[i], state.departments[i-1]];
save(); render();
}
function moveDeptDown(name) {
const i = state.departments.indexOf(name); if (i < 0 || i >= state.departments.length-1) return;
[state.departments[i+1], state.departments[i]] = [state.departments[i], state.departments[i+1]];
save(); render();
}
function deleteDept(name) {
const n = state.people.filter(p => p.dept === name).length;
if (!confirm(`Delete department "${name}"?` + (n ? ` ${n} person(s) will move to Unsorted.` : ""))) return;
state.departments = state.departments.filter(d => d !== name);
for (const p of state.people) if (p.dept === name) p.dept = "Unsorted";
if (state.people.some(p => p.dept === "Unsorted") && !state.departments.includes("Unsorted"))
state.departments.push("Unsorted");
save(); render();
}
function addDept() {
const el = document.getElementById("newDept");
const name = el.value.trim();
if (!name) return;
if (state.departments.includes(name)) { alert("That department already exists."); return; }
state.departments.push(name); el.value = "";
save(); render();
}
// -----------------------------------------------------------------------------
// Drag & drop
// -----------------------------------------------------------------------------
let dragId = null;
function onDragStart(e) {
const row = e.currentTarget.closest(".person");
if (!row) return;
dragId = +row.dataset.id;
row.classList.add("dragging");
e.dataTransfer.effectAllowed = "move";
try { e.dataTransfer.setData("text/plain", String(dragId)); } catch(_) {}
try { e.dataTransfer.setDragImage(row, 20, 10); } catch(_) {}
}
function onDragEnd(e) {
const row = e.currentTarget.closest(".person");
if (row) row.classList.remove("dragging");
document.querySelectorAll(".drop-over").forEach(n => n.classList.remove("drop-over"));
dragId = null;
}
function onDragOver(e) {
e.preventDefault();
const zone = e.currentTarget;
zone.classList.add("drop-over");
e.dataTransfer.dropEffect = "move";
}
function onDragLeave(e) {
e.currentTarget.classList.remove("drop-over");
}
function onDrop(e) {
e.preventDefault();
const zone = e.currentTarget;
zone.classList.remove("drop-over");
const dept = zone.dataset.dept || zone.closest(".dept")?.dataset.dept;
if (!dept) return;
const id = dragId || +e.dataTransfer.getData("text/plain");
const p = state.people.find(x => x.id === id); if (!p) return;
if (p.dept === dept) return;
p.dept = dept; save(); render();
}
// -----------------------------------------------------------------------------
// Export / import
// -----------------------------------------------------------------------------
function saveToFile() {
// Timestamp the state and persist to localStorage as well
state.savedAt = new Date().toISOString();
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch(_) {}
// Build a clone of the document, inject/update the embedded-state <script>,
// strip transient UI bits, and serialize.
const clone = document.documentElement.cloneNode(true);
const headClone = clone.querySelector("head");
let sEl = clone.querySelector("#saved-state");
if (!sEl) {
sEl = document.createElement("script");
sEl.id = "saved-state";
headClone.appendChild(sEl);
}
// Escape any close-script token that might appear in a notes field
const json = JSON.stringify(state).replace(/<\/scr(?=ipt)/gi, "<\\/scr");
sEl.textContent = "window.__SAVED_STATE__ = " + json + ";";
// Empty the rendered board — it's rebuilt from state on load, so we don't
// need to carry the DOM. Also clear any transient input/UI state.
const bc = clone.querySelector("#board"); if (bc) bc.innerHTML = "";
const nd = clone.querySelector("#newDept"); if (nd) nd.removeAttribute("value");
const st = clone.querySelector("#savedTag"); if (st) st.textContent = "loaded";
clone.querySelectorAll(".dragging, .drop-over").forEach(n => {
n.classList.remove("dragging"); n.classList.remove("drop-over");
});
const html = "<!doctype html>\n" + clone.outerHTML;
downloadBlob("cascades-staff-editor.html", html, "text/html");
document.getElementById("savedTag").textContent = "file saved " + new Date().toLocaleTimeString();
}
function exportJSON() {
const data = JSON.stringify(state, null, 2);
downloadBlob(`cascades-staff-${stamp()}.json`, data, "application/json");
}
function exportCSV() {
const cols = ["Department","Name","Title / Role","Access","Outside Access","ALIS","Notes"];
const rows = [cols];
for (const dept of state.departments) {
for (const p of state.people.filter(x => x.dept === dept)) {
rows.push([dept, p.name, p.title, p.access,
p.outside ? "Y":"N", p.alis ? "Y":"N", p.notes || ""]);
}
}
const csv = rows.map(r => r.map(csvEscape).join(",")).join("\r\n");
downloadBlob(`cascades-staff-${stamp()}.csv`, csv, "text/csv");
}
function importJSON() {
const inp = document.createElement("input");
inp.type = "file"; inp.accept = ".json,application/json";
inp.onchange = () => {
const f = inp.files[0]; if (!f) return;
const r = new FileReader();
r.onload = () => {
try {
const obj = JSON.parse(r.result);
if (!obj.departments || !obj.people) throw new Error("Missing fields");
state = obj; save(); render();
alert("Imported OK.");
} catch(e) { alert("Could not read that file: " + e.message); }
};
r.readAsText(f);
};
inp.click();
}
function downloadBlob(name, data, mime) {
const blob = new Blob([data], {type: mime});
const a = document.createElement("a");
a.href = URL.createObjectURL(blob); a.download = name;
document.body.appendChild(a); a.click();
setTimeout(() => { URL.revokeObjectURL(a.href); a.remove(); }, 0);
}
function csvEscape(v) {
v = String(v ?? "");
return /[",\r\n]/.test(v) ? `"${v.replace(/"/g,'""')}"` : v;
}
function stamp() {
const d = new Date(), p = n => String(n).padStart(2,"0");
return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}`;
}
// -----------------------------------------------------------------------------
// Utilities
// -----------------------------------------------------------------------------
function onNotesMouseDown(e) {
// If the click doesn't land on a text position inside this element
// (e.g. the user clicked past the end of the sentence or in the padding),
// place the caret at the end so typing and backspace just work.
const el = e.currentTarget;
let inside = false;
if (document.caretPositionFromPoint) {
const p = document.caretPositionFromPoint(e.clientX, e.clientY);
if (p && el.contains(p.offsetNode)) inside = true;
} else if (document.caretRangeFromPoint) {
const r = document.caretRangeFromPoint(e.clientX, e.clientY);
if (r && el.contains(r.startContainer)) inside = true;
}
if (!inside) {
e.preventDefault();
el.focus();
const r = document.createRange();
r.selectNodeContents(el);
r.collapse(false);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(r);
}
}
function nextId() { return Math.max(0, ...state.people.map(p => p.id)) + 1; }
function escapeHTML(s) { return String(s ?? "").replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
function escapeAttr(s) { return String(s ?? "").replace(/'/g, "\\'"); }
// -----------------------------------------------------------------------------
// Go
// -----------------------------------------------------------------------------
render();
</script>
</body>
</html>

View File

@@ -0,0 +1,225 @@
# Draft email — User + Department + Access confirmation
**To:** Meredith Kuhn, John Trozzi (cc: Ashley Jensen, HR)
**From:** Howard Enos — Computer Guru
**Date:** 2026-04-18
**Subject:** Please review — full staff list, department, and access (before we buy licenses / create caregiver accounts)
---
Meredith / John,
Before we purchase the Microsoft 365 Business Premium licenses and start creating caregiver accounts, I want to make sure we have **every name, spelling, department, and how each person actually uses technology** correct. Changing this AFTER accounts are created is painful (email addresses, phone profiles, security groups all have to be rebuilt).
Please review the list below and mark it up — corrections, additions, or removals. You can reply directly in email, print and mark up, or we can walk through it on a call. A few notes on the columns:
- **Access type** — how they actually use technology:
- **D** = Desktop / PC only (they sit at a computer, they do not need a company phone)
- **P** = Phone only (shared Android phone on shift — no personal PC at Cascades)
- **D+P** = Both (desk for office work, shared phone during rounds / on the floor)
- **Outside access** — should this person be allowed to sign in from **outside the building** (home, personal cell, hotel, etc.)? Y = yes, N = no (locked to Cascades network/trusted device).
- **ALIS** — does this person log into ALIS? ALIS is a website they sign into; it can be connected to their Microsoft account so they do NOT get a second MFA prompt — but only if we know who needs it.
Where I've filled in best guesses I've put them in parentheses — those are the ones I most need you to confirm or correct.
---
## 1. Administrative
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Meredith Kuhn | Executive Director | (D+P) | (Y) | (Y) |
| Ashley Jensen | Assistant Executive Director | (D+P) | (Y) | (Y) |
| Lauren Hasselman | Business Office Director | (D) | (Y) | (N) |
| Allison Reibschied | Accounting Assistant | (D) | (N) | (N) |
## 2. Marketing / Sales
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Megan Hiatt | Sales Director | D+P | **Y** (home + personal cell — handles resident intake forms) | (Y — intake PHI) |
| Crystal Rodriguez | Sales Associate | D+P | **Y** (home + personal cell — handles resident intake forms) | (Y — intake PHI) |
| Tamra Matthews | Move-In Coordinator | (D+P) | **Y** (home + personal cell) — **leaving June 2026** | (Y — intake PHI) |
## 3. Care — Assisted Living (clinical / nursing)
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Lois Lane | Health Services Director | (D+P) | (Y) | (Y) |
| Karen Rossini | Health Services Manager | (D+P) | (Y) | (Y) |
| Britney Thompson | Memory Care Nurse | (D+P) | (Y) | (Y) |
| Veronica Feller | Care, Assisted Living Aide | (P) | (N) | (Y) |
**Note:** Britney's AD department is currently "Care, Assisted Living" but her title says Memory Care. Which department should she belong to for licensing / security group purposes?
## 4. Care — Memory Care
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Shelby Trozzi | Memory Care Director | (D+P) | (Y) | (Y) |
| Christine Nyanzunda | Memory Care Admin Assistant | (D+P) | (Y — or N?) | (Y) |
## 5. Resident Services
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Christina DuPras | Resident Services Director | (D+P) | (Y) | (Y) |
| Cathy Kingston | Receptionist | (D — shared front desk PC) | (N) | (N) |
| Shontiel Nunn | Receptionist | (D — shared front desk PC) | (N) | (N) |
| Kyla Quick Tiffany | Receptionist — **not yet in AD** | (D — shared front desk PC) | (N) | (N) |
| Michelle Shestko | MC Receptionist | (D — shared front desk PC) | (N) | (N) |
| Sebastian Leon | Courtesy Patrol | (P — shared phone on rounds?) | (N) | (N) |
| Sheldon Gardfrey | Courtesy Patrol | (P — shared phone on rounds?) | (N) | (N) |
| Ray Rai | Courtesy Patrol | (P — shared phone on rounds?) | (N) | (N) |
**Question:** Do the Courtesy Patrol staff need email at all, or just a shared-phone identity? And is the spelling **Kyla Quick Tiffany** (three names) correct, or is it **Kyla Quick-Tiffany** (hyphen) or different?
## 6. Life Enrichment
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Susan Hicks | Life Enrichment Director | D | (Y — or N?) | (N) |
| Sharon Edwards | Life Enrichment Assistant | D | (N) | (N) |
## 7. Culinary
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| JD Martin | Culinary Director | (D+P) | (N) | (N) |
| Ramon Castaneda | Kitchen Manager | (D+P) | (N) | (N) |
| Alyssa Brooks | Dining Manager | (D+P) | (N) | (N) |
**Note:** The kitchen has 9 shared iPads planned (separate from the 25 shared phones). Should kitchen staff sign in on those iPads with their own account, or stay on a shared kitchen iPad identity?
## 8. Maintenance
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| John Trozzi | Maintenance Director | (D+P) | (Y) | (N) |
| Matt Brooks | Memory Care Receptionist | (D+P) | (N) | (Y?) |
**Question:** Matt Brooks' HR record shows department = **Maintenance** but his title is **Memory Care Receptionist**. Which is correct? That affects where he gets filed and what he has access to.
## 9. Housekeeping
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Lupe Sanchez (Guadalupe Sanchez) | Housekeeping Director | (D+P) | (N) | (N) |
## 10. Transportation
| Name | Title | Access | Outside Access | ALIS |
|---|---|---|---|---|
| Richard Adams | Driver | (P only) | (N) | (N) |
| Julian Crim | Driver | (P only) | (N) | (N) |
| Christopher Holick | Driver | (P only) | (N) | (N) |
---
## 11. Caregivers — NEW (39 people, not yet in AD or M365)
These are the staff currently using shared workstation logins. None of them has an email account or Microsoft identity today. As part of the shared-phone + HIPAA project, each person would get their own login that gets used ONLY on the shared phones (and shared workstations via the same identity).
**Default assumption for every caregiver:** Phone-only (**P**), no outside access (**N** — locked to Cascades network + managed phone). Please flag anyone who should differ from that default (for example, a MedTech who also charts at a desktop would be **D+P**; a salaried staff member who also works from home would need outside access **Y**).
### TuesdaySaturday shift (15)
| # | Name | Role | Location | Phone | Confirm spelling / access |
|---|---|---|---|---|---|
| 1 | Thelma Abainza | Caregiver | Tower | 520-867-2579 | |
| 2 | Niel Castro | MedTech / CCG | Tower | 520-697-4644 | |
| 3 | Espe Esperance | MedTech | Tower | 520-788-9558 | |
| 4 | Barbara Johnson | Caregiver | Tower | 520-204-3449 | |
| 5 | Kasey Flores | Caregiver | Memory Care | 520-250-1451 | |
| 6 | Richard Flores | Caregiver | Memory Care | 520-873-7727 | |
| 7 | Marie Kastner | Caregiver | Memory Care | 714-576-9858 | |
| 8 | Bella Mendoza | Caregiver | Memory Care | 520-358-2000 | |
| 9 | Rosa Morales | MedTech | Memory Care | 312-213-8780 | |
| 10 | Sandra Padilla | MedTech / CCG | Tower | 520-585-3317 | |
| 11 | **Polett Pinazavala** | MedTech | Memory Care | 520-449-5533 | **Please confirm spelling** |
| 12 | Whisper Reed | MedTech | Tower (overnight) | 520-312-7575 | |
| 13 | **Patricia Sandoval-Beck** | MedTech | Tower | 520-343-8093 | **Please confirm the hyphenated last name is correct** |
| 14 | Charity Sika | Caregiver | Memory Care | 623-251-8032 | |
| 15 | **Ederick Yuzon** | Caregiver | Tower | 520-603-8816 | **Please confirm spelling** |
### SundayThursday shift (10)
| # | Name | Role | Location | Phone | Confirm |
|---|---|---|---|---|---|
| 16 | Juan Andrade | Caregiver | Memory Care | 520-528-4078 | |
| 17 | Jahmeka Clarke | MedTech | Memory Care | 520-649-7034 | |
| 18 | Karina Aziakpo | MedTech / CCG | Memory Care (overnight) | 520-392-6859 | |
| 19 | Jinnelle Dittbenner | Caregiver | Tower | 520-499-9996 | |
| 20 | **Christine Nyanzunda** | MedTech | Memory Care (Sun/Mon AM) | 520-304-4251 | **Same person as the Memory Care Admin Assistant above?** We should only create ONE account. |
| 21 | Agnes McFerren | Caregiver | Tower | 520-406-3063 | |
| 22 | Samuel Ramirez | Caregiver | Tower | 520-488-5798 | |
| 23 | Erica Sanchez | Caregiver | Memory Care | 520-528-3387 | |
| 24 | Katrina Wyzykowski | MedTech | Memory Care | 520-347-1448 | |
| 25 | Corey Tate | Caregiver (no MedTech) | Tower (NOC) | 520-535-7821 | |
### FridayMonday / weekend (5)
| # | Name | Role | Location | Phone | Confirm |
|---|---|---|---|---|---|
| 26 | Ashli Atwood | MedTech / CCG | Memory Care (overnight) | 715-200-1295 | |
| 27 | Cole Johnson | MedTech | Tower | 818-970-0890 | |
| 28 | Roseline Cooper | Caregiver | Memory Care (overnight) | 520-278-6817 | |
| 29 | Monique Lopez | Caregiver (doubles Fri/Sat) | Tower | 520-596-0969 | |
| 30 | Gloria Williford | MedTech (doubles Fri/Sat) | Memory Care | 928-551-1682 | |
### ThursdayMonday shift (3)
| # | Name | Role | Location | Phone | Confirm |
|---|---|---|---|---|---|
| 31 | Sarah Carroll | Caregiver | Tower | 520-409-2341 | |
| 32 | Luke Hogan | Caregiver | Tower | 520-312-0141 | |
| 33 | Gina Williams | Caregiver | Tower | 520-612-5075 | |
### Split / partial-week (3)
| # | Name | Role | Location | Phone | Confirm |
|---|---|---|---|---|---|
| 34 | Jen Higdon | Caregiver | Tower (M/W/F AM) | 520-730-3548 | |
| 35 | Mary Kariuki | Caregiver | Tower (SatMon + Wed PM) | 520-309-1247 | |
| 36 | CeCe Lassey | Caregiver | Tower (Sun/Mon doubles + Tue PM) | 520-248-5982 | |
### Sunday & Monday only (1)
| # | Name | Role | Location | Phone | Confirm |
|---|---|---|---|---|---|
| 37 | **Paty Doran** | MedTech / CCG | Tower | 520-591-7368 | **Is it Paty, Patti, or Patricia?** |
### PRN / float (2)
| # | Name | Role | Location | Phone | Confirm |
|---|---|---|---|---|---|
| 38 | Ezekiel Huerta | Caregiver (PRN) | Tower | 520-591-6113 | |
| 39 | **Maia Baker** | MedTech (PRN) | Memory Care | TBD | **On a secondary sheet only, not the shift list — is she still employed?** |
---
## Things I most need you to confirm
1. **Spelling corrections** for any names flagged in bold above (Polett, Patricia Sandoval-Beck, Ederick, Paty, Maia, and Kyla Quick Tiffany).
2. **Christine Nyanzunda** — one person, or two? If one, she keeps the one existing mailbox and we just extend her to the phones.
3. **Matt Brooks** — department = Maintenance or Memory Care? Title = Memory Care Receptionist, but HR shows Maintenance.
4. **Access type** on every row (D / P / D+P) — especially: do Courtesy Patrol, receptionists, and Drivers use shared phones, shared PCs, or both? Do MedTechs / CCGs chart at a desktop as well as on the phone, or only on the phone?
5. **Outside access** on every row — who needs to work from home, take work email on their personal cell, or travel? The default for everyone else is **in-building only**, which is what HIPAA and Conditional Access would prefer.
6. **ALIS access** — please confirm who actually logs into ALIS. We want to tie ALIS to Microsoft sign-in so those users get a smooth single sign-on with no extra MFA prompts — but only for people who use ALIS. If you don't use it, we won't connect it.
7. **Anyone missing?** Reply with additions. Specifically: I don't have anyone listed under Activities/Life Enrichment outside Susan and Sharon, nor in Transportation beyond the three drivers. Are there PRN/float staff in any other department I should know about?
8. **Tamra Matthews** — we have her transition out in June 2026 confirmed? The Premium license assignment would be temporary if so.
Once this list is confirmed, I will:
1. Clean up the role-based email accounts (accounting@, frontdesk@, hr@, etc.) → shared mailboxes (saves ~$137/mo).
2. Purchase the Business Premium licenses based on the confirmed count.
3. Create the ~38 net-new caregiver accounts in AD and M365.
4. Build the Conditional Access policies that enforce the "outside access" column — in-building users get locked to the building, outside-access users get the flexible "from managed device only" policy.
5. Connect ALIS to Microsoft for the users who use it — that's the piece that removes the second MFA prompt.
Thank you — please mark this up at your convenience.
Howard
---
*Draft — prepared 2026-04-18 for Howard's review before sending.*