rebrand to useSend (#210)

This commit is contained in:
KM Koushik
2025-09-03 08:21:55 +10:00
committed by GitHub
parent b1a59d2705
commit 07c53d3f58
219 changed files with 1349 additions and 2835 deletions

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { Plus } from "lucide-react";
import { useState } from "react";

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { Edit } from "lucide-react";
import { useState } from "react";
@@ -20,13 +20,13 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { api } from "~/trpc/react";
import { Input } from "@unsend/ui/src/input";
import { toast } from "@unsend/ui/src/toaster";
import Spinner from "@unsend/ui/src/spinner";
import { Input } from "@usesend/ui/src/input";
import { toast } from "@usesend/ui/src/toaster";
import Spinner from "@usesend/ui/src/spinner";
import { SesSetting } from "@prisma/client";
const FormSchema = z.object({

View File

@@ -2,12 +2,13 @@
import AddSesConfiguration from "./add-ses-configuration";
import SesConfigurations from "./ses-configurations";
import { H1 } from "@usesend/ui";
export default function ApiKeysPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-bold text-lg">Admin</h1>
<H1>Admin</H1>
<AddSesConfiguration />
</div>
<div className="mt-10">

View File

@@ -7,12 +7,12 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { formatDistanceToNow } from "date-fns";
import { api } from "~/trpc/react";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
import EditSesConfiguration from "./edit-ses-configuration";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
import { TextWithCopyButton } from "@usesend/ui/src/text-with-copy";
export default function SesConfigurations() {
const sesSettingsQuery = api.admin.getSesSettings.useQuery();

View File

@@ -1,10 +1,10 @@
"use client";
import { api } from "~/trpc/react";
import { Spinner } from "@unsend/ui/src/spinner";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Editor } from "@unsend/email-editor";
import { Spinner } from "@usesend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import { Editor } from "@usesend/email-editor";
import { use, useState } from "react";
import { Campaign } from "@prisma/client";
import {
@@ -12,7 +12,7 @@ import {
SelectContent,
SelectItem,
SelectTrigger,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
import {
Dialog,
DialogContent,
@@ -20,7 +20,7 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -31,8 +31,8 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
import { toast } from "@unsend/ui/src/toaster";
} from "@usesend/ui/src/form";
import { toast } from "@usesend/ui/src/toaster";
import { useDebouncedCallback } from "use-debounce";
import { formatDistanceToNow } from "date-fns";
import {
@@ -40,7 +40,7 @@ import {
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@unsend/ui/src/accordion";
} from "@usesend/ui/src/accordion";
const sendSchema = z.object({
confirmation: z.string(),
@@ -63,7 +63,7 @@ export default function EditCampaignPage({
{ campaignId },
{
enabled: !!campaignId,
}
},
);
if (isLoading) {
@@ -98,7 +98,7 @@ function CampaignEditor({
const utils = api.useUtils();
const [json, setJson] = useState<Record<string, any> | undefined>(
campaign.content ? JSON.parse(campaign.content) : undefined
campaign.content ? JSON.parse(campaign.content) : undefined,
);
const [isSaving, setIsSaving] = useState(false);
const [name, setName] = useState(campaign.name);
@@ -106,10 +106,10 @@ function CampaignEditor({
const [from, setFrom] = useState(campaign.from);
const [contactBookId, setContactBookId] = useState(campaign.contactBookId);
const [replyTo, setReplyTo] = useState<string | undefined>(
campaign.replyTo[0]
campaign.replyTo[0],
);
const [previewText, setPreviewText] = useState<string | null>(
campaign.previewText
campaign.previewText,
);
const [openSendDialog, setOpenSendDialog] = useState(false);
@@ -135,7 +135,7 @@ function CampaignEditor({
const deboucedUpdateCampaign = useDebouncedCallback(
updateEditorContent,
1000
1000,
);
async function onSendCampaign(values: z.infer<typeof sendSchema>) {
@@ -160,14 +160,14 @@ function CampaignEditor({
onError: (error) => {
toast.error(`Failed to send campaign: ${error.message}`);
},
}
},
);
}
const handleFileChange = async (file: File) => {
if (file.size > IMAGE_SIZE_LIMIT) {
throw new Error(
`File should be less than ${IMAGE_SIZE_LIMIT / 1024 / 1024}MB`
`File should be less than ${IMAGE_SIZE_LIMIT / 1024 / 1024}MB`,
);
}
@@ -194,7 +194,7 @@ function CampaignEditor({
const confirmation = sendForm.watch("confirmation");
const contactBook = contactBooksQuery.data?.find(
(book) => book.id === contactBookId
(book) => book.id === contactBookId,
);
return (
@@ -220,7 +220,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setName(campaign.name);
},
}
},
);
}}
/>
@@ -315,7 +315,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setSubject(campaign.subject);
},
}
},
);
}}
className="mt-1 py-1 text-sm block w-full outline-none border-b border-transparent focus:border-border bg-transparent"
@@ -350,7 +350,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setFrom(campaign.from);
},
}
},
);
}}
/>
@@ -381,7 +381,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setReplyTo(campaign.replyTo[0]);
},
}
},
);
}}
/>
@@ -414,7 +414,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setPreviewText(campaign.previewText ?? "");
},
}
},
);
}}
className="mt-1 py-1 text-sm block w-full outline-none border-b border-transparent bg-transparent focus:border-border"
@@ -440,7 +440,7 @@ function CampaignEditor({
onError: () => {
setContactBookId(campaign.contactBookId);
},
}
},
);
setContactBookId(val);
}}

View File

@@ -7,10 +7,11 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@unsend/ui/src/breadcrumb";
} from "@usesend/ui/src/breadcrumb";
import Link from "next/link";
import { H2 } from "@usesend/ui";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
import { api } from "~/trpc/react";
import { use } from "react";
@@ -80,7 +81,7 @@ export default function CampaignDetailsPage({
</BreadcrumbList>
</Breadcrumb>
<div className="mt-10">
<h2 className="text-xl font-semibold mb-4"> Statistics</h2>
<H2 className="mb-4"> Statistics</H2>
<div className="flex gap-4">
{statusCards.map((card) => (
<div
@@ -110,7 +111,7 @@ export default function CampaignDetailsPage({
{campaign.html && (
<div className=" rounded-lg mt-16">
<h2 className="text-xl font-semibold mb-4">Email</h2>
<H2 className="mb-4">Email</H2>
<div className="p-2 rounded-lg border shadow flex flex-col gap-4 w-full">
<div className="flex flex-col gap-3 px-4 py-1">

View File

@@ -7,11 +7,11 @@ import {
TableHead,
TableBody,
TableCell,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { api } from "~/trpc/react";
import { useUrlState } from "~/hooks/useUrlState";
import { Button } from "@unsend/ui/src/button";
import Spinner from "@unsend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import Spinner from "@usesend/ui/src/spinner";
import { formatDistanceToNow } from "date-fns";
import { CampaignStatus } from "@prisma/client";
import DeleteCampaign from "./delete-campaign";
@@ -22,7 +22,7 @@ import {
SelectTrigger,
SelectContent,
SelectItem,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
export default function CampaignList() {
const [page, setPage] = useUrlState("page", "1");

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -16,7 +16,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -24,9 +24,9 @@ import { Plus } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { useRouter } from "next/navigation";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
const campaignSchema = z.object({
name: z.string({ required_error: "Name is required" }).min(1, {

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { Campaign } from "@prisma/client";
const campaignSchema = z.object({

View File

@@ -1,6 +1,6 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
@@ -8,10 +8,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Copy } from "lucide-react";
import { Campaign } from "@prisma/client";

View File

@@ -2,12 +2,13 @@
import CampaignList from "./campaign-list";
import CreateCampaign from "./create-campaign";
import { H1 } from "@usesend/ui";
export default function ContactsPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-bold text-lg">Campaigns</h1>
<H1>Campaigns</H1>
<CreateCampaign />
</div>
<CampaignList />

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Textarea } from "@unsend/ui/src/textarea";
import { Button } from "@usesend/ui/src/button";
import { Textarea } from "@usesend/ui/src/textarea";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -17,7 +17,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -26,7 +26,7 @@ import { useRouter } from "next/navigation";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
const contactsSchema = z.object({
contacts: z.string({ required_error: "Contacts are required" }).min(1, {

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "@unsend/ui/src/select";
import Spinner from "@unsend/ui/src/spinner";
} from "@usesend/ui/src/select";
import Spinner from "@usesend/ui/src/spinner";
import {
Table,
TableBody,
@@ -15,7 +15,7 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { formatDistanceToNow } from "date-fns";
import Image from "next/image";
import { useUrlState } from "~/hooks/useUrlState";
@@ -23,14 +23,14 @@ import { api } from "~/trpc/react";
import { getGravatarUrl } from "~/utils/gravatar-utils";
import DeleteContact from "./delete-contact";
import EditContact from "./edit-contact";
import { Input } from "@unsend/ui/src/input";
import { Input } from "@usesend/ui/src/input";
import { useDebouncedCallback } from "use-debounce";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@unsend/ui/src/tooltip";
} from "@usesend/ui/src/tooltip";
import { UnsubscribeReason } from "@prisma/client";
function getUnsubscribeReason(reason: UnsubscribeReason) {

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { Contact } from "@prisma/client";
const contactSchema = z.object({

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -17,7 +17,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -26,8 +26,8 @@ import { useRouter } from "next/navigation";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { Switch } from "@unsend/ui/src/switch";
import { toast } from "@usesend/ui/src/toaster";
import { Switch } from "@usesend/ui/src/switch";
import { Contact } from "@prisma/client";
const contactSchema = z.object({

View File

@@ -8,20 +8,20 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@unsend/ui/src/breadcrumb";
} from "@usesend/ui/src/breadcrumb";
import Link from "next/link";
import AddContact from "./add-contact";
import ContactList from "./contact-list";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
import { TextWithCopyButton } from "@usesend/ui/src/text-with-copy";
import { formatDistanceToNow } from "date-fns";
import EmojiPicker, { Theme } from "emoji-picker-react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@unsend/ui/src/popover";
import { Button } from "@unsend/ui/src/button";
import { useTheme } from "@unsend/ui";
} from "@usesend/ui/src/popover";
import { Button } from "@usesend/ui/src/button";
import { useTheme } from "@usesend/ui";
import { use } from "react";
export default function ContactsPage({
@@ -51,7 +51,7 @@ export default function ContactsPage({
...old,
...data,
};
}
},
);
},
onSettled: () => {

View File

@@ -1,19 +1,19 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import { useState } from "react";
import { Plus } from "lucide-react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { useUpgradeModalStore } from "~/store/upgradeModalStore";
import { LimitReason } from "~/lib/constants/plans";

View File

@@ -8,7 +8,7 @@ import EditContactBook from "./edit-contact-book";
import { useRouter } from "next/navigation";
import { motion } from "framer-motion";
import { useUrlState } from "~/hooks/useUrlState";
import { Input } from "@unsend/ui/src/input";
import { Input } from "@usesend/ui/src/input";
import { useDebouncedCallback } from "use-debounce";
export default function ContactBooksList() {

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { ContactBook } from "@prisma/client";
const contactBookSchema = z.object({

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -16,14 +16,14 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
import { Edit } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
const contactBookSchema = z.object({
name: z.string().min(1, { message: "Name is required" }),

View File

@@ -2,12 +2,13 @@
import AddContactBook from "./add-contact-book";
import ContactBooksList from "./contact-books-list";
import { H1 } from "@usesend/ui";
export default function ContactsPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-semibold text-xl">Contact books</h1>
<H1>Contact books</H1>
<AddContactBook />
</div>
<ContactBooksList />

View File

@@ -1,9 +1,9 @@
"use client";
import { AppSidebar } from "~/components/AppSideBar";
import { SidebarInset, SidebarTrigger } from "@unsend/ui/src/sidebar";
import { SidebarProvider } from "@unsend/ui/src/sidebar";
import { useIsMobile } from "@unsend/ui/src/hooks/use-mobile";
import { SidebarInset, SidebarTrigger } from "@usesend/ui/src/sidebar";
import { SidebarProvider } from "@usesend/ui/src/sidebar";
import { useIsMobile } from "@usesend/ui/src/hooks/use-mobile";
import { UpgradeModal } from "~/components/payments/UpgradeModal";
export function DashboardLayout({ children }: { children: React.ReactNode }) {

View File

@@ -1,12 +1,12 @@
import React from "react";
import { Tabs, TabsList, TabsTrigger } from "@unsend/ui/src/tabs";
import { Tabs, TabsList, TabsTrigger } from "@usesend/ui/src/tabs";
import { useUrlState } from "~/hooks/useUrlState";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
import { api } from "~/trpc/react";
interface DashboardFiltersProps {

View File

@@ -13,8 +13,8 @@ import {
import { EmailStatusIcon } from "../emails/email-status-badge";
import { EmailStatus } from "@prisma/client";
import { api } from "~/trpc/react";
import Spinner from "@unsend/ui/src/spinner";
import { useTheme } from "@unsend/ui";
import Spinner from "@usesend/ui/src/spinner";
import { useTheme } from "@usesend/ui";
import { useColors } from "./hooks/useColors";
interface EmailChartProps {

View File

@@ -1,4 +1,4 @@
import { useTheme } from "@unsend/ui";
import { useTheme } from "@usesend/ui";
export function useColors() {
const { resolvedTheme } = useTheme();

View File

@@ -2,6 +2,7 @@
import EmailChart from "./email-chart";
import DashboardFilters from "./dashboard-filters";
import { H1 } from "@usesend/ui";
import { useUrlState } from "~/hooks/useUrlState";
import { ReputationMetrics } from "./reputation-metrics";
@@ -13,7 +14,7 @@ export default function Dashboard() {
<div>
<div className="w-full">
<div className="flex justify-between items-center mb-10">
<h1 className="font-semibold text-xl">Analytics</h1>
<H1>Analytics</H1>
<DashboardFilters
days={days ?? "7"}
setDays={setDays}

View File

@@ -3,7 +3,7 @@ import {
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@unsend/ui/src/tooltip";
} from "@usesend/ui/src/tooltip";
import {
CheckCircle2,
CheckCircle2Icon,

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,12 +9,12 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import { useState } from "react";
import { CheckIcon, ClipboardCopy, Eye, EyeOff, Plus } from "lucide-react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -26,7 +26,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
const apiKeySchema = z.object({
name: z.string({ required_error: "Name is required" }).min(1, {

View File

@@ -7,11 +7,11 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { formatDistanceToNow } from "date-fns";
import { api } from "~/trpc/react";
import DeleteApiKey from "./delete-api-key";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
export default function ApiList() {
const apiKeysQuery = api.apiKey.getApiKeys.useQuery();

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,11 +9,11 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { ApiKey } from "@prisma/client";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -26,7 +26,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
const apiKeySchema = z.object({
name: z.string(),

View File

@@ -2,12 +2,13 @@
import AddApiKey from "./add-api-key";
import ApiList from "./api-list";
import { H1 } from "@usesend/ui";
export default function ApiKeysPage() {
return (
<div>
<div className="flex justify-between items-center">
<h2 className="font-medium">API Keys</h2>
<H1>API Keys</H1>
<AddApiKey />
</div>
<ApiList />

View File

@@ -2,12 +2,13 @@
import AddApiKey from "./api-keys/add-api-key";
import ApiList from "./api-keys/api-list";
import { H1 } from "@usesend/ui";
export default function ApiKeysPage() {
return (
<div>
<div className="flex justify-between items-center">
<h2 className="font-medium">API Keys</h2>
<H1>API Keys</H1>
<AddApiKey />
</div>
<ApiList />

View File

@@ -5,8 +5,8 @@ import {
CardDescription,
CardHeader,
CardTitle,
} from "@unsend/ui/src/card";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
} from "@usesend/ui/src/card";
import { TextWithCopyButton } from "@usesend/ui/src/text-with-copy";
import { env } from "~/env";
export const dynamic = "force-dynamic";

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,7 +9,7 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
@@ -19,13 +19,13 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { Domain } from "@prisma/client";
import { useRouter } from "next/navigation";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -66,7 +66,7 @@ export const DeleteDomain: React.FC<{ domain: Domain }> = ({ domain }) => {
toast.success(`Domain ${domain.name} deleted`);
router.replace("/domains");
},
}
},
);
}

View File

@@ -9,7 +9,7 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@unsend/ui/src/breadcrumb";
} from "@usesend/ui/src/breadcrumb";
import { DomainStatusBadge } from "../domain-badge";
import {
Table,
@@ -18,15 +18,16 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
} from "@usesend/ui/src/table";
import { TextWithCopyButton } from "@usesend/ui/src/text-with-copy";
import React, { use } from "react";
import { Switch } from "@unsend/ui/src/switch";
import { Switch } from "@usesend/ui/src/switch";
import DeleteDomain from "./delete-domain";
import SendTestMail from "./send-test-mail";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import Link from "next/link";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { H1 } from "@usesend/ui";
export default function DomainItemPage({
params,
@@ -42,7 +43,7 @@ export default function DomainItemPage({
{
refetchInterval: (q) => (q?.state.data?.isVerifying ? 10000 : false),
refetchIntervalInBackground: true,
}
},
);
const verifyQuery = api.domain.startVerification.useMutation();
@@ -54,7 +55,7 @@ export default function DomainItemPage({
onSettled: () => {
domainQuery.refetch();
},
}
},
);
};
@@ -67,7 +68,7 @@ export default function DomainItemPage({
<div className="flex justify-between items-center">
<div className="flex items-center gap-4">
{/* <div className="flex items-center gap-4">
<h1 className="font-medium text-2xl">{domainQuery.data?.name}</h1>
<H1>{domainQuery.data?.name}</H1>
</div> */}
<Breadcrumb>
<BreadcrumbList>
@@ -151,7 +152,7 @@ export default function DomainItemPage({
<TableCell className="">TXT</TableCell>
<TableCell>
<TextWithCopyButton
value={`unsend._domainkey.${domainQuery.data?.subdomain || domainQuery.data?.name}`}
value={`${domainQuery.data?.dkimSelector ?? "unsend"}._domainkey.${domainQuery.data?.subdomain || domainQuery.data?.name}`}
/>
</TableCell>
<TableCell className="">
@@ -232,7 +233,7 @@ const DomainSettings: React.FC<{ domain: Domain }> = ({ domain }) => {
const utils = api.useUtils();
const [clickTracking, setClickTracking] = React.useState(
domain.clickTracking
domain.clickTracking,
);
const [openTracking, setOpenTracking] = React.useState(domain.openTracking);
@@ -245,7 +246,7 @@ const DomainSettings: React.FC<{ domain: Domain }> = ({ domain }) => {
utils.domain.invalidate();
toast.success("Click tracking updated");
},
}
},
);
}
@@ -258,7 +259,7 @@ const DomainSettings: React.FC<{ domain: Domain }> = ({ domain }) => {
utils.domain.invalidate();
toast.success("Open tracking updated");
},
}
},
);
}
return (

View File

@@ -1,121 +1,17 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
import { Button } from "@usesend/ui/src/button";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import React from "react";
import { Domain } from "@prisma/client";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { SendHorizonal } from "lucide-react";
import { Code } from "@unsend/ui/src/code";
import { useSession } from "next-auth/react";
import { getSendTestEmailCode } from "~/lib/constants/example-codes";
const jsCode = `const requestOptions = {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer us_ad9a79256e366399c747cbf0b38eca3c472e8a2e"
},
body: JSON.stringify({
"to": "koushikmohan1996@gmail.com",
"from": "hello@test.splitpro.app",
"subject": "Test mail",
"html": "<p>Hello this is a test mail</p>"
}),
redirect: "follow"
};
fetch("http://localhost:3000/api/v1/emails", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.error(error));
`;
const pythonCode = `import requests
import json
url = "http://localhost:3000/api/v1/emails"
payload = json.dumps({
"to": "koushikmohan1996@gmail.com",
"from": "hello@test.splitpro.app",
"subject": "Test mail",
"html": "<p>Hello this is a test mail</p>"
})
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer us_ad9a79256e366399c747cbf0b38eca3c472e8a2e'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)`;
const rubyCode = `require 'uri'
require 'net/http'
require 'json'
url = URI("http://localhost:3000/api/v1/emails")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Accept"] = 'application/json'
request["Content-Type"] = 'application/json'
request["Authorization"] = 'Bearer us_ad9a79256e366399c747cbf0b38eca3c472e8a2e'
request.body = JSON.dump({
"to" => "koushikmohan1996@gmail.com",
"from" => "hello@test.splitpro.app",
"subject" => "Test mail",
"html" => "<p>Hello this is a test mail</p>"
})
response = http.request(request)
puts response.read_body`;
const phpCode = `$url = "http://localhost:3000/api/v1/emails";
$payload = json_encode(array(
"to" => "koushikmohan1996@gmail.com",
"from" => "hello@test.splitpro.app",
"subject" => "Test mail",
"html" => "<p>Hello this is a test mail</p>"
));
$headers = array(
"Accept: application/json",
"Content-Type: application/json",
"Authorization: Bearer us_ad9a79256e366399c747cbf0b38eca3c472e8a2e"
);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
} else {
echo $response;
}`;
// Removed dialog and example code. Clicking the button now sends the email directly.
export const SendTestMail: React.FC<{ domain: Domain }> = ({ domain }) => {
const [open, setOpen] = useState(false);
const sendTestEmailFromDomainMutation =
api.domain.sendTestEmailFromDomain.useMutation();
const { data: session } = useSession();
const utils = api.useUtils();
function handleSendTestEmail() {
@@ -127,50 +23,24 @@ export const SendTestMail: React.FC<{ domain: Domain }> = ({ domain }) => {
onSuccess: () => {
utils.domain.domains.invalidate();
toast.success(`Test email sent`);
setOpen(false);
},
}
onError: (err) => {
toast.error(err.message || "Failed to send test email");
},
},
);
}
return (
<Dialog
open={open}
onOpenChange={(_open) => (_open !== open ? setOpen(_open) : null)}
<Button
onClick={handleSendTestEmail}
disabled={sendTestEmailFromDomainMutation.isPending}
>
<DialogTrigger asChild>
<Button>
<SendHorizonal className="h-4 w-4 mr-2" />
Send test email
</Button>
</DialogTrigger>
<DialogContent className=" max-w-2xl">
<DialogHeader>
<DialogTitle>Send test email</DialogTitle>
</DialogHeader>
<Code
codeBlocks={getSendTestEmailCode({
from: `hello@${domain.name}`,
to: session?.user?.email || "",
subject: "Unsend test email",
body: "hello,\\n\\nUnsend is the best open source sending platform",
bodyHtml:
"<p>hello,</p><p>Unsend is the best open source sending platform<p><p>check out <a href='https://unsend.dev'>unsend.dev</a>",
})}
codeClassName="max-w-[38rem] h-[20rem]"
/>
<div className="flex justify-end w-full">
<Button
onClick={handleSendTestEmail}
disabled={sendTestEmailFromDomainMutation.isPending}
>
{sendTestEmailFromDomainMutation.isPending
? "Sending email..."
: "Send test email"}
</Button>
</div>
</DialogContent>
</Dialog>
<SendHorizonal className="h-4 w-4 mr-2" />
{sendTestEmailFromDomainMutation.isPending
? "Sending email..."
: "Send test email"}
</Button>
);
};

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -17,10 +17,10 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
import { useEffect, useState } from "react";
import { Plus } from "lucide-react";
import { useRouter } from "next/navigation";
import * as tldts from "tldts";
@@ -33,15 +33,13 @@ import {
SelectItem,
SelectTrigger,
SelectValue,
} from "@unsend/ui/src/select";
import { toast } from "@unsend/ui/src/toaster";
} from "@usesend/ui/src/select";
import { toast } from "@usesend/ui/src/toaster";
import { useUpgradeModalStore } from "~/store/upgradeModalStore";
import { LimitReason } from "~/lib/constants/plans";
const domainSchema = z.object({
region: z.string({ required_error: "Region is required" }).min(1, {
message: "Region is required",
}),
region: z.string().optional(),
domain: z.string({ required_error: "Domain is required" }).min(1, {
message: "Domain is required",
}),
@@ -68,6 +66,11 @@ export default function AddDomain() {
const utils = api.useUtils();
const router = useRouter();
const singleRegion =
regionQuery.data?.length === 1 ? regionQuery.data[0] : undefined;
const showRegionSelect = (regionQuery.data?.length ?? 0) > 1;
async function onDomainAdd(values: z.infer<typeof domainSchema>) {
const domain = tldts.getDomain(values.domain);
if (!domain) {
@@ -78,6 +81,13 @@ export default function AddDomain() {
return;
}
if (!values.region && !singleRegion) {
domainForm.setError("region", {
message: "Region is required",
});
return;
}
if (limitsQuery.data?.isLimitReached) {
openModal(limitsQuery.data.reason);
return;
@@ -86,7 +96,7 @@ export default function AddDomain() {
addDomainMutation.mutate(
{
name: values.domain,
region: values.region,
region: singleRegion ?? values.region ?? "",
},
{
onSuccess: async (data) => {
@@ -151,40 +161,43 @@ export default function AddDomain() {
</FormItem>
)}
/>
<FormField
control={domainForm.control}
name="region"
render={({ field, formState }) => (
<FormItem>
<FormLabel>Region</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
disabled={regionQuery.isLoading}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select region" />
</SelectTrigger>
</FormControl>
<SelectContent>
{regionQuery.data?.map((region) => (
<SelectItem value={region} key={region}>
{region}
</SelectItem>
))}
</SelectContent>
</Select>
{formState.errors.region ? (
<FormMessage />
) : (
<FormDescription>
Select the region from where the email is sent{" "}
</FormDescription>
)}
</FormItem>
)}
/>
{showRegionSelect && (
<FormField
control={domainForm.control}
name="region"
render={({ field, formState }) => (
<FormItem>
<FormLabel>Region</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
disabled={regionQuery.isLoading}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select region" />
</SelectTrigger>
</FormControl>
<SelectContent>
{regionQuery.data?.map((region) => (
<SelectItem value={region} key={region}>
{region}
</SelectItem>
))}
</SelectContent>
</Select>
{formState.errors.region ? (
<FormMessage />
) : (
<FormDescription>
Select the region from where the email is sent{" "}
</FormDescription>
)}
</FormItem>
)}
/>
)}
<div className="flex justify-end">
<Button

View File

@@ -3,12 +3,12 @@
import { Domain } from "@prisma/client";
import { formatDistanceToNow } from "date-fns";
import Link from "next/link";
import { Switch } from "@unsend/ui/src/switch";
import { Switch } from "@usesend/ui/src/switch";
import { api } from "~/trpc/react";
import React from "react";
import { StatusIndicator } from "./status-indicator";
import { DomainStatusBadge } from "./domain-badge";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
export default function DomainsList() {
const domainsQuery = api.domain.domains.useQuery();

View File

@@ -2,12 +2,13 @@
import DomainsList from "./domain-list";
import AddDomain from "./add-domain";
import { H1 } from "@usesend/ui";
export default function DomainsPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-bold text-lg">Domains</h1>
<H1>Domains</H1>
<AddDomain />
</div>
<DomainsList />

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
const cancelSchema = z.object({
confirmation: z.string(),

View File

@@ -1,27 +1,27 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import * as chrono from "chrono-node";
import { api } from "~/trpc/react";
import { useRef, useState } from "react";
import { Edit3 } from "lucide-react";
import { useRouter } from "next/navigation";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSubContent,
DropdownMenuTrigger,
} from "@unsend/ui/src/dropdown-menu";
} from "@usesend/ui/src/dropdown-menu";
import {
Command,
CommandDialog,
@@ -31,7 +31,7 @@ import {
CommandItem,
CommandList,
CommandSeparator,
} from "@unsend/ui/src/command";
} from "@usesend/ui/src/command";
export const EditSchedule: React.FC<{
emailId: string;

View File

@@ -2,7 +2,7 @@
import { UAParser } from "ua-parser-js";
import { api } from "~/trpc/react";
import { Separator } from "@unsend/ui/src/separator";
import { Separator } from "@usesend/ui/src/separator";
import { EmailStatusBadge, EmailStatusIcon } from "./email-status-badge";
import { formatDate } from "date-fns";
import { motion } from "framer-motion";

View File

@@ -7,7 +7,7 @@ import {
TableHead,
TableBody,
TableCell,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { api } from "~/trpc/react";
import {
Mail,
@@ -23,34 +23,34 @@ import { EmailStatusBadge } from "./email-status-badge";
import EmailDetails from "./email-details";
import dynamic from "next/dynamic";
import { useUrlState } from "~/hooks/useUrlState";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "@unsend/ui/src/select";
import Spinner from "@unsend/ui/src/spinner";
} from "@usesend/ui/src/select";
import Spinner from "@usesend/ui/src/spinner";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@unsend/ui/src/tooltip";
import { Input } from "@unsend/ui/src/input";
} from "@usesend/ui/src/tooltip";
import { Input } from "@usesend/ui/src/input";
import { DEFAULT_QUERY_LIMIT } from "~/lib/constants";
import { useDebouncedCallback } from "use-debounce";
import { useState } from "react";
import { SheetTitle, SheetDescription } from "@unsend/ui/src/sheet";
import { SheetTitle, SheetDescription } from "@usesend/ui/src/sheet";
/* Stupid hydrating error. And I so stupid to understand the stupid NextJS docs */
const DynamicSheetWithNoSSR = dynamic(
() => import("@unsend/ui/src/sheet").then((mod) => mod.Sheet),
() => import("@usesend/ui/src/sheet").then((mod) => mod.Sheet),
{ ssr: false },
);
const DynamicSheetContentWithNoSSR = dynamic(
() => import("@unsend/ui/src/sheet").then((mod) => mod.SheetContent),
() => import("@usesend/ui/src/sheet").then((mod) => mod.SheetContent),
{ ssr: false },
);

View File

@@ -1,12 +1,13 @@
"use client";
import EmailList from "./email-list";
import { H1 } from "@usesend/ui";
export default function EmailsPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-semibold text-xl">Emails</h1>
<H1>Emails</H1>
</div>
<EmailList />
</div>

View File

@@ -1,11 +1,12 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import Spinner from "@unsend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import Spinner from "@usesend/ui/src/spinner";
import { CheckCircle2 } from "lucide-react";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { api } from "~/trpc/react";
import { H1 } from "@usesend/ui";
export default function PaymentsPage() {
const searchParams = useSearchParams();
@@ -15,9 +16,7 @@ export default function PaymentsPage() {
return (
<div className="container mx-auto py-10">
<h1 className="text-2xl font-semibold mb-8">
Payment {success ? "Success" : canceled ? "Canceled" : "Unknown"}
</h1>
<H1>Payment {success ? "Success" : canceled ? "Canceled" : "Unknown"}</H1>
{canceled ? (
<Link href="/settings/billing">
<Button>Go to billing</Button>

View File

@@ -1,9 +1,9 @@
"use client";
import { useState } from "react";
import { Button } from "@unsend/ui/src/button";
import { Card } from "@unsend/ui/src/card";
import { Spinner } from "@unsend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import { Card } from "@usesend/ui/src/card";
import { Spinner } from "@usesend/ui/src/spinner";
import { format } from "date-fns";
import { useTeam } from "~/providers/team-context";
import { api } from "~/trpc/react";

View File

@@ -1,6 +1,6 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
@@ -8,10 +8,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
export const DeleteTeamInvite: React.FC<{

View File

@@ -1,6 +1,6 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
@@ -8,10 +8,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Role } from "@prisma/client";
import { LogOut, Trash2 } from "lucide-react";

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -15,7 +15,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -23,7 +23,7 @@ import { PencilIcon } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Role } from "@prisma/client";
import {
Select,
@@ -31,7 +31,7 @@ import {
SelectItem,
SelectTrigger,
SelectValue,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
const teamUserSchema = z.object({
role: z.enum(["MEMBER", "ADMIN"]),

View File

@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import { PlusIcon } from "lucide-react";
import {
Dialog,
@@ -9,18 +9,18 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@unsend/ui/src/select";
import { Input } from "@unsend/ui/src/input";
} from "@usesend/ui/src/select";
import { Input } from "@usesend/ui/src/input";
import { useForm } from "react-hook-form";
import { api } from "~/trpc/react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import {
@@ -31,7 +31,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { useTeam } from "~/providers/team-context";
import { isCloud, isSelfHosted } from "~/utils/common";
import { useUpgradeModalStore } from "~/store/upgradeModalStore";

View File

@@ -1,15 +1,15 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import { api } from "~/trpc/react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Copy, RotateCw } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@unsend/ui/src/tooltip";
} from "@usesend/ui/src/tooltip";
import { isSelfHosted } from "~/utils/common";
export const ResendTeamInvite: React.FC<{

View File

@@ -7,10 +7,10 @@ import {
TableHead,
TableBody,
TableCell,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { api } from "~/trpc/react";
import { Button } from "@unsend/ui/src/button";
import Spinner from "@unsend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import Spinner from "@usesend/ui/src/spinner";
import { formatDistanceToNow } from "date-fns";
import { Role } from "@prisma/client";
import { EditTeamMember } from "./edit-team-member";

View File

@@ -1,8 +1,8 @@
"use client";
import { api } from "~/trpc/react";
import { Card } from "@unsend/ui/src/card";
import Spinner from "@unsend/ui/src/spinner";
import { Card } from "@usesend/ui/src/card";
import Spinner from "@usesend/ui/src/spinner";
import { format } from "date-fns";
import {
getCost,
@@ -14,7 +14,7 @@ import { useTeam } from "~/providers/team-context";
import { EmailUsageType } from "@prisma/client";
import { PlanDetails } from "~/components/payments/PlanDetails";
import { UpgradeButton } from "~/components/payments/UpgradeButton";
import { Progress } from "@unsend/ui/src/progress";
import { Progress } from "@usesend/ui/src/progress";
const FREE_PLAN_LIMIT = 3000;
@@ -49,7 +49,7 @@ function FreePlanUsage({
<div className="text-sm text-muted-foreground mt-1">
{item.type === "TRANSACTIONAL"
? "Mails sent using the send api or SMTP"
: "Mails designed sent from unsend editor"}
: "Mails designed sent from useSend editor"}
</div>
</div>
<div className="font-mono font-medium">

View File

@@ -10,17 +10,17 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@unsend/ui/src/dialog";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Label } from "@unsend/ui/src/label";
} from "@usesend/ui/src/dialog";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import { Label } from "@usesend/ui/src/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
interface AddSuppressionDialogProps {
open: boolean;

View File

@@ -10,18 +10,18 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@unsend/ui/src/dialog";
import { Button } from "@unsend/ui/src/button";
import { Label } from "@unsend/ui/src/label";
import { Textarea } from "@unsend/ui/src/textarea";
} from "@usesend/ui/src/dialog";
import { Button } from "@usesend/ui/src/button";
import { Label } from "@usesend/ui/src/label";
import { Textarea } from "@usesend/ui/src/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@unsend/ui/src/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@unsend/ui/src/tabs";
} from "@usesend/ui/src/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@usesend/ui/src/tabs";
import { Upload, FileText } from "lucide-react";
interface BulkAddSuppressionsDialogProps {

View File

@@ -5,8 +5,9 @@ import AddSuppressionDialog from "./add-suppression";
import BulkAddSuppressionsDialog from "./bulk-add-suppressions";
import SuppressionList from "./suppression-list";
import SuppressionStats from "./suppression-stats";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import { Plus, Upload } from "lucide-react";
import { H1 } from "@usesend/ui";
export default function SuppressionsPage() {
const [showAddDialog, setShowAddDialog] = useState(false);
@@ -16,7 +17,7 @@ export default function SuppressionsPage() {
<div>
{/* Header */}
<div className="flex justify-between items-center mb-10">
<h1 className="font-bold text-lg">Suppression List</h1>
<H1>Suppression List</H1>
<div className="flex gap-2">
<Button variant="outline" onClick={() => setShowBulkAddDialog(true)}>
<Upload className="h-4 w-4 mr-2" />

View File

@@ -7,8 +7,8 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from "@unsend/ui/src/dialog";
import { Button } from "@unsend/ui/src/button";
} from "@usesend/ui/src/dialog";
import { Button } from "@usesend/ui/src/button";
interface RemoveSuppressionDialogProps {
email: string | null;

View File

@@ -6,15 +6,15 @@ import { useUrlState } from "~/hooks/useUrlState";
import { useDebouncedCallback } from "use-debounce";
import { SuppressionReason } from "@prisma/client";
import { formatDistanceToNow } from "date-fns";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
import {
Table,
TableBody,
@@ -22,10 +22,10 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { Trash2, Download } from "lucide-react";
import RemoveSuppressionDialog from "./remove-suppression";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
const reasonLabels = {
HARD_BOUNCE: "Hard Bounce",

View File

@@ -1,12 +1,12 @@
"use client";
import { api } from "~/trpc/react";
import { Spinner } from "@unsend/ui/src/spinner";
import { Input } from "@unsend/ui/src/input";
import { Editor } from "@unsend/email-editor";
import { Spinner } from "@usesend/ui/src/spinner";
import { Input } from "@usesend/ui/src/input";
import { Editor } from "@usesend/email-editor";
import { useState } from "react";
import { Template } from "@prisma/client";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { useDebouncedCallback } from "use-debounce";
import { formatDistanceToNow } from "date-fns";
import { ArrowLeft } from "lucide-react";

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -16,7 +16,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -24,9 +24,9 @@ import { Plus } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { useRouter } from "next/navigation";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
const templateSchema = z.object({
name: z.string({ required_error: "Name is required" }).min(1, {

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { Template } from "@prisma/client";
const templateSchema = z.object({

View File

@@ -1,6 +1,6 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
@@ -8,10 +8,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Copy } from "lucide-react";
import { Template } from "@prisma/client";

View File

@@ -2,12 +2,13 @@
import TemplateList from "./template-list";
import CreateTemplate from "./create-template";
import { H1 } from "@usesend/ui";
export default function TemplatesPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-bold text-lg">Templates</h1>
<H1>Templates</H1>
<CreateTemplate />
</div>
<TemplateList />

View File

@@ -7,17 +7,17 @@ import {
TableHead,
TableBody,
TableCell,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { api } from "~/trpc/react";
import { useUrlState } from "~/hooks/useUrlState";
import { Button } from "@unsend/ui/src/button";
import Spinner from "@unsend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import Spinner from "@usesend/ui/src/spinner";
import { formatDistanceToNow } from "date-fns";
// import DeleteCampaign from "./delete-campaign";
import Link from "next/link";
// import DuplicateCampaign from "./duplicate-campaign";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
import { TextWithCopyButton } from "@usesend/ui/src/text-with-copy";
import DeleteTemplate from "./delete-template";
import DuplicateTemplate from "./duplicate-template";