diff --git a/.eslintrc.json b/.eslintrc.json index bffb357..744bb73 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,8 @@ { - "extends": "next/core-web-vitals" + "extends": "next/core-web-vitals", + "plugins": ["simple-import-sort"], + "rules": { + "simple-import-sort/imports": "warn", + "simple-import-sort/exports": "warn" + } } diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 175db36..da18e8c 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -1,14 +1,20 @@ import "./globals.css"; -import { Inter } from "next/font/google"; + import { Analytics } from "@vercel/analytics/react"; +import { Inter } from "next/font/google"; import { notFound } from "next/navigation"; import { useLocale, useMessages } from "next-intl"; -import Providers from "./Providers"; import Footer from "@/components/Footer"; import NavBar from "@/components/layout/navbar"; -const inter = Inter({ subsets: ["latin"] }); +import Providers from "./Providers"; + +const inter = Inter({ + subsets: ["latin"], + display: "swap", + variable: "--font-inter", +}); export const metadata = { title: "Festival Chooselife", @@ -35,16 +41,14 @@ export default function RootLayout({ // refer to https://github.com/pacocoursey/next-themes#with-app -
- {/* @ts-expect-error Server Component */} +
{children}
+
diff --git a/app/[locale]/profile/[username]/_components/FormattedDate.tsx b/app/[locale]/profile/[username]/_components/FormattedDate.tsx new file mode 100644 index 0000000..185c505 --- /dev/null +++ b/app/[locale]/profile/[username]/_components/FormattedDate.tsx @@ -0,0 +1,19 @@ +import { useFormatter } from "next-intl"; + +interface Props { + date: Date; +} + +function FormatedDate({ date }: Props) { + const format = useFormatter(); + return ( +
+ {format.dateTime(date, { + dateStyle: "medium", + timeStyle: "short", + })} +
+ ); +} + +export default FormatedDate; diff --git a/app/[locale]/profile/[username]/_components/LastWalks.tsx b/app/[locale]/profile/[username]/_components/LastWalks.tsx new file mode 100644 index 0000000..e3ca064 --- /dev/null +++ b/app/[locale]/profile/[username]/_components/LastWalks.tsx @@ -0,0 +1,86 @@ +import { ChevronRightIcon } from "@radix-ui/react-icons"; +import Link from "next/link"; + +import { EnduranceIcon, SpeedlineIcon } from "@/assets"; +import HighlineImage from "@/components/HighlineImage"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/Popover"; +import { transformSecondsToTimeString } from "@/utils/helperFunctions"; +import supabase from "@/utils/supabase-server"; + +import FormattedDate from "./FormattedDate"; + +interface Props { + username: string; +} + +async function LastWalks({ username }: Props) { + const { data: entries } = await supabase + .from("entry") + .select( + ` + *, + highline (*) + ` + ) + .eq("instagram", `@${username}`) + .limit(5) + .order("created_at", { ascending: false }); + + return ( +
+

Last Walks

+ +
+ ); +} + +export default LastWalks; diff --git a/app/[locale]/profile/[username]/_components/Stats.tsx b/app/[locale]/profile/[username]/_components/Stats.tsx new file mode 100644 index 0000000..97f875d --- /dev/null +++ b/app/[locale]/profile/[username]/_components/Stats.tsx @@ -0,0 +1,51 @@ +import { cookies } from "next/headers"; +import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"; + +import type { Database } from "@/utils/database.types"; + +interface Props { + total_distance_walked: number; + total_cadenas: number; + total_full_lines: number; +} +async function Stats({ + total_distance_walked, + total_cadenas, + total_full_lines, +}: Props) { + const displayDistanceInKM = total_distance_walked > 10000; + + return ( +
+
+
+ {displayDistanceInKM + ? total_distance_walked / 1000 + : total_distance_walked} + + {displayDistanceInKM ? "km" : "m"} + +
+
+ caminhados +
+
+
+
+ {total_cadenas} +
+
cadenas
+
+
+
+ {total_full_lines} +
+
+ full lines +
+
+
+ ); +} + +export default Stats; diff --git a/app/[locale]/profile/[username]/_components/UserHeader.tsx b/app/[locale]/profile/[username]/_components/UserHeader.tsx new file mode 100644 index 0000000..e6b8713 --- /dev/null +++ b/app/[locale]/profile/[username]/_components/UserHeader.tsx @@ -0,0 +1,26 @@ +import Image from "next/image"; + +interface Props { + username: string; +} + +function UserHeader({ username }: Props) { + return ( +
+ Profile picture +
+

@{username}

+
+ Este usuário não é verificado +
+
+
+ ); +} + +export default UserHeader; diff --git a/app/[locale]/profile/[username]/_components/UserNotFound.tsx b/app/[locale]/profile/[username]/_components/UserNotFound.tsx new file mode 100644 index 0000000..0b709dc --- /dev/null +++ b/app/[locale]/profile/[username]/_components/UserNotFound.tsx @@ -0,0 +1,33 @@ +import Link from "next/link"; + +interface Props { + username: string; +} + +function UserHeader({ username }: Props) { + return ( +
+
+
+

+ Usuário não existe +

+

+ Não conseguimos achar nenhum usuário com o nome{" "} + @{username} +

+
+ + Voltar para a página inicial + +
+
+
+
+ ); +} + +export default UserHeader; diff --git a/app/[locale]/profile/[username]/page.tsx b/app/[locale]/profile/[username]/page.tsx new file mode 100644 index 0000000..c328820 --- /dev/null +++ b/app/[locale]/profile/[username]/page.tsx @@ -0,0 +1,44 @@ +import { Metadata } from "next"; + +import supabase from "@/utils/supabase-server"; + +import LastWalks from "./_components/LastWalks"; +import Stats from "./_components/Stats"; +import UserHeader from "./_components/UserHeader"; + +export const metadata: Metadata = { + title: "Profile", + description: "User profile", +}; + +export default async function Profile({ + params: { username }, +}: { + params: { username: string }; +}) { + const { + data: { session }, + } = await supabase.auth.getSession(); + + const { data, error } = await supabase + .rpc("profile_stats", { + username: `@${username}`, + }) + .maybeSingle(); + + if (!data || Object.values(data).every((value) => value === null)) { + return ; + } + + return ( +
+ + + +
+ ); +} diff --git a/app/[locale]/profile/page.tsx b/app/[locale]/profile/page.tsx deleted file mode 100644 index cd66a60..0000000 --- a/app/[locale]/profile/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Metadata } from "next"; - -export const metadata: Metadata = { - title: "Profile", - description: "Your profile", -}; - -export default function Profile() { - return ( -
-

Profile

-
- ); -} diff --git a/assets/index.tsx b/assets/index.tsx index 6b8ba13..b91923c 100644 --- a/assets/index.tsx +++ b/assets/index.tsx @@ -1,3 +1,5 @@ +"use client"; + import { SVGAttributes } from "react"; import { motion } from "framer-motion"; @@ -200,6 +202,39 @@ export const GoogleIcon = (props: SVGAttributes) => ( ); +export const SpeedlineIcon = (props: SVGAttributes) => ( + + + +); + +export const EnduranceIcon = (props: SVGAttributes) => ( + + + +); + const themeTransition = { type: "spring", stiffness: 200, diff --git a/components/Footer.tsx b/components/Footer.tsx index 5675359..597e0b1 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -6,7 +6,7 @@ import Link from "next/link"; function Footer() { const t = useTranslations("footer"); return ( -