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());
|
.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({
|
export function AddAccessSourceDialog({
|
||||||
children,
|
children,
|
||||||
organizationId,
|
organizationId,
|
||||||
@@ -184,6 +195,7 @@ export function AddAccessSourceDialog({
|
|||||||
const apiKeyDialogRef = useDialogRef();
|
const apiKeyDialogRef = useDialogRef();
|
||||||
const clientCredentialsDialogRef = useDialogRef();
|
const clientCredentialsDialogRef = useDialogRef();
|
||||||
const datadogDialogRef = useDialogRef();
|
const datadogDialogRef = useDialogRef();
|
||||||
|
const zendeskDialogRef = useDialogRef();
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [activeProvider, setActiveProvider] = useState<ProviderInfo | null>(null);
|
const [activeProvider, setActiveProvider] = useState<ProviderInfo | null>(null);
|
||||||
@@ -191,6 +203,9 @@ export function AddAccessSourceDialog({
|
|||||||
const [datadogSite, setDatadogSite] = useState<string>("US1");
|
const [datadogSite, setDatadogSite] = useState<string>("US1");
|
||||||
const [datadogProvider, setDatadogProvider] = useState<ProviderInfo | null>(null);
|
const [datadogProvider, setDatadogProvider] = useState<ProviderInfo | null>(null);
|
||||||
|
|
||||||
|
const [zendeskSubdomain, setZendeskSubdomain] = useState<string>("");
|
||||||
|
const [zendeskProvider, setZendeskProvider] = useState<ProviderInfo | null>(null);
|
||||||
|
|
||||||
const [apiKeyValue, setApiKeyValue] = useState("");
|
const [apiKeyValue, setApiKeyValue] = useState("");
|
||||||
const [extraSettingValues, setExtraSettingValues] = useState<Record<string, string>>({});
|
const [extraSettingValues, setExtraSettingValues] = useState<Record<string, string>>({});
|
||||||
const [isConnectingAPIKey, setIsConnectingAPIKey] = useState(false);
|
const [isConnectingAPIKey, setIsConnectingAPIKey] = useState(false);
|
||||||
@@ -277,6 +292,12 @@ export function AddAccessSourceDialog({
|
|||||||
datadogDialogRef.current?.open();
|
datadogDialogRef.current?.open();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openZendeskDialog = (info: ProviderInfo) => {
|
||||||
|
setZendeskProvider(info);
|
||||||
|
setZendeskSubdomain("");
|
||||||
|
zendeskDialogRef.current?.open();
|
||||||
|
};
|
||||||
|
|
||||||
const createSourceAfterConnector = (
|
const createSourceAfterConnector = (
|
||||||
connectorId: string,
|
connectorId: string,
|
||||||
displayName: string,
|
displayName: string,
|
||||||
@@ -470,10 +491,15 @@ export function AddAccessSourceDialog({
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
info.provider === "DATADOG"
|
if (info.provider === "DATADOG") {
|
||||||
? openDatadogDialog(info)
|
openDatadogDialog(info);
|
||||||
: connectOAuthProvider(info)}
|
} else if (info.provider === "ZENDESK") {
|
||||||
|
openZendeskDialog(info);
|
||||||
|
} else {
|
||||||
|
connectOAuthProvider(info);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{__("Connect")}
|
{__("Connect")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -808,6 +834,44 @@ export function AddAccessSourceDialog({
|
|||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</Dialog>
|
</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 { Tailscale } from "./Tailscale";
|
||||||
import { Tally } from "./Tally";
|
import { Tally } from "./Tally";
|
||||||
import { Vercel } from "./Vercel";
|
import { Vercel } from "./Vercel";
|
||||||
|
import { Zendesk } from "./Zendesk";
|
||||||
|
|
||||||
const thirdParties: Record<string, FC<ComponentProps<"svg">>> = {
|
const thirdParties: Record<string, FC<ComponentProps<"svg">>> = {
|
||||||
ANTHROPIC: Anthropic,
|
ANTHROPIC: Anthropic,
|
||||||
@@ -93,6 +94,7 @@ const thirdParties: Record<string, FC<ComponentProps<"svg">>> = {
|
|||||||
TAILSCALE: Tailscale,
|
TAILSCALE: Tailscale,
|
||||||
TALLY: Tally,
|
TALLY: Tally,
|
||||||
VERCEL: Vercel,
|
VERCEL: Vercel,
|
||||||
|
ZENDESK: Zendesk,
|
||||||
};
|
};
|
||||||
|
|
||||||
type ThirdPartyLogoProps = ComponentProps<"svg"> & {
|
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 { Tailscale } from "./Tailscale";
|
||||||
export { ThirdPartyLogo } from "./ThirdPartyLogo";
|
export { ThirdPartyLogo } from "./ThirdPartyLogo";
|
||||||
export { Vercel } from "./Vercel";
|
export { Vercel } from "./Vercel";
|
||||||
|
export { Zendesk } from "./Zendesk";
|
||||||
|
|||||||
Reference in New Issue
Block a user