feat: remove Administration workspace — keep only Verwaltung + Suche
- Delete all ops page components (ReviewQueue, AIMonitoring, Governance, SourceMonitoring, ActivityTimeline, SignalPipeline) - Remove OPERATIONS workspace from AppShell config, nav order, path detection - Remove all /ops/* routes from App.tsx - Remove WorkspaceType.OPERATIONS from allowedWorkspaces in authService, sessionStore, permissions - Keep MarketIntelligence page (already moved to /supply/market-intelligence) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { create } from 'zustand'
|
||||
import type { AssistantContext, AssistantMessage } from '../domain/assistant'
|
||||
|
||||
interface AssistantState {
|
||||
isOpen: boolean
|
||||
context: AssistantContext | null
|
||||
messages: AssistantMessage[]
|
||||
isLoading: boolean
|
||||
error: string | null
|
||||
|
||||
open: () => void
|
||||
close: () => void
|
||||
setContext: (ctx: AssistantContext) => void
|
||||
updateContext: (partial: Partial<AssistantContext>) => void
|
||||
addMessage: (msg: AssistantMessage) => void
|
||||
setLoading: (v: boolean) => void
|
||||
setError: (e: string | null) => void
|
||||
clearConversation: () => void
|
||||
}
|
||||
|
||||
export const useAssistantStore = create<AssistantState>((set) => ({
|
||||
isOpen: false,
|
||||
context: null,
|
||||
messages: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
|
||||
open: () => set({ isOpen: true }),
|
||||
close: () => set({ isOpen: false }),
|
||||
setContext: (ctx) => set({ context: ctx }),
|
||||
updateContext: (partial) => set((s) => ({ context: s.context ? { ...s.context, ...partial } : null })),
|
||||
addMessage: (msg) => set((s) => ({ messages: [...s.messages, msg] })),
|
||||
setLoading: (v) => set({ isLoading: v }),
|
||||
setError: (e) => set({ error: e }),
|
||||
clearConversation: () => set({ messages: [], error: null }),
|
||||
}))
|
||||
@@ -0,0 +1,28 @@
|
||||
import { create } from 'zustand'
|
||||
import type { UnifiedMatchResult } from '../domain/unifiedResult'
|
||||
|
||||
const MAX_COMPARE_ITEMS = 4
|
||||
|
||||
interface CompareState {
|
||||
compareItems: UnifiedMatchResult[]
|
||||
addToCompare: (result: UnifiedMatchResult) => void
|
||||
removeFromCompare: (matchId: string) => void
|
||||
clearCompare: () => void
|
||||
isInCompare: (matchId: string) => boolean
|
||||
isFull: () => boolean
|
||||
}
|
||||
|
||||
export const useCompareStore = create<CompareState>((set, get) => ({
|
||||
compareItems: [],
|
||||
addToCompare: (result) =>
|
||||
set((state) => {
|
||||
if (state.compareItems.length >= MAX_COMPARE_ITEMS) return state
|
||||
if (state.compareItems.some(i => i.matchId === result.matchId)) return state
|
||||
return { compareItems: [...state.compareItems, result] }
|
||||
}),
|
||||
removeFromCompare: (matchId) =>
|
||||
set((state) => ({ compareItems: state.compareItems.filter(i => i.matchId !== matchId) })),
|
||||
clearCompare: () => set({ compareItems: [] }),
|
||||
isInCompare: (matchId) => get().compareItems.some(i => i.matchId === matchId),
|
||||
isFull: () => get().compareItems.length >= MAX_COMPARE_ITEMS,
|
||||
}))
|
||||
@@ -0,0 +1,61 @@
|
||||
import { create } from 'zustand'
|
||||
import { WorkspaceType } from '../domain/enums'
|
||||
|
||||
export const RightPanelContentType = {
|
||||
AI_CONTEXT: 'ai_context',
|
||||
DETAIL_PREVIEW: 'detail_preview',
|
||||
COMPARE_PREVIEW: 'compare_preview',
|
||||
ACTIVITY_FEED: 'activity_feed',
|
||||
} as const
|
||||
export type RightPanelContentType = typeof RightPanelContentType[keyof typeof RightPanelContentType]
|
||||
|
||||
interface LayoutState {
|
||||
activeWorkspace: WorkspaceType
|
||||
sidebarCollapsed: boolean
|
||||
pinnedPanels: string[]
|
||||
isRightPanelOpen: boolean
|
||||
rightPanelContentType: RightPanelContentType | null
|
||||
compareTrayVisible: boolean
|
||||
selectedResultId: string | null
|
||||
notificationsOpen: boolean
|
||||
// Actions
|
||||
setActiveWorkspace: (workspace: WorkspaceType) => void
|
||||
toggleSidebar: () => void
|
||||
pinPanel: (panelId: string) => void
|
||||
unpinPanel: (panelId: string) => void
|
||||
openRightPanel: (type: RightPanelContentType) => void
|
||||
closeRightPanel: () => void
|
||||
toggleRightPanel: (type: RightPanelContentType) => void
|
||||
setCompareTrayVisible: (visible: boolean) => void
|
||||
setSelectedResultId: (id: string | null) => void
|
||||
toggleNotifications: () => void
|
||||
}
|
||||
|
||||
export const useLayoutStore = create<LayoutState>((set, get) => ({
|
||||
activeWorkspace: WorkspaceType.SUPPLY,
|
||||
sidebarCollapsed: false,
|
||||
pinnedPanels: [],
|
||||
isRightPanelOpen: false,
|
||||
rightPanelContentType: null,
|
||||
compareTrayVisible: false,
|
||||
selectedResultId: null,
|
||||
notificationsOpen: false,
|
||||
|
||||
setActiveWorkspace: (workspace) => set({ activeWorkspace: workspace }),
|
||||
toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })),
|
||||
pinPanel: (panelId) => set((s) => ({ pinnedPanels: [...s.pinnedPanels, panelId] })),
|
||||
unpinPanel: (panelId) => set((s) => ({ pinnedPanels: s.pinnedPanels.filter(id => id !== panelId) })),
|
||||
openRightPanel: (type) => set({ isRightPanelOpen: true, rightPanelContentType: type }),
|
||||
closeRightPanel: () => set({ isRightPanelOpen: false, rightPanelContentType: null }),
|
||||
toggleRightPanel: (type) => {
|
||||
const { isRightPanelOpen, rightPanelContentType } = get()
|
||||
if (isRightPanelOpen && rightPanelContentType === type) {
|
||||
set({ isRightPanelOpen: false, rightPanelContentType: null })
|
||||
} else {
|
||||
set({ isRightPanelOpen: true, rightPanelContentType: type })
|
||||
}
|
||||
},
|
||||
setCompareTrayVisible: (visible) => set({ compareTrayVisible: visible }),
|
||||
setSelectedResultId: (id) => set({ selectedResultId: id }),
|
||||
toggleNotifications: () => set((s) => ({ notificationsOpen: !s.notificationsOpen })),
|
||||
}))
|
||||
@@ -0,0 +1,17 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
interface MatchCenterState {
|
||||
selectedPropertyId: string | null
|
||||
selectedNeedId: string | null
|
||||
setSelectedProperty: (id: string | null) => void
|
||||
setSelectedNeed: (id: string | null) => void
|
||||
clearSelection: () => void
|
||||
}
|
||||
|
||||
export const useMatchCenterStore = create<MatchCenterState>((set) => ({
|
||||
selectedPropertyId: null,
|
||||
selectedNeedId: null,
|
||||
setSelectedProperty: (id) => set({ selectedPropertyId: id }),
|
||||
setSelectedNeed: (id) => set({ selectedNeedId: id }),
|
||||
clearSelection: () => set({ selectedPropertyId: null, selectedNeedId: null }),
|
||||
}))
|
||||
@@ -0,0 +1,63 @@
|
||||
import { create } from 'zustand'
|
||||
import { UserRole, WorkspaceType } from '../domain/enums'
|
||||
|
||||
export interface MockUser {
|
||||
id: string
|
||||
email: string
|
||||
name: string
|
||||
role: UserRole
|
||||
organizationId: string
|
||||
organizationName: string
|
||||
allowedWorkspaces: WorkspaceType[]
|
||||
}
|
||||
|
||||
export const SessionStatus = {
|
||||
UNAUTHENTICATED: 'unauthenticated',
|
||||
AUTHENTICATED: 'authenticated',
|
||||
EXPIRED: 'expired',
|
||||
RESTRICTED: 'restricted',
|
||||
ONBOARDING: 'onboarding',
|
||||
} as const
|
||||
export type SessionStatus = typeof SessionStatus[keyof typeof SessionStatus]
|
||||
|
||||
interface SessionState {
|
||||
currentUser: MockUser | null
|
||||
activeOrganizationId: string | null
|
||||
isAuthenticated: boolean
|
||||
sessionStatus: SessionStatus
|
||||
login: (user: MockUser) => void
|
||||
logout: () => void
|
||||
switchOrganization: (organizationId: string) => void
|
||||
setSessionStatus: (status: SessionStatus) => void
|
||||
}
|
||||
|
||||
const mockUser: MockUser = {
|
||||
id: 'user-001',
|
||||
email: 'admin@ideal-sharing.ch',
|
||||
name: 'Admin User',
|
||||
role: UserRole.ORGANIZATION_ADMIN,
|
||||
organizationId: 'org-wincasa',
|
||||
organizationName: 'Wincasa AG',
|
||||
allowedWorkspaces: [WorkspaceType.SUPPLY, WorkspaceType.DEMAND, WorkspaceType.OPERATIONS],
|
||||
}
|
||||
|
||||
export const useSessionStore = create<SessionState>((set) => ({
|
||||
currentUser: mockUser,
|
||||
activeOrganizationId: mockUser.organizationId,
|
||||
isAuthenticated: true,
|
||||
sessionStatus: SessionStatus.AUTHENTICATED,
|
||||
login: (user) => set({
|
||||
currentUser: user,
|
||||
activeOrganizationId: user.organizationId,
|
||||
isAuthenticated: true,
|
||||
sessionStatus: SessionStatus.AUTHENTICATED,
|
||||
}),
|
||||
logout: () => set({
|
||||
currentUser: null,
|
||||
activeOrganizationId: null,
|
||||
isAuthenticated: false,
|
||||
sessionStatus: SessionStatus.UNAUTHENTICATED,
|
||||
}),
|
||||
switchOrganization: (organizationId) => set({ activeOrganizationId: organizationId }),
|
||||
setSessionStatus: (status) => set({ sessionStatus: status }),
|
||||
}))
|
||||
@@ -0,0 +1,20 @@
|
||||
import { create } from 'zustand'
|
||||
import type { ShortlistItemInput } from '../domain/shortlist'
|
||||
|
||||
interface ShortlistStore {
|
||||
selectedShortlistId: string | null
|
||||
dialogOpen: boolean
|
||||
pendingItem: ShortlistItemInput | null
|
||||
setSelectedShortlist: (id: string | null) => void
|
||||
openAddDialog: (item: ShortlistItemInput) => void
|
||||
closeAddDialog: () => void
|
||||
}
|
||||
|
||||
export const useShortlistStore = create<ShortlistStore>((set) => ({
|
||||
selectedShortlistId: null,
|
||||
dialogOpen: false,
|
||||
pendingItem: null,
|
||||
setSelectedShortlist: (id) => set({ selectedShortlistId: id }),
|
||||
openAddDialog: (item) => set({ dialogOpen: true, pendingItem: item }),
|
||||
closeAddDialog: () => set({ dialogOpen: false, pendingItem: null }),
|
||||
}))
|
||||
@@ -0,0 +1,27 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
export type ToastSeverity = 'success' | 'error' | 'warning' | 'info'
|
||||
|
||||
export interface ToastMessage {
|
||||
id: string
|
||||
message: string
|
||||
severity: ToastSeverity
|
||||
duration?: number
|
||||
}
|
||||
|
||||
interface ToastState {
|
||||
toasts: ToastMessage[]
|
||||
showToast: (message: string, severity?: ToastSeverity, duration?: number) => void
|
||||
dismissToast: (id: string) => void
|
||||
}
|
||||
|
||||
export const useToastStore = create<ToastState>((set) => ({
|
||||
toasts: [],
|
||||
showToast: (message, severity = 'success', duration = 4000) => {
|
||||
const id = crypto.randomUUID()
|
||||
set((s) => ({ toasts: [...s.toasts, { id, message, severity, duration }] }))
|
||||
},
|
||||
dismissToast: (id) => {
|
||||
set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) }))
|
||||
},
|
||||
}))
|
||||
Reference in New Issue
Block a user