sync: auto-sync from HOWARD-HOME at 2026-04-22 16:24:58
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-04-22 16:24:58
This commit is contained in:
@@ -101,12 +101,13 @@ All UPNs above use the `@cascadestucson.com` suffix (standard).
|
||||
|
||||
## Conflict / verify before creating
|
||||
|
||||
- **Christine Nyanzunda** already exists in AD as **Memory Care Admin Assistant** (`Christine.Nyanzunda`, susan.hicks@ department peer — see `docs/servers/active-directory.md` and existing M365 match in `docs/cloud/m365.md`). The caregiver list entry `Christine Nyanzunda-AM shift/MC MED TECH` is likely the same person picking up clinical shifts, not a second identity. **Do not create a second account.** Confirm with Shelby Trozzi / Meredith that her caregiver shifts should use the existing `christine.nyanzunda@` mailbox.
|
||||
- **Paty Doran** — spelling could be Patricia / Paty / Patti. Confirm with HR before creating.
|
||||
- **Polett Pinazavala** — unusual spelling, verify with HR.
|
||||
- **Patricia Sandoval-Beck** — hyphenated last name; SamAccountName may need to be `Patricia.SandovalBeck` if hyphens are disallowed in downstream tools (ALIS, MDM).
|
||||
- **Ederick Yuzon** — verify spelling.
|
||||
- **Maia Baker** — name on Sheet2 only, no shift/phone data. Confirm employment status with HR.
|
||||
- **Christine Nyanzunda** — **Resolved 2026-04-22:** one person, one account. Existing `christine.nyanzunda@` mailbox covers both MC Admin role and her part-time Sun/Mon MedTech shifts. Do not create a second account.
|
||||
- **Paty Doran** — **Resolved 2026-04-22:** legal name `Patricia Camarena Doran`. Account will be `patricia.doran@`.
|
||||
- **Polett Pinazavala** — **Resolved 2026-04-22:** spelling confirmed; still employed. Setup declared in 2026-04-22 email: MedTech — Memory Care (Tue–Sat), D+P, ALIS=Y, Outside=N.
|
||||
- **Patricia Sandoval-Beck** — **Resolved 2026-04-22 (CSV inline note from Meredith):** hyphen is correct. SamAccountName may still need to be `Patricia.SandovalBeck` if ALIS/MDM reject hyphens — test during Wave 3.
|
||||
- **Ederick Yuzon** — **Still pending:** spelling asked in 2026-04-22 email.
|
||||
- **Maia Baker** — **Resolved 2026-04-22 (CSV inline note):** part-time, still employed.
|
||||
- **Reliable Agency shared logins (x2)** — new since 2026-04-22: John added two agency rows to the returned CSV without per-person names. Decision: two shared-login accounts, not per-person. Usernames pending (`reliable1@` / `reliable2@` proposed 2026-04-22).
|
||||
|
||||
## Licensing plan (when ready — NOT now)
|
||||
|
||||
@@ -167,8 +168,10 @@ Group-policy impact: the `CSC - Folder Redirection (LE)` work done for Life Enri
|
||||
|
||||
## Open items / decisions needed from client
|
||||
|
||||
- [ ] Confirm Christine Nyanzunda is one person, not two (existing M365 account keeps working for caregiver shifts)
|
||||
- [ ] HR spelling confirmation on Paty Doran, Polett Pinazavala, Patricia Sandoval-Beck, Ederick Yuzon, Maia Baker
|
||||
- [x] ~~Confirm Christine Nyanzunda is one person, not two~~ (resolved 2026-04-22 — one person, one account)
|
||||
- [x] ~~HR spelling confirmation on Paty Doran, Polett Pinazavala, Patricia Sandoval-Beck, Maia Baker~~ (all resolved 2026-04-22)
|
||||
- [ ] **Ederick Yuzon first-name spelling** — asked in 2026-04-22 email
|
||||
- [ ] **Reliable Agency shared-login short usernames** — asked in 2026-04-22 email (proposed `reliable1@` / `reliable2@`)
|
||||
- [ ] Will caregivers use ALIS on the shared phones (need ALIS accounts + Entra SSO) or only email?
|
||||
- [ ] Does Cascades want to purchase 39 additional Business Premium licenses up-front, or roll out in waves (e.g., MedTechs first, then CCGs, then Caregivers)?
|
||||
- [ ] Confirm pfSense WAN IP(s) are static enough to rely on in a CA Named Location policy
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
# Follow-up email — staff list loose ends + access-policy decision
|
||||
# Follow-up email — staff list loose ends (SENT)
|
||||
|
||||
**To:** Meredith Kuhn, John Trozzi (cc: Ashley Jensen)
|
||||
**To:** Meredith Kuhn, John Trozzi, Ashley Jensen
|
||||
**From:** Howard Enos — Computer Guru
|
||||
**Date:** 2026-04-22
|
||||
**Subject:** Got the staff list — thank you. A few follow-ups before I set up accounts.
|
||||
**Status:** SENT
|
||||
|
||||
---
|
||||
|
||||
Meredith / John,
|
||||
1. **Britney Thompson** — phone Y/N + outside sign-in Y/N
|
||||
2. **Alma R Montt** — title / role. I see she is in Life Enrichment, is she an admin, manager, or something else?
|
||||
3. **Polett Pinazavala** — MedTech - Memory Care (Tue-Sat) Desktop+phone, ALIS, NO outside access
|
||||
4. **Ederick Yuzon** — first-name spelling
|
||||
5. **Reliable Agency caregiver #1** — what sort name would you like them to have? reliable.agency.caregiver1 is long. Can do reliable1 and 2
|
||||
6. **Reliable Agency caregiver #2**
|
||||
|
||||
Thank you for sending back the staff list — that's exactly what I needed and it's going straight into the account setup plan. Before I start creating M365 accounts and access policies, I want to confirm a few things so I don't make the wrong call:
|
||||
|
||||
1. **Ederick Yuzon** — just confirming the spelling of the first name. "Ederick" vs "Edrick" vs something else?
|
||||
|
||||
2. **Alma R Montt** — what's her actual title / role so I can put it on the account?
|
||||
|
||||
3. **Polett Pinazavala** — I had her on my caregiver roster (AM, Memory Care, MedTech) from earlier notes, and she's not on the returned list either. Did she leave?
|
||||
|
||||
4. **Access policy default:** I am going to set all staff restricted to signing in only from the building by default. It will be easier to set a list of users allowed to log in from outside the network than to try to lock individual people in one at a time.
|
||||
|
||||
Thank you —
|
||||
|
||||
Howard
|
||||
I will send a full list for you all to look over.
|
||||
|
||||
---
|
||||
|
||||
*Edited by Howard from the earlier draft. Dropped questions that were resolved live (Kyla's username = `Kyla.QuickTiffany` per her own preference; Britney Thompson still employed; Christine Nyanzunda = one person / one account). Sent: TBD — Howard to update this doc with the actual sent copy.*
|
||||
*Related: `reports/cascades-staff-2026-04-22.csv`, `docs/cloud/user-account-rollout-plan.md`.*
|
||||
*This is the copy of the email Howard sent to Meredith / John / Ashley on 2026-04-22. Full working list (the referenced follow-up) is `cascades-staff-working-list-2026-04-22.md`.*
|
||||
|
||||
## Notes on what's being asked vs. declared
|
||||
|
||||
- **#1 Britney** — open question. Howard confirmed she's still employed; Meredith/John need to specify phone-yes-or-no and outside-sign-in-yes-or-no.
|
||||
- **#2 Alma** — open question on role (admin / manager / other).
|
||||
- **#3 Polett** — NOT a question. Howard is declaring the setup he intends: D+P, ALIS=Y, Outside=N, MedTech Memory Care Tue–Sat. Meredith/John stop him if wrong.
|
||||
- **#4 Ederick** — open question on first-name spelling.
|
||||
- **#5 + #6 Agency** — open question on preferred short username. Howard's proposal: `reliable1` / `reliable2` rather than `reliable.agency.caregiver1/2`. These are treated as shared agency-login accounts rather than per-person accounts.
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
# Cascades staff — working account setup list
|
||||
|
||||
**Prepared for:** Meredith Kuhn, John Trozzi, Ashley Jensen
|
||||
**From:** Howard Enos — Computer Guru
|
||||
**Date:** 2026-04-22
|
||||
|
||||
---
|
||||
|
||||
This is the working list I'm using to build every staff member's Microsoft 365 account, email, and access policy, based on the returned staff list. **Please skim and flag anything wrong, missing, or miscategorized.** Nothing is created yet — confirming this list is the go-ahead to start.
|
||||
|
||||
## How to read the columns
|
||||
|
||||
- **Email** — the M365 sign-in and email address the user will have. Existing AD accounts keep their current address; new accounts follow the `firstname.lastname@cascadestucson.com` pattern.
|
||||
- **Phone (Y/N)** — Will this person use a Cascades-managed phone (shared handset or personal business phone) to sign into email / ALIS / Teams?
|
||||
- **Outside sign-in (Y/N)** — **Default for everyone is NO** (building-only sign-in). A "Yes" here means the user is on the allow-list that permits signing in from home, personal cell, or off-site devices (with MFA and a managed device requirement). Call out anyone whose value here looks wrong.
|
||||
|
||||
Items marked **[?]** are the few things still needing a confirmation from you — see "Pending confirmations" at the bottom.
|
||||
|
||||
---
|
||||
|
||||
## Administrative
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Meredith Kuhn | Executive Director | meredith.kuhn@cascadestucson.com | Y | Y |
|
||||
| Ashley Jensen | Assistant Executive Director | ashley.jensen@cascadestucson.com | Y | Y |
|
||||
| Lauren Hasselman | Business Office Director | lauren.hasselman@cascadestucson.com | Y | Y |
|
||||
| Allison Reibschied | Accounting Assistant | allison.reibschied@cascadestucson.com | Y | N |
|
||||
|
||||
## Marketing / Sales
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Megan Hiatt | Sales Director | megan.hiatt@cascadestucson.com | Y | Y |
|
||||
| Crystal Rodriguez | Sales Associate | crystal.rodriguez@cascadestucson.com | Y | Y |
|
||||
| Tamra Matthews | Move-In Coordinator *(leaving June 2026)* | tamra.matthews@cascadestucson.com | Y | Y |
|
||||
|
||||
## Assisted Living Nursing / Clinical
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Lois Lane | Health Services Director | lois.lane@cascadestucson.com | Y | Y |
|
||||
| Karen Rossini | Health Services Manager | karen.rossini@cascadestucson.com | Y | Y |
|
||||
| Veronica Feller | Care, AL Aide | veronica.feller@cascadestucson.com | Y | Y |
|
||||
| Britney Thompson | Memory Care Nurse | britney.thompson@cascadestucson.com | Y *(confirm)* | Y *(confirm)* |
|
||||
|
||||
## Memory Care
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Shelby Trozzi | Memory Care Director | shelby.trozzi@cascadestucson.com | Y | Y |
|
||||
| Christine Nyanzunda | MC Admin Assistant *(also part-time MedTech Sun/Mon — one account for both)* | christine.nyanzunda@cascadestucson.com | Y | Y |
|
||||
|
||||
## Resident Services
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Christina DuPras | Resident Services Director | christina.dupras@cascadestucson.com | Y | Y |
|
||||
| Cathy Kingston | Receptionist *(shared front desk)* | cathy.kingston@cascadestucson.com | N | N |
|
||||
| Shontiel Nunn | Receptionist *(shared front desk)* | shontiel.nunn@cascadestucson.com | N | N |
|
||||
| Kyla QuickTiffany | Receptionist *(shared front desk)* | kyla.quicktiffany@cascadestucson.com | N | N |
|
||||
| Michelle Shestko | MC Receptionist *(shared front desk)* | michelle.shestko@cascadestucson.com | N | N |
|
||||
| Sebastian Leon | Courtesy Patrol | sebastian.leon@cascadestucson.com | Y | N |
|
||||
| Sheldon Gardfrey | Courtesy Patrol | sheldon.gardfrey@cascadestucson.com | Y | N |
|
||||
| Ray Rai | Courtesy Patrol | ray.rai@cascadestucson.com | Y | N |
|
||||
|
||||
## Life Enrichment
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Susan Hicks | Life Enrichment Director | susan.hicks@cascadestucson.com | Y | Y |
|
||||
| Sharon Edwards | Life Enrichment Assistant | sharon.edwards@cascadestucson.com | Y | N |
|
||||
| Alma R Montt | *(title TBD — see pending)* **[?]** | alma.montt@cascadestucson.com | Y | Y |
|
||||
|
||||
## Culinary
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| JD Martin | Culinary Director | jd.martin@cascadestucson.com | Y | Y |
|
||||
| Ramon Castaneda | Kitchen Manager | ramon.castaneda@cascadestucson.com | Y | N |
|
||||
| Alyssa Brooks | Dining Manager | alyssa.brooks@cascadestucson.com | Y | Y |
|
||||
|
||||
## Maintenance
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| John Trozzi | Facilities Director | john.trozzi@cascadestucson.com | Y | Y |
|
||||
| Matt Brooks | Memory Care Receptionist / Maintenance *(dual-department)* | matt.brooks@cascadestucson.com | Y | N |
|
||||
|
||||
## Housekeeping
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Lupe Sanchez *(aka Guadalupe)* | Housekeeping Director | lupe.sanchez@cascadestucson.com | Y | Y |
|
||||
|
||||
## Transportation
|
||||
|
||||
| Name | Title | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Richard Adams | Driver | richard.adams@cascadestucson.com | Y *(phone only)* | N |
|
||||
| Julian Crim | Driver | julian.crim@cascadestucson.com | Y *(phone only)* | N |
|
||||
| Christopher Holick | Driver | christopher.holick@cascadestucson.com | Y *(phone only)* | N |
|
||||
|
||||
---
|
||||
|
||||
## Caregivers / Shift staff
|
||||
|
||||
All caregivers sign into the shared company-issued Android phones using their own account — each person needs their own M365 identity. Building-only sign-in for everyone in this group.
|
||||
|
||||
### Tuesday–Saturday
|
||||
|
||||
| Name | Role | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Thelma Abainza | Caregiver — Tower | thelma.abainza@cascadestucson.com | Y | N |
|
||||
| Niel Castro | MedTech / CCG — Tower | niel.castro@cascadestucson.com | Y | N |
|
||||
| Espe Esperance | MedTech — Tower | espe.esperance@cascadestucson.com | Y | N |
|
||||
| Barbara Johnson | Caregiver — Tower | barbara.johnson@cascadestucson.com | Y | N |
|
||||
| Kasey Flores | Caregiver — Memory Care | kasey.flores@cascadestucson.com | Y | N |
|
||||
| Richard Flores | Caregiver — Memory Care | richard.flores@cascadestucson.com | Y | N |
|
||||
| Marie Kastner | Caregiver — Memory Care | marie.kastner@cascadestucson.com | Y | N |
|
||||
| Bella Mendoza | Caregiver — Memory Care | bella.mendoza@cascadestucson.com | Y | N |
|
||||
| Rosa Morales | MedTech — Memory Care | rosa.morales@cascadestucson.com | Y | N |
|
||||
| Sandra Padilla | MedTech / CCG — Tower | sandra.padilla@cascadestucson.com | Y | N |
|
||||
| Whisper Reed | MedTech — Tower *(overnight)* | whisper.reed@cascadestucson.com | Y | N |
|
||||
| Patricia Sandoval-Beck | MedTech — Tower | patricia.sandoval-beck@cascadestucson.com | Y | N |
|
||||
| Charity Sika | Caregiver — Memory Care | charity.sika@cascadestucson.com | Y | N |
|
||||
| Ederick Yuzon **[?]** *(confirm spelling)* | Caregiver — Tower | ederick.yuzon@cascadestucson.com | Y | N |
|
||||
|
||||
### Sunday–Thursday
|
||||
|
||||
| Name | Role | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Juan Andrade | Caregiver — Memory Care | juan.andrade@cascadestucson.com | Y | N |
|
||||
| Jahmeka Clarke | MedTech — Memory Care | jahmeka.clarke@cascadestucson.com | Y | N |
|
||||
| Karina Aziakpo | MedTech / CCG — MC overnight | karina.aziakpo@cascadestucson.com | Y | N |
|
||||
| Jinnelle Dittbenner | Caregiver — Tower | jinnelle.dittbenner@cascadestucson.com | Y | N |
|
||||
| Agnes McFerren | Caregiver — Tower | agnes.mcferren@cascadestucson.com | Y | N |
|
||||
| Samuel Ramirez | Caregiver — Tower | samuel.ramirez@cascadestucson.com | Y | N |
|
||||
| Erica Sanchez | Caregiver — Memory Care | erica.sanchez@cascadestucson.com | Y | N |
|
||||
| Katrina Wyzykowski | MedTech — Memory Care | katrina.wyzykowski@cascadestucson.com | Y | N |
|
||||
| Corey Tate | Caregiver — Tower NOC | corey.tate@cascadestucson.com | Y | N |
|
||||
|
||||
### Friday–Monday / weekend
|
||||
|
||||
| Name | Role | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Ashli Atwood | MedTech / CCG — MC overnight | ashli.atwood@cascadestucson.com | Y | N |
|
||||
| Cole Johnson | MedTech — Tower | cole.johnson@cascadestucson.com | Y | N |
|
||||
| Roseline Cooper | Caregiver — MC overnight | roseline.cooper@cascadestucson.com | Y | N |
|
||||
| Monique Lopez | Caregiver — Tower *(Fri+Sat doubles)* | monique.lopez@cascadestucson.com | Y | N |
|
||||
| Gloria Williford | MedTech — MC *(Fri+Sat doubles)* | gloria.williford@cascadestucson.com | Y | N |
|
||||
|
||||
### Thursday–Monday / other
|
||||
|
||||
| Name | Role | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Sarah Carroll | Caregiver — Tower | sarah.carroll@cascadestucson.com | Y | N |
|
||||
| Luke Hogan | Caregiver — Tower | luke.hogan@cascadestucson.com | Y | N |
|
||||
| Gina Williams | Caregiver — Tower | gina.williams@cascadestucson.com | Y | N |
|
||||
| Jen Higdon | Caregiver — Tower *(M/W/F AM)* | jen.higdon@cascadestucson.com | Y | N |
|
||||
| Mary Kariuki | Caregiver — Tower *(Sat–Mon + Wed PM)* | mary.kariuki@cascadestucson.com | Y | N |
|
||||
| CeCe Lassey | Caregiver — Tower *(Sun/Mon doubles + Tue PM)* | cece.lassey@cascadestucson.com | Y | N |
|
||||
| Patricia (Paty) Camarena Doran | MedTech / CCG — Tower *(Sun/Mon only)* | patricia.doran@cascadestucson.com | Y | N |
|
||||
|
||||
### PRN / part-time
|
||||
|
||||
| Name | Role | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Ezekiel Huerta | Caregiver PRN — Tower | ezekiel.huerta@cascadestucson.com | Y | N |
|
||||
| Maia Baker | MedTech PRN — Memory Care | maia.baker@cascadestucson.com | Y | N |
|
||||
| Polett Pinazavala | MedTech — Memory Care *(Tue–Sat)* | polett.pinazavala@cascadestucson.com | Y | N |
|
||||
|
||||
### Agency — shared-account logins **[?]** *(confirm short username)*
|
||||
|
||||
These are shared logins used by whoever from Reliable Agency is covering a shift. Not tied to a specific person. Proposing short usernames rather than long `reliable.agency.caregiver1/2` — pending Meredith/John preference.
|
||||
|
||||
| Name | Role | Email | Phone | Outside sign-in |
|
||||
|---|---|---|---|---|
|
||||
| Reliable Agency shared login #1 | Agency caregiver | reliable1@cascadestucson.com *(proposed)* | Y | N |
|
||||
| Reliable Agency shared login #2 | Agency caregiver | reliable2@cascadestucson.com *(proposed)* | Y | N |
|
||||
|
||||
---
|
||||
|
||||
## Summary counts
|
||||
|
||||
| Category | Count |
|
||||
|---|---|
|
||||
| Office staff with outside sign-in (Admin, Sales, Clinical incl. Britney, MC, RS Director, LE, Culinary, Maintenance, Housekeeping) | 19 |
|
||||
| Office staff in-building only (Allison, Sharon, Ramon, Matt) | 4 |
|
||||
| Shared front-desk receptionists | 4 |
|
||||
| Courtesy Patrol | 3 |
|
||||
| Drivers (phone-only) | 3 |
|
||||
| Caregivers / shift staff (incl. Polett) | 38 |
|
||||
| Agency shared logins | 2 |
|
||||
| **Total identities to set up** | **73** |
|
||||
|
||||
## Pending confirmations (marked [?] above)
|
||||
|
||||
Asked in the 2026-04-22 email:
|
||||
|
||||
1. **Britney Thompson** — phone Y/N and outside sign-in Y/N? Existing account — just need the two flags.
|
||||
2. **Alma R Montt** — title / role? (Admin, manager, something else in Life Enrichment?)
|
||||
3. **Ederick Yuzon** — first-name spelling? (Ederick / Edrick / other?)
|
||||
4. **Reliable Agency shared logins** — short username preference? Proposing `reliable1` and `reliable2` rather than the long `reliable.agency.caregiver1/2`.
|
||||
|
||||
**Declared (stop me if wrong):**
|
||||
- Polett Pinazavala set up as MedTech — Memory Care (Tue–Sat), Desktop+phone, ALIS, no outside sign-in.
|
||||
|
||||
## Update 2026-04-22 (post-email)
|
||||
|
||||
- Polett's row above is the declared setup — if Meredith/John don't push back, this goes live with the rest of the caregiver wave.
|
||||
- Agency row emails are placeholders pending the short-username confirmation.
|
||||
|
||||
## Policy summary
|
||||
|
||||
- **Sign-in is locked to the Cascades building by default for every account above.**
|
||||
- **Off-site sign-in is only allowed for the accounts marked "Outside sign-in = Y"**, and those accounts still require MFA and a managed device to sign in from off-site.
|
||||
- Phones are provided for anyone marked "Phone = Y". Front-desk receptionists and drivers-without-a-phone are exceptions.
|
||||
- Tamra Matthews' access will be removed in June 2026 when she leaves.
|
||||
|
||||
Please reply with corrections, or reply "all good" and I'll start building accounts.
|
||||
|
||||
Thank you —
|
||||
|
||||
Howard
|
||||
@@ -77,7 +77,7 @@ Allison + Sharon are borderline — ALIS handling alone doesn't mandate P2, but
|
||||
|
||||
**Note on Britney Thompson:** Previously predicted as a likely P2 candidate, absent from the 2026-04-22 CSV return. **Confirmed 2026-04-22 (Howard) — still an employee; needs Desktop + possibly Phone access.** Treated as Office-PHI (external-OK) clinical staff for license math until Meredith specifies a different posture. Add to purchase count.
|
||||
|
||||
**Note on Polett Pinazavala:** On the original 2026-04-18 caregiver roster, absent from the 2026-04-22 CSV return. **Confirmed 2026-04-22 (Howard) — still an employee; needs Desktop + possibly Phone.** Treated as Caregiver for license math (included in the caregiver rollout count, not in the office P2 count).
|
||||
**Note on Polett Pinazavala:** On the original 2026-04-18 caregiver roster, absent from the 2026-04-22 CSV return. **Confirmed 2026-04-22 (Howard) — still employed.** Full setup declared in the 2026-04-22 email to Meredith/John: MedTech — Memory Care (Tue–Sat), D+P, ALIS=Y, Outside=N. Counts as one Business Premium license in the caregiver total (not office P2).
|
||||
|
||||
**Shared-PC receptionists** (D only, no Outside, no ALIS): Cathy Kingston, Shontiel Nunn, Kyla Quick Tiffany, Michelle Shestko — four people on shared front-desk PCs. No individual P2 needed; their story is shared-account vs individual-account, not P2.
|
||||
|
||||
@@ -114,7 +114,7 @@ No answer yet. This decision directly changes the license count and the CA polic
|
||||
|
||||
- [x] ~~Follow up with John Trozzi on the gathering — he owes us the list~~ (received 2026-04-22 via CSV)
|
||||
- [ ] Push Meredith for the "restrict everyone or just some" decision — still unanswered as of 2026-04-22
|
||||
- [ ] Resolve open CSV questions (see `clients/cascades-tucson/docs/cloud/cascades-staff-followup-2026-04-22.md`): Kyla Quick Tiffany spelling, Ederick Yuzon spelling, Christine Nyanzunda caregiver-overlap, Alma R Montt title, Britney Thompson status
|
||||
- [ ] Resolve remaining email questions (see `clients/cascades-tucson/docs/cloud/cascades-staff-followup-2026-04-22.md`): Britney phone+outside flags, Alma R Montt title, Ederick Yuzon spelling, agency shared-login username preference
|
||||
- [ ] Decide: standalone P2 add-on for the 19 OR move those users to Business Premium OR move whole tenant to Business Premium (default recommendation: Premium tenant-wide)
|
||||
- [ ] Build CA policy `CSC - Office Staff PHI Access` separate from the caregiver mobile policy
|
||||
- [ ] Remember to REMOVE Tamra's license + CA exclusion on her departure date (June 2026 — confirmed)
|
||||
|
||||
@@ -0,0 +1,735 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Cascades — Staff & Department Editor (2026-04-22)</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;
|
||||
--pending:#fdecec; --pending-line:#f4b4ae;
|
||||
}
|
||||
* { 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.pending { background:var(--pending); border-color:var(--pending-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); }
|
||||
.legend .l-pending { background:var(--pending); border:1px solid var(--pending-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 & Department Editor <span class="sub">(revised 2026-04-22)</span></h1>
|
||||
<span class="sub" id="savedTag">loaded</span>
|
||||
<button onclick="exportJSON()" title="Download everything as JSON — email this file back to Howard">Export JSON</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 Howard-provided baseline">Reset</button>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="hint">
|
||||
<b>What changed since last time:</b> the list now matches what you sent back. A few items are still marked in <b style="color:#b42318;">red</b> — please take a look and fill in the missing pieces (spelling, title, confirmations).
|
||||
<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.</li>
|
||||
<li><b>Click</b> any name or title to edit it inline. The notes box under each name is for anything you want to tell us.</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 phone,
|
||||
<b>—</b> = not set.
|
||||
</li>
|
||||
<li><b>Outside</b> = this person is allowed to sign in from outside the building (home, personal cell, travel). <b>Default is OFF for everyone</b> — leave it off to lock to Cascades only, tick it only for people who truly need off-site access.</li>
|
||||
<li><b>ALIS</b> = ticks if the person logs into ALIS.</li>
|
||||
<li>Use the <b>+ Add</b> row at the bottom of any department to add anyone we've missed.</li>
|
||||
<li>When you're done, click <b>Export JSON</b> and email the downloaded file to Howard. (JSON is the only format that imports back in, so it's the only export option.)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<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"> </span>
|
||||
<span class="legend">
|
||||
Legend: <span class="l-staff"></span>Staff <span class="l-cg"></span>Caregiver <span class="l-pending"></span>Needs answer
|
||||
</span>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// -----------------------------------------------------------------------------
|
||||
// Baseline roster — 2026-04-22, after merging the returned CSV + Howard's
|
||||
// live confirmations. Items flagged "pending" have notes starting with "[?]"
|
||||
// which colors the row pink so Meredith/John can't miss them.
|
||||
// -----------------------------------------------------------------------------
|
||||
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: [
|
||||
// Administrative
|
||||
["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+P",true,true,""],
|
||||
["Allison Reibschied","Accounting Assistant","Administrative","D+P",false,true,""],
|
||||
|
||||
// Marketing / Sales
|
||||
["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 — confirmed"],
|
||||
|
||||
// Care, Assisted Living (Nursing / Clinical)
|
||||
["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,""],
|
||||
["Veronica Feller","Care, Assisted Living Aide","Care, Assisted Living (Nursing / Clinical)","D+P",true,true,""],
|
||||
["Britney Thompson","Memory Care Nurse","Care, Assisted Living (Nursing / Clinical)","D+P",true,true,"[?] Phone Y/N and outside sign-in Y/N? Currently assumed D+P with outside sign-in — flip the flags if that's wrong."],
|
||||
|
||||
// Care, Memory Care
|
||||
["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,"Dual role — MC Admin + part-time Sun/Mon MedTech — one account covers both (confirmed)"],
|
||||
|
||||
// Resident Services
|
||||
["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 QuickTiffany","Receptionist","Resident Services","D",false,false,"Shared front desk. Surname spelled as one word per her preference — account will be kyla.quicktiffany@"],
|
||||
["Michelle Shestko","MC Receptionist","Resident Services","D",false,false,"MC front desk shared PC"],
|
||||
["Sebastian Leon","Courtesy Patrol","Resident Services","D+P",false,false,""],
|
||||
["Sheldon Gardfrey","Courtesy Patrol","Resident Services","D+P",false,false,""],
|
||||
["Ray Rai","Courtesy Patrol","Resident Services","D+P",false,false,""],
|
||||
|
||||
// Life Enrichment
|
||||
["Susan Hicks","Life Enrichment Director","Life Enrichment","D+P",true,true,""],
|
||||
["Sharon Edwards","Life Enrichment Assistant","Life Enrichment","D+P",false,true,""],
|
||||
["Alma R Montt","","Life Enrichment","D+P",true,true,"[?] What's her title / role — admin, manager, or something else in Life Enrichment? Please type it into the Title box."],
|
||||
|
||||
// Culinary
|
||||
["JD Martin","Culinary Director","Culinary","D+P",true,true,""],
|
||||
["Ramon Castaneda","Kitchen Manager","Culinary","D+P",false,false,""],
|
||||
["Alyssa Brooks","Dining Manager","Culinary","D+P",true,true,""],
|
||||
|
||||
// Maintenance
|
||||
["John Trozzi","Facilities Director","Maintenance","D+P",true,true,""],
|
||||
["Matt Brooks","Memory Care Receptionist / Maintenance","Maintenance","D+P",false,true,"Works in both Maintenance and MC — confirmed"],
|
||||
|
||||
// Housekeeping
|
||||
["Lupe Sanchez","Housekeeping Director","Housekeeping","D+P",true,true,"AKA Guadalupe Sanchez"],
|
||||
|
||||
// Transportation
|
||||
["Richard Adams","Driver","Transportation","P",false,false,"Phone only"],
|
||||
["Julian Crim","Driver","Transportation","P",false,false,"Phone only"],
|
||||
["Christopher Holick","Driver","Transportation","P",false,false,"Phone only"],
|
||||
|
||||
// Caregivers (shift staff) — Tue–Sat
|
||||
["Thelma Abainza","Caregiver — Tower (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Niel Castro","MedTech / CCG — Tower (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Espe Esperance","MedTech — Tower (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Barbara Johnson","Caregiver — Tower (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Kasey Flores","Caregiver — Memory Care (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Richard Flores","Caregiver — Memory Care (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Marie Kastner","Caregiver — Memory Care (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Bella Mendoza","Caregiver — Memory Care (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Rosa Morales","MedTech — Memory Care (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Sandra Padilla","MedTech / CCG — Tower (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Polett Pinazavala","MedTech — Memory Care (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,"Confirmed still employed 2026-04-22. Setup declared: D+P, ALIS, no outside sign-in."],
|
||||
["Whisper Reed","MedTech — Tower overnight (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Patricia Sandoval-Beck","MedTech — Tower (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Charity Sika","Caregiver — Memory Care (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Ederick Yuzon","Caregiver — Tower (Tue–Sat)","Caregivers (shift staff)","D+P",false,true,"[?] Confirm spelling of the first name — Ederick vs Edrick vs other?"],
|
||||
|
||||
// Caregivers — Sun–Thu
|
||||
["Juan Andrade","Caregiver — Memory Care (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Jahmeka Clarke","MedTech — Memory Care (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Karina Aziakpo","MedTech / CCG — MC overnight (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Jinnelle Dittbenner","Caregiver — Tower (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Agnes McFerren","Caregiver — Tower (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Samuel Ramirez","Caregiver — Tower (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Erica Sanchez","Caregiver — Memory Care (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Katrina Wyzykowski","MedTech — Memory Care (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Corey Tate","Caregiver — Tower NOC (Sun–Thu)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
|
||||
// Caregivers — Fri–Mon / weekend doubles
|
||||
["Ashli Atwood","MedTech / CCG — MC overnight (Fri–Mon)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Cole Johnson","MedTech — Tower (Fri–Mon)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Roseline Cooper","Caregiver — MC overnight (Fri–Mon)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Monique Lopez","Caregiver — Tower Fri+Sat doubles","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Gloria Williford","MedTech — MC Fri+Sat doubles","Caregivers (shift staff)","D+P",false,true,""],
|
||||
|
||||
// Caregivers — Thu–Mon
|
||||
["Sarah Carroll","Caregiver — Tower (Thu–Mon)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Luke Hogan","Caregiver — Tower (Thu–Mon)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Gina Williams","Caregiver — Tower (Thu–Mon)","Caregivers (shift staff)","D+P",false,true,""],
|
||||
|
||||
// Caregivers — other patterns
|
||||
["Jen Higdon","Caregiver — Tower M/W/F AM","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Mary Kariuki","Caregiver — Tower Sat–Mon + Wed PM","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["CeCe Lassey","Caregiver — Tower Sun/Mon doubles + Tue PM","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Patricia Camarena Doran","MedTech / CCG — Tower Sun/Mon only","Caregivers (shift staff)","D+P",false,true,"Also goes by Paty / Patti — legal name confirmed. Account will be patricia.doran@"],
|
||||
|
||||
// Caregivers — PRN / part-time
|
||||
["Ezekiel Huerta","Caregiver PRN — Tower","Caregivers (shift staff)","D+P",false,true,""],
|
||||
["Maia Baker","MedTech PRN — Memory Care","Caregivers (shift staff)","D+P",false,true,"Part-time (confirmed)"],
|
||||
|
||||
// Caregivers — agency shared-login accounts
|
||||
["Reliable Agency shared login #1","Agency caregiver","Caregivers (shift staff)","D+P",false,true,"[?] Shared login (not per-person — whoever from Reliable is on shift signs in). Please confirm short username preference: proposing reliable1@ rather than the long reliable.agency.caregiver1@."],
|
||||
["Reliable Agency shared login #2","Agency caregiver","Caregivers (shift staff)","D+P",false,true,"[?] Shared login (not per-person — whoever from Reliable is on shift signs in). Please confirm short username preference: proposing reliable2@ rather than the long reliable.agency.caregiver2@."],
|
||||
]
|
||||
};
|
||||
const CAREGIVER_DEPT = "Caregivers (shift staff)";
|
||||
const STORAGE_KEY = "cascades-staff-editor-2026-04-22";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// State
|
||||
// -----------------------------------------------------------------------------
|
||||
let state = pickInitialState();
|
||||
|
||||
function pickInitialState() {
|
||||
const local = load();
|
||||
return 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 Howard-provided 2026-04-22 baseline? Your edits will be lost.")) return;
|
||||
state = rebuild(INITIAL);
|
||||
save(); render();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Rendering
|
||||
// -----------------------------------------------------------------------------
|
||||
function render() {
|
||||
const board = document.getElementById("board");
|
||||
board.innerHTML = "";
|
||||
|
||||
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));
|
||||
|
||||
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;
|
||||
state.people.push({
|
||||
id: nextId(), name, title, dept,
|
||||
access: "D+P",
|
||||
outside: false, alis: false, notes: ""
|
||||
});
|
||||
save(); render();
|
||||
};
|
||||
list.appendChild(add);
|
||||
det.appendChild(list);
|
||||
board.appendChild(det);
|
||||
}
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
function personRow(p) {
|
||||
const row = document.createElement("div");
|
||||
const isPending = (p.notes || "").trim().startsWith("[?]");
|
||||
row.className = "person"
|
||||
+ (p.dept === CAREGIVER_DEPT ? " caregiver" : "")
|
||||
+ (isPending ? " pending" : "");
|
||||
row.dataset.id = p.id;
|
||||
|
||||
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>
|
||||
`;
|
||||
|
||||
row.querySelectorAll("[contenteditable]").forEach(el => {
|
||||
el.addEventListener("blur", onFieldBlur);
|
||||
el.addEventListener("keydown", e => {
|
||||
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);
|
||||
|
||||
const grip = row.querySelector(".grip");
|
||||
grip.addEventListener("dragstart", onDragStart);
|
||||
grip.addEventListener("dragend", onDragEnd);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
function accessSeg(p) {
|
||||
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;
|
||||
const pending = state.people.filter(p => (p.notes || "").trim().startsWith("[?]")).length;
|
||||
const pendingText = pending ? ` · ${pending} still need answer` : "";
|
||||
document.getElementById("status").textContent =
|
||||
`${total} people (${staff} staff + ${cg} caregiver) · ${outside} outside-access · ${alis} ALIS · ${state.departments.length} departments${pendingText}`;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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();
|
||||
if (k === "notes") render(); else 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 — JSON only (it's the only format we can import back in)
|
||||
// -----------------------------------------------------------------------------
|
||||
function exportJSON() {
|
||||
const data = JSON.stringify(state, null, 2);
|
||||
downloadBlob(`cascades-staff-${stamp()}.json`, data, "application/json");
|
||||
}
|
||||
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 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) {
|
||||
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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
|
||||
function escapeAttr(s) { return String(s ?? "").replace(/'/g, "\\'"); }
|
||||
|
||||
render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -119,14 +119,14 @@ These must be resolved before creating or converting accounts. See also `cascade
|
||||
| Discrepancy | Status | Action |
|
||||
|---|---|---|
|
||||
| **Britney Thompson** — in AD (enabled, Memory Care Nurse), NOT on returned CSV | **Resolved 2026-04-22 (Howard) — still employed. Desktop + maybe Phone.** | Keep existing AD account. Treat as Office-PHI / clinical (D+P, ALIS=Y). Confirm phone tier and Outside posture with Meredith. |
|
||||
| **Polett Pinazavala** — on 2026-04-18 caregiver roster, NOT on returned CSV | **Resolved 2026-04-22 (Howard) — still employed. Desktop + maybe Phone.** | Keep on caregiver roster. Include in Wave 3 caregiver account creation. Confirm phone tier with Meredith. |
|
||||
| **Polett Pinazavala** — on 2026-04-18 caregiver roster, NOT on returned CSV | **Resolved 2026-04-22 (Howard) — still employed. Setup declared in email: MedTech — Memory Care (Tue–Sat), D+P, ALIS=Y, Outside=N.** | Keep on caregiver roster. Include in Wave 3 caregiver account creation unless Meredith/John push back on the declared setup. |
|
||||
| **Christine Nyanzunda** — one person, MC Admin + part-time Sun/Mon MedTech | **Resolved 2026-04-22 (Howard) — one account covers both roles.** | Single account in `OU=Care-MemoryCare`. Default building-only CA policy. When she's covering a MedTech shift she logs into the shared MC phone with her own account. If that sign-in gets blocked by the shared-phone CA, add her to a specific exception group rather than splitting into two accounts. |
|
||||
| **Alma R Montt** — on CSV (Life Enrichment), NOT in AD, title blank | **Username assigned 2026-04-22 (Howard): `Alma.Montt`.** Title still pending Meredith. | Create AD account at `Alma.Montt` (UPN `alma.montt@cascadestucson.com`). Populate title once Meredith answers. |
|
||||
| **Kyla Quick Tiffany** — on CSV and in AD "needs account" list | **Username assigned 2026-04-22 (Howard, per Kyla's preference): `Kyla.QuickTiffany`** — last name treated as a single word. | Create AD account at `Kyla.QuickTiffany` (UPN `kyla.quicktiffany@cascadestucson.com`). Persona: Shared-PC Reception. |
|
||||
| **Ederick Yuzon** — spelling not confirmed | Still pending Meredith. | Block on creation; use `Ederick.Yuzon` tentatively if Meredith confirms. |
|
||||
| **Matt Brooks** — AD dept = Maintenance, CSV note "works in both departments" | Confirmed (CSV-inline). | Keep in Maintenance OU; add to secondary MC group for access overlap. |
|
||||
| **37 caregivers** — on CSV, none in AD | Unchanged. | Create all 37 AD accounts (+ M365) in Wave 3. |
|
||||
| **2 agency placeholders** — on CSV, not in AD | Unchanged. | Decide with Meredith: real accounts or ALIS-only? |
|
||||
| **2 agency placeholders** — on CSV, not in AD | **Decision 2026-04-22 (Howard, asked Meredith for username preference):** shared agency-login accounts, not per-person. Proposed usernames `reliable1@` and `reliable2@` (fallback to `reliable.agency.caregiver1/2@` if Meredith prefers long form). | Create 2 shared AD/M365 accounts once username preference comes back. Shared accounts = caveat on audit attribution — whoever is on shift uses the shared login, so individual accountability in audit logs is weaker for agency staff. Acceptable tradeoff given we don't have agency-staff names. |
|
||||
| **Generic AD accounts** (`Culinary`, `RECEPTIONIST`, `saleshare`, `directoryshare`) | Unchanged. | Phase 5 cleanup after named-account coverage. |
|
||||
|
||||
**Username convention for new accounts:** TitleCase `First.Last` (e.g., `Alma.Montt`, `Kyla.QuickTiffany`). Existing lowercase exceptions in AD (`britney.thompson`, `karen.rossini`, `lauren.hasselman`) are the known legacy cases — leave as-is, don't rename. All net-new accounts follow TitleCase.
|
||||
@@ -134,15 +134,14 @@ These must be resolved before creating or converting accounts. See also `cascade
|
||||
## 7. Rollout sequence
|
||||
|
||||
### Wave 0 — Pre-flight (blocks waves 1+)
|
||||
- Get answers to the 5 follow-up questions (Kyla/Ederick/Christine/Alma/Britney) + the "restrict-everyone or selective" policy decision from Meredith
|
||||
- Close Polett Pinazavala discrepancy
|
||||
- Final license decision (Business Premium tenant-wide vs. mixed)
|
||||
- Get answers to the 4 remaining email questions: Britney flags, Alma title, Ederick spelling, agency short-username preference
|
||||
- Final license decision (Business Premium tenant-wide vs. mixed) — recommendation is Premium tenant-wide, needs Meredith sign-off
|
||||
- Purchase license count locked in
|
||||
|
||||
### Wave 1 — New office accounts (low blast radius)
|
||||
- Create AD + M365 for Alma R Montt and Kyla Quick Tiffany (the only new office/reception accounts the CSV produces)
|
||||
- Create AD + M365 for Alma R Montt (`Alma.Montt`) and Kyla QuickTiffany (`Kyla.QuickTiffany`) — the only new office/reception accounts the CSV produces
|
||||
- Validate group membership + CA policy assignment on these two before touching anyone else
|
||||
- Pilot the `CSC - FrontDesk Building-Only` policy with Kyla
|
||||
- Pilot the `CSC - Building Only (Default)` policy with Kyla
|
||||
|
||||
### Wave 2 — Existing office accounts, reassignment only
|
||||
- Move existing users into new OU layout (no identity changes, just OU move + group membership)
|
||||
@@ -155,7 +154,7 @@ These must be resolved before creating or converting accounts. See also `cascade
|
||||
|
||||
### Wave 4 — Cleanup
|
||||
- Disable/remove `Culinary`, `RECEPTIONIST`, `saleshare`, `directoryshare` generics once their functions are covered by named accounts + shared mailboxes
|
||||
- Disable departed accounts (Britney pending answer, Tamra on departure June 2026)
|
||||
- Disable Tamra's account on her June 2026 departure (other known departures: none as of 2026-04-22)
|
||||
- Rotate `krbtgt` password (noted stale in AD doc — overdue)
|
||||
|
||||
## 8. Account creation template (per new user)
|
||||
@@ -182,20 +181,20 @@ Applies to Wave 1 + Wave 3 (and any future hire). Precise script will be built l
|
||||
## 10. Open decisions blocking the rollout
|
||||
|
||||
1. **Business Premium tenant-wide vs. mixed SKUs** — Meredith, tied to the upgrade proposal. Building-only-by-default decision reinforces Premium tenant-wide (see §5).
|
||||
2. **Ederick Yuzon spelling** — Meredith/John, in the 2026-04-22 follow-up email.
|
||||
3. **Alma R Montt title** — Meredith/John, in the follow-up email.
|
||||
4. **Britney phone + Outside posture** — Meredith (employment confirmed by Howard; access tier still TBD).
|
||||
5. **Polett employment confirmation** — Meredith (Howard assumes still employed; formal Meredith confirmation requested in follow-up email).
|
||||
6. **Agency placeholder accounts — names + ALIS-only vs. real accounts?** — John added two agency rows to the CSV but left Name and Notes blank. Need the actual agency names + whether they need AD/M365 identities or just ALIS web logins.
|
||||
7. **Drivers: F3 or Business Standard?** — Meredith (cost vs. Office install need). Note: drivers need allow-list membership to sign in off-site, so whichever tier must include P1 for CA coverage (F3 does not; Business Premium or Business Standard + Entra P1 add-on required).
|
||||
2. **Britney Thompson phone Y/N + Outside Y/N** — Meredith/John, asked in the 2026-04-22 email.
|
||||
3. **Alma R Montt title** — Meredith/John, asked in the 2026-04-22 email (admin / manager / other in LE?).
|
||||
4. **Ederick Yuzon spelling** — Meredith/John, asked in the 2026-04-22 email.
|
||||
5. **Agency shared-login short username** — Meredith/John, asked in the 2026-04-22 email (proposed `reliable1` / `reliable2`).
|
||||
6. **Drivers: F3 or Business Standard?** — Meredith (cost vs. Office install need). Drivers need allow-list membership to sign in off-site, so whichever tier must include P1 for CA coverage (F3 does not; Business Premium or Business Standard + Entra P1 add-on required).
|
||||
|
||||
**Resolved 2026-04-22 (Howard):**
|
||||
- Restrict-everyone default vs. selective → **building-only by default, allow-list for exceptions** (§5).
|
||||
- Christine Nyanzunda → one account covers both roles.
|
||||
- Kyla Quick Tiffany username → `Kyla.QuickTiffany` (her preference — sign-in confirmed by Howard).
|
||||
- Kyla Quick Tiffany username → `Kyla.QuickTiffany` (her preference — confirmed by Howard).
|
||||
- Alma R Montt username → `Alma.Montt`.
|
||||
- Britney Thompson → still employed; stays in AD.
|
||||
- Polett Pinazavala → still employed (awaiting Meredith formal confirmation via email).
|
||||
- Britney Thompson → still employed; stays in AD. Access-posture flags still open.
|
||||
- Polett Pinazavala → still employed. Declared setup (D+P, ALIS=Y, Outside=N, MedTech Memory Care Tue–Sat) announced in 2026-04-22 email; will proceed unless Meredith/John push back.
|
||||
- Agency placeholders → shared-login accounts (not per-person). Username short-form proposed but preference still open.
|
||||
|
||||
## 11. Related docs
|
||||
|
||||
|
||||
267
clients/cascades-tucson/scripts/build-open-questions-docx.py
Normal file
267
clients/cascades-tucson/scripts/build-open-questions-docx.py
Normal file
@@ -0,0 +1,267 @@
|
||||
"""Build a minimal Word document listing the six people with open questions.
|
||||
|
||||
No external deps — a .docx is a zip of OOXML files, so we construct it directly
|
||||
with the stdlib. Output: cascades-staff-open-questions-2026-04-22.docx
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import zipfile
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
OUT = "clients/cascades-tucson/docs/cloud/questionnaires/cascades-staff-open-questions-2026-04-22.docx"
|
||||
|
||||
TITLE = "Cascades — Open Items on Staff Access List"
|
||||
SUBTITLE = "2026-04-22 · prepared by Howard Enos, Computer Guru · matches the 2026-04-22 email"
|
||||
|
||||
INTRO = (
|
||||
"Thank you for sending back the staff list. Almost everything is squared away. "
|
||||
"Below are the few items I still need from you. One of them (Polett) is NOT a "
|
||||
"question — it is the setup I am planning to use; stop me if it's wrong. The rest "
|
||||
"are questions. Short answers are fine. I will send a full list for you all to "
|
||||
"look over separately."
|
||||
)
|
||||
|
||||
QUESTIONS = [
|
||||
{
|
||||
"name": "Britney Thompson",
|
||||
"dept": "Assisted Living Nursing / Clinical",
|
||||
"context": (
|
||||
"Britney has an active Active Directory account today as Memory Care Nurse. "
|
||||
"She was not on the staff list you returned; Howard has confirmed she is still "
|
||||
"an employee, so the account stays active. I just need the two flags below."
|
||||
),
|
||||
"questions": [
|
||||
"Phone — Y or N? (Does she need a Cascades-issued phone / business cell, in addition to a desktop?)",
|
||||
"Outside sign-in — Y or N? (Default for everyone is N / building-only. Mark Y only if she legitimately works off-site.)",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Alma R Montt",
|
||||
"dept": "Life Enrichment",
|
||||
"context": (
|
||||
"Alma was on the returned list but the Title / Role column was blank. "
|
||||
"I see she is in Life Enrichment — is she an admin, manager, or something else?"
|
||||
),
|
||||
"questions": [
|
||||
"What is Alma's title or role? (It will go on her account and email signature.)",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Polett Pinazavala — NOT a question, just a heads-up",
|
||||
"dept": "Caregivers (Memory Care, MedTech, Tue–Sat)",
|
||||
"context": (
|
||||
"Polett was on an earlier caregiver roster (MedTech, Memory Care, AM shift) but she was "
|
||||
"not on the list you sent back. Howard has confirmed she is still an employee. Unless you "
|
||||
"tell me otherwise, this is the setup she will get:"
|
||||
),
|
||||
"questions": [
|
||||
"MedTech — Memory Care (Tue–Sat), Desktop + phone, ALIS access, NO outside sign-in. "
|
||||
"Stop me below if any of that is wrong — otherwise no action needed.",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Ederick Yuzon",
|
||||
"dept": "Caregivers (Tower, Tue–Sat)",
|
||||
"context": (
|
||||
"Just want to match the spelling on his payroll / ID so his account name is correct."
|
||||
),
|
||||
"questions": [
|
||||
"Is his first name spelled \"Ederick\", \"Edrick\", or something else?",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Reliable Agency caregiver #1 (shared login)",
|
||||
"dept": "Caregivers — Agency",
|
||||
"context": (
|
||||
"John added this agency row without a specific person's name, so I am treating it as a "
|
||||
"shared login — whichever Reliable Agency caregiver is on shift signs in with this account. "
|
||||
"That works, but I want to keep the username short."
|
||||
),
|
||||
"questions": [
|
||||
"What short username would you like for this shared account? "
|
||||
"`reliable.agency.caregiver1` is long — I can use `reliable1` instead. OK, or prefer something else?",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Reliable Agency caregiver #2 (shared login)",
|
||||
"dept": "Caregivers — Agency",
|
||||
"context": (
|
||||
"Same situation as #1."
|
||||
),
|
||||
"questions": [
|
||||
"Short username for the second shared agency login? Proposed: `reliable2`.",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
CLOSING = (
|
||||
"Once I have these answers back, I will set up every account in one pass and let you know "
|
||||
"when they are ready for the users to sign in. Thank you!"
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# OOXML building
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
NSW = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
||||
|
||||
def para(text: str, *, style: str | None = None, bold: bool = False, size: int | None = None) -> str:
|
||||
"""A single <w:p> with one run. Font size is in half-points."""
|
||||
pPr = ""
|
||||
if style:
|
||||
pPr = f'<w:pPr><w:pStyle w:val="{style}"/></w:pPr>'
|
||||
rPr_parts = []
|
||||
if bold:
|
||||
rPr_parts.append("<w:b/>")
|
||||
if size is not None:
|
||||
rPr_parts.append(f'<w:sz w:val="{size}"/>')
|
||||
rPr = f"<w:rPr>{''.join(rPr_parts)}</w:rPr>" if rPr_parts else ""
|
||||
return (
|
||||
f'<w:p>{pPr}'
|
||||
f'<w:r>{rPr}<w:t xml:space="preserve">{escape(text)}</w:t></w:r>'
|
||||
f'</w:p>'
|
||||
)
|
||||
|
||||
|
||||
def bullet(text: str) -> str:
|
||||
return (
|
||||
'<w:p><w:pPr><w:pStyle w:val="ListParagraph"/>'
|
||||
'<w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr></w:pPr>'
|
||||
f'<w:r><w:t xml:space="preserve">{escape(text)}</w:t></w:r></w:p>'
|
||||
)
|
||||
|
||||
|
||||
def blank_answer_line() -> str:
|
||||
"""A hand-written answer placeholder: 'Answer: ________________'."""
|
||||
return (
|
||||
'<w:p>'
|
||||
'<w:r><w:rPr><w:i/><w:color w:val="808080"/></w:rPr>'
|
||||
'<w:t xml:space="preserve">Answer: </w:t></w:r>'
|
||||
'<w:r><w:rPr><w:color w:val="C0C0C0"/></w:rPr>'
|
||||
'<w:t xml:space="preserve">____________________________________________________________</w:t></w:r>'
|
||||
'</w:p>'
|
||||
)
|
||||
|
||||
|
||||
def section_for(q: dict) -> str:
|
||||
parts = []
|
||||
parts.append(para(q["name"], style="Heading2"))
|
||||
parts.append(para(q["dept"], bold=True, size=20))
|
||||
parts.append(para(q["context"]))
|
||||
for ques in q["questions"]:
|
||||
parts.append(bullet(ques))
|
||||
parts.append(blank_answer_line())
|
||||
parts.append(para("")) # spacer
|
||||
return "".join(parts)
|
||||
|
||||
|
||||
def build_document_xml() -> str:
|
||||
body_parts = []
|
||||
body_parts.append(para(TITLE, style="Title"))
|
||||
body_parts.append(para(SUBTITLE, bold=False, size=20))
|
||||
body_parts.append(para(""))
|
||||
body_parts.append(para(INTRO))
|
||||
body_parts.append(para(""))
|
||||
for q in QUESTIONS:
|
||||
body_parts.append(section_for(q))
|
||||
body_parts.append(para(""))
|
||||
body_parts.append(para(CLOSING))
|
||||
|
||||
body = "".join(body_parts)
|
||||
return (
|
||||
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
||||
f'<w:document xmlns:w="{NSW}">'
|
||||
f'<w:body>{body}'
|
||||
'<w:sectPr>'
|
||||
'<w:pgSz w:w="12240" w:h="15840"/>'
|
||||
'<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>'
|
||||
'</w:sectPr>'
|
||||
'</w:body>'
|
||||
'</w:document>'
|
||||
)
|
||||
|
||||
|
||||
STYLES_XML = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:docDefaults>
|
||||
<w:rPrDefault>
|
||||
<w:rPr>
|
||||
<w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/>
|
||||
<w:sz w:val="22"/>
|
||||
</w:rPr>
|
||||
</w:rPrDefault>
|
||||
<w:pPrDefault>
|
||||
<w:pPr><w:spacing w:after="120"/></w:pPr>
|
||||
</w:pPrDefault>
|
||||
</w:docDefaults>
|
||||
<w:style w:type="paragraph" w:styleId="Title">
|
||||
<w:name w:val="Title"/>
|
||||
<w:pPr><w:spacing w:before="240" w:after="120"/></w:pPr>
|
||||
<w:rPr><w:b/><w:sz w:val="44"/><w:color w:val="1A2030"/></w:rPr>
|
||||
</w:style>
|
||||
<w:style w:type="paragraph" w:styleId="Heading2">
|
||||
<w:name w:val="heading 2"/>
|
||||
<w:pPr><w:spacing w:before="360" w:after="80"/></w:pPr>
|
||||
<w:rPr><w:b/><w:sz w:val="28"/><w:color w:val="2B6CB0"/></w:rPr>
|
||||
</w:style>
|
||||
<w:style w:type="paragraph" w:styleId="ListParagraph">
|
||||
<w:name w:val="List Paragraph"/>
|
||||
<w:pPr><w:ind w:left="720"/><w:contextualSpacing/></w:pPr>
|
||||
</w:style>
|
||||
</w:styles>
|
||||
'''
|
||||
|
||||
NUMBERING_XML = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:abstractNum w:abstractNumId="0">
|
||||
<w:lvl w:ilvl="0">
|
||||
<w:start w:val="1"/>
|
||||
<w:numFmt w:val="bullet"/>
|
||||
<w:lvlText w:val="•"/>
|
||||
<w:lvlJc w:val="left"/>
|
||||
<w:pPr><w:ind w:left="720" w:hanging="360"/></w:pPr>
|
||||
<w:rPr><w:rFonts w:ascii="Symbol" w:hAnsi="Symbol"/></w:rPr>
|
||||
</w:lvl>
|
||||
</w:abstractNum>
|
||||
<w:num w:numId="1"><w:abstractNumId w:val="0"/></w:num>
|
||||
</w:numbering>
|
||||
'''
|
||||
|
||||
CONTENT_TYPES_XML = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
||||
<Default Extension="xml" ContentType="application/xml"/>
|
||||
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
||||
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
|
||||
<Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>
|
||||
</Types>
|
||||
'''
|
||||
|
||||
ROOT_RELS_XML = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
||||
</Relationships>
|
||||
'''
|
||||
|
||||
DOC_RELS_XML = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
||||
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/>
|
||||
</Relationships>
|
||||
'''
|
||||
|
||||
|
||||
def build_docx(path: str) -> None:
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) as z:
|
||||
z.writestr("[Content_Types].xml", CONTENT_TYPES_XML)
|
||||
z.writestr("_rels/.rels", ROOT_RELS_XML)
|
||||
z.writestr("word/_rels/document.xml.rels", DOC_RELS_XML)
|
||||
z.writestr("word/document.xml", build_document_xml())
|
||||
z.writestr("word/styles.xml", STYLES_XML)
|
||||
z.writestr("word/numbering.xml", NUMBERING_XML)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
build_docx(OUT)
|
||||
print(f"Wrote {OUT}")
|
||||
Reference in New Issue
Block a user