SurveyVista/Web/Areas/Admin/Views/Questionnaire/SendQuestionnaire.cshtml
2026-03-07 02:37:33 +01:00

496 lines
33 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@model SendQuestionnaireViewModel
@{
ViewData["Title"] = "Send Questionnaire";
}
@section Styles {
<style>
@@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Space+Grotesk:wght@300;400;500;600;700&display=swap');
:root {
--neon-blue:#60a5fa;--neon-purple:#c084fc;--neon-green:#34d399;--neon-pink:#f472b6;
--neon-yellow:#fbbf24;--neon-red:#f87171;--neon-cyan:#22d3ee;--neon-teal:#33b3ae;--neon-indigo:#818cf8;
--dark-900:#0f172a;--dark-800:#1e293b;--dark-700:#334155;--dark-600:#475569;
--dark-500:#64748b;--dark-400:#94a3b8;--dark-300:#cbd5e1;--dark-200:#e2e8f0;
--glass-bg:rgba(255,255,255,0.05);--glass-border:rgba(255,255,255,0.1);--glass-shadow:0 8px 32px rgba(0,0,0,0.12);
--font-main:'Space Grotesk',sans-serif;--font-mono:'JetBrains Mono',monospace;
}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:var(--font-main);background:var(--dark-900);color:#e2e8f0;overflow-x:hidden}
.nexgen-send{min-height:100vh;position:relative}
.bg-pattern{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:-1;overflow:hidden}
.grid-overlay{position:absolute;inset:0;background-image:linear-gradient(rgba(96,165,250,0.1) 1px,transparent 1px),linear-gradient(90deg,rgba(96,165,250,0.1) 1px,transparent 1px);background-size:60px 60px;animation:gridMove 20s linear infinite}
.gradient-mesh{position:absolute;inset:0;background:radial-gradient(circle at 20% 20%,rgba(96,165,250,0.15) 0%,transparent 50%),radial-gradient(circle at 80% 60%,rgba(192,132,252,0.15) 0%,transparent 50%),radial-gradient(circle at 40% 80%,rgba(52,211,153,0.15) 0%,transparent 50%);animation:meshShift 15s ease-in-out infinite}
@@keyframes gridMove{0%{transform:translate(0,0)}100%{transform:translate(60px,60px)}}
@@keyframes meshShift{0%,100%{filter:hue-rotate(0deg);transform:scale(1)}50%{filter:hue-rotate(30deg);transform:scale(1.05)}}
@@keyframes fadeInUp{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}}
@@keyframes slideIn{from{opacity:0;transform:translateX(12px)}to{opacity:1;transform:translateX(0)}}
/* NAV */
.top-nav{position:sticky;top:0;z-index:100;background:rgba(15,23,42,0.85);backdrop-filter:blur(20px);border-bottom:1px solid rgba(96,165,250,0.15);padding:0.8rem 2rem}
.nav-container{max-width:100%;margin:0 auto;padding:0 1rem;display:flex;align-items:center;justify-content:space-between;gap:1rem}
.nav-brand{display:flex;align-items:center;gap:0.8rem}
.brand-icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--neon-green),var(--neon-teal));display:flex;align-items:center;justify-content:center;font-size:16px;color:#fff;box-shadow:0 4px 16px rgba(52,211,153,0.3)}
.brand-text{display:flex;flex-direction:column}
.brand-name{font-family:var(--font-mono);font-size:0.72rem;font-weight:700;color:#fff;letter-spacing:0.1em}
.brand-version{font-family:var(--font-mono);font-size:0.58rem;color:var(--dark-500)}
.breadcrumb-nex{display:flex;align-items:center;gap:0.6rem;font-family:var(--font-mono);font-size:0.7rem}
.breadcrumb-nex .crumb{color:var(--dark-400);text-decoration:none;transition:color 0.2s}
.breadcrumb-nex .crumb:hover{color:var(--neon-blue)}
.breadcrumb-nex .crumb.active{color:var(--neon-green)}
.breadcrumb-nex i.sep{font-size:0.5rem;color:var(--dark-600)}
.nav-btn{display:inline-flex;align-items:center;gap:0.5rem;padding:0.5rem 1.2rem;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.15);border-radius:8px;color:var(--dark-300);font-family:var(--font-mono);font-size:0.7rem;font-weight:600;transition:all 0.2s;text-decoration:none}
.nav-btn:hover{background:rgba(255,255,255,0.1);border-color:rgba(96,165,250,0.3);color:#fff;text-decoration:none}
/* MAIN */
.send-content{max-width:1200px;margin:0 auto;padding:1.5rem 2rem}
/* ALERTS */
.nex-alert{border-radius:12px;padding:0.85rem 1.3rem;font-size:0.82rem;font-weight:500;margin-bottom:1.2rem;display:flex;align-items:center;gap:0.6rem;backdrop-filter:blur(10px);animation:fadeInUp 0.4s ease both}
.nex-alert-success{background:rgba(52,211,153,0.1);color:var(--neon-green);border:1px solid rgba(52,211,153,0.25)}
.nex-alert-error{background:rgba(248,113,113,0.1);color:var(--neon-red);border:1px solid rgba(248,113,113,0.25)}
.nex-alert .btn-close-nex{margin-left:auto;background:none;border:none;color:inherit;opacity:0.6;cursor:pointer;font-size:1rem;padding:0;line-height:1;transition:opacity 0.2s}
.nex-alert .btn-close-nex:hover{opacity:1}
/* HERO */
.send-hero{background:var(--glass-bg);backdrop-filter:blur(20px);border:1px solid var(--glass-border);border-radius:20px;overflow:hidden;box-shadow:var(--glass-shadow);margin-bottom:1.5rem;position:relative;animation:fadeInUp 0.5s ease both}
.send-hero::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--neon-green),var(--neon-teal),var(--neon-cyan))}
.send-hero-inner{padding:1.5rem 2rem;display:flex;align-items:center;gap:1.2rem;flex-wrap:wrap}
.send-hero-icon{width:50px;height:50px;border-radius:14px;background:linear-gradient(135deg,var(--neon-green),var(--neon-teal));display:flex;align-items:center;justify-content:center;font-size:22px;color:#fff;flex-shrink:0;box-shadow:0 8px 24px rgba(52,211,153,0.3)}
.send-hero-title{font-size:1.3rem;font-weight:700;color:#fff;margin-bottom:0.1rem}
.send-hero-sub{font-size:0.8rem;color:var(--dark-400)}
.send-hero-pills{margin-left:auto;display:flex;gap:0.5rem;flex-wrap:wrap}
.hero-pill{font-family:var(--font-mono);font-size:0.68rem;font-weight:700;padding:0.35rem 0.8rem;border-radius:100px;border:1px solid}
.hero-pill.survey{color:var(--neon-indigo);background:rgba(129,140,248,0.1);border-color:rgba(129,140,248,0.3)}
/* FORM CARD */
.send-form-card{background:var(--glass-bg);backdrop-filter:blur(20px);border:1px solid var(--glass-border);border-radius:20px;overflow:hidden;box-shadow:var(--glass-shadow);animation:fadeInUp 0.5s ease 0.08s both;position:relative}
.send-form-card::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--neon-green),var(--neon-cyan),var(--neon-indigo));z-index:1;border-radius:20px 20px 0 0}
/* FORM GROUP */
.nex-form-group{padding:1.5rem 2rem;border-bottom:1px solid rgba(255,255,255,0.04)}
.nex-form-group:last-child{border-bottom:none}
.nex-label{display:flex;align-items:center;gap:0.7rem;font-size:0.88rem;font-weight:700;color:#fff;margin-bottom:0.8rem}
.nex-label-icon{width:30px;height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:0.85rem;color:#fff;flex-shrink:0}
.nex-label-icon.green{background:linear-gradient(135deg,var(--neon-green),var(--neon-teal));box-shadow:0 4px 12px rgba(52,211,153,0.25)}
.nex-label-icon.blue{background:linear-gradient(135deg,var(--neon-blue),var(--neon-cyan));box-shadow:0 4px 12px rgba(96,165,250,0.25)}
.nex-label-icon.purple{background:linear-gradient(135deg,var(--neon-indigo),var(--neon-purple));box-shadow:0 4px 12px rgba(129,140,248,0.25)}
.nex-label-icon.yellow{background:linear-gradient(135deg,var(--neon-yellow),#f59e0b);box-shadow:0 4px 12px rgba(251,191,36,0.25)}
.nex-label-sub{font-family:var(--font-mono);font-size:0.6rem;color:var(--dark-500);font-weight:600;margin-left:auto;text-transform:uppercase;letter-spacing:0.05em}
/* INPUTS */
.nex-input,.nex-textarea,.nex-datetime{width:100%;padding:0.85rem 1.2rem;border-radius:12px;font-size:0.88rem;font-weight:500;color:#fff;background:rgba(255,255,255,0.04);border:1.5px solid rgba(255,255,255,0.1);transition:all 0.3s;font-family:var(--font-main);outline:none}
.nex-input:focus,.nex-textarea:focus,.nex-datetime:focus{border-color:rgba(52,211,153,0.5);box-shadow:0 0 0 3px rgba(52,211,153,0.1),0 8px 24px rgba(0,0,0,0.15);background:rgba(255,255,255,0.06)}
.nex-input::placeholder,.nex-textarea::placeholder{color:var(--dark-600)}
.nex-input:disabled{background:linear-gradient(135deg,rgba(129,140,248,0.15),rgba(192,132,252,0.15));border-color:rgba(129,140,248,0.3);color:var(--neon-indigo);font-weight:700;cursor:not-allowed;text-align:center}
.nex-textarea{min-height:100px;resize:vertical;line-height:1.7;font-family:var(--font-mono);font-size:0.8rem}
.nex-datetime{color-scheme:dark}
.nex-datetime::-webkit-calendar-picker-indicator{filter:invert(0.7);cursor:pointer}
/* ===== SIDE-BY-SIDE PICKER — FORCED HORIZONTAL ===== */
.picker-layout{display:flex;flex-direction:row;border-radius:14px;overflow:hidden;border:1px solid rgba(255,255,255,0.08);background:rgba(0,0,0,0.15)}
.picker-panel{flex:1;min-width:0;display:flex;flex-direction:column;height:380px}
.picker-panel + .picker-divider + .picker-panel {} /* right panel */
.panel-head{padding:0.7rem 1rem;display:flex;align-items:center;gap:0.6rem;border-bottom:1px solid rgba(255,255,255,0.06);flex-shrink:0}
.panel-head.left{background:rgba(96,165,250,0.06)}
.panel-head.right{background:rgba(52,211,153,0.06)}
.panel-badge{width:24px;height:24px;border-radius:7px;display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);font-size:0.65rem;font-weight:700;color:#fff}
.panel-badge.blue{background:linear-gradient(135deg,var(--neon-blue),var(--neon-cyan))}
.panel-badge.green{background:linear-gradient(135deg,var(--neon-green),var(--neon-teal))}
.panel-title{font-family:var(--font-mono);font-size:0.68rem;font-weight:700;text-transform:uppercase;letter-spacing:0.05em}
.panel-title.blue{color:var(--neon-blue)}
.panel-title.green{color:var(--neon-green)}
.panel-count{margin-left:auto;font-family:var(--font-mono);font-size:0.6rem;font-weight:700;color:var(--dark-500);background:rgba(255,255,255,0.05);padding:0.15rem 0.5rem;border-radius:100px}
.panel-search{padding:0.5rem 0.7rem;border-bottom:1px solid rgba(255,255,255,0.04);position:relative;flex-shrink:0}
.panel-search input{width:100%;padding:0.55rem 0.8rem 0.55rem 2.2rem;border-radius:8px;font-size:0.78rem;color:#fff;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);outline:none;font-family:var(--font-main);transition:all 0.2s}
.panel-search input:focus{border-color:rgba(96,165,250,0.4);background:rgba(255,255,255,0.06)}
.panel-search input::placeholder{color:var(--dark-600)}
.panel-search i{position:absolute;left:1.2rem;top:50%;transform:translateY(-50%);color:var(--dark-600);font-size:0.75rem;pointer-events:none}
.panel-list{flex:1;overflow-y:auto;padding:0.3rem}
.panel-list::-webkit-scrollbar{width:4px}
.panel-list::-webkit-scrollbar-track{background:transparent}
.panel-list::-webkit-scrollbar-thumb{background:var(--dark-600);border-radius:100px}
.user-row{display:flex;align-items:center;gap:0.65rem;padding:0.55rem 0.7rem;border-radius:9px;cursor:pointer;transition:all 0.15s;margin-bottom:0.15rem}
.user-row:hover{background:rgba(96,165,250,0.08)}
.user-row.in-selected{opacity:0.3;pointer-events:none}
.user-row-avatar{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;font-size:0.7rem;font-weight:700;color:#fff;flex-shrink:0}
.user-row-avatar.av-blue{background:linear-gradient(135deg,var(--neon-blue),var(--neon-purple))}
.user-row-avatar.av-green{background:linear-gradient(135deg,var(--neon-green),var(--neon-teal))}
.user-row-info{flex:1;min-width:0}
.user-row-name{font-size:0.8rem;font-weight:600;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.user-row-email{font-family:var(--font-mono);font-size:0.65rem;color:var(--dark-400);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.user-row-action{width:24px;height:24px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:0.75rem;flex-shrink:0;transition:all 0.2s}
.user-row-action.add{color:var(--neon-green);background:rgba(52,211,153,0.1)}
.user-row:hover .user-row-action.add{background:rgba(52,211,153,0.2)}
.user-row-action.remove{color:var(--neon-red);background:rgba(248,113,113,0.1)}
.user-row:hover .user-row-action.remove{background:rgba(248,113,113,0.2)}
.picker-divider{width:1px;background:rgba(255,255,255,0.06);flex-shrink:0}
.panel-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;flex:1;padding:2rem 1rem;text-align:center}
.panel-empty-icon{font-size:1.8rem;color:var(--dark-600);margin-bottom:0.5rem}
.panel-empty-text{font-size:0.73rem;color:var(--dark-500);max-width:200px;line-height:1.5}
.panel-actions{padding:0.4rem 0.7rem;border-top:1px solid rgba(255,255,255,0.04);display:flex;gap:0.3rem;flex-shrink:0}
.panel-action-btn{flex:1;padding:0.4rem;border-radius:7px;border:none;font-family:var(--font-mono);font-size:0.62rem;font-weight:700;cursor:pointer;transition:all 0.2s;text-transform:uppercase;letter-spacing:0.04em}
.panel-action-btn.select-all{background:rgba(52,211,153,0.08);color:var(--neon-green);border:1px solid rgba(52,211,153,0.2)}
.panel-action-btn.select-all:hover{background:rgba(52,211,153,0.15)}
.panel-action-btn.deselect-all{background:rgba(248,113,113,0.08);color:var(--neon-red);border:1px solid rgba(248,113,113,0.2)}
.panel-action-btn.deselect-all:hover{background:rgba(248,113,113,0.15)}
/* ===== TWO-COLUMN ROW (Textarea + Expiration) ===== */
.two-col-row{display:flex;gap:1.5rem;padding:1.5rem 2rem;border-bottom:1px solid rgba(255,255,255,0.04)}
.two-col-row .col-left{flex:1;min-width:0}
.two-col-row .col-right{width:340px;flex-shrink:0}
/* DIVIDER */
.or-divider{display:flex;align-items:center;gap:0.8rem;margin:0.8rem 0}
.or-divider .line{flex:1;height:1px;background:rgba(255,255,255,0.06)}
.or-divider .or-text{font-family:var(--font-mono);font-size:0.6rem;font-weight:700;color:var(--dark-600);text-transform:uppercase;letter-spacing:0.1em}
/* HELPER */
.nex-helper{margin-top:0.6rem;font-size:0.73rem;color:var(--dark-500);background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.05);border-left:3px solid var(--neon-cyan);border-radius:0 8px 8px 0;padding:0.5rem 0.9rem;display:flex;align-items:flex-start;gap:0.4rem}
.nex-helper i{color:var(--neon-cyan);margin-top:0.1rem;flex-shrink:0}
/* VALIDATION */
.field-validation-error,.text-danger{color:var(--neon-red);font-size:0.73rem;font-weight:600;margin-top:0.5rem;display:block;background:rgba(248,113,113,0.08);padding:0.4rem 0.7rem;border-radius:8px;border-left:3px solid var(--neon-red)}
.input-validation-error{border-color:rgba(248,113,113,0.5) !important;box-shadow:0 0 0 3px rgba(248,113,113,0.1) !important}
/* SUBMIT */
.submit-section{padding:1.5rem 2rem 2rem;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
.submit-summary{display:flex;align-items:center;gap:0.6rem}
.summary-badge{font-family:var(--font-mono);font-size:0.78rem;font-weight:700;padding:0.45rem 1rem;border-radius:10px;display:flex;align-items:center;gap:0.4rem}
.summary-badge.recipients{color:var(--neon-green);background:rgba(52,211,153,0.1);border:1px solid rgba(52,211,153,0.25)}
.summary-badge.recipients .num{font-size:1rem}
.nex-submit{display:inline-flex;align-items:center;gap:0.7rem;padding:0.9rem 2.5rem;border-radius:14px;font-family:var(--font-mono);font-size:0.85rem;font-weight:700;color:#fff;background:linear-gradient(135deg,var(--neon-green),var(--neon-teal));border:none;cursor:pointer;transition:all 0.3s;box-shadow:0 8px 24px rgba(52,211,153,0.3);text-transform:uppercase;letter-spacing:0.06em;position:relative;overflow:hidden}
.nex-submit::before{content:'';position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,0.2),transparent);transition:left 0.5s}
.nex-submit:hover::before{left:100%}
.nex-submit:hover{transform:translateY(-3px);box-shadow:0 12px 32px rgba(52,211,153,0.4)}
.nex-submit:active{transform:translateY(-1px)}
.nex-submit:disabled{background:var(--dark-600);box-shadow:none;cursor:not-allowed;transform:none}
.nex-submit:disabled::before{display:none}
.nex-submit i{font-size:1.1rem}
/* RESPONSIVE */
@@media(max-width:992px){
.picker-layout{flex-direction:column}
.picker-panel{height:260px}
.picker-divider{width:auto;height:1px}
.two-col-row{flex-direction:column}
.two-col-row .col-right{width:100%}
}
@@media(max-width:768px){
.send-content{padding:1rem}
.send-hero-inner{padding:1.2rem;flex-direction:column;align-items:flex-start}
.send-hero-pills{margin-left:0}
.nex-form-group{padding:1.2rem 1.3rem}
.two-col-row{padding:1.2rem 1.3rem}
.submit-section{flex-direction:column;padding:1.2rem 1.3rem 1.5rem}
.nex-submit{width:100%;justify-content:center}
.nav-container{flex-wrap:wrap}
.breadcrumb-nex{order:3;flex-basis:100%}
}
@@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important}}
@@media print{.bg-pattern,.top-nav{display:none !important}.send-form-card,.send-hero{background:white;color:black;border:1px solid #ccc;backdrop-filter:none}}
</style>
}
@{
var users = ViewBag.Users as IEnumerable<dynamic> ?? Enumerable.Empty<dynamic>();
}
<div class="nexgen-send">
<div class="bg-pattern"><div class="grid-overlay"></div><div class="gradient-mesh"></div></div>
<nav class="top-nav">
<div class="nav-container">
<div class="nav-brand">
<div class="brand-icon"><i class="bi bi-send-fill"></i></div>
<div class="brand-text"><span class="brand-name">DISTRIBUTE</span><span class="brand-version">v1.0</span></div>
</div>
<nav class="breadcrumb-nex">
<a asp-controller="Questionnaire" asp-action="Index" class="crumb"><i class="bi bi-collection"></i> SURVEYS</a>
<i class="bi bi-chevron-right sep"></i>
<span class="crumb active">SEND QUESTIONNAIRE</span>
</nav>
<a asp-controller="Questionnaire" asp-action="Index" class="nav-btn"><i class="bi bi-arrow-left"></i> Back</a>
</div>
</nav>
<div class="send-content">
@if (TempData["Success"] != null)
{ <div class="nex-alert nex-alert-success"><i class="bi bi-check-circle-fill"></i> @TempData["Success"]<button class="btn-close-nex" onclick="this.parentElement.remove()">×</button></div> }
@if (TempData["Error"] != null)
{ <div class="nex-alert nex-alert-error"><i class="bi bi-exclamation-triangle-fill"></i> @TempData["Error"]<button class="btn-close-nex" onclick="this.parentElement.remove()">×</button></div> }
<!-- Hero -->
<div class="send-hero">
<div class="send-hero-inner">
<div class="send-hero-icon"><i class="bi bi-rocket-takeoff-fill"></i></div>
<div>
<h1 class="send-hero-title">Send Questionnaire</h1>
<div class="send-hero-sub">Select recipients and distribute your questionnaire</div>
</div>
<div class="send-hero-pills">
<span class="hero-pill survey"><i class="bi bi-file-text"></i> @ViewBag.questionnaireName</span>
</div>
</div>
</div>
<!-- Form -->
<div class="send-form-card">
<form asp-action="SendQuestionnaire" id="sendForm">
<div asp-validation-summary="ModelOnly" class="text-danger" style="margin:1rem 2rem 0;"></div>
<input type="hidden" asp-for="QuestionnaireId" />
<!-- Recipients: Side-by-Side Picker -->
<div class="nex-form-group">
<label class="nex-label">
<span class="nex-label-icon blue"><i class="bi bi-people-fill"></i></span>
Select Recipients
<span class="nex-label-sub" id="recipientCount">0 selected</span>
</label>
<div class="picker-layout">
<!-- LEFT: Available -->
<div class="picker-panel">
<div class="panel-head left">
<span class="panel-badge blue"><i class="bi bi-people"></i></span>
<span class="panel-title blue">Available Users</span>
<span class="panel-count" id="availCount">0</span>
</div>
<div class="panel-search">
<i class="bi bi-search"></i>
<input type="text" id="searchAvail" placeholder="Search by name or email..." />
</div>
<div class="panel-list" id="availList">
@foreach (var user in users)
{
string uName = user.UserName ?? "Unknown";
string uEmail = user.UserEmail ?? "";
string initial = !string.IsNullOrEmpty(uName) ? uName.Substring(0, 1).ToUpper() : "U";
<div class="user-row avail-user" data-email="@uEmail" data-name="@uName">
<div class="user-row-avatar av-blue">@initial</div>
<div class="user-row-info">
<div class="user-row-name">@uName</div>
<div class="user-row-email">@uEmail</div>
</div>
<div class="user-row-action add"><i class="bi bi-plus"></i></div>
</div>
}
</div>
<div class="panel-actions">
<button type="button" class="panel-action-btn select-all" id="selectAllBtn"><i class="bi bi-check-all"></i> Select All</button>
</div>
</div>
<div class="picker-divider"></div>
<!-- RIGHT: Selected -->
<div class="picker-panel">
<div class="panel-head right">
<span class="panel-badge green"><i class="bi bi-check-lg"></i></span>
<span class="panel-title green">Selected Recipients</span>
<span class="panel-count" id="selCount">0</span>
</div>
<div class="panel-list" id="selectedList">
<div class="panel-empty" id="emptySelected">
<div class="panel-empty-icon"><i class="bi bi-inbox"></i></div>
<div class="panel-empty-text">Click users from the left to add them as recipients</div>
</div>
</div>
<div class="panel-actions">
<button type="button" class="panel-action-btn deselect-all" id="deselectAllBtn"><i class="bi bi-x-lg"></i> Remove All</button>
</div>
</div>
</div>
</div>
<!-- Two-Column: Textarea + Expiration side-by-side -->
<div class="two-col-row">
<!-- Left: Manual Emails -->
<div class="col-left">
<label class="nex-label">
<span class="nex-label-icon purple"><i class="bi bi-envelope-fill"></i></span>
Manual Email Entry
</label>
<textarea asp-for="Emails" class="nex-textarea" id="emailTextarea"
placeholder="Type or paste additional emails, separated by commas&#10;Example: john@@company.com, jane@@org.com"
rows="4"></textarea>
<span asp-validation-for="Emails" class="field-validation-error"></span>
<div class="nex-helper">
<i class="bi bi-info-circle-fill"></i>
<span>Both the user picker and this field combine. Duplicates are handled automatically.</span>
</div>
</div>
<!-- Right: Expiration -->
<div class="col-right">
<label class="nex-label">
<span class="nex-label-icon yellow"><i class="bi bi-calendar-event-fill"></i></span>
Survey Expiration
</label>
<input asp-for="ExpirationDateTime" class="nex-input nex-datetime" type="datetime-local"
min="@DateTime.Now.ToString("yyyy-MM-ddTHH:mm")" />
<span asp-validation-for="ExpirationDateTime" class="field-validation-error"></span>
<div class="nex-helper">
<i class="bi bi-clock"></i>
<span>Links expire on this date. Defaults to 30 days if left empty.</span>
</div>
</div>
</div>
<!-- Submit -->
<div class="submit-section">
<div class="submit-summary">
<div class="summary-badge recipients">
<i class="bi bi-people-fill"></i>
<span class="num" id="totalRecipients">0</span> recipients
</div>
</div>
<button type="submit" class="nex-submit" id="submitBtn">
<i class="bi bi-send-fill"></i>
<span>Send Questionnaire</span>
</button>
</div>
</form>
</div>
</div>
</div>
@section Scripts {
<script>
document.addEventListener('DOMContentLoaded', function () {
const availList = document.getElementById('availList');
const selectedList = document.getElementById('selectedList');
const emptySelected = document.getElementById('emptySelected');
const searchAvail = document.getElementById('searchAvail');
const emailTextarea = document.getElementById('emailTextarea');
const recipientCount = document.getElementById('recipientCount');
const availCount = document.getElementById('availCount');
const selCount = document.getElementById('selCount');
const totalRecipients = document.getElementById('totalRecipients');
const form = document.getElementById('sendForm');
const submitBtn = document.getElementById('submitBtn');
const expirationInput = document.querySelector('input[name="ExpirationDateTime"]');
let selectedEmails = new Set();
updateAvailCount();
// Search
searchAvail.addEventListener('input', function () {
const q = this.value.toLowerCase().trim();
availList.querySelectorAll('.avail-user').forEach(row => {
const match = !q || row.dataset.name.toLowerCase().includes(q) || row.dataset.email.toLowerCase().includes(q);
row.style.display = match ? '' : 'none';
});
});
// Click to add
availList.addEventListener('click', function (e) {
const row = e.target.closest('.avail-user');
if (!row || row.classList.contains('in-selected')) return;
addUser(row.dataset.email, row.dataset.name);
});
// Select All
document.getElementById('selectAllBtn').addEventListener('click', function () {
availList.querySelectorAll('.avail-user').forEach(row => {
if (!row.classList.contains('in-selected') && row.style.display !== 'none') {
addUser(row.dataset.email, row.dataset.name, true);
}
});
syncAll();
});
// Deselect All
document.getElementById('deselectAllBtn').addEventListener('click', function () {
selectedEmails.clear();
selectedList.querySelectorAll('.user-row').forEach(r => r.remove());
availList.querySelectorAll('.avail-user').forEach(r => r.classList.remove('in-selected'));
syncAll();
});
function addUser(email, name, batch) {
const key = email.toLowerCase().trim();
if (!key || selectedEmails.has(key)) return;
selectedEmails.add(key);
const availRow = availList.querySelector('.avail-user[data-email="' + email + '"]');
if (availRow) availRow.classList.add('in-selected');
const initial = name ? name.charAt(0).toUpperCase() : 'U';
const row = document.createElement('div');
row.className = 'user-row';
row.dataset.email = key;
row.style.animation = 'slideIn 0.2s ease both';
row.innerHTML = '<div class="user-row-avatar av-green">' + initial + '</div>' +
'<div class="user-row-info"><div class="user-row-name">' + (name || key) + '</div>' +
'<div class="user-row-email">' + key + '</div></div>' +
'<div class="user-row-action remove"><i class="bi bi-x-lg"></i></div>';
row.addEventListener('click', function () { removeUser(key, row); });
selectedList.appendChild(row);
if (!batch) syncAll();
}
function removeUser(key, row) {
selectedEmails.delete(key);
row.remove();
availList.querySelectorAll('.avail-user').forEach(r => {
if (r.dataset.email.toLowerCase() === key) r.classList.remove('in-selected');
});
syncAll();
}
function syncAll() {
const manualEmails = emailTextarea.value.split(/[,;\n]+/).map(e => e.trim().toLowerCase()).filter(e => e && e.includes('@@'));
const allEmails = new Set([...selectedEmails, ...manualEmails]);
emailTextarea.value = Array.from(allEmails).join(', ');
const count = allEmails.size;
recipientCount.textContent = count + ' selected';
selCount.textContent = selectedEmails.size;
totalRecipients.textContent = count;
updateAvailCount();
emptySelected.style.display = selectedEmails.size > 0 ? 'none' : '';
}
function updateAvailCount() {
availCount.textContent = availList.querySelectorAll('.avail-user:not(.in-selected)').length;
}
emailTextarea.addEventListener('input', function () {
const manualEmails = this.value.split(/[,;\n]+/).map(e => e.trim().toLowerCase()).filter(e => e && e.includes('@@'));
const allEmails = new Set([...selectedEmails, ...manualEmails]);
recipientCount.textContent = allEmails.size + ' selected';
totalRecipients.textContent = allEmails.size;
});
// Default expiration
if (expirationInput && !expirationInput.value) {
const d = new Date(); d.setDate(d.getDate() + 30); d.setHours(9, 0, 0, 0);
expirationInput.value = d.toISOString().slice(0, 16);
}
// Submit loading
if (form && submitBtn) {
form.addEventListener('submit', function () {
syncAll();
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="bi bi-hourglass-split"></i><span>Sending...</span>';
});
}
// Auto-dismiss alerts
document.querySelectorAll('.nex-alert').forEach(a => {
setTimeout(() => { a.style.opacity = '0'; a.style.transform = 'translateY(-20px)'; setTimeout(() => a.remove(), 300); }, 6000);
});
});
</script>
}