anycoder-e12863d4 / index.html
danillo10's picture
Upload folder using huggingface_hub
1629e9b verified
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gerenciamento de Clientes - Laravel CRUD Simulation</title>
<!-- FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300,400,500,600,700&display=swap" rel="stylesheet">
<style>
:root {
--primary: #4f46e5; /* Laravel Indigo */
--primary-hover: #4338ca;
--bg-body: #f3f4f6;
--bg-card: #ffffff;
--text-main: #111827;
--text-secondary: #6b7280;
--border: #e5e7eb;
--danger: #ef4444;
--success: #10b981;
--shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--radius: 0.5rem;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Inter', sans-serif;
}
body {
background-color: var(--bg-body);
color: var(--text-main);
line-height: 1.5;
min-height: 100vh;
}
/* --- Header --- */
header {
background: var(--bg-card);
border-bottom: 1px solid var(--border);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 10;
}
.brand {
font-weight: 700;
font-size: 1.25rem;
color: var(--primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.anycoder-link {
text-decoration: none;
color: var(--text-secondary);
font-size: 0.875rem;
font-weight: 500;
transition: color 0.2s;
border: 1px solid var(--border);
padding: 0.25rem 0.75rem;
border-radius: var(--radius);
}
.anycoder-link:hover {
color: var(--primary);
border-color: var(--primary);
}
/* --- Main Layout --- */
.container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
.card {
background: var(--bg-card);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
}
/* --- Toolbar --- */
.toolbar {
padding: 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border);
flex-wrap: wrap;
gap: 1rem;
}
.toolbar-title h2 {
font-size: 1.5rem;
font-weight: 600;
}
.toolbar-title p {
color: var(--text-secondary);
font-size: 0.875rem;
}
.actions {
display: flex;
gap: 0.75rem;
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1rem;
font-size: 0.875rem;
font-weight: 500;
border-radius: var(--radius);
border: none;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background-color: var(--primary);
color: white;
}
.btn-primary:hover {
background-color: var(--primary-hover);
}
.btn-outline {
background-color: transparent;
border: 1px solid var(--border);
color: var(--text-main);
}
.btn-outline:hover {
background-color: #f9fafb;
border-color: var(--text-secondary);
}
.search-box {
position: relative;
width: 250px;
}
.search-box input {
width: 100%;
padding: 0.625rem 1rem 0.625rem 2.5rem;
border: 1px solid var(--border);
border-radius: var(--radius);
outline: none;
font-size: 0.875rem;
}
.search-box input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
.search-box i {
position: absolute;
left: 1rem;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
}
/* --- Table --- */
.table-container {
width: 100%;
overflow-x: auto;
}
table {
width: 100%;
text-align: left;
border-collapse: collapse;
}
th {
background-color: #f9fafb;
padding: 1rem 1.5rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-secondary);
}
td {
padding: 1rem 1.5rem;
border-top: 1px solid var(--border);
font-size: 0.875rem;
}
tr:last-child td {
border-bottom: 1px solid var(--border);
}
tr:hover td {
background-color: #f9fafb;
}
.status-badge {
display: inline-block;
padding: 0.125rem 0.5rem;
font-size: 0.75rem;
font-weight: 500;
border-radius: 9999px;
}
.status-active { background-color: #d1fae5; color: #065f46; }
.status-inactive { background-color: #fee2e2; color: #991b1b; }
.action-btn {
background: none;
border: none;
cursor: pointer;
padding: 0.25rem;
color: var(--text-secondary);
transition: color 0.2s;
}
.action-btn:hover { color: var(--primary); }
.action-btn.delete:hover { color: var(--danger); }
/* --- Modal --- */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 50;
opacity: 0;
visibility: hidden;
transition: all 0.3s;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background: var(--bg-card);
width: 100%;
max-width: 500px;
border-radius: var(--radius);
box-shadow: var(--shadow-lg);
transform: translateY(20px);
transition: transform 0.3s;
}
.modal-overlay.active .modal {
transform: translateY(0);
}
.modal-header {
padding: 1.5rem;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h3 {
font-size: 1.125rem;
font-weight: 600;
}
.close-modal {
background: none;
border: none;
font-size: 1.25rem;
cursor: pointer;
color: var(--text-secondary);
}
.modal-body {
padding: 1.5rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-size: 0.875rem;
font-weight: 500;
color: var(--text-main);
}
.form-control {
width: 100%;
padding: 0.625rem;
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 0.875rem;
}
.form-control:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
.modal-footer {
padding: 1.5rem;
border-top: 1px solid var(--border);
background: #f9fafb;
display: flex;
justify-content: flex-end;
gap: 0.75rem;
}
/* --- Code Block Section --- */
.code-preview {
margin-top: 3rem;
background: #1e1e1e;
color: #d4d4d4;
border-radius: var(--radius);
padding: 2rem;
overflow-x: auto;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.85rem;
position: relative;
}
.code-preview::before {
content: "Laravel Best Practices (Backend Logic Example)";
position: absolute;
top: -1.5rem;
left: 1rem;
background: var(--bg-card);
color: var(--text-main);
padding: 0.25rem 0.5rem;
border-radius: var(--radius);
font-size: 0.75rem;
font-weight: 600;
font-family: 'Inter', sans-serif;
}
.code-line { margin-bottom: 0.25rem; }
.kw { color: #569cd6; } /* Keyword */
.str { color: #ce9178; } /* String */
.cls { color: #4ec9b0; } /* Class */
.func { color: #dcdcaa; } /* Function */
.comment { color: #6a9955; }
/* --- Toast Notification --- */
.toast-container {
position: fixed;
bottom: 2rem;
right: 2rem;
z-index: 100;
}
.toast {
background: var(--bg-card);
padding: 1rem 1.5rem;
border-radius: var(--radius);
box-shadow: var(--shadow-lg);
border-left: 4px solid var(--primary);
margin-top: 0.5rem;
display: flex;
align-items: center;
gap: 0.75rem;
min-width: 300px;
transform: translateX(100%);
opacity: 0;
transition: all 0.3s;
}
.toast.show {
transform: translateX(0);
opacity: 1;
}
.toast.success { border-left-color: var(--success); }
.toast.error { border-left-color: var(--danger); }
/* Responsive */
@media (max-width: 768px) {
.toolbar { flex-direction: column; align-items: stretch; }
.toolbar-title { margin-bottom: 1rem; }
.actions { justify-content: space-between; }
.search-box { width: 100%; }
}
</style>
</head>
<body>
<!-- Header -->
<header>
<div class="brand">
<i class="fa-brands fa-laravel"></i>
ClientManager
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link">
Built with AnyCoder
</a>
</header>
<!-- Main Content -->
<div class="container">
<!-- Dashboard Card -->
<div class="card">
<!-- Toolbar -->
<div class="toolbar">
<div class="toolbar-title">
<h2>Clientes</h2>
<p>Gerencie seus clientes com CRUD completo.</p>
</div>
<div class="actions">
<div class="search-box">
<i class="fa-solid fa-search"></i>
<input type="text" id="searchInput" placeholder="Buscar cliente...">
</div>
<button class="btn btn-primary" onclick="openModal('create')">
<i class="fa-solid fa-plus"></i> Novo Cliente
</button>
</div>
</div>
<!-- Table -->
<div class="table-container">
<table>
<thead>
<tr>
<th>ID</th>
<th>Nome</th>
<th>Email</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="clientTableBody">
<!-- Data injected by JS -->
</tbody>
</table>
</div>
</div>
<!-- Code Preview Section (Simulating Backend) -->
<div class="code-preview">
<div class="code-line"><span class="kw">public</span> <span class="func">function</span> <span class="func">store</span>(Request $request) {</div>
<div class="code-line">&nbsp;&nbsp;<span class="comment">// 1. Validation (Best Practice)</span></div>
<div class="code-line">&nbsp;&nbsp;$validated = $request->validate([</div>
<div class="code-line">&nbsp;&nbsp;&nbsp;&nbsp;<span class="str">'name'</span> => <span class="str">'required|string|max:255'</span>,</div>
<div class="code-line">&nbsp;&nbsp;&nbsp;&nbsp;<span class="str">'email'</span> => <span class="str">'required|email|unique:clients'</span>,</div>
<div class="code-line">&nbsp;&nbsp;]);</div>
<div class="code-line">&nbsp;&nbsp;<span class="comment">// 2. Creation</span></div>
<div class="code-line">&nbsp;&nbsp;<span class="cls">Client</span>::create($validated);</div>
<div class="code-line">&nbsp;&nbsp;<span class="kw">return</span> redirect()->route(<span class="str">'clients.index'</span>)->with(<span class="str">'success'</span>, <span class="str">'Client created successfully.'</span>);</div>
<div class="code-line>}</div>
</div>
</div>
<!-- Modal Form -->
<div class="modal-overlay" id="clientModal">
<div class="modal">
<div class="modal-header">
<h3 id="modalTitle">Novo Cliente</h3>
<button class="close-modal" onclick="closeModal()">
<i class="fa-solid fa-times"></i>
</button>
</div>
<div class="modal-body">
<form id="clientForm">
<div class="form-group">
<label for="name">Nome Completo</label>
<input type="text" class="form-control" id="name" required placeholder="Ex: João Silva">
</div>
<div class="form-group">
<label for="email">Email Corporativo</label>
<input type="email" class="form-control" id="email" required placeholder="Ex: joao@empresa.com">
</div>
<div class="form-group">
<label for="status">Status</label>
<select class="form-control" id="status">
<option value="active">Ativo</option>
<option value="inactive">Inativo</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-outline" onclick="closeModal()">Cancelar</button>
<button class="btn btn-primary" onclick="saveClient()">Salvar</button>
</div>
</div>
</div>
<!-- Toast Container -->
<div class="toast-container" id="toastContainer"></div>
<script>
// --- Mock Data ---
let clients = [
{ id: 1, name: "Ana Souza", email: "ana.souza@dev.com", status: "active" },
{ id: 2, name: "Carlos Pereira", email: "carlos.p@tech.com", status: "inactive" },
{ id: 3, name: "Maria Oliveira", email: "maria.o@design.com", status: "active" },
];
// --- State ---
let currentMode = 'create'; // 'create' or 'edit'
let editingId = null;
// --- Initialization ---
document.addEventListener('DOMContentLoaded', () => {
renderTable();
});
// --- Render Functions ---
function renderTable(data = clients) {
const tbody = document.getElementById('clientTableBody');
tbody.innerHTML = '';
if (data.length === 0) {
tbody.innerHTML = `<tr><td colspan="5" style="text-align:center; color: var(--text-secondary)">Nenhum cliente encontrado.</td></tr>`;
return;
}
data.forEach(client => {
const tr = document.createElement('tr');
const statusBadge = client.status === 'active'
? '<span class="status-badge status-active">Ativo</span>'
: '<span class="status-badge status-inactive">Inativo</span>';
tr.innerHTML = `
<td>${client.id}</td>
<td><strong>${client.name}</strong></td>
<td>${client.email}</td>
<td>${statusBadge}</td>
<td>
<button class="action-btn" onclick="openModal('edit', ${client.id})" title="Editar">
<i class="fa-solid fa-pen-to-square"></i>
</button>
<button class="action-btn delete" onclick="deleteClient(${client.id})" title="Excluir">
<i class="fa-solid fa-trash"></i>
</button>
</td>
`;
tbody.appendChild(tr);
});
}
// --- Modal Logic ---
function openModal(mode, id = null) {
currentMode = mode;
const modal = document.getElementById('clientModal');
const title = document.getElementById('modalTitle');
const form = document.getElementById('clientForm');
modal.classList.add('active');
if (mode === 'create') {
title.innerText = 'Novo Cliente';
form.reset();
editingId = null;
} else if (mode === 'edit') {
title.innerText = 'Editar Cliente';
editingId = id;
const client = clients.find(c => c.id === id);
if (client) {
document.getElementById('name').value = client.name;
document.getElementById('email').value = client.email;
document.getElementById('status').value = client.status;
}
}
}
function closeModal() {
document.getElementById('clientModal').classList.remove('active');
}
// --- CRUD Operations ---
function saveClient() {
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const status = document.getElementById('status').value;
if (!name || !email) {
showToast('Preencha todos os campos obrigatórios.', 'error');
return;
}
if (currentMode === 'create') {
// Create Logic
const newId = clients.length > 0 ? Math.max(...clients.map(c => c.id)) + 1 : 1;
clients.push({ id: newId, name, email, status });
showToast('Cliente criado com sucesso!', 'success');
} else {
// Update Logic
const index = clients.findIndex(c => c.id === editingId);
if (index !== -1) {
clients[index] = { ...clients[index], name, email, status };
showToast('Cliente atualizado com sucesso!', 'success');
}
}
closeModal();
renderTable();
}
function deleteClient(id) {
if(confirm('Tem certeza que deseja excluir este cliente?')) {
clients = clients.filter(c => c.id !== id);
renderTable();
showToast('Cliente removido.', 'success');
}
}
// --- Search Logic ---
document.getElementById('searchInput').addEventListener('input', (e) => {
const term = e.target.value.toLowerCase();
const filtered = clients.filter(c =>
c.name.toLowerCase().includes(term) ||
c.email.toLowerCase().includes(term)
);
renderTable(filtered);
});
// --- Toast Notification ---
function showToast(message, type = 'success') {
const container = document.getElementById('toastContainer');
const toast = document.createElement('div');
toast.className = `toast ${type}`;
const icon = type === 'success' ? '<i class="fa-solid fa-check-circle"></i>' : '<i class="fa-solid fa-exclamation-circle"></i>';
toast.innerHTML = `
${icon}
<span>${message}</span>
`;
container.appendChild(toast);
// Trigger animation
setTimeout(() => toast.classList.add('show'), 10);
// Remove
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
</script>
</body>
</html>