Fondation
This commit is contained in:
parent
04de49aad5
commit
5744fd9724
@ -6,6 +6,7 @@ import { FileIcon, Calendar, Eye, MapPin, Users, Clock, ThumbsUp, Languages, Arr
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
// Define types for mission details
|
||||
interface User {
|
||||
@ -219,9 +220,11 @@ export default function MissionTabDetailPage() {
|
||||
{/* Display logo instead of Participate button */}
|
||||
<div className="w-24 h-24 rounded-md overflow-hidden flex-shrink-0">
|
||||
{mission.logoUrl ? (
|
||||
<img
|
||||
<Image
|
||||
src={mission.logoUrl}
|
||||
alt={mission.name}
|
||||
width={96}
|
||||
height={96}
|
||||
className="w-full h-full object-cover rounded-md"
|
||||
onError={(e) => {
|
||||
console.error("Logo failed to load:", {
|
||||
@ -238,9 +241,13 @@ export default function MissionTabDetailPage() {
|
||||
parent.classList.add('flex');
|
||||
parent.classList.add('items-center');
|
||||
parent.classList.add('justify-center');
|
||||
parent.innerHTML = `<span className="text-3xl font-medium text-gray-500">${mission.name.slice(0, 2).toUpperCase()}</span>`;
|
||||
const fallback = document.createElement('span');
|
||||
fallback.className = 'text-3xl font-medium text-gray-500';
|
||||
fallback.textContent = mission.name.slice(0, 2).toUpperCase();
|
||||
parent.appendChild(fallback);
|
||||
}
|
||||
}}
|
||||
unoptimized={mission.logoUrl?.includes('localhost') || mission.logoUrl?.includes('127.0.0.1')}
|
||||
/>
|
||||
) : (
|
||||
<div className="bg-gray-100 w-full h-full flex items-center justify-center">
|
||||
|
||||
@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
|
||||
import { Search, Lock, Plus } from "lucide-react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { getPublicUrl } from "@/lib/s3";
|
||||
@ -235,9 +236,11 @@ export default function MissionsPage() {
|
||||
<div className="flex justify-center items-center p-6 flex-grow">
|
||||
<div className="w-48 h-48 relative">
|
||||
{mission.logoUrl ? (
|
||||
<img
|
||||
<Image
|
||||
src={mission.logoUrl}
|
||||
alt={mission.name}
|
||||
width={192}
|
||||
height={192}
|
||||
className="w-full h-full object-cover rounded-md"
|
||||
onError={(e) => {
|
||||
console.error("Logo failed to load:", {
|
||||
@ -254,6 +257,7 @@ export default function MissionsPage() {
|
||||
(fallbackDiv as HTMLElement).style.display = 'flex';
|
||||
}
|
||||
}}
|
||||
unoptimized={mission.logoUrl?.includes('localhost') || mission.logoUrl?.includes('127.0.0.1')}
|
||||
/>
|
||||
) : null}
|
||||
<div
|
||||
|
||||
151
app/page.tsx
151
app/page.tsx
@ -1,77 +1,116 @@
|
||||
"use client";
|
||||
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "@/app/api/auth/options";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Suspense } from "react";
|
||||
import { QuoteCard } from "@/components/quote-card";
|
||||
import { Calendar } from "@/components/calendar";
|
||||
import { News } from "@/components/news";
|
||||
import { Duties } from "@/components/flow";
|
||||
import { Email } from "@/components/email";
|
||||
import { Parole } from "@/components/parole";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { HomeLogoutCheck } from "@/components/home-logout-check";
|
||||
|
||||
export default function Home() {
|
||||
const { data: session, status } = useSession();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
// Loading skeleton components
|
||||
function QuoteCardSkeleton() {
|
||||
return (
|
||||
<div className="col-span-3 animate-pulse">
|
||||
<div className="h-48 bg-gray-200 rounded-lg"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Check if logout is in progress - if so, redirect immediately
|
||||
const justLoggedOut = sessionStorage.getItem('just_logged_out') === 'true';
|
||||
const logoutCookie = document.cookie.split(';').some(c => c.trim().startsWith('logout_in_progress=true'));
|
||||
function CalendarSkeleton() {
|
||||
return (
|
||||
<div className="col-span-3 animate-pulse">
|
||||
<div className="h-48 bg-gray-200 rounded-lg"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (justLoggedOut || logoutCookie) {
|
||||
// Clear the flags and redirect
|
||||
sessionStorage.removeItem('just_logged_out');
|
||||
document.cookie = 'logout_in_progress=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC';
|
||||
window.location.href = '/signin?logout=true';
|
||||
return;
|
||||
}
|
||||
function NewsSkeleton() {
|
||||
return (
|
||||
<div className="col-span-3 animate-pulse">
|
||||
<div className="h-48 bg-gray-200 rounded-lg"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (status !== "loading") {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [status]);
|
||||
function DutiesSkeleton() {
|
||||
return (
|
||||
<div className="col-span-3 animate-pulse">
|
||||
<div className="h-48 bg-gray-200 rounded-lg"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Don't render widgets if logout is in progress
|
||||
const justLoggedOut = sessionStorage.getItem('just_logged_out') === 'true';
|
||||
const logoutCookie = document.cookie.split(';').some(c => c.trim().startsWith('logout_in_progress=true'));
|
||||
function EmailSkeleton() {
|
||||
return (
|
||||
<div className="col-span-6 animate-pulse">
|
||||
<div className="h-96 bg-gray-200 rounded-lg"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading || justLoggedOut || logoutCookie) {
|
||||
return (
|
||||
<main className="h-screen flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-gray-900"></div>
|
||||
</main>
|
||||
);
|
||||
function ParoleSkeleton() {
|
||||
return (
|
||||
<div className="col-span-6 animate-pulse">
|
||||
<div className="h-96 bg-gray-200 rounded-lg"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function Home() {
|
||||
// Check authentication on server side
|
||||
const session = await getServerSession(authOptions);
|
||||
|
||||
if (!session) {
|
||||
redirect("/signin");
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="h-screen overflow-auto">
|
||||
<div className="container mx-auto p-4 mt-12">
|
||||
{/* First row */}
|
||||
<div className="grid grid-cols-12 gap-4 mb-4">
|
||||
<div className="col-span-3">
|
||||
<QuoteCard />
|
||||
<>
|
||||
<HomeLogoutCheck />
|
||||
<main className="h-screen overflow-auto">
|
||||
<div className="container mx-auto p-4 mt-12">
|
||||
{/* First row */}
|
||||
<div className="grid grid-cols-12 gap-4 mb-4">
|
||||
<Suspense fallback={<QuoteCardSkeleton />}>
|
||||
<div className="col-span-3">
|
||||
<QuoteCard />
|
||||
</div>
|
||||
</Suspense>
|
||||
<Suspense fallback={<CalendarSkeleton />}>
|
||||
<div className="col-span-3">
|
||||
<Calendar />
|
||||
</div>
|
||||
</Suspense>
|
||||
<Suspense fallback={<NewsSkeleton />}>
|
||||
<div className="col-span-3">
|
||||
<News />
|
||||
</div>
|
||||
</Suspense>
|
||||
<Suspense fallback={<DutiesSkeleton />}>
|
||||
<div className="col-span-3">
|
||||
<Duties />
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
<div className="col-span-3">
|
||||
<Calendar />
|
||||
</div>
|
||||
<div className="col-span-3">
|
||||
<News />
|
||||
</div>
|
||||
<div className="col-span-3">
|
||||
<Duties />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Second row */}
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-6">
|
||||
<Email />
|
||||
</div>
|
||||
<div className="col-span-6">
|
||||
<Parole />
|
||||
{/* Second row */}
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<Suspense fallback={<EmailSkeleton />}>
|
||||
<div className="col-span-6">
|
||||
<Email />
|
||||
</div>
|
||||
</Suspense>
|
||||
<Suspense fallback={<ParoleSkeleton />}>
|
||||
<div className="col-span-6">
|
||||
<Parole />
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
28
components/home-logout-check.tsx
Normal file
28
components/home-logout-check.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
/**
|
||||
* Client component to handle logout state check
|
||||
* This checks sessionStorage and cookies for logout flags
|
||||
* and redirects if logout is in progress
|
||||
*/
|
||||
export function HomeLogoutCheck() {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
// Check if logout is in progress - if so, redirect immediately
|
||||
const justLoggedOut = sessionStorage.getItem('just_logged_out') === 'true';
|
||||
const logoutCookie = document.cookie.split(';').some(c => c.trim().startsWith('logout_in_progress=true'));
|
||||
|
||||
if (justLoggedOut || logoutCookie) {
|
||||
// Clear the flags and redirect
|
||||
sessionStorage.removeItem('just_logged_out');
|
||||
document.cookie = 'logout_in_progress=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC';
|
||||
router.push('/signin?logout=true');
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
// @ts-ignore - webdav package types may not be available
|
||||
import { createClient, WebDAVClient } from 'webdav';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { logger } from '@/lib/logger';
|
||||
@ -54,12 +55,12 @@ export async function discoverInfomaniakCalendars(
|
||||
|
||||
logger.debug('[CALDAV] Found items in root directory', {
|
||||
count: items.length,
|
||||
items: items.map(item => ({ filename: item.filename, type: item.type, basename: item.basename }))
|
||||
items: items.map((item: any) => ({ filename: item.filename, type: item.type, basename: item.basename }))
|
||||
});
|
||||
|
||||
const calendars: CalDAVCalendar[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
for (const item of items as any[]) {
|
||||
// Skip non-directories, root, and special directories like /principals
|
||||
if (item.type !== 'directory' || item.filename === '/' || item.filename === '/principals') {
|
||||
logger.debug('[CALDAV] Skipping item', { filename: item.filename, type: item.type });
|
||||
|
||||
@ -1,13 +1,28 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
ignoreDuringBuilds: false, // ✅ Réactivé - corriger les erreurs avant build
|
||||
dirs: ['app', 'components', 'lib'], // Limiter aux dossiers pertinents
|
||||
},
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
ignoreBuildErrors: false, // ✅ Réactivé - corriger les erreurs avant build
|
||||
},
|
||||
images: {
|
||||
unoptimized: true,
|
||||
unoptimized: false, // ✅ Activé l'optimisation d'images
|
||||
formats: ['image/avif', 'image/webp'],
|
||||
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
||||
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: '**',
|
||||
},
|
||||
{
|
||||
protocol: 'http',
|
||||
hostname: 'localhost',
|
||||
port: '9000', // MinIO local
|
||||
},
|
||||
],
|
||||
},
|
||||
experimental: {
|
||||
webpackBuildWorker: true,
|
||||
@ -22,9 +37,46 @@ const nextConfig = {
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'X-DNS-Prefetch-Control',
|
||||
value: 'on'
|
||||
},
|
||||
{
|
||||
key: 'Strict-Transport-Security',
|
||||
value: 'max-age=63072000; includeSubDomains; preload'
|
||||
},
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
value: 'SAMEORIGIN'
|
||||
},
|
||||
{
|
||||
key: 'X-Content-Type-Options',
|
||||
value: 'nosniff'
|
||||
},
|
||||
{
|
||||
key: 'X-XSS-Protection',
|
||||
value: '1; mode=block'
|
||||
},
|
||||
{
|
||||
key: 'Referrer-Policy',
|
||||
value: 'origin-when-cross-origin'
|
||||
},
|
||||
{
|
||||
key: 'Permissions-Policy',
|
||||
value: 'camera=(), microphone=(), geolocation=()'
|
||||
},
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
value: "frame-ancestors 'self' https://espace.slm-lab.net https://connect.slm-lab.net"
|
||||
value: [
|
||||
"default-src 'self'",
|
||||
"script-src 'self' 'unsafe-eval' 'unsafe-inline'", // ⚠️ À restreindre davantage si possible
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: https: http://localhost:9000",
|
||||
"font-src 'self' data:",
|
||||
"connect-src 'self' https://*.slm-lab.net https://*.microsoft.com https://*.microsoftonline.com wss://*.slm-lab.net",
|
||||
"frame-src 'self' https://*.slm-lab.net",
|
||||
"frame-ancestors 'self' https://espace.slm-lab.net https://connect.slm-lab.net",
|
||||
].join('; ')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user