Skip to content

Commit bbc9202

Browse files
committed
fix UI
1 parent 8b5cac8 commit bbc9202

4 files changed

Lines changed: 183 additions & 69 deletions

File tree

apps/sim/app/api/workflows/[id]/deployments/route.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createLogger } from '@/lib/logs/console/logger'
44
import { validateWorkflowAccess } from '@/app/api/workflows/middleware'
55
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
66
import { db } from '@/db'
7-
import { workflowDeploymentVersion } from '@/db/schema'
7+
import { user, workflowDeploymentVersion } from '@/db/schema'
88

99
const logger = createLogger('WorkflowDeploymentsListAPI')
1010

@@ -29,8 +29,10 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
2929
isActive: workflowDeploymentVersion.isActive,
3030
createdAt: workflowDeploymentVersion.createdAt,
3131
createdBy: workflowDeploymentVersion.createdBy,
32+
deployedBy: user.name,
3233
})
3334
.from(workflowDeploymentVersion)
35+
.leftJoin(user, eq(workflowDeploymentVersion.createdBy, user.id))
3436
.where(eq(workflowDeploymentVersion.workflowId, id))
3537
.orderBy(desc(workflowDeploymentVersion.version))
3638

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/deploy-modal.tsx

Lines changed: 142 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
'use client'
22

33
import { useEffect, useState } from 'react'
4-
import { Loader2, X } from 'lucide-react'
5-
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui'
4+
import { Loader2, MoreVertical, X } from 'lucide-react'
5+
import {
6+
Button,
7+
Dialog,
8+
DialogContent,
9+
DialogHeader,
10+
DialogTitle,
11+
DropdownMenu,
12+
DropdownMenuContent,
13+
DropdownMenuItem,
14+
DropdownMenuTrigger,
15+
} from '@/components/ui'
616
import { getEnv } from '@/lib/env'
717
import { createLogger } from '@/lib/logs/console/logger'
818
import { cn } from '@/lib/utils'
@@ -89,6 +99,8 @@ export function DeployModal({
8999
const [previewVersion, setPreviewVersion] = useState<number | null>(null)
90100
const [previewing, setPreviewing] = useState(false)
91101
const [previewDeployedState, setPreviewDeployedState] = useState<WorkflowState | null>(null)
102+
const [currentPage, setCurrentPage] = useState(1)
103+
const itemsPerPage = 10
92104

93105
const getInputFormatExample = () => {
94106
let inputFormatExample = ''
@@ -568,44 +580,134 @@ export function DeployModal({
568580
)}
569581

570582
<div className='mt-6'>
571-
<div className='mb-2 font-medium text-sm'>Deployment versions</div>
572-
<div className='rounded-md border'>
573-
{versionsLoading ? (
574-
<div className='p-3 text-muted-foreground text-sm'>Loading…</div>
575-
) : versions.length === 0 ? (
576-
<div className='p-3 text-muted-foreground text-sm'>No deployments yet</div>
577-
) : (
578-
<ul className='divide-y'>
579-
{versions.map((v) => (
580-
<li key={v.id} className='flex items-center justify-between px-3 py-2'>
581-
<button
582-
type='button'
583-
onClick={() => openVersionPreview(v.version)}
584-
className='flex items-center gap-2 text-left hover:opacity-80'
583+
<div className='mb-3 font-medium text-sm'>Deployment Versions</div>
584+
{versionsLoading ? (
585+
<div className='rounded-md border p-4 text-center text-muted-foreground text-sm'>
586+
Loading deployments...
587+
</div>
588+
) : versions.length === 0 ? (
589+
<div className='rounded-md border p-4 text-center text-muted-foreground text-sm'>
590+
No deployments yet
591+
</div>
592+
) : (
593+
<>
594+
<div className='overflow-hidden rounded-md border'>
595+
<table className='w-full'>
596+
<thead className='border-b bg-muted/50'>
597+
<tr>
598+
<th className='w-10' />
599+
<th className='px-4 py-2 text-left text-xs font-medium text-muted-foreground'>
600+
Version
601+
</th>
602+
<th className='px-4 py-2 text-left text-xs font-medium text-muted-foreground'>
603+
Deployed By
604+
</th>
605+
<th className='px-4 py-2 text-left text-xs font-medium text-muted-foreground'>
606+
Created
607+
</th>
608+
<th className='w-10' />
609+
</tr>
610+
</thead>
611+
<tbody className='divide-y'>
612+
{versions
613+
.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage)
614+
.map((v) => (
615+
<tr
616+
key={v.id}
617+
className='hover:bg-muted/30 transition-colors cursor-pointer'
618+
onClick={() => openVersionPreview(v.version)}
619+
>
620+
<td className='px-4 py-2.5'>
621+
<div
622+
className={`h-2 w-2 rounded-full ${
623+
v.isActive ? 'bg-green-500' : 'bg-muted-foreground/40'
624+
}`}
625+
title={v.isActive ? 'Active' : 'Inactive'}
626+
/>
627+
</td>
628+
<td className='px-4 py-2.5'>
629+
<span className='font-medium text-sm'>v{v.version}</span>
630+
</td>
631+
<td className='px-4 py-2.5'>
632+
<span className='text-muted-foreground text-sm'>
633+
{v.deployedBy || 'Unknown'}
634+
</span>
635+
</td>
636+
<td className='px-4 py-2.5'>
637+
<span className='text-muted-foreground text-sm'>
638+
{new Date(v.createdAt).toLocaleDateString()}{' '}
639+
{new Date(v.createdAt).toLocaleTimeString()}
640+
</span>
641+
</td>
642+
<td
643+
className='px-4 py-2.5'
644+
onClick={(e) => e.stopPropagation()}
645+
>
646+
<DropdownMenu>
647+
<DropdownMenuTrigger asChild>
648+
<Button
649+
variant='ghost'
650+
size='icon'
651+
className='h-8 w-8'
652+
disabled={activatingVersion === v.version}
653+
>
654+
<MoreVertical className='h-4 w-4' />
655+
</Button>
656+
</DropdownMenuTrigger>
657+
<DropdownMenuContent align='end'>
658+
<DropdownMenuItem
659+
onClick={() => activateVersion(v.version)}
660+
disabled={v.isActive || activatingVersion === v.version}
661+
>
662+
{v.isActive
663+
? 'Active'
664+
: activatingVersion === v.version
665+
? 'Activating...'
666+
: 'Activate'}
667+
</DropdownMenuItem>
668+
<DropdownMenuItem
669+
onClick={() => openVersionPreview(v.version)}
670+
>
671+
Inspect
672+
</DropdownMenuItem>
673+
</DropdownMenuContent>
674+
</DropdownMenu>
675+
</td>
676+
</tr>
677+
))}
678+
</tbody>
679+
</table>
680+
</div>
681+
{versions.length > itemsPerPage && (
682+
<div className='mt-3 flex items-center justify-between'>
683+
<span className='text-muted-foreground text-sm'>
684+
Showing{' '}
685+
{Math.min((currentPage - 1) * itemsPerPage + 1, versions.length)} -{' '}
686+
{Math.min(currentPage * itemsPerPage, versions.length)} of{' '}
687+
{versions.length}
688+
</span>
689+
<div className='flex gap-2'>
690+
<Button
691+
variant='outline'
692+
size='sm'
693+
onClick={() => setCurrentPage(currentPage - 1)}
694+
disabled={currentPage === 1}
585695
>
586-
<div
587-
className={`h-2 w-2 rounded-full ${v.isActive ? 'bg-green-500' : 'bg-muted-foreground/40'}`}
588-
/>
589-
<div className='text-sm'>v{v.version}</div>
590-
<div className='text-muted-foreground text-xs'>
591-
{new Date(v.createdAt).toLocaleString()}
592-
</div>
593-
</button>
594-
{!v.isActive && (
595-
<Button
596-
size='sm'
597-
variant='outline'
598-
disabled={activatingVersion === v.version}
599-
onClick={() => activateVersion(v.version)}
600-
>
601-
{activatingVersion === v.version ? 'Activating…' : 'Activate'}
602-
</Button>
603-
)}
604-
</li>
605-
))}
606-
</ul>
607-
)}
608-
</div>
696+
Previous
697+
</Button>
698+
<Button
699+
variant='outline'
700+
size='sm'
701+
onClick={() => setCurrentPage(currentPage + 1)}
702+
disabled={currentPage * itemsPerPage >= versions.length}
703+
>
704+
Next
705+
</Button>
706+
</div>
707+
</div>
708+
)}
709+
</>
710+
)}
609711
</div>
610712
</>
611713
)}
@@ -810,6 +912,7 @@ export function DeployModal({
810912
isActivating={activatingVersion === previewVersion}
811913
selectedVersionLabel={`v${previewVersion}`}
812914
workflowId={workflowId}
915+
isSelectedVersionActive={versions.find((v) => v.version === previewVersion)?.isActive}
813916
/>
814917
)}
815918
</Dialog>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deployment-controls/components/deployed-workflow-modal.tsx

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ interface DeployedWorkflowModalProps {
3434
isActivating?: boolean
3535
selectedVersionLabel?: string
3636
workflowId: string
37+
isSelectedVersionActive?: boolean
3738
}
3839

3940
export function DeployedWorkflowModal({
@@ -47,6 +48,7 @@ export function DeployedWorkflowModal({
4748
isActivating,
4849
selectedVersionLabel,
4950
workflowId,
51+
isSelectedVersionActive,
5052
}: DeployedWorkflowModalProps) {
5153
const [showRevertDialog, setShowRevertDialog] = useState(false)
5254
const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId)
@@ -105,38 +107,44 @@ export function DeployedWorkflowModal({
105107
/>
106108

107109
<div className='mt-6 flex justify-between'>
108-
{(needsRedeployment || selectedVersion !== undefined) && (
109-
<AlertDialog open={showRevertDialog} onOpenChange={setShowRevertDialog}>
110-
<AlertDialogTrigger asChild>
111-
<Button variant='destructive'>Revert to Deployment</Button>
112-
</AlertDialogTrigger>
113-
<AlertDialogContent style={{ zIndex: 1001 }} className='sm:max-w-[425px]'>
114-
<AlertDialogHeader>
115-
<AlertDialogTitle>Revert to this Version?</AlertDialogTitle>
116-
<AlertDialogDescription>
117-
This will replace your current workflow with the deployed version. This action
118-
cannot be undone.
119-
</AlertDialogDescription>
120-
</AlertDialogHeader>
121-
<AlertDialogFooter>
122-
<AlertDialogCancel>Cancel</AlertDialogCancel>
123-
<AlertDialogAction
124-
onClick={handleRevert}
125-
className='bg-destructive text-destructive-foreground hover:bg-destructive/90'
126-
>
127-
Revert
128-
</AlertDialogAction>
129-
</AlertDialogFooter>
130-
</AlertDialogContent>
131-
</AlertDialog>
132-
)}
133-
134-
<div className='ml-auto flex items-center gap-2'>
110+
<div className='flex items-center gap-2'>
135111
{onActivateVersion && (
136-
<Button onClick={onActivateVersion} disabled={!!isActivating}>
137-
{isActivating ? 'Activating…' : 'Activate'}
112+
<Button
113+
onClick={onActivateVersion}
114+
disabled={isSelectedVersionActive || !!isActivating}
115+
variant={isSelectedVersionActive ? 'secondary' : 'default'}
116+
>
117+
{isSelectedVersionActive ? 'Active' : isActivating ? 'Activating…' : 'Activate'}
138118
</Button>
139119
)}
120+
</div>
121+
122+
<div className='flex items-center gap-2'>
123+
{(needsRedeployment || selectedVersion !== undefined) && (
124+
<AlertDialog open={showRevertDialog} onOpenChange={setShowRevertDialog}>
125+
<AlertDialogTrigger asChild>
126+
<Button variant='outline'>Load Deployment</Button>
127+
</AlertDialogTrigger>
128+
<AlertDialogContent style={{ zIndex: 1001 }} className='sm:max-w-[425px]'>
129+
<AlertDialogHeader>
130+
<AlertDialogTitle>Load this Deployment?</AlertDialogTitle>
131+
<AlertDialogDescription>
132+
This will replace your current workflow with the deployed version. Your
133+
current changes will be lost.
134+
</AlertDialogDescription>
135+
</AlertDialogHeader>
136+
<AlertDialogFooter>
137+
<AlertDialogCancel>Cancel</AlertDialogCancel>
138+
<AlertDialogAction
139+
onClick={handleRevert}
140+
className='bg-primary text-primary-foreground hover:bg-primary/90'
141+
>
142+
Load Deployment
143+
</AlertDialogAction>
144+
</AlertDialogFooter>
145+
</AlertDialogContent>
146+
</AlertDialog>
147+
)}
140148
<Button variant='outline' onClick={onClose}>
141149
Close
142150
</Button>

apps/sim/lib/workflows/db-helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface WorkflowDeploymentVersionResponse {
2424
isActive: boolean
2525
createdAt: string
2626
createdBy?: string | null
27+
deployedBy?: string | null
2728
}
2829

2930
export interface NormalizedWorkflowData {

0 commit comments

Comments
 (0)