Merge branch 'dev' into refactor/paginated-tables

This commit is contained in:
Fred KISSIE
2026-02-11 04:12:40 +01:00
25 changed files with 692 additions and 702 deletions

View File

@@ -16,11 +16,9 @@ import { useTranslations } from "next-intl";
import Link from "next/link";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { Fragment, useActionState } from "react";
import { ApprovalsEmptyState } from "./ApprovalsEmptyState";
import { Badge } from "./ui/badge";
import { Button } from "./ui/button";
import { Card, CardHeader } from "./ui/card";
import { InfoPopup } from "./ui/info-popup";
import { Label } from "./ui/label";
import {
Select,
@@ -30,6 +28,9 @@ import {
SelectValue
} from "./ui/select";
import { Separator } from "./ui/separator";
import { InfoPopup } from "./ui/info-popup";
import { ApprovalsEmptyState } from "./ApprovalsEmptyState";
import { usePaidStatus } from "@app/hooks/usePaidStatus";
export type ApprovalFeedProps = {
orgId: string;
@@ -50,6 +51,8 @@ export function ApprovalFeed({
Object.fromEntries(searchParams.entries())
);
const { isPaidUser } = usePaidStatus();
const {
data,
isFetching,
@@ -58,7 +61,10 @@ export function ApprovalFeed({
hasNextPage,
fetchNextPage,
isFetchingNextPage
} = useInfiniteQuery(approvalQueries.listApprovals(orgId, filters));
} = useInfiniteQuery({
...approvalQueries.listApprovals(orgId, filters),
enabled: isPaidUser
});
const approvals = data?.pages.flatMap((data) => data.approvals) ?? [];

View File

@@ -160,56 +160,51 @@ export default function CreateRoleForm({
</FormItem>
)}
/>
{build !== "oss" && (
<div>
<PaidFeaturesAlert />
<FormField
control={form.control}
name="requireDeviceApproval"
render={({ field }) => (
<FormItem className="my-2">
<FormControl>
<CheckboxWithLabel
{...field}
disabled={
!isPaidUser
}
value="on"
checked={form.watch(
"requireDeviceApproval"
)}
onCheckedChange={(
<PaidFeaturesAlert />
<FormField
control={form.control}
name="requireDeviceApproval"
render={({ field }) => (
<FormItem className="my-2">
<FormControl>
<CheckboxWithLabel
{...field}
disabled={!isPaidUser}
value="on"
checked={form.watch(
"requireDeviceApproval"
)}
onCheckedChange={(
checked
) => {
if (
checked !==
"indeterminate"
) {
form.setValue(
"requireDeviceApproval",
checked
) => {
if (
checked !==
"indeterminate"
) {
form.setValue(
"requireDeviceApproval",
checked
);
}
}}
label={t(
"requireDeviceApproval"
)}
/>
</FormControl>
);
}
}}
label={t(
"requireDeviceApproval"
)}
/>
</FormControl>
<FormDescription>
{t(
"requireDeviceApprovalDescription"
)}
</FormDescription>
<FormDescription>
{t(
"requireDeviceApprovalDescription"
)}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
</CredenzaBody>

View File

@@ -168,56 +168,50 @@ export default function EditRoleForm({
</FormItem>
)}
/>
{build !== "oss" && (
<div>
<PaidFeaturesAlert />
<PaidFeaturesAlert />
<FormField
control={form.control}
name="requireDeviceApproval"
render={({ field }) => (
<FormItem className="my-2">
<FormControl>
<CheckboxWithLabel
{...field}
disabled={
!isPaidUser
}
value="on"
checked={form.watch(
"requireDeviceApproval"
)}
onCheckedChange={(
<FormField
control={form.control}
name="requireDeviceApproval"
render={({ field }) => (
<FormItem className="my-2">
<FormControl>
<CheckboxWithLabel
{...field}
disabled={!isPaidUser}
value="on"
checked={form.watch(
"requireDeviceApproval"
)}
onCheckedChange={(
checked
) => {
if (
checked !==
"indeterminate"
) {
form.setValue(
"requireDeviceApproval",
checked
) => {
if (
checked !==
"indeterminate"
) {
form.setValue(
"requireDeviceApproval",
checked
);
}
}}
label={t(
"requireDeviceApproval"
)}
/>
</FormControl>
);
}
}}
label={t(
"requireDeviceApproval"
)}
/>
</FormControl>
<FormDescription>
{t(
"requireDeviceApprovalDescription"
)}
</FormDescription>
<FormDescription>
{t(
"requireDeviceApprovalDescription"
)}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
</CredenzaBody>

View File

@@ -1,8 +1,16 @@
"use client";
import { Alert, AlertDescription } from "@app/components/ui/alert";
import { Card, CardContent } from "@app/components/ui/card";
import { build } from "@server/build";
import { useTranslations } from "next-intl";
import { usePaidStatus } from "@app/hooks/usePaidStatus";
import { ExternalLink, KeyRound, Sparkles } from "lucide-react";
import { useTranslations } from "next-intl";
import Link from "next/link";
const bannerClassName =
"mb-6 border-primary/30 bg-linear-to-br from-primary/10 via-background to-background overflow-hidden";
const bannerContentClassName = "py-3 px-4";
const bannerRowClassName =
"flex items-center gap-2.5 text-sm text-muted-foreground";
export function PaidFeaturesAlert() {
const t = useTranslations();
@@ -10,19 +18,50 @@ export function PaidFeaturesAlert() {
return (
<>
{build === "saas" && !hasSaasSubscription ? (
<Alert variant="info" className="mb-6">
<AlertDescription>
{t("subscriptionRequiredToUse")}
</AlertDescription>
</Alert>
<Card className={bannerClassName}>
<CardContent className={bannerContentClassName}>
<div className={bannerRowClassName}>
<KeyRound className="size-4 shrink-0 text-primary" />
<span>{t("subscriptionRequiredToUse")}</span>
</div>
</CardContent>
</Card>
) : null}
{build === "enterprise" && !hasEnterpriseLicense ? (
<Alert variant="info" className="mb-6">
<AlertDescription>
{t("licenseRequiredToUse")}
</AlertDescription>
</Alert>
<Card className={bannerClassName}>
<CardContent className={bannerContentClassName}>
<div className={bannerRowClassName}>
<KeyRound className="size-4 shrink-0 text-primary" />
<span>{t("licenseRequiredToUse")}</span>
</div>
</CardContent>
</Card>
) : null}
{build === "oss" && !hasEnterpriseLicense ? (
<Card className={bannerClassName}>
<CardContent className={bannerContentClassName}>
<div className={bannerRowClassName}>
<KeyRound className="size-4 shrink-0 text-primary" />
<span>
{t.rich("ossEnterpriseEditionRequired", {
enterpriseEditionLink: (chunks) => (
<Link
href="https://docs.pangolin.net/self-host/enterprise-edition"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 font-medium text-foreground underline"
>
{chunks}
<ExternalLink className="size-3.5 shrink-0" />
</Link>
)
})}
</span>
</div>
</CardContent>
</Card>
) : null}
</>
);

View File

@@ -43,11 +43,11 @@ export function OlmInstallCommands({
All: [
{
title: t("install"),
command: `curl -fsSL https://static.pangolin.net/get-olm.sh | bash`
command: `curl -fsSL https://static.pangolin.net/get-cli.sh | bash`
},
{
title: t("run"),
command: `sudo olm --id ${id} --secret ${secret} --endpoint ${endpoint}`
command: `sudo pangolin up --id ${id} --secret ${secret} --endpoint ${endpoint} --attach`
}
]
},