| import gradio as gr |
| import json |
| import os |
| import random |
| import string |
| from datetime import date, datetime |
|
|
| |
| |
| |
|
|
| TODAY = date.today().strftime("%B %d, %Y") |
|
|
| SCENARIOS = { |
| "Alex Turner β Senior Engineer": { |
| "employee": "Alex Turner", |
| "role": "Senior Engineer", |
| "dept": "Engineering", |
| "last_day": TODAY, |
| "manager": "Sarah Chen", |
| "offboarding_id": "OB-20240615-7842", |
| "risk_level": "MEDIUM", |
| "files_transferred": 847, |
| "systems": ["Google Workspace", "Slack", "GitHub", "Google Drive"], |
| "timeline": [ |
| ("T+0min", "Offboarding initiated by HR", "completed"), |
| ("T+1min", "Google Workspace account suspended", "completed"), |
| ("T+2min", "Slack account deactivated", "completed"), |
| ("T+3min", "GitHub org membership revoked (14 repos)", "completed"), |
| ("T+5min", "847 Google Drive files transferred to Sarah Chen","completed"), |
| ("T+6min", "Offboarding audit report generated", "completed"), |
| ("T+7min", "Compliance email sent to IT Security & Legal", "completed"), |
| ], |
| "compliance_flags": [], |
| "audit_summary": ( |
| "Alex Turner's offboarding was completed successfully with all access revoked " |
| "within 7 minutes of initiation. 847 Drive files were transferred to manager " |
| "Sarah Chen. No compliance flags were raised. The engineering credentials " |
| "vault has been updated and all active SSH keys have been invalidated." |
| ), |
| "it_subject": "COMPLETED: Offboarding β Alex Turner (Senior Engineer) β OB-20240615-7842", |
| "it_body": ( |
| "This is an automated confirmation that offboarding for Alex Turner has been " |
| "completed.\n\nActions completed:\nβ’ Google Workspace: SUSPENDED\n" |
| "β’ Slack: DEACTIVATED\nβ’ GitHub: REMOVED (14 repositories)\n" |
| "β’ Drive: 847 files transferred to Sarah Chen\n\n" |
| "Audit ID: OB-20240615-7842\nCompleted at: T+7min\n\nNo further action required." |
| ), |
| "legal_subject": "Offboarding Compliance Notice β Alex Turner β OB-20240615-7842", |
| "legal_body": ( |
| "For compliance records: Employee Alex Turner (Senior Engineer, Engineering) " |
| "has been fully offboarded.\n\nData handling: All work files transferred to " |
| "authorized manager Sarah Chen.\nAccess: All systems revoked.\n" |
| "Risk level: MEDIUM\nCompliance flags: None\n\n" |
| "Audit trail is stored in the compliance database under OB-20240615-7842." |
| ), |
| }, |
| "Maria Santos β VP Marketing": { |
| "employee": "Maria Santos", |
| "role": "VP Marketing", |
| "dept": "Marketing", |
| "last_day": TODAY, |
| "manager": "CEO David Park", |
| "offboarding_id": "OB-20240615-7843", |
| "risk_level": "HIGH", |
| "files_transferred": 2341, |
| "systems": ["Google Workspace", "Slack", "GitHub", "Google Drive", "Salesforce", "Figma", "Notion"], |
| "extra_label": "Elevated Security Protocol", |
| "extra_detail": "Admin access revoked from 3 SaaS tools Β· Marketing credentials rotated", |
| "timeline": [ |
| ("T+0min", "HIGH-RISK offboarding initiated β VP-level departure", "completed"), |
| ("T+1min", "Google Workspace admin privileges stripped", "completed"), |
| ("T+1min", "Google Workspace account suspended", "completed"), |
| ("T+2min", "Slack account deactivated + admin role removed", "completed"), |
| ("T+3min", "Figma org admin revoked", "completed"), |
| ("T+3min", "Notion workspace admin revoked", "completed"), |
| ("T+4min", "Salesforce admin access removed", "completed"), |
| ("T+4min", "Marketing credentials vault rotated (12 credentials)", "completed"), |
| ("T+6min", "2,341 Google Drive files transferred to CEO David Park", "completed"), |
| ("T+7min", "Elevated security audit report generated", "completed"), |
| ("T+8min", "Priority compliance email sent to IT Security & Legal", "completed"), |
| ], |
| "compliance_flags": [ |
| "VP-level departure requires Board notification per policy Β§4.2", |
| "Marketing SaaS admin credentials rotated β IT to verify coverage", |
| ], |
| "audit_summary": ( |
| "Maria Santos's high-priority offboarding was completed in 8 minutes. " |
| "Admin access was revoked from Google Workspace, Figma, Notion, and Salesforce " |
| "before account suspension. 12 marketing credentials were rotated. " |
| "2,341 Drive files transferred to CEO David Park. Two compliance flags " |
| "raised for Board notification and credential rotation verification." |
| ), |
| "it_subject": "URGENT COMPLETED: High-Risk Offboarding β Maria Santos (VP Marketing) β OB-20240615-7843", |
| "it_body": ( |
| "HIGH-RISK offboarding for Maria Santos (VP Marketing) is complete.\n\n" |
| "Actions completed:\nβ’ Google Workspace admin + account: REVOKED & SUSPENDED\n" |
| "β’ Slack admin + account: REVOKED & DEACTIVATED\n" |
| "β’ Figma admin: REVOKED\nβ’ Notion admin: REVOKED\n" |
| "β’ Salesforce admin: REVOKED\nβ’ Marketing credentials: ROTATED (12 items)\n" |
| "β’ Drive: 2,341 files β CEO David Park\n\n" |
| "ACTION REQUIRED: Verify credential rotation coverage with marketing team.\n" |
| "Audit ID: OB-20240615-7843" |
| ), |
| "legal_subject": "HIGH-RISK Offboarding Compliance β Maria Santos β OB-20240615-7843", |
| "legal_body": ( |
| "COMPLIANCE NOTICE β HIGH RISK\n\nEmployee Maria Santos (VP Marketing) " |
| "has been offboarded.\n\nCompliance flags raised:\n" |
| "1. Board notification required per policy Β§4.2\n" |
| "2. IT to confirm marketing credential rotation coverage\n\n" |
| "Risk Level: HIGH\nAdmin systems affected: 4\n" |
| "Data transferred: 2,341 files to CEO David Park\n" |
| "Audit ID: OB-20240615-7843" |
| ), |
| }, |
| "James Kim β Data Analyst": { |
| "employee": "James Kim", |
| "role": "Data Analyst", |
| "dept": "Finance", |
| "last_day": TODAY, |
| "manager": "CFO Lisa Wong", |
| "offboarding_id": "OB-20240615-7844", |
| "risk_level": "HIGH", |
| "files_transferred": 1203, |
| "systems": ["Google Workspace", "Slack", "GitHub", "Google Drive", "AWS", "Jira"], |
| "extra_label": "SOX Compliance Protocol", |
| "extra_detail": "Finance data access audit logged Β· SOX compliance report generated", |
| "timeline": [ |
| ("T+0min", "Finance offboarding initiated β SOX compliance protocol activated", "completed"), |
| ("T+1min", "Google Workspace account suspended", "completed"), |
| ("T+1min", "AWS IAM user deactivated, access keys revoked", "completed"), |
| ("T+2min", "Slack account deactivated", "completed"), |
| ("T+2min", "GitHub org membership revoked (6 repos)", "completed"), |
| ("T+3min", "Jira tickets reassigned to CFO Lisa Wong (23 open items)", "completed"), |
| ("T+4min", "Finance data access audit logged to immutable SOX ledger", "completed"), |
| ("T+5min", "1,203 Google Drive files transferred to CFO Lisa Wong", "completed"), |
| ("T+6min", "SOX compliance report generated and signed", "completed"), |
| ("T+7min", "Compliance email + SOX report sent to IT Security & Legal", "completed"), |
| ], |
| "compliance_flags": [ |
| "SOX audit log entry created β retain for 7 years per SEC Β§802", |
| "Finance data access history exported and sealed", |
| "AWS billing alerts updated to remove departed user", |
| ], |
| "audit_summary": ( |
| "James Kim's Finance-department offboarding triggered the SOX compliance protocol. " |
| "All access was revoked in 2 minutes. 23 open Jira tickets were reassigned to " |
| "CFO Lisa Wong. A full finance data access audit was logged to the immutable SOX " |
| "ledger. 1,203 Drive files transferred. SOX compliance report generated and " |
| "dispatched to Legal for 7-year retention." |
| ), |
| "it_subject": "COMPLETED: SOX Offboarding β James Kim (Data Analyst, Finance) β OB-20240615-7844", |
| "it_body": ( |
| "Offboarding for James Kim (Data Analyst, Finance) is complete.\n\n" |
| "Actions completed:\nβ’ Google Workspace: SUSPENDED\n" |
| "β’ AWS IAM: DEACTIVATED (access keys revoked)\n" |
| "β’ Slack: DEACTIVATED\nβ’ GitHub: REMOVED (6 repos)\n" |
| "β’ Jira: 23 tickets reassigned to CFO Lisa Wong\n" |
| "β’ Drive: 1,203 files β CFO Lisa Wong\n\n" |
| "ACTION REQUIRED: Update AWS billing alerts.\n" |
| "Audit ID: OB-20240615-7844" |
| ), |
| "legal_subject": "SOX Compliance Offboarding β James Kim β OB-20240615-7844", |
| "legal_body": ( |
| "SOX COMPLIANCE NOTICE\n\nEmployee James Kim (Data Analyst, Finance) " |
| "has been offboarded under SOX compliance protocol.\n\n" |
| "Compliance actions:\nβ’ Full data access audit logged to immutable SOX ledger\n" |
| "β’ Finance data access history exported and sealed\n" |
| "β’ SOX compliance report attached β retain for 7 years per SEC Β§802\n\n" |
| "Risk Level: HIGH\nAudit ID: OB-20240615-7844" |
| ), |
| }, |
| "Rachel Adams β Customer Success Manager": { |
| "employee": "Rachel Adams", |
| "role": "Customer Success Manager", |
| "dept": "Sales", |
| "last_day": TODAY, |
| "manager": "Tom Bradley", |
| "offboarding_id": "OB-20240615-7845", |
| "risk_level": "MEDIUM", |
| "files_transferred": 512, |
| "systems": ["Google Workspace", "Slack", "GitHub", "Google Drive", "Salesforce"], |
| "extra_label": "CRM Handoff Protocol", |
| "extra_detail": "Salesforce opportunities reassigned Β· Customer contacts notified of new rep", |
| "timeline": [ |
| ("T+0min", "Sales offboarding initiated β CRM handoff protocol activated", "completed"), |
| ("T+1min", "Google Workspace account suspended", "completed"), |
| ("T+2min", "Slack account deactivated", "completed"), |
| ("T+3min", "Salesforce: 47 open opportunities reassigned to Tom Bradley", "completed"), |
| ("T+3min", "Salesforce: 312 customer contacts updated with new rep info", "completed"), |
| ("T+4min", "Customer notification emails sent (312 contacts)", "completed"), |
| ("T+5min", "512 Google Drive files transferred to Tom Bradley", "completed"), |
| ("T+6min", "CRM handoff report generated", "completed"), |
| ("T+7min", "Compliance email sent to IT Security & Legal", "completed"), |
| ], |
| "compliance_flags": [ |
| "Customer contact notifications sent β log retained for GDPR compliance", |
| "Salesforce pipeline handoff requires VP Sales review within 48 hours", |
| ], |
| "audit_summary": ( |
| "Rachel Adams's offboarding included the full CRM handoff protocol for Sales. " |
| "47 open Salesforce opportunities and 312 customer contacts were reassigned to " |
| "manager Tom Bradley. All 312 customers received automated notification emails " |
| "introducing their new rep. 512 Drive files transferred. GDPR-compliant " |
| "notification log retained. VP Sales review flagged for within 48 hours." |
| ), |
| "it_subject": "COMPLETED: Offboarding + CRM Handoff β Rachel Adams (CSM) β OB-20240615-7845", |
| "it_body": ( |
| "Offboarding for Rachel Adams (Customer Success Manager, Sales) is complete.\n\n" |
| "Actions completed:\nβ’ Google Workspace: SUSPENDED\nβ’ Slack: DEACTIVATED\n" |
| "β’ Salesforce: 47 opportunities β Tom Bradley\n" |
| "β’ Salesforce: 312 contacts updated + notified\n" |
| "β’ Drive: 512 files β Tom Bradley\n\n" |
| "ACTION REQUIRED: VP Sales to review Salesforce pipeline within 48 hours.\n" |
| "Audit ID: OB-20240615-7845" |
| ), |
| "legal_subject": "Offboarding Compliance + GDPR Notice β Rachel Adams β OB-20240615-7845", |
| "legal_body": ( |
| "COMPLIANCE NOTICE\n\nEmployee Rachel Adams (Customer Success Manager, Sales) " |
| "has been offboarded.\n\nGDPR Note: 312 customer notification emails sent β " |
| "log retained per GDPR Article 30 record-keeping requirements.\n\n" |
| "CRM handoff: 47 opportunities, 312 contacts β Tom Bradley\n" |
| "Risk Level: MEDIUM\nAudit ID: OB-20240615-7845" |
| ), |
| }, |
| } |
|
|
| |
| |
| |
|
|
| RISK_COLORS = { |
| "HIGH": ("#fca5a5", "rgba(127,29,29,0.45)"), |
| "MEDIUM": ("#fde68a", "rgba(120,53,15,0.40)"), |
| "LOW": ("#86efac", "rgba(20,83,45,0.40)"), |
| } |
|
|
|
|
| def _badge(text: str, bg: str, color: str) -> str: |
| return ( |
| f'<span style="background:{bg};color:{color};padding:3px 10px;' |
| f'border-radius:12px;font-size:0.75rem;font-weight:700;' |
| f'letter-spacing:0.05em;">{text}</span>' |
| ) |
|
|
|
|
| def _system_chip(name: str) -> str: |
| return ( |
| f'<span style="background:rgba(99,102,241,0.25);color:#c7d2fe;' |
| f'border:1px solid rgba(99,102,241,0.4);padding:3px 10px;' |
| f'border-radius:10px;font-size:0.8rem;margin:2px;display:inline-block;">' |
| f'β
{name}</span>' |
| ) |
|
|
|
|
| def render_demo_card(scenario_key: str) -> str: |
| s = SCENARIOS[scenario_key] |
| risk_color, risk_bg = RISK_COLORS.get(s["risk_level"], ("#e2e8f0", "rgba(50,50,80,0.4)")) |
|
|
| |
| header = f""" |
| <div style="background:rgba(30,27,75,0.55);border:1px solid rgba(99,102,241,0.35); |
| border-radius:14px;padding:20px 24px;margin-bottom:14px;"> |
| <div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;"> |
| <div> |
| <div style="font-size:1.35rem;font-weight:800;color:#e2e8f0;"> |
| π {s['employee']} |
| </div> |
| <div style="color:#a5b4fc;font-size:0.9rem;margin-top:4px;"> |
| {s['role']} Β· {s['dept']} Β· Last day: {s['last_day']} Β· Manager: {s['manager']} |
| </div> |
| <div style="color:#94a3b8;font-size:0.8rem;margin-top:2px;"> |
| Offboarding ID: <code style="color:#c7d2fe;">{s['offboarding_id']}</code> |
| </div> |
| </div> |
| <div style="display:flex;flex-direction:column;align-items:flex-end;gap:6px;"> |
| {_badge('OFFBOARDING COMPLETE β
', 'rgba(21,128,61,0.45)', '#86efac')} |
| {_badge('Risk: ' + s['risk_level'], risk_bg, risk_color)} |
| </div> |
| </div> |
| </div> |
| """ |
|
|
| |
| chips = "".join(_system_chip(sys) for sys in s["systems"]) |
| systems_card = f""" |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:16px 20px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:10px;font-size:0.95rem;"> |
| π‘οΈ Systems Revoked / Actions Completed |
| </div> |
| <div>{chips}</div> |
| <div style="color:#94a3b8;font-size:0.82rem;margin-top:10px;"> |
| π {s['files_transferred']:,} Google Drive files transferred to {s['manager']} |
| </div> |
| </div> |
| """ |
|
|
| |
| extra_html = "" |
| if s.get("extra_label"): |
| extra_html = f""" |
| <div style="background:rgba(234,179,8,0.15);border:1px solid rgba(234,179,8,0.35); |
| border-radius:10px;padding:12px 18px;margin-bottom:14px;"> |
| <span style="color:#fde68a;font-weight:700;">β‘ {s['extra_label']}</span> |
| <span style="color:#fcd34d;font-size:0.85rem;margin-left:10px;">{s['extra_detail']}</span> |
| </div> |
| """ |
|
|
| |
| tl_rows = "" |
| for time, action, status in s["timeline"]: |
| tl_rows += f""" |
| <tr> |
| <td style="color:#7c3aed;font-size:0.8rem;padding:5px 12px 5px 0; |
| white-space:nowrap;font-family:monospace;">{time}</td> |
| <td style="color:#e2e8f0;font-size:0.85rem;padding:5px 0;"> |
| β
{action} |
| </td> |
| </tr> |
| """ |
| timeline_card = f""" |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:16px 20px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:10px;font-size:0.95rem;"> |
| β±οΈ Action Timeline |
| </div> |
| <table style="border-collapse:collapse;width:100%;">{tl_rows}</table> |
| </div> |
| """ |
|
|
| |
| flags_html = "" |
| if s["compliance_flags"]: |
| flags_items = "".join( |
| f'<li style="color:#fde68a;font-size:0.85rem;margin:4px 0;">β οΈ {f}</li>' |
| for f in s["compliance_flags"] |
| ) |
| flags_html = f""" |
| <div style="background:rgba(120,53,15,0.35);border:1px solid rgba(234,179,8,0.35); |
| border-radius:12px;padding:14px 18px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#fcd34d;margin-bottom:8px;">β οΈ Compliance Flags</div> |
| <ul style="margin:0;padding-left:16px;">{flags_items}</ul> |
| </div> |
| """ |
| else: |
| flags_html = """ |
| <div style="background:rgba(21,128,61,0.2);border:1px solid rgba(134,239,172,0.3); |
| border-radius:12px;padding:12px 18px;margin-bottom:14px;"> |
| <span style="color:#86efac;font-size:0.9rem;">β
No compliance flags raised</span> |
| </div> |
| """ |
|
|
| |
| audit_card = f""" |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:16px 20px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:8px;font-size:0.95rem;"> |
| π Audit Report Summary |
| </div> |
| <p style="color:#cbd5e1;font-size:0.88rem;line-height:1.6;margin:0;"> |
| {s['audit_summary']} |
| </p> |
| </div> |
| """ |
|
|
| |
| def email_block(label: str, to: str, subject: str, body: str, accent: str) -> str: |
| body_escaped = body.replace("\n", "<br>") |
| return f""" |
| <div style="background:rgba(15,23,42,0.55);border:1px solid {accent}; |
| border-radius:12px;padding:16px 20px;margin-bottom:12px;"> |
| <div style="font-weight:700;color:#e2e8f0;margin-bottom:8px;font-size:0.9rem;"> |
| π§ {label} |
| </div> |
| <div style="color:#94a3b8;font-size:0.8rem;margin-bottom:6px;"> |
| <b style="color:#a5b4fc;">To:</b> {to} |
| </div> |
| <div style="color:#94a3b8;font-size:0.8rem;margin-bottom:10px;"> |
| <b style="color:#a5b4fc;">Subject:</b> {subject} |
| </div> |
| <div style="background:rgba(0,0,0,0.3);border-radius:8px;padding:12px; |
| color:#cbd5e1;font-size:0.82rem;line-height:1.6; |
| font-family:monospace;white-space:pre-wrap;">{body_escaped}</div> |
| </div> |
| """ |
|
|
| emails_card = f""" |
| <div style="background:rgba(30,27,75,0.35);border:1px solid rgba(99,102,241,0.2); |
| border-radius:12px;padding:14px 18px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:12px;font-size:0.95rem;"> |
| π¨ Compliance Email Previews |
| </div> |
| {email_block('Email to IT Security', 'it-security@company.com', s['it_subject'], s['it_body'], 'rgba(99,102,241,0.4)')} |
| {email_block('Email to Legal / Compliance', 'legal@company.com', s['legal_subject'], s['legal_body'], 'rgba(239,68,68,0.4)')} |
| </div> |
| """ |
|
|
| return header + systems_card + extra_html + timeline_card + flags_html + audit_card + emails_card |
|
|
|
|
| |
| |
| |
|
|
| def generate_offboarding_report( |
| name: str, |
| email: str, |
| role: str, |
| department: str, |
| manager_name: str, |
| manager_email: str, |
| last_day: str, |
| reason: str, |
| extra_systems: list, |
| api_key: str, |
| ) -> str: |
| if not api_key or not api_key.strip(): |
| api_key = os.environ.get("OPENAI_API_KEY", "") |
| if not api_key: |
| return _error_html( |
| "No API Key", |
| "Please provide your OpenAI API key in the field above, or set the " |
| "<code>OPENAI_API_KEY</code> environment variable / HF Space secret.", |
| ) |
| if not name.strip(): |
| return _error_html("Missing Input", "Please enter the employee name.") |
|
|
| last_day_str = last_day if isinstance(last_day, str) else str(last_day) |
| systems_list = ["Google Workspace", "Slack", "GitHub", "Google Drive"] + (extra_systems or []) |
| systems_str = ", ".join(systems_list) |
|
|
| prompt = f"""You are an enterprise IT security agent processing an employee offboarding. |
| |
| Employee details: |
| - Name: {name} |
| - Email: {email} |
| - Role: {role} |
| - Department: {department} |
| - Manager: {manager_name} ({manager_email}) |
| - Last Working Day: {last_day_str} |
| - Reason: {reason} |
| - Systems to revoke: {systems_str} |
| |
| Generate a realistic offboarding report. Return ONLY valid JSON with this exact structure: |
| {{ |
| "offboarding_id": "OB-YYYYMMDD-XXXX", |
| "risk_level": "LOW|MEDIUM|HIGH", |
| "systems_revoked": {json.dumps(systems_list)}, |
| "files_transferred": <integer estimate of files>, |
| "audit_timeline": [ |
| {{"time": "T+0min", "action": "Offboarding initiated", "status": "completed"}}, |
| {{"time": "T+1min", "action": "Google Workspace suspended", "status": "completed"}} |
| ], |
| "compliance_flags": ["flag 1 if any"], |
| "audit_summary": "2-3 sentence audit summary", |
| "it_email_subject": "...", |
| "it_email_body": "...", |
| "legal_email_subject": "...", |
| "legal_email_body": "...", |
| "manager_handoff_notes": "..." |
| }} |
| |
| Make the timeline realistic (6-10 steps). Set risk_level based on role seniority and department. |
| Finance/Legal/C-suite = HIGH. Senior IC = MEDIUM. Junior = LOW. |
| For Finance add SOX flags. For Sales add CRM handoff flags. For VP+ add elevated security flags.""" |
|
|
| try: |
| from openai import OpenAI |
| client = OpenAI(api_key=api_key.strip()) |
| response = client.chat.completions.create( |
| model="gpt-4o-mini", |
| messages=[{"role": "user", "content": prompt}], |
| temperature=0.3, |
| max_tokens=1800, |
| response_format={"type": "json_object"}, |
| ) |
| raw = response.choices[0].message.content |
| data = json.loads(raw) |
| except Exception as exc: |
| return _error_html("API Error", f"<code>{str(exc)}</code>") |
|
|
| return _render_report(data, name, role, department, manager_name, last_day_str) |
|
|
|
|
| def _error_html(title: str, body: str) -> str: |
| return f""" |
| <div style="background:rgba(127,29,29,0.45);border:1px solid rgba(239,68,68,0.5); |
| border-radius:12px;padding:20px 24px;color:#fca5a5;"> |
| <div style="font-weight:700;font-size:1.1rem;margin-bottom:8px;">β {title}</div> |
| <div style="color:#fecaca;font-size:0.9rem;">{body}</div> |
| </div> |
| """ |
|
|
|
|
| def _render_report(data: dict, name: str, role: str, dept: str, manager: str, last_day: str) -> str: |
| risk = data.get("risk_level", "MEDIUM") |
| risk_color, risk_bg = RISK_COLORS.get(risk, ("#e2e8f0", "rgba(50,50,80,0.4)")) |
| oid = data.get("offboarding_id", "OB-UNKNOWN") |
| files = data.get("files_transferred", 0) |
| systems = data.get("systems_revoked", []) |
| timeline = data.get("audit_timeline", []) |
| flags = data.get("compliance_flags", []) |
| summary = data.get("audit_summary", "") |
| it_subj = data.get("it_email_subject", "") |
| it_body = data.get("it_email_body", "") |
| legal_subj = data.get("legal_email_subject", "") |
| legal_body = data.get("legal_email_body", "") |
| handoff = data.get("manager_handoff_notes", "") |
|
|
| chips = "".join(_system_chip(s) for s in systems) |
|
|
| tl_rows = "" |
| for item in timeline: |
| tl_rows += f""" |
| <tr> |
| <td style="color:#7c3aed;font-size:0.8rem;padding:5px 12px 5px 0; |
| white-space:nowrap;font-family:monospace;">{item.get('time','')}</td> |
| <td style="color:#e2e8f0;font-size:0.85rem;padding:5px 0;"> |
| β
{item.get('action','')} |
| </td> |
| </tr> |
| """ |
|
|
| flags_html = "" |
| if flags: |
| items = "".join( |
| f'<li style="color:#fde68a;font-size:0.85rem;margin:4px 0;">β οΈ {f}</li>' |
| for f in flags |
| ) |
| flags_html = f""" |
| <div style="background:rgba(120,53,15,0.35);border:1px solid rgba(234,179,8,0.35); |
| border-radius:12px;padding:14px 18px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#fcd34d;margin-bottom:8px;">β οΈ Compliance Flags</div> |
| <ul style="margin:0;padding-left:16px;">{items}</ul> |
| </div> |
| """ |
| else: |
| flags_html = """ |
| <div style="background:rgba(21,128,61,0.2);border:1px solid rgba(134,239,172,0.3); |
| border-radius:12px;padding:12px 18px;margin-bottom:14px;"> |
| <span style="color:#86efac;">β
No compliance flags raised</span> |
| </div> |
| """ |
|
|
| def email_block(label, to, subject, body_text, accent): |
| body_escaped = body_text.replace("\n", "<br>") |
| return f""" |
| <div style="background:rgba(15,23,42,0.55);border:1px solid {accent}; |
| border-radius:12px;padding:16px 20px;margin-bottom:12px;"> |
| <div style="font-weight:700;color:#e2e8f0;margin-bottom:6px;">π§ {label}</div> |
| <div style="color:#94a3b8;font-size:0.8rem;margin-bottom:4px;"> |
| <b style="color:#a5b4fc;">To:</b> {to} |
| </div> |
| <div style="color:#94a3b8;font-size:0.8rem;margin-bottom:10px;"> |
| <b style="color:#a5b4fc;">Subject:</b> {subject} |
| </div> |
| <div style="background:rgba(0,0,0,0.3);border-radius:8px;padding:12px; |
| color:#cbd5e1;font-size:0.82rem;line-height:1.6; |
| font-family:monospace;">{body_escaped}</div> |
| </div> |
| """ |
|
|
| return f""" |
| <div style="background:rgba(30,27,75,0.55);border:1px solid rgba(99,102,241,0.35); |
| border-radius:14px;padding:20px 24px;margin-bottom:14px;"> |
| <div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:8px;"> |
| <div> |
| <div style="font-size:1.3rem;font-weight:800;color:#e2e8f0;">π {name}</div> |
| <div style="color:#a5b4fc;font-size:0.88rem;margin-top:4px;"> |
| {role} Β· {dept} Β· Last day: {last_day} Β· Manager: {manager} |
| </div> |
| <div style="color:#94a3b8;font-size:0.8rem;margin-top:2px;"> |
| ID: <code style="color:#c7d2fe;">{oid}</code> |
| </div> |
| </div> |
| <div style="display:flex;flex-direction:column;align-items:flex-end;gap:6px;"> |
| {_badge('OFFBOARDING COMPLETE β
', 'rgba(21,128,61,0.45)', '#86efac')} |
| {_badge('Risk: ' + risk, risk_bg, risk_color)} |
| </div> |
| </div> |
| </div> |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:16px 20px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:10px;">π‘οΈ Systems Revoked</div> |
| <div>{chips}</div> |
| <div style="color:#94a3b8;font-size:0.82rem;margin-top:10px;"> |
| π {files:,} Google Drive files transferred to {manager} |
| </div> |
| </div> |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:16px 20px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:10px;">β±οΈ Action Timeline</div> |
| <table style="border-collapse:collapse;width:100%;">{tl_rows}</table> |
| </div> |
| |
| {flags_html} |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:16px 20px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:8px;">π Audit Summary</div> |
| <p style="color:#cbd5e1;font-size:0.88rem;line-height:1.6;margin:0;">{summary}</p> |
| </div> |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:16px 20px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:8px;">π Manager Handoff Notes</div> |
| <p style="color:#cbd5e1;font-size:0.88rem;line-height:1.6;margin:0;">{handoff}</p> |
| </div> |
| |
| <div style="background:rgba(30,27,75,0.35);border:1px solid rgba(99,102,241,0.2); |
| border-radius:12px;padding:14px 18px;margin-bottom:14px;"> |
| <div style="font-weight:700;color:#c7d2fe;margin-bottom:12px;">π¨ Compliance Email Previews</div> |
| {email_block('Email to IT Security', 'it-security@company.com', it_subj, it_body, 'rgba(99,102,241,0.4)')} |
| {email_block('Email to Legal / Compliance', 'legal@company.com', legal_subj, legal_body, 'rgba(239,68,68,0.4)')} |
| </div> |
| """ |
|
|
|
|
| |
| |
| |
|
|
| HOW_IT_WORKS_HTML = """ |
| <div style="color:#e2e8f0;max-width:860px;margin:0 auto;padding:8px 0;"> |
| |
| <div style="background:rgba(30,27,75,0.55);border:1px solid rgba(99,102,241,0.35); |
| border-radius:14px;padding:24px 28px;margin-bottom:20px;"> |
| <h2 style="color:#c7d2fe;margin:0 0 12px;font-size:1.25rem;">π§ n8n Workflow Overview</h2> |
| <p style="color:#cbd5e1;line-height:1.7;margin:0;"> |
| This agent is powered by an <strong style="color:#a5b4fc;">n8n automation workflow</strong> |
| that chains together API calls to every system in your tech stack. HR triggers the workflow |
| via a webhook or form submission β everything else is hands-free. |
| </p> |
| </div> |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:20px 24px;margin-bottom:16px;"> |
| <h3 style="color:#c7d2fe;margin:0 0 16px;font-size:1.05rem;">π Workflow Steps</h3> |
| <div style="display:grid;gap:10px;"> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 1 β HR Trigger</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| Webhook receives employee data from HR system (Workday, BambooHR, etc.) or manual form. |
| Validates required fields and routes to risk assessment. |
| </div> |
| </div> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 2 β Risk Assessment</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| AI node evaluates role, department, and access level to assign LOW / MEDIUM / HIGH risk. |
| HIGH risk triggers immediate revocation; others follow standard schedule. |
| </div> |
| </div> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 3 β Google Workspace</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| Calls Google Admin SDK: suspends account, revokes OAuth tokens, strips admin roles. |
| Parallel sub-flow initiates Drive file transfer to manager. |
| </div> |
| </div> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 4 β Slack</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| Calls Slack Admin API: deactivates user, removes from all channels and user groups, |
| revokes active sessions and OAuth tokens. |
| </div> |
| </div> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 5 β GitHub</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| Calls GitHub REST API: removes org membership, removes from all teams, |
| revokes SSH keys and personal access tokens. |
| </div> |
| </div> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 6 β Additional Systems</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| Conditional branches handle Salesforce (CRM handoff), Jira (ticket reassignment), |
| AWS (IAM deactivation), Figma, and Notion based on selected systems. |
| </div> |
| </div> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 7 β Audit Report</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| Aggregates all action results, timestamps, and compliance flags into a structured |
| audit report. Logs to immutable compliance database with unique Offboarding ID. |
| </div> |
| </div> |
| |
| <div style="background:rgba(99,102,241,0.15);border-radius:10px;padding:12px 16px; |
| border-left:3px solid #6366f1;"> |
| <div style="color:#a5b4fc;font-weight:700;font-size:0.9rem;">Step 8 β Compliance Notification</div> |
| <div style="color:#94a3b8;font-size:0.85rem;margin-top:4px;"> |
| Sends formatted confirmation emails to IT Security and Legal/Compliance teams. |
| Optionally triggers Slack notification to manager with handoff notes. |
| </div> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px;"> |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:18px 20px;"> |
| <h3 style="color:#c7d2fe;margin:0 0 12px;font-size:1rem;">π Credentials Required</h3> |
| <ul style="color:#94a3b8;font-size:0.85rem;line-height:1.8;margin:0;padding-left:16px;"> |
| <li>Google Workspace Admin SDK</li> |
| <li>Google Drive API (service account)</li> |
| <li>Slack Admin API token</li> |
| <li>GitHub Personal Access Token (org:admin)</li> |
| <li>Salesforce Connected App (optional)</li> |
| <li>AWS IAM credentials (optional)</li> |
| <li>SMTP credentials for email</li> |
| </ul> |
| </div> |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:18px 20px;"> |
| <h3 style="color:#c7d2fe;margin:0 0 12px;font-size:1rem;">β‘ Performance</h3> |
| <div style="display:grid;gap:8px;"> |
| <div style="background:rgba(99,102,241,0.15);border-radius:8px;padding:8px 12px;"> |
| <div style="color:#a5b4fc;font-size:0.8rem;">Average completion time</div> |
| <div style="color:#e2e8f0;font-weight:700;">Under 8 minutes</div> |
| </div> |
| <div style="background:rgba(99,102,241,0.15);border-radius:8px;padding:8px 12px;"> |
| <div style="color:#a5b4fc;font-size:0.8rem;">Manual steps required</div> |
| <div style="color:#86efac;font-weight:700;">Zero</div> |
| </div> |
| <div style="background:rgba(99,102,241,0.15);border-radius:8px;padding:8px 12px;"> |
| <div style="color:#a5b4fc;font-size:0.8rem;">Audit trail</div> |
| <div style="color:#e2e8f0;font-weight:700;">100% immutable</div> |
| </div> |
| </div> |
| </div> |
| |
| </div> |
| |
| <div style="background:rgba(15,23,42,0.55);border:1px solid rgba(99,102,241,0.25); |
| border-radius:12px;padding:18px 20px;"> |
| <h3 style="color:#c7d2fe;margin:0 0 12px;font-size:1rem;">ποΈ Tech Stack</h3> |
| <div style="display:flex;flex-wrap:wrap;gap:8px;"> |
| <span style="background:rgba(239,68,68,0.2);color:#fca5a5;border:1px solid rgba(239,68,68,0.3); |
| padding:4px 12px;border-radius:8px;font-size:0.82rem;">n8n Automation</span> |
| <span style="background:rgba(234,179,8,0.15);color:#fde68a;border:1px solid rgba(234,179,8,0.3); |
| padding:4px 12px;border-radius:8px;font-size:0.82rem;">GPT-4o-mini</span> |
| <span style="background:rgba(99,102,241,0.2);color:#c7d2fe;border:1px solid rgba(99,102,241,0.3); |
| padding:4px 12px;border-radius:8px;font-size:0.82rem;">Google Workspace API</span> |
| <span style="background:rgba(99,102,241,0.2);color:#c7d2fe;border:1px solid rgba(99,102,241,0.3); |
| padding:4px 12px;border-radius:8px;font-size:0.82rem;">Slack Admin API</span> |
| <span style="background:rgba(99,102,241,0.2);color:#c7d2fe;border:1px solid rgba(99,102,241,0.3); |
| padding:4px 12px;border-radius:8px;font-size:0.82rem;">GitHub REST API</span> |
| <span style="background:rgba(21,128,61,0.2);color:#86efac;border:1px solid rgba(21,128,61,0.3); |
| padding:4px 12px;border-radius:8px;font-size:0.82rem;">Gradio 5.9.1</span> |
| </div> |
| </div> |
| |
| </div> |
| """ |
|
|
|
|
| |
| |
| |
|
|
| with gr.Blocks( |
| theme=gr.themes.Soft(primary_hue="red", secondary_hue="indigo"), |
| title="Employee Offboarding Security Agent", |
| css=""" |
| .gradio-container { max-width: 960px !important; margin: 0 auto; } |
| footer { display: none !important; } |
| """, |
| ) as demo: |
|
|
| gr.HTML(""" |
| <div style="text-align:center;padding:28px 20px 18px; |
| background:rgba(30,27,75,0.5);border-radius:16px;margin-bottom:8px;"> |
| <div style="font-size:2.4rem;margin-bottom:8px;">π</div> |
| <h1 style="color:#e2e8f0;font-size:1.8rem;font-weight:800;margin:0 0 8px;"> |
| Employee Offboarding Security Agent |
| </h1> |
| <p style="color:#a5b4fc;font-size:1rem;margin:0;"> |
| Zero-touch offboarding Β· Access revoked in <8 minutes Β· Full audit trail |
| </p> |
| <div style="display:flex;justify-content:center;gap:10px;margin-top:14px;flex-wrap:wrap;"> |
| <span style="background:rgba(99,102,241,0.25);color:#c7d2fe; |
| border:1px solid rgba(99,102,241,0.4);padding:4px 14px; |
| border-radius:20px;font-size:0.82rem;">β‘ Powered by n8n</span> |
| <span style="background:rgba(239,68,68,0.2);color:#fca5a5; |
| border:1px solid rgba(239,68,68,0.35);padding:4px 14px; |
| border-radius:20px;font-size:0.82rem;">π‘οΈ Zero Manual Steps</span> |
| <span style="background:rgba(21,128,61,0.2);color:#86efac; |
| border:1px solid rgba(21,128,61,0.35);padding:4px 14px; |
| border-radius:20px;font-size:0.82rem;">π SOX / GDPR Compliant</span> |
| </div> |
| </div> |
| """) |
|
|
| with gr.Tabs(): |
|
|
| |
| |
| |
| with gr.TabItem("π Live Demo"): |
| gr.HTML(""" |
| <div style="background:rgba(15,23,42,0.5);border:1px solid rgba(99,102,241,0.25); |
| border-radius:10px;padding:12px 18px;margin-bottom:14px;"> |
| <span style="color:#a5b4fc;font-size:0.9rem;"> |
| β¨ <strong style="color:#c7d2fe;">Pre-computed scenarios</strong> β no API key required. |
| Select an employee to view their full offboarding report. |
| </span> |
| </div> |
| """) |
|
|
| scenario_selector = gr.Radio( |
| choices=list(SCENARIOS.keys()), |
| value=list(SCENARIOS.keys())[0], |
| label="Select Offboarding Scenario", |
| interactive=True, |
| ) |
|
|
| demo_output = gr.HTML(value=render_demo_card(list(SCENARIOS.keys())[0])) |
|
|
| scenario_selector.change( |
| fn=render_demo_card, |
| inputs=scenario_selector, |
| outputs=demo_output, |
| ) |
|
|
| |
| |
| |
| with gr.TabItem("π€ Run Offboarding"): |
| gr.HTML(""" |
| <div style="background:rgba(127,29,29,0.35);border:1px solid rgba(239,68,68,0.4); |
| border-radius:10px;padding:12px 18px;margin-bottom:16px;"> |
| <span style="color:#fca5a5;font-size:0.9rem;"> |
| π Requires an <strong>OpenAI API key</strong> to call GPT-4o-mini. |
| Set the <code>OPENAI_API_KEY</code> HF Space secret or enter it below. |
| </span> |
| </div> |
| """) |
|
|
| with gr.Row(): |
| with gr.Column(): |
| emp_name = gr.Textbox(label="Employee Name", placeholder="e.g. Jane Smith") |
| emp_email = gr.Textbox(label="Employee Email", placeholder="jane@company.com") |
| emp_role = gr.Textbox(label="Role / Title", placeholder="e.g. Senior Engineer") |
| emp_dept = gr.Textbox(label="Department", placeholder="e.g. Engineering") |
|
|
| with gr.Column(): |
| mgr_name = gr.Textbox(label="Manager Name", placeholder="e.g. Tom Bradley") |
| mgr_email = gr.Textbox(label="Manager Email", placeholder="tom@company.com") |
| last_day = gr.Textbox( |
| label="Last Working Day", |
| placeholder="e.g. 2024-07-31", |
| value=date.today().isoformat(), |
| ) |
| reason = gr.Dropdown( |
| choices=["Resignation", "Termination", "Retirement", "Contract End"], |
| value="Resignation", |
| label="Reason for Offboarding", |
| ) |
|
|
| extra_systems = gr.CheckboxGroup( |
| choices=["Salesforce", "Jira", "AWS", "Figma", "Notion"], |
| label="Additional Systems to Revoke (beyond Google/Slack/GitHub)", |
| value=[], |
| ) |
|
|
| api_key_input = gr.Textbox( |
| label="OpenAI API Key (or set OPENAI_API_KEY secret)", |
| placeholder="sk-...", |
| type="password", |
| ) |
|
|
| run_btn = gr.Button("π Run Offboarding Agent", variant="primary", size="lg") |
|
|
| report_output = gr.HTML( |
| value=""" |
| <div style="background:rgba(15,23,42,0.4);border:1px solid rgba(99,102,241,0.2); |
| border-radius:12px;padding:32px;text-align:center;color:#64748b;"> |
| Fill in the employee details above and click <strong style="color:#a5b4fc;"> |
| Run Offboarding Agent</strong> to generate a full report. |
| </div> |
| """ |
| ) |
|
|
| run_btn.click( |
| fn=generate_offboarding_report, |
| inputs=[ |
| emp_name, emp_email, emp_role, emp_dept, |
| mgr_name, mgr_email, last_day, reason, |
| extra_systems, api_key_input, |
| ], |
| outputs=report_output, |
| ) |
|
|
| |
| |
| |
| with gr.TabItem("βοΈ How It Works"): |
| gr.HTML(HOW_IT_WORKS_HTML) |
|
|
| gr.HTML(""" |
| <div style="text-align:center;padding:14px;color:#475569;font-size:0.8rem;margin-top:8px;"> |
| Employee Offboarding Security Agent Β· Built with n8n + Gradio 5.9.1 Β· MIT License |
| </div> |
| """) |
|
|
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|