Skip to content

Commit

Permalink
1.0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
SlickYeet committed Apr 16, 2024
1 parent 6c914f7 commit a07a26d
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 57 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@react-email/components": "0.0.16",
"@tanstack/react-query": "^5.29.2",
"@tanstack/react-table": "^8.16.0",
"@tremor/react": "^3.15.1",
"@tremor/react": "^3.16.0",
"@uploadthing/react": "^6.4.4",
"axios": "^1.6.8",
"bcrypt": "^5.1.1",
Expand Down Expand Up @@ -81,7 +81,7 @@
"eslint-config-next": "14.2.1",
"postcss": "^8",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.13",
"prettier-plugin-tailwindcss": "^0.5.14",
"prisma": "^5.12.1",
"tailwindcss": "^3.4.3",
"tailwindcss-animated": "^1.0.1",
Expand Down
111 changes: 96 additions & 15 deletions src/app/(navbar)/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,104 @@
"use client";

import { ArrowRight, Github, Linkedin, Mail } from "lucide-react";
import Link from "next/link";

import { Hint } from "@/components/hint";
import { MaxWidthWrapper } from "@/components/max-width-wrapper";
import { Wrapper } from "@/components/wrapper";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { toast } from "@/components/ui/use-toast";
import { Wrapper } from "@/components/wrapper";
import { useCopyToClipboard } from "usehooks-ts";

const ContactPage = () => {
const [copiedText, copyToClipboard] = useCopyToClipboard();
const text = "lasse@famlam.ca";

const onClick = () => {
copyToClipboard(text);
if (!copiedText) {
toast({
title: "Oops!",
description: "Something went wrong, please try again.",
variant: "destructive",
});
} else {
toast({
title: "Email copied to clipboard!",
description: `${text}`,
});
}
};

return (
<MaxWidthWrapper className="flex min-h-[calc(100vh-3.5rem)] w-full items-center justify-center text-center">
<Wrapper>
<h1 className="text-3xl font-extrabold tracking-tight sm:text-5xl">
Get in contact with me!
</h1>
<p className="mt-4 text-lg">
Email me at:&nbsp;
<span>
<Button variant="link" className="cursor-text p-0">
lasse@famlam.ca
</Button>
</span>
</p>
<i>Work in progress</i>
<MaxWidthWrapper className="flex min-h-[calc(100vh-3.5rem)] w-full items-center justify-center">
<Wrapper className="flex items-center justify-center gap-6">
<div>
<h1 className="text-3xl font-extrabold tracking-tight sm:text-5xl">
Get in contact with me!
</h1>
<div className="mt-4 space-y-4 text-lg">
<p>
I'm always open to new opportunities, so if you have a project you
want to work on, or just want to chat, feel free to reach out to
me!
</p>

<p className="font-bold">You can find me here:</p>
<div className="flex items-center gap-x-4">
<Link href="https://github.com/SlickYeet" target="_blank">
<Hint label="Github" side="bottom" asChild>
<Github className="h-6 w-6" />
</Hint>
</Link>
<Link
href="https://www.linkedin.com/in/lasse-lammers-90a050234/"
target="_blank"
>
<Hint label="LinkedIn" side="bottom" asChild>
<Linkedin className="h-6 w-6" />
</Hint>
</Link>
<button onClick={() => onClick()}>
<Hint label="Email" side="bottom" asChild>
<Mail className="h-6 w-6" />
</Hint>
</button>
</div>

<p className="font-bold">More about me and what I do here:</p>
<div className="flex items-center gap-x-4">
<Button variant="link" size="none" asChild>
<Link href="/slickyeet">@SlickYeet</Link>
</Button>
<Button variant="link" size="none" asChild>
<Link href="/slickyeet">
Read more
<ArrowRight className="ml-1 h-4 w-4" />
</Link>
</Button>
</div>
</div>
</div>

<div className="flex flex-col items-center gap-4">
<Avatar className="aspect-square h-80 w-80 outline outline-offset-8 outline-border">
<AvatarImage
src="https://github.com/SlickYeet.png"
alt="SlickYeet.png"
className="object-cover"
/>
<AvatarFallback>SlickYeet</AvatarFallback>
</Avatar>
<Button
variant="outline"
size="lg"
className="cursor-default font-extrabold hover:bg-transparent"
>
Hi, I&apos;m Lasse!
</Button>
</div>
</Wrapper>
</MaxWidthWrapper>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/(navbar)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const HomePage = () => {
return (
<>
<MaxWidthWrapper className="mb-12 mt-28 flex flex-col items-center justify-center text-center sm:mt-40">
<div className="mx-auto mb-4 flex max-w-fit items-center justify-center space-x-2 overflow-hidden rounded-full border border-border bg-foreground px-7 py-2 shadow-md backdrop-blur transition-all hover:border-border/80 hover:bg-text-foreground/50">
<div className="mx-auto mb-4 flex max-w-fit items-center justify-center space-x-2 overflow-hidden rounded-full border border-border bg-foreground px-7 py-2 shadow-md backdrop-blur transition-all hover:border-border/80 hover:bg-secondary/80">
<p className="text-sm font-semibold text-text">
Welcome, enjoy your stay!
</p>
Expand Down
3 changes: 1 addition & 2 deletions src/app/(sidebar)/admin/_components/admin-user-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ export const AdminUserActions = ({ row }: { row: Row<Users> }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<Button variant="ghost" size="none" className="h-8 w-8">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ export const ServerActions = ({ server, type }: ServerActionsProps) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<Button variant="ghost" size="none" className="h-8 w-8">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
Expand Down
8 changes: 8 additions & 0 deletions src/app/api/verify-email/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { NextRequest } from "next/server";

import { db } from "@/lib/db";
import { lucia } from "@/lib/lucia";
import { invalidateAllUserSessions } from "@/lib/services/auth-service";

export const GET = async (req: NextRequest) => {
try {
Expand Down Expand Up @@ -45,6 +46,13 @@ export const GET = async (req: NextRequest) => {
},
});

const userSession = await db.session.findFirst({
where: { userId: decoded.userId },
});
if (userSession) {
await invalidateAllUserSessions({ userId: decoded.userId });
}

const session = await lucia.createSession(decoded.userId, {
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
});
Expand Down
30 changes: 21 additions & 9 deletions src/components/profile/edit-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { BadgeCheck, Loader2 } from "lucide-react";
import { usePathname } from "next/navigation";
import { ElementRef, useRef, useTransition } from "react";
import { ElementRef, useRef, useState, useTransition } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";

Expand All @@ -29,9 +29,8 @@ import {
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { toast } from "@/components/ui/use-toast";
import { signOut } from "@/lib/services/auth-service";
import { sendNewVerificationEmail } from "@/lib/services/email-service";
import { getSelf, updateUser } from "@/lib/services/user-service";
import { updateUser } from "@/lib/services/user-service";
import { CustomUser } from "@/types";
import { EditProfileSchema } from "@/types/user-schema";

Expand All @@ -46,7 +45,8 @@ export const EditProfile = ({ user }: ProfileProps) => {
const closeRef = useRef<ElementRef<"button">>(null);

const [isPending, startTransition] = useTransition();
const isEmailVerified = user.isEmailVerified;
const [isDisabled, setIsDisabled] = useState<boolean>(false);
let isEmailVerified = user.isEmailVerified;

const defaultValues = {
display_name: user.display_name,
Expand Down Expand Up @@ -77,16 +77,18 @@ export const EditProfile = ({ user }: ProfileProps) => {
updateUser({
id: user.id,
email: values.email,
isEmailVerified: values.email ? false : true,
display_name: values.display_name,
bio: values.bio,
image: values.image,
})
.then(() => {
.then(async () => {
const updatedFields = [];
if (values.display_name !== user.display_name) {
updatedFields.push("Display name");
}
if (values.email !== user.email) {
isEmailVerified = false;
updatedFields.push("Email");
}
if (values.bio !== user.bio) {
Expand Down Expand Up @@ -118,12 +120,12 @@ export const EditProfile = ({ user }: ProfileProps) => {
};

const onClick = async () => {
// TODO: invalidate all user session and create a new one.
const res = await sendNewVerificationEmail(user.email);
toast({
title: res.message,
variant: res.success ? "default" : "destructive",
});
setIsDisabled(true);
};

return (
Expand Down Expand Up @@ -153,7 +155,11 @@ export const EditProfile = ({ user }: ProfileProps) => {
<FormItem>
<FormLabel>Display Name</FormLabel>
<FormControl>
<Input placeholder="Display Name" {...field} />
<Input
disabled={isPending}
placeholder="Display Name"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -168,7 +174,11 @@ export const EditProfile = ({ user }: ProfileProps) => {
<FormLabel>Email</FormLabel>
<FormControl>
<div className="relative">
<Input placeholder="Email" {...field} />
<Input
disabled={isPending}
placeholder="Email"
{...field}
/>
{isEmailVerified === true && (
<Hint label="Verified" side="left" asChild>
<BadgeCheck className="absolute right-2 top-1/4 h-5 w-5 text-primary" />
Expand All @@ -183,8 +193,10 @@ export const EditProfile = ({ user }: ProfileProps) => {
<Button
type="button"
variant="link"
size="none"
disabled={isDisabled}
onClick={() => onClick()}
className="h-4 p-0 text-sm"
className="h-4 text-sm"
>
Send new verification email
</Button>
Expand Down
11 changes: 8 additions & 3 deletions src/components/profile/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ import { Skeleton } from "@/components/ui/skeleton";
import { UserAvatar, UserAvatarSkeleton } from "@/components/user-avatar";

interface HeaderProps {
username: string;
display_name: string;
email: string;
image: string;
role: string;
}

export const Header = ({ display_name, email, image, role }: HeaderProps) => {
export const Header = ({
username,
display_name,
image,
role,
}: HeaderProps) => {
const matches = useMediaQuery("(max-width: 1024px)");

return (
Expand All @@ -30,7 +35,7 @@ export const Header = ({ display_name, email, image, role }: HeaderProps) => {
</h2>
</div>
<p className="text-xs font-semibold text-muted-foreground lg:text-sm">
{email}
{username}
</p>
<p className="text-xs font-semibold capitalize text-muted-foreground lg:text-sm">
{role}
Expand Down
2 changes: 1 addition & 1 deletion src/components/profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export const Profile = ({ user }: { user: CustomUser }) => {
<MaxWidthWrapper className="hidden-scrollbar col-span-1 space-y-4 pb-10 lg:col-span-2 lg:overflow-y-auto xl:col-span-2 2xl:col-span-5">
<div className="flex items-center">
<Header
username={user.username}
display_name={user.display_name}
email={user.email}
image={user.image}
role={user.role}
/>
Expand Down
12 changes: 8 additions & 4 deletions src/components/table-pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export function TablePagination<TData>({ table }: TablePaginationProps<TData>) {
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 sm:flex"
size="none"
className="hidden h-8 w-8 sm:flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
Expand All @@ -63,7 +64,8 @@ export function TablePagination<TData>({ table }: TablePaginationProps<TData>) {
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
size="none"
className="h-8 w-8"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Expand All @@ -72,7 +74,8 @@ export function TablePagination<TData>({ table }: TablePaginationProps<TData>) {
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
size="none"
className="h-8 w-8"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Expand All @@ -81,7 +84,8 @@ export function TablePagination<TData>({ table }: TablePaginationProps<TData>) {
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 sm:flex"
size="none"
className="hidden h-8 w-8 sm:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const buttonVariants = cva(
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
none: "p-0 h-0",
},
},
defaultVariants: {
Expand Down
9 changes: 0 additions & 9 deletions src/lib/services/auth-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,16 +213,7 @@ export const invalidateAllUserSessions = async ({
});
} else if (!userId) {
const res = await validateSession();

session = res.session;

const sessionCookie = lucia.createBlankSessionCookie();

cookies().set(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes,
);
}

if (!session) {
Expand Down
Loading

0 comments on commit a07a26d

Please sign in to comment.