File size: 10,501 Bytes
8c03bde | 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 |
import React, { useState } from 'react';
import { AppSettings } from '../types';
import {
Save,
Globe,
User,
Info,
Database,
FileSpreadsheet,
Copy,
CheckCircle2,
Lock,
ExternalLink,
ShieldCheck,
Hash,
Download,
// Added missing AlertTriangle icon import
AlertTriangle
} from 'lucide-react';
interface SettingsPanelProps {
settings: AppSettings;
onSave: (settings: AppSettings) => void;
}
const SettingsPanel: React.FC<SettingsPanelProps> = ({ settings, onSave }) => {
const [localSettings, setLocalSettings] = useState<AppSettings>(settings);
const [copied, setCopied] = useState(false);
const [activeTab, setActiveTab] = useState<'profile' | 'google' | 'info'>('profile');
const googleScriptCode = `
/**
* AUDITPRO UNIVERSAL SYNC SCRIPT
* Copiez ce code dans Extensions > Apps Script de votre Google Sheet
*/
function doPost(e) {
try {
var payload = JSON.parse(e.postData.contents);
var audit = payload.data;
// On utilise l'ID envoyé par l'app pour cibler la bonne feuille
var sheetId = audit.sheetId;
var ss = sheetId ? SpreadsheetApp.openById(sheetId) : SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// En-têtes si feuille vide
if (sheet.getLastRow() == 0) {
sheet.appendRow(["DATE", "INSPECTEUR", "MODÈLE", "QUESTION", "RÉPONSE", "OBSERVATION", "PHOTOS (NB)"]);
sheet.getRange(1, 1, 1, 7).setFontWeight("bold").setBackground("#4F46E5").setFontColor("white");
}
audit.responses.forEach(function(resp) {
sheet.appendRow([
new Date(audit.startedAt).toLocaleString(),
audit.inspectorName,
audit.checklistName,
resp.itemLabel,
resp.value,
resp.comment || "",
resp.photos ? resp.photos.length : 0
]);
});
return ContentService.createTextOutput("SUCCESS").setMimeType(ContentService.MimeType.TEXT);
} catch(f) {
return ContentService.createTextOutput("ERROR: " + f.message).setMimeType(ContentService.MimeType.TEXT);
}
}
`.trim();
const copyScript = () => {
navigator.clipboard.writeText(googleScriptCode);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="space-y-6 pb-20 animate-in fade-in duration-500">
<div className="bg-white rounded-[3rem] shadow-2xl border border-slate-200 overflow-hidden">
{/* Navigation Tabs */}
<div className="flex border-b border-slate-100 bg-slate-50/30 overflow-x-auto">
<button onClick={() => setActiveTab('profile')} className={`px-8 py-6 flex items-center gap-2 text-[11px] font-black uppercase tracking-widest transition-all border-b-2 ${activeTab === 'profile' ? 'border-indigo-600 text-indigo-600 bg-white' : 'border-transparent text-slate-400 hover:text-slate-600'}`}>
<User size={16} /> Identité
</button>
<button onClick={() => setActiveTab('google')} className={`px-8 py-6 flex items-center gap-2 text-[11px] font-black uppercase tracking-widest transition-all border-b-2 ${activeTab === 'google' ? 'border-indigo-600 text-indigo-600 bg-white' : 'border-transparent text-slate-400 hover:text-slate-600'}`}>
<Globe size={16} /> Synchro Google
</button>
<button onClick={() => setActiveTab('info')} className={`px-8 py-6 flex items-center gap-2 text-[11px] font-black uppercase tracking-widest transition-all border-b-2 ${activeTab === 'info' ? 'border-indigo-600 text-indigo-600 bg-white' : 'border-transparent text-slate-400 hover:text-slate-600'}`}>
<Database size={16} /> Mon Espace
</button>
</div>
<div className="p-8 sm:p-12">
{activeTab === 'profile' && (
<div className="space-y-8 animate-in slide-in-from-left-4 duration-300">
<div className="space-y-3">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest block">Signature Inspecteur</label>
<input
type="text"
value={localSettings.inspectorName}
onChange={(e) => setLocalSettings({...localSettings, inspectorName: e.target.value})}
placeholder="Votre nom complet"
className="w-full border-2 border-slate-100 rounded-2xl p-5 text-slate-800 font-bold focus:border-indigo-600 outline-none bg-slate-50/50 transition-all text-lg"
/>
<p className="text-[10px] text-slate-400 font-bold italic italic">Ce nom sera gravé dans chaque audit que vous réaliserez.</p>
</div>
</div>
)}
{activeTab === 'google' && (
<div className="space-y-10 animate-in slide-in-from-left-4 duration-300">
<div className="p-8 bg-indigo-600 rounded-[2rem] text-white shadow-xl shadow-indigo-100 flex items-start gap-6">
<div className="bg-white/20 p-4 rounded-2xl backdrop-blur-md">
<ShieldCheck size={32} />
</div>
<div>
<h4 className="text-xl font-black mb-2">Liaison de Données</h4>
<p className="text-sm text-indigo-100/90 leading-relaxed font-medium">
Ces identifiants sont strictement personnels. Ils permettent à l'application de savoir exactement dans quelle Google Sheet envoyer vos rapports.
</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-3">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest flex items-center gap-2">
<Hash size={14} /> ID de la Google Sheet
</label>
<input
type="text"
value={localSettings.sheetId}
onChange={(e) => setLocalSettings({...localSettings, sheetId: e.target.value})}
placeholder="Ex: 1bc-DEFgH..."
className="w-full border-2 border-slate-100 rounded-2xl p-5 text-slate-700 font-mono text-sm outline-none bg-slate-50 focus:border-indigo-600 transition-all"
/>
</div>
<div className="space-y-3">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest flex items-center gap-2">
<Globe size={14} /> URL du Script (Web App)
</label>
<input
type="url"
value={localSettings.webhookUrl}
onChange={(e) => setLocalSettings({...localSettings, webhookUrl: e.target.value})}
placeholder="https://script.google.com/macros/s/..."
className="w-full border-2 border-slate-100 rounded-2xl p-5 text-slate-700 font-mono text-sm outline-none bg-slate-50 focus:border-indigo-600 transition-all"
/>
</div>
</div>
<div className="space-y-5 pt-4">
<div className="flex items-center justify-between">
<h4 className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Script Apps Script Requis</h4>
<button onClick={copyScript} className={`flex items-center gap-2 px-5 py-2.5 rounded-xl text-xs font-black transition-all shadow-sm ${copied ? 'bg-emerald-500 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'}`}>
{copied ? <CheckCircle2 size={16} /> : <Copy size={16} />} {copied ? 'Copié !' : 'Copier le script'}
</button>
</div>
<div className="bg-slate-900 rounded-[1.5rem] p-6 relative group">
<pre className="text-[10px] text-emerald-400 font-mono overflow-x-auto max-h-56 leading-relaxed whitespace-pre-wrap">
{googleScriptCode}
</pre>
</div>
</div>
</div>
)}
{activeTab === 'info' && (
<div className="space-y-8 animate-in slide-in-from-left-4 duration-300">
<div className="p-10 bg-slate-50 rounded-[2.5rem] border border-slate-100 space-y-6">
<div className="flex items-center gap-4 text-indigo-600">
<Database size={32} />
<h4 className="text-2xl font-black tracking-tight">Souveraineté Totale</h4>
</div>
<p className="text-sm text-slate-600 leading-relaxed font-bold">
Votre espace contient tout votre ADN professionnel : modèles, audits et réglages cloud.
L'application ne stocke rien en ligne de son côté.
</p>
<div className="space-y-4 pt-4 border-t border-slate-200">
<div className="flex items-start gap-3">
<div className="bg-amber-100 text-amber-600 p-2 rounded-lg mt-1"><AlertTriangle size={16} /></div>
<p className="text-xs text-slate-500 font-medium leading-relaxed">
Chaque fois que vous changez d'ordinateur ou que vous videz votre navigateur, vous devez importer votre fichier <code>.auditpro</code>.
</p>
</div>
<div className="flex items-start gap-3">
<div className="bg-indigo-100 text-indigo-600 p-2 rounded-lg mt-1"><Download size={16} /></div>
<p className="text-xs text-slate-500 font-medium leading-relaxed">
Pensez à sauvegarder votre "Clé" régulièrement pour ne perdre aucun audit récent.
</p>
</div>
</div>
</div>
</div>
)}
<div className="mt-12 pt-10 border-t border-slate-100 flex justify-end">
<button
onClick={() => onSave(localSettings)}
className="bg-indigo-600 text-white px-12 py-5 rounded-2xl hover:bg-indigo-700 shadow-2xl shadow-indigo-100 font-black transition-all flex items-center gap-3 active:scale-95"
>
<Save size={20} /> ENREGISTRER L'ESPACE
</button>
</div>
</div>
</div>
</div>
);
};
export default SettingsPanel;
|