feat(screenshots): replace ai mockups with actual application screenshots and enable package publishing
CI & Build NaxOS Web Dashboard / build (push) Successful in 6m50s
@@ -31,11 +31,26 @@ jobs:
|
||||
- name: Publish Package to Gitea Packages
|
||||
run: |
|
||||
tar -czf naxos-ui-dist.tar.gz -C dist .
|
||||
echo "Uploading naxos-ui-dist.tar.gz to Gitea Packages..."
|
||||
curl -s --fail-with-body -X PUT \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
echo "Uploading naxos-ui-dist.tar.gz to Gitea Packages registry..."
|
||||
TOKEN="${{ secrets.PACKAGE_TOKEN }}"
|
||||
if [ -z "$TOKEN" ]; then
|
||||
TOKEN="${{ secrets.GITEA_TOKEN }}"
|
||||
fi
|
||||
RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
--upload-file naxos-ui-dist.tar.gz \
|
||||
"${{ github.server_url }}/api/packages/naxos/generic/naxos-ui/1.0.0/naxos-ui-dist.tar.gz" || echo "Generic package uploaded or exists"
|
||||
"${{ github.server_url }}/api/packages/naxos/generic/naxos-ui/1.0.0/naxos-ui-dist.tar.gz")
|
||||
STATUS=$(echo "$RESPONSE" | grep "HTTP_STATUS:" | cut -d: -f2)
|
||||
BODY=$(echo "$RESPONSE" | grep -v "HTTP_STATUS:")
|
||||
echo "Package upload HTTP status: $STATUS"
|
||||
if [ "$STATUS" -eq 201 ] || [ "$STATUS" -eq 200 ] || [ "$STATUS" -eq 204 ]; then
|
||||
echo "Successfully published naxos-ui-dist.tar.gz to Gitea Packages!"
|
||||
elif [ "$STATUS" -eq 409 ]; then
|
||||
echo "Package version 1.0.0 already published in Gitea Packages: $BODY"
|
||||
else
|
||||
echo "Failed to publish package to Gitea Packages ($STATUS): $BODY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Upload UI Assets Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -6,11 +6,13 @@ The **NaxOS Web Dashboard** is a modern, responsive, non-technical web interface
|
||||
|
||||
## Dashboard Showcase
|
||||
|
||||
| Overview & Telemetry | OpenZFS Storage & Migration |
|
||||
| Overview & Telemetry | OpenZFS Storage Engine |
|
||||
| :---: | :---: |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| **CNCF Perses Analytics** | **App Store & Runtimes** |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| **Setup & Migration Wizard** | **File Shares Management** |
|
||||
|  |  |
|
||||
|
||||
## Key Features
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 494 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 440 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 617 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 554 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 100 KiB |
@@ -30,9 +30,36 @@ import type {
|
||||
} from './types/index.js';
|
||||
|
||||
export const App: React.FC = () => {
|
||||
const [currentTab, setCurrentTab] = useState('overview');
|
||||
const getInitialTab = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
if (hash) return hash;
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const tab = params.get('tab');
|
||||
if (tab) return tab;
|
||||
}
|
||||
return 'overview';
|
||||
};
|
||||
|
||||
const [currentTab, setCurrentTab] = useState(getInitialTab);
|
||||
const [showRebuildModal, setShowRebuildModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const onHashChange = () => {
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
if (hash) setCurrentTab(hash);
|
||||
};
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
return () => window.removeEventListener('hashchange', onHashChange);
|
||||
}, []);
|
||||
|
||||
const selectTab = (tab: string) => {
|
||||
setCurrentTab(tab);
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.hash = tab;
|
||||
}
|
||||
};
|
||||
|
||||
// Application state
|
||||
const [status, setStatus] = useState<SystemStatus | null>(null);
|
||||
const [pools, setPools] = useState<ZfsPool[]>([]);
|
||||
@@ -161,7 +188,7 @@ export const App: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen overflow-hidden bg-slate-950 text-slate-100 font-sans">
|
||||
<Sidebar currentTab={currentTab} onSelectTab={setCurrentTab} />
|
||||
<Sidebar currentTab={currentTab} onSelectTab={selectTab} />
|
||||
|
||||
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
||||
<Topbar
|
||||
@@ -177,7 +204,7 @@ export const App: React.FC = () => {
|
||||
status={status}
|
||||
pools={pools}
|
||||
apps={installedApps}
|
||||
onNavigate={setCurrentTab}
|
||||
onNavigate={selectTab}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||