mirror of
https://github.com/fosrl/pangolin.git
synced 2026-04-15 22:36:37 +00:00
Merge branch 'dev' into private-site-ha
This commit is contained in:
@@ -319,6 +319,7 @@ export default function DeviceLoginForm({
|
||||
<div className="flex justify-center">
|
||||
<InputOTP
|
||||
maxLength={9}
|
||||
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
|
||||
{...field}
|
||||
value={field.value
|
||||
.replace(/-/g, "")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
@@ -40,9 +41,12 @@ import {
|
||||
Check,
|
||||
CheckCircle2,
|
||||
ChevronsUpDown,
|
||||
KeyRound,
|
||||
Zap
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { usePaidStatus } from "@/hooks/usePaidStatus";
|
||||
import { TierFeature, tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import { toUnicode } from "punycode";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
@@ -95,6 +99,7 @@ export default function DomainPicker({
|
||||
const { env } = useEnvContext();
|
||||
const api = createApiClient({ env });
|
||||
const t = useTranslations();
|
||||
const { hasSaasSubscription } = usePaidStatus();
|
||||
|
||||
const { data = [], isLoading: loadingDomains } = useQuery(
|
||||
orgQueries.domains({ orgId })
|
||||
@@ -509,9 +514,11 @@ export default function DomainPicker({
|
||||
<span className="truncate">
|
||||
{selectedBaseDomain.domain}
|
||||
</span>
|
||||
{selectedBaseDomain.verified && (
|
||||
<CheckCircle2 className="h-3 w-3 text-green-500 shrink-0" />
|
||||
)}
|
||||
{selectedBaseDomain.verified &&
|
||||
selectedBaseDomain.domainType !==
|
||||
"wildcard" && (
|
||||
<CheckCircle2 className="h-3 w-3 text-green-500 shrink-0" />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
t("domainPickerSelectBaseDomain")
|
||||
@@ -574,14 +581,23 @@ export default function DomainPicker({
|
||||
}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{orgDomain.type.toUpperCase()}{" "}
|
||||
•{" "}
|
||||
{orgDomain.verified
|
||||
{orgDomain.type ===
|
||||
"wildcard"
|
||||
? t(
|
||||
"domainPickerVerified"
|
||||
"domainPickerManual"
|
||||
)
|
||||
: t(
|
||||
"domainPickerUnverified"
|
||||
: (
|
||||
<>
|
||||
{orgDomain.type.toUpperCase()}{" "}
|
||||
•{" "}
|
||||
{orgDomain.verified
|
||||
? t(
|
||||
"domainPickerVerified"
|
||||
)
|
||||
: t(
|
||||
"domainPickerUnverified"
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -680,6 +696,23 @@ export default function DomainPicker({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{build === "saas" &&
|
||||
!hasSaasSubscription(
|
||||
tierMatrix[TierFeature.DomainNamespaces]
|
||||
) &&
|
||||
!hideFreeDomain && (
|
||||
<Card className="mt-3 border-black-500/30 bg-linear-to-br from-black-500/10 via-background to-background overflow-hidden">
|
||||
<CardContent className="py-3 px-4">
|
||||
<div className="flex items-center gap-2.5 text-sm text-muted-foreground">
|
||||
<KeyRound className="size-4 shrink-0 text-black-500" />
|
||||
<span>
|
||||
{t("domainPickerFreeDomainsPaidFeature")}
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/*showProvidedDomainSearch && build === "saas" && (
|
||||
<Alert>
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
|
||||
@@ -39,7 +39,11 @@ export default function InviteStatusCard({
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
const [type, setType] = useState<
|
||||
"rejected" | "wrong_user" | "user_does_not_exist" | "not_logged_in" | "user_limit_exceeded"
|
||||
| "rejected"
|
||||
| "wrong_user"
|
||||
| "user_does_not_exist"
|
||||
| "not_logged_in"
|
||||
| "user_limit_exceeded"
|
||||
>("rejected");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -90,12 +94,12 @@ export default function InviteStatusCard({
|
||||
|
||||
if (!user && type === "user_does_not_exist") {
|
||||
const redirectUrl = email
|
||||
? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}`
|
||||
? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${email}`
|
||||
: `/auth/signup?redirect=/invite?token=${tokenParam}`;
|
||||
router.push(redirectUrl);
|
||||
} else if (!user && type === "not_logged_in") {
|
||||
const redirectUrl = email
|
||||
? `/auth/login?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}`
|
||||
? `/auth/login?redirect=/invite?token=${tokenParam}&email=${email}`
|
||||
: `/auth/login?redirect=/invite?token=${tokenParam}`;
|
||||
router.push(redirectUrl);
|
||||
} else {
|
||||
@@ -109,7 +113,7 @@ export default function InviteStatusCard({
|
||||
async function goToLogin() {
|
||||
await api.post("/auth/logout", {});
|
||||
const redirectUrl = email
|
||||
? `/auth/login?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}`
|
||||
? `/auth/login?redirect=/invite?token=${tokenParam}&email=${email}`
|
||||
: `/auth/login?redirect=/invite?token=${tokenParam}`;
|
||||
router.push(redirectUrl);
|
||||
}
|
||||
@@ -117,7 +121,7 @@ export default function InviteStatusCard({
|
||||
async function goToSignup() {
|
||||
await api.post("/auth/logout", {});
|
||||
const redirectUrl = email
|
||||
? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}`
|
||||
? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${email}`
|
||||
: `/auth/signup?redirect=/invite?token=${tokenParam}`;
|
||||
router.push(redirectUrl);
|
||||
}
|
||||
@@ -157,7 +161,9 @@ export default function InviteStatusCard({
|
||||
Cannot Accept Invite
|
||||
</p>
|
||||
<p className="text-center text-sm">
|
||||
This organization has reached its user limit. Please contact the organization administrator to upgrade their plan before accepting this invite.
|
||||
This organization has reached its user limit. Please
|
||||
contact the organization administrator to upgrade their
|
||||
plan before accepting this invite.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -164,7 +164,7 @@ const countryClass = cn(
|
||||
|
||||
const highlightedCountryClass = cn(
|
||||
sharedCountryClass,
|
||||
"stroke-2",
|
||||
"stroke-[3]",
|
||||
"fill-[#f4f4f5]",
|
||||
"stroke-[#f36117]",
|
||||
"dark:fill-[#3f3f46]"
|
||||
@@ -194,11 +194,20 @@ function drawInteractiveCountries(
|
||||
const path = setupProjetionPath();
|
||||
const data = parseWorldTopoJsonToGeoJsonFeatures();
|
||||
const svg = d3.select(element);
|
||||
const countriesLayer = svg.append("g");
|
||||
const hoverLayer = svg.append("g").style("pointer-events", "none");
|
||||
const hoverPath = hoverLayer
|
||||
.append("path")
|
||||
.datum(null)
|
||||
.attr("class", highlightedCountryClass)
|
||||
.style("display", "none");
|
||||
|
||||
svg.selectAll("path")
|
||||
countriesLayer
|
||||
.selectAll("path")
|
||||
.data(data)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("data-country-path", "true")
|
||||
.attr("class", countryClass)
|
||||
.attr("d", path as never)
|
||||
|
||||
@@ -209,9 +218,10 @@ function drawInteractiveCountries(
|
||||
y,
|
||||
hoveredCountryAlpha3Code: country.properties.a3
|
||||
});
|
||||
// brings country to front
|
||||
this.parentNode?.appendChild(this);
|
||||
d3.select(this).attr("class", highlightedCountryClass);
|
||||
hoverPath
|
||||
.datum(country)
|
||||
.attr("d", path(country) as string)
|
||||
.style("display", null);
|
||||
})
|
||||
|
||||
.on("mousemove", function (event) {
|
||||
@@ -221,7 +231,7 @@ function drawInteractiveCountries(
|
||||
|
||||
.on("mouseout", function () {
|
||||
setTooltip({ x: 0, y: 0, hoveredCountryAlpha3Code: null });
|
||||
d3.select(this).attr("class", countryClass);
|
||||
hoverPath.style("display", "none");
|
||||
});
|
||||
|
||||
return svg;
|
||||
@@ -257,7 +267,7 @@ function colorInCountriesWithValues(
|
||||
const svg = d3.select(element);
|
||||
|
||||
return svg
|
||||
.selectAll("path")
|
||||
.selectAll('path[data-country-path="true"]')
|
||||
.style("fill", (countryPath) => {
|
||||
const country = getCountryByCountryPath(countryPath);
|
||||
if (!country?.count) {
|
||||
|
||||
Reference in New Issue
Block a user