diff --git a/.github/workflows/frontend_ci_cd.yml b/.github/workflows/frontend_ci_cd.yml index e49c01c..e28fe43 100644 --- a/.github/workflows/frontend_ci_cd.yml +++ b/.github/workflows/frontend_ci_cd.yml @@ -12,9 +12,36 @@ on: - ui/** jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'ui/.nvmrc' + cache: 'npm' + cache-dependency-path: 'ui/package-lock.json' + + - name: Install dependencies + run: npm ci + working-directory: ./ui + + - name: Run Prettier format check + run: npm run format:check + working-directory: ./ui + + - name: Run ESLint + run: npm run lint + working-directory: ./ui + # Currently there are no tests to run before build build: + needs: lint runs-on: ubuntu-latest # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. diff --git a/.github/workflows/on-pull-request.yaml b/.github/workflows/on-pull-request.yaml index 8af7d49..80c2e81 100644 --- a/.github/workflows/on-pull-request.yaml +++ b/.github/workflows/on-pull-request.yaml @@ -49,6 +49,32 @@ jobs: run: poetry run test working-directory: ./backend + lint-frontend: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: 'ui/.nvmrc' + cache: 'npm' + cache-dependency-path: 'ui/package-lock.json' + + - name: Install dependencies + run: npm ci + working-directory: ./ui + + - name: Run Prettier format check + run: npm run format:check + working-directory: ./ui + + - name: Run ESLint + run: npm run lint + working-directory: ./ui + lint-helm: runs-on: ubuntu-latest steps: diff --git a/ui/.eslintrc.json b/ui/.eslintrc.json index 64c0f28..6e13c48 100644 --- a/ui/.eslintrc.json +++ b/ui/.eslintrc.json @@ -23,6 +23,7 @@ }, "plugins": ["react-refresh"], "rules": { + "react/prop-types": "off", "react/jsx-no-target-blank": "off", "react-refresh/only-export-components": [ "warn", diff --git a/ui/public/icons/copy.svg b/ui/public/icons/copy.svg new file mode 100644 index 0000000..f2a68cd --- /dev/null +++ b/ui/public/icons/copy.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/ui/src/App.css b/ui/src/App.css index 8b7e19d..51c6984 100644 --- a/ui/src/App.css +++ b/ui/src/App.css @@ -1,40 +1,41 @@ :root { - --color-sherpa100: #D9F4F4; - --color-sherpa200: #B8E8E9; - --color-sherpa300: #86D6DA; - --color-sherpa400: #63CBCF; - --color-sherpa500: #009AA1; + --color-sherpa100: #d9f4f4; + --color-sherpa200: #b8e8e9; + --color-sherpa300: #86d6da; + --color-sherpa400: #63cbcf; + --color-sherpa500: #009aa1; --color-sherpa600: #087a82; - --color-sherpa700: #0C646D; - --color-sherpa800: #004F59; - --color-sherpa900: #0C3B41; - --color-white: #FFFFFF; + --color-sherpa700: #0c646d; + --color-sherpa800: #004f59; + --color-sherpa900: #0c3b41; + --color-white: #ffffff; --color-black: #000000; - --color-yellow: #F1B828; + --color-yellow: #f1b828; + --color-yellow-hover: #e0a71f; + --color-yellow-active: #c99319; + --color-red: #bf6366; + --color-red-hover: #a65558; + --color-red-active: #8d484b; + --color-white-transparent: rgba(255, 255, 255, 0.1); } .App { min-height: 100vh; display: flex; flex-direction: column; - /* Desktop background (default) */ - background-image: - linear-gradient(180deg, rgba(0, 0, 0, 0.30) 0%, rgba(0, 0, 0, 0.00) 15.58%), - radial-gradient(68.05% 68.05% at 50.79% 4.4%, rgba(12, 59, 65, 0.30) 0%, #0C3B41 100%), + /* Desktop background (default) */ + background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 15.58%), + radial-gradient(68.05% 68.05% at 50.79% 4.4%, rgba(12, 59, 65, 0.3) 0%, #0c3b41 100%), url('/background-image-desktop.jpg'); background-position: center, center, top; - background-size: 100% 100%, 100% 100%, cover; + background-size: + 100% 100%, + 100% 100%, + cover; background-repeat: no-repeat; background-attachment: fixed; } -#infoPanel { - text-align: left; - font-style: italic; - font-weight: bold; - color: rgb(126, 52, 52); -} - /* Button Group Container */ .button-group { display: flex; @@ -65,12 +66,12 @@ } .btn--yellow:hover { - background: #e0a71f !important; + background: var(--color-yellow-hover) !important; border: 4px solid var(--color-white) !important; } .btn--yellow:active { - background: #c99319 !important; + background: var(--color-yellow-active) !important; } .btn--yellow:focus { @@ -109,7 +110,7 @@ /* Button Styles - Red (Delete API key) */ .btn--red { - background: #bf6366 !important; + background: var(--color-red) !important; border: 4px solid var(--color-white) !important; color: var(--color-white) !important; font-family: Heebo, sans-serif !important; @@ -121,12 +122,12 @@ } .btn--red:hover { - background: #a65558 !important; + background: var(--color-red-hover) !important; border: 4px solid var(--color-white) !important; } .btn--red:active { - background: #8d484b !important; + background: var(--color-red-active) !important; } .btn--red:focus { @@ -136,7 +137,7 @@ } /* Button Styles - Green (Logout) */ -.btn--green{ +.btn--green { background: var(--color-sherpa800) !important; border: 4px solid var(--color-white) !important; color: var(--color-white) !important; @@ -151,7 +152,7 @@ .btn--green:hover { border-width: 2px !important; padding: 15px 25px !important; - background: rgba(255, 255, 255, 0.1) !important; + background: var(--color-white-transparent) !important; } .btn--green:active { @@ -176,7 +177,7 @@ /* Headings */ h3 { color: var(--color-white); - font-family: "Exo 2", sans-serif; + font-family: 'Exo 2', sans-serif; font-size: 28px; font-weight: 600; line-height: 32px; @@ -184,110 +185,6 @@ h3 { text-align: center; } -/* Card Styles */ -.p-card { - background: var(--color-sherpa800) !important; - border: 1px solid var(--color-sherpa600) !important; - border-radius: 8px !important; - box-shadow: none !important; -} - -.p-card .p-card-body { - padding: 32px !important; - color: var(--color-white) !important; -} - -.p-card .p-card-content { - color: var(--color-white) !important; - font-family: 'Heebo', sans-serif !important; - font-size: 16px !important; - line-height: 24px !important; - min-height: 200px; -} - -/* DataTable Styles */ -.p-datatable { - background: var(--color-sherpa800) !important; - border: 1px solid var(--color-sherpa600) !important; - border-radius: 8px !important; - overflow: hidden !important; - width: 100% !important; - table-layout: fixed !important; -} - -.p-datatable .p-datatable-table { - width: 100% !important; - table-layout: fixed !important; -} - -.p-datatable .p-datatable-header { - background: var(--color-sherpa700) !important; - border-bottom: 1px solid var(--color-sherpa600) !important; - color: var(--color-white) !important; - padding: 16px !important; -} - -.p-datatable .p-datatable-thead > tr > th { - background: var(--color-sherpa700) !important; - border: 1px solid var(--color-sherpa600) !important; - color: var(--color-white) !important; - font-family: 'Exo 2', sans-serif !important; - font-size: 16px !important; - font-weight: 600 !important; - padding: 16px !important; - box-sizing: border-box !important; -} - -.p-datatable .p-datatable-tbody > tr { - background: var(--color-sherpa800) !important; - color: var(--color-white) !important; -} - -.p-datatable .p-datatable-tbody > tr > td { - border: 1px solid var(--color-sherpa600) !important; - padding: 16px !important; - font-family: 'Heebo', sans-serif !important; - font-size: 16px !important; - overflow-wrap: break-word !important; - word-wrap: break-word !important; - box-sizing: border-box !important; -} - -.p-datatable .p-datatable-tbody > tr:hover { - background: rgba(255, 255, 255, 0.05) !important; -} - -/* Empty message in DataTable */ -.p-datatable .p-datatable-emptymessage > td { - color: var(--color-sherpa400) !important; -} - -/* DataTable Column Widths */ - .p-datatable .p-datatable-thead > tr > th:nth-child(1), - .p-datatable .p-datatable-tbody > tr > td:nth-child(1), - .p-datatable .route-column { - width: 60% !important; - } - - .p-datatable .p-datatable-thead > tr > th:nth-child(2), - .p-datatable .p-datatable-tbody > tr > td:nth-child(2), - .p-datatable .limits-column { - width: 40% !important; - } - -/* Info Panel Styling */ -#infoPanel { - color: var(--color-white); - font-family: 'Heebo', sans-serif; - font-size: 16px; - line-height: 24px; - padding: 24px; - text-align: left; - white-space: pre-line; - overflow-wrap: break-word; - word-wrap: break-word; -} - /* Uniform icons */ .icon { width: 20px; @@ -315,31 +212,97 @@ h3 { text-decoration: underline; } -/* Routes Information */ -.routes-info-box { +/* Login */ +.login-screen { + text-align: center; + padding: 60px 20px; +} + +.login-screen h3 { + margin-bottom: 24px; +} + +.login-screen-text { + color: var(--color-sherpa200); + margin-bottom: 32px; + font-size: 18px; + line-height: 24px; +} + +.login-screen-docs { + margin-top: 24px; +} + +/* API Key Container */ +.api-key-container { + display: flex; + align-items: center; + gap: 12px; + padding: 16px; background-color: var(--color-sherpa700); - border: 1px solid var(--color-sherpa500); border-radius: 8px; - padding: 12px 16px; - margin-bottom: 16px; - font-size: 14px; - color: var(--color-sherpa200); + border: 1px solid var(--color-sherpa600); +} + +.api-key-text { + font-family: monospace; + font-size: 16px; + font-style: normal; + color: var(--color-white); + flex: 1; + word-break: break-all; +} + +/* API Key Label */ +.api-key-label { + margin-bottom: 12px; + font-size: 16px; + font-weight: 600; + font-style: normal; + color: var(--color-white); +} + +.btn-copy { + background: var(--color-sherpa600) !important; + border: 2px solid var(--color-sherpa500) !important; + color: var(--color-white) !important; + padding: 8px !important; + border-radius: 4px !important; + min-width: 40px !important; + width: 40px !important; + height: 40px !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + transition: all 0.3s ease !important; + cursor: pointer; +} + +.btn-copy:hover { + background: var(--color-sherpa500) !important; + border-color: var(--color-sherpa400) !important; } -.routes-info-box .info-item { - margin-bottom: 8px; +.btn-copy:active { + background: var(--color-sherpa700) !important; } -.routes-info-box .info-item:last-child { - margin-bottom: 0; +.btn-copy:focus { + border-color: var(--color-yellow) !important; + outline: none !important; +} + +.copy-icon { + width: 20px; + height: 20px; + display: block; } /* Tablet layout (600px - 1023px) */ @media (min-width: 600px) and (max-width: 1023px) { .App { - background-image: - linear-gradient(180deg, rgba(0, 0, 0, 0.30) 0%, rgba(0, 0, 0, 0.00) 15.58%), - radial-gradient(68.05% 68.05% at 50.79% 4.4%, rgba(12, 59, 65, 0.30) 0%, #0C3B41 100%), + background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 15.58%), + radial-gradient(68.05% 68.05% at 50.79% 4.4%, rgba(12, 59, 65, 0.3) 0%, #0c3b41 100%), url('/background-image-tablet.jpg'); } @@ -352,39 +315,24 @@ h3 { line-height: 28px; } - .p-card .p-card-body { - padding: 24px !important; - } - - .p-datatable .p-datatable-thead > tr > th:nth-child(1), - .p-datatable .p-datatable-tbody > tr > td:nth-child(1), - .p-datatable .route-column { - width: 55% !important; - } - - .p-datatable .p-datatable-thead > tr > th:nth-child(2), - .p-datatable .p-datatable-tbody > tr > td:nth-child(2), - .p-datatable .limits-column { - width: 45% !important; - } - .btn--uniform { min-width: 160px !important; } - .routes-info-box { - font-size: 13px; - padding: 10px 14px; + .login-screen { + padding: 48px 20px; + } + + .login-screen-text { + font-size: 16px; } } /* Mobile layout (up to 599px) */ @media (max-width: 599px) { .App { - background-image: - linear-gradient(180deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.00) 30%), - radial-gradient(90% 90% at 50% 10%, rgba(12, 59, 65, 0.50) 0%, #0C3B41 100%), - url('/background-image-mobile.jpg'); + background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 30%), + radial-gradient(90% 90% at 50% 10%, rgba(12, 59, 65, 0.5) 0%, #0c3b41 100%), url('/background-image-mobile.jpg'); } .content-container { @@ -398,22 +346,6 @@ h3 { margin: 24px 0 16px 0; } - .p-card .p-card-body { - padding: 20px !important; - } - - .p-datatable .p-datatable-thead > tr > th:nth-child(1), - .p-datatable .p-datatable-tbody > tr > td:nth-child(1), - .p-datatable .route-column { - width: 50% !important; - } - - .p-datatable .p-datatable-thead > tr > th:nth-child(2), - .p-datatable .p-datatable-tbody > tr > td:nth-child(2), - .p-datatable .limits-column { - width: 50% !important; - } - .btn--uniform { min-width: 100% !important; width: 100% !important; @@ -423,8 +355,16 @@ h3 { font-size: 14px; } - .routes-info-box { - font-size: 12px; - padding: 8px 12px; + .login-screen { + padding: 40px 16px; + } + + .login-screen-text { + font-size: 16px; + margin-bottom: 24px; } -} \ No newline at end of file + + .login-screen-docs { + margin-top: 20px; + } +} diff --git a/ui/src/App.jsx b/ui/src/App.jsx index 9c9e91c..48512fd 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -1,19 +1,18 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import './App.css'; import 'primereact/resources/themes/lara-light-indigo/theme.css'; import 'primereact/resources/primereact.min.css'; import '/node_modules/primeflex/primeflex.css'; import { Button } from 'primereact/button'; -import { Card } from 'primereact/card'; import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; +import { useAuth } from 'react-oidc-context'; +import { toast } from 'react-toastify'; import Header from './components/Header'; import Footer from './components/Footer'; - +import InfoPanelCard from './components/InfoPanelCard'; import { getAPIKey, deleteAPIKey, getRoutes } from './Services/apiService'; -import { useAuth } from 'react-oidc-context'; -import { toast } from 'react-toastify'; function App() { const auth = useAuth(); @@ -33,12 +32,40 @@ function App() { }; }, [auth]); + const handleCopyApiKey = (apiKey) => { + navigator.clipboard.writeText(apiKey); + toast.success('API key copied to clipboard!', { + position: 'top-right', + autoClose: 3000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + theme: 'dark', + }); + }; + const handleGetAPIKey = async () => { try { const { data, isError } = await getAPIKey(); if (!isError) { const apiKey = data.apiKey; - setInfoMessage(`API key:\n ${apiKey}`); + setInfoMessage( + <> +

API Key

+
+ {apiKey} + +
+ + ); } else { showToaster(data?.message ?? 'Undefined error message'); } @@ -75,10 +102,10 @@ function App() { if (!isError) { const routes = data.routes; const listItems = routes.map((currElement, index) => { - return { - id: index, + return { + id: index, route: currElement.url, - limits: currElement.limits + limits: currElement.limits, }; }); setInfoMessage(generateTable(listItems)); @@ -99,13 +126,10 @@ function App() { return ( <>
- Information + Information - Your effective rate limits are determined by route configuration and your group membership. EUMETNET member users receive higher limits. + Your effective rate limits are determined by route configuration and your group membership. EUMETNET member + users receive higher limits.
@@ -141,82 +165,35 @@ function App() {
{/* */}
- + {auth.isAuthenticated ? (
{/* Button Group */}
-
- {/* Info Panel Card */} -
-

Info Panel

- -
- {infoMessage || 'Click a button above to get started'} -
-
-
+ {infoMessage}
) : (
-
-

Welcome to Developer Portal

-

- Please log in to access your API keys and manage routes -

-
@@ -228,4 +205,4 @@ function App() { ); } -export default App; \ No newline at end of file +export default App; diff --git a/ui/src/components/Footer.css b/ui/src/components/Footer.css index 4e29a31..13487ec 100644 --- a/ui/src/components/Footer.css +++ b/ui/src/components/Footer.css @@ -1,6 +1,6 @@ .footer-container { width: 100%; - background: #00282D; + background: #00282d; padding: 64px 0; margin-top: auto; border-top: 1px solid var(--color-sherpa600); @@ -138,4 +138,4 @@ font-size: 14px; line-height: 20px; } -} \ No newline at end of file +} diff --git a/ui/src/components/Footer.jsx b/ui/src/components/Footer.jsx index e1ca884..06a1081 100644 --- a/ui/src/components/Footer.jsx +++ b/ui/src/components/Footer.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import './Footer.css'; const Footer = () => { @@ -7,17 +6,8 @@ const Footer = () => {
{/* First Row - Logo and Contact */}
- EUMETNET Logo - + EUMETNET Logo +

Contact information

Opens in new tab
@@ -27,12 +17,8 @@ const Footer = () => {

- If you need technical guidance or encounter issues, see the Support Section {' '} - + If you need technical guidance or encounter issues, see the Support Section{' '} + MeteoGate

@@ -45,9 +31,9 @@ const Footer = () => { {/* Third Row - Copyright and Legal Links */}

Copyright EUMETNET SNC ©{new Date().getFullYear()}

- @@ -60,4 +46,4 @@ const Footer = () => { ); }; -export default Footer; \ No newline at end of file +export default Footer; diff --git a/ui/src/components/Header.css b/ui/src/components/Header.css index 5b9acef..d34933d 100644 --- a/ui/src/components/Header.css +++ b/ui/src/components/Header.css @@ -35,7 +35,7 @@ .header-content h1 { color: var(--color-white); - font-family: "Exo 2", sans-serif; + font-family: 'Exo 2', sans-serif; font-size: 48px; font-weight: 600; line-height: 48px; @@ -45,7 +45,7 @@ .header-content .subtitle { color: var(--color-white); - font-family: "Exo 2", sans-serif; + font-family: 'Exo 2', sans-serif; font-size: 36px; font-weight: 600; line-height: 36px; @@ -125,4 +125,4 @@ font-size: 18px; line-height: 24px; } -} \ No newline at end of file +} diff --git a/ui/src/components/Header.jsx b/ui/src/components/Header.jsx index 7104e98..4201f4d 100644 --- a/ui/src/components/Header.jsx +++ b/ui/src/components/Header.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import './Header.css'; const Header = () => { @@ -15,7 +14,8 @@ const Header = () => {

Developer Portal

- Register and manage your API keys and view available routes + Register and manage your API keys and{' '} + view available routes

diff --git a/ui/src/components/InfoPanelCard.css b/ui/src/components/InfoPanelCard.css new file mode 100644 index 0000000..6ead416 --- /dev/null +++ b/ui/src/components/InfoPanelCard.css @@ -0,0 +1,301 @@ +/* Main Content Area */ +.infoPanel { + text-align: left; + color: var(--color-white); + font-family: 'Heebo', sans-serif; + font-size: 16px; + font-style: italic; + line-height: 24px; + white-space: pre-line; + overflow-wrap: break-word; + word-wrap: break-word; +} + +/* Card Styles */ +.p-card { + background: var(--color-sherpa800) !important; + border: 1px solid var(--color-sherpa600) !important; + border-radius: 8px !important; + box-shadow: none !important; +} + +.p-card .p-card-body { + padding: 32px !important; + color: var(--color-white) !important; +} + +.p-card .p-card-content { + color: var(--color-white) !important; + font-family: 'Heebo', sans-serif !important; + font-size: 16px !important; + line-height: 24px !important; + min-height: 200px; +} + +/* Routes Information */ +.routes-info-box { + background-color: var(--color-sherpa700); + border: 1px solid var(--color-sherpa500); + border-radius: 8px; + padding: 12px 16px; + margin-bottom: 16px; + font-size: 14px; + font-style: normal; + color: var(--color-sherpa200); +} + +/* DataTable Styles */ +.p-datatable { + background: var(--color-sherpa800) !important; + border: 1px solid var(--color-sherpa600) !important; + border-radius: 8px !important; + overflow: hidden !important; + width: 100% !important; + table-layout: fixed !important; +} + +.p-datatable .p-datatable-table { + width: 100% !important; + table-layout: fixed !important; +} + +.p-datatable .p-datatable-header { + background: var(--color-sherpa700) !important; + border-bottom: 1px solid var(--color-sherpa600) !important; + color: var(--color-white) !important; + padding: 16px !important; +} + +.p-datatable .p-datatable-thead > tr > th { + background: var(--color-sherpa700) !important; + border: 1px solid var(--color-sherpa600) !important; + color: var(--color-white) !important; + font-family: 'Exo 2', sans-serif !important; + font-size: 16px !important; + font-weight: 600 !important; + padding: 16px !important; + box-sizing: border-box !important; +} + +.p-datatable .p-datatable-tbody > tr { + background: var(--color-sherpa800) !important; + color: var(--color-white) !important; +} + +.p-datatable .p-datatable-tbody > tr > td { + border: 1px solid var(--color-sherpa600) !important; + padding: 16px !important; + font-family: 'Heebo', sans-serif !important; + font-size: 16px !important; + overflow-wrap: break-word !important; + word-wrap: break-word !important; + box-sizing: border-box !important; +} + +.p-datatable .p-datatable-tbody > tr:hover { + background: rgba(255, 255, 255, 0.05) !important; +} + +/* Empty message in DataTable */ +.p-datatable .p-datatable-emptymessage > td { + color: var(--color-sherpa400) !important; +} + +/* DataTable Column Widths */ +.p-datatable .p-datatable-thead > tr > th:nth-child(1), +.p-datatable .p-datatable-tbody > tr > td:nth-child(1), +.p-datatable .route-column { + width: 60% !important; +} + +.p-datatable .p-datatable-thead > tr > th:nth-child(2), +.p-datatable .p-datatable-tbody > tr > td:nth-child(2), +.p-datatable .limits-column { + width: 40% !important; +} + +.routes-info-box .info-item { + margin-bottom: 8px; +} + +.routes-info-box .info-item:last-child { + margin-bottom: 0; +} + +/* Footer Container */ +.info-card-footer { + text-align: left; + font-size: 14px; + color: var(--color-white); + background-color: var(--color-sherpa900); + border-top: 1px solid var(--color-sherpa600); + padding: 24px 32px; + margin: -1px; + border-radius: 4px 4px 8px 8px; +} + +/* Footer Headings */ +.footer-title { + color: var(--color-sherpa400); + font-size: 20px; + font-family: 'Exo 2', sans-serif; + font-weight: 600; + margin-bottom: 16px; + margin-top: 0; +} + +.footer-subtitle { + color: var(--color-sherpa400); + font-size: 16px; + font-weight: 600; + margin-top: 16px; + margin-bottom: 8px; +} + +.footer-section-break { + margin-top: 24px; + padding-top: 16px; + border-top: 1px solid var(--color-sherpa700); +} + +/* Footer Text */ +.footer-text { + margin: 0 0 16px 0; + line-height: 1.5; +} + +/* Code Examples */ +.code-example { + margin-bottom: 16px; +} + +.code-label { + color: var(--color-sherpa300); + font-size: 13px; +} + +.code-block { + background-color: var(--color-sherpa800); + border: 1px solid var(--color-sherpa700); + padding: 12px; + border-radius: 4px; + overflow: auto; + font-size: 13px; + margin-top: 8px; + color: var(--color-sherpa100); +} + +/* Documentation Link */ +.footer-docs-link { + margin-top: 20px; + padding-top: 16px; + border-top: 1px solid var(--color-sherpa700); + font-size: 14px; + line-height: 1.5; +} + +.footer-docs-link a { + color: var(--color-sherpa400); + text-decoration: underline; + transition: color 0.3s ease; +} + +.footer-docs-link a:hover { + color: var(--color-white); +} + +/* Tablet layout (600px - 1023px) */ +@media (min-width: 600px) and (max-width: 1023px) { + .p-card .p-card-body { + padding: 24px !important; + } + + .p-datatable .p-datatable-thead > tr > th:nth-child(1), + .p-datatable .p-datatable-tbody > tr > td:nth-child(1), + .p-datatable .route-column { + width: 55% !important; + } + + .p-datatable .p-datatable-thead > tr > th:nth-child(2), + .p-datatable .p-datatable-tbody > tr > td:nth-child(2), + .p-datatable .limits-column { + width: 45% !important; + } + + .routes-info-box { + font-size: 13px; + padding: 10px 14px; + } + + .info-card-footer { + padding: 20px 24px; + } + + .footer-title { + font-size: 18px; + } + + .footer-subtitle { + font-size: 15px; + } + + .code-block { + font-size: 12px; + } +} + +/* Mobile layout (up to 599px) */ +@media (max-width: 599px) { + .p-card .p-card-body { + padding: 20px !important; + } + + .p-datatable .p-datatable-thead > tr > th:nth-child(1), + .p-datatable .p-datatable-tbody > tr > td:nth-child(1), + .p-datatable .route-column { + width: 50% !important; + } + + .p-datatable .p-datatable-thead > tr > th:nth-child(2), + .p-datatable .p-datatable-tbody > tr > td:nth-child(2), + .p-datatable .limits-column { + width: 50% !important; + } + + .routes-info-box { + font-size: 12px; + padding: 8px 12px; + } + + .info-card-footer { + padding: 16px 20px; + } + + .footer-title { + font-size: 16px; + margin-bottom: 12px; + } + + .footer-subtitle { + font-size: 14px; + margin-top: 12px; + } + + .footer-text { + font-size: 13px; + margin-bottom: 12px; + } + + .code-label { + font-size: 12px; + } + + .code-block { + font-size: 11px; + padding: 10px; + } + + .footer-docs-link { + font-size: 13px; + } +} diff --git a/ui/src/components/InfoPanelCard.jsx b/ui/src/components/InfoPanelCard.jsx new file mode 100644 index 0000000..d8dcf6d --- /dev/null +++ b/ui/src/components/InfoPanelCard.jsx @@ -0,0 +1,49 @@ +import { Card } from 'primereact/card'; +import './InfoPanelCard.css'; + +const InfoPanelCard = ({ children }) => { + const cardFooter = ( + + ); + + return ( + +
{children || 'Click a button above to get started'}
+
+ ); +}; + +export default InfoPanelCard; diff --git a/ui/src/index.css b/ui/src/index.css index 2693d73..f504da0 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -32,8 +32,19 @@ body { margin: 0; - font-family: 'Heebo', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', - 'Droid Sans', 'Helvetica Neue', sans-serif; + font-family: + 'Heebo', + -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; }