WIP: modifs sur Mac Mini
This commit is contained in:
parent
74aa268603
commit
ea06d4c982
10 changed files with 179 additions and 62 deletions
|
|
@ -45,18 +45,6 @@ export default function SimulateurPage() {
|
||||||
plafond_urssaf: data.plafond_urssaf,
|
plafond_urssaf: data.plafond_urssaf,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('📊 PostHog: Événement simulateur_calculation enrichi envoyé', {
|
|
||||||
categorie: data.categorie,
|
|
||||||
ccn: data.ccn_nom,
|
|
||||||
type_calcul: data.type_calcul,
|
|
||||||
montant_saisi: data.montant_saisi,
|
|
||||||
resultats: {
|
|
||||||
brut: data.resultat_brut,
|
|
||||||
net: data.resultat_net,
|
|
||||||
cost: data.resultat_cost
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
101
app/api/staff/contrats/[id]/employee-email/route.ts
Normal file
101
app/api/staff/contrats/[id]/employee-email/route.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
|
||||||
|
import { cookies } from 'next/headers';
|
||||||
|
|
||||||
|
// GET /api/staff/contrats/[id]/employee-email
|
||||||
|
// Récupère l'email du salarié pour un contrat
|
||||||
|
export async function GET(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { id: string } }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const contractId = params.id;
|
||||||
|
|
||||||
|
if (!contractId) {
|
||||||
|
return NextResponse.json({ error: 'Contract ID manquant' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérification de l'authentification
|
||||||
|
const sb = createRouteHandlerClient({ cookies });
|
||||||
|
const { data: { user } } = await sb.auth.getUser();
|
||||||
|
if (!user) {
|
||||||
|
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier staff pour lire la cible via cookie active_org_id
|
||||||
|
let isStaff = false;
|
||||||
|
try {
|
||||||
|
const { data } = await sb.from('staff_users').select('is_staff').eq('user_id', user.id).maybeSingle();
|
||||||
|
isStaff = !!data?.is_staff;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
let orgId: string | null = null;
|
||||||
|
try {
|
||||||
|
if (isStaff) {
|
||||||
|
const c = cookies();
|
||||||
|
orgId = c.get('active_org_id')?.value || null;
|
||||||
|
} else {
|
||||||
|
const { data, error } = await sb
|
||||||
|
.from('organization_members')
|
||||||
|
.select('org_id')
|
||||||
|
.eq('user_id', user.id)
|
||||||
|
.single();
|
||||||
|
if (error || !data?.org_id) {
|
||||||
|
return NextResponse.json({ error: 'Aucune organisation active' }, { status: 403 });
|
||||||
|
}
|
||||||
|
orgId = data.org_id;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Récupération des données du contrat depuis Supabase (cddu_contracts)
|
||||||
|
let query = sb
|
||||||
|
.from('cddu_contracts')
|
||||||
|
.select(`
|
||||||
|
id,
|
||||||
|
employee_matricule,
|
||||||
|
org_id
|
||||||
|
`)
|
||||||
|
.eq('id', contractId);
|
||||||
|
|
||||||
|
if (orgId) {
|
||||||
|
query = query.eq('org_id', orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: contract, error: contractError } = await query.single();
|
||||||
|
|
||||||
|
if (contractError || !contract) {
|
||||||
|
console.error('Erreur récupération contrat:', contractError);
|
||||||
|
return NextResponse.json({ error: 'Contrat non trouvé' }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer l'email du salarié depuis salaries.adresse_mail
|
||||||
|
let employee_email: string | null = null;
|
||||||
|
if (contract.employee_matricule) {
|
||||||
|
try {
|
||||||
|
let salQ = sb
|
||||||
|
.from('salaries')
|
||||||
|
.select('adresse_mail')
|
||||||
|
.or(`code_salarie.eq.${contract.employee_matricule},num_salarie.eq.${contract.employee_matricule}`)
|
||||||
|
.limit(1);
|
||||||
|
if (orgId) salQ = salQ.eq('employer_id', orgId);
|
||||||
|
const { data: salData, error: salErr } = await salQ;
|
||||||
|
if (!salErr && salData && salData[0]?.adresse_mail) {
|
||||||
|
employee_email = salData[0].adresse_mail as string;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Impossible de récupérer adresse_mail depuis salaries:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
employee_email: employee_email || null
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Erreur:', error);
|
||||||
|
return NextResponse.json({
|
||||||
|
error: 'Erreur lors de la récupération de l\'email',
|
||||||
|
message: error.message
|
||||||
|
}, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,7 +29,6 @@ export default function LogoutButton({ variant = "default", className }: { varia
|
||||||
|
|
||||||
// 2. Réinitialiser l'identité PostHog
|
// 2. Réinitialiser l'identité PostHog
|
||||||
posthog?.reset();
|
posthog?.reset();
|
||||||
console.log('👋 PostHog: Utilisateur déconnecté');
|
|
||||||
|
|
||||||
// 3. Nettoyage complet côté client (au cas où il resterait des données)
|
// 3. Nettoyage complet côté client (au cas où il resterait des données)
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ export function MaintenanceButton({ isStaff }: MaintenanceButtonProps) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setStatus(data);
|
setStatus(data);
|
||||||
} else {
|
} else {
|
||||||
console.warn("API maintenance not available yet, using default status");
|
|
||||||
// Fallback par défaut si l'API n'existe pas encore
|
// Fallback par défaut si l'API n'existe pas encore
|
||||||
setStatus({
|
setStatus({
|
||||||
is_maintenance_mode: false,
|
is_maintenance_mode: false,
|
||||||
|
|
@ -139,9 +138,6 @@ export function MaintenanceButton({ isStaff }: MaintenanceButtonProps) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug: afficher les valeurs pour déboguer
|
|
||||||
console.log('MaintenanceButton Debug:', { isStaff, status });
|
|
||||||
|
|
||||||
// Ne pas afficher le composant si ce n'est pas un staff
|
// Ne pas afficher le composant si ce n'est pas un staff
|
||||||
if (!isStaff) {
|
if (!isStaff) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,10 @@ export default function PostHogIdentifier() {
|
||||||
company_name: me.active_org_name,
|
company_name: me.active_org_name,
|
||||||
company_id: me.active_org_id,
|
company_id: me.active_org_id,
|
||||||
is_staff: me.is_staff || false,
|
is_staff: me.is_staff || false,
|
||||||
// Ajoutez d'autres propriétés utiles pour vos analytics
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('👤 PostHog: Utilisateur identifié:', me.user?.email || me.email, 'ID:', userId);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('❌ PostHog: Erreur lors de l\'identification:', e);
|
console.error('PostHog: Erreur lors de l\'identification:', e);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [posthog]);
|
}, [posthog]);
|
||||||
|
|
|
||||||
|
|
@ -11,28 +11,19 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
||||||
const key = process.env.NEXT_PUBLIC_POSTHOG_KEY;
|
const key = process.env.NEXT_PUBLIC_POSTHOG_KEY;
|
||||||
const host = process.env.NEXT_PUBLIC_POSTHOG_HOST;
|
const host = process.env.NEXT_PUBLIC_POSTHOG_HOST;
|
||||||
|
|
||||||
// Debug: vérifier que les variables d'environnement sont bien chargées
|
|
||||||
console.log('🔍 PostHog - Initialisation...');
|
|
||||||
console.log('🔑 Key:', key ? `${key.substring(0, 10)}...` : '❌ MANQUANTE');
|
|
||||||
console.log('🌐 Host:', host || '❌ MANQUANT');
|
|
||||||
|
|
||||||
if (!key || !host) {
|
if (!key || !host) {
|
||||||
console.error('❌ PostHog: Variables d\'environnement manquantes. Avez-vous redémarré le serveur après avoir ajouté NEXT_PUBLIC_POSTHOG_KEY et NEXT_PUBLIC_POSTHOG_HOST ?');
|
console.error('PostHog: Variables d\'environnement manquantes');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
posthog.init(key, {
|
posthog.init(key, {
|
||||||
api_host: host,
|
api_host: host,
|
||||||
person_profiles: 'identified_only',
|
person_profiles: 'identified_only',
|
||||||
capture_pageview: false, // On gère les pageviews manuellement avec PostHogPageView
|
capture_pageview: false,
|
||||||
capture_pageleave: true,
|
capture_pageleave: true,
|
||||||
loaded: (posthog) => {
|
loaded: (posthog) => {
|
||||||
console.log('✅ PostHog initialisé avec succès!');
|
|
||||||
// Rendre posthog accessible globalement pour les tests en dev
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
posthog.debug(true); // Active les logs en dev
|
(window as any).posthog = posthog;
|
||||||
(window as any).posthog = posthog; // Exposer pour les tests console
|
|
||||||
console.log('💡 Vous pouvez maintenant utiliser "window.posthog" dans la console');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1352,7 +1352,6 @@ useEffect(() => {
|
||||||
has_notes: !!payload.notes,
|
has_notes: !!payload.notes,
|
||||||
validation_immediate: payload.valider_direct,
|
validation_immediate: payload.valider_direct,
|
||||||
});
|
});
|
||||||
console.log('📊 PostHog: Événement contract_created envoyé');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Autoriser la navigation après soumission + feedback
|
// Autoriser la navigation après soumission + feedback
|
||||||
|
|
|
||||||
|
|
@ -50,18 +50,6 @@ export default function SimulateurContent({ hideInfoPanel = false }: SimulateurC
|
||||||
// Ajout du contexte sidebar
|
// Ajout du contexte sidebar
|
||||||
opened_from: 'sidebar'
|
opened_from: 'sidebar'
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('📊 PostHog: Événement simulateur_calculation (sidebar) enrichi envoyé', {
|
|
||||||
categorie: data.categorie,
|
|
||||||
ccn: data.ccn_nom,
|
|
||||||
type_calcul: data.type_calcul,
|
|
||||||
montant_saisi: data.montant_saisi,
|
|
||||||
resultats: {
|
|
||||||
brut: data.resultat_brut,
|
|
||||||
net: data.resultat_net,
|
|
||||||
cost: data.resultat_cost
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { useEffect, useMemo, useState, useRef, useImperativeHandle, forwardRef } from "react";
|
import { useEffect, useMemo, useState, useRef, useImperativeHandle, forwardRef } from "react";
|
||||||
import { supabase } from "@/lib/supabaseClient";
|
import { supabase } from "@/lib/supabaseClient";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { RefreshCw, Check, X, Settings, FileText, CheckCircle, BarChart3, Eye, ChevronDown, Trash2, FileDown, FileSignature, DollarSign, XCircle, BellRing } from "lucide-react";
|
import { RefreshCw, Check, X, Settings, FileText, CheckCircle, BarChart3, Eye, ChevronDown, Trash2, FileDown, FileSignature, Euro, XCircle, BellRing } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import BulkPdfProgressModal from "./BulkPdfProgressModal";
|
import BulkPdfProgressModal from "./BulkPdfProgressModal";
|
||||||
import PdfVerificationModal from "./PdfVerificationModal";
|
import PdfVerificationModal from "./PdfVerificationModal";
|
||||||
|
|
@ -1130,8 +1130,23 @@ function ContractsGridImpl({ initialData, activeOrgId }: { initialData: Contract
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Fonction pour relancer un salarié spécifique
|
// Fonction pour relancer un salarié spécifique
|
||||||
const handleReminderClick = (contract: any) => {
|
const handleReminderClick = async (contract: any) => {
|
||||||
setSelectedContractForReminder(contract);
|
setSelectedContractForReminder(contract);
|
||||||
|
|
||||||
|
// Récupérer l'email du salarié depuis la base de données
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/staff/contrats/${contract.id}/employee-email`);
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.json();
|
||||||
|
setSelectedContractForReminder({
|
||||||
|
...contract,
|
||||||
|
employee_email: result.employee_email
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur récupération email:', error);
|
||||||
|
}
|
||||||
|
|
||||||
setShowEmployeeReminderModal(true);
|
setShowEmployeeReminderModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1167,6 +1182,50 @@ function ContractsGridImpl({ initialData, activeOrgId }: { initialData: Contract
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fonction pour relancer en masse tous les salariés sélectionnés
|
||||||
|
const handleBulkReminderClick = async () => {
|
||||||
|
if (selectedContractIds.size === 0) {
|
||||||
|
toast.error("Aucun contrat sélectionné");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoadingReminder(true);
|
||||||
|
const contractIds = Array.from(selectedContractIds);
|
||||||
|
let successCount = 0;
|
||||||
|
let errorCount = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const contractId of contractIds) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/staff/contrats/relance-salarie', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ contractId }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
successCount++;
|
||||||
|
} else {
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successCount > 0) {
|
||||||
|
toast.success(`${successCount} email${successCount > 1 ? 's' : ''} de relance envoyé${successCount > 1 ? 's' : ''} avec succès`);
|
||||||
|
}
|
||||||
|
if (errorCount > 0) {
|
||||||
|
toast.error(`${errorCount} erreur${errorCount > 1 ? 's' : ''} lors de l'envoi`);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setIsLoadingReminder(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// derive options from initialData for simple selects
|
// derive options from initialData for simple selects
|
||||||
const structures = useMemo(() => {
|
const structures = useMemo(() => {
|
||||||
const uniqueStructures = Array.from(new Set((initialData || []).map((r) => r.structure).filter(Boolean) as string[]));
|
const uniqueStructures = Array.from(new Set((initialData || []).map((r) => r.structure).filter(Boolean) as string[]));
|
||||||
|
|
@ -1514,6 +1573,18 @@ function ContractsGridImpl({ initialData, activeOrgId }: { initialData: Contract
|
||||||
<FileSignature className="w-4 h-4" />
|
<FileSignature className="w-4 h-4" />
|
||||||
{isGeneratingESign ? "Envoi..." : "Envoyer e-sign"}
|
{isGeneratingESign ? "Envoi..." : "Envoyer e-sign"}
|
||||||
</button>
|
</button>
|
||||||
|
<div className="border-t border-gray-200 my-1"></div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
handleBulkReminderClick();
|
||||||
|
setShowESignMenu(false);
|
||||||
|
}}
|
||||||
|
disabled={isLoadingReminder}
|
||||||
|
className="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors disabled:text-gray-400 disabled:hover:bg-white flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<BellRing className="w-4 h-4" />
|
||||||
|
{isLoadingReminder ? "Envoi..." : "Relancer salariés"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
@ -1524,7 +1595,7 @@ function ContractsGridImpl({ initialData, activeOrgId }: { initialData: Contract
|
||||||
onClick={() => setShowSalaryModal(true)}
|
onClick={() => setShowSalaryModal(true)}
|
||||||
className="px-3 py-1 text-sm bg-green-600 text-white rounded hover:bg-green-700 transition-colors flex items-center gap-1"
|
className="px-3 py-1 text-sm bg-green-600 text-white rounded hover:bg-green-700 transition-colors flex items-center gap-1"
|
||||||
>
|
>
|
||||||
<DollarSign className="w-4 h-4" />
|
<Euro className="w-4 h-4" />
|
||||||
Saisir brut
|
Saisir brut
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|
@ -1570,7 +1641,6 @@ function ContractsGridImpl({ initialData, activeOrgId }: { initialData: Contract
|
||||||
<th className="text-left px-3 py-2 cursor-pointer" onClick={() => { setSortField('end_date'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}>
|
<th className="text-left px-3 py-2 cursor-pointer" onClick={() => { setSortField('end_date'); setSortOrder((o) => o === 'asc' ? 'desc' : 'asc'); }}>
|
||||||
Date fin {sortField === 'end_date' ? (sortOrder === 'asc' ? '▲' : '▼') : ''}
|
Date fin {sortField === 'end_date' ? (sortOrder === 'asc' ? '▲' : '▼') : ''}
|
||||||
</th>
|
</th>
|
||||||
<th className="text-left px-3 py-2">Actions</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -1673,16 +1743,6 @@ function ContractsGridImpl({ initialData, activeOrgId }: { initialData: Contract
|
||||||
}</td>
|
}</td>
|
||||||
<td className="px-3 py-2">{formatDate(r.start_date)}</td>
|
<td className="px-3 py-2">{formatDate(r.start_date)}</td>
|
||||||
<td className="px-3 py-2">{formatDate(r.end_date)}</td>
|
<td className="px-3 py-2">{formatDate(r.end_date)}</td>
|
||||||
<td className="px-3 py-2">
|
|
||||||
<button
|
|
||||||
onClick={() => handleReminderClick(r)}
|
|
||||||
className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-white bg-indigo-600 rounded hover:bg-indigo-700 transition-colors"
|
|
||||||
title="Relancer le salarié"
|
|
||||||
>
|
|
||||||
<BellRing className="w-3.5 h-3.5" />
|
|
||||||
Relancer
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
||||||
|
|
@ -1625,8 +1625,6 @@ document.getElementById('calcBtn').addEventListener('click', function() {
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
}
|
}
|
||||||
}, '*');
|
}, '*');
|
||||||
|
|
||||||
console.log('📊 PostHog: Événement simulateur_calculation enrichi envoyé');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Erreur lors de l\'envoi de l\'événement PostHog:', e);
|
console.error('Erreur lors de l\'envoi de l\'événement PostHog:', e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue