mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 16:56:22 +02:00
add jira connector implementation in the web
This commit is contained in:
parent
cd05a06a91
commit
6bced733b2
9 changed files with 2740 additions and 2133 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
ChevronDown,
|
||||
import React from "react";
|
||||
import {
|
||||
ChevronDown,
|
||||
Plus,
|
||||
Search,
|
||||
Globe,
|
||||
|
|
@ -12,78 +12,99 @@ import {
|
|||
Webhook,
|
||||
MessageCircle,
|
||||
FileText,
|
||||
} from 'lucide-react';
|
||||
import { IconBrandNotion, IconBrandSlack, IconBrandYoutube, IconBrandGithub, IconLayoutKanban, IconLinkPlus, IconBrandDiscord } from "@tabler/icons-react";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Connector, ResearchMode } from './types';
|
||||
} from "lucide-react";
|
||||
import {
|
||||
IconBrandNotion,
|
||||
IconBrandSlack,
|
||||
IconBrandYoutube,
|
||||
IconBrandGithub,
|
||||
IconLayoutKanban,
|
||||
IconLinkPlus,
|
||||
IconBrandDiscord,
|
||||
IconTicket,
|
||||
} from "@tabler/icons-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Connector, ResearchMode } from "./types";
|
||||
|
||||
// Helper function to get connector icon
|
||||
export const getConnectorIcon = (connectorType: string) => {
|
||||
const iconProps = { className: "h-4 w-4" };
|
||||
|
||||
switch(connectorType) {
|
||||
case 'LINKUP_API':
|
||||
|
||||
switch (connectorType) {
|
||||
case "LINKUP_API":
|
||||
return <IconLinkPlus {...iconProps} />;
|
||||
case 'LINEAR_CONNECTOR':
|
||||
case "LINEAR_CONNECTOR":
|
||||
return <IconLayoutKanban {...iconProps} />;
|
||||
case 'GITHUB_CONNECTOR':
|
||||
case "GITHUB_CONNECTOR":
|
||||
return <IconBrandGithub {...iconProps} />;
|
||||
case 'YOUTUBE_VIDEO':
|
||||
case "YOUTUBE_VIDEO":
|
||||
return <IconBrandYoutube {...iconProps} />;
|
||||
case 'CRAWLED_URL':
|
||||
case "CRAWLED_URL":
|
||||
return <Globe {...iconProps} />;
|
||||
case 'FILE':
|
||||
return <File {...iconProps} />;
|
||||
case 'EXTENSION':
|
||||
return <Webhook {...iconProps} />;
|
||||
case 'SERPER_API':
|
||||
case 'TAVILY_API':
|
||||
case "FILE":
|
||||
return <File {...iconProps} />;
|
||||
case "EXTENSION":
|
||||
return <Webhook {...iconProps} />;
|
||||
case "SERPER_API":
|
||||
case "TAVILY_API":
|
||||
return <Link {...iconProps} />;
|
||||
case 'SLACK_CONNECTOR':
|
||||
case "SLACK_CONNECTOR":
|
||||
return <IconBrandSlack {...iconProps} />;
|
||||
case 'NOTION_CONNECTOR':
|
||||
case "NOTION_CONNECTOR":
|
||||
return <IconBrandNotion {...iconProps} />;
|
||||
case 'DISCORD_CONNECTOR':
|
||||
case "DISCORD_CONNECTOR":
|
||||
return <IconBrandDiscord {...iconProps} />;
|
||||
case 'DEEP':
|
||||
case "JIRA_CONNECTOR":
|
||||
return <IconTicket {...iconProps} />;
|
||||
case "DEEP":
|
||||
return <Sparkles {...iconProps} />;
|
||||
case 'DEEPER':
|
||||
case "DEEPER":
|
||||
return <Microscope {...iconProps} />;
|
||||
case 'DEEPEST':
|
||||
case "DEEPEST":
|
||||
return <Telescope {...iconProps} />;
|
||||
default:
|
||||
return <Search {...iconProps} />;
|
||||
}
|
||||
};
|
||||
|
||||
export const researcherOptions: { value: ResearchMode; label: string; icon: React.ReactNode }[] = [
|
||||
export const researcherOptions: {
|
||||
value: ResearchMode;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
}[] = [
|
||||
{
|
||||
value: 'QNA',
|
||||
label: 'Q/A',
|
||||
icon: getConnectorIcon('GENERAL')
|
||||
value: "QNA",
|
||||
label: "Q/A",
|
||||
icon: getConnectorIcon("GENERAL"),
|
||||
},
|
||||
{
|
||||
value: 'REPORT_GENERAL',
|
||||
label: 'General',
|
||||
icon: getConnectorIcon('GENERAL')
|
||||
value: "REPORT_GENERAL",
|
||||
label: "General",
|
||||
icon: getConnectorIcon("GENERAL"),
|
||||
},
|
||||
{
|
||||
value: 'REPORT_DEEP',
|
||||
label: 'Deep',
|
||||
icon: getConnectorIcon('DEEP')
|
||||
value: "REPORT_DEEP",
|
||||
label: "Deep",
|
||||
icon: getConnectorIcon("DEEP"),
|
||||
},
|
||||
{
|
||||
value: 'REPORT_DEEPER',
|
||||
label: 'Deeper',
|
||||
icon: getConnectorIcon('DEEPER')
|
||||
value: "REPORT_DEEPER",
|
||||
label: "Deeper",
|
||||
icon: getConnectorIcon("DEEPER"),
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Displays a small icon for a connector type
|
||||
*/
|
||||
export const ConnectorIcon = ({ type, index = 0 }: { type: string; index?: number }) => (
|
||||
<div
|
||||
export const ConnectorIcon = ({
|
||||
type,
|
||||
index = 0,
|
||||
}: {
|
||||
type: string;
|
||||
index?: number;
|
||||
}) => (
|
||||
<div
|
||||
className="w-4 h-4 rounded-full flex items-center justify-center bg-muted border border-background"
|
||||
style={{ zIndex: 10 - index }}
|
||||
>
|
||||
|
|
@ -109,24 +130,30 @@ type ConnectorButtonProps = {
|
|||
/**
|
||||
* Button that displays selected connectors and opens connector selection dialog
|
||||
*/
|
||||
export const ConnectorButton = ({ selectedConnectors, onClick, connectorSources }: ConnectorButtonProps) => {
|
||||
export const ConnectorButton = ({
|
||||
selectedConnectors,
|
||||
onClick,
|
||||
connectorSources,
|
||||
}: ConnectorButtonProps) => {
|
||||
const totalConnectors = connectorSources.length;
|
||||
const selectedCount = selectedConnectors.length;
|
||||
const progressPercentage = (selectedCount / totalConnectors) * 100;
|
||||
|
||||
|
||||
// Get the name of a single selected connector
|
||||
const getSingleConnectorName = () => {
|
||||
const connector = connectorSources.find(c => c.type === selectedConnectors[0]);
|
||||
return connector?.name || '';
|
||||
const connector = connectorSources.find(
|
||||
(c) => c.type === selectedConnectors[0],
|
||||
);
|
||||
return connector?.name || "";
|
||||
};
|
||||
|
||||
|
||||
// Get display text based on selection count
|
||||
const getDisplayText = () => {
|
||||
if (selectedCount === totalConnectors) return "All Connectors";
|
||||
if (selectedCount === 1) return getSingleConnectorName();
|
||||
return `${selectedCount} Connectors`;
|
||||
};
|
||||
|
||||
|
||||
// Render the empty state (no connectors selected)
|
||||
const renderEmptyState = () => (
|
||||
<>
|
||||
|
|
@ -134,7 +161,7 @@ export const ConnectorButton = ({ selectedConnectors, onClick, connectorSources
|
|||
<span className="text-muted-foreground">Select Connectors</span>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
// Render the selected connectors preview
|
||||
const renderSelectedConnectors = () => (
|
||||
<>
|
||||
|
|
@ -143,32 +170,36 @@ export const ConnectorButton = ({ selectedConnectors, onClick, connectorSources
|
|||
{selectedConnectors.slice(0, 3).map((type, index) => (
|
||||
<ConnectorIcon key={type} type={type} index={index} />
|
||||
))}
|
||||
|
||||
|
||||
{/* Show count indicator if more than 3 connectors are selected */}
|
||||
{selectedCount > 3 && <ConnectorCountBadge count={selectedCount - 3} />}
|
||||
</div>
|
||||
|
||||
|
||||
{/* Display text */}
|
||||
<span className="font-medium">{getDisplayText()}</span>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 px-2 text-xs font-medium rounded-md border-border relative overflow-hidden group"
|
||||
onClick={onClick}
|
||||
aria-label={selectedCount === 0 ? "Select Connectors" : `${selectedCount} connectors selected`}
|
||||
aria-label={
|
||||
selectedCount === 0
|
||||
? "Select Connectors"
|
||||
: `${selectedCount} connectors selected`
|
||||
}
|
||||
>
|
||||
{/* Progress indicator */}
|
||||
<div
|
||||
className="absolute bottom-0 left-0 h-1 bg-primary"
|
||||
style={{
|
||||
<div
|
||||
className="absolute bottom-0 left-0 h-1 bg-primary"
|
||||
style={{
|
||||
width: `${progressPercentage}%`,
|
||||
transition: 'width 0.3s ease'
|
||||
}}
|
||||
transition: "width 0.3s ease",
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
<div className="flex items-center gap-1.5 z-10 relative">
|
||||
{selectedCount === 0 ? renderEmptyState() : renderSelectedConnectors()}
|
||||
<ChevronDown className="h-3 w-3 ml-0.5 text-muted-foreground opacity-70" />
|
||||
|
|
@ -183,29 +214,32 @@ type ResearchModeControlProps = {
|
|||
onChange: (value: ResearchMode) => void;
|
||||
};
|
||||
|
||||
export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProps) => {
|
||||
export const ResearchModeControl = ({
|
||||
value,
|
||||
onChange,
|
||||
}: ResearchModeControlProps) => {
|
||||
// Determine if we're in Q/A mode or Report mode
|
||||
const isQnaMode = value === 'QNA';
|
||||
const isReportMode = value.startsWith('REPORT_');
|
||||
|
||||
const isQnaMode = value === "QNA";
|
||||
const isReportMode = value.startsWith("REPORT_");
|
||||
|
||||
// Get the current report sub-mode
|
||||
const getCurrentReportMode = () => {
|
||||
if (!isReportMode) return 'GENERAL';
|
||||
return value.replace('REPORT_', '') as 'GENERAL' | 'DEEP' | 'DEEPER';
|
||||
if (!isReportMode) return "GENERAL";
|
||||
return value.replace("REPORT_", "") as "GENERAL" | "DEEP" | "DEEPER";
|
||||
};
|
||||
|
||||
const reportSubOptions = [
|
||||
{ value: 'GENERAL', label: 'General', icon: getConnectorIcon('GENERAL') },
|
||||
{ value: 'DEEP', label: 'Deep', icon: getConnectorIcon('DEEP') },
|
||||
{ value: 'DEEPER', label: 'Deeper', icon: getConnectorIcon('DEEPER') },
|
||||
{ value: "GENERAL", label: "General", icon: getConnectorIcon("GENERAL") },
|
||||
{ value: "DEEP", label: "Deep", icon: getConnectorIcon("DEEP") },
|
||||
{ value: "DEEPER", label: "Deeper", icon: getConnectorIcon("DEEPER") },
|
||||
];
|
||||
|
||||
const handleModeToggle = (mode: 'QNA' | 'REPORT') => {
|
||||
if (mode === 'QNA') {
|
||||
onChange('QNA');
|
||||
const handleModeToggle = (mode: "QNA" | "REPORT") => {
|
||||
if (mode === "QNA") {
|
||||
onChange("QNA");
|
||||
} else {
|
||||
// Default to GENERAL for Report mode
|
||||
onChange('REPORT_GENERAL');
|
||||
onChange("REPORT_GENERAL");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -219,11 +253,11 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
|
|||
<div className="flex h-8 rounded-md border border-border overflow-hidden">
|
||||
<button
|
||||
className={`flex h-full items-center gap-1 px-3 text-xs font-medium transition-colors whitespace-nowrap ${
|
||||
isQnaMode
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-muted text-muted-foreground hover:text-foreground'
|
||||
isQnaMode
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-muted text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => handleModeToggle('QNA')}
|
||||
onClick={() => handleModeToggle("QNA")}
|
||||
aria-pressed={isQnaMode}
|
||||
>
|
||||
<MessageCircle className="h-3 w-3" />
|
||||
|
|
@ -231,11 +265,11 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
|
|||
</button>
|
||||
<button
|
||||
className={`flex h-full items-center gap-1 px-3 text-xs font-medium transition-colors whitespace-nowrap ${
|
||||
isReportMode
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-muted text-muted-foreground hover:text-foreground'
|
||||
isReportMode
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-muted text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => handleModeToggle('REPORT')}
|
||||
onClick={() => handleModeToggle("REPORT")}
|
||||
aria-pressed={isReportMode}
|
||||
>
|
||||
<FileText className="h-3 w-3" />
|
||||
|
|
@ -250,9 +284,9 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
|
|||
<button
|
||||
key={option.value}
|
||||
className={`flex h-full items-center gap-1 px-2 text-xs font-medium transition-colors whitespace-nowrap ${
|
||||
getCurrentReportMode() === option.value
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-muted text-muted-foreground hover:text-foreground'
|
||||
getCurrentReportMode() === option.value
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "hover:bg-muted text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => handleReportSubModeChange(option.value)}
|
||||
aria-pressed={getCurrentReportMode() === option.value}
|
||||
|
|
@ -265,4 +299,4 @@ export const ResearchModeControl = ({ value, onChange }: ResearchModeControlProp
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue