Skip to content

Commit

Permalink
520 - client Update logic around connected user table and detail sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
ivicac committed Nov 3, 2024
1 parent d2d07cc commit 2d411c1
Show file tree
Hide file tree
Showing 11 changed files with 553 additions and 409 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {Form, FormControl, FormField, FormItem, FormLabel, FormMessage} from '@/
import {Input} from '@/components/ui/input';
import {Popover, PopoverContent, PopoverTrigger} from '@/components/ui/popover';
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select';
import ConnectedUserSheet from '@/pages/embedded/connected-users/components/ConnectedUserSheet';
import ConnectedUserTable from '@/pages/embedded/connected-users/components/ConnectedUserTable';
import ConnectedUsersFilterTitle from '@/pages/embedded/connected-users/components/ConnectedUsersFilterTitle';
import ConnectedUserSheet from '@/pages/embedded/connected-users/components/connected-user-sheet/ConnectedUserSheet';
import Footer from '@/shared/layout/Footer';
import Header from '@/shared/layout/Header';
import LayoutContainer from '@/shared/layout/LayoutContainer';
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle} from '@/components/ui/sheet';
import ConnectedUserSheetPanel from '@/pages/embedded/connected-users/components/connected-user-sheet/ConnectedUserSheetPanel';
import ConnectedUserSheetTitle from '@/pages/embedded/connected-users/components/connected-user-sheet/ConnectedUserSheetTitle';
import useConnectedUserSheetStore from '@/pages/embedded/connected-users/stores/useConnectedUserSheetStore';
import {useGetConnectedUserQuery} from '@/shared/queries/embedded/connectedUsers.queries';

const ConnectedUserSheet = () => {
const {connectedUserId, connectedUserSheetOpen, setConnectedUserSheetOpen} = useConnectedUserSheetStore();

const {data: connectedUser, isLoading: connectedUserLoading} = useGetConnectedUserQuery(
connectedUserId,
connectedUserSheetOpen
);

return (
<Sheet onOpenChange={() => setConnectedUserSheetOpen(!connectedUserSheetOpen)} open={connectedUserSheetOpen}>
<SheetContent className="flex w-11/12 flex-col gap-0 p-0 sm:max-w-screen-md">
<SheetHeader className="p-4">
<SheetTitle className="flex justify-between">
{connectedUser && <ConnectedUserSheetTitle connectedUser={connectedUser} />}
</SheetTitle>

<SheetDescription></SheetDescription>
</SheetHeader>

{connectedUserLoading ? (
<span>Loading...</span>
) : (
connectedUser && <ConnectedUserSheetPanel connectedUser={connectedUser} />
)}
</SheetContent>
</Sheet>
);
};

export default ConnectedUserSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ConnectedUserSheetPanelIntegrations from '@/pages/embedded/connected-users/components/connected-user-sheet/ConnectedUserSheetPanelIntegrations';
import ConnectedUserSheetPanelProfile from '@/pages/embedded/connected-users/components/connected-user-sheet/ConnectedUserSheetPanelProfile';
import {ConnectedUser} from '@/shared/middleware/embedded/connected-user';

interface ConnectedUserSheetPanelProps {
connectedUser: ConnectedUser;
}

const ConnectedUserSheetPanel = ({connectedUser}: ConnectedUserSheetPanelProps) => {
return (
<div className="flex w-full flex-col gap-4 px-2 pb-4">
<div className="flex w-full flex-col space-x-4">
<div className="w-full space-y-10">
<div className="w-full space-y-2 px-2">
<div className="text-base font-semibold">Profile</div>

<ConnectedUserSheetPanelProfile connectedUser={connectedUser} />
</div>

<div className="w-full space-y-2">
<div className="px-2 text-base font-semibold">Integrations</div>

{connectedUser.integrationInstances && (
<ConnectedUserSheetPanelIntegrations
connectedUserId={connectedUser.id!}
connectedUserIntegrationInstances={connectedUser.integrationInstances}
/>
)}
</div>
</div>
</div>
</div>
);
};

export default ConnectedUserSheetPanel;
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import {Badge} from '@/components/ui/badge';
import {Button} from '@/components/ui/button';
import {Collapsible, CollapsibleContent, CollapsibleTrigger} from '@/components/ui/collapsible';
import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from '@/components/ui/dropdown-menu';
import {Switch} from '@/components/ui/switch';
import {Tooltip, TooltipContent, TooltipTrigger} from '@/components/ui/tooltip';
import CredentialsStatus from '@/pages/embedded/connected-users/components/CredentialsStatus';
import ConnectedUserSheetPanelIntegrationWorkflows from '@/pages/embedded/connected-users/components/connected-user-sheet/ConnectedUserSheetPanelIntegrationWorkflows';
import {ConnectedUserIntegrationInstance} from '@/shared/middleware/embedded/connected-user';
import {ComponentDefinitionBasic} from '@/shared/middleware/platform/configuration';
import {useEnableIntegrationInstanceMutation} from '@/shared/mutations/embedded/integrationInstances.mutations';
import {ConnectedUserKeys} from '@/shared/queries/embedded/connectedUsers.queries';
import {useGetIntegrationInstanceConfigurationQuery} from '@/shared/queries/embedded/integrationInstanceConfigurations.queries';
import {
IntegrationInstanceKeys,
useGetIntegrationInstanceQuery,
} from '@/shared/queries/embedded/integrationInstances.queries';
import {useGetIntegrationVersionWorkflowsQuery} from '@/shared/queries/embedded/integrationWorkflows.queries';
import {useQueryClient} from '@tanstack/react-query';
import {EllipsisVerticalIcon} from 'lucide-react';
import InlineSVG from 'react-inlinesvg';
import {twMerge} from 'tailwind-merge';

const ConnectedUserSheetPanelIntegration = ({
componentDefinition,
componentDefinitions,
connectedUserId,
connectedUserIntegrationInstance,
}: {
componentDefinition: ComponentDefinitionBasic;
connectedUserIntegrationInstance: ConnectedUserIntegrationInstance;
connectedUserId: number;
componentDefinitions: ComponentDefinitionBasic[];
}) => {
const {data: workflows} = useGetIntegrationVersionWorkflowsQuery(
connectedUserIntegrationInstance.integrationId!,
connectedUserIntegrationInstance.integrationVersion!
);

const {data: integrationInstanceConfiguration} = useGetIntegrationInstanceConfigurationQuery(
connectedUserIntegrationInstance.integrationInstanceConfigurationId!
);

const {data: integrationInstance} = useGetIntegrationInstanceQuery(connectedUserIntegrationInstance.id!);

const queryClient = useQueryClient();

const enableIntegrationInstanceMutation = useEnableIntegrationInstanceMutation({
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ConnectedUserKeys.connectedUser(connectedUserId),
});
queryClient.invalidateQueries({
queryKey: IntegrationInstanceKeys.integrationInstance(connectedUserIntegrationInstance.id!),
});
},
});

return (
<Collapsible key={connectedUserIntegrationInstance.id}>
{componentDefinition && (
<div className="flex w-full items-center justify-between px-2 hover:bg-muted/50">
<CollapsibleTrigger className="flex-1 py-3">
<div className="flex flex-col items-start justify-center gap-y-2">
<div className="flex flex-1 items-center gap-1">
<InlineSVG
className="size-5 flex-none"
key={componentDefinition.name!}
src={componentDefinition.icon!}
/>

<div
className={twMerge(
'text-base font-semibold flex items-center space-x-1',
!connectedUserIntegrationInstance.enabled && 'text-muted-foreground'
)}
>
<span>{componentDefinition.title}</span>
</div>
</div>

<div className="flex gap-4">
<div className="flex items-center space-x-1 text-xs text-muted-foreground">
<CredentialsStatus
enabled={connectedUserIntegrationInstance.credentialStatus === 'VALID'}
/>

<span>{`Account ${connectedUserIntegrationInstance.credentialStatus === 'VALID' ? 'Connected' : 'Errors'}`}</span>
</div>

<div className="flex items-center space-x-1">
{integrationInstance && (
<div className="group flex text-xs font-semibold text-muted-foreground">
{workflows?.length === 1
? `${workflows?.length} workflow`
: `${workflows?.length} workflows`}
</div>
)}
</div>
</div>
</div>
</CollapsibleTrigger>

<div className="flex items-center gap-x-2">
<div className="flex items-center gap-x-4">
{integrationInstance && (
<Badge variant="secondary">
V{integrationInstance.integrationInstanceConfiguration?.integrationVersion}
</Badge>
)}

<div className="flex min-w-52 flex-col items-end gap-y-2">
<Switch
checked={connectedUserIntegrationInstance.enabled}
disabled={!integrationInstance?.integrationInstanceConfiguration?.enabled}
onCheckedChange={(value) => {
enableIntegrationInstanceMutation.mutate({
enable: value,
id: connectedUserIntegrationInstance.id!,
});
}}
/>

<Tooltip>
<TooltipTrigger className="flex items-center text-sm text-gray-500">
{integrationInstance && integrationInstance.lastExecutionDate ? (
<span className="text-xs">
{`Executed at ${integrationInstance.lastExecutionDate?.toLocaleDateString()} ${integrationInstance.lastExecutionDate?.toLocaleTimeString()}`}
</span>
) : (
<span className="text-xs">No executions</span>
)}
</TooltipTrigger>

<TooltipContent>Last Execution Date</TooltipContent>
</Tooltip>
</div>
</div>

<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="ghost">
<EllipsisVerticalIcon className="size-4 hover:cursor-pointer" />
</Button>
</DropdownMenuTrigger>

<DropdownMenuContent align="end">
<DropdownMenuItem className="text-destructive">Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
)}

<CollapsibleContent>
{workflows && integrationInstance && integrationInstanceConfiguration && (
<ConnectedUserSheetPanelIntegrationWorkflows
componentDefinitions={componentDefinitions}
integrationInstance={integrationInstance}
integrationInstanceConfiguration={integrationInstanceConfiguration}
workflows={workflows}
/>
)}
</CollapsibleContent>
</Collapsible>
);
};

export default ConnectedUserSheetPanelIntegration;
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {Button} from '@/components/ui/button';
import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from '@/components/ui/dropdown-menu';
import {Switch} from '@/components/ui/switch';
import {IntegrationInstance, IntegrationInstanceWorkflow, Workflow} from '@/shared/middleware/embedded/configuration';
import {ComponentDefinitionBasic} from '@/shared/middleware/platform/configuration';
import {useEnableIntegrationInstanceWorkflowMutation} from '@/shared/mutations/embedded/integrationInstanceWorkflows.mutations';
import {ConnectedUserKeys} from '@/shared/queries/embedded/connectedUsers.queries';
import {IntegrationInstanceKeys} from '@/shared/queries/embedded/integrationInstances.queries';
import {useQueryClient} from '@tanstack/react-query';
import {EllipsisVerticalIcon} from 'lucide-react';
import InlineSVG from 'react-inlinesvg';

const ConnectedUserSheetPanelIntegrationWorkflow = ({
componentDefinitions,
integrationInstance,
integrationInstanceWorkflow,
workflow,
}: {
componentDefinitions: ComponentDefinitionBasic[];
integrationInstance: IntegrationInstance;
integrationInstanceWorkflow?: IntegrationInstanceWorkflow;
workflow: Workflow;
}) => {
const filteredComponentDefinitions = [
...(workflow.workflowTaskComponentNames ?? []),
...(workflow.workflowTriggerComponentNames ?? []),
].map((componentName) => {
return componentDefinitions?.find((componentDefinition) => componentDefinition.name === componentName);
});

const queryClient = useQueryClient();

const enableIntegrationInstanceWorkflowMutation = useEnableIntegrationInstanceWorkflowMutation({
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ConnectedUserKeys.connectedUser(integrationInstance.connectedUserId!),
});
queryClient.invalidateQueries({
queryKey: IntegrationInstanceKeys.integrationInstance(integrationInstance.id!),
});
},
});

return (
<li className="flex items-center justify-between rounded-md p-2 py-1 hover:bg-gray-50" key={workflow.id}>
<div className="text-sm font-semibold">{workflow.label}</div>

<div>
{filteredComponentDefinitions?.map((componentDefinition) => {
return (
componentDefinition && (
<InlineSVG
className="mr-2 size-6 flex-none"
key={componentDefinition.name!}
src={componentDefinition.icon!}
/>
)
);
})}
</div>

<div className="flex items-center space-x-1">
<Switch
checked={integrationInstanceWorkflow?.enabled}
disabled={integrationInstance.enabled}
onCheckedChange={(value) => {
enableIntegrationInstanceWorkflowMutation.mutate({
enable: value,
id: integrationInstance.id!,
workflowId: workflow.id!,
});
}}
/>

<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="ghost">
<EllipsisVerticalIcon className="size-4 hover:cursor-pointer" />
</Button>
</DropdownMenuTrigger>

<DropdownMenuContent align="end">
<DropdownMenuItem className="text-destructive">Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</li>
);
};

export default ConnectedUserSheetPanelIntegrationWorkflow;
Loading

0 comments on commit 2d411c1

Please sign in to comment.