new testarena
This commit is contained in:
116
asf-cloud-server/testarena_1/app/templates/admin/dashboard.html
Normal file
116
asf-cloud-server/testarena_1/app/templates/admin/dashboard.html
Normal file
@@ -0,0 +1,116 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Admin Dashboard - ASF TestArena{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="admin-container">
|
||||
<div class="admin-header">
|
||||
<h2>User Management</h2>
|
||||
<button class="btn btn-primary" onclick="openModal('createUserModal')">+ Create User</button>
|
||||
</div>
|
||||
|
||||
<table class="user-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Role</th>
|
||||
<th>Created At</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>{{ user.id }}</td>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>
|
||||
{% if user.is_admin %}
|
||||
<span class="badge badge-admin">Admin</span>
|
||||
{% else %}
|
||||
<span class="badge badge-user">User</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ user.created_at.strftime('%Y-%m-%d %H:%M') }}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" onclick="openResetPasswordModal({{ user.id }}, '{{ user.username }}')">Reset Password</button>
|
||||
{% if user.id != current_user.id %}
|
||||
<form method="POST" action="{{ url_for('admin.delete_user', user_id=user.id) }}" style="display: inline;">
|
||||
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Delete user {{ user.username }}?')">Delete</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Create User Modal -->
|
||||
<div id="createUserModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>Create New User</h3>
|
||||
<button class="close-btn" onclick="closeModal('createUserModal')">×</button>
|
||||
</div>
|
||||
<form method="POST" action="{{ url_for('admin.create_user') }}">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="is_admin">
|
||||
Admin User
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Create User</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reset Password Modal -->
|
||||
<div id="resetPasswordModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>Reset Password</h3>
|
||||
<button class="close-btn" onclick="closeModal('resetPasswordModal')">×</button>
|
||||
</div>
|
||||
<form id="resetPasswordForm" method="POST">
|
||||
<p>Reset password for: <strong id="resetUsername"></strong></p>
|
||||
<div class="form-group">
|
||||
<label for="new_password">New Password</label>
|
||||
<input type="password" id="new_password" name="new_password" class="form-control" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Reset Password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openModal(modalId) {
|
||||
document.getElementById(modalId).classList.add('active');
|
||||
}
|
||||
|
||||
function closeModal(modalId) {
|
||||
document.getElementById(modalId).classList.remove('active');
|
||||
}
|
||||
|
||||
function openResetPasswordModal(userId, username) {
|
||||
document.getElementById('resetUsername').textContent = username;
|
||||
document.getElementById('resetPasswordForm').action = `/admin/users/${userId}/reset-password`;
|
||||
openModal('resetPasswordModal');
|
||||
}
|
||||
|
||||
// Close modal when clicking outside
|
||||
window.onclick = function(event) {
|
||||
if (event.target.classList.contains('modal')) {
|
||||
event.target.classList.remove('active');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
42
asf-cloud-server/testarena_1/app/templates/base.html
Normal file
42
asf-cloud-server/testarena_1/app/templates/base.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}ASF TestArena{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
{% if current_user.is_authenticated %}
|
||||
<nav class="navbar">
|
||||
<div class="navbar-brand">
|
||||
<img src="{{ url_for('static', filename='uploads/icon.png') }}" alt="Logo">
|
||||
<h2>ASF TestArena</h2>
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<a href="{{ url_for('dashboard.index') }}">Dashboard</a>
|
||||
<a href="{{ url_for('jobs.submit') }}">Submit Job</a>
|
||||
{% if current_user.is_admin %}
|
||||
<a href="{{ url_for('admin.index') }}">Admin</a>
|
||||
{% endif %}
|
||||
<span>{{ current_user.username }}</span>
|
||||
<a href="{{ url_for('auth.logout') }}">Logout</a>
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<div class="container">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
135
asf-cloud-server/testarena_1/app/templates/dashboard/index.html
Normal file
135
asf-cloud-server/testarena_1/app/templates/dashboard/index.html
Normal file
@@ -0,0 +1,135 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Dashboard - ASF TestArena{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="dashboard-container">
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<h3>Test Jobs</h3>
|
||||
<a href="{{ url_for('jobs.submit') }}" class="btn btn-primary btn-sm">+ New Job</a>
|
||||
</div>
|
||||
|
||||
<div class="job-list">
|
||||
{% if jobs %}
|
||||
{% for job in jobs %}
|
||||
<div class="job-item" data-job-id="{{ job.id }}" onclick="loadJobDetails({{ job.id }})">
|
||||
<div class="job-status-icon">{{ job.get_status_icon() }}</div>
|
||||
<div class="job-info">
|
||||
<h4>Job #{{ job.id }} - {{ job.branch_name }}</h4>
|
||||
<p>{{ job.submitted_at.strftime('%Y-%m-%d %H:%M') }} by {{ job.submitter.username }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<h3>No jobs yet</h3>
|
||||
<p>Submit your first test job to get started</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<h3>Job Details</h3>
|
||||
</div>
|
||||
|
||||
<div id="job-details-container">
|
||||
<div class="empty-state">
|
||||
<h3>Select a job</h3>
|
||||
<p>Click on a job from the list to view details</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function loadJobDetails(jobId) {
|
||||
// Mark job as active
|
||||
document.querySelectorAll('.job-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
document.querySelector(`[data-job-id="${jobId}"]`).classList.add('active');
|
||||
|
||||
// Fetch job details
|
||||
fetch(`/jobs/${jobId}`)
|
||||
.then(response => response.json())
|
||||
.then(job => {
|
||||
const container = document.getElementById('job-details-container');
|
||||
const scenarios = JSON.parse(job.scenarios || '[]');
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Job ID:</div>
|
||||
<div class="detail-value">#${job.id}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Submitter:</div>
|
||||
<div class="detail-value">${job.submitter}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Branch:</div>
|
||||
<div class="detail-value">${job.branch_name}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Status:</div>
|
||||
<div class="detail-value">
|
||||
<span class="status-badge status-${job.status}">${job.status.replace('_', ' ').toUpperCase()}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Environment:</div>
|
||||
<div class="detail-value">${job.environment}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Test Mode:</div>
|
||||
<div class="detail-value">${job.test_mode}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Submitted:</div>
|
||||
<div class="detail-value">${job.submitted_at}</div>
|
||||
</div>
|
||||
${job.completed_at ? `
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Completed:</div>
|
||||
<div class="detail-value">${job.completed_at}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Duration:</div>
|
||||
<div class="detail-value">${job.duration ? Math.floor(job.duration / 60) + 'm ' + (job.duration % 60) + 's' : 'N/A'}</div>
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">Scenarios:</div>
|
||||
<div class="detail-value">${Array.isArray(scenarios) ? scenarios.join(', ') : scenarios}</div>
|
||||
</div>
|
||||
${job.status === 'in_progress' ? `
|
||||
<div style="margin-top: 20px;">
|
||||
<button class="btn btn-danger" onclick="abortJob(${job.id})">Abort Job</button>
|
||||
</div>
|
||||
` : ''}
|
||||
${job.results_path ? `
|
||||
<div style="margin-top: 20px;">
|
||||
<a href="${job.results_path}" class="btn btn-primary" target="_blank">View Results</a>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
function abortJob(jobId) {
|
||||
if (confirm('Are you sure you want to abort this job?')) {
|
||||
fetch(`/jobs/${jobId}/abort`, { method: 'POST' })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
48
asf-cloud-server/testarena_1/app/templates/jobs/submit.html
Normal file
48
asf-cloud-server/testarena_1/app/templates/jobs/submit.html
Normal file
@@ -0,0 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Submit Test Job - ASF TestArena{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="submit-container">
|
||||
<h2 style="margin-bottom: 30px; color: var(--dark);">Submit New Test Job</h2>
|
||||
|
||||
<div class="step-indicator">
|
||||
<div class="step active">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-label">Branch</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-label">Scenarios</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-label">Environment</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-label">Test Mode</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">5</div>
|
||||
<div class="step-label">Review</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('jobs.submit_step1') }}">
|
||||
<div class="form-group">
|
||||
<label for="branch_name">Git Branch Name</label>
|
||||
<input type="text" id="branch_name" name="branch_name" class="form-control"
|
||||
placeholder="e.g., feature/new-feature" required>
|
||||
<small style="color: #6b7280; margin-top: 5px; display: block;">
|
||||
Enter the branch name to analyze available test scenarios
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="{{ url_for('dashboard.index') }}" class="btn" style="background: #6b7280; color: white;">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">Next</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,68 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Select Scenarios - ASF TestArena{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="submit-container">
|
||||
<h2 style="margin-bottom: 30px; color: var(--dark);">Select Test Scenarios</h2>
|
||||
|
||||
<div class="step-indicator">
|
||||
<div class="step completed">
|
||||
<div class="step-number">✓</div>
|
||||
<div class="step-label">Branch</div>
|
||||
</div>
|
||||
<div class="step active">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-label">Scenarios</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-label">Environment</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-label">Test Mode</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">5</div>
|
||||
<div class="step-label">Review</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background: #eff6ff; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
|
||||
<strong>Branch:</strong> {{ branch_name }}
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('jobs.submit_step2') }}">
|
||||
<input type="hidden" name="branch_name" value="{{ branch_name }}">
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="selectAll" onclick="toggleAll(this)">
|
||||
Select All Scenarios
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-group">
|
||||
{% for scenario in scenarios %}
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" name="scenarios" value="{{ scenario }}" id="scenario_{{ loop.index }}">
|
||||
<label for="scenario_{{ loop.index }}">{{ scenario }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="{{ url_for('jobs.submit') }}" class="btn" style="background: #6b7280; color: white;">Back</a>
|
||||
<button type="submit" class="btn btn-primary">Next</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleAll(checkbox) {
|
||||
const checkboxes = document.querySelectorAll('input[name="scenarios"]');
|
||||
checkboxes.forEach(cb => cb.checked = checkbox.checked);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,60 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Select Environment - ASF TestArena{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="submit-container">
|
||||
<h2 style="margin-bottom: 30px; color: var(--dark);">Select Environment</h2>
|
||||
|
||||
<div class="step-indicator">
|
||||
<div class="step completed">
|
||||
<div class="step-number">✓</div>
|
||||
<div class="step-label">Branch</div>
|
||||
</div>
|
||||
<div class="step completed">
|
||||
<div class="step-number">✓</div>
|
||||
<div class="step-label">Scenarios</div>
|
||||
</div>
|
||||
<div class="step active">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-label">Environment</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-label">Test Mode</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">5</div>
|
||||
<div class="step-label">Review</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('jobs.submit_step3') }}">
|
||||
<input type="hidden" name="branch_name" value="{{ branch_name }}">
|
||||
<input type="hidden" name="scenarios" value="{{ scenarios|tojson }}">
|
||||
|
||||
<div class="radio-group">
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="environment" value="sensor_hub" required>
|
||||
<div>
|
||||
<strong>Sensor Hub</strong>
|
||||
<p style="font-size: 12px; color: #6b7280; margin-top: 4px;">Test on sensor hub hardware/simulator</p>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="environment" value="main_board" required>
|
||||
<div>
|
||||
<strong>Main Board</strong>
|
||||
<p style="font-size: 12px; color: #6b7280; margin-top: 4px;">Test on main board hardware/simulator</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn" style="background: #6b7280; color: white;" onclick="history.back()">Back</button>
|
||||
<button type="submit" class="btn btn-primary">Next</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,79 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Select Test Mode - ASF TestArena{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="submit-container">
|
||||
<h2 style="margin-bottom: 30px; color: var(--dark);">Select Test Mode</h2>
|
||||
|
||||
<div class="step-indicator">
|
||||
<div class="step completed">
|
||||
<div class="step-number">✓</div>
|
||||
<div class="step-label">Branch</div>
|
||||
</div>
|
||||
<div class="step completed">
|
||||
<div class="step-number">✓</div>
|
||||
<div class="step-label">Scenarios</div>
|
||||
</div>
|
||||
<div class="step completed">
|
||||
<div class="step-number">✓</div>
|
||||
<div class="step-label">Environment</div>
|
||||
</div>
|
||||
<div class="step active">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-label">Test Mode</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">5</div>
|
||||
<div class="step-label">Review</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('jobs.submit_final') }}">
|
||||
<input type="hidden" name="branch_name" value="{{ branch_name }}">
|
||||
<input type="hidden" name="scenarios" value="{{ scenarios }}">
|
||||
<input type="hidden" name="environment" value="{{ environment }}">
|
||||
|
||||
<div class="radio-group">
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="test_mode" value="devbench_simulator" required>
|
||||
<div>
|
||||
<strong>Devbench Simulator</strong>
|
||||
<p style="font-size: 12px; color: #6b7280; margin-top: 4px;">Fully simulated environment</p>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="radio-item">
|
||||
<input type="radio" name="test_mode" value="testbench_hil" required>
|
||||
<div>
|
||||
<strong>Testbench HIL</strong>
|
||||
<p style="font-size: 12px; color: #6b7280; margin-top: 4px;">Hardware-in-the-Loop with real devices</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top: 30px;">
|
||||
<h3 style="margin-bottom: 15px; color: var(--dark);">Additional Options</h3>
|
||||
|
||||
<div style="margin-bottom: 10px;">
|
||||
<label>
|
||||
<input type="checkbox" name="keep_devbenches">
|
||||
Keep devbenches after test completion
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" name="reuse_results">
|
||||
Reuse previous test results if available
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn" style="background: #6b7280; color: white;" onclick="history.back()">Back</button>
|
||||
<button type="submit" class="btn btn-success">Start Test</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
41
asf-cloud-server/testarena_1/app/templates/login.html
Normal file
41
asf-cloud-server/testarena_1/app/templates/login.html
Normal file
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - ASF TestArena</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<div class="logo">
|
||||
<img src="{{ url_for('static', filename='uploads/icon.png') }}" alt="Logo">
|
||||
<h1>ASF TestArena</h1>
|
||||
</div>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="POST">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" class="form-control" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user