diff --git a/messages/en-US.json b/messages/en-US.json
index 57821a6d..8b04e4ea 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -1480,7 +1480,7 @@
"IAgreeToThe": "I agree to the",
"termsOfService": "terms of service",
"and": "and",
- "privacyPolicy": "privacy policy"
+ "privacyPolicy": "privacy policy."
},
"signUpMarketing": {
"keepMeInTheLoop": "Keep me in the loop with news, updates, and new features by email."
diff --git a/server/routers/client/getClient.ts b/server/routers/client/getClient.ts
index cfb2652b..f054ce80 100644
--- a/server/routers/client/getClient.ts
+++ b/server/routers/client/getClient.ts
@@ -36,7 +36,7 @@ async function query(clientId?: number, niceId?: string, orgId?: string) {
.select()
.from(clients)
.where(and(eq(clients.niceId, niceId), eq(clients.orgId, orgId)))
- .leftJoin(olms, eq(olms.clientId, olms.clientId))
+ .leftJoin(olms, eq(clients.clientId, olms.clientId))
.limit(1);
return res;
}
diff --git a/src/app/[orgId]/settings/(private)/idp/create/page.tsx b/src/app/[orgId]/settings/(private)/idp/create/page.tsx
index 786c8635..f6260073 100644
--- a/src/app/[orgId]/settings/(private)/idp/create/page.tsx
+++ b/src/app/[orgId]/settings/(private)/idp/create/page.tsx
@@ -285,7 +285,7 @@ export default function Page() {
diff --git a/src/app/globals.css b/src/app/globals.css
index 70c614c0..731e1bff 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -178,4 +178,16 @@ p {
.animate-dot-pulse {
animation: dot-pulse 1.4s ease-in-out infinite;
}
+
+ /* Use JavaScript-set viewport height for mobile to handle keyboard properly */
+ .h-screen-safe {
+ height: 100vh; /* Default for desktop and fallback */
+ }
+
+ /* Only apply custom viewport height on mobile */
+ @media (max-width: 767px) {
+ .h-screen-safe {
+ height: var(--vh, 100vh); /* Use CSS variable set by ViewportHeightFix on mobile */
+ }
+ }
}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index e76a5d2f..203dd778 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -22,6 +22,7 @@ import { TopLoader } from "@app/components/Toploader";
import Script from "next/script";
import { TanstackQueryProvider } from "@app/components/TanstackQueryProvider";
import { TailwindIndicator } from "@app/components/TailwindIndicator";
+import { ViewportHeightFix } from "@app/components/ViewportHeightFix";
export const metadata: Metadata = {
title: `Dashboard - ${process.env.BRANDING_APP_NAME || "Pangolin"}`,
@@ -77,7 +78,7 @@ export default async function RootLayout({
return (
-
+
{build === "saas" && (
)}
+
+
{/* Desktop Sidebar */}
{showSidebar && (
{children}
diff --git a/src/components/LayoutMobileMenu.tsx b/src/components/LayoutMobileMenu.tsx
index a7588f35..2b5fb320 100644
--- a/src/components/LayoutMobileMenu.tsx
+++ b/src/components/LayoutMobileMenu.tsx
@@ -48,7 +48,7 @@ export function LayoutMobileMenu({
const t = useTranslations();
return (
-
+
{showSidebar && (
diff --git a/src/components/ProxyResourcesTable.tsx b/src/components/ProxyResourcesTable.tsx
index bd4bb4e1..69b180c4 100644
--- a/src/components/ProxyResourcesTable.tsx
+++ b/src/components/ProxyResourcesTable.tsx
@@ -198,7 +198,7 @@ export default function ProxyResourcesTable({
if (!targets || targets.length === 0) {
return (
-
+
{t("resourcesTableNoTargets")}
diff --git a/src/components/ViewportHeightFix.tsx b/src/components/ViewportHeightFix.tsx
new file mode 100644
index 00000000..340bbcbb
--- /dev/null
+++ b/src/components/ViewportHeightFix.tsx
@@ -0,0 +1,79 @@
+"use client";
+
+import { useEffect } from "react";
+
+/**
+ * Fixes mobile viewport height issues when keyboard opens/closes
+ * by setting a CSS variable with a stable viewport height
+ * Only applies on mobile devices (< 768px, matching Tailwind's md breakpoint)
+ */
+export function ViewportHeightFix() {
+ useEffect(() => {
+ // Check if we're on mobile (md breakpoint is typically 768px)
+ const isMobile = () => window.innerWidth < 768;
+
+ // On desktop, don't set --vh at all, let CSS use 100vh directly
+ if (!isMobile()) {
+ // Remove --vh if it was set, so CSS falls back to 100vh
+ document.documentElement.style.removeProperty("--vh");
+ return;
+ }
+
+ // Mobile-specific logic
+ let maxHeight = window.innerHeight;
+ let resizeTimer: NodeJS.Timeout;
+
+ // Set the viewport height as a CSS variable
+ const setViewportHeight = (height: number) => {
+ document.documentElement.style.setProperty("--vh", `${height}px`);
+ };
+
+ // Set initial value
+ setViewportHeight(maxHeight);
+
+ const handleResize = () => {
+ // If we switched to desktop, remove --vh and stop
+ if (!isMobile()) {
+ document.documentElement.style.removeProperty("--vh");
+ return;
+ }
+
+ clearTimeout(resizeTimer);
+ resizeTimer = setTimeout(() => {
+ const currentHeight = window.innerHeight;
+
+ // Track the maximum height we've seen (when keyboard is closed)
+ if (currentHeight > maxHeight) {
+ maxHeight = currentHeight;
+ setViewportHeight(maxHeight);
+ }
+ // If current height is close to max, update max (keyboard closed)
+ else if (currentHeight >= maxHeight * 0.9) {
+ maxHeight = currentHeight;
+ setViewportHeight(maxHeight);
+ }
+ // Otherwise, keep using the max height (keyboard is open)
+ }, 100);
+ };
+
+ const handleOrientationChange = () => {
+ // Reset on orientation change
+ setTimeout(() => {
+ maxHeight = window.innerHeight;
+ setViewportHeight(maxHeight);
+ }, 150);
+ };
+
+ window.addEventListener("resize", handleResize);
+ window.addEventListener("orientationchange", handleOrientationChange);
+
+ return () => {
+ window.removeEventListener("resize", handleResize);
+ window.removeEventListener("orientationchange", handleOrientationChange);
+ clearTimeout(resizeTimer);
+ };
+ }, []);
+
+ return null;
+}
+
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 9f32e9ce..aca27270 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -73,35 +73,30 @@ const Button = React.forwardRef(
>
{asChild ? (
props.children
- ) : (
+ ) : loading ? (
-
+
{props.children}
- {loading && (
-
-
-
-
-
-
+
+
+
+
+
- )}
+
+ ) : (
+ props.children
)}
);
diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx
index ab68e1bf..85825dc1 100644
--- a/src/components/ui/checkbox.tsx
+++ b/src/components/ui/checkbox.tsx
@@ -14,13 +14,13 @@ const checkboxVariants = cva(
variants: {
variant: {
outlinePrimary:
- "border rounded-[5px] border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
+ "border rounded-[5px] border-input data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
outline:
- "border rounded-[5px] border-input data-[state=checked]:bg-muted data-[state=checked]:text-accent-foreground",
+ "border rounded-[5px] border-input data-[state=checked]:border-primary data-[state=checked]:bg-muted data-[state=checked]:text-accent-foreground",
outlinePrimarySquare:
- "border rounded-[5px] border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
+ "border rounded-[5px] border-input data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
outlineSquare:
- "border rounded-[5px] border-input data-[state=checked]:bg-muted data-[state=checked]:text-accent-foreground"
+ "border rounded-[5px] border-input data-[state=checked]:border-primary data-[state=checked]:bg-muted data-[state=checked]:text-accent-foreground"
}
},
defaultVariants: {
@@ -30,8 +30,7 @@ const checkboxVariants = cva(
);
interface CheckboxProps
- extends
- React.ComponentPropsWithoutRef,
+ extends React.ComponentPropsWithoutRef,
VariantProps {}
const Checkbox = React.forwardRef<
@@ -50,9 +49,8 @@ const Checkbox = React.forwardRef<
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
-interface CheckboxWithLabelProps extends React.ComponentPropsWithoutRef<
- typeof Checkbox
-> {
+interface CheckboxWithLabelProps
+ extends React.ComponentPropsWithoutRef {
label: string;
}