Corrección 1 — Delete de proceso restaurado + Corrección 2 — Patrón userId en repos
This commit is contained in:
@@ -9,6 +9,7 @@ import { supabaseProcessRepo } from '@/persistence/supabase/process-repo'
|
||||
import type { Process, Activity, GatewayConfig } from '@/domain/types'
|
||||
import { bpmnTypeToGatewayType } from '@/domain/types'
|
||||
import { useToast } from '@/components/ui/use-toast'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { AppFooter } from '@/components/AppFooter'
|
||||
import { AppHeader } from '@/components/AppHeader'
|
||||
|
||||
@@ -36,6 +37,7 @@ const SAMPLE_PROCESSES = [
|
||||
export function ImportPage() {
|
||||
const navigate = useNavigate()
|
||||
const { toast } = useToast()
|
||||
const { user } = useAuth()
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [loadingSampleId, setLoadingSampleId] = useState<string | null>(null)
|
||||
@@ -113,7 +115,7 @@ export function ImportPage() {
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
supabaseProcessRepo.save(process),
|
||||
supabaseProcessRepo.save(process, user!.id),
|
||||
activityRepo.saveMany(activities),
|
||||
gatewayRepo.saveMany(gateways),
|
||||
])
|
||||
|
||||
@@ -3,8 +3,10 @@ import { useNavigate } from '@tanstack/react-router'
|
||||
import {
|
||||
Upload, Search, Plus, Building2, FolderX, GitBranch,
|
||||
Clock, TrendingUp, BarChart2, FileX2, X, Settings,
|
||||
MoreVertical, Trash2,
|
||||
} from 'lucide-react'
|
||||
import { useLibraryStore } from '@/store/library-store'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { formatCurrency } from '@/lib/format'
|
||||
import type { Process, ProcessGroup, Simulation } from '@/domain/types'
|
||||
import { AppHeader } from '@/components/AppHeader'
|
||||
@@ -78,18 +80,31 @@ function KpiBadge({ process, simulation }: { process: Process; simulation: Simul
|
||||
|
||||
// ─── Process Card ─────────────────────────────────────────────────────────────
|
||||
|
||||
function ProcessCard({ process, simulation, showGroupChip, groupName }: {
|
||||
function ProcessCard({ process, simulation, showGroupChip, groupName, onDelete }: {
|
||||
process: Process
|
||||
simulation: Simulation | null | undefined
|
||||
showGroupChip?: boolean
|
||||
groupName?: string
|
||||
onDelete?: () => void
|
||||
}) {
|
||||
const navigate = useNavigate()
|
||||
const [menuState, setMenuState] = useState<null | 'menu' | 'confirm'>(null)
|
||||
|
||||
// Cierra el menú al hacer click fuera de él
|
||||
useEffect(() => {
|
||||
if (!menuState) return
|
||||
function handleDocClick() { setMenuState(null) }
|
||||
document.addEventListener('click', handleDocClick)
|
||||
return () => document.removeEventListener('click', handleDocClick)
|
||||
}, [menuState])
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-3 px-4 py-3 rounded-xl border border-border bg-card hover:border-border/80 hover:shadow-sm transition-all cursor-pointer"
|
||||
onClick={() => navigate({ to: '/workspace/$processId', params: { processId: process.id } })}
|
||||
className="flex items-center gap-3 px-4 py-3 rounded-xl border border-border bg-card hover:border-border/80 hover:shadow-sm transition-all cursor-pointer group"
|
||||
onClick={() => {
|
||||
if (menuState) { setMenuState(null); return }
|
||||
navigate({ to: '/workspace/$processId', params: { processId: process.id } })
|
||||
}}
|
||||
>
|
||||
{/* Ícono */}
|
||||
<div className="flex-shrink-0 w-9 h-9 rounded-lg flex items-center justify-center"
|
||||
@@ -125,6 +140,60 @@ function ProcessCard({ process, simulation, showGroupChip, groupName }: {
|
||||
<div className="flex-shrink-0 min-w-[90px]">
|
||||
<KpiBadge process={process} simulation={simulation} />
|
||||
</div>
|
||||
|
||||
{/* Menú de opciones (⋮) — en el flujo flex, a la derecha del KPI */}
|
||||
{onDelete && (
|
||||
<div
|
||||
className="relative flex-shrink-0"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
className={`p-1 rounded hover:bg-secondary text-muted-foreground hover:text-foreground transition-all ${menuState === null ? 'opacity-0 group-hover:opacity-100' : 'opacity-100'}`}
|
||||
onClick={(e) => { e.stopPropagation(); setMenuState(menuState === 'menu' ? null : 'menu') }}
|
||||
title="Opciones"
|
||||
>
|
||||
<MoreVertical className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
{menuState === 'menu' && (
|
||||
<div
|
||||
className="absolute right-0 top-7 z-20 bg-card border border-border rounded-lg shadow-lg py-1 min-w-[130px]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
className="flex items-center gap-2 w-full px-3 py-1.5 text-[12px] text-destructive hover:bg-destructive/10 transition-colors"
|
||||
onClick={(e) => { e.stopPropagation(); setMenuState('confirm') }}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
Eliminar
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{menuState === 'confirm' && (
|
||||
<div
|
||||
className="absolute right-0 top-6 z-20 bg-card border border-border rounded-lg shadow-lg p-3 min-w-[170px]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<p className="text-[12px] font-medium mb-2 text-foreground">¿Eliminar proceso?</p>
|
||||
<p className="text-[11px] text-muted-foreground mb-3">Esta acción no se puede deshacer.</p>
|
||||
<div className="flex gap-1.5">
|
||||
<button
|
||||
className="flex-1 text-[11px] px-2 py-1 rounded font-medium text-white transition-opacity hover:opacity-90"
|
||||
style={{ background: '#ef4444' }}
|
||||
onClick={(e) => { e.stopPropagation(); onDelete(); setMenuState(null) }}
|
||||
>
|
||||
Eliminar
|
||||
</button>
|
||||
<button
|
||||
className="flex-1 text-[11px] px-2 py-1 rounded border border-border text-muted-foreground hover:bg-secondary transition-colors"
|
||||
onClick={(e) => { e.stopPropagation(); setMenuState(null) }}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -244,6 +313,7 @@ function HomeView({
|
||||
onImport: (groupId?: string) => void
|
||||
}) {
|
||||
const { groups, processes, settings, createGroup } = useLibraryStore()
|
||||
const { user } = useAuth()
|
||||
const [search, setSearch] = useState('')
|
||||
const [activeTags, setActiveTags] = useState<string[]>([])
|
||||
|
||||
@@ -402,7 +472,7 @@ function HomeView({
|
||||
})}
|
||||
<NewGroupCard
|
||||
label={settings.groupLabel}
|
||||
onCreate={(name) => createGroup(name)}
|
||||
onCreate={(name) => createGroup(name, user!.id)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -437,7 +507,7 @@ function ProcessCardWithSim({ process, showGroupChip, groupName }: {
|
||||
showGroupChip?: boolean
|
||||
groupName?: string
|
||||
}) {
|
||||
const { getLatestSimulation } = useLibraryStore()
|
||||
const { getLatestSimulation, deleteProcess } = useLibraryStore()
|
||||
const [simulation, setSimulation] = useState<Simulation | null | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -448,7 +518,15 @@ function ProcessCardWithSim({ process, showGroupChip, groupName }: {
|
||||
return () => { cancelled = true }
|
||||
}, [process.id, getLatestSimulation])
|
||||
|
||||
return <ProcessCard process={process} simulation={simulation} showGroupChip={showGroupChip} groupName={groupName} />
|
||||
return (
|
||||
<ProcessCard
|
||||
process={process}
|
||||
simulation={simulation}
|
||||
showGroupChip={showGroupChip}
|
||||
groupName={groupName}
|
||||
onDelete={() => deleteProcess(process.id)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// ─── Group View ───────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Fragment, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { ArrowUpDown, ArrowUp, ArrowDown, ChevronRight } from 'lucide-react'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { activityColor } from '@/lib/colors'
|
||||
@@ -108,9 +108,8 @@ export function ActivitiesTable({ perActivity, pairedBreakdown, mode, currency,
|
||||
const hasBreakdown = !(act.resourceCostBreakdown.length === 0 && act.expectedDirectCost === 0)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Fragment key={act.activityId}>
|
||||
<TableRow
|
||||
key={act.activityId}
|
||||
ref={(el) => { if (el) rowRefs.current.set(act.bpmnElementId, el) }}
|
||||
onClick={() => onRowClick(act.bpmnElementId)}
|
||||
className={`cursor-pointer ${isHighlighted ? 'bg-primary/5 ring-1 ring-inset ring-primary/20' : ''}`}
|
||||
@@ -217,7 +216,7 @@ export function ActivitiesTable({ perActivity, pairedBreakdown, mode, currency,
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</>
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</TableBody>
|
||||
|
||||
@@ -151,9 +151,12 @@ export const HeatmapCanvas = forwardRef<HeatmapCanvasHandle, HeatmapCanvasProps>
|
||||
setIsImported(false)
|
||||
|
||||
const eventBus = viewer.get('eventBus') as any
|
||||
let cancelled = false
|
||||
|
||||
const onImportDone = () => {
|
||||
eventBus.off('import.done', onImportDone)
|
||||
// Si el viewer fue destruido y reemplazado (p.ej. React Strict Mode), ignorar
|
||||
if (cancelled) return
|
||||
const canvas = viewer.get('canvas') as any
|
||||
try { canvas.zoom('fit-viewport', 'auto') } catch { /* canvas puede no estar listo */ }
|
||||
isImportedRef.current = true
|
||||
@@ -163,12 +166,14 @@ export const HeatmapCanvas = forwardRef<HeatmapCanvasHandle, HeatmapCanvasProps>
|
||||
eventBus.on('import.done', onImportDone)
|
||||
|
||||
viewer.importXML(xml).catch((err: unknown) => {
|
||||
if (cancelled) return
|
||||
// Puede rechazar aunque el diagrama ya se haya renderizado (error de root layer en bpmn-js 18).
|
||||
// 'import.done' ya habrá disparado antes de este catch, por lo que isImported ya es true.
|
||||
console.warn('HeatmapCanvas importXML error (diagrama puede estar parcialmente renderizado):', err)
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
eventBus.off('import.done', onImportDone)
|
||||
}
|
||||
}, [xml])
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { useProcessStore } from '@/store/process-store'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { formatCurrency } from '@/lib/format'
|
||||
import type { Activity } from '@/domain/types'
|
||||
|
||||
@@ -19,6 +20,7 @@ interface ActivityPanelProps {
|
||||
|
||||
export function ActivityPanel({ selectedElementId }: ActivityPanelProps) {
|
||||
const { activities, resources, updateActivity, currentProcess } = useProcessStore()
|
||||
const { user } = useAuth()
|
||||
const [localActivity, setLocalActivity] = useState<Activity | null>(null)
|
||||
const [isDirty, setIsDirty] = useState(false)
|
||||
|
||||
@@ -42,7 +44,7 @@ export function ActivityPanel({ selectedElementId }: ActivityPanelProps) {
|
||||
|
||||
async function handleSave() {
|
||||
if (!localActivity) return
|
||||
await updateActivity(localActivity)
|
||||
await updateActivity(localActivity, user!.id)
|
||||
setIsDirty(false)
|
||||
}
|
||||
|
||||
|
||||
@@ -110,12 +110,17 @@ export function BpmnCanvas({
|
||||
if (!viewer || !xml) return
|
||||
isImportedRef.current = false
|
||||
viewer.importXML(xml).then(({ warnings }) => {
|
||||
// Si el viewer fue destruido y reemplazado (p.ej. React Strict Mode), ignorar
|
||||
if (viewerRef.current !== viewer) return
|
||||
if (warnings.length) console.warn('bpmn-js warnings:', warnings)
|
||||
const canvas = viewer.get('canvas') as any
|
||||
canvas.zoom('fit-viewport', 'auto')
|
||||
isImportedRef.current = true
|
||||
syncAutomationOverlays()
|
||||
}).catch((err: Error) => console.error('Error cargando BPMN:', err))
|
||||
}).catch((err: Error) => {
|
||||
if (viewerRef.current !== viewer) return
|
||||
console.error('Error cargando BPMN:', err)
|
||||
})
|
||||
}, [xml, syncAutomationOverlays])
|
||||
|
||||
// ─── Re-sincronizar overlays cuando cambia la lista de automatizables ─────
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Slider } from '@/components/ui/slider'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { useProcessStore } from '@/store/process-store'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { parseBpmnXml } from '@/domain/bpmn-parser'
|
||||
import { formatPercent } from '@/lib/format'
|
||||
import type { GatewayConfig } from '@/domain/types'
|
||||
@@ -28,6 +29,7 @@ export function isXorSumValid(gw: GatewayConfig): boolean {
|
||||
|
||||
export function GatewayPanel({ selectedElementId }: GatewayPanelProps) {
|
||||
const { gateways, currentProcess, updateGateway, activities } = useProcessStore()
|
||||
const { user } = useAuth()
|
||||
const [localGw, setLocalGw] = useState<GatewayConfig | null>(null)
|
||||
const [isDirty, setIsDirty] = useState(false)
|
||||
|
||||
@@ -82,7 +84,7 @@ export function GatewayPanel({ selectedElementId }: GatewayPanelProps) {
|
||||
|
||||
async function handleSave() {
|
||||
if (!localGw) return
|
||||
await updateGateway(localGw)
|
||||
await updateGateway(localGw, user!.id)
|
||||
setIsDirty(false)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { useProcessStore } from '@/store/process-store'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { formatPercent } from '@/lib/format'
|
||||
|
||||
const CURRENCIES = [
|
||||
@@ -24,6 +25,7 @@ const CURRENCIES = [
|
||||
|
||||
export function GlobalSettingsPanel() {
|
||||
const { currentProcess, updateProcess } = useProcessStore()
|
||||
const { user } = useAuth()
|
||||
const [localName, setLocalName] = useState('')
|
||||
const [localClient, setLocalClient] = useState('')
|
||||
const [localCurrency, setLocalCurrency] = useState('USD')
|
||||
@@ -57,7 +59,7 @@ export function GlobalSettingsPanel() {
|
||||
annualFrequency: Math.max(1, localFrequency),
|
||||
analysisHorizonYears: clampedHorizon,
|
||||
automationInvestment: Math.max(0, localInvestment),
|
||||
})
|
||||
}, user!.id)
|
||||
setLocalHorizon(clampedHorizon)
|
||||
setIsDirty(false)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { useProcessStore } from '@/store/process-store'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { formatCurrency } from '@/lib/format'
|
||||
import type { Resource, ResourceType } from '@/domain/types'
|
||||
|
||||
@@ -36,6 +37,7 @@ const EMPTY_FORM: ResourceFormState = { name: '', type: 'role', costPerHour: ''
|
||||
|
||||
export function ResourcesPanel() {
|
||||
const { currentProcess, resources, addResource, updateResource, deleteResource } = useProcessStore()
|
||||
const { user } = useAuth()
|
||||
const [showForm, setShowForm] = useState(false)
|
||||
const [editingId, setEditingId] = useState<string | null>(null)
|
||||
const [form, setForm] = useState<ResourceFormState>(EMPTY_FORM)
|
||||
@@ -65,7 +67,7 @@ export function ResourcesPanel() {
|
||||
if (editingId) {
|
||||
const existing = resources.find((r) => r.id === editingId)
|
||||
if (!existing) return
|
||||
await updateResource({ ...existing, name: form.name.trim(), type: form.type, costPerHour })
|
||||
await updateResource({ ...existing, name: form.name.trim(), type: form.type, costPerHour }, user!.id)
|
||||
} else {
|
||||
const newResource: Resource = {
|
||||
id: uuidv4(),
|
||||
@@ -74,7 +76,7 @@ export function ResourcesPanel() {
|
||||
type: form.type,
|
||||
costPerHour,
|
||||
}
|
||||
await addResource(newResource)
|
||||
await addResource(newResource, user!.id)
|
||||
}
|
||||
cancelForm()
|
||||
}
|
||||
@@ -186,7 +188,7 @@ export function ResourcesPanel() {
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 text-slate-400 hover:text-destructive"
|
||||
onClick={() => deleteResource(resource.id)}
|
||||
onClick={() => deleteResource(resource.id, user!.id)}
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { useProcessStore } from '@/store/process-store'
|
||||
import { useSimulationStore } from '@/store/simulation-store'
|
||||
import { useSimulate } from '../simulation/useSimulate'
|
||||
import { useLibraryStore } from '@/store/library-store'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { AppHeader } from '@/components/AppHeader'
|
||||
|
||||
type SelectedCategory = 'activity' | 'gateway' | null
|
||||
@@ -30,6 +31,7 @@ export function WorkspacePage() {
|
||||
const { isRunning, resultActual, error: simError, selectActivity, loadLatestForProcess, lastPersistedExecutedAt } = useSimulationStore()
|
||||
const { simulate } = useSimulate()
|
||||
const { toast } = useToast()
|
||||
const { user } = useAuth()
|
||||
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true)
|
||||
const [selectedElementId, setSelectedElementId] = useState<string | null>(null)
|
||||
@@ -122,12 +124,12 @@ export function WorkspacePage() {
|
||||
if (field === 'name') {
|
||||
const trimmed = editValue.trim()
|
||||
if (trimmed && trimmed !== currentProcess?.name) {
|
||||
await updateProcess({ name: trimmed })
|
||||
await updateProcess({ name: trimmed }, user!.id)
|
||||
}
|
||||
} else {
|
||||
const trimmed = editValue.trim()
|
||||
if (trimmed !== (currentProcess?.clientName ?? '')) {
|
||||
await updateProcess({ clientName: trimmed })
|
||||
await updateProcess({ clientName: trimmed }, user!.id)
|
||||
}
|
||||
}
|
||||
setEditingField(null)
|
||||
@@ -141,14 +143,14 @@ export function WorkspacePage() {
|
||||
if (!normalized) return
|
||||
const current = currentProcess?.tags ?? []
|
||||
if (current.some((t) => t === normalized)) return
|
||||
updateProcess({ tags: [...current, normalized] })
|
||||
updateProcess({ tags: [...current, normalized] }, user!.id)
|
||||
setTagInput('')
|
||||
setShowTagSuggestions(false)
|
||||
}
|
||||
|
||||
function removeTag(tag: string) {
|
||||
const current = currentProcess?.tags ?? []
|
||||
updateProcess({ tags: current.filter((t) => t !== tag) })
|
||||
updateProcess({ tags: current.filter((t) => t !== tag) }, user!.id)
|
||||
}
|
||||
|
||||
function handleTagKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { supabase } from '@/lib/supabase'
|
||||
import type { ProcessGroup } from '@/domain/types'
|
||||
|
||||
async function getCurrentUserId(): Promise<string> {
|
||||
const { data: { session } } = await supabase.auth.getSession()
|
||||
if (!session?.user) throw new Error('Usuario no autenticado')
|
||||
return session.user.id
|
||||
}
|
||||
|
||||
function fromRow(row: Record<string, unknown>): ProcessGroup {
|
||||
return {
|
||||
id: row.id as string,
|
||||
@@ -17,8 +11,7 @@ function fromRow(row: Record<string, unknown>): ProcessGroup {
|
||||
}
|
||||
|
||||
export const supabaseGroupRepo = {
|
||||
async save(group: ProcessGroup): Promise<void> {
|
||||
const userId = await getCurrentUserId()
|
||||
async save(group: ProcessGroup, userId: string): Promise<void> {
|
||||
const { error } = await supabase.from('process_groups').upsert({
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
|
||||
@@ -2,12 +2,6 @@ import { supabase } from '@/lib/supabase'
|
||||
import { db } from '@/persistence/db'
|
||||
import type { Process } from '@/domain/types'
|
||||
|
||||
async function getCurrentUserId(): Promise<string> {
|
||||
const { data: { session } } = await supabase.auth.getSession()
|
||||
if (!session?.user) throw new Error('Usuario no autenticado')
|
||||
return session.user.id
|
||||
}
|
||||
|
||||
function toRow(process: Process, userId: string) {
|
||||
return {
|
||||
id: process.id,
|
||||
@@ -47,8 +41,7 @@ function fromRow(row: Record<string, unknown>): Process {
|
||||
}
|
||||
|
||||
export const supabaseProcessRepo = {
|
||||
async save(process: Process): Promise<void> {
|
||||
const userId = await getCurrentUserId()
|
||||
async save(process: Process, userId: string): Promise<void> {
|
||||
const { error } = await supabase.from('processes').upsert(toRow(process, userId))
|
||||
if (error) throw error
|
||||
},
|
||||
|
||||
@@ -14,12 +14,12 @@ interface LibraryState {
|
||||
|
||||
load: () => Promise<void>
|
||||
|
||||
createGroup: (name: string) => Promise<ProcessGroup>
|
||||
createGroup: (name: string, userId: string) => Promise<ProcessGroup>
|
||||
deleteGroup: (id: string) => Promise<void>
|
||||
|
||||
deleteProcess: (id: string) => Promise<void>
|
||||
|
||||
assignProcessToGroup: (processId: string, groupId: string | null) => Promise<void>
|
||||
assignProcessToGroup: (processId: string, groupId: string | null, userId: string) => Promise<void>
|
||||
|
||||
getProcessesByGroup: (groupId: string | null) => Process[]
|
||||
getLatestSimulation: (processId: string) => Promise<Simulation | undefined>
|
||||
@@ -51,15 +51,15 @@ export const useLibraryStore = create<LibraryState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
createGroup: async (name: string) => {
|
||||
createGroup: async (name, userId) => {
|
||||
const now = Date.now()
|
||||
const group: ProcessGroup = { id: uuidv4(), name, createdAt: now, updatedAt: now }
|
||||
await supabaseGroupRepo.save(group)
|
||||
await supabaseGroupRepo.save(group, userId)
|
||||
set((state) => ({ groups: [...state.groups, group].sort((a, b) => a.name.localeCompare(b.name)) }))
|
||||
return group
|
||||
},
|
||||
|
||||
deleteGroup: async (id: string) => {
|
||||
deleteGroup: async (id) => {
|
||||
await supabaseGroupRepo.delete(id)
|
||||
set((state) => ({
|
||||
groups: state.groups.filter((g) => g.id !== id),
|
||||
@@ -67,29 +67,29 @@ export const useLibraryStore = create<LibraryState>((set, get) => ({
|
||||
}))
|
||||
},
|
||||
|
||||
deleteProcess: async (id: string) => {
|
||||
deleteProcess: async (id) => {
|
||||
await supabaseProcessRepo.delete(id)
|
||||
set((state) => ({ processes: state.processes.filter((p) => p.id !== id) }))
|
||||
},
|
||||
|
||||
assignProcessToGroup: async (processId: string, groupId: string | null) => {
|
||||
assignProcessToGroup: async (processId, groupId, userId) => {
|
||||
const process = get().processes.find((p) => p.id === processId)
|
||||
if (!process) return
|
||||
const updated = { ...process, groupId, updatedAt: Date.now() }
|
||||
await supabaseProcessRepo.save(updated)
|
||||
await supabaseProcessRepo.save(updated, userId)
|
||||
set((state) => ({
|
||||
processes: state.processes.map((p) => p.id === processId ? updated : p),
|
||||
}))
|
||||
},
|
||||
|
||||
getProcessesByGroup: (groupId: string | null) => {
|
||||
getProcessesByGroup: (groupId) => {
|
||||
const processes = get().processes
|
||||
return groupId === null
|
||||
? processes.filter((p) => p.groupId == null)
|
||||
: processes.filter((p) => p.groupId === groupId)
|
||||
},
|
||||
|
||||
getLatestSimulation: (processId: string) => {
|
||||
getLatestSimulation: (processId) => {
|
||||
return simulationRepo.getLatestByProcess(processId)
|
||||
},
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@ interface ProcessState {
|
||||
error: string | null
|
||||
|
||||
loadProcess: (processId: string) => Promise<void>
|
||||
setCurrentProcess: (process: Process) => Promise<void>
|
||||
updateProcess: (updates: Partial<Process>) => Promise<void>
|
||||
updateActivity: (activity: Activity) => Promise<void>
|
||||
updateGateway: (gateway: GatewayConfig) => Promise<void>
|
||||
addResource: (resource: Resource) => Promise<void>
|
||||
updateResource: (resource: Resource) => Promise<void>
|
||||
deleteResource: (resourceId: string) => Promise<void>
|
||||
setCurrentProcess: (process: Process, userId: string) => Promise<void>
|
||||
updateProcess: (updates: Partial<Process>, userId: string) => Promise<void>
|
||||
updateActivity: (activity: Activity, userId: string) => Promise<void>
|
||||
updateGateway: (gateway: GatewayConfig, userId: string) => Promise<void>
|
||||
addResource: (resource: Resource, userId: string) => Promise<void>
|
||||
updateResource: (resource: Resource, userId: string) => Promise<void>
|
||||
deleteResource: (resourceId: string, userId: string) => Promise<void>
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ export const useProcessStore = create<ProcessState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
setCurrentProcess: async (process) => {
|
||||
await supabaseProcessRepo.save(process)
|
||||
setCurrentProcess: async (process, userId) => {
|
||||
await supabaseProcessRepo.save(process, userId)
|
||||
const [activities, gateways, resources] = await Promise.all([
|
||||
activityRepo.getByProcess(process.id),
|
||||
gatewayRepo.getByProcess(process.id),
|
||||
@@ -56,20 +56,20 @@ export const useProcessStore = create<ProcessState>((set, get) => ({
|
||||
set({ currentProcess: process, activities, gateways, resources })
|
||||
},
|
||||
|
||||
updateProcess: async (updates) => {
|
||||
updateProcess: async (updates, userId) => {
|
||||
const current = get().currentProcess
|
||||
if (!current) return
|
||||
const updated: Process = { ...current, ...updates, updatedAt: Date.now() }
|
||||
await supabaseProcessRepo.save(updated)
|
||||
await supabaseProcessRepo.save(updated, userId)
|
||||
set({ currentProcess: updated })
|
||||
},
|
||||
|
||||
updateActivity: async (activity) => {
|
||||
updateActivity: async (activity, userId) => {
|
||||
await activityRepo.save(activity)
|
||||
const current = get().currentProcess
|
||||
if (current) {
|
||||
const updatedProcess = { ...current, updatedAt: Date.now() }
|
||||
await supabaseProcessRepo.save(updatedProcess)
|
||||
await supabaseProcessRepo.save(updatedProcess, userId)
|
||||
set((state) => ({
|
||||
activities: state.activities.map((a) => (a.id === activity.id ? activity : a)),
|
||||
currentProcess: updatedProcess,
|
||||
@@ -81,12 +81,12 @@ export const useProcessStore = create<ProcessState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
updateGateway: async (gateway) => {
|
||||
updateGateway: async (gateway, userId) => {
|
||||
await gatewayRepo.save(gateway)
|
||||
const current = get().currentProcess
|
||||
if (current) {
|
||||
const updatedProcess = { ...current, updatedAt: Date.now() }
|
||||
await supabaseProcessRepo.save(updatedProcess)
|
||||
await supabaseProcessRepo.save(updatedProcess, userId)
|
||||
set((state) => ({
|
||||
gateways: state.gateways.map((g) => (g.id === gateway.id ? gateway : g)),
|
||||
currentProcess: updatedProcess,
|
||||
@@ -98,24 +98,24 @@ export const useProcessStore = create<ProcessState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
addResource: async (resource) => {
|
||||
addResource: async (resource, userId) => {
|
||||
await resourceRepo.save(resource)
|
||||
const current = get().currentProcess
|
||||
if (current) {
|
||||
const updatedProcess = { ...current, updatedAt: Date.now() }
|
||||
await supabaseProcessRepo.save(updatedProcess)
|
||||
await supabaseProcessRepo.save(updatedProcess, userId)
|
||||
set((state) => ({ resources: [...state.resources, resource], currentProcess: updatedProcess }))
|
||||
} else {
|
||||
set((state) => ({ resources: [...state.resources, resource] }))
|
||||
}
|
||||
},
|
||||
|
||||
updateResource: async (resource) => {
|
||||
updateResource: async (resource, userId) => {
|
||||
await resourceRepo.save(resource)
|
||||
const current = get().currentProcess
|
||||
if (current) {
|
||||
const updatedProcess = { ...current, updatedAt: Date.now() }
|
||||
await supabaseProcessRepo.save(updatedProcess)
|
||||
await supabaseProcessRepo.save(updatedProcess, userId)
|
||||
set((state) => ({
|
||||
resources: state.resources.map((r) => (r.id === resource.id ? resource : r)),
|
||||
currentProcess: updatedProcess,
|
||||
@@ -127,12 +127,12 @@ export const useProcessStore = create<ProcessState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
deleteResource: async (resourceId) => {
|
||||
deleteResource: async (resourceId, userId) => {
|
||||
await resourceRepo.delete(resourceId)
|
||||
const current = get().currentProcess
|
||||
if (current) {
|
||||
const updatedProcess = { ...current, updatedAt: Date.now() }
|
||||
await supabaseProcessRepo.save(updatedProcess)
|
||||
await supabaseProcessRepo.save(updatedProcess, userId)
|
||||
set((state) => ({
|
||||
resources: state.resources.filter((r) => r.id !== resourceId),
|
||||
currentProcess: updatedProcess,
|
||||
|
||||
Reference in New Issue
Block a user