|
|
| import React, { useState } from 'react'; |
| import { AppSettings } from '../types'; |
| import { |
| Save, |
| Globe, |
| User, |
| Info, |
| Database, |
| FileSpreadsheet, |
| Copy, |
| CheckCircle2, |
| Lock, |
| ExternalLink, |
| ShieldCheck, |
| Hash, |
| Download, |
| |
| 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; |
|
|