login select completed

This commit is contained in:
2024-11-30 13:56:54 +03:00
parent 45b8f08a29
commit 9bea33b3c4
138 changed files with 9196 additions and 166 deletions

View File

@@ -0,0 +1,82 @@
"use server";
import { redirect } from "next/navigation";
const defaultHeaders = {
accept: "application/json",
"Content-type": "application/json",
};
interface HeadersObject {
cache: string;
method: string;
headers: Object;
body?: string;
}
const fetchData = async (
endpoint: string,
payload: any,
method: string = "POST",
cache: boolean = false
) => {
let headersObject: any = {
cache: cache ? "force-cache" : "no-store",
method: method,
headers: defaultHeaders,
};
if (method !== "GET") {
headersObject = {
...headersObject,
body: JSON.stringify(payload),
};
}
try {
const response = await fetch(endpoint, headersObject);
if (response.status === 200) {
return response;
} else if (response.status === 401) {
return { status: 401 };
} else {
return { status: response.status };
}
} catch (error) {}
return { status: 500 };
};
const fetchDataWithToken = async (
endpoint: string,
token: string,
payload: any,
method: string = "POST",
cache: boolean = false
) => {
let headersObject: any = {
cache: cache ? "force-cache" : "no-store",
method: method,
headers: {
...defaultHeaders,
"evyos-session-key": token,
},
};
if (method !== "GET") {
headersObject = {
...headersObject,
body: JSON.stringify(payload),
};
}
try {
const response = await fetch(endpoint, headersObject);
if (response.status === 200) {
return response;
} else if (response.status === 401) {
redirect("/login/email");
} else {
return { status: response.status };
}
} catch (error) {}
return { status: 500 };
};
export { fetchData, fetchDataWithToken };

10
src/apicalls/basics.ts Normal file
View File

@@ -0,0 +1,10 @@
export const baseUrl = "http://0.0.0.0:41575";
export const tokenSecret =
"SyIkoYIK5JLD2cuRB0hnB-1zcj5FB5oxbB73ph-Oe3Kn0WWeSOjnWAuzzi6ZUX_5TpFF0-KGpKDZepaUhVEmmdaY5E-_sI3b9UwfN_eg-KgtpCiiWiHADSu9bRSBez_ZI4AFkeNK0LSRWpqq9El6V3pauvgsKJU_ZXwplIW49Y8";
export const cookieObject: any = {
httpOnly: true,
path: "/",
secure: true,
sameSite: "strict",
// maxAge: 3600,
};

View File

@@ -0,0 +1,55 @@
"use server";
import { fetchDataWithToken } from "../api-fetcher";
import { cookies } from "next/headers";
import { baseUrl, tokenSecret } from "../basics";
import NextCrypto from "next-crypto";
const checkToken = `${baseUrl}/authentication/valid`;
const nextCrypto = new NextCrypto(tokenSecret);
async function check_access_token_is_valid() {
const cookieStore = await cookies();
const encrpytAccessToken = cookieStore.get("accessToken")?.value || "";
const decryptedAccessToken =
(await nextCrypto.decrypt(encrpytAccessToken)) || "";
const response = await fetchDataWithToken(
checkToken,
decryptedAccessToken,
{},
"GET",
false
);
return response?.status === 200 ? true : false;
}
async function retrieve_access_token() {
const cookieStore = await cookies();
const encrpytAccessToken = cookieStore.get("accessToken")?.value || "";
return encrpytAccessToken
? await nextCrypto.decrypt(encrpytAccessToken)
: null;
}
async function retrieve_user_type() {
const cookieStore = await cookies();
const encrpytaccessObject = cookieStore.get("accessObject")?.value || "{}";
const decrpytUserType = JSON.parse(
(await nextCrypto.decrypt(encrpytaccessObject)) || "{}"
);
return decrpytUserType ? decrpytUserType?.user_type : null;
}
async function retrieve_access_objects() {
const cookieStore = await cookies();
const encrpytAccessObject = cookieStore.get("accessObject")?.value || "";
const decrpytAccessObject = await nextCrypto.decrypt(encrpytAccessObject);
return decrpytAccessObject ? JSON.parse(decrpytAccessObject) : null;
}
export {
check_access_token_is_valid,
retrieve_access_token,
retrieve_user_type,
retrieve_access_objects,
};

View File

@@ -0,0 +1,153 @@
"use server";
import { fetchData, fetchDataWithToken } from "../api-fetcher";
import { cookies } from "next/headers";
import { baseUrl, cookieObject, tokenSecret } from "../basics";
import NextCrypto from "next-crypto";
const loginEndpoint = `${baseUrl}/authentication/login`;
const loginSelectEndpoint = `${baseUrl}/authentication/select`;
interface LoginViaAccessKeys {
domain: string;
accessKey: string;
password: string;
rememberMe: boolean;
}
interface LoginSelectEmployee {
token: string;
company_uu_id: string;
}
interface LoginSelectOccupant {
token: string;
build_part_uu_id: string;
occupant_uu_id: string;
}
async function login_via_access_keys(payload: LoginViaAccessKeys) {
const cookieStore = await cookies();
const nextCrypto = new NextCrypto(tokenSecret);
let responseData: any = {};
const tokenResponse: any = await fetchData(
loginEndpoint,
{
domain: payload.domain,
access_key: payload.accessKey,
password: payload.password,
remember_me: payload.rememberMe,
},
"POST",
false
);
if (tokenResponse.status === 200) {
responseData = await tokenResponse?.json();
const accessToken = await nextCrypto.encrypt(responseData.access_token);
const accessObject = await nextCrypto.encrypt(
JSON.stringify(responseData.access_object)
);
const userProfile = await nextCrypto.encrypt(
JSON.stringify(responseData.user)
);
const refreshToken = await nextCrypto.encrypt(responseData.refresh_token);
// const userType = await nextCrypto.encrypt(responseData.user_type);
// cookieStore.set({
// name: "refreshToken",
// value: refreshToken,
// httpOnly: true,
// path: "/",
// });
cookieStore.set({
name: "accessToken",
value: accessToken,
...cookieObject,
});
cookieStore.set({
name: "accessObject",
value: accessObject,
...cookieObject,
});
cookieStore.set({
name: "userProfile",
value: JSON.stringify(userProfile),
...cookieObject,
});
// cookieStore.set({
// name: "userType",
// value: userType,
// ...cookieObject,
// });
}
return responseData;
}
async function login_select_employee(payload: LoginSelectEmployee) {
let responseData = null;
const cookieStore = await cookies();
const nextCrypto = new NextCrypto(tokenSecret);
const selectResponse: any = await fetchDataWithToken(
loginSelectEndpoint,
payload.token,
{
company_uu_id: payload.company_uu_id,
},
"POST",
false
);
if (selectResponse.status === 200) {
responseData = await selectResponse?.json();
const usersSelection = await nextCrypto.encrypt(
JSON.stringify({
company_uu_id: payload.company_uu_id,
user_type: "employee",
})
);
cookieStore.set({
name: "userSelection",
value: usersSelection,
...cookieObject,
});
}
return responseData;
}
async function login_select_occupant(payload: LoginSelectOccupant) {
let responseData = null;
const cookieStore = await cookies();
const nextCrypto = new NextCrypto(tokenSecret);
const selectResponse: any = await fetchDataWithToken(
loginSelectEndpoint,
payload.token,
{
build_part_uu_id: payload.build_part_uu_id,
occupant_uu_id: payload.occupant_uu_id,
},
"POST",
false
);
if (selectResponse.status === 200) {
responseData = await selectResponse?.json();
const usersSelection = await nextCrypto.encrypt(
JSON.stringify({
company_uu_id: {
build_part_uu_id: payload.build_part_uu_id,
occupant_uu_id: payload.occupant_uu_id,
},
user_type: "occupant",
})
);
cookieStore.set({
name: "userSelection",
value: usersSelection,
...cookieObject,
});
}
return responseData;
}
export { login_via_access_keys, login_select_employee, login_select_occupant };

View File

@@ -2,20 +2,71 @@
@tailwind components;
@tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
body {
font-family: Arial, Helvetica, sans-serif;
}
@media (prefers-color-scheme: dark) {
@layer base {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 10% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View File

@@ -0,0 +1,13 @@
"use server";
import LoginWithEmail from "@/components/login/loginwithemail";
import { check_access_token_is_valid } from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
export default async function MyForm() {
const token_is_valid = await check_access_token_is_valid();
if (token_is_valid) {
redirect("/login/select");
}
return <LoginWithEmail />;
}

View File

@@ -0,0 +1,40 @@
"use server";
import LoginWithPhone from "@/components/login/loginwithephone";
import Image from "next/image";
import { check_access_token_is_valid } from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
export default async function MyForm() {
const token_is_valid = await check_access_token_is_valid();
if (token_is_valid) {
redirect("/login/select");
}
return (
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm m-5">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<Image
alt="Evyos"
src="/green-house.webp"
className="mx-auto w-auto"
width={100}
height={100}
/>
<h2 className="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">
Telefon Numarası ile Giriş Yapın
</h2>
</div>
<LoginWithPhone />
<p className="mt-10 text-center text-sm/6 text-gray-500">
Üye değilmisiniz?{" "}
<a
href="#"
className="font-semibold text-slate-500 hover:text-slate-900"
>
Üye olmak için tıklayın
</a>
</p>
</div>
);
}

View File

@@ -0,0 +1,29 @@
"use server";
import React from "react";
import LoginEmployeeCard from "@/components/login/loginemployee";
import LoginOccupantCard from "@/components/login/loginoccupant";
import {
check_access_token_is_valid,
retrieve_user_type,
} from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
const SelectPage: React.FC = async () => {
const token_is_valid = await check_access_token_is_valid();
const userType = await retrieve_user_type();
if (!userType || !token_is_valid) {
redirect("/login/email");
}
return userType === "employee" ? (
<div>
<LoginEmployeeCard />
</div>
) : (
<div>
<LoginOccupantCard />
</div>
);
};
export default SelectPage;

View File

@@ -0,0 +1,26 @@
import Link from "next/link";
interface BreadcrumbProps {
pageName: string;
}
const Breadcrumb = ({ pageName }: BreadcrumbProps) => {
return (
<div className="mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<h2 className="text-title-md2 font-semibold text-black dark:text-white">
{pageName}
</h2>
<nav>
<ol className="flex items-center gap-2">
<li>
<Link className="font-medium" href="/">
Dashboard /
</Link>
</li>
<li className="font-medium text-primary">{pageName}</li>
</ol>
</nav>
</div>
);
};
export default Breadcrumb;

View File

@@ -0,0 +1,273 @@
import Breadcrumb from "../Breadcrumbs/Breadcrumb";
const Calendar = () => {
return (
<div className="mx-auto max-w-7xl">
<Breadcrumb pageName="Calendar" />
{/* <!-- ====== Calendar Section Start ====== --> */}
<div className="w-full max-w-full rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<table className="w-full">
<thead>
<tr className="grid grid-cols-7 rounded-t-sm bg-primary text-white">
<th className="flex h-15 items-center justify-center rounded-tl-sm p-1 text-xs font-semibold sm:text-base xl:p-5">
<span className="hidden lg:block"> Sunday </span>
<span className="block lg:hidden"> Sun </span>
</th>
<th className="flex h-15 items-center justify-center p-1 text-xs font-semibold sm:text-base xl:p-5">
<span className="hidden lg:block"> Monday </span>
<span className="block lg:hidden"> Mon </span>
</th>
<th className="flex h-15 items-center justify-center p-1 text-xs font-semibold sm:text-base xl:p-5">
<span className="hidden lg:block"> Tuesday </span>
<span className="block lg:hidden"> Tue </span>
</th>
<th className="flex h-15 items-center justify-center p-1 text-xs font-semibold sm:text-base xl:p-5">
<span className="hidden lg:block"> Wednesday </span>
<span className="block lg:hidden"> Wed </span>
</th>
<th className="flex h-15 items-center justify-center p-1 text-xs font-semibold sm:text-base xl:p-5">
<span className="hidden lg:block"> Thursday </span>
<span className="block lg:hidden"> Thur </span>
</th>
<th className="flex h-15 items-center justify-center p-1 text-xs font-semibold sm:text-base xl:p-5">
<span className="hidden lg:block"> Friday </span>
<span className="block lg:hidden"> Fri </span>
</th>
<th className="flex h-15 items-center justify-center rounded-tr-sm p-1 text-xs font-semibold sm:text-base xl:p-5">
<span className="hidden lg:block"> Saturday </span>
<span className="block lg:hidden"> Sat </span>
</th>
</tr>
</thead>
<tbody>
{/* <!-- Line 1 --> */}
<tr className="grid grid-cols-7">
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
1
</span>
<div className="group h-16 w-full flex-grow cursor-pointer py-1 md:h-30">
<span className="group-hover:text-primary md:hidden">
More
</span>
<div className="event invisible absolute left-2 z-99 mb-1 flex w-[200%] flex-col rounded-sm border-l-[3px] border-primary bg-gray px-3 py-1 text-left opacity-0 group-hover:visible group-hover:opacity-100 dark:bg-meta-4 md:visible md:w-[190%] md:opacity-100">
<span className="event-name text-sm font-semibold text-black dark:text-white">
Redesign Website
</span>
<span className="time text-sm font-medium text-black dark:text-white">
1 Dec - 2 Dec
</span>
</div>
</div>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
2
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
3
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
4
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
5
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
6
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
7
</span>
</td>
</tr>
{/* <!-- Line 1 --> */}
{/* <!-- Line 2 --> */}
<tr className="grid grid-cols-7">
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
8
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
9
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
10
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
11
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
12
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
13
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
14
</span>
</td>
</tr>
{/* <!-- Line 2 --> */}
{/* <!-- Line 3 --> */}
<tr className="grid grid-cols-7">
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
15
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
16
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
17
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
18
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
19
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
20
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
21
</span>
</td>
</tr>
{/* <!-- Line 3 --> */}
{/* <!-- Line 4 --> */}
<tr className="grid grid-cols-7">
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
22
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
23
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
24
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
25
</span>
<div className="group h-16 w-full flex-grow cursor-pointer py-1 md:h-30">
<span className="group-hover:text-primary md:hidden">
More
</span>
<div className="event invisible absolute left-2 z-99 mb-1 flex w-[300%] flex-col rounded-sm border-l-[3px] border-primary bg-gray px-3 py-1 text-left opacity-0 group-hover:visible group-hover:opacity-100 dark:bg-meta-4 md:visible md:w-[290%] md:opacity-100">
<span className="event-name text-sm font-semibold text-black dark:text-white">
App Design
</span>
<span className="time text-sm font-medium text-black dark:text-white">
25 Dec - 27 Dec
</span>
</div>
</div>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
26
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
27
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
28
</span>
</td>
</tr>
{/* <!-- Line 4 --> */}
{/* <!-- Line 5 --> */}
<tr className="grid grid-cols-7">
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
29
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
30
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
31
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
1
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
2
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
3
</span>
</td>
<td className="ease relative h-20 cursor-pointer border border-stroke p-2 transition duration-500 hover:bg-gray dark:border-strokedark dark:hover:bg-meta-4 md:h-25 md:p-6 xl:h-31">
<span className="font-medium text-black dark:text-white">
4
</span>
</td>
</tr>
{/* <!-- Line 5 --> */}
</tbody>
</table>
</div>
{/* <!-- ====== Calendar Section End ====== --> */}
</div>
);
};
export default Calendar;

View File

@@ -0,0 +1,77 @@
import React, { ReactNode } from "react";
interface CardDataStatsProps {
title: string;
total: string;
rate: string;
levelUp?: boolean;
levelDown?: boolean;
children: ReactNode;
}
const CardDataStats: React.FC<CardDataStatsProps> = ({
title,
total,
rate,
levelUp,
levelDown,
children,
}) => {
return (
<div className="rounded-sm border border-stroke bg-white px-7.5 py-6 shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex h-11.5 w-11.5 items-center justify-center rounded-full bg-meta-2 dark:bg-meta-4">
{children}
</div>
<div className="mt-4 flex items-end justify-between">
<div>
<h4 className="text-title-md font-bold text-black dark:text-white">
{total}
</h4>
<span className="text-sm font-medium">{title}</span>
</div>
<span
className={`flex items-center gap-1 text-sm font-medium ${
levelUp && "text-meta-3"
} ${levelDown && "text-meta-5"} `}
>
{rate}
{levelUp && (
<svg
className="fill-meta-3"
width="10"
height="11"
viewBox="0 0 10 11"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.35716 2.47737L0.908974 5.82987L5.0443e-07 4.94612L5 0.0848689L10 4.94612L9.09103 5.82987L5.64284 2.47737L5.64284 10.0849L4.35716 10.0849L4.35716 2.47737Z"
fill=""
/>
</svg>
)}
{levelDown && (
<svg
className="fill-meta-5"
width="10"
height="11"
viewBox="0 0 10 11"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.64284 7.69237L9.09102 4.33987L10 5.22362L5 10.0849L-8.98488e-07 5.22362L0.908973 4.33987L4.35716 7.69237L4.35716 0.0848701L5.64284 0.0848704L5.64284 7.69237Z"
fill=""
/>
</svg>
)}
</span>
</div>
</div>
);
};
export default CardDataStats;

View File

@@ -0,0 +1,197 @@
"use client";
import { ApexOptions } from "apexcharts";
import React from "react";
import dynamic from "next/dynamic";
const ReactApexChart = dynamic(() => import("react-apexcharts"), {
ssr: false,
});
const options: ApexOptions = {
legend: {
show: false,
position: "top",
horizontalAlign: "left",
},
colors: ["#3C50E0", "#80CAEE"],
chart: {
fontFamily: "Satoshi, sans-serif",
height: 335,
type: "area",
dropShadow: {
enabled: true,
color: "#623CEA14",
top: 10,
blur: 4,
left: 0,
opacity: 0.1,
},
toolbar: {
show: false,
},
},
responsive: [
{
breakpoint: 1024,
options: {
chart: {
height: 300,
},
},
},
{
breakpoint: 1366,
options: {
chart: {
height: 350,
},
},
},
],
stroke: {
width: [2, 2],
curve: "straight",
},
// labels: {
// show: false,
// position: "top",
// },
grid: {
xaxis: {
lines: {
show: true,
},
},
yaxis: {
lines: {
show: true,
},
},
},
dataLabels: {
enabled: false,
},
markers: {
size: 4,
colors: "#fff",
strokeColors: ["#3056D3", "#80CAEE"],
strokeWidth: 3,
strokeOpacity: 0.9,
strokeDashArray: 0,
fillOpacity: 1,
discrete: [],
hover: {
size: undefined,
sizeOffset: 5,
},
},
xaxis: {
type: "category",
categories: [
"Sep",
"Oct",
"Nov",
"Dec",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
],
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
yaxis: {
title: {
style: {
fontSize: "0px",
},
},
min: 0,
max: 100,
},
};
interface ChartOneState {
series: {
name: string;
data: number[];
}[];
}
const ChartOne: React.FC = () => {
const series = [
{
name: "Product One",
data: [23, 11, 22, 27, 13, 22, 37, 21, 44, 22, 30, 45],
},
{
name: "Product Two",
data: [30, 25, 36, 30, 45, 35, 64, 52, 59, 36, 39, 51],
},
]
return (
<div className="col-span-12 rounded-sm border border-stroke bg-white px-5 pb-5 pt-7.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:col-span-8">
<div className="flex flex-wrap items-start justify-between gap-3 sm:flex-nowrap">
<div className="flex w-full flex-wrap gap-3 sm:gap-5">
<div className="flex min-w-47.5">
<span className="mr-2 mt-1 flex h-4 w-full max-w-4 items-center justify-center rounded-full border border-primary">
<span className="block h-2.5 w-full max-w-2.5 rounded-full bg-primary"></span>
</span>
<div className="w-full">
<p className="font-semibold text-primary">Total Revenue</p>
<p className="text-sm font-medium">12.04.2022 - 12.05.2022</p>
</div>
</div>
<div className="flex min-w-47.5">
<span className="mr-2 mt-1 flex h-4 w-full max-w-4 items-center justify-center rounded-full border border-secondary">
<span className="block h-2.5 w-full max-w-2.5 rounded-full bg-secondary"></span>
</span>
<div className="w-full">
<p className="font-semibold text-secondary">Total Sales</p>
<p className="text-sm font-medium">12.04.2022 - 12.05.2022</p>
</div>
</div>
</div>
<div className="flex w-full max-w-45 justify-end">
<div className="inline-flex items-center rounded-md bg-whiter p-1.5 dark:bg-meta-4">
<button className="rounded bg-white px-3 py-1 text-xs font-medium text-black shadow-card hover:bg-white hover:shadow-card dark:bg-boxdark dark:text-white dark:hover:bg-boxdark">
Day
</button>
<button className="rounded px-3 py-1 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:text-white dark:hover:bg-boxdark">
Week
</button>
<button className="rounded px-3 py-1 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:text-white dark:hover:bg-boxdark">
Month
</button>
</div>
</div>
</div>
<div>
<div id="chartOne" className="-ml-5">
<ReactApexChart
options={options}
series={series}
type="area"
height={350}
width={"100%"}
/>
</div>
</div>
</div>
);
};
export default ChartOne;

View File

@@ -0,0 +1,149 @@
import { ApexOptions } from "apexcharts";
import React from "react";
import ReactApexChart from "react-apexcharts";
interface ChartThreeState {
series: number[];
}
const options: ApexOptions = {
chart: {
fontFamily: "Satoshi, sans-serif",
type: "donut",
},
colors: ["#3C50E0", "#6577F3", "#8FD0EF", "#0FADCF"],
labels: ["Desktop", "Tablet", "Mobile", "Unknown"],
legend: {
show: false,
position: "bottom",
},
plotOptions: {
pie: {
donut: {
size: "65%",
background: "transparent",
},
},
},
dataLabels: {
enabled: false,
},
responsive: [
{
breakpoint: 2600,
options: {
chart: {
width: 380,
},
},
},
{
breakpoint: 640,
options: {
chart: {
width: 200,
},
},
},
],
};
const ChartThree: React.FC = () => {
const series = [65, 34, 12, 56];
return (
<div className="col-span-12 rounded-sm border border-stroke bg-white px-5 pb-5 pt-7.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:col-span-5">
<div className="mb-3 justify-between gap-4 sm:flex">
<div>
<h5 className="text-xl font-semibold text-black dark:text-white">
Visitors Analytics
</h5>
</div>
<div>
<div className="relative z-20 inline-block">
<select
name=""
id=""
className="relative z-20 inline-flex appearance-none bg-transparent py-1 pl-3 pr-8 text-sm font-medium outline-none"
>
<option value="" className="dark:bg-boxdark">
Monthly
</option>
<option value="" className="dark:bg-boxdark">
Yearly
</option>
</select>
<span className="absolute right-3 top-1/2 z-10 -translate-y-1/2">
<svg
width="10"
height="6"
viewBox="0 0 10 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.47072 1.08816C0.47072 1.02932 0.500141 0.955772 0.54427 0.911642C0.647241 0.808672 0.809051 0.808672 0.912022 0.896932L4.85431 4.60386C4.92785 4.67741 5.06025 4.67741 5.14851 4.60386L9.09079 0.896932C9.19376 0.793962 9.35557 0.808672 9.45854 0.911642C9.56151 1.01461 9.5468 1.17642 9.44383 1.27939L5.50155 4.98632C5.22206 5.23639 4.78076 5.23639 4.51598 4.98632L0.558981 1.27939C0.50014 1.22055 0.47072 1.16171 0.47072 1.08816Z"
fill="#637381"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.22659 0.546578L5.00141 4.09604L8.76422 0.557869C9.08459 0.244537 9.54201 0.329403 9.79139 0.578788C10.112 0.899434 10.0277 1.36122 9.77668 1.61224L9.76644 1.62248L5.81552 5.33722C5.36257 5.74249 4.6445 5.7544 4.19352 5.32924C4.19327 5.32901 4.19377 5.32948 4.19352 5.32924L0.225953 1.61241C0.102762 1.48922 -4.20186e-08 1.31674 -3.20269e-08 1.08816C-2.40601e-08 0.905899 0.0780105 0.712197 0.211421 0.578787C0.494701 0.295506 0.935574 0.297138 1.21836 0.539529L1.22659 0.546578ZM4.51598 4.98632C4.78076 5.23639 5.22206 5.23639 5.50155 4.98632L9.44383 1.27939C9.5468 1.17642 9.56151 1.01461 9.45854 0.911642C9.35557 0.808672 9.19376 0.793962 9.09079 0.896932L5.14851 4.60386C5.06025 4.67741 4.92785 4.67741 4.85431 4.60386L0.912022 0.896932C0.809051 0.808672 0.647241 0.808672 0.54427 0.911642C0.500141 0.955772 0.47072 1.02932 0.47072 1.08816C0.47072 1.16171 0.50014 1.22055 0.558981 1.27939L4.51598 4.98632Z"
fill="#637381"
/>
</svg>
</span>
</div>
</div>
</div>
<div className="mb-2">
<div id="chartThree" className="mx-auto flex justify-center">
<ReactApexChart options={options} series={series} type="donut" />
</div>
</div>
<div className="-mx-8 flex flex-wrap items-center justify-center gap-y-3">
<div className="w-full px-8 sm:w-1/2">
<div className="flex w-full items-center">
<span className="mr-2 block h-3 w-full max-w-3 rounded-full bg-primary"></span>
<p className="flex w-full justify-between text-sm font-medium text-black dark:text-white">
<span> Desktop </span>
<span> 65% </span>
</p>
</div>
</div>
<div className="w-full px-8 sm:w-1/2">
<div className="flex w-full items-center">
<span className="mr-2 block h-3 w-full max-w-3 rounded-full bg-[#6577F3]"></span>
<p className="flex w-full justify-between text-sm font-medium text-black dark:text-white">
<span> Tablet </span>
<span> 34% </span>
</p>
</div>
</div>
<div className="w-full px-8 sm:w-1/2">
<div className="flex w-full items-center">
<span className="mr-2 block h-3 w-full max-w-3 rounded-full bg-[#8FD0EF]"></span>
<p className="flex w-full justify-between text-sm font-medium text-black dark:text-white">
<span> Mobile </span>
<span> 45% </span>
</p>
</div>
</div>
<div className="w-full px-8 sm:w-1/2">
<div className="flex w-full items-center">
<span className="mr-2 block h-3 w-full max-w-3 rounded-full bg-[#0FADCF]"></span>
<p className="flex w-full justify-between text-sm font-medium text-black dark:text-white">
<span> Unknown </span>
<span> 12% </span>
</p>
</div>
</div>
</div>
</div>
);
};
export default ChartThree;

View File

@@ -0,0 +1,151 @@
"use client";
import { ApexOptions } from "apexcharts";
import React from "react";
import dynamic from "next/dynamic";
const ReactApexChart = dynamic(() => import("react-apexcharts"), {
ssr: false,
});
const options: ApexOptions = {
colors: ["#3C50E0", "#80CAEE"],
chart: {
fontFamily: "Satoshi, sans-serif",
type: "bar",
height: 335,
stacked: true,
toolbar: {
show: false,
},
zoom: {
enabled: false,
},
},
responsive: [
{
breakpoint: 1536,
options: {
plotOptions: {
bar: {
borderRadius: 0,
columnWidth: "25%",
},
},
},
},
],
plotOptions: {
bar: {
horizontal: false,
borderRadius: 0,
columnWidth: "25%",
borderRadiusApplication: "end",
borderRadiusWhenStacked: "last",
},
},
dataLabels: {
enabled: false,
},
xaxis: {
categories: ["M", "T", "W", "T", "F", "S", "S"],
},
legend: {
position: "top",
horizontalAlign: "left",
fontFamily: "Satoshi",
fontWeight: 500,
fontSize: "14px",
markers: {
radius: 99,
},
},
fill: {
opacity: 1,
},
};
interface ChartTwoState {
series: {
name: string;
data: number[];
}[];
}
const ChartTwo: React.FC = () => {
const series = [
{
name: "Sales",
data: [44, 55, 41, 67, 22, 43, 65],
},
{
name: "Revenue",
data: [13, 23, 20, 8, 13, 27, 15],
},
];
return (
<div className="col-span-12 rounded-sm border border-stroke bg-white p-7.5 shadow-default dark:border-strokedark dark:bg-boxdark xl:col-span-4">
<div className="mb-4 justify-between gap-4 sm:flex">
<div>
<h4 className="text-xl font-semibold text-black dark:text-white">
Profit this week
</h4>
</div>
<div>
<div className="relative z-20 inline-block">
<select
name="#"
id="#"
className="relative z-20 inline-flex appearance-none bg-transparent py-1 pl-3 pr-8 text-sm font-medium outline-none"
>
<option value="" className="dark:bg-boxdark">
This Week
</option>
<option value="" className="dark:bg-boxdark">
Last Week
</option>
</select>
<span className="absolute right-3 top-1/2 z-10 -translate-y-1/2">
<svg
width="10"
height="6"
viewBox="0 0 10 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.47072 1.08816C0.47072 1.02932 0.500141 0.955772 0.54427 0.911642C0.647241 0.808672 0.809051 0.808672 0.912022 0.896932L4.85431 4.60386C4.92785 4.67741 5.06025 4.67741 5.14851 4.60386L9.09079 0.896932C9.19376 0.793962 9.35557 0.808672 9.45854 0.911642C9.56151 1.01461 9.5468 1.17642 9.44383 1.27939L5.50155 4.98632C5.22206 5.23639 4.78076 5.23639 4.51598 4.98632L0.558981 1.27939C0.50014 1.22055 0.47072 1.16171 0.47072 1.08816Z"
fill="#637381"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.22659 0.546578L5.00141 4.09604L8.76422 0.557869C9.08459 0.244537 9.54201 0.329403 9.79139 0.578788C10.112 0.899434 10.0277 1.36122 9.77668 1.61224L9.76644 1.62248L5.81552 5.33722C5.36257 5.74249 4.6445 5.7544 4.19352 5.32924C4.19327 5.32901 4.19377 5.32948 4.19352 5.32924L0.225953 1.61241C0.102762 1.48922 -4.20186e-08 1.31674 -3.20269e-08 1.08816C-2.40601e-08 0.905899 0.0780105 0.712197 0.211421 0.578787C0.494701 0.295506 0.935574 0.297138 1.21836 0.539529L1.22659 0.546578ZM4.51598 4.98632C4.78076 5.23639 5.22206 5.23639 5.50155 4.98632L9.44383 1.27939C9.5468 1.17642 9.56151 1.01461 9.45854 0.911642C9.35557 0.808672 9.19376 0.793962 9.09079 0.896932L5.14851 4.60386C5.06025 4.67741 4.92785 4.67741 4.85431 4.60386L0.912022 0.896932C0.809051 0.808672 0.647241 0.808672 0.54427 0.911642C0.500141 0.955772 0.47072 1.02932 0.47072 1.08816C0.47072 1.16171 0.50014 1.22055 0.558981 1.27939L4.51598 4.98632Z"
fill="#637381"
/>
</svg>
</span>
</div>
</div>
</div>
<div>
<div id="chartTwo" className="-mb-9 -ml-5">
<ReactApexChart
options={options}
series={series}
type="bar"
height={350}
width={"100%"}
/>
</div>
</div>
</div>
);
};
export default ChartTwo;

View File

@@ -0,0 +1,26 @@
"use client";
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
import ChartOne from "@/components/Charts/ChartOne";
import ChartTwo from "@/components/Charts/ChartTwo";
import dynamic from "next/dynamic";
import React from "react";
const ChartThree = dynamic(() => import("@/components/Charts/ChartThree"), {
ssr: false,
});
const Chart: React.FC = () => {
return (
<>
<Breadcrumb pageName="Chart" />
<div className="grid grid-cols-12 gap-4 md:gap-6 2xl:gap-7.5">
<ChartOne />
<ChartTwo />
<ChartThree />
</div>
</>
);
};
export default Chart;

View File

@@ -0,0 +1,116 @@
import Link from "next/link";
import Image from "next/image";
import { Chat } from "@/types/chat";
const chatData: Chat[] = [
{
avatar: "/images/user/user-01.png",
name: "Devid Heilo",
text: "How are you?",
time: 12,
textCount: 3,
dot: 3,
},
{
avatar: "/images/user/user-02.png",
name: "Henry Fisher",
text: "Waiting for you!",
time: 12,
textCount: 0,
dot: 1,
},
{
avatar: "/images/user/user-04.png",
name: "Jhon Doe",
text: "What's up?",
time: 32,
textCount: 0,
dot: 3,
},
{
avatar: "/images/user/user-05.png",
name: "Jane Doe",
text: "Great",
time: 32,
textCount: 2,
dot: 6,
},
{
avatar: "/images/user/user-01.png",
name: "Jhon Doe",
text: "How are you?",
time: 32,
textCount: 0,
dot: 3,
},
{
avatar: "/images/user/user-03.png",
name: "Jhon Doe",
text: "How are you?",
time: 32,
textCount: 3,
dot: 6,
},
];
const ChatCard = () => {
return (
<div className="col-span-12 rounded-sm border border-stroke bg-white py-6 shadow-default dark:border-strokedark dark:bg-boxdark xl:col-span-4">
<h4 className="mb-6 px-7.5 text-xl font-semibold text-black dark:text-white">
Chats
</h4>
<div>
{chatData.map((chat, key) => (
<Link
href="/"
className="flex items-center gap-5 px-7.5 py-3 hover:bg-gray-3 dark:hover:bg-meta-4"
key={key}
>
<div className="relative h-14 w-14 rounded-full">
<Image
width={56}
height={56}
src={chat.avatar}
alt="User"
style={{
width: "auto",
height: "auto",
}}
/>
<span
className={`absolute bottom-0 right-0 h-3.5 w-3.5 rounded-full border-2 border-white ${
chat.dot === 6 ? "bg-meta-6" : `bg-meta-${chat.dot}`
} `}
></span>
</div>
<div className="flex flex-1 items-center justify-between">
<div>
<h5 className="font-medium text-black dark:text-white">
{chat.name}
</h5>
<p>
<span className="text-sm text-black dark:text-white">
{chat.text}
</span>
<span className="text-xs"> . {chat.time} min</span>
</p>
</div>
{chat.textCount !== 0 && (
<div className="flex h-6 w-6 items-center justify-center rounded-full bg-primary">
<span className="text-sm font-medium text-white">
{" "}
{chat.textCount}
</span>
</div>
)}
</div>
</Link>
))}
</div>
</div>
);
};
export default ChatCard;

View File

@@ -0,0 +1,35 @@
import { useState } from "react";
const CheckboxFive = () => {
const [isChecked, setIsChecked] = useState<boolean>(false);
return (
<div>
<label
htmlFor="checkboxLabelFive"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="checkboxLabelFive"
className="sr-only"
onChange={() => {
setIsChecked(!isChecked);
}}
/>
<div
className={`box mr-4 flex h-5 w-5 items-center justify-center rounded-full border border-primary ${
isChecked && "!border-4"
}`}
>
<span className="h-2.5 w-2.5 rounded-full bg-white dark:bg-transparent"></span>
</div>
</div>
Checkbox Text
</label>
</div>
);
};
export default CheckboxFive;

View File

@@ -0,0 +1,41 @@
import { useState } from "react";
const CheckboxFour = () => {
const [isChecked, setIsChecked] = useState<boolean>(false);
return (
<div>
<label
htmlFor="checkboxLabelFour"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="checkboxLabelFour"
className="sr-only"
onChange={() => {
setIsChecked(!isChecked);
}}
/>
<div
className={`mr-4 flex h-5 w-5 items-center justify-center rounded-full border ${
isChecked && "border-primary"
}`}
>
<span
className={`h-2.5 w-2.5 rounded-full bg-transparent ${
isChecked && "!bg-primary"
}`}
>
{" "}
</span>
</div>
</div>
Checkbox Text
</label>
</div>
);
};
export default CheckboxFour;

View File

@@ -0,0 +1,37 @@
import { useState } from "react";
const CheckboxOne = () => {
const [isChecked, setIsChecked] = useState<boolean>(false);
return (
<div>
<label
htmlFor="checkboxLabelOne"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="checkboxLabelOne"
className="sr-only"
onChange={() => {
setIsChecked(!isChecked);
}}
/>
<div
className={`mr-4 flex h-5 w-5 items-center justify-center rounded border ${
isChecked && "border-primary bg-gray dark:bg-transparent"
}`}
>
<span
className={`h-2.5 w-2.5 rounded-sm ${isChecked && "bg-primary"}`}
></span>
</div>
</div>
Checkbox Text
</label>
</div>
);
};
export default CheckboxOne;

View File

@@ -0,0 +1,53 @@
import { useState } from "react";
const CheckboxThree = () => {
const [isChecked, setIsChecked] = useState<boolean>(false);
return (
<div>
<label
htmlFor="checkboxLabelThree"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="checkboxLabelThree"
className="sr-only"
onChange={() => {
setIsChecked(!isChecked);
}}
/>
<div
className={`box mr-4 flex h-5 w-5 items-center justify-center rounded border ${
isChecked && "border-primary bg-gray dark:bg-transparent"
}`}
>
<span
className={`text-primary opacity-0 ${
isChecked && "!opacity-100"
}`}
>
<svg
className="h-3.5 w-3.5 stroke-current"
fill="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</span>
</div>
</div>
Checkbox Text
</label>
</div>
);
};
export default CheckboxThree;

View File

@@ -0,0 +1,50 @@
import { useState } from "react";
const CheckboxTwo = () => {
const [isChecked, setIsChecked] = useState<boolean>(false);
return (
<div>
<label
htmlFor="checkboxLabelTwo"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="checkboxLabelTwo"
className="sr-only"
onChange={() => {
setIsChecked(!isChecked);
}}
/>
<div
className={`mr-4 flex h-5 w-5 items-center justify-center rounded border ${
isChecked && "border-primary bg-gray dark:bg-transparent"
}`}
>
<span className={`opacity-0 ${isChecked && "!opacity-100"}`}>
<svg
width="11"
height="8"
viewBox="0 0 11 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.0915 0.951972L10.0867 0.946075L10.0813 0.940568C9.90076 0.753564 9.61034 0.753146 9.42927 0.939309L4.16201 6.22962L1.58507 3.63469C1.40401 3.44841 1.11351 3.44879 0.932892 3.63584C0.755703 3.81933 0.755703 4.10875 0.932892 4.29224L0.932878 4.29225L0.934851 4.29424L3.58046 6.95832C3.73676 7.11955 3.94983 7.2 4.1473 7.2C4.36196 7.2 4.55963 7.11773 4.71406 6.9584L10.0468 1.60234C10.2436 1.4199 10.2421 1.1339 10.0915 0.951972ZM4.2327 6.30081L4.2317 6.2998C4.23206 6.30015 4.23237 6.30049 4.23269 6.30082L4.2327 6.30081Z"
fill="#3056D3"
stroke="#3056D3"
strokeWidth="0.4"
></path>
</svg>
</span>
</div>
</div>
Checkbox Text
</label>
</div>
);
};
export default CheckboxTwo;

View File

@@ -0,0 +1,51 @@
import React, { useRef, useEffect } from "react";
interface Props {
children: React.ReactNode;
exceptionRef?: React.RefObject<HTMLElement>;
onClick: () => void;
className?: string;
}
const ClickOutside: React.FC<Props> = ({
children,
exceptionRef,
onClick,
className,
}) => {
const wrapperRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickListener = (event: MouseEvent) => {
let clickedInside: null | boolean = false;
if (exceptionRef) {
clickedInside =
(wrapperRef.current &&
wrapperRef.current.contains(event.target as Node)) ||
(exceptionRef.current && exceptionRef.current === event.target) ||
(exceptionRef.current &&
exceptionRef.current.contains(event.target as Node));
} else {
clickedInside =
wrapperRef.current &&
wrapperRef.current.contains(event.target as Node);
}
if (!clickedInside) onClick();
};
document.addEventListener("mousedown", handleClickListener);
return () => {
document.removeEventListener("mousedown", handleClickListener);
};
}, [exceptionRef, onClick]);
return (
<div ref={wrapperRef} className={`${className || ""}`}>
{children}
</div>
);
};
export default ClickOutside;

View File

@@ -0,0 +1,122 @@
"use client";
import dynamic from "next/dynamic";
import React from "react";
import ChartOne from "../Charts/ChartOne";
import ChartTwo from "../Charts/ChartTwo";
import ChatCard from "../Chat/ChatCard";
import TableOne from "../Tables/TableOne";
import CardDataStats from "../CardDataStats";
const MapOne = dynamic(() => import("@/components/Maps/MapOne"), {
ssr: false,
});
const ChartThree = dynamic(() => import("@/components/Charts/ChartThree"), {
ssr: false,
});
const ECommerce: React.FC = () => {
return (
<>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 xl:grid-cols-4 2xl:gap-7.5">
<CardDataStats title="Total views" total="$3.456K" rate="0.43%" levelUp>
<svg
className="fill-primary dark:fill-white"
width="22"
height="16"
viewBox="0 0 22 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11 15.1156C4.19376 15.1156 0.825012 8.61876 0.687512 8.34376C0.584387 8.13751 0.584387 7.86251 0.687512 7.65626C0.825012 7.38126 4.19376 0.918762 11 0.918762C17.8063 0.918762 21.175 7.38126 21.3125 7.65626C21.4156 7.86251 21.4156 8.13751 21.3125 8.34376C21.175 8.61876 17.8063 15.1156 11 15.1156ZM2.26876 8.00001C3.02501 9.27189 5.98126 13.5688 11 13.5688C16.0188 13.5688 18.975 9.27189 19.7313 8.00001C18.975 6.72814 16.0188 2.43126 11 2.43126C5.98126 2.43126 3.02501 6.72814 2.26876 8.00001Z"
fill=""
/>
<path
d="M11 10.9219C9.38438 10.9219 8.07812 9.61562 8.07812 8C8.07812 6.38438 9.38438 5.07812 11 5.07812C12.6156 5.07812 13.9219 6.38438 13.9219 8C13.9219 9.61562 12.6156 10.9219 11 10.9219ZM11 6.625C10.2437 6.625 9.625 7.24375 9.625 8C9.625 8.75625 10.2437 9.375 11 9.375C11.7563 9.375 12.375 8.75625 12.375 8C12.375 7.24375 11.7563 6.625 11 6.625Z"
fill=""
/>
</svg>
</CardDataStats>
<CardDataStats title="Total Profit" total="$45,2K" rate="4.35%" levelUp>
<svg
className="fill-primary dark:fill-white"
width="20"
height="22"
viewBox="0 0 20 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.7531 16.4312C10.3781 16.4312 9.27808 17.5312 9.27808 18.9062C9.27808 20.2812 10.3781 21.3812 11.7531 21.3812C13.1281 21.3812 14.2281 20.2812 14.2281 18.9062C14.2281 17.5656 13.0937 16.4312 11.7531 16.4312ZM11.7531 19.8687C11.2375 19.8687 10.825 19.4562 10.825 18.9406C10.825 18.425 11.2375 18.0125 11.7531 18.0125C12.2687 18.0125 12.6812 18.425 12.6812 18.9406C12.6812 19.4219 12.2343 19.8687 11.7531 19.8687Z"
fill=""
/>
<path
d="M5.22183 16.4312C3.84683 16.4312 2.74683 17.5312 2.74683 18.9062C2.74683 20.2812 3.84683 21.3812 5.22183 21.3812C6.59683 21.3812 7.69683 20.2812 7.69683 18.9062C7.69683 17.5656 6.56245 16.4312 5.22183 16.4312ZM5.22183 19.8687C4.7062 19.8687 4.2937 19.4562 4.2937 18.9406C4.2937 18.425 4.7062 18.0125 5.22183 18.0125C5.73745 18.0125 6.14995 18.425 6.14995 18.9406C6.14995 19.4219 5.73745 19.8687 5.22183 19.8687Z"
fill=""
/>
<path
d="M19.0062 0.618744H17.15C16.325 0.618744 15.6031 1.23749 15.5 2.06249L14.95 6.01562H1.37185C1.0281 6.01562 0.684353 6.18749 0.443728 6.46249C0.237478 6.73749 0.134353 7.11562 0.237478 7.45937C0.237478 7.49374 0.237478 7.49374 0.237478 7.52812L2.36873 13.9562C2.50623 14.4375 2.9531 14.7812 3.46873 14.7812H12.9562C14.2281 14.7812 15.3281 13.8187 15.5 12.5469L16.9437 2.26874C16.9437 2.19999 17.0125 2.16562 17.0812 2.16562H18.9375C19.35 2.16562 19.7281 1.82187 19.7281 1.37499C19.7281 0.928119 19.4187 0.618744 19.0062 0.618744ZM14.0219 12.3062C13.9531 12.8219 13.5062 13.2 12.9906 13.2H3.7781L1.92185 7.56249H14.7094L14.0219 12.3062Z"
fill=""
/>
</svg>
</CardDataStats>
<CardDataStats title="Total Product" total="2.450" rate="2.59%" levelUp>
<svg
className="fill-primary dark:fill-white"
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M21.1063 18.0469L19.3875 3.23126C19.2157 1.71876 17.9438 0.584381 16.3969 0.584381H5.56878C4.05628 0.584381 2.78441 1.71876 2.57816 3.23126L0.859406 18.0469C0.756281 18.9063 1.03128 19.7313 1.61566 20.3844C2.20003 21.0375 2.99066 21.3813 3.85003 21.3813H18.1157C18.975 21.3813 19.8 21.0031 20.35 20.3844C20.9 19.7656 21.2094 18.9063 21.1063 18.0469ZM19.2157 19.3531C18.9407 19.6625 18.5625 19.8344 18.15 19.8344H3.85003C3.43753 19.8344 3.05941 19.6625 2.78441 19.3531C2.50941 19.0438 2.37191 18.6313 2.44066 18.2188L4.12503 3.43751C4.19378 2.71563 4.81253 2.16563 5.56878 2.16563H16.4313C17.1532 2.16563 17.7719 2.71563 17.875 3.43751L19.5938 18.2531C19.6282 18.6656 19.4907 19.0438 19.2157 19.3531Z"
fill=""
/>
<path
d="M14.3345 5.29375C13.922 5.39688 13.647 5.80938 13.7501 6.22188C13.7845 6.42813 13.8189 6.63438 13.8189 6.80625C13.8189 8.35313 12.547 9.625 11.0001 9.625C9.45327 9.625 8.1814 8.35313 8.1814 6.80625C8.1814 6.6 8.21577 6.42813 8.25015 6.22188C8.35327 5.80938 8.07827 5.39688 7.66577 5.29375C7.25327 5.19063 6.84077 5.46563 6.73765 5.87813C6.6689 6.1875 6.63452 6.49688 6.63452 6.80625C6.63452 9.2125 8.5939 11.1719 11.0001 11.1719C13.4064 11.1719 15.3658 9.2125 15.3658 6.80625C15.3658 6.49688 15.3314 6.1875 15.2626 5.87813C15.1595 5.46563 14.747 5.225 14.3345 5.29375Z"
fill=""
/>
</svg>
</CardDataStats>
<CardDataStats title="Total Users" total="3.456" rate="0.95%" levelDown>
<svg
className="fill-primary dark:fill-white"
width="22"
height="18"
viewBox="0 0 22 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.18418 8.03751C9.31543 8.03751 11.0686 6.35313 11.0686 4.25626C11.0686 2.15938 9.31543 0.475006 7.18418 0.475006C5.05293 0.475006 3.2998 2.15938 3.2998 4.25626C3.2998 6.35313 5.05293 8.03751 7.18418 8.03751ZM7.18418 2.05626C8.45605 2.05626 9.52168 3.05313 9.52168 4.29063C9.52168 5.52813 8.49043 6.52501 7.18418 6.52501C5.87793 6.52501 4.84668 5.52813 4.84668 4.29063C4.84668 3.05313 5.9123 2.05626 7.18418 2.05626Z"
fill=""
/>
<path
d="M15.8124 9.6875C17.6687 9.6875 19.1468 8.24375 19.1468 6.42188C19.1468 4.6 17.6343 3.15625 15.8124 3.15625C13.9905 3.15625 12.478 4.6 12.478 6.42188C12.478 8.24375 13.9905 9.6875 15.8124 9.6875ZM15.8124 4.7375C16.8093 4.7375 17.5999 5.49375 17.5999 6.45625C17.5999 7.41875 16.8093 8.175 15.8124 8.175C14.8155 8.175 14.0249 7.41875 14.0249 6.45625C14.0249 5.49375 14.8155 4.7375 15.8124 4.7375Z"
fill=""
/>
<path
d="M15.9843 10.0313H15.6749C14.6437 10.0313 13.6468 10.3406 12.7874 10.8563C11.8593 9.61876 10.3812 8.79376 8.73115 8.79376H5.67178C2.85303 8.82814 0.618652 11.0625 0.618652 13.8469V16.3219C0.618652 16.975 1.13428 17.4906 1.7874 17.4906H20.2468C20.8999 17.4906 21.4499 16.9406 21.4499 16.2875V15.4625C21.4155 12.4719 18.9749 10.0313 15.9843 10.0313ZM2.16553 15.9438V13.8469C2.16553 11.9219 3.74678 10.3406 5.67178 10.3406H8.73115C10.6562 10.3406 12.2374 11.9219 12.2374 13.8469V15.9438H2.16553V15.9438ZM19.8687 15.9438H13.7499V13.8469C13.7499 13.2969 13.6468 12.7469 13.4749 12.2313C14.0937 11.7844 14.8499 11.5781 15.6405 11.5781H15.9499C18.0812 11.5781 19.8343 13.3313 19.8343 15.4625V15.9438H19.8687Z"
fill=""
/>
</svg>
</CardDataStats>
</div>
<div className="mt-4 grid grid-cols-12 gap-4 md:mt-6 md:gap-6 2xl:mt-7.5 2xl:gap-7.5">
<ChartOne />
<ChartTwo />
<ChartThree />
<MapOne />
<div className="col-span-12 xl:col-span-8">
<TableOne />
</div>
<ChatCard />
</div>
</>
);
};
export default ECommerce;

View File

@@ -0,0 +1,128 @@
import { useEffect, useRef, useState } from "react";
const DropdownDefault = () => {
const [dropdownOpen, setDropdownOpen] = useState(false);
const trigger = useRef<any>(null);
const dropdown = useRef<any>(null);
// close on click outside
useEffect(() => {
const clickHandler = ({ target }: MouseEvent) => {
if (!dropdown.current) return;
if (
!dropdownOpen ||
dropdown.current.contains(target) ||
trigger.current.contains(target)
)
return;
setDropdownOpen(false);
};
document.addEventListener("click", clickHandler);
return () => document.removeEventListener("click", clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }: KeyboardEvent) => {
if (!dropdownOpen || keyCode !== 27) return;
setDropdownOpen(false);
};
document.addEventListener("keydown", keyHandler);
return () => document.removeEventListener("keydown", keyHandler);
});
return (
<div className="relative flex">
<button
className="text-[#98A6AD] hover:text-body"
ref={trigger}
onClick={() => setDropdownOpen(!dropdownOpen)}
>
<svg
className="fill-current"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.25 11.25C3.49264 11.25 4.5 10.2426 4.5 9C4.5 7.75736 3.49264 6.75 2.25 6.75C1.00736 6.75 0 7.75736 0 9C0 10.2426 1.00736 11.25 2.25 11.25Z"
fill=""
/>
<path
d="M9 11.25C10.2426 11.25 11.25 10.2426 11.25 9C11.25 7.75736 10.2426 6.75 9 6.75C7.75736 6.75 6.75 7.75736 6.75 9C6.75 10.2426 7.75736 11.25 9 11.25Z"
fill=""
/>
<path
d="M15.75 11.25C16.9926 11.25 18 10.2426 18 9C18 7.75736 16.9926 6.75 15.75 6.75C14.5074 6.75 13.5 7.75736 13.5 9C13.5 10.2426 14.5074 11.25 15.75 11.25Z"
fill=""
/>
</svg>
</button>
<div
ref={dropdown}
onFocus={() => setDropdownOpen(true)}
onBlur={() => setDropdownOpen(false)}
className={`absolute right-0 top-full z-40 w-40 space-y-1 rounded-sm border border-stroke bg-white p-1.5 shadow-default dark:border-strokedark dark:bg-boxdark ${
dropdownOpen === true ? "block" : "hidden"
}`}
>
<button className="flex w-full items-center gap-2 rounded-sm px-4 py-1.5 text-left text-sm hover:bg-gray dark:hover:bg-meta-4">
<svg
className="fill-current"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_62_9787)">
<path
d="M15.55 2.97499C15.55 2.77499 15.475 2.57499 15.325 2.42499C15.025 2.12499 14.725 1.82499 14.45 1.52499C14.175 1.24999 13.925 0.974987 13.65 0.724987C13.525 0.574987 13.375 0.474986 13.175 0.449986C12.95 0.424986 12.75 0.474986 12.575 0.624987L10.875 2.32499H2.02495C1.17495 2.32499 0.449951 3.02499 0.449951 3.89999V14C0.449951 14.85 1.14995 15.575 2.02495 15.575H12.15C13 15.575 13.725 14.875 13.725 14V5.12499L15.35 3.49999C15.475 3.34999 15.55 3.17499 15.55 2.97499ZM8.19995 8.99999C8.17495 9.02499 8.17495 9.02499 8.14995 9.02499L6.34995 9.62499L6.94995 7.82499C6.94995 7.79999 6.97495 7.79999 6.97495 7.77499L11.475 3.27499L12.725 4.49999L8.19995 8.99999ZM12.575 14C12.575 14.25 12.375 14.45 12.125 14.45H2.02495C1.77495 14.45 1.57495 14.25 1.57495 14V3.87499C1.57495 3.62499 1.77495 3.42499 2.02495 3.42499H9.72495L6.17495 6.99999C6.04995 7.12499 5.92495 7.29999 5.87495 7.49999L4.94995 10.3C4.87495 10.5 4.92495 10.675 5.02495 10.85C5.09995 10.95 5.24995 11.1 5.52495 11.1H5.62495L8.49995 10.15C8.67495 10.1 8.84995 9.97499 8.97495 9.84999L12.575 6.24999V14ZM13.5 3.72499L12.25 2.49999L13.025 1.72499C13.225 1.92499 14.05 2.74999 14.25 2.97499L13.5 3.72499Z"
fill=""
/>
</g>
<defs>
<clipPath id="clip0_62_9787">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
Edit
</button>
<button className="flex w-full items-center gap-2 rounded-sm px-4 py-1.5 text-left text-sm hover:bg-gray dark:hover:bg-meta-4">
<svg
className="fill-current"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.225 2.20005H10.3V1.77505C10.3 1.02505 9.70005 0.425049 8.95005 0.425049H7.02505C6.27505 0.425049 5.67505 1.02505 5.67505 1.77505V2.20005H3.75005C3.02505 2.20005 2.42505 2.80005 2.42505 3.52505V4.27505C2.42505 4.82505 2.75005 5.27505 3.22505 5.47505L3.62505 13.75C3.67505 14.775 4.52505 15.575 5.55005 15.575H10.4C11.425 15.575 12.275 14.775 12.325 13.75L12.75 5.45005C13.225 5.25005 13.55 4.77505 13.55 4.25005V3.50005C13.55 2.80005 12.95 2.20005 12.225 2.20005ZM6.82505 1.77505C6.82505 1.65005 6.92505 1.55005 7.05005 1.55005H8.97505C9.10005 1.55005 9.20005 1.65005 9.20005 1.77505V2.20005H6.85005V1.77505H6.82505ZM3.57505 3.52505C3.57505 3.42505 3.65005 3.32505 3.77505 3.32505H12.225C12.325 3.32505 12.425 3.40005 12.425 3.52505V4.27505C12.425 4.37505 12.35 4.47505 12.225 4.47505H3.77505C3.67505 4.47505 3.57505 4.40005 3.57505 4.27505V3.52505V3.52505ZM10.425 14.45H5.57505C5.15005 14.45 4.80005 14.125 4.77505 13.675L4.40005 5.57505H11.625L11.25 13.675C11.2 14.1 10.85 14.45 10.425 14.45Z"
fill=""
/>
<path
d="M8.00005 8.1001C7.70005 8.1001 7.42505 8.3501 7.42505 8.6751V11.8501C7.42505 12.1501 7.67505 12.4251 8.00005 12.4251C8.30005 12.4251 8.57505 12.1751 8.57505 11.8501V8.6751C8.57505 8.3501 8.30005 8.1001 8.00005 8.1001Z"
fill=""
/>
<path
d="M9.99994 8.60004C9.67494 8.57504 9.42494 8.80004 9.39994 9.12504L9.24994 11.325C9.22494 11.625 9.44994 11.9 9.77494 11.925C9.79994 11.925 9.79994 11.925 9.82494 11.925C10.1249 11.925 10.3749 11.7 10.3749 11.4L10.5249 9.20004C10.5249 8.87504 10.2999 8.62504 9.99994 8.60004Z"
fill=""
/>
<path
d="M5.97497 8.60004C5.67497 8.62504 5.42497 8.90004 5.44997 9.20004L5.62497 11.4C5.64997 11.7 5.89997 11.925 6.17497 11.925C6.19997 11.925 6.19997 11.925 6.22497 11.925C6.52497 11.9 6.77497 11.625 6.74997 11.325L6.57497 9.12504C6.57497 8.80004 6.29997 8.57504 5.97497 8.60004Z"
fill=""
/>
</svg>
Delete
</button>
</div>
</div>
);
};
export default DropdownDefault;

View File

@@ -0,0 +1,50 @@
import flatpickr from "flatpickr";
import { useEffect } from "react";
const DatePickerOne = () => {
useEffect(() => {
// Init flatpickr
flatpickr(".form-datepicker", {
mode: "single",
static: true,
monthSelectorType: "static",
dateFormat: "M j, Y",
prevArrow:
'<svg className="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M5.4 10.8l1.4-1.4-4-4 4-4L5.4 0 0 5.4z" /></svg>',
nextArrow:
'<svg className="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M1.4 10.8L0 9.4l4-4-4-4L1.4 0l5.4 5.4z" /></svg>',
});
}, []);
return (
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Date picker
</label>
<div className="relative">
<input
className="form-datepicker w-full rounded border-[1.5px] border-stroke bg-transparent px-5 py-3 font-normal outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary"
placeholder="mm/dd/yyyy"
data-class="flatpickr-right"
/>
<div className="pointer-events-none absolute inset-0 left-auto right-5 flex items-center">
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.7504 2.9812H14.2879V2.36245C14.2879 2.02495 14.0066 1.71558 13.641 1.71558C13.2754 1.71558 12.9941 1.99683 12.9941 2.36245V2.9812H4.97852V2.36245C4.97852 2.02495 4.69727 1.71558 4.33164 1.71558C3.96602 1.71558 3.68477 1.99683 3.68477 2.36245V2.9812H2.25039C1.29414 2.9812 0.478516 3.7687 0.478516 4.75308V14.5406C0.478516 15.4968 1.26602 16.3125 2.25039 16.3125H15.7504C16.7066 16.3125 17.5223 15.525 17.5223 14.5406V4.72495C17.5223 3.7687 16.7066 2.9812 15.7504 2.9812ZM1.77227 8.21245H4.16289V10.9968H1.77227V8.21245ZM5.42852 8.21245H8.38164V10.9968H5.42852V8.21245ZM8.38164 12.2625V15.0187H5.42852V12.2625H8.38164V12.2625ZM9.64727 12.2625H12.6004V15.0187H9.64727V12.2625ZM9.64727 10.9968V8.21245H12.6004V10.9968H9.64727ZM13.8379 8.21245H16.2285V10.9968H13.8379V8.21245ZM2.25039 4.24683H3.71289V4.83745C3.71289 5.17495 3.99414 5.48433 4.35977 5.48433C4.72539 5.48433 5.00664 5.20308 5.00664 4.83745V4.24683H13.0504V4.83745C13.0504 5.17495 13.3316 5.48433 13.6973 5.48433C14.0629 5.48433 14.3441 5.20308 14.3441 4.83745V4.24683H15.7504C16.0316 4.24683 16.2566 4.47183 16.2566 4.75308V6.94683H1.77227V4.75308C1.77227 4.47183 1.96914 4.24683 2.25039 4.24683ZM1.77227 14.5125V12.2343H4.16289V14.9906H2.25039C1.96914 15.0187 1.77227 14.7937 1.77227 14.5125ZM15.7504 15.0187H13.8379V12.2625H16.2285V14.5406C16.2566 14.7937 16.0316 15.0187 15.7504 15.0187Z"
fill="#64748B"
/>
</svg>
</div>
</div>
</div>
);
};
export default DatePickerOne;

View File

@@ -0,0 +1,50 @@
import flatpickr from "flatpickr";
import { useEffect } from "react";
const DatePickerTwo = () => {
useEffect(() => {
// Init flatpickr
flatpickr(".form-datepicker", {
mode: "single",
static: true,
monthSelectorType: "static",
dateFormat: "M j, Y",
prevArrow:
'<svg className="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M5.4 10.8l1.4-1.4-4-4 4-4L5.4 0 0 5.4z" /></svg>',
nextArrow:
'<svg className="fill-current" width="7" height="11" viewBox="0 0 7 11"><path d="M1.4 10.8L0 9.4l4-4-4-4L1.4 0l5.4 5.4z" /></svg>',
});
}, []);
return (
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Select date
</label>
<div className="relative">
<input
className="form-datepicker w-full rounded border-[1.5px] border-stroke bg-transparent px-5 py-3 font-normal outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary"
placeholder="mm/dd/yyyy"
data-class="flatpickr-right"
/>
<div className="pointer-events-none absolute inset-0 left-auto right-5 flex items-center">
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.0002 12.8249C8.83145 12.8249 8.69082 12.7687 8.5502 12.6562L2.08145 6.2999C1.82832 6.04678 1.82832 5.65303 2.08145 5.3999C2.33457 5.14678 2.72832 5.14678 2.98145 5.3999L9.0002 11.278L15.0189 5.34365C15.2721 5.09053 15.6658 5.09053 15.9189 5.34365C16.1721 5.59678 16.1721 5.99053 15.9189 6.24365L9.45019 12.5999C9.30957 12.7405 9.16895 12.8249 9.0002 12.8249Z"
fill="#64748B"
/>
</svg>
</div>
</div>
</div>
);
};
export default DatePickerTwo;

View File

@@ -0,0 +1,226 @@
import React, { useState, useEffect, useRef } from "react";
interface Option {
value: string;
text: string;
selected: boolean;
element?: HTMLElement;
}
interface DropdownProps {
id: string;
}
const MultiSelect: React.FC<DropdownProps> = ({ id }) => {
const [options, setOptions] = useState<Option[]>([]);
const [selected, setSelected] = useState<number[]>([]);
const [show, setShow] = useState(false);
const dropdownRef = useRef<any>(null);
const trigger = useRef<any>(null);
useEffect(() => {
const loadOptions = () => {
const select = document.getElementById(id) as HTMLSelectElement | null;
if (select) {
const newOptions: Option[] = [];
for (let i = 0; i < select.options.length; i++) {
newOptions.push({
value: select.options[i].value,
text: select.options[i].innerText,
selected: select.options[i].hasAttribute("selected"),
});
}
setOptions(newOptions);
}
};
loadOptions();
}, [id]);
const open = () => {
setShow(true);
};
const isOpen = () => {
return show === true;
};
const select = (index: number, event: React.MouseEvent) => {
const newOptions = [...options];
if (!newOptions[index].selected) {
newOptions[index].selected = true;
newOptions[index].element = event.currentTarget as HTMLElement;
setSelected([...selected, index]);
} else {
const selectedIndex = selected.indexOf(index);
if (selectedIndex !== -1) {
newOptions[index].selected = false;
setSelected(selected.filter((i) => i !== index));
}
}
setOptions(newOptions);
};
const remove = (index: number) => {
const newOptions = [...options];
const selectedIndex = selected.indexOf(index);
if (selectedIndex !== -1) {
newOptions[index].selected = false;
setSelected(selected.filter((i) => i !== index));
setOptions(newOptions);
}
};
const selectedValues = () => {
return selected.map((option) => options[option].value);
};
useEffect(() => {
const clickHandler = ({ target }: MouseEvent) => {
if (!dropdownRef.current) return;
if (
!show ||
dropdownRef.current.contains(target) ||
trigger.current.contains(target)
)
return;
setShow(false);
};
document.addEventListener("click", clickHandler);
return () => document.removeEventListener("click", clickHandler);
});
return (
<div className="relative z-50">
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Multiselect Dropdown
</label>
<div>
<select className="hidden" id={id}>
<option value="1">Option 2</option>
<option value="2">Option 3</option>
<option value="3">Option 4</option>
<option value="4">Option 5</option>
</select>
<div className="flex flex-col items-center">
<input name="values" type="hidden" defaultValue={selectedValues()} />
<div className="relative z-20 inline-block w-full">
<div className="relative flex flex-col items-center">
<div ref={trigger} onClick={open} className="w-full">
<div className="mb-2 flex rounded border border-stroke py-2 pl-3 pr-3 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input">
<div className="flex flex-auto flex-wrap gap-3">
{selected.map((index) => (
<div
key={index}
className="my-1.5 flex items-center justify-center rounded border-[.5px] border-stroke bg-gray px-2.5 py-1.5 text-sm font-medium dark:border-strokedark dark:bg-white/30"
>
<div className="max-w-full flex-initial">
{options[index].text}
</div>
<div className="flex flex-auto flex-row-reverse">
<div
onClick={() => remove(index)}
className="cursor-pointer pl-2 hover:text-danger"
>
<svg
className="fill-current"
role="button"
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.35355 3.35355C9.54882 3.15829 9.54882 2.84171 9.35355 2.64645C9.15829 2.45118 8.84171 2.45118 8.64645 2.64645L6 5.29289L3.35355 2.64645C3.15829 2.45118 2.84171 2.45118 2.64645 2.64645C2.45118 2.84171 2.45118 3.15829 2.64645 3.35355L5.29289 6L2.64645 8.64645C2.45118 8.84171 2.45118 9.15829 2.64645 9.35355C2.84171 9.54882 3.15829 9.54882 3.35355 9.35355L6 6.70711L8.64645 9.35355C8.84171 9.54882 9.15829 9.54882 9.35355 9.35355C9.54882 9.15829 9.54882 8.84171 9.35355 8.64645L6.70711 6L9.35355 3.35355Z"
fill="currentColor"
></path>
</svg>
</div>
</div>
</div>
))}
{selected.length === 0 && (
<div className="flex-1">
<input
placeholder="Select an option"
className="h-full w-full appearance-none bg-transparent p-1 px-2 outline-none"
defaultValue={selectedValues()}
/>
</div>
)}
</div>
<div className="flex w-8 items-center py-1 pl-1 pr-1">
<button
type="button"
onClick={open}
className="h-6 w-6 cursor-pointer outline-none focus:outline-none"
>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z"
fill="#637381"
></path>
</g>
</svg>
</button>
</div>
</div>
</div>
<div className="w-full px-4">
<div
className={`max-h-select absolute left-0 top-full z-40 w-full overflow-y-auto rounded bg-white shadow dark:bg-form-input ${
isOpen() ? "" : "hidden"
}`}
ref={dropdownRef}
onFocus={() => setShow(true)}
onBlur={() => setShow(false)}
>
<div className="flex w-full flex-col">
{options.map((option, index) => (
<div key={index}>
<div
className="w-full cursor-pointer rounded-t border-b border-stroke hover:bg-primary/5 dark:border-form-strokedark"
onClick={(event) => select(index, event)}
>
<div
className={`relative flex w-full items-center border-l-2 border-transparent p-2 pl-2 ${
option.selected ? "border-primary" : ""
}`}
>
<div className="flex w-full items-center">
<div className="mx-2 leading-6">
{option.text}
</div>
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default MultiSelect;

View File

@@ -0,0 +1,206 @@
"use client";
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
import CheckboxFive from "@/components/Checkboxes/CheckboxFive";
import CheckboxFour from "@/components/Checkboxes/CheckboxFour";
import CheckboxOne from "@/components/Checkboxes/CheckboxOne";
import CheckboxThree from "@/components/Checkboxes/CheckboxThree";
import CheckboxTwo from "@/components/Checkboxes/CheckboxTwo";
import SwitcherFour from "@/components/Switchers/SwitcherFour";
import SwitcherOne from "@/components/Switchers/SwitcherOne";
import SwitcherThree from "@/components/Switchers/SwitcherThree";
import SwitcherTwo from "@/components/Switchers/SwitcherTwo";
import DatePickerTwo from "@/components/FormElements/DatePicker/DatePickerTwo";
import DatePickerOne from "@/components/FormElements/DatePicker/DatePickerOne";
import MultiSelect from "@/components/FormElements/MultiSelect";
import SelectGroupTwo from "@/components/SelectGroup/SelectGroupTwo";
const FormElements = () => {
return (
<>
<Breadcrumb pageName="FormElements" />
<div className="grid grid-cols-1 gap-9 sm:grid-cols-2">
<div className="flex flex-col gap-9">
{/* <!-- Input Fields --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke px-6.5 py-4 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Input Fields
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Default Input
</label>
<input
type="text"
placeholder="Default Input"
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5 py-3 text-black outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
/>
</div>
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Active Input
</label>
<input
type="text"
placeholder="Active Input"
className="w-full rounded-lg border-[1.5px] border-primary bg-transparent px-5 py-3 text-black outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:bg-form-input dark:text-white"
/>
</div>
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Disabled label
</label>
<input
type="text"
placeholder="Disabled label"
disabled
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5 py-3 text-black outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary dark:disabled:bg-black"
/>
</div>
</div>
</div>
{/* <!-- Toggle switch input --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke px-6.5 py-4 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Toggle switch input
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<SwitcherOne />
<SwitcherTwo />
<SwitcherThree />
<SwitcherFour />
</div>
</div>
{/* <!-- Time and date --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke px-6.5 py-4 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Time and date
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<DatePickerOne />
<DatePickerTwo />
</div>
</div>
{/* <!-- File upload --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke px-6.5 py-4 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
File upload
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Attach file
</label>
<input
type="file"
className="w-full cursor-pointer rounded-lg border-[1.5px] border-stroke bg-transparent outline-none transition file:mr-5 file:border-collapse file:cursor-pointer file:border-0 file:border-r file:border-solid file:border-stroke file:bg-whiter file:px-5 file:py-3 file:hover:bg-primary file:hover:bg-opacity-10 focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:file:border-form-strokedark dark:file:bg-white/30 dark:file:text-white dark:focus:border-primary"
/>
</div>
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Attach file
</label>
<input
type="file"
className="w-full rounded-md border border-stroke p-3 outline-none transition file:mr-4 file:rounded file:border-[0.5px] file:border-stroke file:bg-[#EEEEEE] file:px-2.5 file:py-1 file:text-sm focus:border-primary file:focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:file:border-strokedark dark:file:bg-white/30 dark:file:text-white"
/>
</div>
</div>
</div>
</div>
<div className="flex flex-col gap-9">
{/* <!-- Textarea Fields --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke px-6.5 py-4 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Textarea Fields
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Default textarea
</label>
<textarea
rows={6}
placeholder="Default textarea"
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5 py-3 text-black outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
></textarea>
</div>
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Active textarea
</label>
<textarea
rows={6}
placeholder="Active textarea"
className="w-full rounded-lg border-[1.5px] border-primary bg-transparent px-5 py-3 text-black outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:bg-form-input dark:text-white"
></textarea>
</div>
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Disabled textarea
</label>
<textarea
rows={6}
disabled
placeholder="Disabled textarea"
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5 py-3 text-black outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary dark:disabled:bg-black"
></textarea>
</div>
</div>
</div>
{/* <!-- Checkbox and radio --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke px-6.5 py-4 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Checkbox and radio
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<CheckboxOne />
<CheckboxTwo />
<CheckboxThree />
<CheckboxFour />
<CheckboxFive />
</div>
</div>
{/* <!-- Select input --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke px-6.5 py-4 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Select input
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<SelectGroupTwo />
<MultiSelect id="multiSelect" />
</div>
</div>
</div>
</div>
</>
);
};
export default FormElements;

View File

@@ -0,0 +1,65 @@
import useColorMode from "@/hooks/useColorMode";
const DarkModeSwitcher = () => {
const [colorMode, setColorMode] = useColorMode();
return (
<li>
<label
className={`relative m-0 block h-7.5 w-14 rounded-full ${
colorMode === "dark" ? "bg-primary" : "bg-stroke"
}`}
>
<input
type="checkbox"
onChange={() => {
if (typeof setColorMode === "function") {
setColorMode(colorMode === "light" ? "dark" : "light");
}
}}
className="dur absolute top-0 z-50 m-0 h-full w-full cursor-pointer opacity-0"
/>
<span
className={`absolute left-[3px] top-1/2 flex h-6 w-6 -translate-y-1/2 translate-x-0 items-center justify-center rounded-full bg-white shadow-switcher duration-75 ease-linear ${
colorMode === "dark" && "!right-[3px] !translate-x-full"
}`}
>
<span className="dark:hidden">
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.99992 12.6666C10.5772 12.6666 12.6666 10.5772 12.6666 7.99992C12.6666 5.42259 10.5772 3.33325 7.99992 3.33325C5.42259 3.33325 3.33325 5.42259 3.33325 7.99992C3.33325 10.5772 5.42259 12.6666 7.99992 12.6666Z"
fill="#969AA1"
/>
<path
d="M8.00008 15.3067C7.63341 15.3067 7.33342 15.0334 7.33342 14.6667V14.6134C7.33342 14.2467 7.63341 13.9467 8.00008 13.9467C8.36675 13.9467 8.66675 14.2467 8.66675 14.6134C8.66675 14.9801 8.36675 15.3067 8.00008 15.3067ZM12.7601 13.4267C12.5867 13.4267 12.4201 13.3601 12.2867 13.2334L12.2001 13.1467C11.9401 12.8867 11.9401 12.4667 12.2001 12.2067C12.4601 11.9467 12.8801 11.9467 13.1401 12.2067L13.2267 12.2934C13.4867 12.5534 13.4867 12.9734 13.2267 13.2334C13.1001 13.3601 12.9334 13.4267 12.7601 13.4267ZM3.24008 13.4267C3.06675 13.4267 2.90008 13.3601 2.76675 13.2334C2.50675 12.9734 2.50675 12.5534 2.76675 12.2934L2.85342 12.2067C3.11342 11.9467 3.53341 11.9467 3.79341 12.2067C4.05341 12.4667 4.05341 12.8867 3.79341 13.1467L3.70675 13.2334C3.58008 13.3601 3.40675 13.4267 3.24008 13.4267ZM14.6667 8.66675H14.6134C14.2467 8.66675 13.9467 8.36675 13.9467 8.00008C13.9467 7.63341 14.2467 7.33342 14.6134 7.33342C14.9801 7.33342 15.3067 7.63341 15.3067 8.00008C15.3067 8.36675 15.0334 8.66675 14.6667 8.66675ZM1.38675 8.66675H1.33341C0.966748 8.66675 0.666748 8.36675 0.666748 8.00008C0.666748 7.63341 0.966748 7.33342 1.33341 7.33342C1.70008 7.33342 2.02675 7.63341 2.02675 8.00008C2.02675 8.36675 1.75341 8.66675 1.38675 8.66675ZM12.6734 3.99341C12.5001 3.99341 12.3334 3.92675 12.2001 3.80008C11.9401 3.54008 11.9401 3.12008 12.2001 2.86008L12.2867 2.77341C12.5467 2.51341 12.9667 2.51341 13.2267 2.77341C13.4867 3.03341 13.4867 3.45341 13.2267 3.71341L13.1401 3.80008C13.0134 3.92675 12.8467 3.99341 12.6734 3.99341ZM3.32675 3.99341C3.15341 3.99341 2.98675 3.92675 2.85342 3.80008L2.76675 3.70675C2.50675 3.44675 2.50675 3.02675 2.76675 2.76675C3.02675 2.50675 3.44675 2.50675 3.70675 2.76675L3.79341 2.85342C4.05341 3.11342 4.05341 3.53341 3.79341 3.79341C3.66675 3.92675 3.49341 3.99341 3.32675 3.99341ZM8.00008 2.02675C7.63341 2.02675 7.33342 1.75341 7.33342 1.38675V1.33341C7.33342 0.966748 7.63341 0.666748 8.00008 0.666748C8.36675 0.666748 8.66675 0.966748 8.66675 1.33341C8.66675 1.70008 8.36675 2.02675 8.00008 2.02675Z"
fill="#969AA1"
/>
</svg>
</span>
<span className="hidden dark:inline-block">
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.3533 10.62C14.2466 10.44 13.9466 10.16 13.1999 10.2933C12.7866 10.3667 12.3666 10.4 11.9466 10.38C10.3933 10.3133 8.98659 9.6 8.00659 8.5C7.13993 7.53333 6.60659 6.27333 6.59993 4.91333C6.59993 4.15333 6.74659 3.42 7.04659 2.72666C7.33993 2.05333 7.13326 1.7 6.98659 1.55333C6.83326 1.4 6.47326 1.18666 5.76659 1.48C3.03993 2.62666 1.35326 5.36 1.55326 8.28666C1.75326 11.04 3.68659 13.3933 6.24659 14.28C6.85993 14.4933 7.50659 14.62 8.17326 14.6467C8.27993 14.6533 8.38659 14.66 8.49326 14.66C10.7266 14.66 12.8199 13.6067 14.1399 11.8133C14.5866 11.1933 14.4666 10.8 14.3533 10.62Z"
fill="#969AA1"
/>
</svg>
</span>
</span>
</label>
</li>
);
};
export default DarkModeSwitcher;

View File

@@ -0,0 +1,211 @@
import { useEffect, useRef, useState } from "react";
import Link from "next/link";
import Image from "next/image";
import ClickOutside from "@/components/ClickOutside";
const DropdownMessage = () => {
const [dropdownOpen, setDropdownOpen] = useState(false);
const [notifying, setNotifying] = useState(true);
return (
<ClickOutside onClick={() => setDropdownOpen(false)} className="relative">
<li className="relative">
<Link
onClick={() => {
setNotifying(false);
setDropdownOpen(!dropdownOpen);
}}
className="relative flex h-8.5 w-8.5 items-center justify-center rounded-full border-[0.5px] border-stroke bg-gray hover:text-primary dark:border-strokedark dark:bg-meta-4 dark:text-white"
href="#"
>
<span
className={`absolute -right-0.5 -top-0.5 z-1 h-2 w-2 rounded-full bg-meta-1 ${
notifying === false ? "hidden" : "inline"
}`}
>
<span className="absolute -z-1 inline-flex h-full w-full animate-ping rounded-full bg-meta-1 opacity-75"></span>
</span>
<svg
className="fill-current duration-300 ease-in-out"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.9688 1.57495H7.03135C3.43135 1.57495 0.506348 4.41558 0.506348 7.90308C0.506348 11.3906 2.75635 13.8375 8.26885 16.3125C8.40947 16.3687 8.52197 16.3968 8.6626 16.3968C8.85947 16.3968 9.02822 16.3406 9.19697 16.2281C9.47822 16.0593 9.64697 15.75 9.64697 15.4125V14.2031H10.9688C14.5688 14.2031 17.522 11.3625 17.522 7.87495C17.522 4.38745 14.5688 1.57495 10.9688 1.57495ZM10.9688 12.9937H9.3376C8.80322 12.9937 8.35322 13.4437 8.35322 13.9781V15.0187C3.6001 12.825 1.74385 10.8 1.74385 7.9312C1.74385 5.14683 4.10635 2.8687 7.03135 2.8687H10.9688C13.8657 2.8687 16.2563 5.14683 16.2563 7.9312C16.2563 10.7156 13.8657 12.9937 10.9688 12.9937Z"
fill=""
/>
<path
d="M5.42812 7.28442C5.0625 7.28442 4.78125 7.56567 4.78125 7.9313C4.78125 8.29692 5.0625 8.57817 5.42812 8.57817C5.79375 8.57817 6.075 8.29692 6.075 7.9313C6.075 7.56567 5.79375 7.28442 5.42812 7.28442Z"
fill=""
/>
<path
d="M9.00015 7.28442C8.63452 7.28442 8.35327 7.56567 8.35327 7.9313C8.35327 8.29692 8.63452 8.57817 9.00015 8.57817C9.33765 8.57817 9.64702 8.29692 9.64702 7.9313C9.64702 7.56567 9.33765 7.28442 9.00015 7.28442Z"
fill=""
/>
<path
d="M12.5719 7.28442C12.2063 7.28442 11.925 7.56567 11.925 7.9313C11.925 8.29692 12.2063 8.57817 12.5719 8.57817C12.9375 8.57817 13.2188 8.29692 13.2188 7.9313C13.2188 7.56567 12.9094 7.28442 12.5719 7.28442Z"
fill=""
/>
</svg>
</Link>
{/* <!-- Dropdown Start --> */}
{dropdownOpen && (
<div
className={`absolute -right-16 mt-2.5 flex h-90 w-75 flex-col rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark sm:right-0 sm:w-80`}
>
<div className="px-4.5 py-3">
<h5 className="text-sm font-medium text-bodydark2">Messages</h5>
</div>
<ul className="flex h-auto flex-col overflow-y-auto">
<li>
<Link
className="flex gap-4.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="/messages"
>
<div className="h-12.5 w-12.5 rounded-full">
<Image
width={112}
height={112}
src={"/images/user/user-02.png"}
alt="User"
style={{
width: "auto",
height: "auto",
}}
/>
</div>
<div>
<h6 className="text-sm font-medium text-black dark:text-white">
Mariya Desoja
</h6>
<p className="text-sm">I like your confidence 💪</p>
<p className="text-xs">2min ago</p>
</div>
</Link>
</li>
<li>
<Link
className="flex gap-4.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="/messages"
>
<div className="h-12.5 w-12.5 rounded-full">
<Image
width={112}
height={112}
src={"/images/user/user-01.png"}
alt="User"
style={{
width: "auto",
height: "auto",
}}
/>
</div>
<div>
<h6 className="text-sm font-medium text-black dark:text-white">
Robert Jhon
</h6>
<p className="text-sm">Can you share your offer?</p>
<p className="text-xs">10min ago</p>
</div>
</Link>
</li>
<li>
<Link
className="flex gap-4.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="/messages"
>
<div className="h-12.5 w-12.5 rounded-full">
<Image
width={112}
height={112}
src={"/images/user/user-03.png"}
alt="User"
style={{
width: "auto",
height: "auto",
}}
/>
</div>
<div>
<h6 className="text-sm font-medium text-black dark:text-white">
Henry Dholi
</h6>
<p className="text-sm">I cam across your profile and...</p>
<p className="text-xs">1day ago</p>
</div>
</Link>
</li>
<li>
<Link
className="flex gap-4.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="/messages"
>
<div className="h-12.5 w-12.5 rounded-full">
<Image
width={112}
height={112}
src={"/images/user/user-04.png"}
alt="User"
style={{
width: "auto",
height: "auto",
}}
/>
</div>
<div>
<h6 className="text-sm font-medium text-black dark:text-white">
Cody Fisher
</h6>
<p className="text-sm">Im waiting for you response!</p>
<p className="text-xs">5days ago</p>
</div>
</Link>
</li>
<li>
<Link
className="flex gap-4.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="/messages"
>
<div className="h-12.5 w-12.5 rounded-full">
<Image
width={112}
height={112}
src={"/images/user/user-02.png"}
alt="User"
style={{
width: "auto",
height: "auto",
}}
/>
</div>
<div>
<h6 className="text-sm font-medium text-black dark:text-white">
Mariya Desoja
</h6>
<p className="text-sm">I like your confidence 💪</p>
<p className="text-xs">2min ago</p>
</div>
</Link>
</li>
</ul>
</div>
)}
{/* <!-- Dropdown End --> */}
</li>
</ClickOutside>
);
};
export default DropdownMessage;

View File

@@ -0,0 +1,125 @@
import { useState } from "react";
import Link from "next/link";
import ClickOutside from "@/components/ClickOutside";
const DropdownNotification = () => {
const [dropdownOpen, setDropdownOpen] = useState(false);
const [notifying, setNotifying] = useState(true);
return (
<ClickOutside onClick={() => setDropdownOpen(false)} className="relative">
<li>
<Link
onClick={() => {
setNotifying(false);
setDropdownOpen(!dropdownOpen);
}}
href="#"
className="relative flex h-8.5 w-8.5 items-center justify-center rounded-full border-[0.5px] border-stroke bg-gray hover:text-primary dark:border-strokedark dark:bg-meta-4 dark:text-white"
>
<span
className={`absolute -top-0.5 right-0 z-1 h-2 w-2 rounded-full bg-meta-1 ${
notifying === false ? "hidden" : "inline"
}`}
>
<span className="absolute -z-1 inline-flex h-full w-full animate-ping rounded-full bg-meta-1 opacity-75"></span>
</span>
<svg
className="fill-current duration-300 ease-in-out"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.1999 14.9343L15.6374 14.0624C15.5249 13.8937 15.4687 13.7249 15.4687 13.528V7.67803C15.4687 6.01865 14.7655 4.47178 13.4718 3.31865C12.4312 2.39053 11.0812 1.7999 9.64678 1.6874V1.1249C9.64678 0.787402 9.36553 0.478027 8.9999 0.478027C8.6624 0.478027 8.35303 0.759277 8.35303 1.1249V1.65928C8.29678 1.65928 8.24053 1.65928 8.18428 1.6874C4.92178 2.05303 2.4749 4.66865 2.4749 7.79053V13.528C2.44678 13.8093 2.39053 13.9499 2.33428 14.0343L1.7999 14.9343C1.63115 15.2155 1.63115 15.553 1.7999 15.8343C1.96865 16.0874 2.2499 16.2562 2.55928 16.2562H8.38115V16.8749C8.38115 17.2124 8.6624 17.5218 9.02803 17.5218C9.36553 17.5218 9.6749 17.2405 9.6749 16.8749V16.2562H15.4687C15.778 16.2562 16.0593 16.0874 16.228 15.8343C16.3968 15.553 16.3968 15.2155 16.1999 14.9343ZM3.23428 14.9905L3.43115 14.653C3.5999 14.3718 3.68428 14.0343 3.74053 13.6405V7.79053C3.74053 5.31553 5.70928 3.23428 8.3249 2.95303C9.92803 2.78428 11.503 3.2624 12.6562 4.2749C13.6687 5.1749 14.2312 6.38428 14.2312 7.67803V13.528C14.2312 13.9499 14.3437 14.3437 14.5968 14.7374L14.7655 14.9905H3.23428Z"
fill=""
/>
</svg>
</Link>
{dropdownOpen && (
<div
className={`absolute -right-27 mt-2.5 flex h-90 w-75 flex-col rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark sm:right-0 sm:w-80`}
>
<div className="px-4.5 py-3">
<h5 className="text-sm font-medium text-bodydark2">
Notification
</h5>
</div>
<ul className="flex h-auto flex-col overflow-y-auto">
<li>
<Link
className="flex flex-col gap-2.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="#"
>
<p className="text-sm">
<span className="text-black dark:text-white">
Edit your information in a swipe
</span>{" "}
Sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim.
</p>
<p className="text-xs">12 May, 2025</p>
</Link>
</li>
<li>
<Link
className="flex flex-col gap-2.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="#"
>
<p className="text-sm">
<span className="text-black dark:text-white">
It is a long established fact
</span>{" "}
that a reader will be distracted by the readable.
</p>
<p className="text-xs">24 Feb, 2025</p>
</Link>
</li>
<li>
<Link
className="flex flex-col gap-2.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="#"
>
<p className="text-sm">
<span className="text-black dark:text-white">
There are many variations
</span>{" "}
of passages of Lorem Ipsum available, but the majority have
suffered
</p>
<p className="text-xs">04 Jan, 2025</p>
</Link>
</li>
<li>
<Link
className="flex flex-col gap-2.5 border-t border-stroke px-4.5 py-3 hover:bg-gray-2 dark:border-strokedark dark:hover:bg-meta-4"
href="#"
>
<p className="text-sm">
<span className="text-black dark:text-white">
There are many variations
</span>{" "}
of passages of Lorem Ipsum available, but the majority have
suffered
</p>
<p className="text-xs">01 Dec, 2024</p>
</Link>
</li>
</ul>
</div>
)}
</li>
</ClickOutside>
);
};
export default DropdownNotification;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,129 @@
import Link from "next/link";
import DarkModeSwitcher from "./DarkModeSwitcher";
import DropdownMessage from "./DropdownMessage";
import DropdownNotification from "./DropdownNotification";
import DropdownUser from "./DropdownUser";
import Image from "next/image";
const Header = (props: {
sidebarOpen: string | boolean | undefined;
setSidebarOpen: (arg0: boolean) => void;
}) => {
return (
<header className="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
<div className="flex flex-grow items-center justify-between px-4 py-4 shadow-2 md:px-6 2xl:px-11">
<div className="flex items-center gap-2 sm:gap-4 lg:hidden">
{/* <!-- Hamburger Toggle BTN --> */}
<button
aria-controls="sidebar"
onClick={(e) => {
e.stopPropagation();
props.setSidebarOpen(!props.sidebarOpen);
}}
className="z-99999 block rounded-sm border border-stroke bg-white p-1.5 shadow-sm dark:border-strokedark dark:bg-boxdark lg:hidden"
>
<span className="relative block h-5.5 w-5.5 cursor-pointer">
<span className="du-block absolute right-0 h-full w-full">
<span
className={`relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-&lsqb;0&rsqb duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!w-full delay-300"
}`}
></span>
<span
className={`relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-150 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "delay-400 !w-full"
}`}
></span>
<span
className={`relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-200 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!w-full delay-500"
}`}
></span>
</span>
<span className="absolute right-0 h-full w-full rotate-45">
<span
className={`absolute left-2.5 top-0 block h-full w-0.5 rounded-sm bg-black delay-300 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!h-0 !delay-&lsqb;0&rsqb"
}`}
></span>
<span
className={`delay-400 absolute left-0 top-2.5 block h-0.5 w-full rounded-sm bg-black duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!h-0 !delay-200"
}`}
></span>
</span>
</span>
</button>
{/* <!-- Hamburger Toggle BTN --> */}
<Link className="block flex-shrink-0 lg:hidden" href="/">
<Image
width={32}
height={32}
src={"/images/logo/logo-icon.svg"}
alt="Logo"
/>
</Link>
</div>
<div className="hidden sm:block">
<form action="https://formbold.com/s/unique_form_id" method="POST">
<div className="relative">
<button className="absolute left-0 top-1/2 -translate-y-1/2">
<svg
className="fill-body hover:fill-primary dark:fill-bodydark dark:hover:fill-primary"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.16666 3.33332C5.945 3.33332 3.33332 5.945 3.33332 9.16666C3.33332 12.3883 5.945 15 9.16666 15C12.3883 15 15 12.3883 15 9.16666C15 5.945 12.3883 3.33332 9.16666 3.33332ZM1.66666 9.16666C1.66666 5.02452 5.02452 1.66666 9.16666 1.66666C13.3088 1.66666 16.6667 5.02452 16.6667 9.16666C16.6667 13.3088 13.3088 16.6667 9.16666 16.6667C5.02452 16.6667 1.66666 13.3088 1.66666 9.16666Z"
fill=""
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13.2857 13.2857C13.6112 12.9603 14.1388 12.9603 14.4642 13.2857L18.0892 16.9107C18.4147 17.2362 18.4147 17.7638 18.0892 18.0892C17.7638 18.4147 17.2362 18.4147 16.9107 18.0892L13.2857 14.4642C12.9603 14.1388 12.9603 13.6112 13.2857 13.2857Z"
fill=""
/>
</svg>
</button>
<input
type="text"
placeholder="Type to search..."
className="w-full bg-transparent pl-9 pr-4 font-medium focus:outline-none xl:w-125"
/>
</div>
</form>
</div>
<div className="flex items-center gap-3 2xsm:gap-7">
<ul className="flex items-center gap-2 2xsm:gap-4">
{/* <!-- Dark Mode Toggler --> */}
<DarkModeSwitcher />
{/* <!-- Dark Mode Toggler --> */}
{/* <!-- Notification Menu Area --> */}
<DropdownNotification />
{/* <!-- Notification Menu Area --> */}
{/* <!-- Chat Notification Area --> */}
<DropdownMessage />
{/* <!-- Chat Notification Area --> */}
</ul>
{/* <!-- User Area --> */}
<DropdownUser />
{/* <!-- User Area --> */}
</div>
</div>
</header>
);
};
export default Header;

View File

@@ -0,0 +1,38 @@
"use client";
import React, { useState, ReactNode } from "react";
import Sidebar from "@/components/Sidebar";
import Header from "@/components/Header";
export default function DefaultLayout({
children,
}: {
children: React.ReactNode;
}) {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<>
{/* <!-- ===== Page Wrapper Start ===== --> */}
<div className="flex">
{/* <!-- ===== Sidebar Start ===== --> */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* <!-- ===== Sidebar End ===== --> */}
{/* <!-- ===== Content Area Start ===== --> */}
<div className="relative flex flex-1 flex-col lg:ml-72.5">
{/* <!-- ===== Header Start ===== --> */}
{/* <!-- ===== Header End ===== --> */}
{/* <!-- ===== Main Content Start ===== --> */}
<main>
<div className="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
{children}
</div>
</main>
{/* <!-- ===== Main Content End ===== --> */}
</div>
{/* <!-- ===== Content Area End ===== --> */}
</div>
{/* <!-- ===== Page Wrapper End ===== --> */}
</>
);
}

View File

@@ -0,0 +1,64 @@
"use client";
import jsVectorMap from "jsvectormap";
import "jsvectormap/dist/jsvectormap.css";
import React, { useEffect } from "react";
import "../../js/us-aea-en";
const MapOne: React.FC = () => {
useEffect(() => {
const mapOne = new jsVectorMap({
selector: "#mapOne",
map: "us_aea_en",
zoomButtons: true,
regionStyle: {
initial: {
fill: "#C8D0D8",
},
hover: {
fillOpacity: 1,
fill: "#3056D3",
},
},
regionLabelStyle: {
initial: {
fontFamily: "Satoshi",
fontWeight: "semibold",
fill: "#fff",
},
hover: {
cursor: "pointer",
},
},
labels: {
regions: {
render(code: string) {
return code.split("-")[1];
},
},
},
});
return () => {
const map = document.getElementById("mapOne");
if (map) {
map.innerHTML = "";
}
// mapOne.destroy();
};
}, []);
return (
<div className="col-span-12 rounded-sm border border-stroke bg-white px-7.5 py-6 shadow-default dark:border-strokedark dark:bg-boxdark xl:col-span-7">
<h4 className="mb-2 text-xl font-semibold text-black dark:text-white">
Region labels
</h4>
<div className="h-90">
<div id="mapOne" className="mapOne map-btn"></div>
</div>
</div>
);
};
export default MapOne;

View File

@@ -0,0 +1,68 @@
"use client";
import React, { useState } from "react";
const SelectGroupOne: React.FC = () => {
const [selectedOption, setSelectedOption] = useState<string>("");
const [isOptionSelected, setIsOptionSelected] = useState<boolean>(false);
const changeTextColor = () => {
setIsOptionSelected(true);
};
return (
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
{" "}
Subject{" "}
</label>
<div className="relative z-20 bg-transparent dark:bg-form-input">
<select
value={selectedOption}
onChange={(e) => {
setSelectedOption(e.target.value);
changeTextColor();
}}
className={`relative z-20 w-full appearance-none rounded border border-stroke bg-transparent px-5 py-3 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary ${
isOptionSelected ? "text-black dark:text-white" : ""
}`}
>
<option value="" disabled className="text-body dark:text-bodydark">
Select your subject
</option>
<option value="USA" className="text-body dark:text-bodydark">
USA
</option>
<option value="UK" className="text-body dark:text-bodydark">
UK
</option>
<option value="Canada" className="text-body dark:text-bodydark">
Canada
</option>
</select>
<span className="absolute right-4 top-1/2 z-30 -translate-y-1/2">
<svg
className="fill-current"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z"
fill=""
></path>
</g>
</svg>
</span>
</div>
</div>
);
};
export default SelectGroupOne;

View File

@@ -0,0 +1,97 @@
"use client";
import React, { useState } from "react";
const SelectGroupTwo: React.FC = () => {
const [selectedOption, setSelectedOption] = useState<string>("");
const [isOptionSelected, setIsOptionSelected] = useState<boolean>(false);
const changeTextColor = () => {
setIsOptionSelected(true);
};
return (
<div>
<label className="mb-3 block text-sm font-medium text-black dark:text-white">
Select Country
</label>
<div className="relative z-20 bg-white dark:bg-form-input">
<span className="absolute left-4 top-1/2 z-30 -translate-y-1/2">
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.0007 2.50065C5.85852 2.50065 2.50065 5.85852 2.50065 10.0007C2.50065 14.1428 5.85852 17.5007 10.0007 17.5007C14.1428 17.5007 17.5007 14.1428 17.5007 10.0007C17.5007 5.85852 14.1428 2.50065 10.0007 2.50065ZM0.833984 10.0007C0.833984 4.93804 4.93804 0.833984 10.0007 0.833984C15.0633 0.833984 19.1673 4.93804 19.1673 10.0007C19.1673 15.0633 15.0633 19.1673 10.0007 19.1673C4.93804 19.1673 0.833984 15.0633 0.833984 10.0007Z"
fill="#637381"
></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0.833984 9.99935C0.833984 9.53911 1.20708 9.16602 1.66732 9.16602H18.334C18.7942 9.16602 19.1673 9.53911 19.1673 9.99935C19.1673 10.4596 18.7942 10.8327 18.334 10.8327H1.66732C1.20708 10.8327 0.833984 10.4596 0.833984 9.99935Z"
fill="#637381"
></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.50084 10.0008C7.55796 12.5632 8.4392 15.0301 10.0006 17.0418C11.5621 15.0301 12.4433 12.5632 12.5005 10.0008C12.4433 7.43845 11.5621 4.97153 10.0007 2.95982C8.4392 4.97153 7.55796 7.43845 7.50084 10.0008ZM10.0007 1.66749L9.38536 1.10547C7.16473 3.53658 5.90275 6.69153 5.83417 9.98346C5.83392 9.99503 5.83392 10.0066 5.83417 10.0182C5.90275 13.3101 7.16473 16.4651 9.38536 18.8962C9.54325 19.069 9.76655 19.1675 10.0007 19.1675C10.2348 19.1675 10.4581 19.069 10.6159 18.8962C12.8366 16.4651 14.0986 13.3101 14.1671 10.0182C14.1674 10.0066 14.1674 9.99503 14.1671 9.98346C14.0986 6.69153 12.8366 3.53658 10.6159 1.10547L10.0007 1.66749Z"
fill="#637381"
></path>
</g>
</svg>
</span>
<select
value={selectedOption}
onChange={(e) => {
setSelectedOption(e.target.value);
changeTextColor();
}}
className={`relative z-20 w-full appearance-none rounded border border-stroke bg-transparent px-12 py-3 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input ${
isOptionSelected ? "text-black dark:text-white" : ""
}`}
>
<option value="" disabled className="text-body dark:text-bodydark">
Select Country
</option>
<option value="USA" className="text-body dark:text-bodydark">
USA
</option>
<option value="UK" className="text-body dark:text-bodydark">
UK
</option>
<option value="Canada" className="text-body dark:text-bodydark">
Canada
</option>
</select>
<span className="absolute right-4 top-1/2 z-10 -translate-y-1/2">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z"
fill="#637381"
></path>
</g>
</svg>
</span>
</div>
</div>
);
};
export default SelectGroupTwo;

View File

@@ -0,0 +1,28 @@
import React from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
const SidebarDropdown = ({ item }: any) => {
const pathname = usePathname();
return (
<>
<ul className="mb-5.5 mt-4 flex flex-col gap-2.5 pl-6">
{item.map((item: any, index: number) => (
<li key={index}>
<Link
href={item.route}
className={`group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ${
pathname === item.route ? "text-white" : ""
}`}
>
{item.label}
</Link>
</li>
))}
</ul>
</>
);
};
export default SidebarDropdown;

View File

@@ -0,0 +1,70 @@
import React from "react";
import Link from "next/link";
import SidebarDropdown from "@/components/Sidebar/SidebarDropdown";
import { usePathname } from "next/navigation";
const SidebarItem = ({ item, pageName, setPageName }: any) => {
const handleClick = () => {
const updatedPageName =
pageName !== item.label.toLowerCase() ? item.label.toLowerCase() : "";
return setPageName(updatedPageName);
};
const pathname = usePathname();
const isActive = (item: any) => {
if (item.route === pathname) return true;
if (item.children) {
return item.children.some((child: any) => isActive(child));
}
return false;
};
const isItemActive = isActive(item);
return (
<>
<li>
<Link
href={item.route}
onClick={handleClick}
className={`${isItemActive ? "bg-graydark dark:bg-meta-4" : ""} group relative flex items-center gap-2.5 rounded-sm px-4 py-2 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4`}
>
{item.icon}
{item.label}
{item.children && (
<svg
className={`absolute right-4 top-1/2 -translate-y-1/2 fill-current ${
pageName === item.label.toLowerCase() && "rotate-180"
}`}
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.41107 6.9107C4.73651 6.58527 5.26414 6.58527 5.58958 6.9107L10.0003 11.3214L14.4111 6.91071C14.7365 6.58527 15.2641 6.58527 15.5896 6.91071C15.915 7.23614 15.915 7.76378 15.5896 8.08922L10.5896 13.0892C10.2641 13.4147 9.73651 13.4147 9.41107 13.0892L4.41107 8.08922C4.08563 7.76378 4.08563 7.23614 4.41107 6.9107Z"
fill=""
/>
</svg>
)}
</Link>
{item.children && (
<div
className={`translate transform overflow-hidden ${
pageName !== item.label.toLowerCase() && "hidden"
}`}
>
<SidebarDropdown item={item.children} />
</div>
)}
</li>
</>
);
};
export default SidebarItem;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
import { useState } from "react";
const SwitcherFour = () => {
const [enabled, setEnabled] = useState<boolean>(false);
return (
<div>
<label
htmlFor="toggle4"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="toggle4"
className="sr-only"
onChange={() => {
setEnabled(!enabled);
}}
/>
<div className="block h-8 w-14 rounded-full bg-black"></div>
<div
className={`absolute left-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-white transition ${
enabled && "!right-1 !translate-x-full"
}`}
></div>
</div>
</label>
</div>
);
};
export default SwitcherFour;

View File

@@ -0,0 +1,33 @@
import { useState } from "react";
const SwitcherOne = () => {
const [enabled, setEnabled] = useState<boolean>(false);
return (
<div>
<label
htmlFor="toggle1"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="toggle1"
className="sr-only"
onChange={() => {
setEnabled(!enabled);
}}
/>
<div className="block h-8 w-14 rounded-full bg-meta-9 dark:bg-[#5A616B]"></div>
<div
className={`absolute left-1 top-1 h-6 w-6 rounded-full bg-white transition ${
enabled && "!right-1 !translate-x-full !bg-primary dark:!bg-white"
}`}
></div>
</div>
</label>
</div>
);
};
export default SwitcherOne;

View File

@@ -0,0 +1,66 @@
import { useState } from "react";
const SwitcherThree = () => {
const [enabled, setEnabled] = useState(false);
return (
<div>
<label
htmlFor="toggle3"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
type="checkbox"
id="toggle3"
className="sr-only"
onChange={() => {
setEnabled(!enabled);
}}
/>
<div className="block h-8 w-14 rounded-full bg-meta-9 dark:bg-[#5A616B]"></div>
<div
className={`dot absolute left-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-white transition ${
enabled && "!right-1 !translate-x-full !bg-primary dark:!bg-white"
}`}
>
<span className={`hidden ${enabled && "!block"}`}>
<svg
className="fill-white dark:fill-black"
width="11"
height="8"
viewBox="0 0 11 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.0915 0.951972L10.0867 0.946075L10.0813 0.940568C9.90076 0.753564 9.61034 0.753146 9.42927 0.939309L4.16201 6.22962L1.58507 3.63469C1.40401 3.44841 1.11351 3.44879 0.932892 3.63584C0.755703 3.81933 0.755703 4.10875 0.932892 4.29224L0.932878 4.29225L0.934851 4.29424L3.58046 6.95832C3.73676 7.11955 3.94983 7.2 4.1473 7.2C4.36196 7.2 4.55963 7.11773 4.71406 6.9584L10.0468 1.60234C10.2436 1.4199 10.2421 1.1339 10.0915 0.951972ZM4.2327 6.30081L4.2317 6.2998C4.23206 6.30015 4.23237 6.30049 4.23269 6.30082L4.2327 6.30081Z"
fill=""
stroke=""
strokeWidth="0.4"
></path>
</svg>
</span>
<span className={`${enabled && "hidden"}`}>
<svg
className="h-4 w-4 stroke-current"
fill="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</span>
</div>
</div>
</label>
</div>
);
};
export default SwitcherThree;

View File

@@ -0,0 +1,33 @@
import { useState } from "react";
const SwitcherTwo = () => {
const [enabled, setEnabled] = useState(false);
return (
<div x-data="{ switcherToggle: false }">
<label
htmlFor="toggle2"
className="flex cursor-pointer select-none items-center"
>
<div className="relative">
<input
id="toggle2"
type="checkbox"
className="sr-only"
onChange={() => {
setEnabled(!enabled);
}}
/>
<div className="h-5 w-14 rounded-full bg-meta-9 shadow-inner dark:bg-[#5A616B]"></div>
<div
className={`dot absolute -top-1 left-0 h-7 w-7 rounded-full bg-white shadow-switch-1 transition ${
enabled && "!right-0 !translate-x-full !bg-primary dark:!bg-white"
}`}
></div>
</div>
</label>
</div>
);
};
export default SwitcherTwo;

View File

@@ -0,0 +1,124 @@
import { BRAND } from "@/types/brand";
import Image from "next/image";
import DropdownDefault from "../Dropdowns/DropdownDefault";
const brandData: BRAND[] = [
{
logo: "/images/brand/brand-01.svg",
name: "Google",
visitors: 3.5,
revenues: "5,768",
sales: 590,
conversion: 4.8,
},
{
logo: "/images/brand/brand-02.svg",
name: "Twitter",
visitors: 2.2,
revenues: "4,635",
sales: 467,
conversion: 4.3,
},
{
logo: "/images/brand/brand-06.svg",
name: "Youtube",
visitors: 2.1,
revenues: "4,290",
sales: 420,
conversion: 3.7,
},
{
logo: "/images/brand/brand-04.svg",
name: "Vimeo",
visitors: 1.5,
revenues: "3,580",
sales: 389,
conversion: 2.5,
},
{
logo: "/images/brand/brand-05.svg",
name: "Facebook",
visitors: 3.5,
revenues: "6,768",
sales: 390,
conversion: 4.2,
},
];
const TableFour: React.FC = () => {
return (
<div className="col-span-12 xl:col-span-7">
<div className="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
<div className="mb-6 flex justify-between">
<div>
<h4 className="text-title-sm2 font-bold text-black dark:text-white">
Top Channels
</h4>
</div>
<DropdownDefault />
</div>
<div className="flex flex-col">
<div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-4">
<div className="p-2.5 xl:p-4">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Source
</h5>
</div>
<div className="p-2.5 text-center xl:p-4">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Visitors
</h5>
</div>
<div className="p-2.5 text-center xl:p-4">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Revenues
</h5>
</div>
<div className="hidden p-2.5 text-center sm:block xl:p-4">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Conversion
</h5>
</div>
</div>
{brandData.map((brand, key) => (
<div
className={`grid grid-cols-3 sm:grid-cols-4 ${
key === brandData.length - 1
? ""
: "border-b border-stroke dark:border-strokedark"
}`}
key={key}
>
<div className="flex items-center gap-3 p-2.5 xl:p-5">
<div className="h-9 w-full max-w-9 flex-shrink-0">
<Image src={brand.logo} width={60} height={50} alt="Brand" />
</div>
<p className="hidden font-medium text-black dark:text-white sm:block">
{brand.name}
</p>
</div>
<div className="flex items-center justify-center p-2.5 xl:p-5">
<p className="font-medium text-black dark:text-white">
{brand.visitors}K
</p>
</div>
<div className="flex items-center justify-center p-2.5 xl:p-5">
<p className="font-medium text-meta-3">${brand.revenues}</p>
</div>
<div className="hidden items-center justify-center p-2.5 sm:flex xl:p-5">
<p className="font-medium text-meta-5">{brand.conversion}%</p>
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default TableFour;

View File

@@ -0,0 +1,123 @@
import { BRAND } from "@/types/brand";
import Image from "next/image";
const brandData: BRAND[] = [
{
logo: "/images/brand/brand-01.svg",
name: "Google",
visitors: 3.5,
revenues: "5,768",
sales: 590,
conversion: 4.8,
},
{
logo: "/images/brand/brand-02.svg",
name: "Twitter",
visitors: 2.2,
revenues: "4,635",
sales: 467,
conversion: 4.3,
},
{
logo: "/images/brand/brand-03.svg",
name: "Github",
visitors: 2.1,
revenues: "4,290",
sales: 420,
conversion: 3.7,
},
{
logo: "/images/brand/brand-04.svg",
name: "Vimeo",
visitors: 1.5,
revenues: "3,580",
sales: 389,
conversion: 2.5,
},
{
logo: "/images/brand/brand-05.svg",
name: "Facebook",
visitors: 3.5,
revenues: "6,768",
sales: 390,
conversion: 4.2,
},
];
const TableOne = () => {
return (
<div className="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
<h4 className="mb-6 text-xl font-semibold text-black dark:text-white">
Top Channels
</h4>
<div className="flex flex-col">
<div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-5">
<div className="p-2.5 xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Source
</h5>
</div>
<div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Visitors
</h5>
</div>
<div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Revenues
</h5>
</div>
<div className="hidden p-2.5 text-center sm:block xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Sales
</h5>
</div>
<div className="hidden p-2.5 text-center sm:block xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base">
Conversion
</h5>
</div>
</div>
{brandData.map((brand, key) => (
<div
className={`grid grid-cols-3 sm:grid-cols-5 ${
key === brandData.length - 1
? ""
: "border-b border-stroke dark:border-strokedark"
}`}
key={key}
>
<div className="flex items-center gap-3 p-2.5 xl:p-5">
<div className="flex-shrink-0">
<Image src={brand.logo} alt="Brand" width={48} height={48} />
</div>
<p className="hidden text-black dark:text-white sm:block">
{brand.name}
</p>
</div>
<div className="flex items-center justify-center p-2.5 xl:p-5">
<p className="text-black dark:text-white">{brand.visitors}K</p>
</div>
<div className="flex items-center justify-center p-2.5 xl:p-5">
<p className="text-meta-3">${brand.revenues}</p>
</div>
<div className="hidden items-center justify-center p-2.5 sm:flex xl:p-5">
<p className="text-black dark:text-white">{brand.sales}</p>
</div>
<div className="hidden items-center justify-center p-2.5 sm:flex xl:p-5">
<p className="text-meta-5">{brand.conversion}%</p>
</div>
</div>
))}
</div>
</div>
);
};
export default TableOne;

View File

@@ -0,0 +1,156 @@
import { Package } from "@/types/package";
const packageData: Package[] = [
{
name: "Free package",
price: 0.0,
invoiceDate: `Jan 13,2023`,
status: "Paid",
},
{
name: "Standard Package",
price: 59.0,
invoiceDate: `Jan 13,2023`,
status: "Paid",
},
{
name: "Business Package",
price: 99.0,
invoiceDate: `Jan 13,2023`,
status: "Unpaid",
},
{
name: "Standard Package",
price: 59.0,
invoiceDate: `Jan 13,2023`,
status: "Pending",
},
];
const TableThree = () => {
return (
<div className="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
<div className="max-w-full overflow-x-auto">
<table className="w-full table-auto">
<thead>
<tr className="bg-gray-2 text-left dark:bg-meta-4">
<th className="min-w-[220px] px-4 py-4 font-medium text-black dark:text-white xl:pl-11">
Package
</th>
<th className="min-w-[150px] px-4 py-4 font-medium text-black dark:text-white">
Invoice date
</th>
<th className="min-w-[120px] px-4 py-4 font-medium text-black dark:text-white">
Status
</th>
<th className="px-4 py-4 font-medium text-black dark:text-white">
Actions
</th>
</tr>
</thead>
<tbody>
{packageData.map((packageItem, key) => (
<tr key={key}>
<td className="border-b border-[#eee] px-4 py-5 pl-9 dark:border-strokedark xl:pl-11">
<h5 className="font-medium text-black dark:text-white">
{packageItem.name}
</h5>
<p className="text-sm">${packageItem.price}</p>
</td>
<td className="border-b border-[#eee] px-4 py-5 dark:border-strokedark">
<p className="text-black dark:text-white">
{packageItem.invoiceDate}
</p>
</td>
<td className="border-b border-[#eee] px-4 py-5 dark:border-strokedark">
<p
className={`inline-flex rounded-full bg-opacity-10 px-3 py-1 text-sm font-medium ${
packageItem.status === "Paid"
? "bg-success text-success"
: packageItem.status === "Unpaid"
? "bg-danger text-danger"
: "bg-warning text-warning"
}`}
>
{packageItem.status}
</p>
</td>
<td className="border-b border-[#eee] px-4 py-5 dark:border-strokedark">
<div className="flex items-center space-x-3.5">
<button className="hover:text-primary">
<svg
className="fill-current"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.99981 14.8219C3.43106 14.8219 0.674805 9.50624 0.562305 9.28124C0.47793 9.11249 0.47793 8.88749 0.562305 8.71874C0.674805 8.49374 3.43106 3.20624 8.99981 3.20624C14.5686 3.20624 17.3248 8.49374 17.4373 8.71874C17.5217 8.88749 17.5217 9.11249 17.4373 9.28124C17.3248 9.50624 14.5686 14.8219 8.99981 14.8219ZM1.85605 8.99999C2.4748 10.0406 4.89356 13.5562 8.99981 13.5562C13.1061 13.5562 15.5248 10.0406 16.1436 8.99999C15.5248 7.95936 13.1061 4.44374 8.99981 4.44374C4.89356 4.44374 2.4748 7.95936 1.85605 8.99999Z"
fill=""
/>
<path
d="M9 11.3906C7.67812 11.3906 6.60938 10.3219 6.60938 9C6.60938 7.67813 7.67812 6.60938 9 6.60938C10.3219 6.60938 11.3906 7.67813 11.3906 9C11.3906 10.3219 10.3219 11.3906 9 11.3906ZM9 7.875C8.38125 7.875 7.875 8.38125 7.875 9C7.875 9.61875 8.38125 10.125 9 10.125C9.61875 10.125 10.125 9.61875 10.125 9C10.125 8.38125 9.61875 7.875 9 7.875Z"
fill=""
/>
</svg>
</button>
<button className="hover:text-primary">
<svg
className="fill-current"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M13.7535 2.47502H11.5879V1.9969C11.5879 1.15315 10.9129 0.478149 10.0691 0.478149H7.90352C7.05977 0.478149 6.38477 1.15315 6.38477 1.9969V2.47502H4.21914C3.40352 2.47502 2.72852 3.15002 2.72852 3.96565V4.8094C2.72852 5.42815 3.09414 5.9344 3.62852 6.1594L4.07852 15.4688C4.13477 16.6219 5.09102 17.5219 6.24414 17.5219H11.7004C12.8535 17.5219 13.8098 16.6219 13.866 15.4688L14.3441 6.13127C14.8785 5.90627 15.2441 5.3719 15.2441 4.78127V3.93752C15.2441 3.15002 14.5691 2.47502 13.7535 2.47502ZM7.67852 1.9969C7.67852 1.85627 7.79102 1.74377 7.93164 1.74377H10.0973C10.2379 1.74377 10.3504 1.85627 10.3504 1.9969V2.47502H7.70664V1.9969H7.67852ZM4.02227 3.96565C4.02227 3.85315 4.10664 3.74065 4.24727 3.74065H13.7535C13.866 3.74065 13.9785 3.82502 13.9785 3.96565V4.8094C13.9785 4.9219 13.8941 5.0344 13.7535 5.0344H4.24727C4.13477 5.0344 4.02227 4.95002 4.02227 4.8094V3.96565ZM11.7285 16.2563H6.27227C5.79414 16.2563 5.40039 15.8906 5.37227 15.3844L4.95039 6.2719H13.0785L12.6566 15.3844C12.6004 15.8625 12.2066 16.2563 11.7285 16.2563Z"
fill=""
/>
<path
d="M9.00039 9.11255C8.66289 9.11255 8.35352 9.3938 8.35352 9.75942V13.3313C8.35352 13.6688 8.63477 13.9782 9.00039 13.9782C9.33789 13.9782 9.64727 13.6969 9.64727 13.3313V9.75942C9.64727 9.3938 9.33789 9.11255 9.00039 9.11255Z"
fill=""
/>
<path
d="M11.2502 9.67504C10.8846 9.64692 10.6033 9.90004 10.5752 10.2657L10.4064 12.7407C10.3783 13.0782 10.6314 13.3875 10.9971 13.4157C11.0252 13.4157 11.0252 13.4157 11.0533 13.4157C11.3908 13.4157 11.6721 13.1625 11.6721 12.825L11.8408 10.35C11.8408 9.98442 11.5877 9.70317 11.2502 9.67504Z"
fill=""
/>
<path
d="M6.72245 9.67504C6.38495 9.70317 6.1037 10.0125 6.13182 10.35L6.3287 12.825C6.35683 13.1625 6.63808 13.4157 6.94745 13.4157C6.97558 13.4157 6.97558 13.4157 7.0037 13.4157C7.3412 13.3875 7.62245 13.0782 7.59433 12.7407L7.39745 10.2657C7.39745 9.90004 7.08808 9.64692 6.72245 9.67504Z"
fill=""
/>
</svg>
</button>
<button className="hover:text-primary">
<svg
className="fill-current"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.8754 11.6719C16.5379 11.6719 16.2285 11.9531 16.2285 12.3187V14.8219C16.2285 15.075 16.0316 15.2719 15.7785 15.2719H2.22227C1.96914 15.2719 1.77227 15.075 1.77227 14.8219V12.3187C1.77227 11.9812 1.49102 11.6719 1.12539 11.6719C0.759766 11.6719 0.478516 11.9531 0.478516 12.3187V14.8219C0.478516 15.7781 1.23789 16.5375 2.19414 16.5375H15.7785C16.7348 16.5375 17.4941 15.7781 17.4941 14.8219V12.3187C17.5223 11.9531 17.2129 11.6719 16.8754 11.6719Z"
fill=""
/>
<path
d="M8.55074 12.3469C8.66324 12.4594 8.83199 12.5156 9.00074 12.5156C9.16949 12.5156 9.31012 12.4594 9.45074 12.3469L13.4726 8.43752C13.7257 8.1844 13.7257 7.79065 13.5007 7.53752C13.2476 7.2844 12.8539 7.2844 12.6007 7.5094L9.64762 10.4063V2.1094C9.64762 1.7719 9.36637 1.46252 9.00074 1.46252C8.66324 1.46252 8.35387 1.74377 8.35387 2.1094V10.4063L5.40074 7.53752C5.14762 7.2844 4.75387 7.31252 4.50074 7.53752C4.24762 7.79065 4.27574 8.1844 4.50074 8.43752L8.55074 12.3469Z"
fill=""
/>
</svg>
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default TableThree;

View File

@@ -0,0 +1,108 @@
import Image from "next/image";
import { Product } from "@/types/product";
const productData: Product[] = [
{
image: "/images/product/product-01.png",
name: "Apple Watch Series 7",
category: "Electronics",
price: 296,
sold: 22,
profit: 45,
},
{
image: "/images/product/product-02.png",
name: "Macbook Pro M1",
category: "Electronics",
price: 546,
sold: 12,
profit: 125,
},
{
image: "/images/product/product-03.png",
name: "Dell Inspiron 15",
category: "Electronics",
price: 443,
sold: 64,
profit: 247,
},
{
image: "/images/product/product-04.png",
name: "HP Probook 450",
category: "Electronics",
price: 499,
sold: 72,
profit: 103,
},
];
const TableTwo = () => {
return (
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="px-4 py-6 md:px-6 xl:px-7.5">
<h4 className="text-xl font-semibold text-black dark:text-white">
Top Products
</h4>
</div>
<div className="grid grid-cols-6 border-t border-stroke px-4 py-4.5 dark:border-strokedark sm:grid-cols-8 md:px-6 2xl:px-7.5">
<div className="col-span-3 flex items-center">
<p className="font-medium">Product Name</p>
</div>
<div className="col-span-2 hidden items-center sm:flex">
<p className="font-medium">Category</p>
</div>
<div className="col-span-1 flex items-center">
<p className="font-medium">Price</p>
</div>
<div className="col-span-1 flex items-center">
<p className="font-medium">Sold</p>
</div>
<div className="col-span-1 flex items-center">
<p className="font-medium">Profit</p>
</div>
</div>
{productData.map((product, key) => (
<div
className="grid grid-cols-6 border-t border-stroke px-4 py-4.5 dark:border-strokedark sm:grid-cols-8 md:px-6 2xl:px-7.5"
key={key}
>
<div className="col-span-3 flex items-center">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center">
<div className="h-12.5 w-15 rounded-md">
<Image
src={product.image}
width={60}
height={50}
alt="Product"
/>
</div>
<p className="text-sm text-black dark:text-white">
{product.name}
</p>
</div>
</div>
<div className="col-span-2 hidden items-center sm:flex">
<p className="text-sm text-black dark:text-white">
{product.category}
</p>
</div>
<div className="col-span-1 flex items-center">
<p className="text-sm text-black dark:text-white">
${product.price}
</p>
</div>
<div className="col-span-1 flex items-center">
<p className="text-sm text-black dark:text-white">{product.sold}</p>
</div>
<div className="col-span-1 flex items-center">
<p className="text-sm text-meta-3">${product.profit}</p>
</div>
</div>
))}
</div>
);
};
export default TableTwo;

View File

@@ -0,0 +1,9 @@
const Loader = () => {
return (
<div className="flex h-screen items-center justify-center bg-white dark:bg-black">
<div className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent"></div>
</div>
);
};
export default Loader;

View File

@@ -0,0 +1,30 @@
import { useEffect, useState } from "react";
function useLocalStorage(itemName, initialValue) {
const [item, setItem] = useState(initialValue);
useEffect(() => {
const localStorageItem = localStorage.getItem(itemName);
let parsedItem;
if (!localStorageItem) {
localStorage.setItem(itemName, JSON.stringify(initialValue));
parsedItem = initialValue;
} else {
parsedItem = JSON.parse(localStorageItem);
}
setItem(parsedItem);
}, []);
const saveItem = (newItem) => {
const stringifiedItem = JSON.stringify(newItem);
localStorage.setItem(itemName, stringifiedItem);
setItem(newItem);
};
return {
item,
saveItem,
};
}
export { useLocalStorage };

View File

@@ -0,0 +1,22 @@
"use client";
import React from "react";
import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation";
const LoginButton: React.FC = () => {
const router = useRouter();
const onClick = () => {
router.push("/login/email");
};
return (
<Button
className="w-[450px] h-[70px] text-lg"
color="primary"
onClick={onClick}
>
Giriş Yapın
</Button>
);
};
export default LoginButton;

View File

@@ -0,0 +1,29 @@
"use server";
import React from "react";
import LoginSelectEmployee from "./loginselectemployee";
import {
retrieve_access_objects,
retrieve_access_token,
} from "@/apicalls/cookies/token";
const LoginEmployeeCard: React.FC = async () => {
const accessObject: any = await retrieve_access_objects();
const accessToken: any = await retrieve_access_token();
return (
<>
<div className="min-h-full min-w-full">
<h1 className="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">
Şirket Seçimi Yapınız
</h1>
<div>
<LoginSelectEmployee
company_list={accessObject?.companies_list}
access_token={accessToken}
/>
</div>
</div>
</>
);
};
export default LoginEmployeeCard;

View File

@@ -0,0 +1,30 @@
"use server";
import React from "react";
import LoginSelectOccupant from "./loginselectoccupant";
import {
retrieve_access_objects,
retrieve_access_token,
} from "@/apicalls/cookies/token";
const LoginOccupantCard: React.FC = async () => {
const accessObject: any = await retrieve_access_objects();
const accessToken: any = await retrieve_access_token();
return (
<>
<div className="min-h-full min-w-full">
<h1 className="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">
Görev Seçimi Yapınız
</h1>
<div>
<LoginSelectOccupant
available_occupants={accessObject?.available_occupants}
access_token={accessToken}
/>
</div>
</div>
</>
);
};
export default LoginOccupantCard;

View File

@@ -0,0 +1,91 @@
"use client";
import Image from "next/image";
import { Toaster } from "@/components/ui/toaster";
import { useToast } from "@/hooks/use-toast";
import { showToast } from "./toaster";
import { useRouter } from "next/navigation";
import { login_select_employee } from "@/apicalls/login/login";
interface CompanyList {
company_list: Array<string>;
access_token: string;
}
const LoginSelectEmployee: React.FC<CompanyList> = ({
company_list,
access_token,
}) => {
const { toast } = useToast();
const router = useRouter();
const companiesList = company_list || [];
const onClick = (data: any) => {
login_select_employee({ company_uu_id: data?.uu_id, token: access_token })
.then((responseData: any) => {
if (responseData?.completed) {
showToast(toast, "Şirket seçimi", {
message: "Şirket seçimi başarılı",
data: JSON.stringify(responseData),
});
router.push("/dashboard");
}
})
.catch((error) => {
console.error(error);
showToast(toast, "Şirket seçimi", {
message: "Şirket seçimi başarılı",
data: JSON.stringify(error),
});
});
};
return (
<>
<div>
<div>
<Toaster />
</div>
<div className="sm:grid sm:grid-cols-3 sm:gap-4">
{companiesList.map((data: any, index: number) => (
<div className="flex">
<div
className="flex sm:h-56 hover:bg-white bg-emerald-800 m-3 ring-1 shadow-xl ring-emerald-700 p-3 h-64 rounded-2xl"
key={data.uu_id}
onClick={() => onClick(data)}
>
<div className="w-1/4">
<Image
src={"/green-house.webp"}
alt={`Evyos ${index}`}
className=" w-full h-full object-cover"
width={300}
height={300}
/>
</div>
<div className="w-3/4 m-5">
<h2 className="text-lg font-bold mb-2">
UUID : {data.uu_id}
</h2>
<h2 className="text-lg font-bold mb-2">
Şirket Unvanı : {data.public_name}
</h2>
<h2 className="text-lg font-bold mb-2">
Şirket Tipi Name : {data.company_type}
</h2>
<h2 className="text-lg font-bold mb-2">
Adres :{" "}
{data.company_address
? data.company_address
: "Tanımlı Değil"}
</h2>
</div>
</div>
</div>
))}
</div>
</div>
</>
);
};
export default LoginSelectEmployee;

View File

@@ -0,0 +1,164 @@
"use client";
import React from "react";
import Image from "next/image";
import { Toaster } from "@/components/ui/toaster";
import { useToast } from "@/hooks/use-toast";
import { showToast } from "./toaster";
import { login_select_occupant } from "@/apicalls/login/login";
import { useRouter } from "next/navigation";
interface LoginSelectOccupantProps {
access_token: string;
available_occupants: Array<any>;
}
const LoginSelectOccupant: React.FC<LoginSelectOccupantProps> = ({
access_token,
available_occupants,
}) => {
const { toast } = useToast();
const router = useRouter();
const [activeBuildingList, setActiveBuildingList] = React.useState([]);
const [activeOccupantList, setactiveOccupantList] = React.useState([]);
const [isBuildingSelected, setIsBuildingSelected] = React.useState(false);
const [selectedBuilding, setselectedBuilding] = React.useState("");
const onClickBuild = (data: any) => {
setselectedBuilding(data.build_uu_id);
setIsBuildingSelected(true);
};
const onClick = (data: any) => {
login_select_occupant({
build_part_uu_id: data?.part_uu_id,
occupant_uu_id: data?.uu_id,
token: access_token,
})
.then((responseData: any) => {
if (responseData?.completed) {
showToast(toast, "Şirket seçimi", {
message: "Şirket seçimi başarılı",
data: JSON.stringify(responseData),
});
router.push("/dashboard");
}
})
.catch((error) => {
console.error(error);
showToast(toast, "Şirket seçimi", {
message: "Şirket seçimi başarılı",
data: JSON.stringify(error),
});
});
};
if (!isBuildingSelected) {
for (const [key, value] of Object.entries(available_occupants)) {
const building = JSON.parse(
JSON.stringify({
build_uu_id: value?.build_uu_id,
build_name: value?.build_name,
build_no: value?.build_no,
})
);
const isNotInList =
activeBuildingList ||
[].some((item) => JSON.stringify(item) === JSON.stringify(building));
if (!isNotInList) {
setActiveBuildingList(activeBuildingList.push(building));
} else if (activeBuildingList.length === 0) {
setActiveBuildingList([building]);
}
}
} else if (isBuildingSelected && activeOccupantList.length === 0) {
for (const [key, value] of Object.entries(available_occupants)) {
const occupants = value?.occupants;
if (value?.build_uu_id === selectedBuilding) {
setactiveOccupantList(occupants);
}
}
}
return (
<>
<div>
<div>
<Toaster />
</div>
{!isBuildingSelected ? (
<div className="sm:grid sm:grid-cols-3 sm:gap-4">
{activeBuildingList.map((data: any) => (
<div
className="flex sm:h-56 hover:bg-white bg-emerald-800 m-3 ring-1 shadow-xl ring-emerald-700 p-3 h-64 rounded-2xl"
onClick={() => onClickBuild(data)}
key={data.build_uu_id}
>
<div className="sm:w-1/4">
<Image
src={"/green-house.webp"}
alt={`Evyos ${data.build_uu_id}`}
className=" w-full h-full object-cover"
width={300}
height={300}
/>
</div>
<div className="sm:w-3/4 m-5">
<h2 className="text-lg font-bold mb-2">
UUID : {data.build_uu_id}
</h2>
<h2 className="text-lg font-bold mb-2">
Bina : {data.build_name}
</h2>
<h2 className="text-lg font-bold mb-2">
Bina No : {data.build_no}
</h2>
{/* <h2 className="text-lg font-bold mb-2">
Adres :{" "}
{data.company_address
? data.company_address
: "Tanımlı Değil"}
</h2> */}
</div>
</div>
))}
</div>
) : (
<div className="sm:grid sm:grid-cols-3 sm:gap-4">
{activeOccupantList.map((data: any) => (
<div
className="flex sm:h-56 hover:bg-white bg-emerald-800 m-3 ring-1 shadow-xl ring-emerald-700 p-3 h-64 rounded-2xl"
key={`${data.part_uu_id}-${data.uu_id}`}
onClick={() => onClick(data)}
>
<div className="sm:w-1/4">
<Image
src={"/green-house.webp"}
alt={`Evyos ${data.part_uu_id}`}
className=" w-full h-full object-cover"
width={300}
height={300}
/>
</div>
<div className="sm:w-3/4 m-5">
<h2 className="text-lg font-bold mb-2">
UUID : {data.part_uu_id}
</h2>
<h2 className="text-lg font-bold mb-2">
Bina : {data.part_name}
</h2>
<h2 className="text-lg font-bold mb-2">
Daire Kat : {data.part_level}
</h2>
<h2 className="text-lg font-bold mb-2">
Giriş Tipi : {data.code} - {data.description}
</h2>
</div>
</div>
))}
</div>
)}
</div>
</>
);
};
export default LoginSelectOccupant;

View File

@@ -0,0 +1,440 @@
"use client";
import { useToast } from "@/hooks/use-toast";
import { Toaster } from "@/components/ui/toaster";
import { set, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Button } from "@/components/ui/button";
import { PasswordInput } from "@/components/ui/password-input";
import { useRouter } from "next/navigation";
import { showToast } from "./toaster";
import { login_via_access_keys } from "@/apicalls/login/login";
import { Link } from "lucide-react";
import Image from "next/image";
const formSchema = z.object({
loginEmailInput: z
.string()
.min(4, { message: "Email adresi minimum 4 karaterden oluşmalıdır" })
.email("Geçerli bir mail adresi giriniz")
.default(""),
loginPassword: z
.string()
.min(5, { message: "Şifre 6 karakterden az olamaz" })
.default(""),
loginRememberMe: z.boolean().optional().default(false),
});
const LoginWithEmail: React.FC = () => {
const { toast } = useToast();
const router = useRouter();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
function onSubmit(values: z.infer<typeof formSchema>) {
login_via_access_keys({
domain: "evyos.com.tr",
accessKey: values.loginEmailInput,
password: values.loginPassword,
rememberMe: values.loginRememberMe,
})
.then((res: any) => {
if (res?.completed) {
showToast(toast, "Giriş başarılı", {
message: res?.message,
data: res?.user,
});
setTimeout(() => {
router.push("/login/select");
}, 3000);
}
})
.catch((error) => {
console.error(error);
showToast(toast, "Giriş başarısız", {
message: "Kullanıcı adı veya şifre hatalı",
data: JSON.stringify(error.code),
});
});
}
return (
<>
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div className="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex flex-wrap items-center">
<div className="hidden w-full xl:block xl:w-1/2">
<div className="px-26 py-17.5 text-center">
<Link className="mb-5.5 inline-block" href="/">
<Image
className="hidden dark:block"
src={"/images/logo/logo.svg"}
alt="Logo"
width={176}
height={32}
/>
<Image
className="dark:hidden"
src={"/images/logo/logo-dark.svg"}
alt="Logo"
width={176}
height={32}
/>
</Link>
<p className="2xl:px-20">
Lorem ipsum dolor sit amet, consectetur adipiscing elit
suspendisse.
</p>
<span className="mt-15 inline-block">
<svg
width="350"
height="350"
viewBox="0 0 350 350"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M33.5825 294.844L30.5069 282.723C25.0538 280.414 19.4747 278.414 13.7961 276.732L13.4079 282.365L11.8335 276.159C4.79107 274.148 0 273.263 0 273.263C0 273.263 6.46998 297.853 20.0448 316.653L35.8606 319.429L23.5737 321.2C25.2813 323.253 27.1164 325.196 29.0681 327.019C48.8132 345.333 70.8061 353.736 78.1898 345.787C85.5736 337.838 75.5526 316.547 55.8074 298.235C49.6862 292.557 41.9968 288.001 34.2994 284.415L33.5825 294.844Z"
fill="#F2F2F2"
/>
<path
d="M62.8332 281.679L66.4705 269.714C62.9973 264.921 59.2562 260.327 55.2652 255.954L52.019 260.576L53.8812 254.45C48.8923 249.092 45.2489 245.86 45.2489 245.86C45.2489 245.86 38.0686 270.253 39.9627 293.358L52.0658 303.903L40.6299 299.072C41.0301 301.712 41.596 304.324 42.3243 306.893C49.7535 332.77 64.2336 351.323 74.6663 348.332C85.0989 345.341 87.534 321.939 80.1048 296.063C77.8019 288.041 73.5758 280.169 68.8419 273.123L62.8332 281.679Z"
fill="#F2F2F2"
/>
<path
d="M243.681 82.9153H241.762V30.3972C241.762 26.4054 240.975 22.4527 239.447 18.7647C237.918 15.0768 235.677 11.7258 232.853 8.90314C230.028 6.0805 226.674 3.84145 222.984 2.31385C219.293 0.786245 215.337 0 211.343 0H99.99C91.9222 0 84.1848 3.20256 78.48 8.90314C72.7752 14.6037 69.5703 22.3354 69.5703 30.3972V318.52C69.5703 322.512 70.3571 326.465 71.8859 330.153C73.4146 333.841 75.6553 337.192 78.48 340.015C81.3048 342.837 84.6582 345.076 88.3489 346.604C92.0396 348.131 95.9952 348.918 99.99 348.918H211.343C219.41 348.918 227.148 345.715 232.852 340.014C238.557 334.314 241.762 326.582 241.762 318.52V120.299H243.68L243.681 82.9153Z"
fill="#E6E6E6"
/>
<path
d="M212.567 7.9054H198.033C198.701 9.54305 198.957 11.3199 198.776 13.0793C198.595 14.8387 197.984 16.5267 196.997 17.9946C196.01 19.4625 194.676 20.6652 193.114 21.4967C191.552 22.3283 189.809 22.7632 188.039 22.7632H124.247C122.477 22.7631 120.734 22.3281 119.172 21.4964C117.61 20.6648 116.277 19.462 115.289 17.9942C114.302 16.5263 113.691 14.8384 113.511 13.079C113.33 11.3197 113.585 9.54298 114.254 7.9054H100.678C94.6531 7.9054 88.8749 10.297 84.6146 14.5542C80.3543 18.8113 77.9609 24.5852 77.9609 30.6057V318.31C77.9609 324.331 80.3543 330.105 84.6146 334.362C88.8749 338.619 94.6531 341.011 100.678 341.011H212.567C218.592 341.011 224.37 338.619 228.63 334.362C232.891 330.105 235.284 324.331 235.284 318.31V30.6053C235.284 24.5848 232.891 18.811 228.63 14.554C224.37 10.297 218.592 7.9054 212.567 7.9054Z"
fill="white"
/>
<path
d="M142.368 122.512C142.368 120.501 142.898 118.526 143.904 116.784C144.911 115.043 146.359 113.597 148.102 112.592C146.36 111.587 144.383 111.057 142.371 111.057C140.358 111.057 138.381 111.586 136.639 112.591C134.896 113.596 133.448 115.042 132.442 116.784C131.436 118.525 130.906 120.501 130.906 122.512C130.906 124.522 131.436 126.498 132.442 128.239C133.448 129.981 134.896 131.427 136.639 132.432C138.381 133.437 140.358 133.966 142.371 133.966C144.383 133.966 146.36 133.436 148.102 132.431C146.359 131.426 144.911 129.981 143.905 128.24C142.898 126.499 142.368 124.523 142.368 122.512Z"
fill="#CCCCCC"
/>
<path
d="M156.779 122.512C156.778 120.501 157.308 118.526 158.315 116.784C159.321 115.043 160.769 113.597 162.513 112.592C160.77 111.587 158.793 111.057 156.781 111.057C154.769 111.057 152.792 111.586 151.049 112.591C149.306 113.596 147.859 115.042 146.852 116.784C145.846 118.525 145.316 120.501 145.316 122.512C145.316 124.522 145.846 126.498 146.852 128.239C147.859 129.981 149.306 131.427 151.049 132.432C152.792 133.437 154.769 133.966 156.781 133.966C158.793 133.966 160.77 133.436 162.513 132.431C160.769 131.426 159.322 129.981 158.315 128.24C157.308 126.499 156.779 124.523 156.779 122.512Z"
fill="#CCCCCC"
/>
<path
d="M170.862 133.966C177.192 133.966 182.325 128.838 182.325 122.512C182.325 116.186 177.192 111.057 170.862 111.057C164.531 111.057 159.398 116.186 159.398 122.512C159.398 128.838 164.531 133.966 170.862 133.966Z"
fill="#3056D3"
/>
<path
d="M190.017 158.289H123.208C122.572 158.288 121.962 158.035 121.512 157.586C121.062 157.137 120.809 156.527 120.809 155.892V89.1315C120.809 88.496 121.062 87.8866 121.512 87.4372C121.962 86.9878 122.572 86.735 123.208 86.7343H190.017C190.653 86.735 191.263 86.9878 191.713 87.4372C192.163 87.8866 192.416 88.496 192.416 89.1315V155.892C192.416 156.527 192.163 157.137 191.713 157.586C191.263 158.035 190.653 158.288 190.017 158.289ZM123.208 87.6937C122.826 87.6941 122.46 87.8457 122.19 88.1154C121.92 88.385 121.769 88.7507 121.768 89.132V155.892C121.769 156.274 121.92 156.639 122.19 156.909C122.46 157.178 122.826 157.33 123.208 157.33H190.017C190.399 157.33 190.765 157.178 191.035 156.909C191.304 156.639 191.456 156.274 191.457 155.892V89.132C191.456 88.7507 191.304 88.385 191.035 88.1154C190.765 87.8457 190.399 87.6941 190.017 87.6937H123.208Z"
fill="#CCCCCC"
/>
<path
d="M204.934 209.464H102.469V210.423H204.934V209.464Z"
fill="#CCCCCC"
/>
<path
d="M105.705 203.477C107.492 203.477 108.941 202.029 108.941 200.243C108.941 198.457 107.492 197.01 105.705 197.01C103.918 197.01 102.469 198.457 102.469 200.243C102.469 202.029 103.918 203.477 105.705 203.477Z"
fill="#3056D3"
/>
<path
d="M204.934 241.797H102.469V242.757H204.934V241.797Z"
fill="#CCCCCC"
/>
<path
d="M105.705 235.811C107.492 235.811 108.941 234.363 108.941 232.577C108.941 230.791 107.492 229.344 105.705 229.344C103.918 229.344 102.469 230.791 102.469 232.577C102.469 234.363 103.918 235.811 105.705 235.811Z"
fill="#3056D3"
/>
<path
d="M203.062 278.617H170.68C170.121 278.617 169.584 278.394 169.189 277.999C168.793 277.604 168.571 277.068 168.57 276.509V265.168C168.571 264.609 168.793 264.073 169.189 263.678C169.584 263.283 170.121 263.06 170.68 263.06H203.062C203.621 263.06 204.158 263.283 204.553 263.678C204.949 264.073 205.171 264.609 205.172 265.168V276.509C205.171 277.068 204.949 277.604 204.553 277.999C204.158 278.394 203.621 278.617 203.062 278.617Z"
fill="#3056D3"
/>
<path
d="M116.263 203.477C118.05 203.477 119.499 202.029 119.499 200.243C119.499 198.457 118.05 197.01 116.263 197.01C114.476 197.01 113.027 198.457 113.027 200.243C113.027 202.029 114.476 203.477 116.263 203.477Z"
fill="#3056D3"
/>
<path
d="M126.818 203.477C128.605 203.477 130.054 202.029 130.054 200.243C130.054 198.457 128.605 197.01 126.818 197.01C125.031 197.01 123.582 198.457 123.582 200.243C123.582 202.029 125.031 203.477 126.818 203.477Z"
fill="#3056D3"
/>
<path
d="M116.263 235.811C118.05 235.811 119.499 234.363 119.499 232.577C119.499 230.791 118.05 229.344 116.263 229.344C114.476 229.344 113.027 230.791 113.027 232.577C113.027 234.363 114.476 235.811 116.263 235.811Z"
fill="#3056D3"
/>
<path
d="M126.818 235.811C128.605 235.811 130.054 234.363 130.054 232.577C130.054 230.791 128.605 229.344 126.818 229.344C125.031 229.344 123.582 230.791 123.582 232.577C123.582 234.363 125.031 235.811 126.818 235.811Z"
fill="#3056D3"
/>
<path
d="M264.742 229.309C264.972 229.414 265.193 229.537 265.404 229.678L286.432 220.709L287.183 215.174L295.585 215.123L295.089 227.818L267.334 235.153C267.275 235.345 267.205 235.535 267.124 235.719C266.722 236.574 266.077 237.292 265.269 237.783C264.46 238.273 263.525 238.514 262.58 238.475C261.636 238.436 260.723 238.119 259.958 237.563C259.193 237.008 258.61 236.239 258.28 235.353C257.951 234.467 257.892 233.504 258.108 232.584C258.325 231.664 258.809 230.829 259.5 230.183C260.19 229.538 261.056 229.11 261.989 228.955C262.922 228.799 263.879 228.922 264.742 229.309Z"
fill="#FFB8B8"
/>
<path
d="M298.642 344.352H292.894L290.16 322.198L298.643 322.198L298.642 344.352Z"
fill="#FFB8B8"
/>
<path
d="M288.788 342.711H299.873V349.685H281.809C281.809 347.835 282.544 346.062 283.853 344.754C285.162 343.446 286.937 342.711 288.788 342.711Z"
fill="#1C2434"
/>
<path
d="M320.995 342.729L315.274 343.292L310.379 321.513L318.822 320.682L320.995 342.729Z"
fill="#FFB8B8"
/>
<path
d="M311.028 342.061L322.059 340.975L322.744 347.916L304.766 349.685C304.676 348.774 304.767 347.854 305.033 346.977C305.299 346.101 305.735 345.285 306.317 344.577C306.898 343.869 307.614 343.283 308.422 342.851C309.23 342.419 310.116 342.151 311.028 342.061Z"
fill="#1C2434"
/>
<path
d="M300.242 191.677C306.601 191.677 311.757 186.525 311.757 180.17C311.757 173.815 306.601 168.663 300.242 168.663C293.882 168.663 288.727 173.815 288.727 180.17C288.727 186.525 293.882 191.677 300.242 191.677Z"
fill="#FFB8B8"
/>
<path
d="M291.607 339.872C291.113 339.873 290.635 339.7 290.256 339.383C289.877 339.066 289.623 338.626 289.537 338.139C286.562 321.636 276.838 267.676 276.605 266.181C276.6 266.147 276.597 266.112 276.598 266.077V262.054C276.597 261.907 276.643 261.764 276.729 261.645L278.013 259.847C278.074 259.761 278.154 259.689 278.247 259.639C278.34 259.588 278.444 259.559 278.549 259.554C285.874 259.211 309.86 258.206 311.019 259.652C312.183 261.106 311.772 265.512 311.678 266.38L311.682 266.471L322.459 335.337C322.543 335.886 322.408 336.446 322.082 336.896C321.756 337.347 321.265 337.65 320.717 337.742L313.986 338.85C313.485 338.931 312.971 338.829 312.539 338.563C312.107 338.297 311.784 337.885 311.63 337.401C309.548 330.754 302.568 308.393 300.149 299.741C300.133 299.686 300.099 299.639 300.051 299.607C300.004 299.576 299.946 299.563 299.89 299.571C299.834 299.579 299.782 299.608 299.745 299.651C299.708 299.694 299.688 299.749 299.689 299.806C299.81 308.054 300.102 329.098 300.203 336.366L300.214 337.148C300.218 337.678 300.023 338.191 299.668 338.584C299.313 338.978 298.823 339.224 298.295 339.274L291.804 339.863C291.738 339.869 291.672 339.872 291.607 339.872Z"
fill="#1C2434"
/>
<path
d="M292.933 196.201C290.924 197.395 289.721 199.588 289.031 201.821C287.754 205.953 286.985 210.226 286.741 214.545L286.012 227.475L276.984 261.755C284.809 268.37 289.322 266.867 299.855 261.455C310.387 256.044 311.591 263.26 311.591 263.26L313.697 234.092L316.706 202.219C316.031 201.407 315.266 200.672 314.427 200.03C311.645 197.868 308.409 196.366 304.962 195.636C301.516 194.906 297.948 194.967 294.528 195.815L292.933 196.201Z"
fill="#3056D3"
/>
<path
d="M290.001 236.232C290.244 236.324 290.479 236.434 290.704 236.562L311.497 226.163L311.842 220.529L320.419 219.938L320.878 232.781L293.092 241.963C292.865 242.935 292.347 243.816 291.608 244.487C290.868 245.158 289.941 245.588 288.951 245.72C287.96 245.852 286.953 245.68 286.063 245.226C285.173 244.772 284.442 244.058 283.968 243.179C283.494 242.301 283.299 241.298 283.409 240.306C283.519 239.313 283.928 238.378 284.583 237.624C285.238 236.869 286.107 236.332 287.075 236.084C288.043 235.835 289.063 235.887 290.001 236.232Z"
fill="#FFB8B8"
/>
<path
d="M316.556 202.365C321.672 204.17 322.573 223.716 322.573 223.716C316.554 220.409 309.332 225.821 309.332 225.821C309.332 225.821 307.827 220.709 306.022 214.094C305.477 212.233 305.412 210.265 305.832 208.372C306.253 206.479 307.147 204.724 308.429 203.269C308.429 203.269 311.44 200.56 316.556 202.365Z"
fill="#3056D3"
/>
<path
d="M310.566 183.213C309.132 182.066 307.174 184.151 307.174 184.151L306.026 173.828C306.026 173.828 298.853 174.687 294.261 173.542C289.67 172.396 288.953 177.7 288.953 177.7C288.716 175.557 288.668 173.399 288.81 171.248C289.096 168.667 292.827 166.087 299.427 164.366C306.026 162.646 309.47 170.101 309.47 170.101C314.061 172.395 312.001 184.36 310.566 183.213Z"
fill="#1C2434"
/>
</svg>
</span>
</div>
</div>
<div className="w-full border-stroke dark:border-strokedark xl:w-1/2 xl:border-l-2">
<div className="w-full p-4 sm:p-12.5 xl:p-17.5">
<span className="mb-1.5 block font-medium">
Start for free
</span>
<h2 className="mb-9 text-2xl font-bold text-black dark:text-white sm:text-title-xl2">
Mail ile Giriş Yap
</h2>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-5 max-w-3xl mx-auto py-10"
>
<div className="mb-4">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Email
</label>
<div className="relative">
<FormField
control={form.control}
name="loginEmailInput"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type="email"
placeholder="example@example.net"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span className="absolute right-4 top-4">
<svg
className="fill-current"
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.5">
<path
d="M19.2516 3.30005H2.75156C1.58281 3.30005 0.585938 4.26255 0.585938 5.46567V16.6032C0.585938 17.7719 1.54844 18.7688 2.75156 18.7688H19.2516C20.4203 18.7688 21.4172 17.8063 21.4172 16.6032V5.4313C21.4172 4.26255 20.4203 3.30005 19.2516 3.30005ZM19.2516 4.84692C19.2859 4.84692 19.3203 4.84692 19.3547 4.84692L11.0016 10.2094L2.64844 4.84692C2.68281 4.84692 2.71719 4.84692 2.75156 4.84692H19.2516ZM19.2516 17.1532H2.75156C2.40781 17.1532 2.13281 16.8782 2.13281 16.5344V6.35942L10.1766 11.5157C10.4172 11.6875 10.6922 11.7563 10.9672 11.7563C11.2422 11.7563 11.5172 11.6875 11.7578 11.5157L19.8016 6.35942V16.5688C19.8703 16.9125 19.5953 17.1532 19.2516 17.1532Z"
fill=""
/>
</g>
</svg>
</span>
</div>
<div className="mb-4">
<span className="absolute right-4 -top-4">
<svg
className="fill-current"
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.5">
<path
d="M19.2516 3.30005H2.75156C1.58281 3.30005 0.585938 4.26255 0.585938 5.46567V16.6032C0.585938 17.7719 1.54844 18.7688 2.75156 18.7688H19.2516C20.4203 18.7688 21.4172 17.8063 21.4172 16.6032V5.4313C21.4172 4.26255 20.4203 3.30005 19.2516 3.30005ZM19.2516 4.84692C19.2859 4.84692 19.3203 4.84692 19.3547 4.84692L11.0016 10.2094L2.64844 4.84692C2.68281 4.84692 2.71719 4.84692 2.75156 4.84692H19.2516ZM19.2516 17.1532H2.75156C2.40781 17.1532 2.13281 16.8782 2.13281 16.5344V6.35942L10.1766 11.5157C10.4172 11.6875 10.6922 11.7563 10.9672 11.7563C11.2422 11.7563 11.5172 11.6875 11.7578 11.5157L19.8016 6.35942V16.5688C19.8703 16.9125 19.5953 17.1532 19.2516 17.1532Z"
fill=""
/>
</g>
</svg>
</span>
</div>
</div>
<div className="mb-6">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Password
</label>
<FormField
control={form.control}
name="loginPassword"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type="password"
placeholder="Şifre giriniz"
className="w-full rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-black outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:text-white dark:focus:border-primary"
{...field}
/>
</FormControl>
<FormDescription>
Şifrenizi giriniz
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="relative">
<span className="absolute right-4 top-4">
<svg
className="fill-current"
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.5">
<path
d="M16.1547 6.80626V5.91251C16.1547 3.16251 14.0922 0.825009 11.4797 0.618759C10.0359 0.481259 8.59219 0.996884 7.52656 1.95938C6.46094 2.92188 5.84219 4.29688 5.84219 5.70626V6.80626C3.84844 7.18438 2.33594 8.93751 2.33594 11.0688V17.2906C2.33594 19.5594 4.19219 21.3813 6.42656 21.3813H15.5016C17.7703 21.3813 19.6266 19.525 19.6266 17.2563V11C19.6609 8.93751 18.1484 7.21876 16.1547 6.80626ZM8.55781 3.09376C9.31406 2.40626 10.3109 2.06251 11.3422 2.16563C13.1641 2.33751 14.6078 3.98751 14.6078 5.91251V6.70313H7.38906V5.67188C7.38906 4.70938 7.80156 3.78126 8.55781 3.09376ZM18.1141 17.2906C18.1141 18.7 16.9453 19.8688 15.5359 19.8688H6.46094C5.05156 19.8688 3.91719 18.7344 3.91719 17.325V11.0688C3.91719 9.52189 5.15469 8.28438 6.70156 8.28438H15.2953C16.8422 8.28438 18.1141 9.52188 18.1141 11V17.2906Z"
fill=""
/>
<path
d="M10.9977 11.8594C10.5852 11.8594 10.207 12.2031 10.207 12.65V16.2594C10.207 16.6719 10.5508 17.05 10.9977 17.05C11.4102 17.05 11.7883 16.7063 11.7883 16.2594V12.6156C11.7883 12.2031 11.4102 11.8594 10.9977 11.8594Z"
fill=""
/>
</g>
</svg>
</span>
</div>
</div>
<FormField
control={form.control}
name="loginRememberMe"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-lg">
Beni Hatırla
</FormLabel>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
aria-readonly
/>
</FormControl>
</FormItem>
)}
/>
<div className="mb-5">
<input
type="submit"
value="Sign In"
className="w-full cursor-pointer rounded-lg border border-primary bg-primary p-4 text-white transition hover:bg-opacity-90"
/>
</div>
<button className="flex w-full items-center justify-center gap-3.5 rounded-lg border border-stroke bg-gray p-4 hover:bg-opacity-50 dark:border-strokedark dark:bg-meta-4 dark:hover:bg-opacity-50">
<span>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_191_13499)">
<path
d="M19.999 10.2217C20.0111 9.53428 19.9387 8.84788 19.7834 8.17737H10.2031V11.8884H15.8266C15.7201 12.5391 15.4804 13.162 15.1219 13.7195C14.7634 14.2771 14.2935 14.7578 13.7405 15.1328L13.7209 15.2571L16.7502 17.5568L16.96 17.5774C18.8873 15.8329 19.9986 13.2661 19.9986 10.2217"
fill="#4285F4"
/>
<path
d="M10.2055 19.9999C12.9605 19.9999 15.2734 19.111 16.9629 17.5777L13.7429 15.1331C12.8813 15.7221 11.7248 16.1333 10.2055 16.1333C8.91513 16.1259 7.65991 15.7205 6.61791 14.9745C5.57592 14.2286 4.80007 13.1801 4.40044 11.9777L4.28085 11.9877L1.13101 14.3765L1.08984 14.4887C1.93817 16.1456 3.24007 17.5386 4.84997 18.5118C6.45987 19.4851 8.31429 20.0004 10.2059 19.9999"
fill="#34A853"
/>
<path
d="M4.39899 11.9777C4.1758 11.3411 4.06063 10.673 4.05807 9.99996C4.06218 9.32799 4.1731 8.66075 4.38684 8.02225L4.38115 7.88968L1.19269 5.4624L1.0884 5.51101C0.372763 6.90343 0 8.4408 0 9.99987C0 11.5589 0.372763 13.0963 1.0884 14.4887L4.39899 11.9777Z"
fill="#FBBC05"
/>
<path
d="M10.2059 3.86663C11.668 3.84438 13.0822 4.37803 14.1515 5.35558L17.0313 2.59996C15.1843 0.901848 12.7383 -0.0298855 10.2059 -3.6784e-05C8.31431 -0.000477834 6.4599 0.514732 4.85001 1.48798C3.24011 2.46124 1.9382 3.85416 1.08984 5.51101L4.38946 8.02225C4.79303 6.82005 5.57145 5.77231 6.61498 5.02675C7.65851 4.28118 8.9145 3.87541 10.2059 3.86663Z"
fill="#EB4335"
/>
</g>
<defs>
<clipPath id="clip0_191_13499">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
</span>
Sign in with Google
</button>
</form>
</Form>
<div className="my-5">
<button
className="w-full cursor-pointer rounded-lg border border-secondary bg-secondary p-4 text-white transition hover:bg-opacity-90"
onClick={() => router.push("/login/phone")}
>
Telefon ile giriş yap
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<Toaster />
</>
);
};
export default LoginWithEmail;

View File

@@ -0,0 +1,131 @@
"use client";
import { useToast } from "@/hooks/use-toast";
import { Toaster } from "@/components/ui/toaster";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { PhoneInput } from "@/components/ui/phone-input";
import { PasswordInput } from "@/components/ui/password-input";
import { Switch } from "@/components/ui/switch";
import { useRouter } from "next/navigation";
import { showToast } from "./toaster";
const formSchema = z.object({
loginPhone: z
.string()
.min(10, { message: "Telefon numarası 10 karakterden az olamaz" })
.max(14, { message: "Telefon numarası 14 karakterden fazla olamaz" })
.default(""),
loginPassword: z
.string()
.min(5, { message: "Şifre 6 karakterden az olamaz" })
.default(""),
loginRememberMe: z.boolean().optional().default(false),
});
const LoginWithPhone: React.FC = () => {
const { toast } = useToast();
const router = useRouter();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
function onSubmit(values: z.infer<typeof formSchema>) {
showToast(toast, "Form submitted", values, "success");
router.push("/login/select");
}
return (
<>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-5 max-w-3xl mx-auto py-10"
>
<FormField
control={form.control}
name="loginPhone"
render={({ field }) => (
<FormItem className="flex flex-col items-start">
<FormLabel>Phone number</FormLabel>
<FormControl className="w-full">
<PhoneInput
placeholder="Placeholder"
{...field}
defaultCountry="TR"
/>
</FormControl>
<FormDescription>
Telefonunuzu girerek giriş yapabilirsiniz
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="loginPassword"
render={({ field }) => (
<FormItem>
<FormLabel>Şifre</FormLabel>
<FormControl>
<PasswordInput placeholder="" {...field} />
</FormControl>
<FormDescription>Şifrenizi giriniz</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="loginRememberMe"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel>Beni Hatırla</FormLabel>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
aria-readonly
/>
</FormControl>
</FormItem>
)}
/>
<Button
type="submit"
className="flex w-full justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-black focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Giriş yap
</Button>
</form>
</Form>
<Button
className="flex w-full justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-black focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
onClick={() => router.push("/login/email")}
>
Mail ile giriş yap
</Button>
<Toaster />
</>
);
};
export default LoginWithPhone;

View File

@@ -0,0 +1,25 @@
import React from "react";
export function showToast(
toast: any,
message: string,
data: any,
type: "success" | "error" | "info" | "warning" = "info"
) {
try {
toast({
variant: type,
title: message,
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">
{JSON.stringify(data || { message: "No data provided" }, null, 2)}
</code>
</pre>
),
});
} catch (error) {
console.error("Form submission error", error);
toast.error("Failed to submit the form. Please try again.");
}
}

View File

@@ -0,0 +1,50 @@
"use client"
import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
import { cn } from "@/lib/utils"
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
{...props}
/>
))
Avatar.displayName = AvatarPrimitive.Root.displayName
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
{...props}
/>
))
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
export { Avatar, AvatarImage, AvatarFallback }

View File

@@ -0,0 +1,57 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@@ -0,0 +1,153 @@
"use client"
import * as React from "react"
import { type DialogProps } from "@radix-ui/react-dialog"
import { Command as CommandPrimitive } from "cmdk"
import { Search } from "lucide-react"
import { cn } from "@/lib/utils"
import { Dialog, DialogContent } from "@/components/ui/dialog"
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
className
)}
{...props}
/>
))
Command.displayName = CommandPrimitive.displayName
const CommandDialog = ({ children, ...props }: DialogProps) => {
return (
<Dialog {...props}>
<DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</DialogContent>
</Dialog>
)
}
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
/>
</div>
))
CommandInput.displayName = CommandPrimitive.Input.displayName
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
))
CommandList.displayName = CommandPrimitive.List.displayName
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
))
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className
)}
{...props}
/>
))
CommandGroup.displayName = CommandPrimitive.Group.displayName
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn("-mx-1 h-px bg-border", className)}
{...props}
/>
))
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
className
)}
{...props}
/>
))
CommandItem.displayName = CommandPrimitive.Item.displayName
const CommandShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className
)}
{...props}
/>
)
}
CommandShortcut.displayName = "CommandShortcut"
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
}

View File

@@ -0,0 +1,122 @@
"use client"
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal = DialogPrimitive.Portal
const DialogClose = DialogPrimitive.Close
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}

178
src/components/ui/form.tsx Normal file
View File

@@ -0,0 +1,178 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form"
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
const Form = FormProvider
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
name: TName
}
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
}
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>")
}
const { id } = itemContext
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
}
type FormItemContextValue = {
id: string
}
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
)
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
)
})
FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
)
})
FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-[0.8rem] text-muted-foreground", className)}
{...props}
/>
)
})
FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children
if (!body) {
return null
}
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)}
{...props}
>
{body}
</p>
)
})
FormMessage.displayName = "FormMessage"
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
}

View File

@@ -0,0 +1,22 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }

View File

@@ -0,0 +1,26 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }

View File

@@ -0,0 +1,57 @@
"use client";
import * as React from "react";
import { EyeIcon, EyeOffIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input, type InputProps } from "@/components/ui/input";
import { cn } from "@/lib/utils";
const PasswordInput = React.forwardRef<HTMLInputElement, InputProps>(
({ className, ...props }, ref) => {
const [showPassword, setShowPassword] = React.useState(false);
const disabled =
props.value === "" || props.value === undefined || props.disabled;
return (
<div className="relative">
<Input
type={showPassword ? "text" : "password"}
className={cn("hide-password-toggle pr-10", className)}
ref={ref}
{...props}
/>
<Button
type="button"
variant="ghost"
size="sm"
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
onClick={() => setShowPassword((prev) => !prev)}
disabled={disabled}
>
{showPassword && !disabled ? (
<EyeIcon className="h-4 w-4" aria-hidden="true" />
) : (
<EyeOffIcon className="h-4 w-4" aria-hidden="true" />
)}
<span className="sr-only">
{showPassword ? "Hide password" : "Show password"}
</span>
</Button>
{/* hides browsers password toggles */}
<style>{`
.hide-password-toggle::-ms-reveal,
.hide-password-toggle::-ms-clear {
visibility: hidden;
pointer-events: none;
display: none;
}
`}</style>
</div>
);
}
);
PasswordInput.displayName = "PasswordInput";
export { PasswordInput };

View File

@@ -0,0 +1,172 @@
import * as React from "react";
import * as RPNInput from "react-phone-number-input";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import flags from "react-phone-number-input/flags";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import { cn } from "@/lib/utils";
type PhoneInputProps = Omit<
React.ComponentProps<"input">,
"onChange" | "value" | "ref"
> &
Omit<RPNInput.Props<typeof RPNInput.default>, "onChange"> & {
onChange?: (value: RPNInput.Value) => void;
};
const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> =
React.forwardRef<React.ElementRef<typeof RPNInput.default>, PhoneInputProps>(
({ className, onChange, ...props }, ref) => {
return (
<RPNInput.default
ref={ref}
className={cn("flex", className)}
flagComponent={FlagComponent}
countrySelectComponent={CountrySelect}
inputComponent={InputComponent}
smartCaret={false}
/**
* Handles the onChange event.
*
* react-phone-number-input might trigger the onChange event as undefined
* when a valid phone number is not entered. To prevent this,
* the value is coerced to an empty string.
*
* @param {E164Number | undefined} value - The entered value
*/
onChange={(value) => onChange?.(value || ("" as RPNInput.Value))}
{...props}
/>
);
}
);
PhoneInput.displayName = "PhoneInput";
const InputComponent = React.forwardRef<
HTMLInputElement,
React.ComponentProps<"input">
>(({ className, ...props }, ref) => (
<Input
className={cn("rounded-e-lg rounded-s-none", className)}
{...props}
ref={ref}
/>
));
InputComponent.displayName = "InputComponent";
type CountryEntry = { label: string; value: RPNInput.Country | undefined };
type CountrySelectProps = {
disabled?: boolean;
value: RPNInput.Country;
options: CountryEntry[];
onChange: (country: RPNInput.Country) => void;
};
const CountrySelect = ({
disabled,
value: selectedCountry,
options: countryList,
onChange,
}: CountrySelectProps) => {
return (
<Popover>
<PopoverTrigger asChild>
<Button
type="button"
variant="outline"
className="flex gap-1 rounded-e-none rounded-s-lg border-r-0 px-3 focus:z-10"
disabled={disabled}
>
<FlagComponent
country={selectedCountry}
countryName={selectedCountry}
/>
<ChevronsUpDown
className={cn(
"-mr-2 size-4 opacity-50",
disabled ? "hidden" : "opacity-100"
)}
/>
</Button>
</PopoverTrigger>
<PopoverContent className="w-[300px] p-0">
<Command>
<CommandInput placeholder="Search country..." />
<CommandList>
<ScrollArea className="h-72">
<CommandEmpty>No country found.</CommandEmpty>
<CommandGroup>
{countryList.map(({ value, label }) =>
value ? (
<CountrySelectOption
key={value}
country={value}
countryName={label}
selectedCountry={selectedCountry}
onChange={onChange}
/>
) : null
)}
</CommandGroup>
</ScrollArea>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
};
interface CountrySelectOptionProps extends RPNInput.FlagProps {
selectedCountry: RPNInput.Country;
onChange: (country: RPNInput.Country) => void;
}
const CountrySelectOption = ({
country,
countryName,
selectedCountry,
onChange,
}: CountrySelectOptionProps) => {
return (
<CommandItem className="gap-2" onSelect={() => onChange(country)}>
<FlagComponent country={country} countryName={countryName} />
<span className="flex-1 text-sm">{countryName}</span>
<span className="text-sm text-foreground/50">{`+${RPNInput.getCountryCallingCode(
country
)}`}</span>
<CheckIcon
className={`ml-auto size-4 ${
country === selectedCountry ? "opacity-100" : "opacity-0"
}`}
/>
</CommandItem>
);
};
const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
const Flag = flags[country];
return (
<span className="flex h-4 w-6 overflow-hidden rounded-sm bg-foreground/20">
{Flag && <Flag title={countryName} />}
</span>
);
};
export { PhoneInput };

View File

@@ -0,0 +1,33 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverAnchor = PopoverPrimitive.Anchor
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }

View File

@@ -0,0 +1,48 @@
"use client"
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils"
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
))
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
export { ScrollArea, ScrollBar }

View File

@@ -0,0 +1,31 @@
"use client";
import { useTheme } from "next-themes";
import { Toaster as Sonner } from "sonner";
type ToasterProps = React.ComponentProps<typeof Sonner>;
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
toastOptions={{
classNames: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton:
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton:
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
},
}}
{...props}
/>
);
};
export { Toaster };

View File

@@ -0,0 +1,29 @@
"use client"
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }

129
src/components/ui/toast.tsx Normal file
View File

@@ -0,0 +1,129 @@
"use client"
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const ToastProvider = ToastPrimitives.Provider
const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Viewport
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className
)}
{...props}
/>
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
ref={ref}
className={cn(toastVariants({ variant }), className)}
{...props}
/>
)
})
Toast.displayName = ToastPrimitives.Root.displayName
const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Action
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className
)}
{...props}
/>
))
ToastAction.displayName = ToastPrimitives.Action.displayName
const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Close
ref={ref}
className={cn(
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className
)}
toast-close=""
{...props}
>
<X className="h-4 w-4" />
</ToastPrimitives.Close>
))
ToastClose.displayName = ToastPrimitives.Close.displayName
const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold [&+div]:text-xs", className)}
{...props}
/>
))
ToastTitle.displayName = ToastPrimitives.Title.displayName
const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn("text-sm opacity-90", className)}
{...props}
/>
))
ToastDescription.displayName = ToastPrimitives.Description.displayName
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastActionElement = React.ReactElement<typeof ToastAction>
export {
type ToastProps,
type ToastActionElement,
ToastProvider,
ToastViewport,
Toast,
ToastTitle,
ToastDescription,
ToastClose,
ToastAction,
}

View File

@@ -0,0 +1,35 @@
"use client"
import { useToast } from "@/hooks/use-toast"
import {
Toast,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from "@/components/ui/toast"
export function Toaster() {
const { toasts } = useToast()
return (
<ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
</div>
{action}
<ToastClose />
</Toast>
)
})}
<ToastViewport />
</ToastProvider>
)
}

194
src/hooks/use-toast.ts Normal file
View File

@@ -0,0 +1,194 @@
"use client"
// Inspired by react-hot-toast library
import * as React from "react"
import type {
ToastActionElement,
ToastProps,
} from "@/components/ui/toast"
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
type ToasterToast = ToastProps & {
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const
let count = 0
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
}
type ActionType = typeof actionTypes
type Action =
| {
type: ActionType["ADD_TOAST"]
toast: ToasterToast
}
| {
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
interface State {
toasts: ToasterToast[]
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)
toastTimeouts.set(toastId, timeout)
}
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
}
case "DISMISS_TOAST": {
const { toastId } = action
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t
),
}
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
}
}
}
const listeners: Array<(state: State) => void> = []
let memoryState: State = { toasts: [] }
function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
listeners.forEach((listener) => {
listener(memoryState)
})
}
type Toast = Omit<ToasterToast, "id">
function toast({ ...props }: Toast) {
const id = genId()
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
dispatch({
type: "ADD_TOAST",
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
},
},
})
return {
id: id,
dismiss,
update,
}
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState)
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
}
}
export { useToast, toast }

View File

@@ -0,0 +1,19 @@
import { useEffect } from "react";
import useLocalStorage from "./useLocalStorage";
const useColorMode = () => {
const [colorMode, setColorMode] = useLocalStorage("color-theme", "light");
useEffect(() => {
const className = "dark";
const bodyClass = window.document.body.classList;
colorMode === "dark"
? bodyClass.add(className)
: bodyClass.remove(className);
}, [colorMode]);
return [colorMode, setColorMode];
};
export default useColorMode;

View File

@@ -0,0 +1,50 @@
"use client";
import { useEffect, useState } from "react";
type SetValue<T> = T | ((val: T) => T);
function useLocalStorage<T>(
key: string,
initialValue: T,
): [T, (value: SetValue<T>) => void] {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
// Get from local storage by key
if (typeof window !== "undefined") {
// browser code
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
}
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
});
// useEffect to update local storage when the state changes
useEffect(() => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
typeof storedValue === "function"
? storedValue(storedValue)
: storedValue;
// Save state
if (typeof window !== "undefined") {
// browser code
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
export default useLocalStorage;

6
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}