feat: support send email
This commit is contained in:
@@ -5,7 +5,6 @@ import { User } from "@prisma/client";
|
||||
import { PenLine, RefreshCwIcon } from "lucide-react";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { fetcher, timeAgo } from "@/lib/utils";
|
||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -15,8 +14,8 @@ import {
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
Table,
|
||||
@@ -33,9 +32,9 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import StatusDot from "@/components/dashboard/status-dot";
|
||||
import { FormType } from "@/components/forms/record-form";
|
||||
import { UserForm } from "@/components/forms/user-form";
|
||||
import { EmptyPlaceholder } from "@/components/shared/empty-placeholder";
|
||||
import { Icons } from "@/components/shared/icons";
|
||||
import { PaginationWrapper } from "@/components/shared/pagination";
|
||||
|
||||
import CountUpFn from "../../../../components/dashboard/count-up";
|
||||
@@ -46,7 +45,7 @@ export interface UrlListProps {
|
||||
|
||||
function TableColumnSekleton({ className }: { className?: string }) {
|
||||
return (
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-7">
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-8">
|
||||
<TableCell className="col-span-1">
|
||||
<Skeleton className="h-5 w-20" />
|
||||
</TableCell>
|
||||
@@ -62,6 +61,9 @@ function TableColumnSekleton({ className }: { className?: string }) {
|
||||
<TableCell className="col-span-1 hidden justify-center sm:flex">
|
||||
<Skeleton className="h-5 w-16" />
|
||||
</TableCell>
|
||||
<TableCell className="col-span-1 hidden justify-center sm:flex">
|
||||
<Skeleton className="h-5 w-16" />
|
||||
</TableCell>
|
||||
<TableCell className="col-span-1 flex justify-center">
|
||||
<Skeleton className="h-5 w-16" />
|
||||
</TableCell>
|
||||
@@ -75,10 +77,14 @@ export default function UsersList({ user }: UrlListProps) {
|
||||
const [currentEditUser, setcurrentEditUser] = useState<User | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
email: "",
|
||||
userName: "",
|
||||
});
|
||||
|
||||
const { mutate } = useSWRConfig();
|
||||
const { data, error, isLoading } = useSWR<{ total: number; list: User[] }>(
|
||||
`/api/user/admin?page=${currentPage}&size=${pageSize}`,
|
||||
`/api/user/admin?page=${currentPage}&size=${pageSize}&email=${searchParams.email}&userName=${searchParams.userName}`,
|
||||
fetcher,
|
||||
{
|
||||
revalidateOnFocus: false,
|
||||
@@ -86,7 +92,10 @@ export default function UsersList({ user }: UrlListProps) {
|
||||
);
|
||||
|
||||
const handleRefresh = () => {
|
||||
mutate(`/api/user/admin?page=${currentPage}&size=${pageSize}`, undefined);
|
||||
mutate(
|
||||
`/api/user/admin?page=${currentPage}&size=${pageSize}&email=${searchParams.email}&userName=${searchParams.userName}`,
|
||||
undefined,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -124,9 +133,60 @@ export default function UsersList({ user }: UrlListProps) {
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
)}
|
||||
<div className="mb-2 flex-row items-center gap-2 space-y-2 sm:flex sm:space-y-0">
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
className="h-8 text-xs md:text-xs"
|
||||
placeholder="Search by email..."
|
||||
value={searchParams.email}
|
||||
onChange={(e) => {
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
email: e.target.value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{searchParams.email && (
|
||||
<Button
|
||||
className="absolute right-2 top-1/2 h-6 -translate-y-1/2 rounded-full px-1 text-gray-500 hover:text-gray-700"
|
||||
onClick={() =>
|
||||
setSearchParams({ ...searchParams, email: "" })
|
||||
}
|
||||
variant={"ghost"}
|
||||
>
|
||||
<Icons.close className="size-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
className="h-8 text-xs md:text-xs"
|
||||
placeholder="Search by user name..."
|
||||
value={searchParams.userName}
|
||||
onChange={(e) => {
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
userName: e.target.value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{searchParams.userName && (
|
||||
<Button
|
||||
className="absolute right-2 top-1/2 h-6 -translate-y-1/2 rounded-full px-1 text-gray-500 hover:text-gray-700"
|
||||
onClick={() =>
|
||||
setSearchParams({ ...searchParams, userName: "" })
|
||||
}
|
||||
variant={"ghost"}
|
||||
>
|
||||
<Icons.close className="size-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Table>
|
||||
<TableHeader className="bg-gray-100/50 dark:bg-primary-foreground">
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-7">
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-8">
|
||||
<TableHead className="col-span-1 flex items-center font-bold">
|
||||
Name
|
||||
</TableHead>
|
||||
@@ -136,6 +196,9 @@ export default function UsersList({ user }: UrlListProps) {
|
||||
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
|
||||
Role
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
|
||||
Plan
|
||||
</TableHead>
|
||||
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
|
||||
Status
|
||||
</TableHead>
|
||||
@@ -158,7 +221,7 @@ export default function UsersList({ user }: UrlListProps) {
|
||||
data.list.map((user) => (
|
||||
<TableRow
|
||||
key={user.id}
|
||||
className="grid animate-fade-in grid-cols-3 items-center animate-in sm:grid-cols-7"
|
||||
className="grid animate-fade-in grid-cols-3 items-center animate-in sm:grid-cols-8"
|
||||
>
|
||||
<TableCell className="col-span-1 truncate">
|
||||
<TooltipProvider>
|
||||
@@ -187,6 +250,11 @@ export default function UsersList({ user }: UrlListProps) {
|
||||
{user.role}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="col-span-1 hidden justify-center sm:flex">
|
||||
<Badge className="text-xs" variant="outline">
|
||||
{user.team}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="col-span-1 hidden justify-center sm:flex">
|
||||
<StatusDot status={user.active} />
|
||||
</TableCell>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getUserRecordCount } from "@/lib/dto/cloudflare-dns-record";
|
||||
import { getAllUserEmailsCount } from "@/lib/dto/email";
|
||||
import { getUserShortUrlCount } from "@/lib/dto/short-urls";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { Team_Plan_Quota } from "@/lib/team";
|
||||
import { constructMetadata } from "@/lib/utils";
|
||||
import { DashboardHeader } from "@/components/dashboard/header";
|
||||
|
||||
@@ -35,12 +36,15 @@ export default async function DashboardPage() {
|
||||
<DashboardHeader heading="Dashboard" text="" />
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3 xl:grid-cols-3">
|
||||
<HeroCard count={email_count} total={siteConfig.freeQuota.url} />
|
||||
<HeroCard
|
||||
count={email_count}
|
||||
total={Team_Plan_Quota[user.team].EM_EmailAddresses}
|
||||
/>
|
||||
<DashboardInfoCard
|
||||
userId={user.id}
|
||||
title="DNS Records"
|
||||
count={record_count}
|
||||
total={siteConfig.freeQuota.record}
|
||||
total={Team_Plan_Quota[user.team].RC_NewRecords}
|
||||
link="/dashboard/records"
|
||||
icon="globeLock"
|
||||
/>
|
||||
@@ -48,7 +52,7 @@ export default async function DashboardPage() {
|
||||
userId={user.id}
|
||||
title="Short URLs"
|
||||
count={url_count}
|
||||
total={siteConfig.freeQuota.url}
|
||||
total={Team_Plan_Quota[user.team].SL_NewLinks}
|
||||
link="/dashboard/urls"
|
||||
icon="link"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { resend } from "@/lib/email";
|
||||
import { isValidEmail } from "@/lib/utils";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const { from, to, subject, html } = await req.json();
|
||||
|
||||
if (!from || !to || !subject || !html) {
|
||||
return NextResponse.json("Missing required fields", { status: 400 });
|
||||
}
|
||||
|
||||
if (!isValidEmail(from) || !isValidEmail(to)) {
|
||||
return NextResponse.json("Invalid email address", { status: 403 });
|
||||
}
|
||||
|
||||
const { data, error } = await resend.emails.send({
|
||||
from,
|
||||
to,
|
||||
subject,
|
||||
html,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.log("Resend error:", error);
|
||||
return NextResponse.json("Failed to send email", { status: 500 });
|
||||
}
|
||||
|
||||
return NextResponse.json("success", { status: 200 });
|
||||
} catch (error) {
|
||||
console.log("Error sending email:", error);
|
||||
return NextResponse.json("Internal server error", { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,14 @@ export async function GET(req: Request) {
|
||||
const url = new URL(req.url);
|
||||
const page = url.searchParams.get("page");
|
||||
const size = url.searchParams.get("size");
|
||||
const data = await getAllUsers(Number(page || "1"), Number(size || "10"));
|
||||
const email = url.searchParams.get("email") || "";
|
||||
const userName = url.searchParams.get("userName") || "";
|
||||
const data = await getAllUsers(
|
||||
Number(page || "1"),
|
||||
Number(size || "10"),
|
||||
email,
|
||||
userName,
|
||||
);
|
||||
|
||||
return Response.json(data);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { User } from "@prisma/client";
|
||||
|
||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
// import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
import EmailList from "@/components/email/EmailList";
|
||||
import EmailSidebar from "@/components/email/EmailSidebar";
|
||||
|
||||
@@ -13,7 +13,7 @@ export function EmailDashboard({ user }: { user: User }) {
|
||||
>(null);
|
||||
const [selectedEmailId, setSelectedEmailId] = useState<string | null>(null);
|
||||
|
||||
const { isTablet } = useMediaQuery();
|
||||
// const { isTablet } = useMediaQuery();
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
||||
// useEffect(() => {
|
||||
@@ -23,7 +23,7 @@ export function EmailDashboard({ user }: { user: User }) {
|
||||
return (
|
||||
<div className="flex h-[calc(100vh-60px)]">
|
||||
<EmailSidebar
|
||||
className={!isCollapsed ? "w-56 xl:w-64" : "w-16"}
|
||||
className={!isCollapsed ? "w-64 xl:w-72" : "w-16"}
|
||||
user={user}
|
||||
onSelectEmail={setSelectedEmailAddress}
|
||||
selectedEmailAddress={selectedEmailAddress}
|
||||
|
||||
@@ -65,7 +65,7 @@ export const {
|
||||
token.picture = dbUser.image;
|
||||
token.role = dbUser.role;
|
||||
token.active = dbUser.active;
|
||||
token.team = dbUser.team;
|
||||
token.team = dbUser.team || "free";
|
||||
token.apiKey = dbUser.apiKey;
|
||||
|
||||
return token;
|
||||
|
||||
@@ -220,15 +220,15 @@ export default function EmailDetail({
|
||||
<h3 className="mb-2 text-sm font-semibold text-neutral-700 dark:text-neutral-400">
|
||||
Attachments ({attachments.length})
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<div className="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3">
|
||||
{attachments.map((attachment, index) => {
|
||||
const FileIcon = getFileIcon(attachment.mimeType); // 动态获取图标
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="group flex items-center justify-between rounded-md border border-dotted bg-gray-100 p-2 transition-shadow hover:border-dashed dark:bg-neutral-800"
|
||||
className="group relative flex items-center justify-between rounded-md border border-dotted bg-gray-100 p-2 transition-shadow hover:border-dashed dark:bg-neutral-800"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2 overflow-hidden">
|
||||
{attachment.mimeType.startsWith("image/") ? (
|
||||
<BlurImg
|
||||
src={`${siteConfig.emailR2Domain}/${attachment.r2Path}`}
|
||||
@@ -244,7 +244,10 @@ export default function EmailDetail({
|
||||
<FileIcon className="size-4 text-neutral-500 dark:text-neutral-400" />
|
||||
)}
|
||||
<div>
|
||||
<p className="max-w-[120px] truncate text-xs text-neutral-800 dark:text-neutral-400">
|
||||
<p
|
||||
className="max-w-full truncate text-xs text-neutral-800 dark:text-neutral-400"
|
||||
title={attachment.filename}
|
||||
>
|
||||
{attachment.filename}
|
||||
</p>
|
||||
<p className="text-xs text-neutral-500">
|
||||
@@ -256,7 +259,7 @@ export default function EmailDetail({
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => handleDownload(attachment)}
|
||||
className="hidden animate-fade-in px-2 group-hover:block"
|
||||
className="absolute right-0 top-0 hidden transform animate-fade-in px-2 group-hover:block"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useTransition } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import Link from "next/link";
|
||||
import { ForwardEmail } from "@prisma/client";
|
||||
import { toast } from "sonner";
|
||||
@@ -12,8 +13,11 @@ import { Icons } from "../shared/icons";
|
||||
import { PaginationWrapper } from "../shared/pagination";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Button } from "../ui/button";
|
||||
import { Input } from "../ui/input";
|
||||
import { Modal } from "../ui/modal";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import { Switch } from "../ui/switch";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -23,6 +27,19 @@ import {
|
||||
import EmailDetail from "./EmailDetail";
|
||||
import Loader from "./Loader";
|
||||
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from "../ui/drawer";
|
||||
|
||||
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
|
||||
|
||||
interface EmailListProps {
|
||||
emailAddress: string | null;
|
||||
selectedEmailId: string | null;
|
||||
@@ -40,6 +57,9 @@ export default function EmailList({
|
||||
const [isAutoRefresh, setIsAutoRefresh] = useState(false);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [showSendDrawer, setShowSendDrawer] = useState(false);
|
||||
const [sendForm, setSendForm] = useState({ to: "", subject: "", html: "" });
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const { data, error, isLoading, mutate } = useSWR<{
|
||||
total: number;
|
||||
@@ -217,7 +237,44 @@ export default function EmailList({
|
||||
};
|
||||
|
||||
const handleOpenSendEmailModal = () => {
|
||||
toast.warning(`Work in progress...`);
|
||||
setShowSendDrawer(true);
|
||||
setSendForm({ to: "", subject: "", html: "" });
|
||||
};
|
||||
|
||||
const handleSendEmail = async () => {
|
||||
if (!emailAddress) {
|
||||
toast.error("No email address selected");
|
||||
return;
|
||||
}
|
||||
if (!sendForm.to || !sendForm.subject || !sendForm.html) {
|
||||
toast.error("Please fill in all fields");
|
||||
return;
|
||||
}
|
||||
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const response = await fetch("/api/email/send", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
from: emailAddress,
|
||||
to: sendForm.to,
|
||||
subject: sendForm.subject,
|
||||
html: sendForm.html,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
toast.success("Email sent successfully");
|
||||
setShowSendDrawer(false);
|
||||
} else {
|
||||
toast.error("Failed to send email", {
|
||||
description: await response.text(),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(error.message || "Error sending email");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -344,6 +401,83 @@ export default function EmailList({
|
||||
setCurrentPage={setCurrentPage}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 发送邮件 Modal */}
|
||||
<Drawer open={showSendDrawer} onOpenChange={setShowSendDrawer}>
|
||||
<DrawerContent className="fixed bottom-0 right-0 top-0 w-full rounded-none sm:max-w-xl">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle className="flex items-center gap-1">
|
||||
Send Email{" "}
|
||||
<Icons.help className="size-5 text-neutral-600 hover:text-neutral-400" />
|
||||
</DrawerTitle>
|
||||
<DrawerClose asChild>
|
||||
<Button variant="ghost" className="absolute right-4 top-4">
|
||||
<Icons.close className="h-4 w-4" />
|
||||
</Button>
|
||||
</DrawerClose>
|
||||
</DrawerHeader>
|
||||
<div className="scrollbar-hidden h-[calc(100vh)] space-y-4 overflow-y-auto p-6">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
||||
From
|
||||
</label>
|
||||
<Input value={emailAddress || ""} disabled className="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
||||
To
|
||||
</label>
|
||||
<Input
|
||||
value={sendForm.to}
|
||||
onChange={(e) =>
|
||||
setSendForm({ ...sendForm, to: e.target.value })
|
||||
}
|
||||
placeholder="recipient@example.com"
|
||||
className="mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
||||
Subject
|
||||
</label>
|
||||
<Input
|
||||
value={sendForm.subject}
|
||||
onChange={(e) =>
|
||||
setSendForm({ ...sendForm, subject: e.target.value })
|
||||
}
|
||||
placeholder="Enter subject"
|
||||
className="mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-neutral-700 dark:text-neutral-300">
|
||||
Content
|
||||
</label>
|
||||
<ReactQuill
|
||||
value={sendForm.html}
|
||||
onChange={(value) => setSendForm({ ...sendForm, html: value })}
|
||||
className="mt-1 h-40 rounded-lg"
|
||||
theme="snow"
|
||||
placeholder="Enter your message"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DrawerFooter>
|
||||
<DrawerClose asChild>
|
||||
<Button variant="outline" disabled={isPending}>
|
||||
Cancel
|
||||
</Button>
|
||||
</DrawerClose>
|
||||
<Button
|
||||
onClick={handleSendEmail}
|
||||
disabled={isPending}
|
||||
variant={"default"}
|
||||
>
|
||||
{isPending ? "Sending..." : "Send"}
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useTransition } from "react";
|
||||
import Link from "next/link";
|
||||
import { useState, useTransition } from "react";
|
||||
import { User, UserEmail } from "@prisma/client";
|
||||
import randomName from "@scaleway/random-name";
|
||||
import {
|
||||
@@ -18,7 +17,6 @@ import useSWRInfinite from "swr/infinite";
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { UserEmailList } from "@/lib/dto/email";
|
||||
import { cn, fetcher, timeAgo } from "@/lib/utils";
|
||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
|
||||
import { CopyButton } from "../shared/copy-button";
|
||||
import { EmptyPlaceholder } from "../shared/empty-placeholder";
|
||||
@@ -79,8 +77,7 @@ export default function EmailSidebar({
|
||||
const { data, isLoading, error, size, setSize, mutate } = useSWRInfinite<{
|
||||
list: UserEmailList[];
|
||||
total: number;
|
||||
}>(getKey, fetcher, { revalidateOnFocus: false, dedupingInterval: 3000 });
|
||||
console.log("[数据]", data);
|
||||
}>(getKey, fetcher, { dedupingInterval: 3000 });
|
||||
|
||||
if (
|
||||
!selectedEmailAddress &&
|
||||
@@ -208,7 +205,6 @@ export default function EmailSidebar({
|
||||
<div
|
||||
className={cn(`flex h-full flex-col border-r transition-all`, className)}
|
||||
>
|
||||
{isLoading}
|
||||
{/* Header */}
|
||||
<div className="border-b p-2 text-center">
|
||||
<div className="mb-2 flex items-center justify-center gap-2">
|
||||
|
||||
@@ -60,7 +60,7 @@ export function UserForm({
|
||||
email: initData?.email || "",
|
||||
image: initData?.image || "",
|
||||
role: initData?.role || "USER",
|
||||
team: initData?.team || "",
|
||||
team: initData?.team || "free",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -151,6 +151,22 @@ export function UserForm({
|
||||
</p>
|
||||
)}
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Active">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="active">
|
||||
Active
|
||||
</Label>
|
||||
<Switch
|
||||
id="active"
|
||||
{...register("active")}
|
||||
defaultChecked={initData?.active === 1}
|
||||
onCheckedChange={(value) => setValue("active", value ? 1 : 0)}
|
||||
/>
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Role">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
@@ -171,21 +187,25 @@ export function UserForm({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Active">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="active">
|
||||
Active
|
||||
</Label>
|
||||
<Switch
|
||||
id="active"
|
||||
{...register("active")}
|
||||
defaultChecked={initData?.active === 1}
|
||||
onCheckedChange={(value) => setValue("active", value ? 1 : 0)}
|
||||
/>
|
||||
</div>
|
||||
<FormSectionColumns title="Plan">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
setValue("team", value);
|
||||
}}
|
||||
name="team"
|
||||
defaultValue={`${initData?.team}` || "free"}
|
||||
>
|
||||
<SelectTrigger className="w-full shadow-inner">
|
||||
<SelectValue placeholder="Select a plan" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{["free", "premium", "business"].map((role) => (
|
||||
<SelectItem key={role} value={role}>
|
||||
{role}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { Drawer as DrawerPrimitive } from "vaul";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Drawer = ({
|
||||
shouldScaleBackground = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
||||
<DrawerPrimitive.Root
|
||||
shouldScaleBackground={shouldScaleBackground}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
Drawer.displayName = "Drawer";
|
||||
|
||||
const DrawerTrigger = DrawerPrimitive.Trigger;
|
||||
|
||||
const DrawerPortal = DrawerPrimitive.Portal;
|
||||
|
||||
const DrawerClose = DrawerPrimitive.Close;
|
||||
|
||||
const DrawerOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn("fixed inset-0 z-50 bg-black/80", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
||||
|
||||
const DrawerContent = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed bottom-0 right-0 z-50 flex h-auto flex-col rounded-t-[10px] border bg-background",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
));
|
||||
DrawerContent.displayName = "DrawerContent";
|
||||
|
||||
const DrawerHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DrawerHeader.displayName = "DrawerHeader";
|
||||
|
||||
const DrawerFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DrawerFooter.displayName = "DrawerFooter";
|
||||
|
||||
const DrawerTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
||||
|
||||
const DrawerDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Drawer,
|
||||
DrawerPortal,
|
||||
DrawerOverlay,
|
||||
DrawerTrigger,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerFooter,
|
||||
DrawerTitle,
|
||||
DrawerDescription,
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
import * as React from "react";
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
@@ -14,14 +14,14 @@ const ScrollArea = React.forwardRef<
|
||||
className={cn("relative overflow-hidden", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="size-full rounded-[inherit]">
|
||||
<ScrollAreaPrimitive.Viewport className="rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
));
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
@@ -36,13 +36,13 @@ const ScrollBar = React.forwardRef<
|
||||
"h-full w-2.5 border-l border-l-transparent p-px",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent p-px",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
));
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
export { ScrollArea, ScrollBar };
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
title: Team Plan
|
||||
description: Three different plans to choose from
|
||||
---
|
||||
+21
-2
@@ -33,16 +33,35 @@ export const getUserById = async (id: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllUsers = async (page: number, size: number) => {
|
||||
export const getAllUsers = async (
|
||||
page: number,
|
||||
size: number,
|
||||
email?: string,
|
||||
userName?: string,
|
||||
) => {
|
||||
try {
|
||||
let options;
|
||||
if (email) {
|
||||
options = { where: { email: { contains: email } } };
|
||||
}
|
||||
if (userName) {
|
||||
options = { where: { name: { contains: userName } } };
|
||||
}
|
||||
if (email && userName) {
|
||||
options = {
|
||||
where: { email: { contains: email }, name: { contains: userName } },
|
||||
};
|
||||
}
|
||||
|
||||
const [total, list] = await prisma.$transaction([
|
||||
prisma.user.count(), // 获取所有用户的总数
|
||||
prisma.user.count(options),
|
||||
prisma.user.findMany({
|
||||
skip: (page - 1) * size,
|
||||
take: size,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
...options,
|
||||
}),
|
||||
]);
|
||||
return {
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
export const Team_Plan_Quota = {
|
||||
free: {
|
||||
SL_TrackedClicks: 100000,
|
||||
SL_NewLinks: 1000,
|
||||
SL_AnalyticsRetention: 180,
|
||||
SL_Domains: 1,
|
||||
SL_AdvancedAnalytics: true,
|
||||
RC_NewRecords: 3,
|
||||
EM_EmailAddresses: 1000,
|
||||
EM_Domains: 1,
|
||||
APP_Support: "basic",
|
||||
APP_ApiAccess: true,
|
||||
},
|
||||
premium: {
|
||||
SL_TrackedClicks: 10000000,
|
||||
SL_NewLinks: 10000,
|
||||
SL_AnalyticsRetention: 360,
|
||||
SL_Domains: 3,
|
||||
SL_AdvancedAnalytics: true,
|
||||
RC_NewRecords: 3,
|
||||
EM_EmailAddresses: 10000,
|
||||
EM_Domains: 3,
|
||||
APP_Support: "live",
|
||||
APP_ApiAccess: true,
|
||||
},
|
||||
business: {
|
||||
SL_TrackedClicks: 10000000,
|
||||
SL_NewLinks: 10000,
|
||||
SL_AnalyticsRetention: 360,
|
||||
SL_Domains: 3,
|
||||
SL_AdvancedAnalytics: true,
|
||||
RC_NewRecords: 3,
|
||||
EM_EmailAddresses: 10000,
|
||||
EM_Domains: 3,
|
||||
APP_Support: "live",
|
||||
APP_ApiAccess: true,
|
||||
},
|
||||
};
|
||||
@@ -147,6 +147,9 @@ export async function fetcher<JSON = any>(
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export const isValidEmail = (email: string) =>
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
||||
|
||||
export function nFormatter(num: number, digits?: number) {
|
||||
if (!num) return "0";
|
||||
const lookup = [
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
"react-dom": "18.3.1",
|
||||
"react-email": "2.1.5",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
"recharts": "^2.12.7",
|
||||
"resend": "^3.4.0",
|
||||
|
||||
Generated
+190
-6
@@ -218,6 +218,9 @@ importers:
|
||||
react-hook-form:
|
||||
specifier: ^7.52.1
|
||||
version: 7.52.1(react@18.3.1)
|
||||
react-quill:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-textarea-autosize:
|
||||
specifier: ^8.5.3
|
||||
version: 8.5.3(@types/react@18.3.3)(react@18.3.1)
|
||||
@@ -3174,6 +3177,9 @@ packages:
|
||||
'@types/qrcode@1.5.5':
|
||||
resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
|
||||
|
||||
'@types/quill@1.3.10':
|
||||
resolution: {integrity: sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==}
|
||||
|
||||
'@types/react-dom@18.3.0':
|
||||
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
|
||||
|
||||
@@ -3661,10 +3667,18 @@ packages:
|
||||
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
|
||||
engines: {node: '>=10.16.0'}
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-bind@1.0.7:
|
||||
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-bound@1.0.4:
|
||||
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
callsites@3.1.0:
|
||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -3767,6 +3781,10 @@ packages:
|
||||
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
clone@2.1.2:
|
||||
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
clsx@1.2.1:
|
||||
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -4184,6 +4202,10 @@ packages:
|
||||
decode-named-character-reference@1.0.2:
|
||||
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
|
||||
|
||||
deep-equal@1.1.2:
|
||||
resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
|
||||
@@ -4268,6 +4290,10 @@ packages:
|
||||
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
earcut@2.2.4:
|
||||
resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==}
|
||||
|
||||
@@ -4340,6 +4366,10 @@ packages:
|
||||
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-define-property@1.0.1:
|
||||
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-errors@1.3.0:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4355,6 +4385,10 @@ packages:
|
||||
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-set-tostringtag@2.0.3:
|
||||
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4561,6 +4595,9 @@ packages:
|
||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
eventemitter3@2.0.3:
|
||||
resolution: {integrity: sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==}
|
||||
|
||||
eventemitter3@4.0.7:
|
||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||
|
||||
@@ -4589,6 +4626,9 @@ packages:
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
fast-diff@1.1.2:
|
||||
resolution: {integrity: sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==}
|
||||
|
||||
fast-equals@5.0.1:
|
||||
resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -4729,6 +4769,10 @@ packages:
|
||||
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-nonce@1.0.1:
|
||||
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -4736,6 +4780,10 @@ packages:
|
||||
get-own-enumerable-property-symbols@3.0.2:
|
||||
resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
|
||||
|
||||
get-proto@1.0.1:
|
||||
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-stream@6.0.1:
|
||||
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -4822,6 +4870,10 @@ packages:
|
||||
gopd@1.0.1:
|
||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
@@ -4858,6 +4910,10 @@ packages:
|
||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-symbols@1.1.0:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5012,6 +5068,10 @@ packages:
|
||||
is-alphanumerical@2.0.1:
|
||||
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
|
||||
|
||||
is-arguments@1.2.0:
|
||||
resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-array-buffer@3.0.4:
|
||||
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5451,6 +5511,10 @@ packages:
|
||||
engines: {node: '>= 16'}
|
||||
hasBin: true
|
||||
|
||||
math-intrinsics@1.1.0:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
md-to-react-email@5.0.2:
|
||||
resolution: {integrity: sha512-x6kkpdzIzUhecda/yahltfEl53mH26QdWu4abUF9+S0Jgam8P//Ciro8cdhyMHnT5MQUJYrIbO6ORM2UxPiNNA==}
|
||||
peerDependencies:
|
||||
@@ -5856,6 +5920,10 @@ packages:
|
||||
object-inspect@1.13.1:
|
||||
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
|
||||
|
||||
object-is@1.1.6:
|
||||
resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
object-keys@1.1.1:
|
||||
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5937,6 +6005,9 @@ packages:
|
||||
pako@0.2.9:
|
||||
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
|
||||
|
||||
parchment@1.1.4:
|
||||
resolution: {integrity: sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==}
|
||||
|
||||
parent-module@1.0.1:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -6246,6 +6317,13 @@ packages:
|
||||
quickselect@2.0.0:
|
||||
resolution: {integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==}
|
||||
|
||||
quill-delta@3.6.3:
|
||||
resolution: {integrity: sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
quill@1.3.7:
|
||||
resolution: {integrity: sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==}
|
||||
|
||||
randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
|
||||
@@ -6282,6 +6360,12 @@ packages:
|
||||
react-promise-suspense@0.3.4:
|
||||
resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==}
|
||||
|
||||
react-quill@2.0.0:
|
||||
resolution: {integrity: sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==}
|
||||
peerDependencies:
|
||||
react: ^16 || ^17 || ^18
|
||||
react-dom: ^16 || ^17 || ^18
|
||||
|
||||
react-remove-scroll-bar@2.3.4:
|
||||
resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -10689,6 +10773,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 20.14.11
|
||||
|
||||
'@types/quill@1.3.10':
|
||||
dependencies:
|
||||
parchment: 1.1.4
|
||||
|
||||
'@types/react-dom@18.3.0':
|
||||
dependencies:
|
||||
'@types/react': 18.3.3
|
||||
@@ -11295,6 +11383,11 @@ snapshots:
|
||||
dependencies:
|
||||
streamsearch: 1.1.0
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
|
||||
call-bind@1.0.7:
|
||||
dependencies:
|
||||
es-define-property: 1.0.0
|
||||
@@ -11303,6 +11396,11 @@ snapshots:
|
||||
get-intrinsic: 1.2.4
|
||||
set-function-length: 1.2.2
|
||||
|
||||
call-bound@1.0.4:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
callsites@3.1.0: {}
|
||||
|
||||
camel-case@4.1.2:
|
||||
@@ -11411,6 +11509,8 @@ snapshots:
|
||||
|
||||
clone@1.0.4: {}
|
||||
|
||||
clone@2.1.2: {}
|
||||
|
||||
clsx@1.2.1: {}
|
||||
|
||||
clsx@2.0.0: {}
|
||||
@@ -11840,6 +11940,15 @@ snapshots:
|
||||
dependencies:
|
||||
character-entities: 2.0.2
|
||||
|
||||
deep-equal@1.1.2:
|
||||
dependencies:
|
||||
is-arguments: 1.2.0
|
||||
is-date-object: 1.0.5
|
||||
is-regex: 1.1.4
|
||||
object-is: 1.1.6
|
||||
object-keys: 1.1.1
|
||||
regexp.prototype.flags: 1.5.2
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
deepmerge@4.3.1: {}
|
||||
@@ -11931,6 +12040,12 @@ snapshots:
|
||||
|
||||
dotenv@16.0.3: {}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
earcut@2.2.4: {}
|
||||
|
||||
eastasianwidth@0.2.0: {}
|
||||
@@ -12057,6 +12172,8 @@ snapshots:
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.4
|
||||
|
||||
es-define-property@1.0.1: {}
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
es-iterator-helpers@1.0.19:
|
||||
@@ -12082,6 +12199,10 @@ snapshots:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
es-set-tostringtag@2.0.3:
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.4
|
||||
@@ -12144,7 +12265,7 @@ snapshots:
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
|
||||
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0)
|
||||
eslint-plugin-react: 7.35.0(eslint@8.57.0)
|
||||
eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0)
|
||||
@@ -12180,8 +12301,8 @@ snapshots:
|
||||
debug: 4.3.4
|
||||
enhanced-resolve: 5.15.0
|
||||
eslint: 8.57.0
|
||||
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
|
||||
eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
|
||||
fast-glob: 3.3.2
|
||||
get-tsconfig: 4.7.2
|
||||
is-core-module: 2.13.1
|
||||
@@ -12192,7 +12313,7 @@ snapshots:
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.8.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
eslint-module-utils@2.8.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
@@ -12203,7 +12324,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||
eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
|
||||
dependencies:
|
||||
array-includes: 3.1.8
|
||||
array.prototype.findlastindex: 1.2.3
|
||||
@@ -12213,7 +12334,7 @@ snapshots:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.13.1
|
||||
is-glob: 4.0.3
|
||||
@@ -12399,6 +12520,8 @@ snapshots:
|
||||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
eventemitter3@2.0.3: {}
|
||||
|
||||
eventemitter3@4.0.7: {}
|
||||
|
||||
events@3.3.0: {}
|
||||
@@ -12437,6 +12560,8 @@ snapshots:
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-diff@1.1.2: {}
|
||||
|
||||
fast-equals@5.0.1: {}
|
||||
|
||||
fast-glob@3.3.2:
|
||||
@@ -12575,10 +12700,28 @@ snapshots:
|
||||
has-symbols: 1.0.3
|
||||
hasown: 2.0.2
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-define-property: 1.0.1
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
function-bind: 1.1.2
|
||||
get-proto: 1.0.1
|
||||
gopd: 1.2.0
|
||||
has-symbols: 1.1.0
|
||||
hasown: 2.0.2
|
||||
math-intrinsics: 1.1.0
|
||||
|
||||
get-nonce@1.0.1: {}
|
||||
|
||||
get-own-enumerable-property-symbols@3.0.2: {}
|
||||
|
||||
get-proto@1.0.1:
|
||||
dependencies:
|
||||
dunder-proto: 1.0.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
get-stream@6.0.1: {}
|
||||
|
||||
get-stream@8.0.1: {}
|
||||
@@ -12688,6 +12831,8 @@ snapshots:
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.4
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
graphemer@1.4.0: {}
|
||||
@@ -12715,6 +12860,8 @@ snapshots:
|
||||
|
||||
has-symbols@1.0.3: {}
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
dependencies:
|
||||
has-symbols: 1.0.3
|
||||
@@ -12941,6 +13088,11 @@ snapshots:
|
||||
is-alphabetical: 2.0.1
|
||||
is-decimal: 2.0.1
|
||||
|
||||
is-arguments@1.2.0:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
has-tostringtag: 1.0.2
|
||||
|
||||
is-array-buffer@3.0.4:
|
||||
dependencies:
|
||||
call-bind: 1.0.7
|
||||
@@ -13343,6 +13495,8 @@ snapshots:
|
||||
|
||||
marked@7.0.4: {}
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
md-to-react-email@5.0.2(react@18.3.1):
|
||||
dependencies:
|
||||
marked: 7.0.4
|
||||
@@ -14043,6 +14197,11 @@ snapshots:
|
||||
|
||||
object-inspect@1.13.1: {}
|
||||
|
||||
object-is@1.1.6:
|
||||
dependencies:
|
||||
call-bind: 1.0.7
|
||||
define-properties: 1.2.1
|
||||
|
||||
object-keys@1.1.1: {}
|
||||
|
||||
object.assign@4.1.5:
|
||||
@@ -14143,6 +14302,8 @@ snapshots:
|
||||
|
||||
pako@0.2.9: {}
|
||||
|
||||
parchment@1.1.4: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
@@ -14396,6 +14557,21 @@ snapshots:
|
||||
|
||||
quickselect@2.0.0: {}
|
||||
|
||||
quill-delta@3.6.3:
|
||||
dependencies:
|
||||
deep-equal: 1.1.2
|
||||
extend: 3.0.2
|
||||
fast-diff: 1.1.2
|
||||
|
||||
quill@1.3.7:
|
||||
dependencies:
|
||||
clone: 2.1.2
|
||||
deep-equal: 1.1.2
|
||||
eventemitter3: 2.0.3
|
||||
extend: 3.0.2
|
||||
parchment: 1.1.4
|
||||
quill-delta: 3.6.3
|
||||
|
||||
randombytes@2.1.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
@@ -14481,6 +14657,14 @@ snapshots:
|
||||
dependencies:
|
||||
fast-deep-equal: 2.0.1
|
||||
|
||||
react-quill@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@types/quill': 1.3.10
|
||||
lodash: 4.17.21
|
||||
quill: 1.3.7
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
react-remove-scroll-bar@2.3.4(@types/react@18.2.47)(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
@@ -43,7 +43,7 @@ CREATE TABLE "users"
|
||||
"emailVerified" TIMESTAMP(3),
|
||||
"image" TEXT,
|
||||
"active" INTEGER NOT NULL DEFAULT 1,
|
||||
"team" TEXT,
|
||||
"team" TEXT NOT NULL DEFAULT 'free',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"role" "UserRole" NOT NULL DEFAULT 'USER',
|
||||
|
||||
@@ -56,7 +56,7 @@ model User {
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
active Int @default(1) // 0 封禁,1 正常
|
||||
team String?
|
||||
team String? @default("free")
|
||||
apiKey String?
|
||||
createdAt DateTime @default(now()) @map(name: "created_at")
|
||||
updatedAt DateTime @default(now()) @map(name: "updated_at")
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user