feat: add SVG assets for React and Vite, configure Tailwind CSS, and set up main entry point
- Added react.svg and vite.svg to the assets directory. - Created index.css for global styles and imported Google Fonts and Tailwind CSS. - Set up main.jsx as the entry point for the React application with StrictMode. - Configured Vite with React and Tailwind CSS plugins in vite.config.js.
This commit is contained in:
1 parent
bab0e310a9
commit
e91c7b3fa8
16 files changed
+2158
No files matched your search
+24
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": ["react", "oxc"],
|
||||
"rules": {
|
||||
"react/rules-of-hooks": "error",
|
||||
"react/only-export-components": ["warn", { "allowConstantExport": true }]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# React + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some Oxlint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
|
||||
|
||||
## React Compiler
|
||||
|
||||
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
|
||||
|
||||
## Expanding the Oxlint configuration
|
||||
|
||||
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and Oxlint's TypeScript related rules in your project.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>iatracker</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+1977
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "iatracker",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "oxlint",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"lucide-react": "^1.39.0",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-router-dom": "^7.18.3",
|
||||
"tailwindcss": "^4.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react-dom": "^19.2.4",
|
||||
"@vitejs/plugin-react": "^6.1.0",
|
||||
"oxlint": "^1.79.0",
|
||||
"vite": "^8.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filtLine truncated
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
@@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
||||
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
||||
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
||||
</symbol>
|
||||
<symbol id="discord-icon" viewBox="0 0 20 19">
|
||||
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
||||
</symbol>
|
||||
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
||||
</symbol>
|
||||
<symbol id="github-icon" viewBox="0 0 19 19">
|
||||
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
||||
</symbol>
|
||||
<symbol id="social-icon" viewBox="0 0 20 20">
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
||||
</symbol>
|
||||
<symbol id="x-icon" viewBox="0 0 19 19">
|
||||
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,8 @@
|
||||
:root{--ink:#182522;--muted:#71807b;--line:#e2e7e2;--paper:#fff;--canvas:#f7f8f5;--coral:#ee7258;--teal:#275c55}
|
||||
.app-shell{min-height:100vh;display:flex;background:var(--canvas)}.sidebar{width:246px;flex:none;min-height:100vh;padding:28px 15px 18px;display:flex;flex-direction:column;background:#163c37;color:#dce8e0}.brand,.workspace-switcher,.profile,.nav-link,.header-actions,.page-intro,.panel-heading,.table-toolbar,.incident-name,.table-location,.site-row,.form-footer{display:flex;align-items:center}.brand{gap:10px;padding:0 12px 31px;color:#fff}.brand strong{display:block;font:700 22px 'Space Grotesk',sans-serif;letter-spacing:-.6px}.brand span,.profile span{display:block;color:#9bb4ab;font-size:10px;margin-top:2px}.brand-mark{width:34px;height:34px;display:grid;place-items:center;border-radius:9px;background:var(--coral);color:#fff}.close-menu{display:none!important;margin-left:auto}.workspace-switcher{gap:10px;padding:11px;border:1px solid #316158;border-radius:7px;margin-bottom:27px}.workspace-switcher strong{display:block;color:#fff;font-size:13px}.workspace-switcher>span{margin-left:auto}.workspace-avatar,.profile-avatar,.header-avatar{display:grid;place-items:center;border-radius:50%;font-weight:700}.workspace-avatar{width:29px;height:29px;color:#143d37;background:#f1c865;font:700 11px 'DM Mono'}.eyebrow{display:block;color:#8da29b;font:500 10px 'DM Mono',monospace;letter-spacing:.09em;text-transform:uppercase}.primary-nav,.nav-section{border-bottom:1px solid #315b54;padding-bottom:23px;margin-bottom:24px}.nav-section>.eyebrow{padding:0 12px 8px}.nav-link{gap:12px;height:42px;padding:0 12px;color:#aac0b8;text-decoration:none;font-size:13px;border-radius:6px}.nav-link:hover,.nav-link.active{color:#fff;background:#28574f}.nav-link em{font:11px 'DM Mono';background:var(--coral);color:#fff;border-radius:10px;padding:2px 7px;margin-left:auto;font-style:normal}.sidebar-bottom{margin-top:auto}.profile{border-top:1px solid #315b54;gap:10px;padding:20px 8px 0;margin-top:16px}.profile strong{display:block;color:#fff;font-size:12px}.profile-avatar{width:31px;height:31px;color:#fff;background:#5c8274;font:11px 'DM Mono'}.more-dots{color:#91aaa0;letter-spacing:2px;margin-left:auto}
|
||||
.main-area{min-width:0;flex:1}.topbar{height:72px;border-bottom:1px solid var(--line);padding:0 42px;display:flex;align-items:center;justify-content:space-between;background:#ffffff99}.breadcrumb{display:flex;gap:10px;align-items:center;color:#94a09b;font-size:12px}.breadcrumb strong{color:var(--ink)}.header-actions{gap:15px}.icon-button{display:grid;place-items:center;border:0;background:transparent;color:#72817d;cursor:pointer;padding:6px}.notification{position:relative}.notification i{position:absolute;width:6px;height:6px;background:var(--coral);border-radius:50%;top:4px;right:3px;border:1px solid var(--canvas)}.header-avatar{width:32px;height:32px;background:#d2e0d8;color:#275c55;font:11px 'DM Mono'}.mobile-menu{display:none}.content{max-width:1360px;margin:0 auto;padding:40px 42px 60px}.page-intro{justify-content:space-between;margin-bottom:31px}.page-intro h1{font:600 34px 'Space Grotesk',sans-serif;letter-spacing:-1px;margin:7px 0 5px;color:var(--ink)}.page-intro p{margin:0;color:var(--muted);font-size:14px}.primary-button,.secondary-button,.light-button{display:inline-flex;gap:8px;align-items:center;justify-content:center;border:0;border-radius:5px;padding:11px 14px;cursor:pointer;text-decoration:none;font-size:12px;font-weight:700}.primary-button{background:var(--coral);color:#fff;box-shadow:0 4px 12px #ee725833}.secondary-button{background:#fff;border:1px solid var(--line);color:var(--ink)}.panel{background:var(--paper);border:1px solid var(--line);border-radius:8px;box-shadow:0 5px 20px #1b252305}.metric-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:13px;margin-bottom:20px}.metric-card{padding:18px;border:1px solid var(--line);border-radius:8px;background:#fff}.metric-card>span{display:block;font-size:12px;color:var(--muted);margin:15px 0 6px}.metric-card strong{display:block;color:var(--ink);font:600 30px 'Space Grotesk';letter-spacing:-1px}.metric-card small{display:block;color:#7d8b86;margin-top:9px;font-size:11px}.metric-card small b{color:#38a17f}.metric-icon{width:35px;height:35px;display:grid;place-items:center;border-radius:7px}.metric-icon.coral{background:#fbe3dd;color:var(--coral)}.metric-icon.yellow{background:#fff2cd;color:#b47a11}.metric-icon.mint{background:#dcefe4;color:#3d9576}.metric-icon.blue{background:#dcebf0;color:#39819c}
|
||||
.dashboard-grid{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(260px,.8fr);gap:20px;margin-bottom:20px}.bottom-grid{display:grid;grid-template-columns:1.3fr .9fr;gap:20px}.panel-heading{justify-content:space-between;padding:24px 24px 18px}.panel-heading h2{margin:6px 0 0;font:600 19px 'Space Grotesk';color:var(--ink)}.text-link{display:inline-flex;align-items:center;gap:5px;color:var(--coral);font-size:12px;font-weight:700;text-decoration:none}.table-scroll{overflow:auto}.incidents-panel table{min-width:720px}.full-panel{overflow:hidden}.full-panel table{min-width:900px}.table-toolbar{gap:10px;padding:18px 24px;border-bottom:1px solid var(--line)}.search-field,.filter-group{display:flex;align-items:center;gap:8px;height:38px;border:1px solid var(--line);border-radius:5px;padding:0 11px;color:#85938e}.search-field{flex:1;max-width:350px}.search-field input{border:0;outline:0;width:100%;color:var(--ink);background:transparent;font-size:12px}.filter-group{margin-left:auto}.filter-group select,select{border:0;outline:0;color:var(--ink);background:transparent;font-size:12px}
|
||||
table{width:100%;border-collapse:collapse;text-align:left}th{color:#9aa6a2;font:500 10px 'DM Mono';text-transform:uppercase;letter-spacing:.06em;padding:12px 10px;border-bottom:1px solid var(--line)}th:first-child,td:first-child{padding-left:24px}th:last-child,td:last-child{padding-right:18px}td{padding:14px 10px;border-bottom:1px solid #edf0ec;color:#53625d;font-size:11px;white-space:nowrap}tr:last-child td{border-bottom:0}.incident-name{gap:10px;min-width:250px}.incident-name strong{display:block;color:var(--ink);font-size:12px;font-weight:600}.incident-name small,.table-location small{display:block;color:#9aa6a2;font-size:10px;margin-top:4px}.incident-dot{width:28px;height:28px;display:grid;place-items:center;border-radius:7px}.incident-dot.amber{background:#fff0c8;color:#b68118}.incident-dot.blue{background:#e0eff3;color:#38859d}.incident-dot.red{background:#fbe2dd;color:#dc634e}.incident-dot.green{background:#def0e5;color:#3e9777}.severity{display:inline-flex;align-items:center;gap:5px}.severity i{width:6px;height:6px;border-radius:50%;background:#db6652}.severity.medium i{background:#d8a631}.severity.low i{background:#52a57e}.status{padding:5px 8px;background:#fff0d2;color:#9a7319;border-radius:4px;font-size:10px}.status.resolved{background:#dff1e6;color:#398461}.status.review{background:#fbe0da;color:#c75e4b}.table-location{gap:4px;flex-wrap:wrap;max-width:120px}.table-location small{width:100%;margin-left:18px}.date-cell{color:#87938f}.response-panel{padding-bottom:18px}.progress-ring{width:126px;height:126px;margin:12px auto 20px;border:10px solid #e8f0e9;border-top-color:var(--coral);border-right-color:var(--coral);border-radius:50%;display:grid;place-items:center;transform:rotate(28deg)}.progress-ring>div{transform:rotate(-28deg);text-align:center}.progress-ring strong{display:block;font:600 25px 'Space Grotesk'}.progress-ring span{color:var(--muted);font-size:10px}.action-row{padding:12px 24px 0}.action-row>div:first-child{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}.action-row strong{color:var(--ink);font:11px 'DM Mono'}.progress-track{height:6px;background:#edf1ed;border-radius:20px;overflow:hidden;margin-top:8px}.progress-track i{display:block;height:100%;background:var(--coral);border-radius:20px}.location-bar{padding:7px 24px}.location-bar>div:first-child{display:flex;justify-content:space-between;font-size:11px;color:#63726d}.location-bar strong{font:11px 'DM Mono';color:var(--ink)}.location-bar .progress-track i{background:#70a59a}.chart-panel{min-height:260px}.chart{position:relative;height:170px;margin:4px 25px 22px}.chart svg{width:100%;height:100%;position:absolute;inset:0}.grid-lines{position:absolute;inset:0;background:repeating-linear-gradient(to bottom,transparent 0,transparent 41px,#edf0ec 42px)}.chart-x{display:flex;justify-content:space-between;position:absolute;left:0;right:0;bottom:-20px;color:#a0aba7;font:10px 'DM Mono'}
|
||||
.location-layout{display:grid;grid-template-columns:1fr 1fr;gap:20px}.site-row{gap:13px;padding:18px 24px;border-top:1px solid var(--line)}.site-icon{width:38px;height:38px;display:grid;place-items:center;border-radius:7px}.site-icon.coral{background:#fbe3dd;color:var(--coral)}.site-icon.yellow{background:#fff1ca;color:#b9821e}.site-icon.blue{background:#dfedf1;color:#3c849a}.site-row>div:nth-child(2){flex:1}.site-row strong,.site-row span{display:block}.site-row strong{font-size:13px}.site-row span{font-size:11px;color:var(--muted);margin-top:4px}.site-row>b{font:15px 'DM Mono'}.site-row b small{font:10px 'DM Sans';color:var(--muted)}.map-placeholder{min-height:360px;position:relative;overflow:hidden;background:#e3eee7}.map-grid{position:absolute;inset:0;opacity:.45;background-image:linear-gradient(30deg,transparent 47%,#b1cfc0 48%,transparent 49%),linear-gradient(115deg,transparent 47%,#b1cfc0 48%,transparent 49%);background-size:80px 80px;transform:rotate(12deg) scale(1.4)}.map-label{position:absolute;left:28px;bottom:27px;padding:11px 14px;background:#fff;border-radius:6px;box-shadow:0 5px 15px #275c5520}.map-label svg{color:var(--coral);vertical-align:middle;margin-right:5px}.map-label strong{font-size:12px}.map-label span{display:block;color:var(--muted);font-size:10px;margin-top:4px}.map-pin{position:absolute;width:14px;height:14px;background:var(--coral);border:3px solid #fff;border-radius:50%;box-shadow:0 2px 5px #275c5550}.pin-one{left:40%;top:30%}.pin-two{left:64%;top:48%}.pin-three{left:24%;top:65%}
|
||||
.analytics-grid{display:grid;grid-template-columns:1.3fr 1fr 1fr;gap:20px}.insight-card{min-height:240px;padding:26px;display:flex;flex-direction:column;align-items:flex-start}.insight-card.dark{background:#1d4841;color:#dfece5;border-color:#1d4841}.insight-card.dark .eyebrow{color:#9dc0b3}.insight-card h2{font:600 24px 'Space Grotesk';line-height:1.15;margin:20px 0 10px}.insight-card p{color:var(--muted);font-size:12px;line-height:1.6}.dark p{color:#a7c0b8}.light-button{margin-top:auto;background:#edf6ef;color:var(--teal)}.big-stat{margin:32px 0 25px;font:600 25px 'Space Grotesk'}.stat-line{display:flex;justify-content:space-between;width:100%;color:var(--muted);font-size:11px}.insight-card>.progress-track{width:100%;margin-top:10px}.mini-bars{display:flex;align-items:flex-end;gap:7px;height:70px;width:100%;margin-top:auto}.mini-bars i{flex:1;min-height:15px;background:#76a99d;border-radius:3px 3px 0 0}.narrow-content{max-width:900px}.report-form{overflow:hidden}.form-section{padding:28px 36px;border-bottom:1px solid var(--line)}.form-heading{display:flex;gap:13px;margin-bottom:24px}.step{color:var(--coral);font:12px 'DM Mono';padding-top:3px}.form-heading h2{margin:0 0 4px;font:600 19px 'Space Grotesk'}.form-heading p{margin:0;color:var(--muted);font-size:12px}.report-form label{display:block;color:#586762;font-size:11px;font-weight:600;margin-bottom:17px}.report-form input,.report-form select,.report-form textarea{display:block;width:100%;border:1px solid var(--line);border-radius:5px;padding:11px 12px;margin-top:7px;outline:0;color:var(--ink);background:#fbfcfa;font-size:12px}.report-form textarea{resize:vertical}.report-form input:focus,.report-form textarea:focus,.report-form select:focus{border-color:#83b3a5;box-shadow:0 0 0 3px #83b3a522}.form-row{display:grid;grid-template-columns:1fr 1fr;gap:18px}.report-form label em{float:right;font-style:normal;color:#9ba7a2;font-weight:400}.form-footer{justify-content:space-between;padding:20px 36px;color:var(--muted);font-size:11px}.form-footer>span:before{content:'✓';color:#3d9c7e;margin-right:6px}.form-footer>div{display:flex;gap:9px}
|
||||
@media(max-width:900px){.sidebar{width:210px}.topbar,.content{padding-left:24px;padding-right:24px}.metric-grid{grid-template-columns:repeat(2,1fr)}.bottom-grid,.location-layout,.analytics-grid{grid-template-columns:1fr}.dashboard-grid{grid-template-columns:1fr}}@media(max-width:650px){.sidebar{position:fixed;z-index:10;left:-250px;transition:left .2s;box-shadow:8px 0 30px #163c3740}.sidebar.mobile-visible{left:0}.close-menu,.mobile-menu{display:grid!important}.topbar{height:62px;padding:0 17px}.mobile-menu{margin-right:10px}.breadcrumb span:not(:last-child){display:none}.content{padding:28px 17px 40px}.page-intro{align-items:flex-start;gap:20px;flex-direction:column}.page-intro h1{font-size:29px}.metric-grid{gap:9px}.metric-card{padding:13px}.metric-card strong{font-size:25px}.metric-card>span{margin-top:10px}.metric-card small{font-size:9px}.panel-heading{padding:19px 17px 14px}.table-toolbar{flex-wrap:wrap;padding:14px 17px}.search-field{max-width:none;flex-basis:100%}.filter-group{margin-left:0}.incidents-panel{overflow:hidden}.form-section{padding:24px 20px}.form-row{grid-template-columns:1fr;gap:0}.form-footer{padding:17px 20px;align-items:flex-end;gap:15px;flex-direction:column}.form-footer>div{width:100%;justify-content:flex-end}}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { useState } from 'react'
|
||||
import { BrowserRouter, NavLink, Route, Routes, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { AlertTriangle, ArrowUpRight, BarChart3, Bell, Building2, CheckCircle2, ClipboardList, FileText, Filter, Flame, LayoutDashboard, MapPin, Menu, Plus, Search, Settings, ShieldCheck, Siren, X } from 'lucide-react'
|
||||
import './App.css'
|
||||
|
||||
const seed = [
|
||||
{ id: 'INC-2408', title: 'Forklift reversed into pallet stack', type: 'Near miss', severity: 'High', status: 'Investigation', building: 'Warehouse 04', location: 'Loading bay C', date: 'Aug 28, 2024', reporter: 'Jordan Lee', color: 'amber' },
|
||||
{ id: 'INC-2407', title: 'Wet floor near employee entrance', type: 'Hazard', severity: 'Medium', status: 'Action open', building: 'Building A', location: 'Ground floor lobby', date: 'Aug 27, 2024', reporter: 'Maya Chen', color: 'blue' },
|
||||
{ id: 'INC-2406', title: 'Hand laceration during material handling', type: 'Injury', severity: 'High', status: 'Review required', building: 'Fabrication 02', location: 'Line 3', date: 'Aug 26, 2024', reporter: 'Sam Rivera', color: 'red' },
|
||||
{ id: 'INC-2405', title: 'Damaged guardrail on mezzanine', type: 'Property damage', severity: 'Low', status: 'Resolved', building: 'Warehouse 01', location: 'Mezzanine east', date: 'Aug 24, 2024', reporter: 'Priya Shah', color: 'green' },
|
||||
]
|
||||
const navItems = [{ label: 'Overview', to: '/', icon: LayoutDashboard }, { label: 'Incidents', to: '/incidents', icon: ClipboardList, count: '24' }, { label: 'Locations', to: '/locations', icon: MapPin }, { label: 'Analytics', to: '/analytics', icon: BarChart3 }]
|
||||
|
||||
export default function App() {
|
||||
const [incidents, setIncidents] = useState(seed)
|
||||
const [mobileOpen, setMobileOpen] = useState(false)
|
||||
return <BrowserRouter><div className="app-shell"><Sidebar open={mobileOpen} close={() => setMobileOpen(false)} /><main className="main-area"><Header openMenu={() => setMobileOpen(true)} /><Routes><Route path="/" element={<Overview incidents={incidents} />} /><Route path="/incidents" element={<Incidents incidents={incidents} />} /><Route path="/locations" element={<Locations />} /><Route path="/analytics" element={<Analytics />} /><Route path="/report" element={<ReportIncident addIncident={(incident) => setIncidents([incident, ...incidents])} />} /></Routes></main></div></BrowserRouter>
|
||||
}
|
||||
function Sidebar({ open, close }) { return <aside className={`sidebar ${open ? 'mobile-visible' : ''}`}><div className="brand"><div className="brand-mark"><ShieldCheck size={20} /></div><div><strong>Safesite</strong><span>Incident intelligence</span></div><button className="icon-button close-menu" onClick={close} aria-label="Close menu"><X size={20} /></button></div><div className="workspace-switcher"><div className="workspace-avatar">NC</div><div><span className="eyebrow">Workspace</span><strong>Northstar Co.</strong></div><span>⌄</span></div><nav className="primary-nav">{navItems.map(({ label, to, icon: Icon, count }) => <NavLink key={to} to={to} onClick={close} className={({ isActive }) => isActive ? 'nav-link active' : 'nav-link'}><Icon size={18} /><span>{label}</span>{count && <em>{count}</em>}</NavLink>)}</nav><div className="nav-section"><span className="eyebrow">Manage</span><NavLink to="/report" onClick={close} className="nav-link"><Plus size={18} /><span>Report an incident</span></NavLink><a href="#resources" className="nav-link"><FileText size={18} /><span>Resources</span></a></div><div className="sidebar-bottom"><a href="#settings" className="nav-link"><Settings size={18} /><span>Settings</span></a><div className="profile"><div className="profile-avatar">JL</div><div><strong>Jordan Lee</strong><span>Safety manager</span></div><span className="more-dots">•••</span></div></div></aside> }
|
||||
function Header({ openMenu }) { const location = useLocation(); const titles = { '/': 'Overview', '/incidents': 'Incidents', '/locations': 'Locations', '/analytics': 'Analytics', '/report': 'Report an incident' }; return <header className="topbar"><button className="icon-button mobile-menu" onClick={openMenu} aria-label="Open menu"><Menu size={21} /></button><div className="breadcrumb"><span>Safety workspace</span><span>/</span><strong>{titles[location.pathname]}</strong></div><div className="header-actions"><button className="icon-button" aria-label="Search"><Search size={19} /></button><button className="icon-button notification" aria-label="Notifications"><Bell size={19} /><i /></button><div className="header-avatar">JL</div></div></header> }
|
||||
function Intro({ eyebrow, title, copy, action }) { return <div className="page-intro"><div><span className="eyebrow">{eyebrow}</span><h1>{title}</h1>{copy && <p>{copy}</p>}</div>{action}</div> }
|
||||
function Heading({ eyebrow, title, link, to }) { return <div className="panel-heading"><div><span className="eyebrow">{eyebrow}</span><h2>{title}</h2></div>{link && <NavLink to={to} className="text-link">{link} <ArrowUpRight size={15} /></NavLink>}</div> }
|
||||
function Overview({ incidents }) { return <div className="content"><Intro eyebrow="Tuesday, August 29, 2024" title="Good morning, Jordan" copy="Here’s what’s happening across your safety program." action={<NavLink className="primary-button" to="/report"><Plus size={17} /> Report incident</NavLink>} /><div className="metric-grid"><Metric label="Open incidents" value="18" delta="3 from last week" icon={Siren} tone="coral" /><Metric label="Due this week" value="07" delta="2 due today" icon={Bell} tone="yellow" /><Metric label="Avg. close time" value="4.2d" delta="0.8d faster" icon={CheckCircle2} tone="mint" /><Metric label="Program health" value="92%" delta="Up 4% this month" icon={ShieldCheck} tone="blue" /></div><div className="dashboard-grid"><section className="panel incidents-panel"><Heading eyebrow="Needs attention" title="Recent incidents" link="View all" to="/incidents" /><Table incidents={incidents} /></section><section className="panel response-panel"><Heading eyebrow="Response progress" title="Open actions" /><div className="progress-ring"><div><strong>68%</strong><span>complete</span></div></div><Action label="Root cause review" value="8 / 12" percent="66%" /><Action label="Corrective actions" value="14 / 21" percent="67%" /><Action label="Team sign-offs" value="28 / 32" percent="87%" /></section></div><div className="bottom-grid"><section className="panel chart-panel"><Heading eyebrow="Signal check" title="Incident trend" /><div className="chart"><div className="grid-lines" /><svg viewBox="0 0 620 190" preserveAspectRatio="none" aria-label="Incident trend"><path d="M0,142 C30,132 42,146 69,130 S105,116 131,126 S166,110 191,120 S225,103 250,115 S285,89 314,103 S355,104 380,85 S412,100 439,72 S468,88 498,61 S530,71 555,52 S590,65 620,32" fill="none" stroke="#ee7258" strokeWidth="3" /><path d="M0,142 C30,132 42,146 69,130 S105,116 131,126 S166,110 191,120 S225,103 250,115 S285,89 314,103 S355,104 380,85 S412,100 439,72 S468,88 498,61 S530,71 555,52 S590,65 620,32 L620,190 L0,190Z" fill="#ee7258" opacity=".12" /></svg><div className="chart-x"><span>Aug 01</span><span>Aug 08</span><span>Aug 15</span><span>Aug 22</span><span>Aug 29</span></div></div></section><section className="panel location-panel"><Heading eyebrow="By location" title="Where incidents happen" link="Explore" to="/locations" /><Location label="North Campus" value="12" width="82%" /><Location label="South Campus" value="7" width="52%" /><Location label="West Campus" value="5" width="38%" /><Location label="Remote / other" value="3" width="24%" /></section></div></div> }
|
||||
function Metric({ label, value, delta, icon: Icon, tone }) { return <div className="metric-card"><div className={`metric-icon ${tone}`}><Icon size={19} /></div><span>{label}</span><strong>{value}</strong><small><b>↗</b> {delta}</small></div> }
|
||||
function Action({ label, value, percent }) { return <div className="action-row"><div><span>{label}</span><strong>{value}</strong></div><div className="progress-track"><i style={{ width: percent }} /></div></div> }
|
||||
function Location({ label, value, width }) { return <div className="location-bar"><div><span>{label}</span><strong>{value}</strong></div><div className="progress-track"><i style={{ width }} /></div></div> }
|
||||
function Table({ incidents }) { return <div className="table-scroll"><table><thead><tr><th>Incident</th><th>Type</th><th>Severity</th><th>Location</th><th>Status</th><th>Date</th><th /></tr></thead><tbody>{incidents.slice(0, 5).map((incident) => <tr key={incident.id}><td><div className="incident-name"><span className={`incident-dot ${incident.color}`}><AlertTriangle size={14} /></span><div><strong>{incident.title}</strong><small>{incident.id} · {incident.reporter}</small></div></div></td><td>{incident.type}</td><td><span className={`severity ${incident.severity.toLowerCase()}`}><i />{incident.severity}</span></td><td><div className="table-location"><MapPin size={14} />{incident.location}<small>{incident.building}</small></div></td><td><span className={`status ${incident.status === 'Resolved' ? 'resolved' : incident.status.includes('Review') ? 'review' : ''}`}>{incident.status}</span></td><td className="date-cell">{incident.date}</td><td><button className="icon-button" aria-label="More actions"><span className="more-dots">•••</span></button></td></tr>)}</tbody></table></div> }
|
||||
function Incidents({ incidents }) { const [query, setQuery] = useState(''); const [filter, setFilter] = useState('All incidents'); const filtered = incidents.filter((incident) => `${incident.title} ${incident.id} ${incident.location}`.toLowerCase().includes(query.toLowerCase()) && (filter === 'All incidents' || incident.type === filter)); return <div className="content"><Intro eyebrow="Safety register" title="Incidents" copy="A complete record of reported events, hazards, and learnings." action={<NavLink className="primary-button" to="/report"><Plus size={17} /> Report incident</NavLink>} /><section className="panel full-panel"><div className="table-toolbar"><div className="search-field"><Search size={17} /><input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="Search incidents" /></div><div className="filter-group"><Filter size={16} /><select value={filter} onChange={(event) => setFilter(event.target.value)}><option>All incidents</option><option>Near miss</option><option>Hazard</option><option>Injury</option><option>Property damage</option></select></div><button className="secondary-button"><FileText size={16} /> Export</button></div><Table incidents={filtered} /></section></div> }
|
||||
function Locations() { const locations = [['North Campus', 'Building A, Warehouse 04, Lab 2', 12, 'coral'], ['South Campus', 'Fabrication 02, Yard, Office block', 7, 'yellow'], ['West Campus', 'Warehouse 01, Loading dock', 5, 'blue']]; return <div className="content"><Intro eyebrow="Site map" title="Locations" copy="Understand incident patterns across every level of your workplace." action={<button className="primary-button"><Plus size={17} /> Add location</button>} /><div className="location-layout"><section className="panel location-tree"><Heading eyebrow="Sites" title="Location register" />{locations.map(([name, detail, count, tone]) => <div className="site-row" key={name}><div className={`site-icon ${tone}`}><Building2 size={19} /></div><div><strong>{name}</strong><span>{detail}</span></div><b>{count}<small> incidents</small></b><span>⌄</span></div>)}</section><section className="panel map-placeholder"><div className="map-grid" /><div className="map-label"><MapPin size={17} /><strong>North Campus</strong><span>12 incidents this year</span></div><i className="map-pin pin-one" /><i className="map-pin pin-two" /><i className="map-pin pin-three" /></section></div></div> }
|
||||
function Analytics() { return <div className="content"><Intro eyebrow="Program intelligence" title="Analytics" copy="Patterns that help your team prevent the next incident." /><div className="analytics-grid"><div className="panel insight-card dark"><Flame size={22} /><span className="eyebrow">Top signal</span><h2>Material handling is your highest-risk activity.</h2><p>42% of incidents in the last 90 days relate to movement or storage of materials.</p><button className="light-button">View contributing incidents <ArrowUpRight size={16} /></button></div><div className="panel insight-card"><span className="eyebrow">Most common root cause</span><strong className="big-stat">Inadequate guarding</strong><div className="stat-line"><span>8 incidents</span><span>31% of total</span></div><div className="progress-track"><i style={{ width: '31%' }} /></div></div><div className="panel insight-card"><span className="eyebrow">Response quality</span><strong className="big-stat">94%</strong><p>of corrective actions are completed on time.</p><div className="mini-bars"><i style={{ height: '38%' }} /><i style={{ height: '54%' }} /><i style={{ height: '45%' }} /><i style={{ height: '72%' }} /><i style={{ height: '86%' }} /><i style={{ height: '94%' }} /></div></div></div></div> }
|
||||
function ReportIncident({ addIncident }) { const navigate = useNavigate(); const [form, setForm] = useState({ title: '', type: 'Near miss', severity: 'Medium', site: 'North Campus', building: 'Building A', location: '', details: '', cause: '', response: '' }); const update = (key) => (event) => setForm({ ...form, [key]: event.target.value }); const submit = (event) => { event.preventDefault(); if (!form.title || !form.location) return; addIncident({ ...form, id: `INC-${2410 + Math.floor(Math.random() * 9)}`, status: 'New report', date: 'Aug 29, 2024', reporter: 'Jordan Lee', color: form.severity === 'High' ? 'red' : 'amber' }); navigate('/incidents'); }; return <div className="content narrow-content"><Intro eyebrow="New report" title="Report an incident" copy="Capture the facts while they are fresh. You can add investigation details later." /><form className="panel report-form" onSubmit={submit}><FormSection number="01" title="What happened?" copy="Start with a clear, factual description."><label>Incident title<input required value={form.title} onChange={update('title')} placeholder="e.g. Slip near loading dock" /></label><div className="form-row"><label>Incident type<select value={form.type} onChange={update('type')}><option>Near miss</option><option>Hazard</option><option>Injury</option><option>Property damage</option></select></label><label>Severity<select value={form.severity} onChange={update('severity')}><option>Low</option><option>Medium</option><option>High</option></select></label></div><label>What happened? <em>Optional</em><textarea value={form.details} onChange={update('details')} placeholder="Describe what you observed, without assumptions..." rows="4" /></label></FormSection><FormSection number="02" title="Where did it happen?" copy="Pinpoint the location so patterns become visible."><div className="form-row"><label>Site<select value={form.site} onChange={update('site')}><option>North Campus</option><option>South Campus</option><option>West Campus</option></select></label><label>Building<select value={form.building} onChange={update('building')}><option>Building A</option><option>Warehouse 04</option><option>Fabrication 02</option></select></label></div><label>Specific location<input required value={form.location} onChange={update('location')} placeholder="e.g. Ground floor lobby, Line 3" /></label></FormSection><FormSection number="03" title="Response & root cause" copy="Add what you know now. These can be refined during investigation."><label>Immediate response <em>Optional</em><textarea value={form.response} onChange={update('response')} placeholder="e.g. Area isolated, supervisor notified..." rows="3" /></label><label>Suspected root cause <em>Optional</em><input value={form.cause} onChange={update('cause')} placeholder="e.g. Inadequate guarding" /></label></FormSection><div className="form-footer"><span>Save a draft at any time.</span><div><NavLink to="/" className="secondary-button">Cancel</NavLink><button className="primary-button" type="submit">Submit report <ArrowUpRight size={16} /></button></div></div></form></div> }
|
||||
function FormSection({ number, title, copy, children }) { return <div className="form-section"><div className="form-heading"><span className="step">{number}</span><div><h2>{title}</h2><p>{copy}</p></div></div>{children}</div> }
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="77" height="47" fill="none" aria-labelledby="vite-logo-title" viewBox="0 0 77 47"><title id="vite-logo-title">Vite</title><style>.parenthesis{fill:#000}@media (prefers-color-scheme:dark){.parenthesis{fill:#fff}}</style><path fill="#9135ff" d="M40.151 45.71c-.663.844-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.493c-.92 0-1.457-1.04-.92-1.788l7.479-10.471c1.07-1.498 0-3.578-1.842-3.578H15.443c-.92 0-1.456-1.04-.92-1.788l9.696-13.576c.213-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.472c-1.07 1.497 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.087.89 1.83L40.153 45.712z"/><mask id="a" width="48" height="47" x="14" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M40.047 45.71c-.663.843-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.389c-.92 0-1.457-1.04-.92-1.788l7.479-10.472c1.07-1.497 0-3.578-1.842-3.578H15.34c-.92 0-1.456-1.04-.92-1.788l9.696-13.575c.213-.297.556-.474.92-.474H53.93c.92 0 1.456 1.04.92 1.788L47.37 13.03c-1.07 1.498 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.088.89 1.831L40.049 45.712z"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#eee6ff" rx="5.508" ry="14.704" transform="rotate(269.814 20.96 11.29)scale(-1 1)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#eee6ff" rx="10.399" ry="29.851" transform="rotate(89.814 -16.902 -8.275)scale(1 -1)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#8900ff" rx="5.508" ry="30.487" transform="rotate(89.814 -19.197 -7.127)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.928 4.177)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.738 5.52)scale(1 -1)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#eee6ff" rx="14.072" ry="22.078" transform="rotate(93.35 31.245 55.578)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx="14.592" cy="9.743" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(39.51 14.592 9.743)"/></g><g filter="url(#k)"><ellipse cx="61.728" cy="-5.321" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 61.728 -5.32)"/></g><g filter="url(#l)"><ellipse cx="55.618" cy="7.104" fill="#00c2ff" rx="5.971" ry="9.665" transform="rotate(37.892 55.618 7.104)"/></g><g filter="url(#m)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#n)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#o)"><ellipse cx="49.857" cy="30.678" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 49.857 30.678)"/></g><g filter="url(#p)"><ellipse cx="52.623" cy="33.171" fill="#00c2ff" rx="5.971" ry="15.297" transform="rotate(37.892 52.623 33.17)"/></g></g><path d="M6.919 0c-9.198 13.166-9.252 33.575 0 46.789h6.215c-9.25-13.214-9.196-33.623 0-46.789zm62.424 0h-6.215c9.198 13.166 9.252 33.575 0 46.789h6.215c9.25-13.214 9.196-33.623 0-46.789" class="parenthesis"/><defs><filter id="b" width="60.045" height="41.654" x="-5.564" y="16.92" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-40.407" y="-6.762" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-35.435" y="2.801" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-30.84" y="20.8" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-29.307" y="21.949" color-interpolation-filters="sRGB" filterUnits="usLine truncated
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
@@ -0,0 +1,8 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=DM+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap');
|
||||
@import "tailwindcss";
|
||||
|
||||
:root { font-family: 'DM Sans', sans-serif; color: #1b2523; background: #f7f8f5; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; min-width: 320px; }
|
||||
button, input, select, textarea { font: inherit; }
|
||||
button, a { -webkit-tap-highlight-color: transparent; }
|
||||
@@ -0,0 +1,10 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
})
|
||||
Reference in new issue
Block a user