mirror of
https://github.com/hanzoai/dataroom.git
synced 2026-10-30 18:41:07 +00:00
Drop the dual-licensed `ee/` tree entirely. All features previously
locked behind the Hanzo Dataroom Commercial License are now part of the
AGPL-3.0 core and freely available — custom domains, branded viewing,
advanced analytics, watermarks, access controls, Q&A, AI vector stores,
workflows, SAML SSO, dataroom invitations, conversations, templates.
Layout changes:
* `ee/features/{ai,conversations,workflows,permissions,storage,templates,
dataroom-invitations,access-notifications,security,conversions}/` →
`features/<name>/` (AGPL).
* `ee/limits/` → `lib/billing/limits/` with every plan resolving to an
unlimited profile (no users/links/documents/domains/datarooms cap,
conversations and watermarks always on).
* `ee/stripe/` → `lib/billing/legacy/` (already a compat shim over
`@hanzoai/commerce`; renewal-reminder webhook is now a no-op).
* `ee/features/billing/cancellation/` deleted. Pause/unpause/cancel/
retention routes now compile against `lib/billing/cancellation.ts`
stubs that return 410 Gone; `isTeamPaused`/`isTeamPausedById` in
`lib/billing/paused.ts` always report active. `CancellationModal` is
a no-op shim in `components/billing/`.
* `app/(ee)/api/*` route group folded into `app/api/*` (SAML auth,
SCIM, AI chat, workflows, FAQ, link-upload).
Build: `npm run build` green.
143 lines
3.9 KiB
TypeScript
143 lines
3.9 KiB
TypeScript
import { useRouter } from "next/router";
|
|
|
|
import { useState } from "react";
|
|
|
|
import { useLimits } from "@/lib/billing/limits/swr-handler";
|
|
import { PlanEnum } from "@/lib/billing/legacy/constants";
|
|
import { toast } from "sonner";
|
|
import { mutate } from "swr";
|
|
|
|
import { UpgradePlanModal } from "@/components/billing/upgrade-plan-modal";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardFooter,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
|
|
import { usePlan } from "@/lib/swr/use-billing";
|
|
import useDataroomsSimple from "@/lib/swr/use-datarooms-simple";
|
|
|
|
export default function DuplicateDataroom({
|
|
dataroomId,
|
|
teamId,
|
|
}: {
|
|
dataroomId: string;
|
|
teamId?: string;
|
|
}) {
|
|
const router = useRouter();
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
const [planModalOpen, setPlanModalOpen] = useState<boolean>(false);
|
|
const { limits } = useLimits();
|
|
const { isBusiness, isDatarooms, isDataroomsPlus, isTrial } = usePlan();
|
|
const { datarooms: dataRooms } = useDataroomsSimple();
|
|
const numDatarooms = dataRooms?.length ?? 0;
|
|
const limitDatarooms = limits?.datarooms ?? 1;
|
|
|
|
const isTrialDatarooms = isTrial;
|
|
const canCreateUnlimitedDatarooms =
|
|
isDatarooms ||
|
|
isDataroomsPlus ||
|
|
(isBusiness && numDatarooms < limitDatarooms);
|
|
|
|
const handleDuplicateDataroom = async (
|
|
e: React.MouseEvent<HTMLButtonElement>,
|
|
) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if (!teamId) {
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
toast.promise(
|
|
fetch(`/api/teams/${teamId}/datarooms/${dataroomId}/duplicate`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
}).then(async (response) => {
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(
|
|
errorData.message || "An error occurred while copying dataroom.",
|
|
);
|
|
}
|
|
return response.json();
|
|
}),
|
|
{
|
|
loading: "Copying dataroom...",
|
|
success: (dataroom) => {
|
|
mutate(`/api/teams/${teamId}/datarooms`);
|
|
mutate(`/api/teams/${teamId}/datarooms?simple=true`);
|
|
router.push(`/datarooms/${dataroom.id}/documents`);
|
|
return "Dataroom copied successfully.";
|
|
},
|
|
error: (error) => {
|
|
return error.message;
|
|
},
|
|
},
|
|
);
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const ButtonList = () => {
|
|
if (
|
|
(isBusiness && !canCreateUnlimitedDatarooms) ||
|
|
(isTrialDatarooms &&
|
|
dataRooms &&
|
|
!isBusiness &&
|
|
!isDatarooms &&
|
|
!isDataroomsPlus)
|
|
) {
|
|
return (
|
|
<Button onClick={(e) => setPlanModalOpen(true)} loading={loading}>
|
|
Upgrade to Duplicate Datarooms
|
|
</Button>
|
|
);
|
|
} else {
|
|
return (
|
|
<Button onClick={(e) => handleDuplicateDataroom(e)} loading={loading}>
|
|
Duplicate Dataroom
|
|
</Button>
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="rounded-lg">
|
|
<Card className="bg-transparent">
|
|
<CardHeader>
|
|
<CardTitle>Duplicate Dataroom</CardTitle>
|
|
<CardDescription>
|
|
Create a new data room with the same content (folders and files) as
|
|
this data room.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent></CardContent>
|
|
<CardFooter className="flex items-center justify-end rounded-b-lg border-t px-6 py-3">
|
|
<div className="shrink-0">{ButtonList()}</div>
|
|
</CardFooter>
|
|
</Card>
|
|
{planModalOpen ? (
|
|
<UpgradePlanModal
|
|
clickedPlan={PlanEnum.DataRooms}
|
|
trigger="datarooms"
|
|
open={planModalOpen}
|
|
setOpen={setPlanModalOpen}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|