mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-12 01:37:40 +02:00
Created a checkpoint
Replit-Commit-Author: Agent Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 0a1b1aa1-ca7d-4f74-8ecb-8057b6feb3f6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/7RN8LqE
This commit is contained in:
1 parent
345a2d968f
commit
0cf817e03f
18 files changed
+658
-33310
No files matched your search
@@ -24,6 +24,7 @@ import SavTickets from "@/pages/SavTickets";
|
||||
import Avoirs from "@/pages/Avoirs";
|
||||
import WeatherSettings from "@/pages/WeatherSettings";
|
||||
import Analytics from "@/pages/Analytics";
|
||||
import SalesAnalysisPage from "@/pages/SalesAnalysisPage";
|
||||
import Layout from "@/components/Layout";
|
||||
|
||||
function RouterProduction() {
|
||||
@@ -104,6 +105,7 @@ function RouterProduction() {
|
||||
<Route path="/sav" component={SavTickets} />
|
||||
<Route path="/avoirs" component={Avoirs} />
|
||||
<Route path="/analytics" component={Analytics} />
|
||||
<Route path="/sales-analysis" component={SalesAnalysisPage} />
|
||||
|
||||
{/* Routes de compatibilité - redirection vers utilities */}
|
||||
<Route path="/backup" component={Utilities} />
|
||||
|
||||
@@ -34,7 +34,8 @@ import {
|
||||
Wrench,
|
||||
FileUp,
|
||||
Send,
|
||||
Receipt
|
||||
Receipt,
|
||||
TrendingUp
|
||||
} from "lucide-react";
|
||||
|
||||
export default function Sidebar() {
|
||||
@@ -263,6 +264,12 @@ export default function Sidebar() {
|
||||
icon: BarChart3,
|
||||
roles: ["admin", "directeur", "manager"]
|
||||
},
|
||||
{
|
||||
path: "/sales-analysis",
|
||||
label: "Analyse Vente",
|
||||
icon: TrendingUp,
|
||||
roles: ["admin", "directeur"]
|
||||
},
|
||||
{
|
||||
path: "/avoirs",
|
||||
label: "Avoirs",
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Save, Loader2 } from "lucide-react";
|
||||
import { queryClient, apiRequest } from "@/lib/queryClient";
|
||||
|
||||
interface UtilitiesConfig {
|
||||
id?: number;
|
||||
salesAnalysisUrl?: string | null;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export default function SalesAnalysisConfig() {
|
||||
const { toast } = useToast();
|
||||
const [salesAnalysisUrl, setSalesAnalysisUrl] = useState("");
|
||||
|
||||
// Récupérer la configuration existante
|
||||
const { data: config, isLoading } = useQuery<UtilitiesConfig>({
|
||||
queryKey: ['/api/utilities']
|
||||
});
|
||||
|
||||
// Mettre à jour l'état quand les données sont chargées
|
||||
useEffect(() => {
|
||||
if (config?.salesAnalysisUrl) {
|
||||
setSalesAnalysisUrl(config.salesAnalysisUrl);
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
// Mutation pour sauvegarder la configuration
|
||||
const saveMutation = useMutation({
|
||||
mutationFn: async (data: { salesAnalysisUrl: string | null }) => {
|
||||
return await apiRequest('/api/utilities', 'POST', data);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['/api/utilities'] });
|
||||
toast({
|
||||
title: "Configuration sauvegardée",
|
||||
description: "L'URL d'analyse des ventes a été mise à jour avec succès.",
|
||||
});
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Erreur",
|
||||
description: error.message || "Impossible de sauvegarder la configuration",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
saveMutation.mutate({
|
||||
salesAnalysisUrl: salesAnalysisUrl.trim() || null
|
||||
});
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-blue-600" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Configuration Analyse des Ventes</CardTitle>
|
||||
<CardDescription>
|
||||
Configurez l'URL ou l'adresse IP pour accéder à l'interface d'analyse des ventes.
|
||||
Cette page sera accessible via le menu "Analyse Vente".
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="salesAnalysisUrl">URL ou Adresse IP</Label>
|
||||
<Input
|
||||
id="salesAnalysisUrl"
|
||||
type="text"
|
||||
placeholder="https://exemple.com/analyse ou http://192.168.1.100:8080"
|
||||
value={salesAnalysisUrl}
|
||||
onChange={(e) => setSalesAnalysisUrl(e.target.value)}
|
||||
data-testid="input-sales-analysis-url"
|
||||
/>
|
||||
<p className="text-sm text-gray-500">
|
||||
Exemple : https://votre-domaine.com/analyse ou http://192.168.1.100:8080
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={saveMutation.isPending}
|
||||
data-testid="button-save-sales-analysis"
|
||||
>
|
||||
{saveMutation.isPending ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
Sauvegarde...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
Sauvegarder
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{config?.salesAnalysisUrl && (
|
||||
<div className="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-md">
|
||||
<p className="text-sm text-blue-800">
|
||||
<strong>URL configurée :</strong> {config.salesAnalysisUrl}
|
||||
</p>
|
||||
<p className="text-xs text-blue-600 mt-1">
|
||||
Cette URL sera affichée dans le menu "Analyse Vente"
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Loader2, TrendingUp, AlertCircle } from "lucide-react";
|
||||
import { Link } from "wouter";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface UtilitiesConfig {
|
||||
id?: number;
|
||||
salesAnalysisUrl?: string | null;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export default function SalesAnalysisPage() {
|
||||
// Récupérer l'URL configurée
|
||||
const { data: config, isLoading } = useQuery<UtilitiesConfig>({
|
||||
queryKey: ['/api/utilities']
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-blue-600" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const salesAnalysisUrl = config?.salesAnalysisUrl;
|
||||
|
||||
if (!salesAnalysisUrl) {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<Card className="w-96">
|
||||
<CardHeader className="text-center">
|
||||
<AlertCircle className="w-12 h-12 text-orange-500 mx-auto mb-4" />
|
||||
<CardTitle>URL non configurée</CardTitle>
|
||||
<CardDescription>
|
||||
L'URL d'analyse des ventes n'a pas encore été configurée.
|
||||
Veuillez la définir dans les utilitaires.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex justify-center">
|
||||
<Link href="/utilities">
|
||||
<Button variant="default" data-testid="button-configure-url">
|
||||
Configurer l'URL
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="bg-white shadow-sm border-b border-gray-200 p-6">
|
||||
<div className="flex items-center">
|
||||
<TrendingUp className="w-8 h-8 text-blue-600 mr-3" />
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
Analyse des Ventes
|
||||
</h2>
|
||||
<p className="text-gray-600 text-sm">
|
||||
{salesAnalysisUrl}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Iframe Content */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<iframe
|
||||
src={salesAnalysisUrl}
|
||||
className="w-full h-full border-0"
|
||||
title="Analyse des Ventes"
|
||||
data-testid="iframe-sales-analysis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
Bug,
|
||||
Cloud,
|
||||
Send,
|
||||
Code
|
||||
Code,
|
||||
TrendingUp
|
||||
} from "lucide-react";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
@@ -21,6 +22,7 @@ import DatabaseDebug from "./DatabaseDebug";
|
||||
import WeatherSettings from "./WeatherSettings";
|
||||
import WebhookBAPConfig from "./WebhookBAPConfig";
|
||||
import SQLExecutor from "./SQLExecutor";
|
||||
import SalesAnalysisConfig from "./SalesAnalysisConfig";
|
||||
|
||||
export default function Utilities() {
|
||||
const { user } = useAuthUnified();
|
||||
@@ -41,9 +43,10 @@ export default function Utilities() {
|
||||
const canManageWeather = user?.role === 'admin';
|
||||
const canManageWebhookBAP = user?.role === 'admin';
|
||||
const canExecuteSQL = user?.role === 'admin';
|
||||
const canManageSalesAnalysis = user?.role === 'admin' || user?.role === 'directeur';
|
||||
|
||||
// Si l'utilisateur n'a aucune permission, afficher le message d'accès refusé
|
||||
if (!canManageBackups && !canManageNocoDB && !canDebugDatabase && !canManageWeather && !canManageWebhookBAP && !canExecuteSQL) {
|
||||
if (!canManageBackups && !canManageNocoDB && !canDebugDatabase && !canManageWeather && !canManageWebhookBAP && !canExecuteSQL && !canManageSalesAnalysis) {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<Card className="w-96">
|
||||
@@ -79,7 +82,7 @@ export default function Utilities() {
|
||||
{/* Content avec onglets */}
|
||||
<div className="flex-1 p-6 overflow-hidden">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="h-full flex flex-col">
|
||||
<TabsList className="grid w-full grid-cols-6 mb-6">
|
||||
<TabsList className="grid w-full grid-cols-7 mb-6">
|
||||
{canManageBackups && (
|
||||
<TabsTrigger value="backups" className="flex items-center gap-2">
|
||||
<Database className="w-4 h-4" />
|
||||
@@ -116,6 +119,12 @@ export default function Utilities() {
|
||||
Exécution SQL
|
||||
</TabsTrigger>
|
||||
)}
|
||||
{canManageSalesAnalysis && (
|
||||
<TabsTrigger value="salesanalysis" className="flex items-center gap-2">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
Analyse Vente
|
||||
</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
|
||||
{/* Contenu des onglets */}
|
||||
@@ -166,6 +175,14 @@ export default function Utilities() {
|
||||
</div>
|
||||
</TabsContent>
|
||||
)}
|
||||
|
||||
{canManageSalesAnalysis && (
|
||||
<TabsContent value="salesanalysis" className="flex-1 overflow-hidden">
|
||||
<div className="h-full bg-gray-50 -m-6 p-6">
|
||||
<SalesAnalysisConfig />
|
||||
</div>
|
||||
</TabsContent>
|
||||
)}
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user