Add prettier and reformat project

This commit is contained in:
pmoharana-cmd 2024-04-23 09:42:15 -04:00
parent 3fd0a545e7
commit f2765ecb47
45 changed files with 6157 additions and 5172 deletions

6
compass/.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": false
}

View File

@ -0,0 +1,26 @@
"use server";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { createClient } from "@/utils/supabase/server";
export async function login(username: string, password: string) {
const supabase = createClient();
// type-casting here for convenience
// in practice, you should validate your inputs
const data = {
email: username,
password: password,
};
const { error } = await supabase.auth.signInWithPassword(data);
if (error) {
redirect("/auth/error");
}
revalidatePath("/resource", "layout");
redirect("/resource");
}

View File

@ -0,0 +1,3 @@
export default function ErrorPage() {
return <p>Sorry, something went wrong</p>;
}

View File

@ -1,25 +1,23 @@
// pages/forgot-password.tsx
"use client";
import React, { useState } from 'react';
import Input from '@/components/Input';
import Button from '@/components/Button';
import InlineLink from '@/components/InlineLink';
import ErrorBanner from '@/components/auth/ErrorBanner';
import React, { useState } from "react";
import Input from "@/components/Input";
import Button from "@/components/Button";
import InlineLink from "@/components/InlineLink";
import ErrorBanner from "@/components/auth/ErrorBanner";
export default function ForgotPasswordPage() {
const [confirmEmail, setConfirmEmail] = useState("");
const [emailError, setEmailError] = useState<string | null>(null);
function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (email.trim() === '') {
setEmailError('Email cannot be empty');
if (email.trim() === "") {
setEmailError("Email cannot be empty");
return false;
} else if (!emailRegex.test(email)) {
setEmailError('Invalid email format');
setEmailError("Invalid email format");
return false;
}
return true; // No error
@ -27,14 +25,16 @@ export default function ForgotPasswordPage() {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
isValidEmail(confirmEmail);
event.preventDefault();
}
};
return (
<>
<h1 className="font-bold text-xl text-purple-800">Forgot Password</h1>
<h1 className="font-bold text-xl text-purple-800">
Forgot Password
</h1>
<div className="mb-6">
<Input
type='email'
type="email"
valid={emailError == null}
title="Enter your email address"
placeholder="janedoe@gmail.com"
@ -44,14 +44,11 @@ export default function ForgotPasswordPage() {
</div>
{emailError && <ErrorBanner heading={emailError} />}
<div className="flex flex-col items-left space-y-4">
<InlineLink href="/auth/login">
Back to Sign In
</InlineLink>
<InlineLink href="/auth/login">Back to Sign In</InlineLink>
<Button type="submit" onClick={handleClick}>
Send
</Button>
</div>
</>
);
}

View File

@ -1,13 +1,11 @@
import Paper from '@/components/auth/Paper';
import Paper from "@/components/auth/Paper";
export default function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}: {
children: React.ReactNode
children: React.ReactNode;
}) {
return (
<Paper>
@ -18,5 +16,5 @@ export default function RootLayout({
&copy; 2024 Compass Center
</p>
</Paper>
)
);
}

View File

@ -1,13 +1,13 @@
// pages/index.tsx
"use client";
import Button from '@/components/Button';
import Input from '@/components/Input'
import InlineLink from '@/components/InlineLink';
import Image from 'next/image';
import Button from "@/components/Button";
import Input from "@/components/Input";
import InlineLink from "@/components/InlineLink";
import Image from "next/image";
import { useState } from "react";
import PasswordInput from '@/components/auth/PasswordInput';
import ErrorBanner from '@/components/auth/ErrorBanner';
import PasswordInput from "@/components/auth/PasswordInput";
import ErrorBanner from "@/components/auth/ErrorBanner";
export default function Page() {
const [email, setEmail] = useState("");
@ -17,51 +17,62 @@ export default function Page() {
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setEmail(event.currentTarget.value);
}
};
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const handlePasswordChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setPassword(event.currentTarget.value);
}
};
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
// Priority: Incorrect combo > Missing email > Missing password
if (password.trim().length === 0) {
setEmailError("Please enter your password.")
setEmailError("Please enter your password.");
event.preventDefault();
}
// This shouldn't happen, <input type="email"> already provides validation, but just in case.
if (email.trim().length === 0) {
setPasswordError("Please enter your email.")
setPasswordError("Please enter your email.");
event.preventDefault();
}
// Placeholder for incorrect email + password combo.
if (email === "incorrect@gmail.com" && password) {
setPasswordError("Incorrect password.")
setPasswordError("Incorrect password.");
event.preventDefault();
}
}
};
return (
<>
<Image
src="/logo.png"
alt='Compass Center logo.'
alt="Compass Center logo."
width={100}
height={91}
/>
<h1 className='font-bold text-2xl text-purple-800'>Login</h1>
<h1 className="font-bold text-2xl text-purple-800">Login</h1>
<div className="mb-6">
<Input type='email' valid={emailError == ""} title="Email" placeholder="janedoe@gmail.com" onChange={handleEmailChange} required />
<Input
type="email"
valid={emailError == ""}
title="Email"
placeholder="janedoe@gmail.com"
onChange={handleEmailChange}
required
/>
</div>
{emailError && <ErrorBanner heading={emailError} />}
<div className="mb-6">
<PasswordInput title="Password" valid={passwordError == ""} onChange={handlePasswordChange} />
<PasswordInput
title="Password"
valid={passwordError == ""}
onChange={handlePasswordChange}
/>
</div>
{passwordError && <ErrorBanner heading={passwordError} />}
@ -69,13 +80,8 @@ export default function Page() {
<InlineLink href="/auth/forgot_password">
Forgot password?
</InlineLink>
<Button onClick={handleClick}>
Login
</Button>
<Button onClick={handleClick}>Login</Button>
</div>
</>
);
};
}

View File

@ -1,31 +1,36 @@
// pages/index.tsx
"use client";
import { useState, useEffect } from 'react';
import Button from '@/components/Button';
import PasswordInput from '@/components/auth/PasswordInput';
import ErrorBanner from '@/components/auth/ErrorBanner';
import { useState, useEffect } from "react";
import Button from "@/components/Button";
import PasswordInput from "@/components/auth/PasswordInput";
import ErrorBanner from "@/components/auth/ErrorBanner";
function isStrongPassword(password: string): boolean {
const strongPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/;
const strongPasswordRegex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/;
return strongPasswordRegex.test(password);
}
export default function Page() {
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [isButtonDisabled, setIsButtonDisabled] = useState(true);
useEffect(() => {
setIsButtonDisabled(newPassword === '' || confirmPassword === '' || newPassword !== confirmPassword|| !isStrongPassword(newPassword));
setIsButtonDisabled(
newPassword === "" ||
confirmPassword === "" ||
newPassword !== confirmPassword ||
!isStrongPassword(newPassword)
);
}, [newPassword, confirmPassword]);
return (
<>
<div className="text-center sm:text-left">
<h1 className="font-bold text-xl text-purple-800">New Password</h1>
<h1 className="font-bold text-xl text-purple-800">
New Password
</h1>
</div>
<div className="mb-4">
<PasswordInput
@ -37,18 +42,33 @@ export default function Page() {
}}
/>
</div>
{isStrongPassword(newPassword) || newPassword === '' ? null : <ErrorBanner heading="Password is not strong enough." description="Tip: Use a mix of letters, numbers, and symbols for a strong password. Aim for at least 8 characters!" />}
{isStrongPassword(newPassword) || newPassword === "" ? null : (
<ErrorBanner
heading="Password is not strong enough."
description="Tip: Use a mix of letters, numbers, and symbols for a strong password. Aim for at least 8 characters!"
/>
)}
<div className="mb-6">
<PasswordInput
title="Confirm Password"
value={confirmPassword}
valid={!isButtonDisabled || (newPassword === confirmPassword && confirmPassword !== '')}
valid={
!isButtonDisabled ||
(newPassword === confirmPassword &&
confirmPassword !== "")
}
onChange={(e) => {
setConfirmPassword(e.target.value);
}}
/>
</div>
{newPassword === confirmPassword || confirmPassword === '' ? null : <ErrorBanner heading="Passwords do not match." description="Please make sure both passwords are the exact same!"/>}
{newPassword === confirmPassword ||
confirmPassword === "" ? null : (
<ErrorBanner
heading="Passwords do not match."
description="Please make sure both passwords are the exact same!"
/>
)}
<div className="flex flex-col items-left space-y-4">
<Button type="submit" disabled={isButtonDisabled}>
Send
@ -57,6 +77,3 @@ export default function Page() {
</>
);
}

View File

@ -1,16 +1,15 @@
import '../styles/globals.css';
import "../styles/globals.css";
export default function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}: {
children: React.ReactNode
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
);
}

View File

@ -28,7 +28,9 @@ export default function Page() {
console.log("email " + email);
};
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const handlePasswordChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setPassword(event.currentTarget.value);
console.log("password " + password);
};
@ -77,9 +79,14 @@ export default function Page() {
/>
</div>
<div className="flex flex-col items-left space-y-4">
<InlineLink href="/forgot_password">Forgot password?</InlineLink>
<InlineLink href="/forgot_password">
Forgot password?
</InlineLink>
<Button onClick={handleClick}>Login</Button>
<div className="text-center text-red-600" hidden={!error}>
<div
className="text-center text-red-600"
hidden={!error}
>
<p>{error}</p>
</div>
</div>

View File

@ -1,14 +1,13 @@
"use client"
"use client";
import Sidebar from '@/components/resource/Sidebar';
import React, { useState } from 'react';
import { ChevronDoubleRightIcon } from '@heroicons/react/24/outline';
import Sidebar from "@/components/resource/Sidebar";
import React, { useState } from "react";
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
export default function RootLayout({
children,
}: {
children: React.ReactNode
children: React.ReactNode;
}) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
@ -18,20 +17,26 @@ export default function RootLayout({
<button
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
className={`fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0`}
aria-label={'Open sidebar'}
aria-label={"Open sidebar"}
>
{!isSidebarOpen &&
<ChevronDoubleRightIcon className="h-5 w-5" /> // Icon for closing the sidebar
{
!isSidebarOpen && (
<ChevronDoubleRightIcon className="h-5 w-5" />
) // Icon for closing the sidebar
}
</button>
{/* sidebar */}
<div className={`absolute inset-y-0 left-0 transform ${isSidebarOpen ? 'translate-x-0' : '-translate-x-full'} w-64 transition duration-300 ease-in-out`}>
<div
className={`absolute inset-y-0 left-0 transform ${isSidebarOpen ? "translate-x-0" : "-translate-x-full"} w-64 transition duration-300 ease-in-out`}
>
<Sidebar setIsSidebarOpen={setIsSidebarOpen} />
</div>
{/* page ui */}
<div className={`flex-1 transition duration-300 ease-in-out ${isSidebarOpen ? 'ml-64' : 'ml-0'}`}>
<div
className={`flex-1 transition duration-300 ease-in-out ${isSidebarOpen ? "ml-64" : "ml-0"}`}
>
{children}
</div>
</div>
)
);
}

View File

@ -1,9 +1,13 @@
"use client"
"use client";
import Callout from "@/components/resource/Callout";
import Card from "@/components/resource/Card";
import { LandingSearchBar } from "@/components/resource/LandingSearchBar";
import { BookOpenIcon, BookmarkIcon, ClipboardIcon } from "@heroicons/react/24/solid";
import Image from 'next/image';
import {
BookOpenIcon,
BookmarkIcon,
ClipboardIcon,
} from "@heroicons/react/24/solid";
import Image from "next/image";
export default function Page() {
return (
@ -13,15 +17,26 @@ export default function Page() {
<div className="mb-4 flex items-center space-x-4">
<Image
src="/logo.png"
alt='Compass Center logo.'
alt="Compass Center logo."
width={25}
height={25}
/>
<h1 className='font-bold text-2xl text-purple-800'>Compass Center Advocate Landing Page</h1>
<h1 className="font-bold text-2xl text-purple-800">
Compass Center Advocate Landing Page
</h1>
</div>
<Callout>
Welcome! Below you will find a list of resources for the Compass Center's trained advocates. These materials serve to virtually provide a collection of advocacy, resource, and hotline manuals and information.
<b> If you are an advocate looking for the contact information of a particular Compass Center employee, please directly contact your staff back-up or the person in charge of your training.</b>
Welcome! Below you will find a list of resources for the
Compass Center's trained advocates. These materials serve to
virtually provide a collection of advocacy, resource, and
hotline manuals and information.
<b>
{" "}
If you are an advocate looking for the contact
information of a particular Compass Center employee,
please directly contact your staff back-up or the person
in charge of your training.
</b>
</Callout>
</div>
<div className="p-8 flex-grow border-t border-gray-200 bg-gray-50">
@ -35,5 +50,5 @@ export default function Page() {
<LandingSearchBar />
</div>
</div>
)
);
}

View File

@ -1,4 +1,4 @@
import { FunctionComponent, ReactNode } from 'react';
import { FunctionComponent, ReactNode } from "react";
type ButtonProps = {
children: ReactNode;
@ -7,9 +7,13 @@ type ButtonProps = {
disabled?: boolean;
};
const Button: FunctionComponent<ButtonProps> = ({ children, type, disabled, onClick}) => {
const buttonClassName = `inline-block rounded border ${disabled ? 'bg-gray-400 text-gray-600 cursor-not-allowed' : 'border-purple-600 bg-purple-600 text-white hover:bg-transparent hover:text-purple-600 focus:outline-none focus:ring active:text-purple-500'} px-4 py-1 text-md font-semibold w-20 h-10 text-center`;
const Button: FunctionComponent<ButtonProps> = ({
children,
type,
disabled,
onClick,
}) => {
const buttonClassName = `inline-block rounded border ${disabled ? "bg-gray-400 text-gray-600 cursor-not-allowed" : "border-purple-600 bg-purple-600 text-white hover:bg-transparent hover:text-purple-600 focus:outline-none focus:ring active:text-purple-500"} px-4 py-1 text-md font-semibold w-20 h-10 text-center`;
return (
<button

View File

@ -1,16 +1,19 @@
import React, { ReactNode } from 'react';
import React, { ReactNode } from "react";
interface Link {
href?: string;
children: ReactNode;
}
const InlineLink: React.FC<Link> = ({href = '#', children}) => {
const InlineLink: React.FC<Link> = ({ href = "#", children }) => {
return (
<a href={href} className='text-sm text-purple-600 hover:underline font-semibold'>
<a
href={href}
className="text-sm text-purple-600 hover:underline font-semibold"
>
{children}
</a>
)
}
);
};
export default InlineLink;

View File

@ -1,22 +1,42 @@
import React, { FunctionComponent, InputHTMLAttributes, ReactNode, ChangeEvent } from 'react';
import React, {
FunctionComponent,
InputHTMLAttributes,
ReactNode,
ChangeEvent,
} from "react";
type InputProps = InputHTMLAttributes<HTMLInputElement> & {
icon?: ReactNode;
title?: ReactNode;
type?: ReactNode;
placeholder?:ReactNode
placeholder?: ReactNode;
valid?: boolean;
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
};
const Input: FunctionComponent<InputProps> = ({ icon, type, title, placeholder, onChange, valid = true, ...rest }) => {
const Input: FunctionComponent<InputProps> = ({
icon,
type,
title,
placeholder,
onChange,
valid = true,
...rest
}) => {
return (
<div>
<label
htmlFor={title}
className={valid ? "block overflow-hidden rounded-md border border-gray-200 px-3 py-2 shadow-sm focus-within:border-purple-600 focus-within:ring-1 focus-within:ring-purple-600" : "block overflow-hidden rounded-md border border-gray-200 px-3 py-2 shadow-sm focus-within:border-red-600 focus-within:ring-1 focus-within:ring-red-600"}
className={
valid
? "block overflow-hidden rounded-md border border-gray-200 px-3 py-2 shadow-sm focus-within:border-purple-600 focus-within:ring-1 focus-within:ring-purple-600"
: "block overflow-hidden rounded-md border border-gray-200 px-3 py-2 shadow-sm focus-within:border-red-600 focus-within:ring-1 focus-within:ring-red-600"
}
>
<span className="text-xs font-semibold text-gray-700"> {title} </span>
<span className="text-xs font-semibold text-gray-700">
{" "}
{title}{" "}
</span>
<div className="mt-1 flex items-center">
<input
type={type}

View File

@ -1,17 +1,27 @@
import React from 'react';
import React from "react";
interface ErrorBannerProps {
heading: string;
description?: string | null;
}
const ErrorBanner: React.FC<ErrorBannerProps> = ({ heading, description = null }) => {
const ErrorBanner: React.FC<ErrorBannerProps> = ({
heading,
description = null,
}) => {
return (
<div role="alert" className="rounded border-s-4 border-red-500 bg-red-50 p-4">
<strong className="block text-sm font-semibold text-red-800">{heading}</strong>
{description && <p className="mt-2 text-xs font-thin text-red-700">
<div
role="alert"
className="rounded border-s-4 border-red-500 bg-red-50 p-4"
>
<strong className="block text-sm font-semibold text-red-800">
{heading}
</strong>
{description && (
<p className="mt-2 text-xs font-thin text-red-700">
{description}
</p>}
</p>
)}
</div>
);
};

View File

@ -1,4 +1,4 @@
import React, { ReactNode } from 'react';
import React, { ReactNode } from "react";
interface PageInterface {
children: ReactNode;

View File

@ -1,6 +1,11 @@
import React, { useState, FunctionComponent, ChangeEvent, ReactNode } from 'react';
import Input from '../Input'; // Adjust the import path as necessary
import { Icons } from '@/utils/constants';
import React, {
useState,
FunctionComponent,
ChangeEvent,
ReactNode,
} from "react";
import Input from "../Input"; // Adjust the import path as necessary
import { Icons } from "@/utils/constants";
type PasswordInputProps = {
title?: ReactNode; // Assuming you might want to reuse title, placeholder etc.
@ -9,14 +14,20 @@ type PasswordInputProps = {
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
};
const PasswordInput: FunctionComponent<PasswordInputProps> = ({ onChange, valid = true, ...rest }) => {
const PasswordInput: FunctionComponent<PasswordInputProps> = ({
onChange,
valid = true,
...rest
}) => {
const [visible, setVisible] = useState(false);
const toggleVisibility = () => {
setVisible(!visible);
};
const PasswordIcon = visible ? Icons['HidePasswordIcon'] : Icons['UnhidePasswordIcon'];
const PasswordIcon = visible
? Icons["HidePasswordIcon"]
: Icons["UnhidePasswordIcon"];
// Render the Input component and pass the PasswordIcon as an icon prop
return (

View File

@ -1,6 +1,5 @@
import React, { ReactNode } from "react";
interface TagProps {
text: string;
icon: React.ReactNode;
@ -9,9 +8,7 @@ interface TagProps {
const Card: React.FC<TagProps> = ({ text, icon }) => {
return (
<div className="flex flex-row space-x-2 items-start justify-start border border-gray-200 bg-white hover:bg-gray-50 shadow rounded-md p-4">
<span className="h-5 text-purple-700 w-5">
{icon}
</span>
<span className="h-5 text-purple-700 w-5">{icon}</span>
<span className="text-sm text-gray-800 font-semibold">{text}</span>
</div>
);

View File

@ -1,16 +1,16 @@
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/solid"
import React, { useState } from 'react';
import Image from 'next/image';
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/solid";
import React, { useState } from "react";
import Image from "next/image";
export const LandingSearchBar: React.FC = () => {
const [searchTerm, setSearchTerm] = useState('');
const [searchTerm, setSearchTerm] = useState("");
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value);
};
const clearSearch = () => {
setSearchTerm('');
setSearchTerm("");
};
return (
@ -28,20 +28,32 @@ export const LandingSearchBar: React.FC = () => {
</div>
{/* input */}
{searchTerm && (
<button
onClick={clearSearch}
>
<XMarkIcon className="h-5 w-5 text-gray-500" aria-hidden="true" />
<button onClick={clearSearch}>
<XMarkIcon
className="h-5 w-5 text-gray-500"
aria-hidden="true"
/>
</button>
)}
<div className="p-3">
<MagnifyingGlassIcon className="h-5 w-5 text-gray-500" aria-hidden="true" />
<MagnifyingGlassIcon
className="h-5 w-5 text-gray-500"
aria-hidden="true"
/>
</div>
</div>
{/* search results, for now since it's empty this is the default screen */}
<div className="flex flex-col pt-16 space-y-2 justify-center items-center">
<Image alt="Landing illustration" src="/landing_illustration.png" width={250} height={250} />
<h2 className="font-medium text-medium text-gray-800">Need to find something? Use the links or the search bar above to get your results.</h2>
<Image
alt="Landing illustration"
src="/landing_illustration.png"
width={250}
height={250}
/>
<h2 className="font-medium text-medium text-gray-800">
Need to find something? Use the links or the search bar
above to get your results.
</h2>
</div>
</div>
);

View File

@ -1,7 +1,13 @@
import React from 'react';
import { HomeIcon, ChevronDoubleLeftIcon, BookmarkIcon, ClipboardIcon, BookOpenIcon } from '@heroicons/react/24/solid';
import { SidebarItem } from './SidebarItem';
import { UserProfile } from './UserProfile';
import React from "react";
import {
HomeIcon,
ChevronDoubleLeftIcon,
BookmarkIcon,
ClipboardIcon,
BookOpenIcon,
} from "@heroicons/react/24/solid";
import { SidebarItem } from "./SidebarItem";
import { UserProfile } from "./UserProfile";
interface SidebarProps {
setIsSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>;
@ -21,20 +27,23 @@ const Sidebar: React.FC<SidebarProps> = ({ setIsSidebarOpen }) => {
</button>
</div>
<div className="flex flex-col space-y-8">
{/* user + logout button */}
<div className="flex items-center p-4 space-x-2 border border-gray-200 rounded-md ">
<UserProfile />
</div>
{/* navigation menu */}
<div className="flex flex-col space-y-2">
<h4 className="text-xs font-semibold text-gray-500">Pages</h4>
<h4 className="text-xs font-semibold text-gray-500">
Pages
</h4>
<nav className="flex flex-col">
<SidebarItem icon={<HomeIcon />} text="Home" />
<SidebarItem icon={<BookmarkIcon />} text="Resources" />
<SidebarItem icon={<ClipboardIcon />} text="Services" />
<SidebarItem icon={<BookOpenIcon />} text="Training Manuals" />
<SidebarItem
icon={<BookOpenIcon />}
text="Training Manuals"
/>
</nav>
</div>
</div>
@ -42,5 +51,4 @@ const Sidebar: React.FC<SidebarProps> = ({ setIsSidebarOpen }) => {
);
};
export default Sidebar;

View File

@ -1,4 +1,3 @@
interface SidebarItemProps {
icon: React.ReactElement;
text: string;
@ -6,11 +5,14 @@ interface SidebarItemProps {
export const SidebarItem: React.FC<SidebarItemProps> = ({ icon, text }) => {
return (
<a href="#" className="flex items-center p-2 space-x-2 hover:bg-gray-200 rounded-md">
<span className="h-5 text-gray-500 w-5">
{icon}
<a
href="#"
className="flex items-center p-2 space-x-2 hover:bg-gray-200 rounded-md"
>
<span className="h-5 text-gray-500 w-5">{icon}</span>
<span className="flex-grow font-medium text-xs text-gray-500">
{text}
</span>
<span className="flex-grow font-medium text-xs text-gray-500">{text}</span>
</a>
);
};

View File

@ -2,10 +2,14 @@ export const UserProfile = () => {
return (
<div className="flex flex-col items-start space-y-2">
<div className="flex flex-col">
<span className="text-sm font-semibold text-gray-800">Compass Center</span>
<span className="text-sm font-semibold text-gray-800">
Compass Center
</span>
<span className="text-xs text-gray-500">cssgunc@gmail.com</span>
</div>
<button className="text-red-600 font-semibold text-xs hover:underline mt-1">Sign out</button>
<button className="text-red-600 font-semibold text-xs hover:underline mt-1">
Sign out
</button>
</div>
)
}
);
};

View File

@ -1,8 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ['notioly.com']
domains: ["notioly.com"],
},
}
};
module.exports = nextConfig
module.exports = nextConfig;

View File

@ -9,6 +9,8 @@
"version": "0.1.0",
"dependencies": {
"@heroicons/react": "^2.1.1",
"@supabase/ssr": "^0.3.0",
"@supabase/supabase-js": "^2.42.3",
"next": "13.5.6",
"react": "^18",
"react-dom": "^18"
@ -20,6 +22,8 @@
"autoprefixer": "^10",
"eslint": "^8",
"eslint-config-next": "13.5.6",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"postcss": "^8",
"tailwindcss": "^3",
"typescript": "^5"
@ -393,6 +397,85 @@
"integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==",
"dev": true
},
"node_modules/@supabase/auth-js": {
"version": "2.63.0",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.63.0.tgz",
"integrity": "sha512-yIgcHnlgv24GxHtVGUhwGqAFDyJkPIC/xjx7HostN08A8yCy8HIfl4JEkTKyBqD1v1L05jNEJOUke4Lf4O1+Qg==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/functions-js": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.2.2.tgz",
"integrity": "sha512-sJGq1nludmi7pY/fdtCpyY/pYonx7MfHdN408bqb736guWcVI1AChYVbI4kUM978EuOE4Ci6l7bUudfGg07QRw==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/node-fetch": {
"version": "2.6.15",
"resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
"integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/@supabase/postgrest-js": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.15.2.tgz",
"integrity": "sha512-9/7pUmXExvGuEK1yZhVYXPZnLEkDTwxgMQHXLrN5BwPZZm4iUCL1YEyep/Z2lIZah8d8M433mVAUEGsihUj5KQ==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/realtime-js": {
"version": "2.9.4",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.9.4.tgz",
"integrity": "sha512-wdq+2hZpgw0r2ldRs87d3U08Y8BrsO1bZxPNqbImpYshAEkusDz4vufR8KaqujKxqewmXS6YnUhuRVdvSEIKCA==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14",
"@types/phoenix": "^1.5.4",
"@types/ws": "^8.5.10",
"ws": "^8.14.2"
}
},
"node_modules/@supabase/ssr": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.3.0.tgz",
"integrity": "sha512-lcVyQ7H6eumb2FB1Wa2N+jYWMfq6CFza3KapikT0fgttMQ+QvDgpNogx9jI8bZgKds+XFSMCojxFvFb+gwdbfA==",
"dependencies": {
"cookie": "^0.5.0",
"ramda": "^0.29.0"
},
"peerDependencies": {
"@supabase/supabase-js": "^2.33.1"
}
},
"node_modules/@supabase/storage-js": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.5.5.tgz",
"integrity": "sha512-OpLoDRjFwClwc2cjTJZG8XviTiQH4Ik8sCiMK5v7et0MDu2QlXjCAW3ljxJB5+z/KazdMOTnySi+hysxWUPu3w==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/supabase-js": {
"version": "2.42.3",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.42.3.tgz",
"integrity": "sha512-/o52L/ngsGapcOUygWigvxzBo/bUVM4bubZMsUSZqZc+9sgjXZsgP/cwyggWUv3QOIqmbBrfSPgDLUh+Ofgi7Q==",
"dependencies": {
"@supabase/auth-js": "2.63.0",
"@supabase/functions-js": "2.2.2",
"@supabase/node-fetch": "2.6.15",
"@supabase/postgrest-js": "1.15.2",
"@supabase/realtime-js": "2.9.4",
"@supabase/storage-js": "2.5.5"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
@ -411,11 +494,15 @@
"version": "20.8.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.8.tgz",
"integrity": "sha512-YRsdVxq6OaLfmR9Hy816IMp33xOBjfyOgUd77ehqg96CFywxAPbDbXvAsuN2KVg2HOT8Eh6uAfU+l4WffwPVrQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.25.1"
}
},
"node_modules/@types/phoenix": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.4.tgz",
"integrity": "sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA=="
},
"node_modules/@types/prop-types": {
"version": "15.7.9",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz",
@ -448,6 +535,14 @@
"integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==",
"dev": true
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz",
@ -593,6 +688,18 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ansi-escapes": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz",
"integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==",
"dev": true,
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@ -1055,6 +1162,37 @@
"node": ">= 6"
}
},
"node_modules/cli-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
"integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
"dev": true,
"dependencies": {
"restore-cursor": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-truncate": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
"integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
"dev": true,
"dependencies": {
"slice-ansi": "^5.0.0",
"string-width": "^7.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@ -1078,6 +1216,12 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -1093,6 +1237,14 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -1810,6 +1962,35 @@
"node": ">=0.10.0"
}
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"dev": true
},
"node_modules/execa": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^8.0.1",
"human-signals": "^5.0.0",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^4.1.0",
"strip-final-newline": "^3.0.0"
},
"engines": {
"node": ">=16.17"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -2003,6 +2184,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-east-asian-width": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz",
"integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==",
"dev": true,
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
@ -2018,6 +2211,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-stream": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
"dev": true,
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-symbol-description": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@ -2246,6 +2451,30 @@
"node": ">= 0.4"
}
},
"node_modules/human-signals": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
"dev": true,
"engines": {
"node": ">=16.17.0"
}
},
"node_modules/husky": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz",
"integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==",
"dev": true,
"bin": {
"husky": "bin.mjs"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -2439,6 +2668,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-generator-function": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
@ -2557,6 +2798,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-string": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@ -2784,6 +3037,80 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/lint-staged": {
"version": "15.2.2",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz",
"integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==",
"dev": true,
"dependencies": {
"chalk": "5.3.0",
"commander": "11.1.0",
"debug": "4.3.4",
"execa": "8.0.1",
"lilconfig": "3.0.0",
"listr2": "8.0.1",
"micromatch": "4.0.5",
"pidtree": "0.6.0",
"string-argv": "0.3.2",
"yaml": "2.3.4"
},
"bin": {
"lint-staged": "bin/lint-staged.js"
},
"engines": {
"node": ">=18.12.0"
},
"funding": {
"url": "https://opencollective.com/lint-staged"
}
},
"node_modules/lint-staged/node_modules/chalk": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"dev": true,
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/lint-staged/node_modules/commander": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
"dev": true,
"engines": {
"node": ">=16"
}
},
"node_modules/lint-staged/node_modules/lilconfig": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz",
"integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==",
"dev": true,
"engines": {
"node": ">=14"
}
},
"node_modules/listr2": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz",
"integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==",
"dev": true,
"dependencies": {
"cli-truncate": "^4.0.0",
"colorette": "^2.0.20",
"eventemitter3": "^5.0.1",
"log-update": "^6.0.0",
"rfdc": "^1.3.0",
"wrap-ansi": "^9.0.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@ -2805,6 +3132,95 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
"node_modules/log-update": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz",
"integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==",
"dev": true,
"dependencies": {
"ansi-escapes": "^6.2.0",
"cli-cursor": "^4.0.0",
"slice-ansi": "^7.0.0",
"strip-ansi": "^7.1.0",
"wrap-ansi": "^9.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-update/node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/log-update/node_modules/ansi-styles": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/log-update/node_modules/is-fullwidth-code-point": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
"integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
"dev": true,
"dependencies": {
"get-east-asian-width": "^1.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-update/node_modules/slice-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
"integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
"dev": true,
"dependencies": {
"ansi-styles": "^6.2.1",
"is-fullwidth-code-point": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
"node_modules/log-update/node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -2828,6 +3244,12 @@
"node": ">=10"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -2850,6 +3272,18 @@
"node": ">=8.6"
}
},
"node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -2980,6 +3414,33 @@
"node": ">=0.10.0"
}
},
"node_modules/npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"dev": true,
"dependencies": {
"path-key": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/npm-run-path/node_modules/path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -3116,6 +3577,21 @@
"wrappy": "1"
}
},
"node_modules/onetime": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"dev": true,
"dependencies": {
"mimic-fn": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/optionator": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@ -3234,6 +3710,18 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pidtree": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
"dev": true,
"bin": {
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@ -3431,6 +3919,15 @@
}
]
},
"node_modules/ramda": {
"version": "0.29.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz",
"integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ramda"
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
@ -3559,6 +4056,52 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/restore-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
"dev": true,
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/restore-cursor/node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/restore-cursor/node_modules/onetime": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"dev": true,
"dependencies": {
"mimic-fn": "^2.1.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/restore-cursor/node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -3569,6 +4112,12 @@
"node": ">=0.10.0"
}
},
"node_modules/rfdc": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
"integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==",
"dev": true
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@ -3726,6 +4275,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -3735,6 +4296,34 @@
"node": ">=8"
}
},
"node_modules/slice-ansi": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^6.0.0",
"is-fullwidth-code-point": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
"node_modules/slice-ansi/node_modules/ansi-styles": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@ -3751,6 +4340,65 @@
"node": ">=10.0.0"
}
},
"node_modules/string-argv": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
"dev": true,
"engines": {
"node": ">=0.6.19"
}
},
"node_modules/string-width": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz",
"integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==",
"dev": true,
"dependencies": {
"emoji-regex": "^10.3.0",
"get-east-asian-width": "^1.0.0",
"strip-ansi": "^7.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width/node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/string-width/node_modules/emoji-regex": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
"dev": true
},
"node_modules/string-width/node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/string.prototype.matchall": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz",
@ -3837,6 +4485,18 @@
"node": ">=4"
}
},
"node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@ -4022,6 +4682,11 @@
"node": ">=8.0"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@ -4177,8 +4842,7 @@
"node_modules/undici-types": {
"version": "5.25.3",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
"integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
"dev": true
"integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA=="
},
"node_modules/update-browserslist-db": {
"version": "1.0.13",
@ -4237,6 +4901,20 @@
"node": ">=10.13.0"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@ -4328,12 +5006,88 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/wrap-ansi": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
"dev": true,
"dependencies": {
"ansi-styles": "^6.2.1",
"string-width": "^7.0.0",
"strip-ansi": "^7.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi/node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/wrap-ansi/node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/ws": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@ -4341,9 +5095,9 @@
"dev": true
},
"node_modules/yaml": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz",
"integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==",
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
"integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
"dev": true,
"engines": {
"node": ">= 14"

View File

@ -6,10 +6,13 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"prettier": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
},
"dependencies": {
"@heroicons/react": "^2.1.1",
"@supabase/ssr": "^0.3.0",
"@supabase/supabase-js": "^2.42.3",
"next": "13.5.6",
"react": "^18",
"react-dom": "^18"
@ -21,8 +24,20 @@
"autoprefixer": "^10",
"eslint": "^8",
"eslint-config-next": "13.5.6",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"postcss": "^8",
"tailwindcss": "^3",
"typescript": "^5"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css,scss,md}": [
"prettier --write"
]
}
}

View File

@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {},
autoprefixer: {},
},
}
};

View File

@ -1,8 +1,7 @@
/* globals.css */
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
:root {
/* Colors */
@ -11,7 +10,8 @@
--ring-opacity: 0.5;
/* Shadows */
--shadow-default: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--shadow-default: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px 0 rgba(0, 0, 0, 0.06);
--shadow-focus: 0 0 0 3px rgba(66, 153, 225, 0.5);
/* Borders */
@ -31,66 +31,65 @@
/* A shade of gray */
}
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 400;
src: url('/fonts/Inter-Regular.ttf') format('truetype');
src: url("/fonts/Inter-Regular.ttf") format("truetype");
}
/* Inter-Bold */
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 700;
src: url('/fonts/Inter-Bold.ttf') format('truetype');
src: url("/fonts/Inter-Bold.ttf") format("truetype");
}
/* Inter-Black */
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 900;
src: url('/fonts/Inter-Black.ttf') format('truetype');
src: url("/fonts/Inter-Black.ttf") format("truetype");
}
/* Inter-ExtraBold */
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 800;
src: url('/fonts/Inter-ExtraBold.ttf') format('truetype');
src: url("/fonts/Inter-ExtraBold.ttf") format("truetype");
}
/* Inter-ExtraLight */
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 200;
src: url('/fonts/Inter-ExtraLight.ttf') format('truetype');
src: url("/fonts/Inter-ExtraLight.ttf") format("truetype");
}
/* Inter-Medium */
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 500;
src: url('/fonts/Inter-Medium.ttf') format('truetype');
src: url("/fonts/Inter-Medium.ttf") format("truetype");
}
/* Inter-SemiBold */
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 600;
src: url('/fonts/Inter-SemiBold.ttf') format('truetype');
src: url("/fonts/Inter-SemiBold.ttf") format("truetype");
}
/* Inter-Thin */
@font-face {
font-family: 'Inter';
font-family: "Inter";
font-style: normal;
font-weight: 100;
src: url('/fonts/Inter-Thin.ttf') format('truetype');
src: url("/fonts/Inter-Thin.ttf") format("truetype");
}

View File

@ -1,23 +1,24 @@
import type { Config } from 'tailwindcss';
import type { Config } from "tailwindcss";
const config: Config = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
fontFamily: {
'sans': ['Inter', 'sans-serif'], // Add 'Inter' to the fontFamily theme
sans: ["Inter", "sans-serif"], // Add 'Inter' to the fontFamily theme
},
fontWeight: {
'medium': 500, // Ensure medium is correctly set to 500
}
medium: 500, // Ensure medium is correctly set to 500
},
},
},
plugins: [],

View File

@ -12,5 +12,4 @@ class CollectionImpl {
setData(data: any) {
this.data = data;
}
}

View File

@ -1,4 +1,23 @@
import { ListBulletIcon, HashtagIcon, Bars3BottomLeftIcon, EnvelopeIcon, AtSymbolIcon, ClipboardIcon, ArrowsUpDownIcon, ChevronDoubleRightIcon, ChevronDoubleLeftIcon, ChevronRightIcon, ChevronLeftIcon, EyeIcon, EyeSlashIcon, UserIcon, BookOpenIcon, MagnifyingGlassIcon, LinkIcon, ClipboardDocumentCheckIcon } from '@heroicons/react/24/solid';
import {
ListBulletIcon,
HashtagIcon,
Bars3BottomLeftIcon,
EnvelopeIcon,
AtSymbolIcon,
ClipboardIcon,
ArrowsUpDownIcon,
ChevronDoubleRightIcon,
ChevronDoubleLeftIcon,
ChevronRightIcon,
ChevronLeftIcon,
EyeIcon,
EyeSlashIcon,
UserIcon,
BookOpenIcon,
MagnifyingGlassIcon,
LinkIcon,
ClipboardDocumentCheckIcon,
} from "@heroicons/react/24/solid";
export const Icons = {
EmailInputIcon: EnvelopeIcon,
@ -18,32 +37,32 @@ export const Icons = {
TextTableIcon: Bars3BottomLeftIcon,
NumberTableIcon: HashtagIcon,
MultiselectTableIcon: ListBulletIcon,
RequirementsTableIcon: ClipboardDocumentCheckIcon
RequirementsTableIcon: ClipboardDocumentCheckIcon,
};
export enum USER {
ADMIN,
EMPLOYEE,
VOLUNTEER
VOLUNTEER,
}
export enum COLLECTION {
RESOURCE,
SERVICE,
USER,
TRAINING_MANUAL
TRAINING_MANUAL,
}
export enum PROGRAM {
DOMESTIC_VIOLENCE,
ECONOMIC_STABILITY,
COMMUNITY_EDUCATION
COMMUNITY_EDUCATION,
}
export enum STATUS {
FULL,
CLOSED,
ACCEPTING_CLIENTS
ACCEPTING_CLIENTS,
}
export enum DATATYPE {
@ -52,7 +71,5 @@ export enum DATATYPE {
LINK,
EMAIL,
MULTISELECT,
SELECT
SELECT,
}

View File

@ -4,15 +4,18 @@ const serviceEntries = [
{
name: "Empowerment Workshops",
status: [STATUS.ACCEPTING_CLIENTS],
summary: "Workshops to empower victims through education and skill-building.",
requirements: "Resident of the community and victim of domestic violence.",
summary:
"Workshops to empower victims through education and skill-building.",
requirements:
"Resident of the community and victim of domestic violence.",
program: [PROGRAM.DOMESTIC_VIOLENCE, PROGRAM.COMMUNITY_EDUCATION],
tags: ["empowerment", "education"],
},
{
name: "Financial Literacy Courses",
status: [STATUS.ACCEPTING_CLIENTS, STATUS.FULL],
summary: "Courses aimed at improving financial independence for victims.",
summary:
"Courses aimed at improving financial independence for victims.",
requirements: "Open to all domestic violence victims.",
program: [PROGRAM.ECONOMIC_STABILITY],
tags: ["finance", "literacy"],
@ -20,7 +23,8 @@ const serviceEntries = [
{
name: "Counseling Services",
status: [STATUS.ACCEPTING_CLIENTS],
summary: "Professional counseling for individuals and families affected by domestic violence.",
summary:
"Professional counseling for individuals and families affected by domestic violence.",
requirements: "Appointment required.",
program: [PROGRAM.DOMESTIC_VIOLENCE],
tags: ["counseling", "mental health"],
@ -36,11 +40,12 @@ const serviceEntries = [
{
name: "Legal Advocacy",
status: [STATUS.FULL],
summary: "Legal advice and representation for victims of domestic violence.",
summary:
"Legal advice and representation for victims of domestic violence.",
requirements: "Legal documentation of domestic violence required.",
program: [PROGRAM.DOMESTIC_VIOLENCE],
tags: ["legal", "advocacy"],
}
},
];
const resourceEntries = [
@ -60,7 +65,8 @@ const resourceEntries = [
},
{
name: "Support Group Finder",
summary: "Find local support groups for survivors of domestic violence.",
summary:
"Find local support groups for survivors of domestic violence.",
link: "https://supportgroups.example.com",
program: [PROGRAM.COMMUNITY_EDUCATION],
tags: ["support", "community"],
@ -78,7 +84,7 @@ const resourceEntries = [
link: "https://workshops.example.com",
program: [PROGRAM.COMMUNITY_EDUCATION],
tags: ["education", "workshops"],
}
},
];
const userEntries = [
@ -121,17 +127,17 @@ const userEntries = [
program: [PROGRAM.DOMESTIC_VIOLENCE],
experience: 4,
group: "Counseling Services Team",
}
},
];
export const mockFetchServices = () => {
return serviceEntries;
}
};
export const mockFetchResources = () => {
return resourceEntries;
}
};
export const mockFetchUsers = () => {
return userEntries;
}
};

View File

@ -15,7 +15,9 @@ export class CollectionDataImpl {
for (const header of this.headers) {
const value = row[header.title];
if (!header.validateInput(value)) {
console.error(`Validation failed for ${header.title} with value ${value}`);
console.error(
`Validation failed for ${header.title} with value ${value}`
);
isValidRow = false;
break;
}
@ -24,7 +26,7 @@ export class CollectionDataImpl {
if (isValidRow) {
this.rows.push(row);
} else {
console.log('Row not added due to validation failure.');
console.log("Row not added due to validation failure.");
}
}
@ -35,5 +37,4 @@ export class CollectionDataImpl {
getHeaders(): Field[] {
return this.headers;
}
}

View File

@ -10,6 +10,4 @@ export class CollectionImpl {
this.icon = icon;
this.data = data;
}
}

View File

@ -1,18 +1,15 @@
import { Field } from "@/utils/classes/Field";
export class EmailFieldImpl extends Field {
constructor() {
super('EmailTableIcon', "Email");
super("EmailTableIcon", "Email");
}
validateInput(value: any): boolean {
if (typeof value !== 'string') {
if (typeof value !== "string") {
return false;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value);
}
}

View File

@ -1,15 +1,12 @@
import { Icons } from "@/utils/constants";
import { Field } from "@/utils/classes/Field";
export class IntegerFieldImpl extends Field {
constructor(title: string) {
super('NumberTableIcon', title);
super("NumberTableIcon", title);
}
validateInput(value: any): boolean {
return Number.isInteger(value);
}
}

View File

@ -1,18 +1,16 @@
import { Field } from "@/utils/classes/Field";
export class LinkFieldImpl extends Field {
constructor() {
super('LinkTableIcon', "Link");
super("LinkTableIcon", "Link");
}
validateInput(value: any): boolean {
if (typeof value !== 'string') {
if (typeof value !== "string") {
return false;
}
const urlRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
const urlRegex =
/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
return urlRegex.test(value);
}
}

View File

@ -1,19 +1,17 @@
import { Field } from "@/utils/classes/Field";
export class MultiselectFieldImpl extends Field {
tags: Set<any>;
selectedTags: Set<any>;
constructor(title: string, tags: Set<any> = new Set()) {
super('MultiselectTableIcon', title);
this.tags = tags
super("MultiselectTableIcon", title);
this.tags = tags;
this.selectedTags = new Set();
}
getTags() {
return this.tags
return this.tags;
}
addTag(tag: any) {
@ -35,5 +33,4 @@ export class MultiselectFieldImpl extends Field {
this.selectedTags.delete(tag);
}
}
}

View File

@ -1,15 +1,12 @@
import { Icons } from "@/utils/constants";
import { Field } from "@/utils/classes/Field";
export class StringFieldImpl extends Field {
constructor(title: string, iconKey: keyof typeof Icons = "TextTableIcon") {
super(iconKey, title);
}
validateInput(value: any): boolean {
return typeof value === 'string';
return typeof value === "string";
}
}

View File

@ -1,15 +1,43 @@
import { mockFetchResources, mockFetchServices, mockFetchUsers } from "../functions/mockFetch";
import {
mockFetchResources,
mockFetchServices,
mockFetchUsers,
} from "../functions/mockFetch";
import { CollectionDataImpl } from "../implementations/CollectionDataImpl";
import { CollectionImpl } from "../implementations/CollectionImpl";
import { ResourceCollectionDataType, ServiceCollectionDataType, UserCollectionDataType } from "./CollectionDataType";
import {
ResourceCollectionDataType,
ServiceCollectionDataType,
UserCollectionDataType,
} from "./CollectionDataType";
const ServiceCollectionData = new CollectionDataImpl(ServiceCollectionDataType, mockFetchServices());
const ResourceCollectionData = new CollectionDataImpl(ResourceCollectionDataType, mockFetchResources());
const UserCollectionData = new CollectionDataImpl(UserCollectionDataType, mockFetchUsers());
const ServiceCollectionData = new CollectionDataImpl(
ServiceCollectionDataType,
mockFetchServices()
);
const ResourceCollectionData = new CollectionDataImpl(
ResourceCollectionDataType,
mockFetchResources()
);
const UserCollectionData = new CollectionDataImpl(
UserCollectionDataType,
mockFetchUsers()
);
export const ServiceCollection = new CollectionImpl('Service','ServiceIcon',new CollectionDataImpl(ServiceCollectionDataType))
export const ServiceCollection = new CollectionImpl(
"Service",
"ServiceIcon",
new CollectionDataImpl(ServiceCollectionDataType)
);
export const ResourceCollection = new CollectionImpl('Resource','ResourceIcon',new CollectionDataImpl(ResourceCollectionDataType))
export const UserCollection = new CollectionImpl('User','UserIcon',new CollectionDataImpl(UserCollectionDataType))
export const ResourceCollection = new CollectionImpl(
"Resource",
"ResourceIcon",
new CollectionDataImpl(ResourceCollectionDataType)
);
export const UserCollection = new CollectionImpl(
"User",
"UserIcon",
new CollectionDataImpl(UserCollectionDataType)
);

View File

@ -6,21 +6,52 @@ import { LinkFieldImpl } from "../implementations/FieldImpl/LinkFieldImpl";
import { MultiselectFieldImpl } from "../implementations/FieldImpl/MultiselectFieldImpl";
import { StringFieldImpl } from "../implementations/FieldImpl/StringFieldImpl";
const programSet: Set<PROGRAM> = new Set([PROGRAM.COMMUNITY_EDUCATION, PROGRAM.DOMESTIC_VIOLENCE, PROGRAM.ECONOMIC_STABILITY]);
const programSet: Set<PROGRAM> = new Set([
PROGRAM.COMMUNITY_EDUCATION,
PROGRAM.DOMESTIC_VIOLENCE,
PROGRAM.ECONOMIC_STABILITY,
]);
const program = new MultiselectFieldImpl("program", programSet);
const requirements = new StringFieldImpl("requirements","RequirementsTableIcon");
const requirements = new StringFieldImpl(
"requirements",
"RequirementsTableIcon"
);
const name = new StringFieldImpl("name");
const summary = new StringFieldImpl("summary");
const statusSet: Set<STATUS> = new Set([STATUS.ACCEPTING_CLIENTS, STATUS.CLOSED, STATUS.FULL])
const statusSet: Set<STATUS> = new Set([
STATUS.ACCEPTING_CLIENTS,
STATUS.CLOSED,
STATUS.FULL,
]);
const status = new MultiselectFieldImpl("status", statusSet);
const link = new LinkFieldImpl()
const tags = new MultiselectFieldImpl("tags")
const link = new LinkFieldImpl();
const tags = new MultiselectFieldImpl("tags");
const roleSet: Set<USER> = new Set([USER.ADMIN, USER.EMPLOYEE, USER.VOLUNTEER]);
const role = new MultiselectFieldImpl("role", roleSet)
const experience = new IntegerFieldImpl("yoe")
const email = new EmailFieldImpl()
const group = new StringFieldImpl("group")
const role = new MultiselectFieldImpl("role", roleSet);
const experience = new IntegerFieldImpl("yoe");
const email = new EmailFieldImpl();
const group = new StringFieldImpl("group");
export const ServiceCollectionDataType: Field[] = [name, status, summary, requirements, program, tags]
export const ResourceCollectionDataType: Field[] = [name, summary, link, program, tags]
export const UserCollectionDataType: Field[] = [name, role, email, program, experience, group]
export const ServiceCollectionDataType: Field[] = [
name,
status,
summary,
requirements,
program,
tags,
];
export const ResourceCollectionDataType: Field[] = [
name,
summary,
link,
program,
tags,
];
export const UserCollectionDataType: Field[] = [
name,
role,
email,
program,
experience,
group,
];