Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui): feedback env settings page #3444

Merged
merged 2 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/webapp/src/layout/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const DashboardLayout = forwardRef<HTMLDivElement, DashboardLayoutI>(function Da
<div className="w-[250px] h-screen z-10 flex-grow-0">
<LeftNavBar selectedItem={selectedItem} />
</div>
<div className="flex-grow relative h-screen flex flex-col overflow-hidden">
<div className="flex-grow relative h-screen flex flex-col">
<div className="h-[57px] w-full">
<TopNavBar />
</div>
Expand Down
19 changes: 14 additions & 5 deletions packages/webapp/src/pages/Environment/Settings/Authorization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ import { EditableInput } from './EditableInput';

import { useToast } from '../../../hooks/useToast';
import { Switch } from '../../../components/ui/Switch';
import { useState } from 'react';
import Spinner from '../../../components/ui/Spinner';

export const AuthorizationSettings: React.FC = () => {
const { toast } = useToast();

const env = useStore((state) => state.env);
const { environmentAndAccount, mutate } = useEnvironment(env);

const [loading, setLoading] = useState(false);

const onHmacEnabled = async (isChecked: boolean) => {
setLoading(true);
const res = await apiPatchEnvironment(env, {
hmac_enabled: isChecked
});
setLoading(false);

if ('error' in res.json) {
toast({ title: 'There was an issue updating the HMAC', variant: 'error' });
Expand Down Expand Up @@ -57,11 +63,14 @@ export const AuthorizationSettings: React.FC = () => {
<label htmlFor={'hmac_enabled'} className={`text-s`}>
Enabled
</label>
<Switch
name="hmac_enabled"
checked={environmentAndAccount.environment.hmac_enabled}
onCheckedChange={(checked) => onHmacEnabled(!!checked)}
/>
<div className="flex gap-2 items-center">
{loading && <Spinner size={1} />}
<Switch
name="hmac_enabled"
checked={environmentAndAccount.environment.hmac_enabled}
onCheckedChange={(checked) => onHmacEnabled(!!checked)}
/>
</div>
</div>

<EditableInput
Expand Down
8 changes: 4 additions & 4 deletions packages/webapp/src/pages/Environment/Settings/Backend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ export const BackendSettings: React.FC = () => {
const hasNewSecretKey = environmentAndAccount.environment.pending_secret_key;
return (
<div className="text-grayscale-100 flex flex-col gap-10">
<div className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10">
<Link className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10" to="#backend" id="backend">
<div>
<IconServer stroke={1} size={18} />
</div>
<h3 className="uppercase text-sm">Backend Settings</h3>
</div>
</Link>
<div className="px-8 flex flex-col gap-10 w-3/5">
<fieldset className="flex flex-col gap-2">
<label htmlFor="secretKey" className="font-semibold mb-2">
Expand All @@ -104,9 +104,9 @@ export const BackendSettings: React.FC = () => {
/>
</div>
{!hasNewSecretKey && (
<div className="">
<div className="flex justify-end">
<Button variant={'secondary'} onClick={onGenerate} isLoading={loading}>
<IconKey stroke={1} />
<IconKey stroke={1} size={18} />
Generate new secret key
</Button>
</div>
Expand Down
9 changes: 5 additions & 4 deletions packages/webapp/src/pages/Environment/Settings/Export.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconEdit, IconServer, IconTrash } from '@tabler/icons-react';
import { IconEdit, IconPackageExport, IconTrash } from '@tabler/icons-react';
import { useStore } from '../../../store';
import { apiPatchEnvironment, useEnvironment } from '../../../hooks/useEnvironment';
import { Input } from '../../../components/ui/input/Input';
Expand All @@ -8,6 +8,7 @@ import { useToast } from '../../../hooks/useToast';
import { Button } from '../../../components/ui/button/Button';
import SecretInput from '../../../components/ui/input/SecretInput';
import { EditableInput } from './EditableInput';
import { Link } from 'react-router-dom';

export const ExportSettings: React.FC = () => {
const env = useStore((state) => state.env);
Expand Down Expand Up @@ -100,12 +101,12 @@ export const ExportSettings: React.FC = () => {

return (
<div className="text-grayscale-100 flex flex-col gap-10">
<div className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10">
<Link className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10" to="#export" id="export">
<div>
<IconServer stroke={1} size={18} />
<IconPackageExport stroke={1} size={18} />
</div>
<h3 className="uppercase text-sm">Export Settings</h3>
</div>
</Link>
<div className="px-8 flex flex-col gap-4 w-3/5">
<fieldset className="flex flex-col">
<label className="font-semibold">OpenTelemetry</label>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconServer } from '@tabler/icons-react';
import { IconBell } from '@tabler/icons-react';
import { useStore } from '../../../store';
import { apiPatchEnvironment, apiPatchWebhook, useEnvironment } from '../../../hooks/useEnvironment';
import { Button } from '../../../components/ui/button/Button';
Expand All @@ -9,6 +9,7 @@ import { connectSlack } from '../../../utils/slack-connection';
import IntegrationLogo from '../../../components/ui/IntegrationLogo';
import { WebhookCheckboxes } from './WebhookCheckboxes';
import { EditableInput } from './EditableInput';
import { Link } from 'react-router-dom';

export const NotificationSettings: React.FC = () => {
const env = useStore((state) => state.env);
Expand Down Expand Up @@ -60,12 +61,12 @@ export const NotificationSettings: React.FC = () => {

return (
<div className="text-grayscale-100 flex flex-col gap-10">
<div className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10">
<Link className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10" to="#notification" id="notification">
<div>
<IconServer stroke={1} size={18} />
<IconBell stroke={1} size={18} />
</div>
<h3 className="uppercase text-sm">Notification Settings</h3>
</div>
</Link>
<div className="px-8 flex flex-col gap-10 w-3/5">
<div className="flex flex-col gap-4">
<label className="font-semibold">Webhooks URLs</label>
Expand Down
24 changes: 23 additions & 1 deletion packages/webapp/src/pages/Environment/Settings/Show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,32 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '..
import { useEnvironment } from '../../../hooks/useEnvironment';
import { useStore } from '../../../store';
import { Skeleton } from '../../../components/ui/Skeleton';
import { useEffect, useState } from 'react';

export const EnvironmentSettings: React.FC = () => {
const env = useStore((state) => state.env);
const { environmentAndAccount } = useEnvironment(env);
const [scrolled, setScrolled] = useState(false);

useEffect(() => {
if (!environmentAndAccount || scrolled) {
return;
}

setScrolled(true);
const hash = window.location.hash.slice(1); // Remove the '#' character from the hash
if (!hash) {
return;
}

const element = document.getElementById(hash);
if (!element) {
return;
}

element.scrollIntoView({ behavior: 'smooth' });
}, [environmentAndAccount]);

if (!environmentAndAccount) {
return (
<DashboardLayout selectedItem={LeftNavBarItems.EnvironmentSettings} className="p-6">
Expand Down Expand Up @@ -47,7 +69,7 @@ export const EnvironmentSettings: React.FC = () => {
<VariablesSettings />
<ExportSettings />
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionItem value="item-1" id="authorization">
<AccordionTrigger>Deprecated authorization settings</AccordionTrigger>
<AccordionContent>
<AuthorizationSettings />
Expand Down
9 changes: 5 additions & 4 deletions packages/webapp/src/pages/Environment/Settings/Variables.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconEdit, IconServer, IconTrash } from '@tabler/icons-react';
import { IconCode, IconEdit, IconTrash } from '@tabler/icons-react';
import { useStore } from '../../../store';
import { apiPostVariables, useEnvironment } from '../../../hooks/useEnvironment';
import { useState } from 'react';
Expand All @@ -8,6 +8,7 @@ import { cn } from '../../../utils/utils';
import SecretInput from '../../../components/ui/input/SecretInput';
import type { ApiEnvironmentVariable } from '@nangohq/types';
import { useToast } from '../../../hooks/useToast';
import { Link } from 'react-router-dom';

export const VariablesSettings: React.FC = () => {
const { toast } = useToast();
Expand Down Expand Up @@ -113,12 +114,12 @@ export const VariablesSettings: React.FC = () => {

return (
<div className="text-grayscale-100 flex flex-col gap-10">
<div className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10">
<Link className="flex gap-2 items-center rounded-md bg-grayscale-900 px-8 h-10" to="#script" id="script">
<div>
<IconServer stroke={1} size={18} />
<IconCode stroke={1} size={18} />
</div>
<h3 className="uppercase text-sm">Script Settings</h3>
</div>
</Link>
<div className="px-8 flex flex-col gap-10 w-3/5">
<fieldset className="flex flex-col gap-2">
<label htmlFor="envvar" className="font-semibold mb-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ApiWebhooks } from '@nangohq/types';
import { useToast } from '../../../hooks/useToast';
import { apiPatchWebhook } from '../../../hooks/useEnvironment';
import { Switch } from '../../../components/ui/Switch';
import Spinner from '../../../components/ui/Spinner';

interface CheckboxConfig {
label: string;
Expand Down Expand Up @@ -42,49 +43,51 @@ interface CheckboxFormProps {
export const WebhookCheckboxes: React.FC<CheckboxFormProps> = ({ env, checkboxState, mutate }) => {
const { toast } = useToast();

const [isLoading, setIsLoading] = useState(false);
const [loading, setLoading] = useState<string | false>();

const handleCheckboxChange = async (name: string, checked: boolean) => {
if (isLoading) {
if (loading) {
return;
}

setIsLoading(true);
setLoading(name);
const res = await apiPatchWebhook(env, {
on_auth_creation: checkboxState['on_auth_creation'],
on_auth_refresh_error: checkboxState['on_auth_refresh_error'],
on_sync_completion_always: checkboxState['on_sync_completion_always'],
on_sync_error: checkboxState['on_sync_error'],
[name]: checked
});
setLoading(false);

if ('error' in res.json) {
toast({ title: 'There was an issue updating the webhook settings', variant: 'error' });
setIsLoading(false);
return;
}

toast({ title: 'Webhook settings updated successfully!', variant: 'success' });
mutate();

setIsLoading(false);
toast({ title: 'Webhook settings updated successfully!', variant: 'success' });
};

return (
<form onSubmit={(e) => e.preventDefault()} className="flex flex-col gap-5 mt-1">
<div className="flex flex-col gap-5 mt-1">
{checkboxesConfig.map(({ label, stateKey }) => (
<div className="flex items-center justify-between" key={stateKey}>
<label htmlFor={stateKey} className={`text-sm font-medium`}>
{label}
</label>

<Switch
name="hmac_enabled"
checked={checkboxState[stateKey] as boolean}
onCheckedChange={(checked) => handleCheckboxChange(stateKey, Boolean(checked))}
/>
<div className="flex gap-2 items-center">
{loading === stateKey && <Spinner size={1} />}
<Switch
name="hmac_enabled"
checked={checkboxState[stateKey] as boolean}
onCheckedChange={(checked) => handleCheckboxChange(stateKey, Boolean(checked))}
/>
</div>
</div>
))}
</form>
</div>
);
};
Loading