Add Zendesk connector logo and connect dialog
Add the Zendesk brand mark under ThirdParties and wire it into the logo map. Because the customer subdomain is free-form (not a fixed region list), the connect flow opens a dialog with a subdomain text input that is sanitized and passed as ?site= to the OAuth initiate endpoint, mirroring the Datadog multi-site dialog. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
@@ -171,6 +171,17 @@ function hasRequiredExtraSettings(
|
||||
.every(s => values[s.key]?.trim());
|
||||
}
|
||||
|
||||
// Accepts either a bare subdomain ("acme") or a pasted host
|
||||
// ("https://acme.zendesk.com/") and reduces it to the bare subdomain the
|
||||
// backend expects as the `site` query param.
|
||||
function cleanZendeskSubdomain(raw: string): string {
|
||||
let value = raw.trim();
|
||||
value = value.replace(/^https?:\/\//i, "");
|
||||
value = value.replace(/\/+$/, "");
|
||||
value = value.replace(/\.zendesk\.com$/i, "");
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
export function AddAccessSourceDialog({
|
||||
children,
|
||||
organizationId,
|
||||
@@ -184,6 +195,7 @@ export function AddAccessSourceDialog({
|
||||
const apiKeyDialogRef = useDialogRef();
|
||||
const clientCredentialsDialogRef = useDialogRef();
|
||||
const datadogDialogRef = useDialogRef();
|
||||
const zendeskDialogRef = useDialogRef();
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [activeProvider, setActiveProvider] = useState<ProviderInfo | null>(null);
|
||||
@@ -191,6 +203,9 @@ export function AddAccessSourceDialog({
|
||||
const [datadogSite, setDatadogSite] = useState<string>("US1");
|
||||
const [datadogProvider, setDatadogProvider] = useState<ProviderInfo | null>(null);
|
||||
|
||||
const [zendeskSubdomain, setZendeskSubdomain] = useState<string>("");
|
||||
const [zendeskProvider, setZendeskProvider] = useState<ProviderInfo | null>(null);
|
||||
|
||||
const [apiKeyValue, setApiKeyValue] = useState("");
|
||||
const [extraSettingValues, setExtraSettingValues] = useState<Record<string, string>>({});
|
||||
const [isConnectingAPIKey, setIsConnectingAPIKey] = useState(false);
|
||||
@@ -277,6 +292,12 @@ export function AddAccessSourceDialog({
|
||||
datadogDialogRef.current?.open();
|
||||
};
|
||||
|
||||
const openZendeskDialog = (info: ProviderInfo) => {
|
||||
setZendeskProvider(info);
|
||||
setZendeskSubdomain("");
|
||||
zendeskDialogRef.current?.open();
|
||||
};
|
||||
|
||||
const createSourceAfterConnector = (
|
||||
connectorId: string,
|
||||
displayName: string,
|
||||
@@ -470,10 +491,15 @@ export function AddAccessSourceDialog({
|
||||
return (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
info.provider === "DATADOG"
|
||||
? openDatadogDialog(info)
|
||||
: connectOAuthProvider(info)}
|
||||
onClick={() => {
|
||||
if (info.provider === "DATADOG") {
|
||||
openDatadogDialog(info);
|
||||
} else if (info.provider === "ZENDESK") {
|
||||
openZendeskDialog(info);
|
||||
} else {
|
||||
connectOAuthProvider(info);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{__("Connect")}
|
||||
</Button>
|
||||
@@ -808,6 +834,44 @@ export function AddAccessSourceDialog({
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
|
||||
<Dialog ref={zendeskDialogRef} title={__("Connect Zendesk")}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (zendeskProvider) {
|
||||
const site = cleanZendeskSubdomain(zendeskSubdomain);
|
||||
if (site) {
|
||||
connectOAuthProvider(zendeskProvider, { site });
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent padded className="space-y-4">
|
||||
<p className="text-txt-secondary text-sm">
|
||||
{__("Enter your Zendesk subdomain, then continue to authorize access.")}
|
||||
</p>
|
||||
<Field
|
||||
label={__("Zendesk subdomain")}
|
||||
placeholder={__("acme")}
|
||||
value={zendeskSubdomain}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setZendeskSubdomain(e.target.value)}
|
||||
help={__("The <subdomain> part of <subdomain>.zendesk.com")}
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!cleanZendeskSubdomain(zendeskSubdomain)}
|
||||
>
|
||||
{__("Continue")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import { Supabase } from "./Supabase";
|
||||
import { Tailscale } from "./Tailscale";
|
||||
import { Tally } from "./Tally";
|
||||
import { Vercel } from "./Vercel";
|
||||
import { Zendesk } from "./Zendesk";
|
||||
|
||||
const thirdParties: Record<string, FC<ComponentProps<"svg">>> = {
|
||||
ANTHROPIC: Anthropic,
|
||||
@@ -93,6 +94,7 @@ const thirdParties: Record<string, FC<ComponentProps<"svg">>> = {
|
||||
TAILSCALE: Tailscale,
|
||||
TALLY: Tally,
|
||||
VERCEL: Vercel,
|
||||
ZENDESK: Zendesk,
|
||||
};
|
||||
|
||||
type ThirdPartyLogoProps = ComponentProps<"svg"> & {
|
||||
|
||||
12
packages/ui/src/Atoms/ThirdParties/Zendesk.tsx
Normal file
12
packages/ui/src/Atoms/ThirdParties/Zendesk.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
export function Zendesk(props: ComponentProps<"svg">) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M12.914 2.904V16.29L24 2.905H12.914zM0 2.906C0 5.966 2.483 8.45 5.543 8.45s5.542-2.484 5.543-5.544H0zm11.086 4.807L0 21.096h11.086V7.713zm7.37 7.84c-3.063 0-5.542 2.48-5.542 5.543H24c0-3.06-2.48-5.543-5.543-5.543z"
|
||||
fill="#03363D"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -36,3 +36,4 @@ export { Tally } from "./Tally";
|
||||
export { Tailscale } from "./Tailscale";
|
||||
export { ThirdPartyLogo } from "./ThirdPartyLogo";
|
||||
export { Vercel } from "./Vercel";
|
||||
export { Zendesk } from "./Zendesk";
|
||||
|
||||
Reference in New Issue
Block a user