diff --git a/messages/en-US.json b/messages/en-US.json index 24587cae6..af2287db7 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -80,7 +80,7 @@ "siteManageSites": "Manage Sites", "siteDescription": "Create and manage sites to enable connectivity to private networks", "sitesBannerTitle": "Connect Any Network", - "sitesBannerDescription": "A site is a connection to a remote network that allows Pangolin to provide access to resources, whether public or private, to users anywhere. Install the site network connector (Newt) anywhere you can run a binary or container to establish the connection.", + "sitesBannerDescription": "A site is a connection to a remote network that allows Pangolin to provide access to resources, whether public or private, to users anywhere. Install the site network connector anywhere you can run a binary or container to establish the connection.", "sitesBannerButtonText": "Install Site Connector", "approvalsBannerTitle": "Approve or Deny Device Access", "approvalsBannerDescription": "Review and approve or deny device access requests from users. When device approvals are required, users must get admin approval before their devices can connect to your organization's resources.", @@ -596,7 +596,7 @@ "licensePricingPage": "For the most up-to-date pricing and discounts, please visit the ", "invite": "Invitations", "inviteRegenerate": "Regenerate Invitation", - "inviteRegenerateDescription": "Revoke previous invitation and create a new one", + "inviteRegenerateDescription": "Create a new invite link for this user. The previous invitation will be revoked.", "inviteRemove": "Remove Invitation", "inviteRemoveError": "Failed to remove invitation", "inviteRemoveErrorDescription": "An error occurred while removing the invitation.", @@ -1356,7 +1356,7 @@ "siteLabelsDescription": "Manage labels associated with this site.", "labelsNotFound": "No labels found.", "labelsEmptyCreateHint": "Start typing above to create a label.", - "labelSearch": "Search labels", + "labelSearch": "Search labels...", "labelSearchOrCreate": "Search or create a label", "accessLabelFilterCount": "{count, plural, one {# label} other {# labels}}", "labelOverflowCount": "+{count, plural, one {# label} other {# labels}}", @@ -3996,11 +3996,12 @@ "disconnected": "Disconnected", "approvalsEmptyStateTitle": "Device Approvals Not Enabled", "approvalsEmptyStateDescription": "Enable device approvals for roles to require admin approval before users can connect new devices.", + "approvalsEmptyStateHowToTitle": "How to Enable", "approvalsEmptyStateStep1Title": "Go to Roles", "approvalsEmptyStateStep1Description": "Navigate to your organization's roles settings to configure device approvals.", "approvalsEmptyStateStep2Title": "Enable Device Approvals", "approvalsEmptyStateStep2Description": "Edit a role and enable the 'Require Device Approvals' option. Users with this role will need admin approval for new devices.", - "approvalsEmptyStatePreviewDescription": "Preview: When enabled, pending device requests will appear here for review", + "approvalsEmptyStatePreviewDescription": "When enabled, pending device requests will appear here for review.", "approvalsEmptyStateButtonText": "Manage Roles", "domainErrorTitle": "We are having trouble verifying your domain", "idpAdminAutoProvisionPoliciesTabHint": "Configure role mapping and organization policies on the Auto Provision Settings tab.", diff --git a/src/app/[orgId]/settings/resources/public/ProxyResourceTargetsForm.tsx b/src/app/[orgId]/settings/resources/public/ProxyResourceTargetsForm.tsx index ba331ed4e..04a92ace6 100644 --- a/src/app/[orgId]/settings/resources/public/ProxyResourceTargetsForm.tsx +++ b/src/app/[orgId]/settings/resources/public/ProxyResourceTargetsForm.tsx @@ -954,7 +954,6 @@ export const ProxyResourceTargetsForm = forwardRef< colSpan={columns.length} message={emptyMessage ?? t("targetNoOne")} action={addTargetButton} - compact /> )} diff --git a/src/components/ApprovalsEmptyState.tsx b/src/components/ApprovalsEmptyState.tsx index 098c3488a..809fe0476 100644 --- a/src/components/ApprovalsEmptyState.tsx +++ b/src/components/ApprovalsEmptyState.tsx @@ -1,15 +1,12 @@ "use client"; import { Button } from "@app/components/ui/button"; -import { Card, CardContent } from "@app/components/ui/card"; import { - ShieldCheck, - Check, - Ban, - User, - Settings, - ArrowRight -} from "lucide-react"; + SettingsSection, + SettingsSectionBody, + SettingsSectionFooter +} from "@app/components/Settings"; +import { ArrowRight, Settings, ShieldCheck, User } from "lucide-react"; import { useTranslations } from "next-intl"; import Link from "next/link"; @@ -21,102 +18,50 @@ export function ApprovalsEmptyState({ orgId }: ApprovalsEmptyStateProps) { const t = useTranslations(); return ( -
- - -
-
-

- {t("approvalsEmptyStateTitle")} -

-

- {t("approvalsEmptyStateDescription")} -

-
+ + +
+ +

+ {t("approvalsEmptyStateTitle")} +

+

+ {t("approvalsEmptyStateDescription")} +

-
-
-
-
- -
-
-

- {t("approvalsEmptyStateStep1Title")} -

-

- {t( - "approvalsEmptyStateStep1Description" - )} -

-
-
- -
-
- -
-
-

- {t("approvalsEmptyStateStep2Title")} -

-

- {t( - "approvalsEmptyStateStep2Description" - )} -

-
-
-
- - {/* Abstract UI Preview - Hidden on mobile */} -
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - - - +
+

+ {t("approvalsEmptyStateHowToTitle")} +

+
    +
  • + + + {t("approvalsEmptyStateStep1Description")} + +
  • +
  • + + + {t("approvalsEmptyStateStep2Description")} + +
  • +
- - -
+ +

+ {t("approvalsEmptyStatePreviewDescription")} +

+
+ + + + + ); } diff --git a/src/components/RegenerateInvitationForm.tsx b/src/components/RegenerateInvitationForm.tsx index ce261d02e..e9e36d53c 100644 --- a/src/components/RegenerateInvitationForm.tsx +++ b/src/components/RegenerateInvitationForm.tsx @@ -52,9 +52,11 @@ export default function RegenerateInvitationForm({ }: RegenerateInvitationFormProps) { const [loading, setLoading] = useState(false); const [inviteLink, setInviteLink] = useState(null); - const [sendEmail, setSendEmail] = useState(true); + const [expiresInDays, setExpiresInDays] = useState(3); + const { env } = useEnvContext(); + const [sendEmail, setSendEmail] = useState(env.email.emailEnabled); const [validHours, setValidHours] = useState(72); - const api = createApiClient(useEnvContext()); + const api = createApiClient({ env }); const { org } = useOrgContext(); const t = useTranslations(); @@ -71,10 +73,11 @@ export default function RegenerateInvitationForm({ useEffect(() => { if (open) { - setSendEmail(true); + setSendEmail(env.email.emailEnabled); setValidHours(72); + setExpiresInDays(3); } - }, [open]); + }, [open, env.email.emailEnabled]); async function handleRegenerate() { if (!invitation) return; @@ -96,15 +99,16 @@ export default function RegenerateInvitationForm({ email: invitation.email, roleIds: invitation.roleIds, validHours, - sendEmail, + sendEmail: env.email.emailEnabled && sendEmail, regenerate: true }); if (res.status === 200) { const link = res.data.data.inviteLink; setInviteLink(link); + setExpiresInDays(validHours / 24); - if (sendEmail) { + if (sendEmail && env.email.emailEnabled) { toast({ variant: "default", title: t("inviteRegenerated"), @@ -127,9 +131,7 @@ export default function RegenerateInvitationForm({ onRegenerate({ id: invitation.id, email: invitation.email, - expiresAt: new Date( - res.data.data.expiresAt - ).toISOString(), + expiresAt: new Date(res.data.data.expiresAt).toISOString(), roleLabels: invitation.roleLabels, roleIds: invitation.roleIds }); @@ -174,33 +176,47 @@ export default function RegenerateInvitationForm({ > - {t("inviteRegenerate")} + + {inviteLink + ? t("inviteRegenerated") + : t("inviteRegenerate")} + - {t("inviteRegenerateDescription")} + {inviteLink + ? sendEmail && env.email.emailEnabled + ? t("inviteEmailSentDescription") + : t("inviteSentDescription") + : t("inviteRegenerateDescription")} {!inviteLink ? ( -
-

- {t("inviteQuestionRegenerate", { - email: invitation?.email || "" - })} -

-
- - setSendEmail(e as boolean) - } - /> - +
+
+ +

{invitation?.email}

-
- + + {env.email.emailEnabled && ( +
+ + setSendEmail(e as boolean) + } + /> + +
+ )} + +
+