Download index.html from NaderY/idph: direct link, hf CLI and curl.
- Browser
- Download file 27.9 kB
-
https://huggingface.co/spaces/NaderY/idph/resolve/main/index.html
- Command line
-
hf download hf://spaces/NaderY/idph/index.html
-
curl -L -o index.html https://huggingface.co/spaces/NaderY/idph/resolve/main/index.html
27.9 kB
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Workflow de Validation Multi-Niveaux</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .workflow-step { | |
| position: relative; | |
| } | |
| .workflow-step:not(:last-child):after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| right: -20px; | |
| width: 40px; | |
| height: 2px; | |
| background-color: #d1d5db; | |
| } | |
| .workflow-connector { | |
| height: 30px; | |
| width: 2px; | |
| background-color: #d1d5db; | |
| margin: 0 auto; | |
| } | |
| .animate-pulse { | |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { | |
| opacity: 1; | |
| } | |
| 50% { | |
| opacity: 0.5; | |
| } | |
| } | |
| .custom-scrollbar::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-thumb { | |
| background: #888; | |
| border-radius: 4px; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-thumb:hover { | |
| background: #555; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 font-sans"> | |
| <div class="flex h-screen overflow-hidden"> | |
| <!-- Sidebar --> | |
| <div class="hidden md:flex md:flex-shrink-0"> | |
| <div class="flex flex-col w-64 bg-indigo-700 text-white"> | |
| <div class="flex items-center justify-center h-16 px-4 bg-indigo-800"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-tasks text-xl mr-2"></i> | |
| <span class="text-xl font-semibold">WorkflowPro</span> | |
| </div> | |
| </div> | |
| <div class="flex flex-col flex-grow px-4 py-4 overflow-y-auto"> | |
| <div class="space-y-1"> | |
| <a href="#" class="flex items-center px-2 py-3 text-sm font-medium rounded-md bg-indigo-800 text-white"> | |
| <i class="fas fa-tachometer-alt mr-3"></i> | |
| Tableau de bord | |
| </a> | |
| <a href="#" class="flex items-center px-2 py-3 text-sm font-medium rounded-md text-indigo-200 hover:text-white hover:bg-indigo-600"> | |
| <i class="fas fa-inbox mr-3"></i> | |
| Mes demandes | |
| </a> | |
| <a href="#" class="flex items-center px-2 py-3 text-sm font-medium rounded-md text-indigo-200 hover:text-white hover:bg-indigo-600"> | |
| <i class="fas fa-check-circle mr-3"></i> | |
| Validations | |
| </a> | |
| <a href="#" class="flex items-center px-2 py-3 text-sm font-medium rounded-md text-indigo-200 hover:text-white hover:bg-indigo-600"> | |
| <i class="fas fa-chart-bar mr-3"></i> | |
| Statistiques | |
| </a> | |
| <a href="#" class="flex items-center px-2 py-3 text-sm font-medium rounded-md text-indigo-200 hover:text-white hover:bg-indigo-600"> | |
| <i class="fas fa-cog mr-3"></i> | |
| Paramètres | |
| </a> | |
| </div> | |
| <div class="mt-auto mb-4"> | |
| <div class="p-4 bg-indigo-800 rounded-lg"> | |
| <p class="text-sm text-indigo-200">Besoin d'aide?</p> | |
| <a href="#" class="text-white font-medium hover:underline">Contactez le support</a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main content --> | |
| <div class="flex flex-col flex-1 overflow-hidden"> | |
| <!-- Top navigation --> | |
| <div class="flex items-center justify-between h-16 px-4 bg-white border-b border-gray-200"> | |
| <div class="flex items-center"> | |
| <button class="md:hidden text-gray-500 focus:outline-none"> | |
| <i class="fas fa-bars"></i> | |
| </button> | |
| <h1 class="ml-4 text-xl font-semibold text-gray-800">Tableau de bord</h1> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <button class="p-1 text-gray-500 rounded-full hover:bg-gray-100 focus:outline-none"> | |
| <i class="fas fa-bell"></i> | |
| </button> | |
| <div class="relative"> | |
| <button id="user-menu" class="flex items-center text-sm text-gray-700 rounded-full focus:outline-none"> | |
| <img class="w-8 h-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="User photo"> | |
| <span class="ml-2 hidden md:inline">Jean Dupont</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main content area --> | |
| <div class="flex-1 overflow-auto custom-scrollbar p-4 md:p-6"> | |
| <!-- Stats cards --> | |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6"> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-blue-100 text-blue-600"> | |
| <i class="fas fa-file-alt"></i> | |
| </div> | |
| <div class="ml-4"> | |
| <p class="text-sm font-medium text-gray-500">Demandes en cours</p> | |
| <p class="text-xl font-semibold text-gray-800">24</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-green-100 text-green-600"> | |
| <i class="fas fa-check-circle"></i> | |
| </div> | |
| <div class="ml-4"> | |
| <p class="text-sm font-medium text-gray-500">Demandes approuvées</p> | |
| <p class="text-xl font-semibold text-gray-800">42</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-red-100 text-red-600"> | |
| <i class="fas fa-times-circle"></i> | |
| </div> | |
| <div class="ml-4"> | |
| <p class="text-sm font-medium text-gray-500">Demandes rejetées</p> | |
| <p class="text-xl font-semibold text-gray-800">8</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-yellow-100 text-yellow-600"> | |
| <i class="fas fa-clock"></i> | |
| </div> | |
| <div class="ml-4"> | |
| <p class="text-sm font-medium text-gray-500">En attente de moi</p> | |
| <p class="text-xl font-semibold text-gray-800">5</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Action buttons and recent requests --> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Left column - Create request --> | |
| <div class="bg-white rounded-lg shadow overflow-hidden"> | |
| <div class="px-4 py-5 sm:px-6 bg-gray-50 border-b border-gray-200"> | |
| <h3 class="text-lg font-medium text-gray-900">Créer une nouvelle demande</h3> | |
| </div> | |
| <div class="p-4"> | |
| <form id="request-form"> | |
| <div class="mb-4"> | |
| <label for="request-type" class="block text-sm font-medium text-gray-700 mb-1">Type de demande</label> | |
| <select id="request-type" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> | |
| <option value="">Sélectionnez un type</option> | |
| <option value="conges">Demande de congés</option> | |
| <option value="achat">Demande d'achat</option> | |
| <option value="formation">Demande de formation</option> | |
| <option value="remboursement">Demande de remboursement</option> | |
| </select> | |
| </div> | |
| <div class="mb-4"> | |
| <label for="request-title" class="block text-sm font-medium text-gray-700 mb-1">Titre</label> | |
| <input type="text" id="request-title" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> | |
| </div> | |
| <div class="mb-4"> | |
| <label for="request-description" class="block text-sm font-medium text-gray-700 mb-1">Description</label> | |
| <textarea id="request-description" rows="3" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"></textarea> | |
| </div> | |
| <div class="mb-4"> | |
| <label for="request-amount" class="block text-sm font-medium text-gray-700 mb-1">Montant (si applicable)</label> | |
| <input type="number" id="request-amount" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> | |
| </div> | |
| <div class="mb-4"> | |
| <label for="request-date" class="block text-sm font-medium text-gray-700 mb-1">Date (si applicable)</label> | |
| <input type="date" id="request-date" class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> | |
| </div> | |
| <div class="flex justify-end"> | |
| <button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | |
| Soumettre la demande | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- Middle column - Recent requests --> | |
| <div class="bg-white rounded-lg shadow overflow-hidden"> | |
| <div class="px-4 py-5 sm:px-6 bg-gray-50 border-b border-gray-200"> | |
| <h3 class="text-lg font-medium text-gray-900">Mes demandes récentes</h3> | |
| </div> | |
| <div class="overflow-y-auto" style="max-height: 500px;"> | |
| <ul class="divide-y divide-gray-200" id="recent-requests"> | |
| <!-- Requests will be added here by JavaScript --> | |
| </ul> | |
| </div> | |
| </div> | |
| <!-- Right column - Approval workflow --> | |
| <div class="bg-white rounded-lg shadow overflow-hidden"> | |
| <div class="px-4 py-5 sm:px-6 bg-gray-50 border-b border-gray-200"> | |
| <h3 class="text-lg font-medium text-gray-900">Workflow de validation</h3> | |
| </div> | |
| <div class="p-4"> | |
| <div class="flex justify-between mb-6" id="workflow-steps"> | |
| <!-- Workflow steps will be added here by JavaScript --> | |
| </div> | |
| <div class="workflow-connector"></div> | |
| <div class="flex justify-between mt-6" id="approvers-list"> | |
| <!-- Approvers will be added here by JavaScript --> | |
| </div> | |
| </div> | |
| <div class="px-4 py-4 border-t border-gray-200"> | |
| <h4 class="text-md font-medium text-gray-900 mb-2">Détails de la demande</h4> | |
| <div class="bg-gray-50 p-3 rounded-lg"> | |
| <p class="text-sm text-gray-600" id="workflow-details">Sélectionnez une demande pour voir son workflow de validation.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Sample data | |
| const requests = [ | |
| { | |
| id: 1, | |
| type: 'conges', | |
| title: 'Demande de congés annuels', | |
| description: 'Je souhaite prendre 2 semaines de congés en août pour des vacances familiales.', | |
| date: '2023-08-15', | |
| status: 'pending', | |
| created: '2023-06-10', | |
| workflow: [ | |
| { level: 1, approver: 'Responsable d\'équipe', status: 'approved', date: '2023-06-12' }, | |
| { level: 2, approver: 'DRH', status: 'pending', date: null }, | |
| { level: 3, approver: 'Directeur Général', status: 'pending', date: null } | |
| ] | |
| }, | |
| { | |
| id: 2, | |
| type: 'achat', | |
| title: 'Achat nouveau matériel', | |
| description: 'Besoin d\'un nouvel ordinateur portable pour le nouveau collaborateur.', | |
| amount: 1200, | |
| status: 'approved', | |
| created: '2023-05-28', | |
| workflow: [ | |
| { level: 1, approver: 'Responsable d\'équipe', status: 'approved', date: '2023-05-29' }, | |
| { level: 2, approver: 'Responsable financier', status: 'approved', date: '2023-05-30' } | |
| ] | |
| }, | |
| { | |
| id: 3, | |
| type: 'formation', | |
| title: 'Formation React Avancé', | |
| description: 'Participation à une formation React avancé de 3 jours pour améliorer mes compétences.', | |
| amount: 800, | |
| status: 'rejected', | |
| created: '2023-06-05', | |
| workflow: [ | |
| { level: 1, approver: 'Responsable d\'équipe', status: 'approved', date: '2023-06-06' }, | |
| { level: 2, approver: 'Responsable formation', status: 'rejected', date: '2023-06-07', comment: 'Budget formation épuisé pour ce trimestre.' } | |
| ] | |
| }, | |
| { | |
| id: 4, | |
| type: 'remboursement', | |
| title: 'Remboursement frais de transport', | |
| description: 'Remboursement des frais de transport pour le déplacement client du 20 mai.', | |
| amount: 145.50, | |
| status: 'pending', | |
| created: '2023-06-08', | |
| workflow: [ | |
| { level: 1, approver: 'Responsable d\'équipe', status: 'pending', date: null }, | |
| { level: 2, approver: 'Comptabilité', status: 'pending', date: null } | |
| ] | |
| } | |
| ]; | |
| // DOM elements | |
| const recentRequestsList = document.getElementById('recent-requests'); | |
| const workflowSteps = document.getElementById('workflow-steps'); | |
| const approversList = document.getElementById('approvers-list'); | |
| const workflowDetails = document.getElementById('workflow-details'); | |
| const requestForm = document.getElementById('request-form'); | |
| // Render recent requests | |
| function renderRecentRequests() { | |
| recentRequestsList.innerHTML = ''; | |
| // Sort requests by creation date (newest first) | |
| const sortedRequests = [...requests].sort((a, b) => new Date(b.created) - new Date(a.created)); | |
| sortedRequests.forEach(request => { | |
| const listItem = document.createElement('li'); | |
| // Determine status color and icon | |
| let statusColor, statusIcon; | |
| if (request.status === 'approved') { | |
| statusColor = 'bg-green-100 text-green-800'; | |
| statusIcon = 'fa-check-circle'; | |
| } else if (request.status === 'rejected') { | |
| statusColor = 'bg-red-100 text-red-800'; | |
| statusIcon = 'fa-times-circle'; | |
| } else { | |
| statusColor = 'bg-yellow-100 text-yellow-800'; | |
| statusIcon = 'fa-clock'; | |
| } | |
| // Format date | |
| const createdDate = new Date(request.created); | |
| const formattedDate = createdDate.toLocaleDateString('fr-FR'); | |
| listItem.className = 'px-4 py-4 hover:bg-gray-50 cursor-pointer'; | |
| listItem.innerHTML = ` | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center"> | |
| <div class="min-w-0"> | |
| <p class="text-sm font-medium text-indigo-600 truncate">${request.title}</p> | |
| <p class="text-sm text-gray-500 truncate">${request.description.substring(0, 50)}...</p> | |
| </div> | |
| </div> | |
| <div class="ml-2 flex-shrink-0 flex"> | |
| <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${statusColor}"> | |
| <i class="fas ${statusIcon} mr-1"></i> | |
| ${request.status === 'approved' ? 'Approuvé' : request.status === 'rejected' ? 'Rejeté' : 'En attente'} | |
| </span> | |
| </div> | |
| </div> | |
| <div class="mt-2 flex justify-between"> | |
| <div class="flex"> | |
| <p class="flex items-center text-sm text-gray-500"> | |
| <i class="fas fa-calendar-alt mr-1.5"></i> | |
| ${formattedDate} | |
| </p> | |
| </div> | |
| <div class="flex"> | |
| <button class="text-sm text-indigo-600 hover:text-indigo-900 view-workflow" data-id="${request.id}"> | |
| Voir workflow | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| recentRequestsList.appendChild(listItem); | |
| }); | |
| // Add event listeners to view workflow buttons | |
| document.querySelectorAll('.view-workflow').forEach(button => { | |
| button.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const requestId = parseInt(button.getAttribute('data-id')); | |
| const request = requests.find(r => r.id === requestId); | |
| renderWorkflow(request); | |
| }); | |
| }); | |
| // Add click event to list items to show details | |
| document.querySelectorAll('#recent-requests li').forEach((item, index) => { | |
| item.addEventListener('click', () => { | |
| renderWorkflow(sortedRequests[index]); | |
| }); | |
| }); | |
| } | |
| // Render workflow for a specific request | |
| function renderWorkflow(request) { | |
| // Clear previous workflow | |
| workflowSteps.innerHTML = ''; | |
| approversList.innerHTML = ''; | |
| // Render workflow steps | |
| request.workflow.forEach(step => { | |
| const stepElement = document.createElement('div'); | |
| stepElement.className = 'workflow-step'; | |
| // Determine step status color and icon | |
| let stepStatusColor, stepStatusIcon; | |
| if (step.status === 'approved') { | |
| stepStatusColor = 'bg-green-500'; | |
| stepStatusIcon = 'fa-check'; | |
| } else if (step.status === 'rejected') { | |
| stepStatusColor = 'bg-red-500'; | |
| stepStatusIcon = 'fa-times'; | |
| } else { | |
| stepStatusColor = 'bg-gray-300 animate-pulse'; | |
| stepStatusIcon = 'fa-clock'; | |
| } | |
| stepElement.innerHTML = ` | |
| <div class="flex flex-col items-center"> | |
| <div class="w-10 h-10 rounded-full ${stepStatusColor} flex items-center justify-center text-white"> | |
| <i class="fas ${stepStatusIcon}"></i> | |
| </div> | |
| <p class="mt-2 text-xs font-medium text-gray-700">Étape ${step.level}</p> | |
| </div> | |
| `; | |
| workflowSteps.appendChild(stepElement); | |
| }); | |
| // Render approvers | |
| request.workflow.forEach(step => { | |
| const approverElement = document.createElement('div'); | |
| approverElement.className = 'text-center'; | |
| approverElement.innerHTML = ` | |
| <p class="text-sm font-medium text-gray-900">${step.approver}</p> | |
| <p class="text-xs text-gray-500"> | |
| ${step.date ? new Date(step.date).toLocaleDateString('fr-FR') : 'En attente'} | |
| </p> | |
| ${step.comment ? `<p class="text-xs text-red-500 mt-1">${step.comment}</p>` : ''} | |
| `; | |
| approversList.appendChild(approverElement); | |
| }); | |
| // Render request details | |
| let detailsHtml = ` | |
| <p><strong>Type:</strong> ${getRequestTypeName(request.type)}</p> | |
| <p><strong>Titre:</strong> ${request.title}</p> | |
| <p><strong>Description:</strong> ${request.description}</p> | |
| `; | |
| if (request.amount) { | |
| detailsHtml += `<p><strong>Montant:</strong> ${request.amount} €</p>`; | |
| } | |
| if (request.date) { | |
| detailsHtml += `<p><strong>Date:</strong> ${new Date(request.date).toLocaleDateString('fr-FR')}</p>`; | |
| } | |
| detailsHtml += `<p><strong>Statut:</strong> ${request.status === 'approved' ? 'Approuvé' : request.status === 'rejected' ? 'Rejeté' : 'En attente'}</p>`; | |
| workflowDetails.innerHTML = detailsHtml; | |
| } | |
| // Helper function to get request type name | |
| function getRequestTypeName(type) { | |
| const types = { | |
| 'conges': 'Demande de congés', | |
| 'achat': 'Demande d\'achat', | |
| 'formation': 'Demande de formation', | |
| 'remboursement': 'Demande de remboursement' | |
| }; | |
| return types[type] || type; | |
| } | |
| // Form submission handler | |
| requestForm.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const type = document.getElementById('request-type').value; | |
| const title = document.getElementById('request-title').value; | |
| const description = document.getElementById('request-description').value; | |
| const amount = document.getElementById('request-amount').value; | |
| const date = document.getElementById('request-date').value; | |
| if (!type || !title || !description) { | |
| alert('Veuillez remplir tous les champs obligatoires.'); | |
| return; | |
| } | |
| // Create new request | |
| const newRequest = { | |
| id: requests.length + 1, | |
| type, | |
| title, | |
| description, | |
| status: 'pending', | |
| created: new Date().toISOString().split('T')[0], | |
| workflow: [ | |
| { level: 1, approver: 'Responsable d\'équipe', status: 'pending', date: null }, | |
| { level: 2, approver: type === 'conges' ? 'DRH' : 'Responsable financier', status: 'pending', date: null } | |
| ] | |
| }; | |
| if (amount) newRequest.amount = parseFloat(amount); | |
| if (date) newRequest.date = date; | |
| requests.unshift(newRequest); | |
| // Reset form | |
| requestForm.reset(); | |
| // Show success message | |
| alert('Votre demande a été soumise avec succès!'); | |
| // Refresh the list | |
| renderRecentRequests(); | |
| // Show the workflow for the new request | |
| renderWorkflow(newRequest); | |
| }); | |
| // Initialize the app | |
| document.addEventListener('DOMContentLoaded', () => { | |
| renderRecentRequests(); | |
| // Show the first request's workflow by default | |
| if (requests.length > 0) { | |
| renderWorkflow(requests[0]); | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=NaderY/idph" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |