Skip to content

Commit

Permalink
refactored API structure + started work on adding domains
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey committed Sep 11, 2022
1 parent 0a64ac3 commit e3d1fda
Show file tree
Hide file tree
Showing 32 changed files with 833 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts

/pages/api/test.ts
/pages/api/scripts*
83 changes: 47 additions & 36 deletions components/app/add-modal.tsx → components/app/add-link-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Dispatch,
SetStateAction,
} from "react";
import { useRouter } from "next/router";
import BlurImage from "@/components/shared/blur-image";
import { LinkProps } from "@/lib/types";
import {
Expand All @@ -18,19 +19,22 @@ import { useDebounce } from "use-debounce";
import TextareaAutosize from "react-textarea-autosize";
import { mutate } from "swr";

function AddModalHelper({
showAddModal,
setShowAddModal,
slug, // project domain
function AddLinkModalHelper({
showAddLinkModal,
setShowAddLinkModal,
domain,
}: {
showAddModal: boolean;
setShowAddModal: Dispatch<SetStateAction<boolean>>;
slug?: string;
showAddLinkModal: boolean;
setShowAddLinkModal: Dispatch<SetStateAction<boolean>>;
domain?: string;
}) {
const router = useRouter();
const { slug } = router.query as { slug: string };

const [keyExistsError, setKeyExistsError] = useState(false);
const [generatingSlug, setGeneratingSlug] = useState(false);
const [generatingTitle, setGeneratingTitle] = useState(false);
const [buttonText, setButtonText] = useState("Save changes");
const [buttonText, setButtonText] = useState("Add link");
const [saving, setSaving] = useState(false);

const [data, setData] = useState<LinkProps>({
Expand All @@ -45,8 +49,8 @@ function AddModalHelper({
useEffect(() => {
if (debouncedKey.length > 0) {
fetch(
slug
? `/api/projects/${slug}/links/${debouncedKey}/exists`
domain
? `/api/projects/${slug}/domains/${domain}/links/${debouncedKey}/exists`
: `/api/edge/links/${debouncedKey}/exists`
).then(async (res) => {
if (res.status === 200) {
Expand All @@ -60,7 +64,9 @@ function AddModalHelper({
const generateRandomSlug = useCallback(async () => {
setGeneratingSlug(true);
const res = await fetch(
slug ? `/api/projects/${slug}/links/random` : `/api/edge/links/random`
domain
? `/api/projects/${slug}/domains/${domain}/links/random`
: `/api/edge/links/random`
);
const key = await res.json();
setData((prev) => ({ ...prev, key }));
Expand Down Expand Up @@ -90,7 +96,7 @@ function AddModalHelper({
);

return (
<Modal showModal={showAddModal} setShowModal={setShowAddModal}>
<Modal showModal={showAddLinkModal} setShowModal={setShowAddLinkModal}>
<div className="inline-block w-full max-w-md overflow-hidden align-middle transition-all transform bg-white shadow-xl rounded-2xl">
<div className="flex flex-col justify-center items-center space-y-3 sm:px-16 px-4 pt-8 py-4 border-b border-gray-200">
<BlurImage
Expand All @@ -107,27 +113,32 @@ function AddModalHelper({
onSubmit={async (e) => {
e.preventDefault();
setSaving(true);
fetch(slug ? `/api/projects/${slug}/links` : `/api/links`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
fetch(
domain
? `/api/projects/${slug}/domains/${domain}/links`
: `/api/links`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}
)
.then((res) => {
setSaving(false);
if (res.status === 200) {
setButtonText("Saved!");
mutate(slug ? `/api/projects/${slug}` : `/api/links`);
mutate(domain ? `/api/projects/${slug}` : `/api/links`);
setTimeout(() => {
setButtonText("Save changes");
setButtonText("Add link");
});
setShowAddModal(false);
setShowAddLinkModal(false);
}
})
.catch(() => {
setSaving(false);
setButtonText("Save changes");
setButtonText("Add link");
setKeyExistsError(true);
});
}}
Expand All @@ -142,7 +153,7 @@ function AddModalHelper({
Short Link
</label>
<button
className="hover:text-black active:scale-95 flex items-center text-gray-500 text-sm transition-all duration-75"
className="hover:text-black active:scale-95 flex items-center space-x-2 text-gray-500 text-sm transition-all duration-75"
onClick={generateRandomSlug}
disabled={generatingSlug}
type="button"
Expand All @@ -153,7 +164,7 @@ function AddModalHelper({
</div>
<div className="relative flex mt-1 rounded-md shadow-sm">
<span className="inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-5 text-gray-500 sm:text-sm">
{slug || "dub.sh"}
{domain || "dub.sh"}/
</span>
<input
type="text"
Expand Down Expand Up @@ -228,7 +239,7 @@ function AddModalHelper({
url.length === 0
? "cursor-not-allowed text-gray-300"
: "hover:text-black active:scale-95"
} flex items-center text-gray-500 text-sm transition-all duration-75`}
} flex items-center space-x-2 text-gray-500 text-sm transition-all duration-75`}
onClick={() => generateTitleFromUrl(url)}
disabled={url.length === 0 || generatingTitle}
type="button"
Expand Down Expand Up @@ -270,21 +281,21 @@ function AddModalHelper({
);
}

export function useAddModal({ slug }: { slug?: string }) {
const [showAddModal, setShowAddModal] = useState(false);
export function useAddLinkModal({ domain }: { domain?: string }) {
const [showAddLinkModal, setShowAddLinkModal] = useState(false);

const AddModal = useCallback(() => {
const AddLinkModal = useCallback(() => {
return (
<AddModalHelper
showAddModal={showAddModal}
setShowAddModal={setShowAddModal}
slug={slug}
<AddLinkModalHelper
showAddLinkModal={showAddLinkModal}
setShowAddLinkModal={setShowAddLinkModal}
domain={domain}
/>
);
}, [showAddModal, setShowAddModal, slug]);
}, [showAddLinkModal, setShowAddLinkModal, domain]);

return useMemo(
() => ({ setShowAddModal, AddModal }),
[setShowAddModal, AddModal]
() => ({ setShowAddLinkModal, AddLinkModal }),
[setShowAddLinkModal, AddLinkModal]
);
}
10 changes: 10 additions & 0 deletions components/app/custom-domain/configurations-placeholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const ConfigurationsPlaceholder = () => {
return (
<div className="flex items-center space-x-3 mt-3 px-2 sm:px-10">
<div className="w-6 h-6 rounded-full bg-gray-300 animate-pulse" />
<p className="text-gray-500 font-normal text-sm">Loading Configuration</p>
</div>
);
};

export default ConfigurationsPlaceholder;
202 changes: 202 additions & 0 deletions components/app/custom-domain/configurations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { useState } from "react";
import ConfigurationsPlaceholder from "./configurations-placeholder";

function getVerificationError(verificationResponse) {
try {
const error = verificationResponse.error;
if (error.code === "missing_txt_record") {
return null;
}
return error.message;
} catch {
return null;
}
}

const Configurations = ({ data }) => {
const [recordType, setRecordType] = useState("CNAME");
if (!data) {
return <ConfigurationsPlaceholder />;
}

if (!data.verified) {
const txtVerification = data.verification.find((x) => x.type === "TXT");
return (
<>
<div className="flex items-center space-x-3 my-3 px-2 sm:px-10">
<svg
viewBox="0 0 24 24"
width="24"
height="24"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
>
<circle cx="12" cy="12" r="10" fill="#d32f2f" />
<>
<path d="M15 9l-6 6" stroke="white" />
<path d="M9 9l6 6" stroke="white" />
</>
</svg>
<p className={`text-red-700 font-medium text-sm`}>
Domain is pending verification
</p>
</div>

<div className="w-full border-t border-gray-100 mt-5 mb-8" />

<div className="px-2 sm:px-10">
<div className="flex justify-start space-x-4">
<div
onClick={() => setRecordType("CNAME")}
className={`${
recordType == "CNAME"
? "text-black border-black"
: "text-gray-400 border-white"
} text-sm border-b-2 pb-1 transition-all ease duration-150`}
>
Verify Domain Ownership
</div>
</div>
<div className="my-3 text-left">
<p className="my-5 text-sm">
Please set the following TXT record on {data.apexName} to prove
ownership of {data.name}:
</p>
<div className="flex justify-start items-start space-x-10 bg-gray-50 p-2 rounded-md">
<div>
<p className="text-sm font-bold">Type</p>
<p className="text-sm font-mono mt-2">{txtVerification.type}</p>
</div>
<div>
<p className="text-sm font-bold">Name</p>
<p className="text-sm font-mono mt-2">
{txtVerification.domain.slice(
0,
txtVerification.domain.length - data.apexName.length - 1
)}
</p>
</div>
<div>
<p className="text-sm font-bold">Value</p>
<p className="text-sm font-mono mt-2">
<span className="text-ellipsis">{txtVerification.value}</span>
</p>
</div>
</div>
{getVerificationError(data.verificationResponse) && (
<p className="my-5 text-sm text-red-700">
{getVerificationError(data.verificationResponse)}
</p>
)}
</div>
</div>
</>
);
}

return (
<>
<div className="flex items-center space-x-3">
<svg
viewBox="0 0 24 24"
width="24"
height="24"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
shapeRendering="geometricPrecision"
>
<circle
cx="12"
cy="12"
r="10"
fill={data.configured ? "#1976d2" : "#d32f2f"}
/>
{data.configured ? (
<>
<path
d="M8 11.8571L10.5 14.3572L15.8572 9"
fill="none"
stroke="white"
/>
</>
) : (
<>
<path d="M15 9l-6 6" stroke="white" />
<path d="M9 9l6 6" stroke="white" />
</>
)}
</svg>
<p
className={`${
data.configured
? "text-black font-normal"
: "text-red-700 font-medium"
} text-sm`}
>
{data.configured ? "Valid" : "Invalid"} Configuration
</p>
</div>

{!data.configured && (
<>
<div className="w-full border-t border-gray-100 mt-5 mb-8" />

<div className="px-2 sm:px-10">
<div className="flex justify-start space-x-4">
<button
onClick={() => setRecordType("CNAME")}
className={`${
recordType == "CNAME"
? "text-black border-black"
: "text-gray-400 border-white"
} text-sm border-b-2 pb-1 transition-all ease duration-150`}
>
CNAME Record (subdomains)
</button>
<button
onClick={() => setRecordType("A")}
className={`${
recordType == "A"
? "text-black border-black"
: "text-gray-400 border-white"
} text-sm border-b-2 pb-1 transition-all ease duration-150`}
>
A Record (apex domain)
</button>
</div>
<div className="my-3 text-left">
<p className="my-5 text-sm">
Set the following record on your DNS provider to continue:
</p>
<div className="flex justify-start items-center space-x-10 bg-gray-50 p-2 rounded-md">
<div>
<p className="text-sm font-bold">Type</p>
<p className="text-sm font-mono mt-2">{recordType}</p>
</div>
<div>
<p className="text-sm font-bold">Name</p>
<p className="text-sm font-mono mt-2">
{recordType == "CNAME" ? "www" : "@"}
</p>
</div>
<div>
<p className="text-sm font-bold">Value</p>
<p className="text-sm font-mono mt-2">
{recordType == "CNAME"
? `cname.platformize.co`
: `76.76.21.21`}
</p>
</div>
</div>
</div>
</div>
</>
)}
</>
);
};

export default Configurations;
Loading

0 comments on commit e3d1fda

Please sign in to comment.