
<style>

:root{
--chair1:#0f172a;
--chair2:#1d4ed8;
--ok:#10b981;
--err:#ef4444;
}

body{
background:#eef3f8!important;
}

.card,
.table,
form,
.container-fluid>div{
border-radius:18px!important;
}

.kpi-card{
background:
linear-gradient(
135deg,
#fff,
#f8fbff
);
box-shadow:
0 18px 50px rgba(15,23,42,.08);
transition:.25s;
}

.kpi-card:hover{
transform:
translateY(-3px);
}

.assign-btn{
background:
linear-gradient(
135deg,
#059669,
#10b981
)!important;

border:none!important;

font-weight:700;
}

.assign-btn:hover{
transform:translateY(-2px);
}

select,
input{
border-radius:14px!important;
}

.ec-toast-wrap{

position:fixed;

top:24px;

right:24px;

z-index:999999;

display:flex;

flex-direction:column;

gap:10px;

}

.ec-toast{

min-width:360px;

padding:18px;

color:#fff;

border-radius:16px;

box-shadow:
0 25px 60px rgba(0,0,0,.22);

animation:
slide .35s ease;

}

.success{
background:
linear-gradient(
135deg,
#059669,
#10b981
);
}

.error{
background:
linear-gradient(
135deg,
#b91c1c,
#ef4444
);
}

.info{
background:
linear-gradient(
135deg,
#2563eb,
#60a5fa
);
}

@keyframes slide{

from{
opacity:0;
transform:
translateX(100px);
}

to{
opacity:1;
transform:none;
}

}

.group-table tr:hover{
background:#f8fbff;
}

</style>

<div id="toastStack" class="ec-toast-wrap"></div>

<script>

window.ecToast=
function(
msg,
type='success'
){

let t=
document.createElement(
'div'
);

t.className=
'ec-toast '+type;

t.innerHTML=
msg;

document
.getElementById(
'toastStack'
)
.appendChild(
t
);

setTimeout(
()=>t.remove(),
4500
);

};

document
.addEventListener(
'DOMContentLoaded',
function(){

<?php if(!empty($_SESSION['toast']['msg'])): ?>

ecToast(
<?= json_encode($_SESSION['toast']['msg']) ?>,
<?= json_encode($_SESSION['toast']['type'] ?? 'success') ?>
);

<?php unset($_SESSION['toast']); endif; ?>

let btn=
document
.querySelector(
'button[type=submit]'
);

if(btn){

btn.classList
.add(
'assign-btn'
);

btn.onclick=
function(){

btn.disabled=true;

btn.innerHTML=
'Assigning…';

};

}

});

</script>


<?php

$subs=
is_array($submissions??null)
?
$submissions
:
[];

$revs=
is_array($reviewers??null)
?
$reviewers
:
[];

$rows=
is_array($rows??null)
?$rows
:(is_array($assignments??null)
?$assignments
:[]);

$group=[];

foreach(($rows??[]) as $r){

$key=
$r['title']
??
$r['paper_title']
??
$r['title_en']
??
$r['title_ar']
??
('Submission #'.($r['submission_id']??0));

$group[$key][]=
$r['email']
??'Reviewer';

}

?>




<div class="container-fluid">

<?php if(!empty($_SESSION['toast'])): ?>

<div
class="alert alert-success"
style="
position:fixed;
right:25px;
top:20px;
z-index:999999;
"
>

<?=htmlspecialchars(
$_SESSION['toast']['msg']
)?>

</div>

<script>
setTimeout(
()=>{
document
.querySelector('.alert')
?.remove()
},
3500
)
</script>

<?php unset($_SESSION['toast']); ?>

<?php endif; ?>


<div class="row mb-3">

<div class="col">




<details class="card p-3 mb-4">
<summary style="cursor:pointer;font-weight:bold">
🤖 Smart Recommendations
</summary>


<h4>

🤖 Smart Recommendations

</h4>

<?php

try{

$db=
\App\Core\Database::getInstance();

$rowsRec=
$db->query("

SELECT

rrs.*,

u.email

FROM reviewer_reassignment_suggestions rrs

JOIN users u
ON u.id=rrs.suggested_reviewer_id

WHERE rrs.status='pending'

ORDER BY rrs.score DESC

LIMIT 20

")->fetchAll(\PDO::FETCH_ASSOC);

foreach($rowsRec as $r):

?>

<div
class="alert alert-warning"
>

Suggested Reviewer:

<b>
<?=htmlspecialchars(
$r['email']
)?>
</b>

Score:

<?=number_format(
(float)$r['score'],
2
)?>

<br><br>

<a
class="btn btn-success btn-sm"
href="/easychair/admin/reviewer-assignments/approve-suggestion?id=<?= (int)$r['id'] ?>"
>
Assign Suggested Reviewer
</a>

<a
class="btn btn-outline-danger btn-sm"
href="/easychair/admin/reviewer-assignments/reject-suggestion?id=<?= (int)$r['id'] ?>"
>
Dismiss
</a>

</div>

<?php

endforeach;

}catch(Throwable $e){

echo '<div class="alert alert-danger">';
echo 'PACK842 PANEL ERROR: '
    . htmlspecialchars($e->getMessage());
echo '</div>';

}

?>

</div>


<div class="card p-3">

Submissions

<h2>

<?=count($subs)?>

</h2>

</div>

</details>

<div class="col">

<div class="card p-3">

Reviewers

<h2>

<?=count($revs)?>

</h2>

</div>

</div>

<div class="col">

<div class="card p-3">

Assignments

<h2>

<?=count($rows??[])?>

</h2>

</div>

</div>

</div>

<div class="row mb-4">

<div class="col-md-3">
<div class="card p-3 border-success">
<h6>Pending Reviews</h6>
<h2><?= (int)($pendingReviews ?? 0) ?></h2>
</div>
</div>

<div class="col-md-3">
<div class="card p-3 border-warning">
<h6>Overdue (14+ Days)</h6>
<h2><?= (int)($overdueReviews ?? 0) ?></h2>
</div>
</div>

<div class="col-md-3">
<div class="card p-3 border-danger">
<h6>Critical (21+ Days)</h6>
<h2><?= (int)($criticalReviews ?? 0) ?></h2>
</div>
</div>

<div class="col-md-3">
<div class="card p-3 border-primary">
<h6>Reminders Sent</h6>
<h2><?= (int)($remindersSent ?? 0) ?></h2>
</div>
</div>

<div class="col-md-3">
<div class="card p-3 border-danger">
<h6>Overloaded Reviewers</h6>
<h2><?= (int)($overloadedReviewers ?? 0) ?></h2>
</div>
</div>

</div>

<div class="d-flex gap-2 mb-3">

<a
class="btn btn-success"
href="/easychair/admin/conference-home/reviewers/upload"
>

Bulk CSV

</a>

<a
class="btn btn-primary"
href="/easychair/admin/conference-home"
>

Reviewer Import

</a>

<input
id="globalSearch"
class="form-control"
placeholder="Search"
/>

</div>



<div class="card p-3 mb-4 border-primary">

<h3 class="mb-1">
🎯 Assignment Command Center
</h3>

<div class="text-muted">
AI Powered Reviewer Selection
</div>

</div>

<form

method="post"
action="/easychair/admin/reviewer-assignments/assign"
class="row mb-4"
>

<?php
if(function_exists('csrf_field'))
echo csrf_field();
?>

<div class="col-md-2">

<button
class="btn btn-success w-100"
>

Assign

</button>

<a
href="#"
id="autoAssignBtn"
class="btn btn-warning w-100 mt-2"
>
🤖 Auto Assign Top 3
</a>

</div>

<div class="col-md-2">

<input
name="round_no"
value="1"
class="form-control"
>

</div>

<div class="col-md-4">

<select
name="reviewer_id"
class="form-select"
required
>

<option value="">

Search reviewer

</option>

<?php foreach(($revs??[]) as $r): ?>

<option
value="<?=$r['id']?>"

data-expertise="<?= (int)($r['expertise_score'] ?? 0) ?>"

data-active="<?= (int)($r['active_assignments'] ?? 0) ?>"

data-final-score="<?= (float)($r['final_score'] ?? 0) ?>"

data-completion="<?= (float)($r['completion_rate'] ?? 0) ?>"

data-acceptance="<?= (float)($r['acceptance_rate'] ?? 0) ?>"

data-quality="<?= (float)($r['quality_score'] ?? 0) ?>"

data-track-exp="<?= (int)($r['track_experience'] ?? 0) ?>"

data-capacity="<?= (int)($r['capacity_percent'] ?? 0) ?>"
>

<?=htmlspecialchars(
trim(
($r['first_name']??'')
.' '.
($r['last_name']??'')
)
)?>

—

<?=htmlspecialchars(
$r['email']
)?>

<?php if(!empty($r['expertise'])): ?>

    —
    <?=htmlspecialchars(
        $r['expertise']
    )?>

<?php endif; ?>

<?php if(isset($r['active_assignments'])): ?>

    —
    Active:
    <?= (int)$r['active_assignments'] ?>

    /

    <?= (int)($r['capacity_limit'] ?? 5) ?>

    —

    Capacity:

    <?= (int)($r['capacity_percent'] ?? 0) ?>%

<?php endif; ?>

<?php if(isset($r['completion_rate'])): ?>

    —
    Completion:
    <?= (int)$r['completion_rate'] ?>%

<?php endif; ?>

<?php if(isset($r['track_experience'])): ?>

    —
    TrackExp:
    <?= (int)$r['track_experience'] ?>

<?php endif; ?>

<?php if(isset($r['review_quality'])): ?>

    —
    Quality:
    <?= number_format(
        (float)$r['review_quality'],
        2
    ) ?>

<?php endif; ?>

</option>

<?php endforeach; ?>

</select>

</div>

<div class="col-md-4">

<select
name="submission_id"
class="form-select"
required
>

<option>

Search submission

</option>

<?php foreach(($subs??[]) as $s): ?>

<option
value="<?=$s['id']?>"
data-track="<?= htmlspecialchars($s['track_name'] ?? '') ?>"
>

<?=htmlspecialchars(
$s['title']
??
$s['title_en']
??
$s['title_ar']
??
('Submission #'.$s['id'])
)?>

<?php if(!empty($s['track_name'])): ?>

[<?= htmlspecialchars($s['track_name']) ?>]

<?php endif; ?>

</option>

<?php endforeach; ?>

</select>

</div>

</form>





</div>

<div class="card p-3 mb-4">

<h4>

Assignments

</h4>

<table
class="table group-table"
id="assignTable"
>

<tr>

<th>#</th>

<th>Submission</th>

<th>Reviewer</th>

<th>Age</th>

<th>Status</th>

<th width="220">
Actions
</th>

</tr>

<?php foreach(($rows??[]) as $r): ?>

<tr>

<td>

#<?=$r['id']?>

</td>

<td>

<?=htmlspecialchars(
$r['title']
??
('Submission #'.$r['submission_id'])
)?>

</td>

<td>

<?=htmlspecialchars(
$r['email']
)?>

</td>

<td>

<?php
$age=(int)($r['age_days'] ?? 0);

$cls='bg-success';

if($age>=21){
    $cls='bg-danger';
}
elseif($age>=14){
    $cls='bg-warning';
}
elseif($age>=7){
    $cls='bg-info';
}
?>

<span class="badge <?= $cls ?>">
<?= $age ?> d
</span>

</td>

<td>

<span class="badge bg-primary">

<?=htmlspecialchars(
$r['status']
)?>

</span>

</td>

<td>

<a
href="/easychair/admin/reviewer-assignments/unassign?id=<?= (int)$r['id'] ?>"
class="btn btn-outline-danger btn-sm"
title="
Reviewer:
<?= htmlspecialchars(
trim(
($r['first_name'] ?? '')
.' '.
($r['last_name'] ?? '')
)
) ?>

Email:
<?= htmlspecialchars(
$r['email'] ?? ''
) ?>

Round:
<?= (int)($r['round_no'] ?? 1) ?>
"
onclick="
return confirm(
'Remove reviewer assignment?\n\nReviewer: <?= addslashes($r['email'] ?? '') ?>'
);
"
>

<i class="fa fa-user-minus"></i>

Unassign

</a>

<a
href="/easychair/admin/reviewer-assignments/reminder?id=<?= (int)$r['id'] ?>"
class="btn btn-outline-warning btn-sm ms-1"
onclick="
return confirm(
'Send review reminder to reviewer?'
);
"
>

🔔 Reminder

</a>

</td>

</tr>

<?php endforeach; ?>

</table>

</div>

<div
id="reviewerBalancePanel"
class="card p-3 mb-4 border-warning"
>

<h4>

⚖ Reviewer Workload Balance

</h4>

<div
id="reviewerBalanceContent"
class="small"
>

Loading recommendations...

<hr>

<div class="small text-muted">

Green  = Available<br>
Orange = Busy<br>
Red    = Full Capacity

</div>

</div>



<div class="card p-3">

<h4>

Submission → Assigned Reviewers

</h4>

<table class="table group-table">

<tr>

<th>
Submission
</th>

<th>
Reviewers
</th>

<th>
Total
</th>

</tr>

<?php
if(empty($group)&&!empty($rows)){
foreach($rows as $r){
$t=$r['title']??('Submission #'.($r['submission_id']??0));
$group[$t][]=$r['email']??'Reviewer';
}
}
foreach(($group??[]) as $k=>$v):
?>

<tr>

<td>

<?=htmlspecialchars(
$k
)?>

</td>

<td>

<?=htmlspecialchars(
implode(
' • ',
array_unique($v)
)
)?>

</td>

<td>

<?=count(
array_unique($v)
)?>

</td>

</tr>

<?php endforeach; ?>

</table>

<div
id="assignSummary"
class="small text-muted mt-2"
></div>

<div
id="assignPager"
class="d-flex justify-content-center mt-3"
></div>

</div>


<script>

const PAGE_SIZE = 15;

let currentPage = 1;

function renderAssignments(){

    const search =
    (
        document
        .getElementById('globalSearch')
        ?.value
        ||
        ''
    ).toLowerCase();

    const allRows =
    Array.from(
        document.querySelectorAll(
            '#assignTable tr'
        )
    );

    if(allRows.length<=1){
        return;
    }

    const header =
    allRows[0];

    const rows =
    allRows.slice(1);

    const filtered =
    rows.filter(
        r =>
        r.innerText
        .toLowerCase()
        .includes(search)
    );

    rows.forEach(
        r=>r.style.display='none'
    );

    const total =
    filtered.length;

    const pages =
    Math.max(
        1,
        Math.ceil(
            total / PAGE_SIZE
        )
    );

    if(currentPage > pages){
        currentPage = pages;
    }

    const start =
    (currentPage-1)*PAGE_SIZE;

    const end =
    start + PAGE_SIZE;

    filtered
    .slice(start,end)
    .forEach(
        r=>r.style.display=''
    );

    let pager='';

    if(pages>1){

        pager +=
        '<button class="btn btn-sm btn-outline-secondary me-1" '+
        (currentPage===1?'disabled':'')+
        ' onclick="currentPage--;renderAssignments()">«</button>';

        for(
            let i=1;
            i<=pages;
            i++
        ){

            pager +=
            '<button class="btn btn-sm '+
            (
                i===currentPage
                ?
                'btn-primary'
                :
                'btn-outline-primary'
            )+
            ' me-1" onclick="currentPage='+i+';renderAssignments()">'+
            i+
            '</button>';
        }

        pager +=
        '<button class="btn btn-sm btn-outline-secondary" '+
        (currentPage===pages?'disabled':'')+
        ' onclick="currentPage++;renderAssignments()">»</button>';
    }

    document
    .getElementById(
        'assignPager'
    )
    .innerHTML =
    pager;

    const from =
    total
    ?
    start+1
    :
    0;

    const to =
    Math.min(
        end,
        total
    );

    document
    .getElementById(
        'assignSummary'
    )
    .innerHTML =
    'Showing '
    + from
    + ' - '
    + to
    + ' of '
    + total
    + ' assignments';
}

document
.getElementById(
'globalSearch'
)
?.addEventListener(
'keyup',
function(){

currentPage=1;

renderAssignments();

}
);

document.addEventListener(
'DOMContentLoaded',
renderAssignments
);

</script>

<link
href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css"
rel="stylesheet">

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

<script>

document.addEventListener(
'DOMContentLoaded',
function(){

if(
window.jQuery
&&
$.fn.select2
){

$('select[name="reviewer_id"]')
.select2({

width:'100%',

placeholder:
'Search reviewer',

allowClear:true

});

$('select[name="submission_id"]')
.select2({

width:'100%',

placeholder:
'Search submission',

allowClear:true

});

}

});

</script>

<script>

document.addEventListener(
'DOMContentLoaded',
function(){

    const submission =
        document.querySelector(
            'select[name="submission_id"]'
        );

    const reviewer =
        document.querySelector(
            'select[name="reviewer_id"]'
        );

    if(
        !submission
        ||
        !reviewer
    ){
        return;
    }

    function scoreReviewer(
        track,
        text
    ){

        track =
            (track || '')
            .toLowerCase();

        text =
            (text || '')
            .toLowerCase();

        let score = 0;

        if(
            track.includes('artificial intelligence')
            ||
            track.includes('ai')
        ){

            if(
                text.includes('ai')
                ||
                text.includes('artificial intelligence')
            ){
                score += 100;
            }

            if(
                text.includes('nlp')
            ){
                score += 30;
            }

        }

        if(
            track.includes('cyber')
            ||
            track.includes('security')
        ){

            if(
                text.includes('cyber')
                ||
                text.includes('security')
            ){
                score += 100;
            }

        }

        if(
            track.includes('software')
        ){

            if(
                text.includes('software')
            ){
                score += 100;
            }

        }

        if(
            track.includes('data')
        ){

            if(
                text.includes('data science')
            ){
                score += 100;
            }

        }

        if(
            track.includes('network')
        ){

            if(
                text.includes('network')
            ){
                score += 100;
            }

        }

        return score;
    }

    function rankReviewers(){

        const selected =
            submission.options[
                submission.selectedIndex
            ];

        const track =
            selected
            ? (
                selected.dataset.track
                || ''
            )
            : '';

        let options =
            Array.from(
                reviewer.options
            );

        if(options.length <= 1){
            return;
        }

        const first =
            options.shift();

        options.sort(
            function(a,b){

                const sa =
parseFloat(
a.dataset.intelligentScore || 0
);

const sb =
parseFloat(
b.dataset.intelligentScore || 0
);

return sb-sa;

            }
        );

        reviewer.innerHTML='';

        reviewer.appendChild(first);

        options.forEach(
            function(o){
                reviewer.appendChild(o);
            }
        );

        if(
            window.jQuery
            &&
            $.fn.select2
        ){

            $(reviewer)
            .trigger(
                'change.select2'
            );

        }

    }

    submission.addEventListener(
        'change',
        rankReviewers
    );

    function autoSelectBestReviewer(){

        if(
            reviewer.options.length <= 1
        ){
            return;
        }

        reviewer.selectedIndex = 1;

        if(
            window.jQuery
            &&
            $.fn.select2
        ){

            $(reviewer)
            .trigger('change');

        }

    }

function renderReviewerBalance(){

    const panel =
    document.getElementById(
        'reviewerBalanceContent'
    );

    if(!panel){
        return;
    }

    let rows=[];

    Array.from(
        reviewer.options
    ).forEach(function(opt){

        if(!opt.value){
            return;
        }

        const active =
        parseInt(
            opt.dataset.active || 0
        );

        const score =

        (

        parseFloat(
            opt.dataset.finalScore || 0
        )

        +

        (
        parseInt(
            opt.dataset.trackExp || 0
        ) * 3
        )

        +

        (
        parseFloat(
            opt.dataset.completion || 0
        ) * 0.20
        )

        -

        (
        active * 5
        )

        -

        (
        parseInt(
            opt.dataset.capacity || 0
        ) * 0.10
        )

        );

        let state='🟢 Available';

let tier='';

if(score >= 85){
    tier='🥇 Elite';
}
else if(score >= 70){
    tier='🥈 Strong';
}
else if(score >= 40){
    tier='🥉 Eligible';
}


        if(active >= 11){
            state='🔴 Overloaded';
        }
        else if(active >= 6){
            state='🟠 Busy';
        }
        else if(active >= 3){
            state='🟡 Normal';
        }

        rows.push({
            text:opt.text,
            active:active,
            score:Math.round(score),
            state:state
        });

    });

    rows.sort(
        (a,b)=>b.score-a.score
    );

    let html =
    '<strong>Top Recommendations</strong><hr>';

    rows.slice(0,3).forEach(
        function(r,i){

            html +=
            '<div class="mb-2">'
            +
            '<strong>#'+(i+1)+'</strong> '
            +
            r.state
            +
            '<br>'
            +
            r.text
            +
            '<br>'
            +
            'Intelligent Score: '
            +
            r.score
            +
            ' | Active: '
            +
            r.active
            +
            ' | 🤖 AI Recommended'
            +
            '</div>';

        }
    );

    panel.innerHTML=html;

}

    rankReviewers();

    autoSelectBestReviewer();

    renderReviewerBalance();

    submission.addEventListener(
        'change',
        function(){

            rankReviewers();

            autoSelectBestReviewer();

            renderReviewerBalance();

        }
    );

});

</script>

<style>

.select2-container{

width:100%!important;

}

.select2-selection{

min-height:46px!important;

border-radius:14px!important;

padding-top:7px!important;

}

.select2-dropdown{

border-radius:14px!important;

overflow:hidden;

}

</style>



<script>

document.addEventListener(
'DOMContentLoaded',
function(){

const sub=
document.querySelector(
'select[name="submission_id"]'
);

const btn=
document.getElementById(
'autoAssignBtn'
);

if(btn && sub){

btn.addEventListener(
'click',
function(e){

e.preventDefault();

if(!sub.value){
alert('Select submission first');
return;
}

window.location=
'/easychair/admin/reviewer-assignments/auto-assign?submission_id='
+
sub.value;

}
);

}

}
);

</script>

