rebrand to useSend (#210)
@@ -1,7 +1,7 @@
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["@unsend/eslint-config/next.js"],
|
||||
extends: ["@usesend/eslint-config/next.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: true,
|
||||
|
@@ -34,8 +34,8 @@
|
||||
"@trpc/next": "^11.1.1",
|
||||
"@trpc/react-query": "^11.1.1",
|
||||
"@trpc/server": "^11.1.1",
|
||||
"@unsend/email-editor": "workspace:*",
|
||||
"@unsend/ui": "workspace:*",
|
||||
"@usesend/email-editor": "workspace:*",
|
||||
"@usesend/ui": "workspace:*",
|
||||
"bullmq": "^5.51.1",
|
||||
"chrono-node": "^2.8.0",
|
||||
"date-fns": "^4.1.0",
|
||||
@@ -65,7 +65,7 @@
|
||||
"superjson": "^2.2.2",
|
||||
"tldts": "^7.0.4",
|
||||
"ua-parser-js": "^2.0.3",
|
||||
"unsend": "workspace:*",
|
||||
"usesend": "workspace:*",
|
||||
"use-debounce": "^10.0.4",
|
||||
"zod": "^3.24.3",
|
||||
"zustand": "^5.0.8"
|
||||
@@ -82,10 +82,10 @@
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.0",
|
||||
"@typescript-eslint/parser": "^8.31.0",
|
||||
"@unsend/eslint-config": "workspace:*",
|
||||
"@unsend/tailwind-config": "workspace:*",
|
||||
"@unsend/typescript-config": "workspace:*",
|
||||
"eslint": "^9.25.1",
|
||||
"@usesend/eslint-config": "workspace:*",
|
||||
"@usesend/tailwind-config": "workspace:*",
|
||||
"@usesend/typescript-config": "workspace:*",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-next": "^15.3.1",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
@@ -100,4 +100,4 @@
|
||||
"initVersion": "7.30.0"
|
||||
},
|
||||
"packageManager": "pnpm@8.9.2"
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
-- Add dkimSelector column (nullable) with default 'usesend'
|
||||
ALTER TABLE "Domain" ADD COLUMN "dkimSelector" TEXT;
|
||||
|
||||
-- Set existing rows to 'unsend' to preserve current selector
|
||||
UPDATE "Domain" SET "dkimSelector" = 'unsend' WHERE "dkimSelector" IS NULL;
|
||||
|
||||
-- Set default for new rows to 'usesend'
|
||||
ALTER TABLE "Domain" ALTER COLUMN "dkimSelector" SET DEFAULT 'usesend';
|
||||
|
@@ -181,6 +181,7 @@ model Domain {
|
||||
clickTracking Boolean @default(false)
|
||||
openTracking Boolean @default(false)
|
||||
publicKey String
|
||||
dkimSelector String? @default("usesend")
|
||||
dkimStatus String?
|
||||
spfDetails String?
|
||||
dmarcAdded Boolean @default(false)
|
||||
|
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 901 B After Width: | Height: | Size: 475 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 23 KiB |
@@ -1,14 +0,0 @@
|
||||
<svg width="650" height="650" viewBox="0 0 650 650" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_43_2" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="650" height="650">
|
||||
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_43_2)">
|
||||
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="white"/>
|
||||
<path d="M431.817 203.253C435.698 188.755 437.639 181.506 441.477 181.506C445.314 181.506 447.255 188.755 451.136 203.253L481.757 317.625C488.908 344.332 492.483 357.686 488.449 359.37C484.415 361.055 477.433 349.124 463.467 325.262L450.107 302.435C446.265 295.87 444.344 292.588 441.477 292.588C438.61 292.588 436.688 295.87 432.846 302.435L419.486 325.262C405.521 349.124 398.538 361.055 394.504 359.37C390.47 357.686 394.046 344.332 401.196 317.625L431.817 203.253Z" fill="black"/>
|
||||
<path d="M467.077 364.883C467.077 385.571 463.002 406.057 455.085 425.17C447.168 444.284 435.564 461.651 420.935 476.28C406.306 490.908 388.939 502.513 369.826 510.43C350.712 518.347 330.227 522.422 309.538 522.422C288.85 522.422 268.365 518.347 249.251 510.43C230.138 502.513 212.771 490.908 198.142 476.28C183.513 461.651 171.909 444.284 163.992 425.17C156.075 406.057 152 385.571 152 364.883L202.528 364.883C202.528 378.936 205.296 392.851 210.674 405.834C216.052 418.817 223.934 430.614 233.871 440.551C243.808 450.488 255.604 458.37 268.587 463.748C281.571 469.125 295.486 471.893 309.538 471.893C323.591 471.893 337.506 469.125 350.489 463.748C363.473 458.37 375.269 450.488 385.206 440.551C395.143 430.614 403.025 418.817 408.403 405.834C413.781 392.851 416.549 378.936 416.549 364.883H467.077Z" fill="black"/>
|
||||
<path d="M152 156.145C152 142.224 163.285 130.938 177.206 130.938V130.938C191.127 130.938 202.412 142.224 202.412 156.145V366.458C202.412 380.379 191.127 391.665 177.206 391.665V391.665C163.285 391.665 152 380.379 152 366.458V156.145Z" fill="black"/>
|
||||
<path d="M416.665 152.206C416.665 138.285 427.95 127 441.871 127V127C455.792 127 467.077 138.285 467.077 152.206V362.52C467.077 376.441 455.792 387.726 441.871 387.726V387.726C427.95 387.726 416.665 376.441 416.665 362.52V152.206Z" fill="black"/>
|
||||
<path d="M419.676 139.67C421.558 133.877 427.78 130.708 433.572 132.59V132.59C439.364 134.472 442.534 140.693 440.652 146.485L366.899 373.475C365.017 379.267 358.795 382.437 353.003 380.555V380.555C347.211 378.673 344.041 372.452 345.923 366.659L419.676 139.67Z" fill="black"/>
|
||||
<path d="M442.916 147.452C441.034 141.66 444.203 135.439 449.996 133.556V133.556C455.788 131.674 462.009 134.844 463.891 140.637L537.645 367.626C539.527 373.418 536.357 379.64 530.565 381.522V381.522C524.772 383.404 518.551 380.234 516.669 374.442L442.916 147.452Z" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 23 KiB |
@@ -1,14 +0,0 @@
|
||||
<svg width="650" height="650" viewBox="0 0 650 650" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_43_13" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="650" height="650">
|
||||
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_43_13)">
|
||||
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="black"/>
|
||||
<path d="M431.817 203.253C435.698 188.755 437.639 181.506 441.477 181.506C445.314 181.506 447.255 188.755 451.136 203.253L481.757 317.625C488.908 344.332 492.483 357.686 488.449 359.37C484.415 361.055 477.433 349.124 463.467 325.262L450.107 302.435C446.265 295.87 444.344 292.588 441.477 292.588C438.61 292.588 436.688 295.87 432.846 302.435L419.486 325.262C405.521 349.124 398.538 361.055 394.504 359.37C390.47 357.686 394.046 344.332 401.196 317.625L431.817 203.253Z" fill="white"/>
|
||||
<path d="M467.077 364.883C467.077 385.571 463.002 406.057 455.085 425.17C447.168 444.284 435.564 461.651 420.935 476.28C406.306 490.908 388.939 502.513 369.826 510.43C350.712 518.347 330.227 522.422 309.538 522.422C288.85 522.422 268.365 518.347 249.251 510.43C230.138 502.513 212.771 490.908 198.142 476.28C183.513 461.651 171.909 444.284 163.992 425.17C156.075 406.057 152 385.571 152 364.883L202.528 364.883C202.528 378.936 205.296 392.851 210.674 405.834C216.052 418.817 223.934 430.614 233.871 440.551C243.808 450.488 255.604 458.37 268.587 463.748C281.571 469.125 295.486 471.893 309.538 471.893C323.591 471.893 337.506 469.125 350.489 463.748C363.473 458.37 375.269 450.488 385.206 440.551C395.143 430.614 403.025 418.817 408.403 405.834C413.781 392.851 416.549 378.936 416.549 364.883H467.077Z" fill="white"/>
|
||||
<path d="M152 156.145C152 142.224 163.285 130.938 177.206 130.938V130.938C191.127 130.938 202.412 142.224 202.412 156.145V366.458C202.412 380.379 191.127 391.665 177.206 391.665V391.665C163.285 391.665 152 380.379 152 366.458V156.145Z" fill="white"/>
|
||||
<path d="M416.665 152.206C416.665 138.285 427.95 127 441.871 127V127C455.792 127 467.077 138.285 467.077 152.206V362.52C467.077 376.441 455.792 387.726 441.871 387.726V387.726C427.95 387.726 416.665 376.441 416.665 362.52V152.206Z" fill="white"/>
|
||||
<path d="M419.676 139.67C421.558 133.877 427.78 130.708 433.572 132.59V132.59C439.364 134.472 442.534 140.693 440.652 146.485L366.899 373.475C365.017 379.267 358.795 382.437 353.003 380.555V380.555C347.211 378.673 344.041 372.452 345.923 366.659L419.676 139.67Z" fill="white"/>
|
||||
<path d="M442.916 147.452C441.034 141.66 444.203 135.439 449.996 133.556V133.556C455.788 131.674 462.009 134.844 463.891 140.637L537.645 367.626C539.527 373.418 536.357 379.64 530.565 381.522V381.522C524.772 383.404 518.551 380.234 516.669 374.442L442.916 147.452Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.2 KiB |
BIN
apps/web/public/logo-squircle.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
@@ -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";
|
||||
|
@@ -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({
|
||||
|
@@ -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">
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
}}
|
||||
|
@@ -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">
|
||||
|
@@ -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");
|
||||
|
@@ -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, {
|
||||
|
@@ -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({
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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 />
|
||||
|
@@ -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, {
|
||||
|
@@ -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) {
|
||||
|
@@ -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({
|
||||
|
@@ -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({
|
||||
|
@@ -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: () => {
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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() {
|
||||
|
@@ -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({
|
||||
|
@@ -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" }),
|
||||
|
@@ -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 />
|
||||
|
@@ -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 }) {
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useTheme } from "@unsend/ui";
|
||||
import { useTheme } from "@usesend/ui";
|
||||
|
||||
export function useColors() {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
@@ -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}
|
||||
|
@@ -3,7 +3,7 @@ import {
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@unsend/ui/src/tooltip";
|
||||
} from "@usesend/ui/src/tooltip";
|
||||
import {
|
||||
CheckCircle2,
|
||||
CheckCircle2Icon,
|
||||
|
@@ -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, {
|
||||
|
@@ -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();
|
||||
|
@@ -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(),
|
||||
|
@@ -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 />
|
||||
|
@@ -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 />
|
||||
|
@@ -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";
|
||||
|
@@ -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");
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -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 (
|
||||
|
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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 />
|
||||
|
@@ -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(),
|
||||
|
@@ -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;
|
||||
|
@@ -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";
|
||||
|
@@ -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 },
|
||||
);
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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";
|
||||
|
@@ -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<{
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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"]),
|
||||
|
@@ -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";
|
||||
|
@@ -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<{
|
||||
|
@@ -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";
|
||||
|
@@ -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">
|
||||
|
@@ -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;
|
||||
|
@@ -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 {
|
||||
|
@@ -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" />
|
||||
|
@@ -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;
|
||||
|
@@ -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",
|
||||
|
@@ -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";
|
||||
|
@@ -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, {
|
||||
|
@@ -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({
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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 />
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -18,13 +18,13 @@ export async function GET(request: NextRequest) {
|
||||
if (type === "otp") {
|
||||
html = await renderOtpEmail({
|
||||
otpCode: "ABC123",
|
||||
loginUrl: "https://app.unsend.dev/login?token=abc123",
|
||||
hostName: "Unsend",
|
||||
loginUrl: "https://app.usesend.com/login?token=abc123",
|
||||
hostName: "useSend",
|
||||
});
|
||||
} else if (type === "invite") {
|
||||
html = await renderTeamInviteEmail({
|
||||
teamName: "My Awesome Team",
|
||||
inviteUrl: "https://app.unsend.dev/join-team?inviteId=123",
|
||||
inviteUrl: "https://app.usesend.com/join-team?inviteId=123",
|
||||
inviterName: "John Doe",
|
||||
role: "admin",
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { EmailRenderer } from "@unsend/email-editor/src/renderer";
|
||||
import { EmailRenderer } from "@usesend/email-editor/src/renderer";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -11,7 +11,8 @@ export async function POST(req: Request) {
|
||||
const html = await renderer.render({
|
||||
shouldReplaceVariableValues: true,
|
||||
linkValues: {
|
||||
"{{unsend_unsubscribe_url}}": "https://unsend.com/unsubscribe",
|
||||
"{{usesend_unsubscribe_url}}": "https://usesend.com/unsubscribe",
|
||||
"{{unsend_unsubscribe_url}}": "https://usesend.com/unsubscribe",
|
||||
},
|
||||
});
|
||||
console.log(`Time taken: ${Date.now() - time}ms`);
|
||||
@@ -34,7 +35,7 @@ export async function POST(req: Request) {
|
||||
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type",
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import JoinTeam from "~/components/team/JoinTeam";
|
||||
import { Suspense } from "react";
|
||||
import Spinner from "@unsend/ui/src/spinner";
|
||||
import Spinner from "@usesend/ui/src/spinner";
|
||||
import { getServerAuthSession } from "~/server/auth";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import "@unsend/ui/styles/globals.css";
|
||||
import "@usesend/ui/styles/globals.css";
|
||||
|
||||
import { Inter } from "next/font/google";
|
||||
import { ThemeProvider } from "@unsend/ui";
|
||||
import { Toaster } from "@unsend/ui/src/toaster";
|
||||
import { JetBrains_Mono } from "next/font/google";
|
||||
import { ThemeProvider } from "@usesend/ui";
|
||||
import { Toaster } from "@usesend/ui/src/toaster";
|
||||
|
||||
import { TRPCReactProvider } from "~/trpc/react";
|
||||
import { Metadata } from "next";
|
||||
@@ -12,9 +13,14 @@ const inter = Inter({
|
||||
variable: "--font-sans",
|
||||
});
|
||||
|
||||
const jetbrainsMono = JetBrains_Mono({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-mono",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Unsend",
|
||||
description: "Open source sending infrastructure for developers",
|
||||
title: "useSend",
|
||||
description: "Open source email platoform",
|
||||
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||
};
|
||||
|
||||
@@ -25,7 +31,9 @@ export default async function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning className="bg-sidebar-background">
|
||||
<body className={`font-sans ${inter.variable} app bg-sidebar-background`}>
|
||||
<body
|
||||
className={`font-sans ${inter.variable} ${jetbrainsMono.variable} app bg-sidebar-background`}
|
||||
>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<Toaster />
|
||||
<TRPCReactProvider>{children}</TRPCReactProvider>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@unsend/ui/src/button";
|
||||
import { Button } from "@usesend/ui/src/button";
|
||||
import Image from "next/image";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
@@ -14,18 +14,18 @@ import {
|
||||
FormField,
|
||||
FormItem,
|
||||
FormMessage,
|
||||
} from "@unsend/ui/src/form";
|
||||
} from "@usesend/ui/src/form";
|
||||
import {
|
||||
InputOTP,
|
||||
InputOTPGroup,
|
||||
InputOTPSlot,
|
||||
REGEXP_ONLY_DIGITS_AND_CHARS,
|
||||
} from "@unsend/ui/src/input-otp";
|
||||
import { Input } from "@unsend/ui/src/input";
|
||||
} from "@usesend/ui/src/input-otp";
|
||||
import { Input } from "@usesend/ui/src/input";
|
||||
import { BuiltInProviderType } from "next-auth/providers/index";
|
||||
import Spinner from "@unsend/ui/src/spinner";
|
||||
import Spinner from "@usesend/ui/src/spinner";
|
||||
import Link from "next/link";
|
||||
import { useTheme } from "@unsend/ui";
|
||||
import { useTheme } from "@usesend/ui";
|
||||
import { useSearchParams as useNextSearchParams } from "next/navigation";
|
||||
|
||||
const emailSchema = z.object({
|
||||
@@ -94,7 +94,9 @@ export default function LoginPage({
|
||||
const email = emailForm.getValues().email;
|
||||
console.log("email", email);
|
||||
|
||||
const finalCallbackUrl = inviteId ? `/join-team?inviteId=${inviteId}` : `${callbackUrl}/dashboard`;
|
||||
const finalCallbackUrl = inviteId
|
||||
? `/join-team?inviteId=${inviteId}`
|
||||
: `${callbackUrl}/dashboard`;
|
||||
window.location.href = `/api/auth/callback/email?email=${encodeURIComponent(
|
||||
email.toLowerCase()
|
||||
)}&token=${values.otp.toLowerCase()}&callbackUrl=${encodeURIComponent(finalCallbackUrl)}`;
|
||||
@@ -106,13 +108,15 @@ export default function LoginPage({
|
||||
|
||||
const [submittedProvider, setSubmittedProvider] =
|
||||
useState<LiteralUnion<BuiltInProviderType> | null>(null);
|
||||
|
||||
|
||||
const searchParams = useNextSearchParams();
|
||||
const inviteId = searchParams.get("inviteId");
|
||||
|
||||
const handleSubmit = (provider: LiteralUnion<BuiltInProviderType>) => {
|
||||
setSubmittedProvider(provider);
|
||||
const callbackUrl = inviteId ? `/join-team?inviteId=${inviteId}` : "/dashboard";
|
||||
const callbackUrl = inviteId
|
||||
? `/join-team?inviteId=${inviteId}`
|
||||
: "/dashboard";
|
||||
signIn(provider, { callbackUrl });
|
||||
};
|
||||
|
||||
@@ -122,18 +126,18 @@ export default function LoginPage({
|
||||
<main className="h-screen flex justify-center items-center">
|
||||
<div className="flex flex-col gap-6">
|
||||
<Image
|
||||
src={resolvedTheme === "dark" ? "/logo-dark.png" : "/logo-light.png"}
|
||||
alt="Unsend"
|
||||
src={"/logo-squircle.png"}
|
||||
alt="useSend"
|
||||
width={50}
|
||||
height={50}
|
||||
className="mx-auto"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-2xl text-center font-semibold">
|
||||
{isSignup ? "Create new account" : "Sign into Unsend"}
|
||||
{isSignup ? "Create new account" : "Sign into useSend"}
|
||||
</p>
|
||||
<p className="text-center mt-2 text-sm text-muted-foreground">
|
||||
{isSignup ? "Already have an account?" : "New to Unsend?"}
|
||||
{isSignup ? "Already have an account?" : "New to useSend?"}
|
||||
<Link
|
||||
href={isSignup ? "/login" : "/signup"}
|
||||
className=" text-foreground hover:underline ml-1"
|
||||
|
@@ -37,8 +37,8 @@ async function UnsubscribePage({
|
||||
<div className=" fixed bottom-10 p-4">
|
||||
<p>
|
||||
Powered by{" "}
|
||||
<a href="https://unsend.dev" className="font-bold" target="_blank">
|
||||
Unsend
|
||||
<a href="https://usesend.com" className="font-bold" target="_blank">
|
||||
useSend
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { Contact } from "@prisma/client";
|
||||
import { Button } from "@unsend/ui/src/button";
|
||||
import Spinner from "@unsend/ui/src/spinner";
|
||||
import { toast } from "@unsend/ui/src/toaster";
|
||||
import { Button } from "@usesend/ui/src/button";
|
||||
import Spinner from "@usesend/ui/src/spinner";
|
||||
import { toast } from "@usesend/ui/src/toaster";
|
||||
import { useState } from "react";
|
||||
import { api } from "~/trpc/react";
|
||||
|
||||
|
@@ -31,14 +31,14 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@unsend/ui/src/sidebar";
|
||||
} from "@usesend/ui/src/sidebar";
|
||||
import Link from "next/link";
|
||||
import { MiniThemeSwitcher, ThemeSwitcher } from "./theme/ThemeSwitcher";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { isSelfHosted } from "~/utils/common";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Badge } from "@unsend/ui/src/badge";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@unsend/ui/src/avatar";
|
||||
import { Badge } from "@usesend/ui/src/badge";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@usesend/ui/src/avatar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -47,7 +47,7 @@ import {
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@unsend/ui/src/dropdown-menu";
|
||||
} from "@usesend/ui/src/dropdown-menu";
|
||||
|
||||
// General items
|
||||
const generalItems = [
|
||||
@@ -125,8 +125,8 @@ export function AppSidebar() {
|
||||
<SidebarHeader>
|
||||
<SidebarGroupLabel>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-lg font-semibold text-foreground">
|
||||
Unsend
|
||||
<span className="text-lg font-semibold text-foreground font-mono">
|
||||
useSend
|
||||
</span>
|
||||
<Badge variant="outline">Beta</Badge>
|
||||
</div>
|
||||
@@ -234,7 +234,7 @@ export function AppSidebar() {
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton asChild tooltip="Docs">
|
||||
<Link href="https://docs.unsend.dev" target="_blank">
|
||||
<Link href="https://docs.usesend.com" target="_blank">
|
||||
<BookOpenText />
|
||||
<span>Docs</span>
|
||||
</Link>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useTheme } from "@unsend/ui";
|
||||
import { useTheme } from "@usesend/ui";
|
||||
import Image from "next/image";
|
||||
|
||||
export const FullScreenLoading = () => {
|
||||
@@ -6,8 +6,8 @@ export const FullScreenLoading = () => {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<Image
|
||||
src={"/logo-light.png"}
|
||||
alt="Unsend"
|
||||
src={"/logo-squircle.png"}
|
||||
alt="useSend"
|
||||
width={45}
|
||||
height={45}
|
||||
className="mx-auto"
|
||||
|
@@ -2,9 +2,9 @@ import { Plan } from "@prisma/client";
|
||||
import { PLAN_PERKS } from "~/lib/constants/payments";
|
||||
import { CheckCircle2 } from "lucide-react";
|
||||
import { api } from "~/trpc/react";
|
||||
import Spinner from "@unsend/ui/src/spinner";
|
||||
import Spinner from "@usesend/ui/src/spinner";
|
||||
import { useTeam } from "~/providers/team-context";
|
||||
import { Badge } from "@unsend/ui/src/badge";
|
||||
import { Badge } from "@usesend/ui/src/badge";
|
||||
import { format } from "date-fns";
|
||||
|
||||
export const PlanDetails = () => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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 { api } from "~/trpc/react";
|
||||
|
||||
export const UpgradeButton = () => {
|
||||
|
@@ -6,7 +6,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from "@unsend/ui/src/dialog";
|
||||
} from "@usesend/ui/src/dialog";
|
||||
import { CheckCircle2 } from "lucide-react";
|
||||
import { useUpgradeModalStore } from "~/store/upgradeModalStore";
|
||||
import { PLAN_PERKS } from "~/lib/constants/payments";
|
||||
|
@@ -9,19 +9,19 @@ 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 { Button } from "@unsend/ui/src/button";
|
||||
import Spinner from "@unsend/ui/src/spinner";
|
||||
import { toast } from "@unsend/ui/src/toaster";
|
||||
import { Input } from "@usesend/ui/src/input";
|
||||
import { Button } from "@usesend/ui/src/button";
|
||||
import Spinner from "@usesend/ui/src/spinner";
|
||||
import { toast } from "@usesend/ui/src/toaster";
|
||||
import { isLocalhost } from "~/utils/client";
|
||||
|
||||
const FormSchema = z.object({
|
||||
region: z.string(),
|
||||
unsendUrl: z.string().url(),
|
||||
usesendUrl: z.string().url(),
|
||||
sendRate: z.coerce.number(),
|
||||
transactionalQuota: z.coerce.number().min(0).max(100),
|
||||
});
|
||||
@@ -56,7 +56,7 @@ export const AddSesSettingsForm: React.FC<SesSettingsProps> = ({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
region: "",
|
||||
unsendUrl: "",
|
||||
usesendUrl: "",
|
||||
sendRate: 1,
|
||||
transactionalQuota: 50,
|
||||
},
|
||||
@@ -65,31 +65,39 @@ export const AddSesSettingsForm: React.FC<SesSettingsProps> = ({
|
||||
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||
const localhost = isLocalhost();
|
||||
|
||||
if (!data.unsendUrl.startsWith("https://") && !localhost) {
|
||||
form.setError("unsendUrl", {
|
||||
if (!data.usesendUrl.startsWith("https://") && !localhost) {
|
||||
form.setError("usesendUrl", {
|
||||
message: "URL must start with https://",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.unsendUrl.includes("localhost") && !localhost) {
|
||||
form.setError("unsendUrl", {
|
||||
if (data.usesendUrl.includes("localhost") && !localhost) {
|
||||
form.setError("usesendUrl", {
|
||||
message: "URL must be a valid url",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
addSesSettings.mutate(data, {
|
||||
onSuccess: () => {
|
||||
utils.admin.invalidate();
|
||||
onSuccess?.();
|
||||
addSesSettings.mutate(
|
||||
{
|
||||
region: data.region,
|
||||
usesendUrl: data.usesendUrl,
|
||||
sendRate: data.sendRate,
|
||||
transactionalQuota: data.transactionalQuota,
|
||||
},
|
||||
onError: (e) => {
|
||||
toast.error("Failed to create", {
|
||||
description: e.message,
|
||||
});
|
||||
{
|
||||
onSuccess: () => {
|
||||
utils.admin.invalidate();
|
||||
onSuccess?.();
|
||||
},
|
||||
onError: (e) => {
|
||||
toast.error("Failed to create", {
|
||||
description: e.message,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
const onRegionInputOutOfFocus = async () => {
|
||||
@@ -134,7 +142,7 @@ export const AddSesSettingsForm: React.FC<SesSettingsProps> = ({
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="unsendUrl"
|
||||
name="usesendUrl"
|
||||
render={({ field, formState }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Callback URL</FormLabel>
|
||||
@@ -145,7 +153,7 @@ export const AddSesSettingsForm: React.FC<SesSettingsProps> = ({
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
{formState.errors.unsendUrl ? (
|
||||
{formState.errors.usesendUrl ? (
|
||||
<FormMessage />
|
||||
) : (
|
||||
<FormDescription>
|
||||
|
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Button } from "@unsend/ui/src/button";
|
||||
import { Button } from "@usesend/ui/src/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
FormField,
|
||||
FormItem,
|
||||
FormMessage,
|
||||
} from "@unsend/ui/src/form";
|
||||
import { Input } from "@unsend/ui/src/input";
|
||||
import { Spinner } from "@unsend/ui/src/spinner";
|
||||
} from "@usesend/ui/src/form";
|
||||
import { Input } from "@usesend/ui/src/input";
|
||||
import { Spinner } from "@usesend/ui/src/spinner";
|
||||
import { api } from "~/trpc/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { toast } from "@unsend/ui/src/toaster";
|
||||
import { toast } from "@usesend/ui/src/toaster";
|
||||
import JoinTeam from "./JoinTeam";
|
||||
|
||||
const FormSchema = z.object({
|
||||
|
@@ -1,17 +1,17 @@
|
||||
"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 { api } from "~/trpc/react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { toast } from "@unsend/ui/src/toaster";
|
||||
import { toast } from "@usesend/ui/src/toaster";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@unsend/ui/src/dialog";
|
||||
} from "@usesend/ui/src/dialog";
|
||||
import { useState } from "react";
|
||||
import type { AppRouter } from "~/server/api/root";
|
||||
import type { inferRouterOutputs } from "@trpc/server";
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { cn, useTheme } from "@unsend/ui";
|
||||
import { Button } from "@unsend/ui/src/button";
|
||||
import { cn, useTheme } from "@usesend/ui";
|
||||
import { Button } from "@usesend/ui/src/button";
|
||||
import { Monitor, Sun, Moon, SunMoonIcon } from "lucide-react";
|
||||
|
||||
export const ThemeSwitcher = () => {
|
||||
@@ -17,7 +17,7 @@ export const ThemeSwitcher = () => {
|
||||
size="sm"
|
||||
className={cn(
|
||||
"p-0.5 rounded-[0.20rem] h-5 w-5",
|
||||
theme === "system" ? " bg-muted" : ""
|
||||
theme === "system" ? " bg-muted" : "",
|
||||
)}
|
||||
onClick={() => setTheme("system")}
|
||||
>
|
||||
@@ -28,7 +28,7 @@ export const ThemeSwitcher = () => {
|
||||
size="sm"
|
||||
className={cn(
|
||||
"p-0.5 rounded-[0.20rem] h-5 w-5",
|
||||
theme === "light" ? " bg-muted" : ""
|
||||
theme === "light" ? " bg-muted" : "",
|
||||
)}
|
||||
onClick={() => setTheme("light")}
|
||||
>
|
||||
@@ -39,7 +39,7 @@ export const ThemeSwitcher = () => {
|
||||
size="sm"
|
||||
className={cn(
|
||||
"p-0.5 rounded-[0.20rem] h-5 w-5",
|
||||
theme === "dark" ? "bg-muted" : ""
|
||||
theme === "dark" ? "bg-muted" : "",
|
||||
)}
|
||||
onClick={() => setTheme("dark")}
|
||||
>
|
||||
|
@@ -13,7 +13,7 @@ export const env = createEnv({
|
||||
.url()
|
||||
.refine(
|
||||
(str) => !str.includes("YOUR_MYSQL_URL_HERE"),
|
||||
"You forgot to change the default URL"
|
||||
"You forgot to change the default URL",
|
||||
),
|
||||
NODE_ENV: z
|
||||
.enum(["development", "test", "production"])
|
||||
@@ -27,12 +27,13 @@ export const env = createEnv({
|
||||
// Since NextAuth.js automatically uses the VERCEL_URL if present.
|
||||
(str) => process.env.VERCEL_URL ?? str,
|
||||
// VERCEL_URL doesn't include `https` so it cant be validated as a URL
|
||||
process.env.VERCEL ? z.string() : z.string().url()
|
||||
process.env.VERCEL ? z.string() : z.string().url(),
|
||||
),
|
||||
GITHUB_ID: z.string().optional(),
|
||||
GITHUB_SECRET: z.string().optional(),
|
||||
AWS_ACCESS_KEY: z.string(),
|
||||
AWS_SECRET_KEY: z.string(),
|
||||
USESEND_API_KEY: z.string().optional(),
|
||||
UNSEND_API_KEY: z.string().optional(),
|
||||
GOOGLE_CLIENT_ID: z.string().optional(),
|
||||
GOOGLE_CLIENT_SECRET: z.string().optional(),
|
||||
@@ -56,8 +57,8 @@ export const env = createEnv({
|
||||
STRIPE_BASIC_USAGE_PRICE_ID: z.string().optional(),
|
||||
STRIPE_LEGACY_BASIC_PRICE_ID: z.string().optional(),
|
||||
STRIPE_WEBHOOK_SECRET: z.string().optional(),
|
||||
SMTP_HOST: z.string().default("smtp.unsend.dev"),
|
||||
SMTP_USER: z.string().default("unsend"),
|
||||
SMTP_HOST: z.string().default("smtp.usesend.com"),
|
||||
SMTP_USER: z.string().default("usesend"),
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -86,6 +87,7 @@ export const env = createEnv({
|
||||
GITHUB_SECRET: process.env.GITHUB_SECRET,
|
||||
AWS_ACCESS_KEY: process.env.AWS_ACCESS_KEY,
|
||||
AWS_SECRET_KEY: process.env.AWS_SECRET_KEY,
|
||||
USESEND_API_KEY: process.env.USESEND_API_KEY,
|
||||
UNSEND_API_KEY: process.env.UNSEND_API_KEY,
|
||||
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
|
||||
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { CodeBlock } from "@unsend/ui/src/code";
|
||||
import { CodeBlock } from "@usesend/ui/src/code";
|
||||
|
||||
export const getSendTestEmailCode = ({
|
||||
from,
|
||||
@@ -17,13 +17,13 @@ export const getSendTestEmailCode = ({
|
||||
{
|
||||
language: "js",
|
||||
title: "Node.js",
|
||||
code: `import { Unsend } from "unsend";
|
||||
code: `import { UseSend } from "usesend";
|
||||
|
||||
const unsend = new Unsend("us_12345");
|
||||
const usesend = new UseSend("us_12345");
|
||||
|
||||
// const unsend = new Unsend("us_12345", "https://my-unsend-instance.com");
|
||||
// const usesend = new UseSend("us_12345", "https://app.usesend.com");
|
||||
|
||||
unsend.emails.send({
|
||||
usesend.emails.send({
|
||||
to: "${to}",
|
||||
from: "${from}",
|
||||
subject: "${subject}",
|
||||
@@ -37,7 +37,7 @@ unsend.emails.send({
|
||||
title: "Python",
|
||||
code: `import requests
|
||||
|
||||
url = "https://app.unsend.dev/api/v1/emails"
|
||||
url = "https://app.usesend.com/api/v1/emails"
|
||||
|
||||
payload = {
|
||||
"to": "${to}",
|
||||
@@ -63,7 +63,7 @@ response = requests.request("POST", url, json=payload, headers=headers)
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://app.unsend.dev/api/v1/emails",
|
||||
CURLOPT_URL => "https://app.usesend.com/api/v1/emails",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
@@ -97,7 +97,7 @@ if ($err) {
|
||||
require 'uri'
|
||||
require 'json'
|
||||
|
||||
url = URI("https://app.unsend.dev/api/v1/emails")
|
||||
url = URI("https://app.usesend.com/api/v1/emails")
|
||||
|
||||
payload = {
|
||||
"to" => "${to}",
|
||||
@@ -126,7 +126,7 @@ puts response.body
|
||||
{
|
||||
language: "curl",
|
||||
title: "cURL",
|
||||
code: `curl -X POST https://app.unsend.dev/api/v1/emails \\
|
||||
code: `curl -X POST https://app.usesend.com/api/v1/emails \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Authorization: Bearer us_12345" \\
|
||||
-d '{"to": "${to}", "from": "${from}", "subject": "${subject}", "text": "${body}", "html": "${bodyHtml}"}'`,
|
||||
|