File size: 12,844 Bytes
f238780
 
6095cee
 
7241f5b
f238780
dfa5d71
36e21e1
c3d1f75
f238780
 
dfa5d71
f238780
6095cee
7241f5b
6095cee
36e21e1
a393989
68cfcdd
7241f5b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68cfcdd
 
 
 
 
 
 
 
6095cee
a393989
36e21e1
 
 
 
a393989
 
3f09099
 
 
 
 
 
 
 
 
a393989
3f09099
a393989
3f09099
 
 
 
a393989
 
 
 
 
 
3f09099
a393989
3f09099
a393989
 
 
fe9007b
6095cee
fe9007b
 
 
1f39e1b
6095cee
 
 
 
fe9007b
6095cee
 
8b68925
6095cee
 
8b68925
6095cee
b5c985b
 
 
68cfcdd
 
b5c985b
68cfcdd
b5c985b
76fd853
68cfcdd
 
b5c985b
68cfcdd
 
 
 
b5c985b
68cfcdd
b5c985b
 
 
 
 
68cfcdd
b5c985b
fe9007b
 
 
be48683
6095cee
1f39e1b
8a2847a
6095cee
 
e5db1e5
6095cee
 
e5db1e5
 
fe9007b
1f39e1b
fe9007b
6095cee
fe9007b
 
e5db1e5
fe9007b
 
 
6095cee
fe9007b
 
 
 
 
 
e5db1e5
fe9007b
 
6095cee
 
1f39e1b
 
 
 
 
 
6095cee
 
 
 
fe9007b
6095cee
b5c985b
1f39e1b
 
b5c985b
 
 
 
 
 
1f39e1b
b5c985b
 
 
 
 
 
 
6095cee
 
 
 
 
fe9007b
 
 
 
6095cee
fe9007b
 
 
 
c8e2285
fe9007b
f238780
80d1ca4
36e21e1
 
 
 
 
 
 
cb8f5db
3f09099
 
cb8f5db
a393989
 
cb8f5db
 
 
 
 
36e21e1
02253a0
36e21e1
 
02253a0
36e21e1
 
02253a0
36e21e1
 
d68ff6f
7241f5b
02253a0
36e21e1
 
02253a0
36e21e1
 
 
 
c3d1f75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36e21e1
c3d1f75
36e21e1
 
fe9007b
36e21e1
dfa5d71
 
f238780
 
8a2847a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
'use client';

import { useAuth } from '@/lib/auth';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import { Sidebar } from './Sidebar';
import Chatbot from './Chatbot';
import { Loader2, AlertTriangle, RefreshCw, Menu, Newspaper } from 'lucide-react';
import { supabase } from '@/lib/supabase';

export function AppShell({ children }: { children: React.ReactNode }) {
    const { user, profile, loading } = useAuth();
    const pathname = usePathname();
    const router = useRouter();

    const [loadingTimeout, setLoadingTimeout] = useState(false);
    const [sidebarOpen, setSidebarOpen] = useState(false);
    const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
    const [minLoadingDone, setMinLoadingDone] = useState(false);
    const [isSystemPaused, setIsSystemPaused] = useState(false);

    const isLoginPage = pathname === '/login';
    const isDebugPage = pathname === '/debug-supabase';
    const isPublicPage = isLoginPage || isDebugPage;

    // Check pause status
    useEffect(() => {
        if (!user || isPublicPage) return;

        const checkPauseStatus = async () => {
            try {
                // Determine source table based on role
                const isClientRole = profile?.role === 'client' && profile?.client_id;

                if (isClientRole) {
                    const { data } = await supabase.from('client_settings').select('is_paused').eq('client_id', profile.client_id).maybeSingle();
                    if (data) setIsSystemPaused(!!data.is_paused);
                } else {
                    // Fallback for admin or special cases
                    const { data } = await supabase.from('settings').select('is_paused').eq('id', 1).maybeSingle();
                    if (data) setIsSystemPaused(!!data.is_paused);
                }
            } catch (e) {
                console.warn("Failed to fetch pause status:", e);
            }
        };

        checkPauseStatus();
        const interval = setInterval(checkPauseStatus, 30000); // Check every 30s
        return () => clearInterval(interval);
    }, [user, profile, isPublicPage]);

    // Minimum loading duration (3 seconds) to ensure a smooth transition
    useEffect(() => {
        const timer = setTimeout(() => {
            setMinLoadingDone(true);
        }, 3000);
        return () => clearTimeout(timer);
    }, []);

    // Fermer la sidebar mobile quand on change de page
    useEffect(() => {
        setSidebarOpen(false);
    }, [pathname]);

    // Persistance de l'état réduit de la sidebar
    useEffect(() => {
        const syncState = () => {
            const saved = localStorage.getItem('sidebar-collapsed') === 'true';
            setIsSidebarCollapsed(saved);
        };

        syncState();
        
        if (localStorage.getItem('sidebar-collapsed') === 'true') {
            document.documentElement.classList.add('sidebar-collapsed');
        } else {
            document.documentElement.classList.remove('sidebar-collapsed');
        }

        window.addEventListener('sidebar-toggled', syncState);
        return () => window.removeEventListener('sidebar-toggled', syncState);
    }, [pathname]); // Refresh on pathname change ensures consistency

    const toggleSidebar = () => {
        const newValue = !isSidebarCollapsed;
        setIsSidebarCollapsed(newValue);
        localStorage.setItem('sidebar-collapsed', String(newValue));
        if (newValue) {
            document.documentElement.classList.add('sidebar-collapsed');
        } else {
            document.documentElement.classList.remove('sidebar-collapsed');
        }
    };

    // Timeout fallback: si loading dure plus de 15 secondes, on affiche une erreur
    useEffect(() => {
        if (loading) {
            const timer = setTimeout(() => {
                setLoadingTimeout(true);
            }, 15000);
            return () => clearTimeout(timer);
        } else {
            setLoadingTimeout(false);
        }
    }, [loading]);

    useEffect(() => {
        if (!loading && !user && !isPublicPage) {
            router.push('/login');
        }
    }, [user, loading, isPublicPage, router]);

    const [progress, setProgress] = useState(0);
    const [statusText, setStatusText] = useState("Initialisation...");

    const isReallyLoading = loading || !minLoadingDone;

    useEffect(() => {
        if (isReallyLoading) {
            const interval = setInterval(() => {
                setProgress((prev: number) => {
                    if (prev >= 98) return 98;
                    const inc = Math.random() * 10;
                    const next = prev + inc;
                    if (next > 20 && next < 50) setStatusText("Authentification sécurisée...");
                    if (next >= 50 && next < 80) setStatusText("Récupération de vos radars...");
                    if (next >= 80) setStatusText("Préparation de l'interface...");
                    return next > 98 ? 98 : next;
                });
            }, 300);
            return () => clearInterval(interval);
        } else {
            setStatusText("Prêt !");
            setProgress(100);
        }
    }, [isReallyLoading]);

    // Debug page - always accessible
    if (isDebugPage) {
        return <>{children}</>;
    }

    // Loading state with timeout fallback
    if (loading) {
        if (loadingTimeout) {
            return (
                <div className="min-h-screen flex items-center justify-center bg-[var(--bg-main)]">
                    <div className="flex flex-col items-center gap-4 max-w-md text-center p-6">
                        <AlertTriangle size={48} className="text-amber-500" />
                        <h2 className="text-xl font-bold theme-title">Connexion lente</h2>
                        <p className="text-sm theme-description">
                            La connexion à Supabase prend plus de temps que prévu.
                            Cela peut être dû à un problème réseau.
                        </p>
                        <div className="flex gap-3 mt-4">
                            <button
                                onClick={() => window.location.reload()}
                                className="flex items-center gap-2 px-4 py-2 bg-[var(--accent)] text-[var(--accent-foreground)] rounded-lg font-medium hover:opacity-90"
                            >
                                <RefreshCw size={16} />
                                Réessayer
                            </button>
                            <button
                                onClick={() => {
                                    localStorage.clear();
                                    sessionStorage.clear();
                                    window.location.href = '/login';
                                }}
                                className="px-4 py-2 theme-card-sec theme-title rounded-lg font-medium hover:opacity-80"
                            >
                                Réinitialiser
                            </button>
                        </div>
                        <a
                            href="/debug-supabase"
                            className="text-xs text-blue-500 hover:underline mt-4"
                        >
                            Page de diagnostic Supabase
                        </a>
                    </div>
                </div>
            );
        }

        return (
            <div className="min-h-screen flex items-center justify-center bg-[#0a0a0c]">
                <div className="w-64 space-y-4">
                    <div className="flex items-center justify-between text-[10px] font-black uppercase tracking-widest">
                        <span className="text-indigo-400">{statusText}</span>
                        <span className="theme-metadata tabular-nums">{Math.round(progress)}%</span>
                    </div>

                    <div className="h-1.5 w-full bg-white/5 rounded-full overflow-hidden border border-white/5 p-[1px]">
                        <div
                            className="h-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 rounded-full transition-all duration-500 ease-out shadow-[0_0_15px_rgba(99,102,241,0.4)]"
                            style={{ width: `${progress}%` }}
                        />
                    </div>

                    <div className="flex justify-center">
                        <p className="text-[10px] theme-metadata font-bold uppercase tracking-[0.2em] opacity-40 animate-pulse">SaaS Veille v3</p>
                    </div>
                </div>
            </div>
        );
    }

    // Login page - no sidebar
    if (isLoginPage) {
        return <>{children}</>;
    }

    // Not authenticated - redirect will happen
    if (!user) {
        return null;
    }

    // Authenticated - show app with sidebar
    return (
        <div className="flex min-h-screen bg-[var(--bg-main)] max-w-screen-fix">
            {/* Sidebar (responsive) */}
            <Sidebar
                isOpen={sidebarOpen}
                onClose={() => setSidebarOpen(false)}
            />

            {/* Main content */}
            <main className="flex-1 min-h-screen flex flex-col w-full relative transition-all duration-300">
                {/* Desktop Sidebar Toggle (Burger) — Toujours visible car FIXED */}
                <div className="hidden lg:flex fixed top-4 z-[60]" style={{ left: "calc(var(--sidebar-w, 256px) + 16px)" }}>
                    <button
                        onClick={toggleSidebar}
                        className={`p-1.5 bg-[var(--bg-sidebar)] border border-[var(--border-subtle)] rounded-full text-[var(--accent)] shadow-xl hover:scale-110 transition-all group ${isSidebarCollapsed ? 'rotate-180' : ''}`}
                        title="Réduire/Agrandir la navigation"
                    >
                        <Menu size={14} className="group-hover:rotate-90 transition-transform duration-300" />
                    </button>
                </div>
                {/* Mobile Header avec bouton hamburger */}
                <header className="lg:hidden sticky top-0 z-30 flex items-center justify-between px-4 py-4 bg-[var(--bg-sidebar)] border-b border-[var(--border-subtle)] backdrop-blur-xl">
                    <button
                        onClick={() => setSidebarOpen(true)}
                        className="p-2 -ml-2 rounded-xl hover:bg-[var(--accent)]/10 theme-title transition-colors"
                        aria-label="Ouvrir le menu"
                    >
                        <Menu size={24} />
                    </button>

                    <div className="flex items-center gap-2">
                        < Newspaper size={20} className="theme-accent" />
                        <span className="text-sm font-black tracking-tight theme-title">ARGOS <span className="opacity-50 theme-accent">VEILLE</span></span>
                    </div>

                    <div className="w-10 h-10 rounded-xl bg-[var(--accent)]/10 border border-[var(--accent)]/30 flex items-center justify-center theme-accent text-xs font-bold">
                        {profile?.email?.charAt(0).toUpperCase() || 'U'}
                    </div>
                </header>

                {/* PAUSE BANNER */}
                {isSystemPaused && (
                    <div className="sticky top-0 lg:top-0 z-40 animate-in slide-in-from-top duration-500">
                        <div className="bg-gradient-to-r from-amber-600 via-amber-500 to-amber-600 px-4 py-3 flex items-center justify-center gap-3 shadow-2xl">
                            <AlertTriangle size={18} className="text-white animate-pulse" />
                            <p className="text-[11px] font-black uppercase tracking-[0.2em] text-white">
                                Mode Veille Activé : L'agent automatique est actuellement à l'arrêt
                            </p>
                            <button
                                onClick={() => router.push('/config')}
                                className="ml-4 px-3 py-1 bg-white/20 hover:bg-white/30 rounded-lg text-[9px] font-black uppercase text-white border border-white/20 transition-all"
                            >
                                Réactiver
                            </button>
                        </div>
                    </div>
                )}

                {/* Page content */}
                <div className="flex-1 flex flex-col h-full">
                    {children}
                </div>
            </main>

            {/* Chatbot - Disponible sur toutes les pages */}
            <Chatbot clientId={profile?.client_id || undefined} />
        </div>
    );
}