Initial commit: CMM Report Analyzer
FastAPI app that parses CMM inspection reports (PDF/Excel/CSV), computes SPC metrics (Cp/Cpk/Pp/Ppk, control limits, Shapiro-Wilk), generates interactive Plotly charts, and provides AI-powered quality summaries via Azure OpenAI with graceful fallback. Includes 21 passing tests covering parsers, SPC calculations, and API endpoints.
This commit is contained in:
247
app/static/css/style.css
Normal file
247
app/static/css/style.css
Normal file
@@ -0,0 +1,247 @@
|
||||
:root {
|
||||
--bg: #f4f6f8;
|
||||
--surface: #ffffff;
|
||||
--primary: #1a5276;
|
||||
--primary-light: #2980b9;
|
||||
--accent: #2ecc71;
|
||||
--danger: #e74c3c;
|
||||
--warn: #f39c12;
|
||||
--text: #2c3e50;
|
||||
--text-muted: #7f8c8d;
|
||||
--border: #dce1e6;
|
||||
--radius: 8px;
|
||||
}
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
header {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
padding: 1.5rem 2rem;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 0.9rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
/* Drop zone */
|
||||
#drop-zone {
|
||||
border: 2px dashed var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 3rem 2rem;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
#drop-zone:hover, #drop-zone.dragover {
|
||||
border-color: var(--primary-light);
|
||||
background: rgba(41, 128, 185, 0.04);
|
||||
}
|
||||
|
||||
.drop-content svg { color: var(--text-muted); margin-bottom: 0.75rem; }
|
||||
.drop-content p { color: var(--text); font-size: 1rem; }
|
||||
.drop-content .hint { color: var(--text-muted); font-size: 0.85rem; margin-top: 0.25rem; }
|
||||
|
||||
/* File list */
|
||||
#file-list {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.file-tag {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
padding: 0.3rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.85rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.file-tag .remove {
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.file-tag .remove:hover { opacity: 1; }
|
||||
|
||||
/* Upload button */
|
||||
#upload-btn {
|
||||
margin-top: 1rem;
|
||||
padding: 0.6rem 2rem;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
#upload-btn:hover:not(:disabled) { background: var(--primary-light); }
|
||||
#upload-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
|
||||
/* Spinner */
|
||||
#status-section {
|
||||
text-align: center;
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid var(--border);
|
||||
border-top-color: var(--primary);
|
||||
border-radius: 50%;
|
||||
margin: 0 auto 1rem;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
/* Results */
|
||||
.file-result {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.file-result h2 {
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary);
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
|
||||
.file-result .error {
|
||||
color: var(--danger);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* SPC table */
|
||||
.spc-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 1.5rem;
|
||||
overflow-x: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.spc-table th, .spc-table td {
|
||||
padding: 0.5rem 0.75rem;
|
||||
text-align: right;
|
||||
border-bottom: 1px solid var(--border);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.spc-table th {
|
||||
background: var(--bg);
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.spc-table th:first-child, .spc-table td:first-child { text-align: left; }
|
||||
|
||||
.spc-table tr:hover td { background: rgba(41, 128, 185, 0.04); }
|
||||
|
||||
.cpk-good { color: var(--accent); font-weight: 600; }
|
||||
.cpk-warn { color: var(--warn); font-weight: 600; }
|
||||
.cpk-bad { color: var(--danger); font-weight: 600; }
|
||||
|
||||
/* Summary */
|
||||
.summary {
|
||||
background: var(--bg);
|
||||
border-left: 4px solid var(--primary);
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 0 var(--radius) var(--radius) 0;
|
||||
white-space: pre-wrap;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* Charts */
|
||||
.charts-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chart-full { grid-column: 1 / -1; }
|
||||
|
||||
/* Measurements table */
|
||||
.meas-toggle {
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
padding: 0.4rem 1rem;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.meas-toggle:hover { background: var(--bg); }
|
||||
|
||||
.meas-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.meas-table th, .meas-table td {
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.meas-table th { background: var(--bg); font-weight: 600; }
|
||||
.meas-table th:first-child, .meas-table td:first-child { text-align: left; }
|
||||
|
||||
.meas-table .oot { background: rgba(231, 76, 60, 0.08); color: var(--danger); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.charts-grid { grid-template-columns: 1fr; }
|
||||
main { padding: 1rem; }
|
||||
}
|
||||
Reference in New Issue
Block a user