feat: Phase 2 feature flags — hide/show sidebar entries per client

- usePhase2Features.js hook: manages localStorage-based feature flags for all 8 Phase 2
  sidebar entries (notes, expenses, contacts, customers, tabs, waste, kds, schedule)
  All default to enabled=true. setFeatureEnabled() writes to localStorage and dispatches
  a 'phase2features' event so all subscribers re-render immediately without page reload
- Phase2FeaturesTab.jsx: new Settings tab 'Λειτουργίες' — toggle each Phase 2 page on/off
  with master 'Ενεργοποίηση όλων' / 'Απενεργοποίηση όλων' buttons
  Note shown: changes are localStorage-only, instant, no reload needed, no data deleted
- SettingsPage.jsx: adds 'Λειτουργίες' and reinstates 'Developer' tab (was missing import)
- Sidebar.jsx: subscribes to phase2features + storage events; filters ALL_NAV to remove
  disabled Phase 2 entries; each phase2 item has a phase2 id that isFeatureEnabled() checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 04:01:54 +03:00
parent 49f19ed65c
commit 0fc027eb5f
4 changed files with 177 additions and 9 deletions

View File

@@ -4,6 +4,8 @@ import ColoursTab from './tabs/ColoursTab'
import OperationTab from './tabs/OperationTab'
import PrintFontsTab from './tabs/PrintFontsTab'
import SecurityTab from './tabs/SecurityTab'
import DevelopmentTab from './tabs/DevelopmentTab'
import Phase2FeaturesTab from './tabs/Phase2FeaturesTab'
import { TabGroup } from '../../ui/Tabs'
const TABS = [
@@ -12,6 +14,8 @@ const TABS = [
{ id: 'operation', label: 'Λειτουργία' },
{ id: 'colours', label: 'Εμφάνιση' },
{ id: 'print-fonts', label: 'Εκτύπωση' },
{ id: 'features', label: 'Λειτουργίες' },
{ id: 'development', label: 'Developer' },
]
export default function SettingsPage() {
@@ -26,6 +30,8 @@ export default function SettingsPage() {
{activeTab === 'operation' && <OperationTab />}
{activeTab === 'colours' && <ColoursTab />}
{activeTab === 'print-fonts' && <PrintFontsTab />}
{activeTab === 'features' && <Phase2FeaturesTab />}
{activeTab === 'development' && <DevelopmentTab />}
</div>
</div>
)

View File

@@ -0,0 +1,83 @@
import { PHASE2_FEATURE_DEFS, isFeatureEnabled, setFeatureEnabled, usePhase2Features } from '../../../hooks/usePhase2Features'
function Toggle({ checked, onChange }) {
return (
<button
role="switch"
aria-checked={checked}
onClick={() => onChange(!checked)}
style={{
width: 44, height: 24, borderRadius: 999, border: 'none', cursor: 'pointer',
background: checked ? '#16a34a' : '#d1d5db',
position: 'relative', transition: 'background 150ms', flexShrink: 0,
}}
>
<span style={{
position: 'absolute', top: 3, left: checked ? 23 : 3,
width: 18, height: 18, borderRadius: '50%', background: 'white',
transition: 'left 150ms', boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
}} />
</button>
)
}
export default function Phase2FeaturesTab() {
const features = usePhase2Features()
const allOn = features.every(f => f.enabled)
const allOff = features.every(f => !f.enabled)
function toggleAll(value) {
PHASE2_FEATURE_DEFS.forEach(f => setFeatureEnabled(f.id, value))
}
return (
<div className="space-y-4 max-w-xl">
<div className="rounded-lg border border-blue-200 bg-blue-50 px-4 py-3 text-xs text-blue-800">
Αυτές οι επιλογές ελέγχουν ποιες σελίδες Phase 2 εμφανίζονται στο πλαϊνό μενού.
Απενεργοποίηση <strong>δεν σβήνει δεδομένα</strong> απλώς κρύβει τον σύνδεσμο.
Χρήσιμο για να κρατάτε σελίδες σε εξέλιξη κρυφές από πελάτες.
</div>
<div className="card divide-y divide-gray-100">
{/* Master toggle row */}
<div className="flex items-center justify-between gap-4 px-5 py-3 bg-gray-50/60">
<span className="text-sm font-semibold text-gray-600">Όλες οι λειτουργίες</span>
<div className="flex gap-3">
<button
onClick={() => toggleAll(true)}
disabled={allOn}
className="text-xs font-semibold px-3 py-1 rounded-full border border-green-300 text-green-700 hover:bg-green-50 disabled:opacity-40 disabled:cursor-default"
>
Ενεργοποίηση όλων
</button>
<button
onClick={() => toggleAll(false)}
disabled={allOff}
className="text-xs font-semibold px-3 py-1 rounded-full border border-red-300 text-red-600 hover:bg-red-50 disabled:opacity-40 disabled:cursor-default"
>
Απενεργοποίηση όλων
</button>
</div>
</div>
{features.map(f => (
<div key={f.id} className="flex items-center justify-between gap-4 px-5 py-3">
<div>
<p className="text-sm font-medium text-gray-700">{f.label}</p>
<p className="text-xs text-gray-400 mt-0.5 font-mono">{f.route}</p>
</div>
<Toggle
checked={f.enabled}
onChange={val => setFeatureEnabled(f.id, val)}
/>
</div>
))}
</div>
<p className="text-xs text-gray-400">
Οι ρυθμίσεις αποθηκεύονται τοπικά στον browser. Ισχύουν αμέσως χωρίς ανανέωση.
</p>
</div>
)
}