chore: bootstrap devopspanel scaffold

This commit is contained in:
dev-agent
2026-04-28 16:06:16 +00:00
commit 99878bae4c
13 changed files with 325 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
node_modules
dist
.vite
.DS_Store
*.tsbuildinfo

37
README.md Normal file
View File

@@ -0,0 +1,37 @@
# devopspanel
Baseline scaffold for the DevOps orchestration admin panel.
## Baseline stack choice
- Existing Mission Control backend runtime
- React 19
- TypeScript
- Vite 7
- OpenAPI-first typed integration in the next slices
## Setup
```bash
npm install
```
## Run
```bash
npm run dev
```
Application defaults to `http://localhost:5173`.
## Quality checks
```bash
npm run build
npm run lint
```
## Current scope
- Minimal frontend shell only.
- No live API wiring in the baseline commit.
- `environment summary` stays mock-only until a worker-visible route exists in the current backend runtime.
## Next development slice
1. Add app shell sections for `timeline`, `audit feed`, and `environment summary`.
2. Introduce typed DTO/fixture layer from the approved first-slice contracts.
3. Keep `environment summary` behind mock data until the live route is available.

25
eslint.config.js Normal file
View File

@@ -0,0 +1,25 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
},
)

12
index.html Normal file
View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>devopspanel</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

29
package.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "devopspanel",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@eslint/js": "^9.31.0",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^5.0.0",
"eslint": "^9.31.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.37.0",
"vite": "^7.0.4"
}
}

93
src/App.css Normal file
View File

@@ -0,0 +1,93 @@
:root {
color: #e5eef7;
background: #0b1220;
font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
body {
margin: 0;
min-width: 320px;
min-height: 100vh;
background:
radial-gradient(circle at top, rgba(56, 189, 248, 0.18), transparent 35%),
linear-gradient(180deg, #020617 0%, #0f172a 100%);
}
#root {
min-height: 100vh;
}
.app-shell {
max-width: 1120px;
margin: 0 auto;
padding: 48px 24px 64px;
}
.hero-card,
.next-slice-card,
.status-grid article {
background: rgba(15, 23, 42, 0.8);
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: 24px;
box-shadow: 0 20px 45px rgba(2, 6, 23, 0.35);
}
.hero-card,
.next-slice-card {
padding: 28px;
}
.eyebrow {
margin: 0 0 12px;
text-transform: uppercase;
letter-spacing: 0.12em;
font-size: 0.8rem;
color: #38bdf8;
}
h1,
h2,
p,
ul {
margin-top: 0;
}
h1 {
font-size: clamp(2.4rem, 5vw, 4rem);
margin-bottom: 16px;
}
.lede {
font-size: 1.1rem;
line-height: 1.7;
max-width: 68ch;
color: #cbd5e1;
}
.status-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 20px;
margin: 28px 0;
}
.status-grid article {
padding: 22px;
}
.status-grid h2,
.next-slice-card h2 {
margin-bottom: 12px;
font-size: 1.1rem;
}
.status-grid p,
.next-slice-card li {
color: #cbd5e1;
line-height: 1.6;
}
.next-slice-card ul {
padding-left: 20px;
margin-bottom: 0;
}

48
src/App.tsx Normal file
View File

@@ -0,0 +1,48 @@
import './App.css'
const nextSlice = [
'Timeline block wired to approved DTO fixtures',
'Audit feed block wired to approved DTO fixtures',
'Environment summary placeholder kept mock-only',
]
function App() {
return (
<main className="app-shell">
<section className="hero-card">
<p className="eyebrow">Mission Control / devopspanel</p>
<h1>DevOps orchestration admin panel</h1>
<p className="lede">
Baseline frontend scaffold for the first implementation slice. This commit
intentionally sets the stack and shell contract before live integrations.
</p>
</section>
<section className="status-grid">
<article>
<h2>Baseline stack</h2>
<p>React + TypeScript + Vite</p>
</article>
<article>
<h2>Current state</h2>
<p>Scaffold only, no runtime data wiring yet</p>
</article>
<article>
<h2>Guardrail</h2>
<p>Environment summary remains mock-only until live worker-visible route exists</p>
</article>
</section>
<section className="next-slice-card">
<h2>Next development slice</h2>
<ul>
{nextSlice.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</section>
</main>
)
}
export default App

15
src/index.css Normal file
View File

@@ -0,0 +1,15 @@
html {
color-scheme: dark;
}
body {
margin: 0;
}
* {
box-sizing: border-box;
}
a {
color: inherit;
}

10
src/main.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

22
tsconfig.app.json Normal file
View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

16
tsconfig.node.json Normal file
View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true
},
"include": ["vite.config.ts"]
}

6
vite.config.ts Normal file
View File

@@ -0,0 +1,6 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})