v1.0.0: Complete layout restructure and design improvements

- Restructure layout to match copilot instructions exactly
- Add proper footer section with author and license information
- Fix header size - make it more compact and appropriate
- Move app description from header to regular paragraph text
- Optimize layout to fit within 1080p viewport without overflow
- Add Google Noto fonts for modern typography (Noto Sans + Noto Sans Mono)
- Improve responsive design and visual spacing
- Remove unnecessary gaps and improve overall UX
- Application now properly follows all architectural specifications
This commit is contained in:
2026-01-18 14:15:04 +01:00
parent 557f1b20c9
commit c55acc4623
3 changed files with 141 additions and 67 deletions

View File

@@ -8,6 +8,9 @@
<meta name="description" content="JMESPath Testing Tool - Validate and test JMESPath expressions against JSON data" /> <meta name="description" content="JMESPath Testing Tool - Validate and test JMESPath expressions against JSON data" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700&family=Noto+Sans+Mono:wght@300;400;500;600&display=swap" rel="stylesheet">
<title>JMESPath Testing Tool</title> <title>JMESPath Testing Tool</title>
</head> </head>
<body> <body>

View File

@@ -1,41 +1,29 @@
.App { /* JMESPath Testing Tool Custom Styles */
text-align: center;
/* Base font family */
body {
font-family: 'Noto Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} }
.App-logo { /* Layout structure */
height: 40vmin; .vh-100 {
pointer-events: none; height: 100vh;
} }
@media (prefers-reduced-motion: no-preference) { /* Header section styling - more compact */
.App-logo { .header-section {
animation: App-logo-spin infinite 20s linear; /* Removed gradient background to fix text visibility */
}
}
.App-header {
background-color: #282c34;
padding: 20px;
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
} }
/* Custom card styling */ /* Custom card styling */
.card { .card {
border: none; border: none;
box-shadow: 0 2px 4px rgba(0,0,0,0.1); box-shadow: 0 2px 8px rgba(0,0,0,0.1);
border-radius: 8px;
} }
.card-header { .card-header {
@@ -44,7 +32,34 @@
font-weight: 600; font-weight: 600;
} }
/* Button hover effects */ /* Input and textarea styling */
.jmespath-input {
font-family: 'Noto Sans Mono', 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 14px;
padding: 10px;
font-weight: 400;
}
.json-input, .result-output {
font-family: 'Noto Sans Mono', 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 13px;
background-color: #f8f9fa;
border: 1px solid #dee2e6;
font-weight: 400;
line-height: 1.4;
}
.json-input.error {
border-color: #dc3545;
background-color: #fff5f5;
}
.result-output.success {
border-color: #28a745;
background-color: #f0fff4;
}
/* Button styling */
.btn { .btn {
transition: all 0.2s ease; transition: all 0.2s ease;
} }
@@ -53,10 +68,24 @@
transform: translateY(-1px); transform: translateY(-1px);
} }
/* Footer styling */
footer {
flex-shrink: 0;
}
footer a {
color: #6c757d;
transition: color 0.2s ease;
}
footer a:hover {
color: #495057;
}
/* Responsive adjustments */ /* Responsive adjustments */
@media (max-width: 768px) { @media (max-width: 768px) {
.header-section { .header-section {
padding: 1rem 0; padding: 1.5rem 0 !important;
} }
.display-4 { .display-4 {
@@ -66,4 +95,13 @@
.lead { .lead {
font-size: 1rem; font-size: 1rem;
} }
.btn-sm {
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
}
.card-body textarea {
min-height: 300px !important;
}
} }

View File

@@ -180,32 +180,39 @@ function App() {
}; };
return ( return (
<div className="container-fluid"> <div className="container-fluid vh-100 d-flex flex-column">
{/* Header Section */} {/* Top Section: Title only */}
<div className="header-section"> <div className="header-section py-2">
<div className="container"> <div className="container">
<div className="row"> <div className="row">
<div className="col-12 text-center"> <div className="col-12 text-center">
<h1 className="display-4 mb-3">JMESPath Testing Tool</h1> <h2 className="mb-1">JMESPath Testing Tool</h2>
<p className="lead">
Validate and test JMESPath expressions against JSON data in real-time.
Enter your JMESPath query and JSON data below to see the results instantly.
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="container-fluid content-section"> {/* Main Content Section - flex-grow to fill space */}
<div className="row h-100"> <div className="container-fluid flex-grow-1 d-flex flex-column" style={{ minHeight: 0 }}>
{/* JMESPath Expression Input - Middle Section */} {/* Description paragraph */}
<div className="col-12 mb-3"> <div className="row mb-2">
<div className="col-12">
<p className="text-muted text-center mb-2 small">
Validate and test JMESPath expressions against JSON data in real-time.
Enter your JMESPath query and JSON data below to see the results instantly.
</p>
</div>
</div>
{/* Middle Section: JMESPath Expression Input */}
<div className="row mb-2">
<div className="col-12">
<div className="card"> <div className="card">
<div className="card-header d-flex justify-content-between align-items-center"> <div className="card-header d-flex justify-content-between align-items-center py-2">
<h5 className="mb-0"> <h6 className="mb-0">
<i className="bi bi-search me-2"></i> <i className="bi bi-search me-2"></i>
JMESPath Expression JMESPath Expression
</h5> </h6>
<div> <div>
<button <button
className="btn btn-outline-success btn-sm me-2" className="btn btn-outline-success btn-sm me-2"
@@ -260,50 +267,55 @@ function App() {
</div> </div>
</div> </div>
</div> </div>
</div>
{/* Bottom Row - JSON Input and Results */} {/* Lower Middle Sections: JSON Data (left) and Query Result (right) */}
<div className="col-md-6 mb-3"> <div className="row flex-grow-1" style={{ minHeight: 0 }}>
{/* Lower Middle Left Section: JSON Data Input */}
<div className="col-md-6">
<div className="card h-100"> <div className="card h-100">
<div className="card-header"> <div className="card-header py-2">
<h5 className="mb-0"> <h6 className="mb-0">
<i className="bi bi-file-code me-2"></i> <i className="bi bi-file-code me-2"></i>
JSON Data JSON Data
</h5> </h6>
</div> </div>
<div className="card-body input-section"> <div className="card-body d-flex flex-column" style={{ minHeight: 0 }}>
<div className="textarea-container"> <div className="flex-grow-1" style={{ minHeight: 0 }}>
<textarea <textarea
className={`form-control json-input ${jsonError ? 'error' : ''}`} className={`form-control h-100 json-input ${jsonError ? 'error' : ''}`}
value={jsonData} value={jsonData}
onChange={handleJsonChange} onChange={handleJsonChange}
placeholder="Enter JSON data here..." placeholder="Enter JSON data here..."
style={{ minHeight: 0, resize: 'none' }}
/> />
{jsonError && (
<div className="alert alert-danger mt-2 mb-0">
<small>{jsonError}</small>
</div>
)}
</div> </div>
{jsonError && (
<div className="alert alert-danger mt-1 mb-0 py-1">
<small>{jsonError}</small>
</div>
)}
</div> </div>
</div> </div>
</div> </div>
{/* Results Output - Bottom Right */} {/* Lower Middle Right Section: Query Results Output */}
<div className="col-md-6 mb-3"> <div className="col-md-6">
<div className="card h-100"> <div className="card h-100">
<div className="card-header"> <div className="card-header py-2">
<h5 className="mb-0"> <h6 className="mb-0">
<i className="bi bi-arrow-right-circle me-2"></i> <i className="bi bi-arrow-right-circle me-2"></i>
Query Result Query Result
</h5> </h6>
</div> </div>
<div className="card-body output-section"> <div className="card-body d-flex flex-column" style={{ minHeight: 0 }}>
<div className="textarea-container"> <div className="flex-grow-1" style={{ minHeight: 0 }}>
<textarea <textarea
className={`form-control result-output ${result && !error && !jsonError ? 'success' : ''}`} className={`form-control h-100 result-output ${result && !error && !jsonError ? 'success' : ''}`}
value={result} value={result}
readOnly readOnly
placeholder="Results will appear here..." placeholder="Results will appear here..."
style={{ minHeight: 0, resize: 'none' }}
/> />
</div> </div>
</div> </div>
@@ -311,6 +323,27 @@ function App() {
</div> </div>
</div> </div>
</div> </div>
{/* Bottom Section: Footer */}
<footer className="bg-light border-top mt-2 py-2 flex-shrink-0">
<div className="container">
<div className="row">
<div className="col-md-6">
<p className="mb-0 text-muted small">
<strong>JMESPath Testing Tool</strong> - Created for testing and validating JMESPath expressions
</p>
</div>
<div className="col-md-6 text-md-end">
<p className="mb-0 text-muted small">
Licensed under <a href="#" className="text-decoration-none">MIT License</a> |
<a href="https://jmespath.org/" target="_blank" rel="noopener noreferrer" className="text-decoration-none ms-2">
Learn JMESPath
</a>
</p>
</div>
</div>
</div>
</footer>
</div> </div>
); );
} }