Skip to content

Commit

Permalink
Merge branch 'main' into feature/inv-153
Browse files Browse the repository at this point in the history
  • Loading branch information
xilucks authored Aug 22, 2024
2 parents ea7288f + 2df03a2 commit 0803279
Show file tree
Hide file tree
Showing 52 changed files with 1,807 additions and 240 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,18 @@
"drizzle-orm": "^0.33.0",
"es-hangul": "^1.4.2",
"es-toolkit": "^1.13.1",
"framer-motion": "^11.3.28",
"ky": "^1.4.0",
"lucia": "^3.2.0",
"lucide-react": "^0.414.0",
"nanoid": "^5.0.7",
"next": "14.2.4",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-contenteditable": "^3.3.7",
"react-dom": "^18.3.1",
"react-dropzone": "^14.2.3",
"sanitize-html": "^2.13.0",
"sonner": "^1.5.0",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
Expand All @@ -77,6 +80,7 @@
"@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/sanitize-html": "^2.13.0",
"drizzle-kit": "^0.22.8",
"eslint": "^8",
"eslint-config-next": "14.2.4",
Expand Down
Binary file added public/landing-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/app/(auth)/sign-out/actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use server";

import { revalidatePath } from "next/cache";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import {
Expand All @@ -16,6 +17,7 @@ export async function signOutAction(redirectUrl = "/") {
}

await invalidateAuth(session.id);
revalidatePath("/sign-in");

const headersList = headers();
const referer = headersList.get("referer") || "/";
Expand Down
22 changes: 17 additions & 5 deletions src/app/(main)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,20 @@ export default async function Page() {
return redirect("/sign-in");
}

const templates = await getAllTemplates();
const invitations = await getInvitationsByAuth();
const [templates, invitations] = await Promise.all([
getAllTemplates(),
getInvitationsByAuth(),
]);

const sortByUpdatedAtDescending = (
a: { updatedAt: string | Date | number },
b: { updatedAt: string | Date | number },
) => {
return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
};

const sortedTemplates = templates.toSorted(sortByUpdatedAtDescending);
const sortedInvitations = invitations.toSorted(sortByUpdatedAtDescending);

return (
<div className="flex h-dvh flex-col overflow-hidden">
Expand All @@ -49,13 +61,13 @@ export default async function Page() {
</div>
</div>
<div className="mt-9 grid grid-cols-[repeat(1,_minmax(15rem,_1fr))] gap-8 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5">
{templates.map((template) => (
{sortedTemplates.map((template) => (
<TemplateItem key={template.id} template={template} />
))}
</div>
</div>
{/* 초대장 목록 */}
{!!invitations.length && (
{!!sortedInvitations.length && (
<div className="mx-auto max-w-7xl p-14">
<div className="relative flex items-start justify-between gap-4">
<div className="flex w-full max-w-[80ch] flex-col gap-3">
Expand All @@ -65,7 +77,7 @@ export default async function Page() {
</div>
</div>
<div className="mt-9 grid grid-cols-[repeat(1,_minmax(15rem,_1fr))] gap-8 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5">
{invitations.map((invitation) => (
{sortedInvitations.map((invitation) => (
<InvitationItem key={invitation.id} invitation={invitation} />
))}
</div>
Expand Down
22 changes: 17 additions & 5 deletions src/app/(main)/dashboard/template-item.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
"use client";

import { useMutation } from "@tanstack/react-query";
import { delay } from "es-toolkit";
import { PlusIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { useLoadingStore } from "~/components/gloabl-loading";
import { createInvitation } from "~/lib/db/schema/invitations.query";
import type { Template } from "~/lib/db/schema/templates";

export default function TemplateItem({ template }: { template: Template }) {
const router = useRouter();
const loadingLayer = useLoadingStore();

const createMutation = useMutation({
mutationFn: async () => {
// TODO: global loading
return await createInvitation({
title: template.title,
customFields: template.customFields,
});
loadingLayer.open("초대장을 만들고 있어요...");

const [data] = await Promise.all([
await createInvitation({
title: template.title,
thumbnailUrl: template.thumbnailUrl,
customFields: template.customFields,
}),
await delay(1000),
]);

return data;
},
onSuccess: (data) => {
toast.success("초대장이 생성되었습니다.");
router.push(`/i/${data.eventUrl}/edit`);
loadingLayer.close();
},
onError: (error) => {
console.error("Error creating invitation:", error);
toast.error("초대장 생성 중 오류가 발생했습니다.");
loadingLayer.close();
},
});

Expand Down
10 changes: 9 additions & 1 deletion src/app/(main)/i/[subdomain]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@ export async function generateMetadata(
if (!invitation) {
return {};
}
const previousImages = (await parent).openGraph?.images || [];

const images = (await parent).openGraph?.images || [];
if (invitation.thumbnailUrl) {
images.unshift(invitation.thumbnailUrl);
}

return {
title: {
default: invitation.title,
template: "%s | 인비",
},
description: invitation.description ?? "",
openGraph: {
images,
},
};
}

Expand Down
25 changes: 25 additions & 0 deletions src/app/(main)/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default function Loading() {
return (
<div className="grid h-screen w-screen animate-pulse place-items-center p-4 text-muted-foreground">
<div role="status">
<svg
aria-hidden="true"
className="h-8 w-8 animate-spin fill-muted text-muted-foreground"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
<span className="sr-only">Loading...</span>
</div>
</div>
);
}
146 changes: 87 additions & 59 deletions src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,100 @@
import { CircleIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import MainImage from "~/assets/main.png";
import MainImage2 from "~/assets/main2.png";
import MainImage3 from "~/assets/main3.png";
import MainImage4 from "~/assets/main4.png";
import MainPiece from "~/assets/main_piece.png";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";

export default function Home() {
return (
<div className="flex min-h-screen flex-col">
<header className="flex h-14 items-center px-4 lg:px-6">
<Link className="flex items-center justify-center" href="/">
<CircleIcon className="h-6 w-6" />
<span className="sr-only">Invi</span>
</Link>
<nav className="ml-auto flex items-center gap-4 sm:gap-6">
<Link href="/sign-in">
<Button size="sm">시작하기</Button>
</Link>
</nav>
<div className="flex min-h-screen flex-col px-7">
<header className="pt-[58px]">
<Image
src="/landing-logo.png"
alt="logo"
width={85}
height={29}
className="pb-5"
/>
<section className="flex flex-col gap-y-3">
<h1 className="text-4xl font-bold leading-[40px]">
당신의 환대, INVI
</h1>
<h3 className="break-keep text-lg font-medium leading-5 -tracking-[0.2px] text-[#888888]">
따뜻한 마음을 담아 당신의 환대를 전해보세요.
</h3>
</section>
</header>
<main className="flex-1">
<section className="w-full py-6 sm:py-12 md:py-24 lg:py-32 xl:py-48">
<div className="container px-4 md:px-6">
<div className="grid gap-6 lg:grid-cols-[1fr_400px] lg:gap-12 xl:grid-cols-[1fr_600px]">
<div className="mx-auto aspect-video overflow-hidden rounded-xl bg-secondary object-cover sm:w-full lg:order-last lg:aspect-square" />
<div className="flex flex-col justify-center gap-8">
<h1 className="text-3xl font-bold tracking-tighter sm:text-5xl xl:text-6xl">
초대장 플랫폼, <b className="font-black">인비</b> 🐝
</h1>
<p className="max-w-[600px] text-muted-foreground md:text-xl">
따뜻한 마음을 담아, 당신의 환대를 전해주세요.
</p>
<div className="flex flex-col gap-2 min-[400px]:flex-row">
<Link href="/sign-in">
<Button size="lg">무료로 시작하기</Button>
</Link>
</div>
</div>
</div>
<div className="mt-[156px] flex justify-center">
<Image src={MainImage} alt="main" width={185} height={269} />
</div>
<section className="my-[152px] mt-20 flex justify-center">
<Image src={MainPiece} alt="main" width={20} height={24} />
</section>
<ul className="flex flex-col gap-y-[180px] pb-[100px]">
<section>
<Badge className="h-[38px] rounded-[10px] bg-[#424242] px-4 text-sm leading-none">
최적화 템플릿 사용
</Badge>
<div className="my-6 flex justify-center py-9">
<Image src={MainImage2} alt="main" width={185} height={135} />
</div>
<article className="flex flex-col gap-y-3">
<p className="whitespace-pre-wrap text-3xl font-semibold leading-[40px] -tracking-[0.5px] text-[#222]">
{`다양한 템플릿으로\n빠른 초대장 만들기`}
</p>
<p className="whitespace-pre text-base leading-6 -tracking-[0.2px] text-[#333]">
{`상황에 따라 제공되는 다양한 템플릿을\n사용해 멋진 초대장을 빠르게 만들 수 있어요`}
</p>
</article>
</section>
<section>
<Badge className="h-[38px] rounded-[10px] bg-[#424242] px-4 text-sm leading-none">
자유로운 커스터마이징
</Badge>
<div className="my-6 flex justify-center py-9">
<Image src={MainImage3} alt="main" width={240} height={120} />
</div>
<article className="flex flex-col gap-y-3">
<p className="whitespace-pre-wrap text-3xl font-semibold leading-[40px] -tracking-[0.5px] text-[#222]">
{`원하는대로 커스텀해\n나만의 초대장 만들기`}
</p>
<p className="whitespace-pre text-base leading-6 -tracking-[0.2px] text-[#333]">
{`높은 커스터마이징 자유도로 세상에\n하나뿐인 나만의 초대장을 만들 수 있어요`}
</p>
</article>
</section>
<section id="features" className="w-full py-12 md:py-24 lg:py-32">
<div className="container px-4 md:px-6">
<div className="flex flex-col items-center justify-center space-y-4 text-center">
<div className="space-y-2">
<div className="inline-block rounded-lg bg-secondary px-3 py-1 text-sm text-secondary-foreground">
커스텀 디자인
</div>
<h2 className="text-3xl font-bold tracking-tighter md:text-4xl/tight">
간편하지만, 자유롭게
</h2>
<p className="max-w-[900px] text-muted-foreground md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed">
커스텀 디자인, 실시간 미리보기, 간편한 공유 등
</p>
</div>
</div>
<section>
<Badge className="h-[38px] rounded-[10px] bg-[#424242] px-4 text-sm leading-none">
초대장 특화 기능
</Badge>
<div className="my-6 flex justify-center py-6">
<Image
src={MainImage4}
alt="main"
width={288}
height={269}
className="h-[269px] w-[288px]"
/>
</div>
<article className="flex flex-col gap-y-3">
<p className="whitespace-pre-wrap text-3xl font-semibold leading-[40px] -tracking-[0.5px] text-[#222]">
{`초대장 특화 기능으로\n편하게 초대하기`}
</p>
<p className="whitespace-pre text-base leading-6 -tracking-[0.2px] text-[#333]">
{`쉬운 SNS 공유, 지도 연동, 참석여부 조사 등\n초대장 특화 기능으로 참석자를 초대할 수 있어요`}
</p>
</article>
</section>
</main>
<footer className="flex w-full shrink-0 flex-col items-center gap-2 border-t px-4 py-6 sm:flex-row md:px-6">
<p className="text-[0.875rem] font-normal leading-[1.125rem] text-muted-foreground">
Invi © 2024. All rights reserved
</p>
<nav className="flex gap-4 text-muted-foreground sm:ml-auto sm:gap-6">
<Link className="text-xs" href="#">
이용약관
</Link>
<Link className="text-xs" href="#">
개인정보 처리방침
</Link>
</nav>
</ul>

<footer className="flex justify-center px-4 py-8">
<Button className="h-[60px] w-full rounded-[12px] bg-[#2B2D36] text-lg font-bold">
<Link href="/dashboard">무료로 시작하기</Link>
</Button>
</footer>
</div>
);
Expand Down
Loading

0 comments on commit 0803279

Please sign in to comment.