Add settins connector view
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
useMutation,
|
||||
} from "react-relay";
|
||||
import { useParams } from "react-router";
|
||||
import { Link } from "react-router";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
@@ -41,6 +42,21 @@ import type { SettingsViewRemoveUserMutation as SettingsViewRemoveUserMutationTy
|
||||
import { PageTemplate } from "@/components/PageTemplate";
|
||||
import { SettingsViewSkeleton } from "./SettingsPage";
|
||||
|
||||
// Define the connector type until the generated type is available
|
||||
interface Connector {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface AvailableConnector {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const settingsViewQuery = graphql`
|
||||
query SettingsViewQuery($organizationID: ID!) {
|
||||
organization: node(id: $organizationID) {
|
||||
@@ -58,6 +74,16 @@ const settingsViewQuery = graphql`
|
||||
}
|
||||
}
|
||||
}
|
||||
connectors(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
type
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,6 +127,7 @@ function SettingsViewContent({
|
||||
const data = usePreloadedQuery(settingsViewQuery, queryRef);
|
||||
const organization = data.organization;
|
||||
const users = organization.users?.edges.map((edge) => edge.node) || [];
|
||||
const connectors = (organization as any).connectors?.edges.map((edge: any) => edge.node) || [];
|
||||
const { toast } = useToast();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -115,6 +142,18 @@ function SettingsViewContent({
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [isRemoving, setIsRemoving] = useState(false);
|
||||
|
||||
// Available connectors
|
||||
const availableConnectors: AvailableConnector[] = [
|
||||
{ id: "github", name: "GitHub", type: "oauth2", description: "Connect to GitHub repositories and issues" },
|
||||
{ id: "slack", name: "Slack", type: "oauth2", description: "Connect to Slack workspace and channels" },
|
||||
];
|
||||
|
||||
// Filter out connectors that are already connected
|
||||
const connectedConnectorIds = connectors.map((connector: Connector) => connector.name);
|
||||
const notConnectedConnectors = availableConnectors.filter(
|
||||
connector => !connectedConnectorIds.includes(connector.id)
|
||||
);
|
||||
|
||||
const [updateOrganization] =
|
||||
useMutation<SettingsViewUpdateOrganizationMutationType>(
|
||||
updateOrganizationMutation
|
||||
@@ -129,7 +168,7 @@ function SettingsViewContent({
|
||||
const { organizationId } = useParams();
|
||||
const [, loadQuery] =
|
||||
useQueryLoader<SettingsViewQueryType>(settingsViewQuery);
|
||||
|
||||
|
||||
const handleUpdateName = () => {
|
||||
updateOrganization({
|
||||
variables: {
|
||||
@@ -444,6 +483,96 @@ function SettingsViewContent({
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>Integrations</CardTitle>
|
||||
<CardDescription>
|
||||
Connect to third-party services to enhance your workflow
|
||||
</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-6">
|
||||
{connectors.length > 0 && (
|
||||
<div>
|
||||
<h3 className="mb-4 text-sm font-medium">Connected services</h3>
|
||||
<div className="space-y-4">
|
||||
{connectors.map((connector: Connector) => (
|
||||
<div
|
||||
key={connector.id}
|
||||
className="flex items-center justify-between rounded-lg border p-3 shadow-xs"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg border bg-subtle-bg">
|
||||
<Building2 className="h-5 w-5 text-tertiary" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium">
|
||||
{connector.name}
|
||||
</span>
|
||||
<span className="text-sm text-tertiary">
|
||||
{connector.type} · Connected on {new Date(connector.createdAt).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notConnectedConnectors.length > 0 && (
|
||||
<div>
|
||||
<h3 className="mb-4 text-sm font-medium">Available services</h3>
|
||||
<div className="space-y-4">
|
||||
{notConnectedConnectors.map((connector) => (
|
||||
<div
|
||||
key={connector.id}
|
||||
className="flex items-center justify-between rounded-lg border p-3 shadow-xs"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg border bg-subtle-bg">
|
||||
<Building2 className="h-5 w-5 text-tertiary" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium">
|
||||
{connector.name}
|
||||
</span>
|
||||
<span className="text-sm text-tertiary">
|
||||
{connector.description}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<a
|
||||
href={`${process.env.API_SERVER_HOST}/api/console/v1/connectors/initiate?organization_id=${encodeURIComponent(organization.id)}&connector_id=${encodeURIComponent(connector.id)}&continue=${encodeURIComponent(window.location.href)}`}
|
||||
className="inline-flex items-center justify-center h-9 px-3 text-sm font-medium rounded-md border border-input bg-background hover:bg-accent hover:text-accent-foreground"
|
||||
>
|
||||
Connect
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{connectors.length === 0 && notConnectedConnectors.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center p-8 text-center">
|
||||
<div className="rounded-full bg-subtle-bg p-3">
|
||||
<Building2 className="h-6 w-6 text-tertiary" />
|
||||
</div>
|
||||
<h3 className="mt-4 text-lg font-medium">No integrations available</h3>
|
||||
<p className="mt-2 text-sm text-tertiary">
|
||||
There are currently no integrations available for your workspace.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Dialog open={isEditNameOpen} onOpenChange={setIsEditNameOpen}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<d22ffd34989ddeb523e19aab24261e58>>
|
||||
* @generated SignedSource<<302c6e908c1aea64db4820b83e5e2bcd>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -14,6 +14,16 @@ export type SettingsViewQuery$variables = {
|
||||
};
|
||||
export type SettingsViewQuery$data = {
|
||||
readonly organization: {
|
||||
readonly connectors?: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: string;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly type: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly logoUrl?: string | null | undefined;
|
||||
readonly name?: string;
|
||||
@@ -57,15 +67,30 @@ v2 = {
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -75,13 +100,7 @@ v3 = {
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "UserConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "users",
|
||||
@@ -118,13 +137,7 @@ v3 = {
|
||||
"name": "email",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
}
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -133,6 +146,49 @@ v3 = {
|
||||
}
|
||||
],
|
||||
"storageKey": "users(first:100)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "ConnectorConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "connectors",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "ConnectorEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Connector",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "type",
|
||||
"storageKey": null
|
||||
},
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "connectors(first:100)"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
@@ -154,7 +210,7 @@ return {
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v6/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
@@ -184,23 +240,23 @@ return {
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
(v6/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "1749c3e17b6efd13be678a0e968b7ac4",
|
||||
"cacheID": "e243498081f3875e74172abba80a88e8",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "SettingsViewQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query SettingsViewQuery(\n $organizationID: ID!\n) {\n organization: node(id: $organizationID) {\n __typename\n id\n ... on Organization {\n name\n logoUrl\n users(first: 100) {\n edges {\n node {\n id\n fullName\n email\n createdAt\n }\n }\n }\n }\n }\n}\n"
|
||||
"text": "query SettingsViewQuery(\n $organizationID: ID!\n) {\n organization: node(id: $organizationID) {\n __typename\n id\n ... on Organization {\n name\n logoUrl\n users(first: 100) {\n edges {\n node {\n id\n fullName\n email\n createdAt\n }\n }\n }\n connectors(first: 100) {\n edges {\n node {\n id\n name\n type\n createdAt\n }\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "147757d21500b3eb42848ebe8a43bd9f";
|
||||
(node as any).hash = "52b24e3845bb31016ddad6f815be1cdb";
|
||||
|
||||
export default node;
|
||||
|
||||
Reference in New Issue
Block a user