chore style
This commit is contained in:
@@ -233,6 +233,8 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
|
||||
<TableColumnSekleton />
|
||||
<TableColumnSekleton />
|
||||
<TableColumnSekleton />
|
||||
<TableColumnSekleton />
|
||||
<TableColumnSekleton />
|
||||
</>
|
||||
) : data && data.list && data.list.length ? (
|
||||
data.list.map((record) => (
|
||||
|
||||
@@ -170,9 +170,6 @@ export function DailyPVUVChart({
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
const areaData = data.map((item) => ({
|
||||
id: item.country,
|
||||
}));
|
||||
// const pointData = data.map((item) => ({
|
||||
// id: item.id,
|
||||
// city: item.city,
|
||||
@@ -194,6 +191,13 @@ export function DailyPVUVChart({
|
||||
}
|
||||
});
|
||||
|
||||
const areaData = Object.entries(countryClicks).map(
|
||||
([country, clicks], index) => ({
|
||||
id: country,
|
||||
// color: getColorByClicks(clicks, index, countryClicks),
|
||||
}),
|
||||
);
|
||||
|
||||
const triggers = {
|
||||
[TopoJSONMap.selectors.feature]: (d: any) =>
|
||||
`${getCountryName(d.id)} · ${countryClicks[d.id] || 0}`,
|
||||
@@ -441,3 +445,46 @@ export function StatsList({ data, title }: { data: Stat[]; title: string }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// const baseColors = [
|
||||
// "#ff6b7e",
|
||||
// "#a6cc74",
|
||||
// "#4d8cfd",
|
||||
// "#f4b83e",
|
||||
// "#FF00FF",
|
||||
// "#6859be",
|
||||
// ];
|
||||
|
||||
// const hexToRgb = (hex: string) => {
|
||||
// const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
// return result
|
||||
// ? {
|
||||
// r: parseInt(result[1], 16),
|
||||
// g: parseInt(result[2], 16),
|
||||
// b: parseInt(result[3], 16),
|
||||
// }
|
||||
// : null;
|
||||
// };
|
||||
|
||||
// const getColorByClicks = (
|
||||
// clicks: number,
|
||||
// baseColorIndex: number,
|
||||
// countryClicks: { [key: string]: number },
|
||||
// ) => {
|
||||
// const maxClicks = Math.max(...Object.values(countryClicks));
|
||||
// const minClicks = Math.min(...Object.values(countryClicks));
|
||||
|
||||
// // 归一化点击次数
|
||||
// const normalized =
|
||||
// maxClicks === minClicks
|
||||
// ? 0
|
||||
// : (clicks - minClicks) / (maxClicks - minClicks);
|
||||
|
||||
// // 获取基础颜色
|
||||
// const baseColor = hexToRgb(baseColors[baseColorIndex % baseColors.length]);
|
||||
|
||||
// // 最低60%透明度,最高100%不透明
|
||||
// const alpha = 0.5 + normalized * 0.5;
|
||||
|
||||
// return `rgba(${baseColor!.r}, ${baseColor!.g}, ${baseColor!.b}, ${alpha})`;
|
||||
// };
|
||||
|
||||
@@ -66,8 +66,8 @@ export interface UrlListProps {
|
||||
|
||||
function TableColumnSekleton() {
|
||||
return (
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-9">
|
||||
<TableCell className="col-span-1">
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-10">
|
||||
<TableCell className="col-span-1 sm:col-span-2">
|
||||
<Skeleton className="h-5 w-20" />
|
||||
</TableCell>
|
||||
<TableCell className="col-span-1 sm:col-span-2">
|
||||
@@ -223,6 +223,17 @@ export default function UserUrlsList({ user, action }: UrlListProps) {
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isShowForm && (
|
||||
<UrlForm
|
||||
user={{ id: user.id, name: user.name || "" }}
|
||||
isShowForm={isShowForm}
|
||||
setShowForm={setShowForm}
|
||||
type={formType}
|
||||
initData={currentEditUrl}
|
||||
action={action}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
)}
|
||||
<div className="mb-2 flex-row items-center gap-2 space-y-2 sm:flex sm:space-y-0">
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
@@ -299,17 +310,7 @@ export default function UserUrlsList({ user, action }: UrlListProps) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isShowForm && (
|
||||
<UrlForm
|
||||
user={{ id: user.id, name: user.name || "" }}
|
||||
isShowForm={isShowForm}
|
||||
setShowForm={setShowForm}
|
||||
type={formType}
|
||||
initData={currentEditUrl}
|
||||
action={action}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Table>
|
||||
<TableHeader className="bg-gray-100/50 dark:bg-primary-foreground">
|
||||
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-10">
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const { email } = await req.json();
|
||||
return Response.json({ email });
|
||||
} catch (error) {
|
||||
return Response.json({ statusText: "Server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
+167
-165
@@ -155,144 +155,145 @@ export function RecordForm({
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
className="mb-4 rounded-lg border border-dashed p-4 shadow-sm animate-in fade-in-50"
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Type">
|
||||
<Select
|
||||
onValueChange={(value: RecordType) => {
|
||||
setValue("type", value);
|
||||
setCurrentRecordType(value);
|
||||
}}
|
||||
name={"type"}
|
||||
defaultValue={initData?.type || "CNAME"}
|
||||
<div className="mb-4 rounded-lg border border-dashed shadow-sm animate-in fade-in-50">
|
||||
<div className="rounded-t-lg bg-muted px-4 py-2 text-lg font-semibold">
|
||||
{type === "add" ? "Create" : "Edit"} record
|
||||
</div>
|
||||
<form className="p-4" onSubmit={onSubmit}>
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Type">
|
||||
<Select
|
||||
onValueChange={(value: RecordType) => {
|
||||
setValue("type", value);
|
||||
setCurrentRecordType(value);
|
||||
}}
|
||||
name={"type"}
|
||||
defaultValue={initData?.type || "CNAME"}
|
||||
>
|
||||
<SelectTrigger className="w-full shadow-inner">
|
||||
<SelectValue placeholder="Select a type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{RECORD_TYPE_ENUMS.map((type) => (
|
||||
<SelectItem key={type.value} value={type.value}>
|
||||
{type.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">Required.</p>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Name">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="name">
|
||||
Name (required)
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="name"
|
||||
className="flex-1 shadow-inner"
|
||||
size={32}
|
||||
{...register("name")}
|
||||
/>
|
||||
{currentRecordType === "CNAME" ||
|
||||
(currentRecordType === "A" && (
|
||||
<span className="absolute right-2 top-1/2 -translate-y-1/2 text-sm text-slate-500">
|
||||
.wr.do
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.name ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.name.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
Required. Use @ for root.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns
|
||||
title={
|
||||
currentRecordType === "CNAME"
|
||||
? "Content"
|
||||
: currentRecordType === "A"
|
||||
? "IPv4 address"
|
||||
: "Content"
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full shadow-inner">
|
||||
<SelectValue placeholder="Select a type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{RECORD_TYPE_ENUMS.map((type) => (
|
||||
<SelectItem key={type.value} value={type.value}>
|
||||
{type.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">Required.</p>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Name">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="name">
|
||||
Name (required)
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="content">
|
||||
Content
|
||||
</Label>
|
||||
<Input
|
||||
id="name"
|
||||
id="content"
|
||||
className="flex-1 shadow-inner"
|
||||
size={32}
|
||||
{...register("name")}
|
||||
{...register("content")}
|
||||
/>
|
||||
{currentRecordType === "CNAME" ||
|
||||
(currentRecordType === "A" && (
|
||||
<span className="absolute right-2 top-1/2 -translate-y-1/2 text-sm text-slate-500">
|
||||
.wr.do
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.name ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.name.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
Required. Use @ for root.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns
|
||||
title={
|
||||
currentRecordType === "CNAME"
|
||||
? "Content"
|
||||
: currentRecordType === "A"
|
||||
? "IPv4 address"
|
||||
: "Content"
|
||||
}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="content">
|
||||
Content
|
||||
</Label>
|
||||
<Input
|
||||
id="content"
|
||||
className="flex-1 shadow-inner"
|
||||
size={32}
|
||||
{...register("content")}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.content ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.content.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
{currentRecordType === "CNAME"
|
||||
? "Required. E.g. www.example.com"
|
||||
: currentRecordType === "A"
|
||||
? "Required. E.g. 8.8.8.8"
|
||||
: "Required."}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.content ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.content.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
{currentRecordType === "CNAME"
|
||||
? "Required. E.g. www.example.com"
|
||||
: currentRecordType === "A"
|
||||
? "Required. E.g. 8.8.8.8"
|
||||
: "Required."}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="TTL">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
setValue("ttl", Number(value));
|
||||
}}
|
||||
name="ttl"
|
||||
defaultValue={String(initData?.ttl || 1)}
|
||||
>
|
||||
<SelectTrigger className="w-full shadow-inner">
|
||||
<SelectValue placeholder="Select a time" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{TTL_ENUMS.map((ttl) => (
|
||||
<SelectItem key={ttl.value} value={ttl.value}>
|
||||
{ttl.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Optional. Time To Live.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Comment">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="comment">
|
||||
Comment
|
||||
</Label>
|
||||
<Input
|
||||
id="comment"
|
||||
className="flex-2 shadow-inner"
|
||||
size={74}
|
||||
{...register("comment")}
|
||||
/>
|
||||
</div>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Enter your comment here (up to 100 characters)
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
{/* <FormSectionColumns title="Proxy">
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="TTL">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
setValue("ttl", Number(value));
|
||||
}}
|
||||
name="ttl"
|
||||
defaultValue={String(initData?.ttl || 1)}
|
||||
>
|
||||
<SelectTrigger className="w-full shadow-inner">
|
||||
<SelectValue placeholder="Select a time" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{TTL_ENUMS.map((ttl) => (
|
||||
<SelectItem key={ttl.value} value={ttl.value}>
|
||||
{ttl.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Optional. Time To Live.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Comment">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="comment">
|
||||
Comment
|
||||
</Label>
|
||||
<Input
|
||||
id="comment"
|
||||
className="flex-2 shadow-inner"
|
||||
size={74}
|
||||
{...register("comment")}
|
||||
/>
|
||||
</div>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Enter your comment here (up to 100 characters)
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
{/* <FormSectionColumns title="Proxy">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="proxy">
|
||||
Proxy
|
||||
@@ -301,46 +302,47 @@ export function RecordForm({
|
||||
</div>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">Proxy status</p>
|
||||
</FormSectionColumns> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action buttons */}
|
||||
<div className="mt-3 flex justify-end gap-3">
|
||||
{type === "edit" && (
|
||||
{/* Action buttons */}
|
||||
<div className="mt-3 flex justify-end gap-3">
|
||||
{type === "edit" && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
className="mr-auto w-[80px] px-0"
|
||||
onClick={() => handleDeleteRecord()}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{isDeleting ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>Delete</p>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
className="mr-auto w-[80px] px-0"
|
||||
onClick={() => handleDeleteRecord()}
|
||||
disabled={isDeleting}
|
||||
type="reset"
|
||||
variant="outline"
|
||||
className="w-[80px] px-0"
|
||||
onClick={() => setShowForm(false)}
|
||||
>
|
||||
{isDeleting ? (
|
||||
Cancle
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="blue"
|
||||
disabled={isPending}
|
||||
className="w-[80px] shrink-0 px-0"
|
||||
>
|
||||
{isPending ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>Delete</p>
|
||||
<p>{type === "edit" ? "Update" : "Save"}</p>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="reset"
|
||||
variant="outline"
|
||||
className="w-[80px] px-0"
|
||||
onClick={() => setShowForm(false)}
|
||||
>
|
||||
Cancle
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="blue"
|
||||
disabled={isPending}
|
||||
className="w-[80px] shrink-0 px-0"
|
||||
>
|
||||
{isPending ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>{type === "edit" ? "Update" : "Save"}</p>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+151
-149
@@ -145,124 +145,125 @@ export function UrlForm({
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
className="mb-4 rounded-lg border border-dashed p-4 shadow-sm animate-in fade-in-50"
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Target URL">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="target">
|
||||
Target
|
||||
</Label>
|
||||
<Input
|
||||
id="target"
|
||||
className="flex-1 shadow-inner"
|
||||
size={32}
|
||||
{...register("target")}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.target ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.target.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
Required. https://your-origin-url
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Short Link">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="url">
|
||||
Url
|
||||
</Label>
|
||||
<div className="mb-4 rounded-lg border border-dashed shadow-sm animate-in fade-in-50">
|
||||
<div className="rounded-t-lg bg-muted px-4 py-2 text-lg font-semibold">
|
||||
{type === "add" ? "Create" : "Edit"} short link
|
||||
</div>
|
||||
<form className="p-4" onSubmit={onSubmit}>
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Target URL">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="target">
|
||||
Target
|
||||
</Label>
|
||||
<Input
|
||||
id="target"
|
||||
className="flex-1 shadow-inner"
|
||||
size={32}
|
||||
{...register("target")}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.target ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.target.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
Required. https://your-origin-url
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
<FormSectionColumns title="Short Link">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="url">
|
||||
Url
|
||||
</Label>
|
||||
|
||||
<div className="relative flex items-center">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
setValue("prefix", value);
|
||||
}}
|
||||
name="prefix"
|
||||
defaultValue={initData?.prefix || siteConfig.shortDomains[0]}
|
||||
disabled={type === "edit"}
|
||||
>
|
||||
<SelectTrigger className="w-2/5 rounded-r-none shadow-inner">
|
||||
<SelectValue placeholder="Select a domain" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{siteConfig.shortDomains.map((v) => (
|
||||
<SelectItem key={v} value={v}>
|
||||
{v}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{/* <span className="pointer-events-none absolute left-20 whitespace-nowrap text-sm text-gray-400">
|
||||
<div className="relative flex items-center">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
setValue("prefix", value);
|
||||
}}
|
||||
name="prefix"
|
||||
defaultValue={initData?.prefix || siteConfig.shortDomains[0]}
|
||||
disabled={type === "edit"}
|
||||
>
|
||||
<SelectTrigger className="w-2/5 rounded-r-none shadow-inner">
|
||||
<SelectValue placeholder="Select a domain" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{siteConfig.shortDomains.map((v) => (
|
||||
<SelectItem key={v} value={v}>
|
||||
{v}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{/* <span className="pointer-events-none absolute left-20 whitespace-nowrap text-sm text-gray-400">
|
||||
/s/
|
||||
</span> */}
|
||||
<Input
|
||||
id="url"
|
||||
className="w-3/5 flex-1 rounded-none pl-[8px] shadow-inner"
|
||||
size={20}
|
||||
{...register("url")}
|
||||
disabled={type === "edit"}
|
||||
/>
|
||||
<Button
|
||||
className="rounded-l-none"
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={type === "edit"}
|
||||
onClick={() => {
|
||||
setValue("url", generateUrlSuffix(6));
|
||||
}}
|
||||
>
|
||||
<Sparkles className="h-4 w-4 text-slate-500" />
|
||||
</Button>
|
||||
<Input
|
||||
id="url"
|
||||
className="w-3/5 flex-1 rounded-none pl-[8px] shadow-inner"
|
||||
size={20}
|
||||
{...register("url")}
|
||||
disabled={type === "edit"}
|
||||
/>
|
||||
<Button
|
||||
className="rounded-l-none"
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={type === "edit"}
|
||||
onClick={() => {
|
||||
setValue("url", generateUrlSuffix(6));
|
||||
}}
|
||||
>
|
||||
<Sparkles className="h-4 w-4 text-slate-500" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.url ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.url.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
A random url suffix. Final url like「wr.do/s/suffix」
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between p-1">
|
||||
{errors?.url ? (
|
||||
<p className="pb-0.5 text-[13px] text-red-600">
|
||||
{errors.url.message}
|
||||
</p>
|
||||
) : (
|
||||
<p className="pb-0.5 text-[13px] text-muted-foreground">
|
||||
A random url suffix. Final url like「wr.do/s/suffix」
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</FormSectionColumns>
|
||||
</div>
|
||||
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Expiration">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
setValue("expiration", value);
|
||||
}}
|
||||
name="expiration"
|
||||
defaultValue={initData?.expiration || "-1"}
|
||||
>
|
||||
<SelectTrigger className="w-full shadow-inner">
|
||||
<SelectValue placeholder="Select a time" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{EXPIRATION_ENUMS.map((e) => (
|
||||
<SelectItem key={e.value} value={e.value}>
|
||||
{e.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Expiration time, default for never.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
{/* <div>
|
||||
<div className="items-center justify-start gap-4 md:flex">
|
||||
<FormSectionColumns title="Expiration">
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
setValue("expiration", value);
|
||||
}}
|
||||
name="expiration"
|
||||
defaultValue={initData?.expiration || "-1"}
|
||||
>
|
||||
<SelectTrigger className="w-full shadow-inner">
|
||||
<SelectValue placeholder="Select a time" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{EXPIRATION_ENUMS.map((e) => (
|
||||
<SelectItem key={e.value} value={e.value}>
|
||||
{e.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="p-1 text-[13px] text-muted-foreground">
|
||||
Expiration time, default for never.
|
||||
</p>
|
||||
</FormSectionColumns>
|
||||
{/* <div>
|
||||
<p className="text-sm text-gray-700 dark:text-white">
|
||||
Your Final URL:
|
||||
</p>
|
||||
@@ -270,7 +271,7 @@ export function UrlForm({
|
||||
{getValues("prefix")}/s/{getValues("url")}
|
||||
</p>
|
||||
</div> */}
|
||||
{/* <FormSectionColumns title="Visible">
|
||||
{/* <FormSectionColumns title="Visible">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="visible">
|
||||
Visible
|
||||
@@ -287,7 +288,7 @@ export function UrlForm({
|
||||
Public or private short url.
|
||||
</p>
|
||||
</FormSectionColumns> */}
|
||||
{/* <FormSectionColumns title="Active">
|
||||
{/* <FormSectionColumns title="Active">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Label className="sr-only" htmlFor="active">
|
||||
Active
|
||||
@@ -303,46 +304,47 @@ export function UrlForm({
|
||||
Enable or disable short url.
|
||||
</p>
|
||||
</FormSectionColumns> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action buttons */}
|
||||
<div className="mt-3 flex justify-end gap-3">
|
||||
{type === "edit" && (
|
||||
{/* Action buttons */}
|
||||
<div className="mt-3 flex justify-end gap-3">
|
||||
{type === "edit" && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
className="mr-auto w-[80px] px-0"
|
||||
onClick={() => handleDeleteUrl()}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{isDeleting ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>Delete</p>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
className="mr-auto w-[80px] px-0"
|
||||
onClick={() => handleDeleteUrl()}
|
||||
disabled={isDeleting}
|
||||
type="reset"
|
||||
variant="outline"
|
||||
className="w-[80px] px-0"
|
||||
onClick={() => setShowForm(false)}
|
||||
>
|
||||
{isDeleting ? (
|
||||
Cancle
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="blue"
|
||||
disabled={isPending}
|
||||
className="w-[80px] shrink-0 px-0"
|
||||
>
|
||||
{isPending ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>Delete</p>
|
||||
<p>{type === "edit" ? "Update" : "Save"}</p>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="reset"
|
||||
variant="outline"
|
||||
className="w-[80px] px-0"
|
||||
onClick={() => setShowForm(false)}
|
||||
>
|
||||
Cancle
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="blue"
|
||||
disabled={isPending}
|
||||
className="w-[80px] shrink-0 px-0"
|
||||
>
|
||||
{isPending ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>{type === "edit" ? "Update" : "Save"}</p>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Hr,
|
||||
Html, Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text
|
||||
} from '@react-email/components';
|
||||
import { Icons } from '../components/shared/icons';
|
||||
|
||||
type MagicLinkEmailProps = {
|
||||
actionUrl: string
|
||||
firstName: string
|
||||
mailType: "login" | "register"
|
||||
siteName: string
|
||||
}
|
||||
|
||||
export const MagicLinkEmail = ({
|
||||
firstName = '',
|
||||
actionUrl,
|
||||
mailType,
|
||||
siteName
|
||||
}: MagicLinkEmailProps) => (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>
|
||||
The sales intelligence platform that helps you uncover qualified leads.
|
||||
</Preview>
|
||||
<Tailwind>
|
||||
<Body className="bg-white font-sans">
|
||||
<Container className="mx-auto py-5 pb-12">
|
||||
<Icons.logo className="m-auto block size-10" />
|
||||
<Text className="text-base">
|
||||
Hi {firstName},
|
||||
</Text>
|
||||
<Text className="text-base">
|
||||
Welcome to {siteName} ! Click the link below to {mailType === "login" ? "sign in to" : "activate"} your account.
|
||||
</Text>
|
||||
<Section className="my-5 text-center">
|
||||
<Button
|
||||
className="inline-block rounded-md bg-zinc-900 px-4 py-2 text-base text-white no-underline"
|
||||
href={actionUrl}
|
||||
>
|
||||
{mailType === "login" ? "Sign in" : "Activate Account"}
|
||||
</Button>
|
||||
</Section>
|
||||
<Text className="text-base">
|
||||
This link expires in 24 hours and can only be used once.
|
||||
</Text>
|
||||
{mailType === "login" ? (
|
||||
<Text className="text-base">
|
||||
If you did not try to log into your account, you can safely ignore it.
|
||||
</Text>
|
||||
) : null}
|
||||
<Hr className="my-4 border-t-2 border-gray-300" />
|
||||
<Text className="text-sm text-gray-600">
|
||||
123 Code Street, Suite 404, Devtown, CA 98765
|
||||
</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
|
||||
export default MagicLinkEmail;
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user