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
+
+ {entries?.map((entry) => (
+ -
+
+
+ {entry.highline?.name}
+
+
+
+
+
+
+
{entry.highline?.description}
+
+ Ver mais{" "}
+
+
+
+
+
+
+
+ {entry.crossing_time && (
+
+
+
{transformSecondsToTimeString(entry.crossing_time)}
+
+ )}
+
+
+
{entry.distance_walked}m
+
+
+ {entry.comment}
+
+ ))}
+
+
+ );
+}
+
+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 (
+
+ );
+}
+
+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 (
-