PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC]);
$db->exec("SET search_path TO admin,public");
$SN=['Register','Verify Email','DNS Setup','Domain Verify','Exchange Config','AntiSpam','Warmup','Live'];
// === ACTIONS ===
$msg='';
if($_SERVER['REQUEST_METHOD']==='POST'){
$aid=(int)($_POST['aid']??0);
$act=$_POST['act']??'';
if($act==='advance'&&$aid){
$cur=(int)$db->query("SELECT current_step FROM office_accounts WHERE id=$aid")->fetchColumn();
if($cur<7){$db->exec("UPDATE office_accounts SET current_step=".($cur+1)." WHERE id=$aid");$msg="✅ Account #$aid → Step ".($cur+1).": ".$SN[$cur+1];}
}
if($act==='retreat'&&$aid){
$cur=(int)$db->query("SELECT current_step FROM office_accounts WHERE id=$aid")->fetchColumn();
if($cur>0){$db->exec("UPDATE office_accounts SET current_step=".($cur-1)." WHERE id=$aid");$msg="⬅ Account #$aid → Step ".($cur-1).": ".$SN[$cur-1];}
}
if($act==='setstatus'&&$aid){
$ns=preg_replace('/[^a-z]/','',strtolower($_POST['ns']??''));
if(in_array($ns,['active','warming','suspended','blocked','pending'])){
$db->exec("UPDATE office_accounts SET status='$ns' WHERE id=$aid");$msg="✅ Account #$aid → Status: $ns";
}
}
if($act==='batch_advance'){
$bs=(int)($_POST['bs']??-1);
if($bs>=0&&$bs<7){
$n=$db->exec("UPDATE office_accounts SET current_step=".($bs+1)." WHERE current_step=$bs AND LOWER(status) IN ('active','warming','pending')");
$msg="✅ Batch: $n comptes Step $bs → Step ".($bs+1);
}
}
if($act==='exchange_on'&&$aid){$db->exec("UPDATE office_accounts SET exchange_configured=true WHERE id=$aid");$msg="✅ Exchange activé #$aid";}
if($act==='exchange_off'&&$aid){$db->exec("UPDATE office_accounts SET exchange_configured=false WHERE id=$aid");$msg="❌ Exchange désactivé #$aid";}
// OPUS3-OFFICE-FACTORY -- Factory Create + Recovery + Batch Recovery
if($act==='factory_create'){
$t=$_POST['tenant']??'';$c=(int)($_POST['count']??1);$p=$_POST['prefix']??'user';
if($t&&$c>0&&$c<=50){
$cr=0;
for($i=0;$i<$c;$i++){
$nm=$p.rand(100,999);
$db->exec("INSERT INTO office_accounts (name,tenant_domain,status,current_step,domains_count,has_license,exchange_configured) VALUES ('$nm','$t','pending',0,0,false,false)");
$cr++;
}
$msg="Factory: $cr comptes crees sur $t";
} else $msg="Factory: tenant requis, count 1-50";
}
if($act==='recovery'&&$aid){
$db->exec("UPDATE office_accounts SET status='active',current_step=0 WHERE id=$aid");
$msg="Recovery #$aid: reset Active Step 0";
}
if($act==='batch_recovery'){
$t=$_POST['tenant']??'';
if($t){
$n2=$db->exec("UPDATE office_accounts SET status='active',current_step=0 WHERE tenant_domain='$t' AND LOWER(status) IN ('suspended','blocked')");
$msg="Batch Recovery: $n2 comptes reactives sur $t";
}
}
if($act==='add_graph_tenant'){
$tn=$_POST['tname']??'';$td=$_POST['tid']??'';$ci=$_POST['cid']??'';$cs2=$_POST['cs']??'';
if($tn&&$td&&$ci&&$cs2){
$db->exec("INSERT INTO graph_tenants (tenant_domain,tenant_id,client_id,client_secret,status,daily_limit) VALUES ('$tn','$td','$ci','$cs2','active',10000) ON CONFLICT DO NOTHING");
$msg="Tenant $tn ajoute";
}
}
if(!isset($_GET['account_id']))header("Location:office-workflow.php".($msg?"?msg=".urlencode($msg):""));
}
if(!$msg)$msg=$_GET['msg']??'';
// === DRILL-DOWN MODE ===
$drill=(int)($_GET['account_id']??0);
if($drill){
$acc=$db->query("SELECT * FROM office_accounts WHERE id=$drill")->fetch();
if(!$acc){echo"
Account #$drill not found
";exit;}
$doms=$db->query("SELECT * FROM office_domains WHERE account_id=$drill ORDER BY domain_name")->fetchAll();
$tenant=$db->query("SELECT * FROM graph_tenants WHERE tenant_domain='".$acc['tenant_domain']."' LIMIT 1")->fetch();
?>
Account #=$drill?> — Office Workflow
query("SELECT COUNT(*) FROM office_accounts")->fetchColumn();
$_active=(int)$db->query("SELECT COUNT(*) FROM office_accounts WHERE LOWER(status)='active'")->fetchColumn();
$_warming=(int)$db->query("SELECT COUNT(*) FROM office_accounts WHERE LOWER(status)='warming'")->fetchColumn();
$_suspended=(int)$db->query("SELECT COUNT(*) FROM office_accounts WHERE LOWER(status) IN ('suspended','blocked')")->fetchColumn();
$_domains=(int)$db->query("SELECT COUNT(*) FROM office_domains")->fetchColumn();
$_dom_v=(int)$db->query("SELECT COUNT(*) FROM office_domains WHERE verification_status='Verified'")->fetchColumn();
$_tenants=(int)$db->query("SELECT COUNT(*) FROM graph_tenants")->fetchColumn();
// OPUS3-ALL-TENANTS
$_all_tenants=(int)$db->query("SELECT COUNT(DISTINCT tenant_domain) FROM office_accounts WHERE tenant_domain IS NOT NULL AND tenant_domain != '' ")->fetchColumn();
$_tenant_list=$db->query("SELECT tenant_domain, COUNT(*) as accounts, SUM(domains_count) as total_domains, MAX(domains_count) as max_dom FROM office_accounts WHERE tenant_domain IS NOT NULL AND tenant_domain != '' GROUP BY tenant_domain ORDER BY accounts DESC LIMIT 100")->fetchAll();
$_sends=(int)$db->query("SELECT COUNT(*) FROM graph_send_log")->fetchColumn();
$_exchange=(int)$db->query("SELECT COUNT(*) FROM office_accounts WHERE exchange_configured=true")->fetchColumn();
$_by_step=$db->query("SELECT current_step,COUNT(*) as cnt FROM office_accounts GROUP BY current_step ORDER BY current_step")->fetchAll();
// Filters + pagination
$pg=max(1,(int)($_GET['p']??1));$pp=50;$off=($pg-1)*$pp;
$q=$_GET['q']??'';$sf=$_GET['sf']??'';$stf=$_GET['stf']??'';
$w=[];$prm=[];
if($q){$w[]="(name ILIKE :q OR tenant_domain ILIKE :q)";$prm[':q']="%$q%";}
if($sf!==''){$w[]="current_step=:sf";$prm[':sf']=(int)$sf;}
if($stf){$w[]="LOWER(status)=:stf";$prm[':stf']=strtolower($stf);}
$wh=$w?"WHERE ".implode(" AND ",$w):"";
$st=$db->prepare("SELECT id,name,tenant_domain,LOWER(status) as status,current_step,exchange_configured,domains_count,has_license FROM office_accounts $wh ORDER BY id DESC LIMIT $pp OFFSET $off");
$st->execute($prm);$accs=$st->fetchAll();
$st2=$db->prepare("SELECT COUNT(*) FROM office_accounts $wh");$st2->execute($prm);
$filt=(int)$st2->fetchColumn();$pages=max(1,ceil($filt/$pp));
$_top_tenants=$db->query("SELECT tenant_domain,status,users_count,sends_today,daily_limit FROM graph_tenants ORDER BY users_count DESC LIMIT 12")->fetchAll();
$_dom_list=$db->query("SELECT d.domain_name,d.verification_status,a.name as account_name FROM office_domains d LEFT JOIN office_accounts a ON d.account_id=a.id ORDER BY d.verification_status,d.domain_name LIMIT 50")->fetchAll();
?>
WEVADS — Office Workflow v2
=htmlspecialchars($msg)?>
🏢 Office Workflow v2
O365 accounts • Graph API tenants • Domains verification • Exchange pipeline — Click account ID to drill-down
=number_format($_total)?>
Office Accounts
=$_active?>
Active
=$_warming?>
Warming
=$_suspended?>
Susp/Blocked
=$_dom_v?>/=$_domains?>
Domains Verified
=$_all_tenants?>
Tenants (Office)
=number_format($_sends)?>
Graph Sends
📊 Account Pipeline — 8 Steps ( click Batch to advance all accounts at a step )
$sn):$cnt=0;foreach($_by_step as $s)if((int)$s['current_step']==$i)$cnt=(int)$s['cnt'];$clr=$i<3?'var(--am)':($i<6?'var(--cy)':'var(--gn)');?>
query("SELECT LOWER(status) as status,COUNT(*) as cnt FROM office_accounts GROUP BY LOWER(status) ORDER BY cnt DESC")->fetchAll();
foreach($by_st as $s):$pct=$_total>0?round($s['cnt']/$_total*100,1):0;$nc=$s['status']==='active'?'nt-g':($s['status']==='warming'?'nt-a':'nt-r');?>