mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-07 23:01:06 +02:00
Added back navigation to user management page
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Loader2, Plus, Plug, AlertTriangle, RotateCcw, Bell, Download, RefreshCw, ExternalLink, Globe, Droplets, Thermometer, FileText, Edit2, Send, CheckCircle, XCircle, History, Trash2, Upload, Zap, TrendingUp, Calendar, DollarSign, Power, PowerOff, Key, Copy, Database, Info, X, Shield, Printer, Cylinder, Wifi, Home, Video, Users, Lock, Unlock } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { api } from '../api/client';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { formatDateOnly } from '../utils/date';
|
||||
@@ -29,9 +29,13 @@ import { useTheme, type ThemeStyle, type DarkBackground, type LightBackground, t
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { Palette } from 'lucide-react';
|
||||
|
||||
const validTabs = ['general', 'network', 'plugs', 'notifications', 'filament', 'apikeys', 'virtual-printer', 'users'] as const;
|
||||
type TabType = typeof validTabs[number];
|
||||
|
||||
export function SettingsPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { t, i18n } = useTranslation();
|
||||
const { showToast, showPersistentToast, dismissToast } = useToast();
|
||||
const { authEnabled, user, refreshAuth } = useAuth();
|
||||
@@ -50,7 +54,22 @@ export function SettingsPage() {
|
||||
const [editingTemplate, setEditingTemplate] = useState<NotificationTemplate | null>(null);
|
||||
const [showLogViewer, setShowLogViewer] = useState(false);
|
||||
const [defaultView, setDefaultViewState] = useState<string>(getDefaultView());
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'network' | 'plugs' | 'notifications' | 'filament' | 'apikeys' | 'virtual-printer' | 'users'>('general');
|
||||
|
||||
// Initialize tab from URL params
|
||||
const tabParam = searchParams.get('tab');
|
||||
const initialTab = tabParam && validTabs.includes(tabParam as TabType) ? tabParam as TabType : 'general';
|
||||
const [activeTab, setActiveTab] = useState<TabType>(initialTab);
|
||||
|
||||
// Update URL when tab changes
|
||||
const handleTabChange = (tab: TabType) => {
|
||||
setActiveTab(tab);
|
||||
if (tab === 'general') {
|
||||
searchParams.delete('tab');
|
||||
} else {
|
||||
searchParams.set('tab', tab);
|
||||
}
|
||||
setSearchParams(searchParams, { replace: true });
|
||||
};
|
||||
const [showCreateAPIKey, setShowCreateAPIKey] = useState(false);
|
||||
const [newAPIKeyName, setNewAPIKeyName] = useState('');
|
||||
const [newAPIKeyPermissions, setNewAPIKeyPermissions] = useState({
|
||||
@@ -481,7 +500,7 @@ export function SettingsPage() {
|
||||
{/* Tab Navigation */}
|
||||
<div className="flex gap-1 mb-6 border-b border-bambu-dark-tertiary overflow-x-auto">
|
||||
<button
|
||||
onClick={() => setActiveTab('general')}
|
||||
onClick={() => handleTabChange('general')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px ${
|
||||
activeTab === 'general'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -491,7 +510,7 @@ export function SettingsPage() {
|
||||
General
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('plugs')}
|
||||
onClick={() => handleTabChange('plugs')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px flex items-center gap-2 ${
|
||||
activeTab === 'plugs'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -507,7 +526,7 @@ export function SettingsPage() {
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('notifications')}
|
||||
onClick={() => handleTabChange('notifications')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px flex items-center gap-2 ${
|
||||
activeTab === 'notifications'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -523,7 +542,7 @@ export function SettingsPage() {
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('filament')}
|
||||
onClick={() => handleTabChange('filament')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px flex items-center gap-2 ${
|
||||
activeTab === 'filament'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -534,7 +553,7 @@ export function SettingsPage() {
|
||||
Filament
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('network')}
|
||||
onClick={() => handleTabChange('network')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px flex items-center gap-2 ${
|
||||
activeTab === 'network'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -546,7 +565,7 @@ export function SettingsPage() {
|
||||
<span className={`w-2 h-2 rounded-full ${mqttStatus?.enabled ? 'bg-green-400' : 'bg-gray-500'}`} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('apikeys')}
|
||||
onClick={() => handleTabChange('apikeys')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px flex items-center gap-2 ${
|
||||
activeTab === 'apikeys'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -562,7 +581,7 @@ export function SettingsPage() {
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('virtual-printer')}
|
||||
onClick={() => handleTabChange('virtual-printer')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px flex items-center gap-2 ${
|
||||
activeTab === 'virtual-printer'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -574,7 +593,7 @@ export function SettingsPage() {
|
||||
<span className={`w-2 h-2 rounded-full ${virtualPrinterRunning ? 'bg-green-400' : 'bg-gray-500'}`} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('users')}
|
||||
onClick={() => handleTabChange('users')}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 -mb-px flex items-center gap-2 ${
|
||||
activeTab === 'users'
|
||||
? 'text-bambu-green border-bambu-green'
|
||||
@@ -2973,8 +2992,8 @@ export function SettingsPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||
user.role === 'admin'
|
||||
? 'bg-purple-500/20 text-purple-300'
|
||||
user.role === 'admin'
|
||||
? 'bg-purple-500/20 text-purple-300'
|
||||
: 'bg-blue-500/20 text-blue-300'
|
||||
}`}>
|
||||
{user.role === 'admin' ? 'Admin' : 'User'}
|
||||
@@ -2992,7 +3011,7 @@ export function SettingsPage() {
|
||||
<Users className="w-4 h-4" />
|
||||
Manage Users
|
||||
</Button>
|
||||
|
||||
|
||||
{user?.role === 'admin' && (
|
||||
<Button
|
||||
onClick={() => setShowDisableAuthConfirm(true)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { X, Plus, Edit2, Trash2, Save, Loader2, Users as UsersIcon, Shield } from 'lucide-react';
|
||||
import { X, Plus, Edit2, Trash2, Save, Loader2, Users as UsersIcon, Shield, ArrowLeft } from 'lucide-react';
|
||||
import { api } from '../api/client';
|
||||
import type { UserCreate, UserUpdate } from '../api/client';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
@@ -10,6 +11,7 @@ import { Card, CardContent, CardHeader } from '../components/Card';
|
||||
import { ConfirmModal } from '../components/ConfirmModal';
|
||||
|
||||
export function UsersPage() {
|
||||
const navigate = useNavigate();
|
||||
const { user: currentUser } = useAuth();
|
||||
const { showToast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
@@ -128,14 +130,23 @@ export function UsersPage() {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white flex items-center gap-2">
|
||||
<UsersIcon className="w-6 h-6 text-bambu-green" />
|
||||
User Management
|
||||
</h1>
|
||||
<p className="text-sm text-bambu-gray mt-1">
|
||||
Manage users and their access to your Bambuddy instance
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={() => navigate('/settings?tab=users')}
|
||||
className="p-2 rounded-lg bg-bambu-dark-secondary hover:bg-bambu-dark-tertiary text-bambu-gray hover:text-white transition-colors"
|
||||
title="Back to Settings"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white flex items-center gap-2">
|
||||
<UsersIcon className="w-6 h-6 text-bambu-green" />
|
||||
User Management
|
||||
</h1>
|
||||
<p className="text-sm text-bambu-gray mt-1">
|
||||
Manage users and their access to your Bambuddy instance
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -199,8 +210,8 @@ export function UsersPage() {
|
||||
</select>
|
||||
) : (
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||
user.role === 'admin'
|
||||
? 'bg-purple-500/20 text-purple-300'
|
||||
user.role === 'admin'
|
||||
? 'bg-purple-500/20 text-purple-300'
|
||||
: 'bg-blue-500/20 text-blue-300'
|
||||
}`}>
|
||||
{user.role}
|
||||
@@ -209,8 +220,8 @@ export function UsersPage() {
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||
user.is_active
|
||||
? 'bg-bambu-green/20 text-bambu-green'
|
||||
user.is_active
|
||||
? 'bg-bambu-green/20 text-bambu-green'
|
||||
: 'bg-red-500/20 text-red-400'
|
||||
}`}>
|
||||
{user.is_active ? 'Active' : 'Inactive'}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -23,7 +23,7 @@
|
||||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-BK0uiRXP.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BNJwArDp.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-COZJGA_d.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user