feat: add CSS styles and JavaScript functionality for projects and scenarios, including filtering and layout enhancements
This commit is contained in:
@@ -9,4 +9,5 @@ jinja2
|
|||||||
pandas
|
pandas
|
||||||
numpy
|
numpy
|
||||||
passlib
|
passlib
|
||||||
python-jose
|
python-jose
|
||||||
|
python-multipart
|
||||||
109
static/css/projects.css
Normal file
109
static/css/projects.css
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
:root {
|
||||||
|
--card-bg: rgba(21, 27, 35, 0.8);
|
||||||
|
--card-border: rgba(255, 255, 255, 0.08);
|
||||||
|
--hover-highlight: rgba(241, 178, 26, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.projects-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-radius: var(--table-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.projects-table th,
|
||||||
|
.projects-table td {
|
||||||
|
padding: 0.875rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--card-border);
|
||||||
|
background: var(--card-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.projects-table tbody tr:hover {
|
||||||
|
background: var(--hover-highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.definition-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
gap: 1.25rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.definition-list dt {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.definition-list dd {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
background: rgba(209, 75, 75, 0.2);
|
||||||
|
border: 1px solid rgba(209, 75, 75, 0.4);
|
||||||
|
color: var(--color-text-invert);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input,
|
||||||
|
.form-group select,
|
||||||
|
.form-group textarea {
|
||||||
|
padding: 0.75rem 0.85rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
background: rgba(8, 12, 19, 0.75);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
78
static/css/scenarios.css
Normal file
78
static/css/scenarios.css
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
.scenario-meta {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-radius: var(--table-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th,
|
||||||
|
.table td {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
background: rgba(21, 27, 35, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:hover {
|
||||||
|
background: rgba(43, 165, 143, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb a {
|
||||||
|
color: var(--brand-2);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.6rem 1.1rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(90deg, var(--brand) 0%, var(--brand-2) 100%);
|
||||||
|
color: var(--color-text-dark);
|
||||||
|
box-shadow: 0 8px 18px rgba(241, 178, 26, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: rgba(148, 197, 255, 0.2);
|
||||||
|
color: var(--color-text-invert);
|
||||||
|
border: 1px solid rgba(148, 197, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link {
|
||||||
|
padding: 0.35rem 0.5rem;
|
||||||
|
color: var(--brand-3);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover,
|
||||||
|
.btn:focus {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
19
static/js/projects.js
Normal file
19
static/js/projects.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const table = document.querySelector("[data-project-table]");
|
||||||
|
if (!table) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = Array.from(table.querySelectorAll("tbody tr"));
|
||||||
|
const filterInput = document.querySelector("[data-project-filter]");
|
||||||
|
|
||||||
|
if (filterInput) {
|
||||||
|
filterInput.addEventListener("input", () => {
|
||||||
|
const query = filterInput.value.trim().toLowerCase();
|
||||||
|
rows.forEach((row) => {
|
||||||
|
const match = row.textContent.toLowerCase().includes(query);
|
||||||
|
row.style.display = match ? "" : "none";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}{{ project.name }} · Project · CalMiner{% endblock %}
|
{% block title %}{{ project.name }} · Project · CalMiner{% endblock %}
|
||||||
|
|
||||||
|
{% block head_extra %}
|
||||||
|
<link rel="stylesheet" href="/static/css/projects.css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<nav class="breadcrumb">
|
<nav class="breadcrumb">
|
||||||
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}{% if project %}Edit {{ project.name }}{% else %}New Project{% endif %} · CalMiner{% endblock %}
|
{% block title %}{% if project %}Edit {{ project.name }}{% else %}New Project{% endif %} · CalMiner{% endblock %}
|
||||||
|
|
||||||
|
{% block head_extra %}
|
||||||
|
<link rel="stylesheet" href="/static/css/projects.css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<nav class="breadcrumb">
|
<nav class="breadcrumb">
|
||||||
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Projects · CalMiner{% endblock %}
|
{% block title %}Projects · CalMiner{% endblock %}
|
||||||
|
|
||||||
|
{% block head_extra %}
|
||||||
|
<link rel="stylesheet" href="/static/css/projects.css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="page-header">
|
<section class="page-header">
|
||||||
<div>
|
<div>
|
||||||
<h1>Projects</h1>
|
<h1>Projects</h1>
|
||||||
<p class="text-muted">Manage mining projects and explore their scenarios.</p>
|
<p class="text-muted">Manage mining projects and explore their scenarios.</p>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-primary" href="{{ url_for('projects.create_project_form') }}">New Project</a>
|
<div class="actions">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Filter projects..."
|
||||||
|
data-project-filter
|
||||||
|
/>
|
||||||
|
<a class="btn btn-primary" href="{{ url_for('projects.create_project_form') }}">New Project</a>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% if projects %}
|
{% if projects %}
|
||||||
<table class="table">
|
<table class="projects-table" data-project-table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
@@ -40,3 +52,8 @@
|
|||||||
<p>No projects yet. <a href="{{ url_for('projects.create_project_form') }}">Create your first project.</a></p>
|
<p>No projects yet. <a href="{{ url_for('projects.create_project_form') }}">Create your first project.</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{{ super() }}
|
||||||
|
<script src="/static/js/projects.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}{{ scenario.name }} · Scenario · CalMiner{% endblock %}
|
{% block title %}{{ scenario.name }} · Scenario · CalMiner{% endblock %}
|
||||||
|
|
||||||
|
{% block head_extra %}
|
||||||
|
<link rel="stylesheet" href="/static/css/scenarios.css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block head_extra %}
|
||||||
|
<link rel="stylesheet" href="/static/css/scenarios.css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<nav class="breadcrumb">
|
<nav class="breadcrumb">
|
||||||
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}{% if scenario %}Edit {{ scenario.name }}{% else %}New Scenario{% endif %} · CalMiner{% endblock %}
|
{% block title %}{% if scenario %}Edit {{ scenario.name }}{% else %}New Scenario{% endif %} · CalMiner{% endblock %}
|
||||||
|
|
||||||
|
{% block head_extra %}
|
||||||
|
<link rel="stylesheet" href="/static/css/scenarios.css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<nav class="breadcrumb">
|
<nav class="breadcrumb">
|
||||||
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user