old version placed

This commit is contained in:
2024-12-31 12:22:36 +03:00
parent 08b7ad5c00
commit 00acc8c320
93 changed files with 7481 additions and 1 deletions

15
src/app/building/page.tsx Normal file
View File

@@ -0,0 +1,15 @@
"use server";
import React from "react";
const Page = () => {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-2xl font-bold mb-4">Building Management</h1>
<div className="bg-white rounded-lg shadow p-6">
<p>Building page content goes here</p>
</div>
</div>
);
};
export default Page;

View File

@@ -0,0 +1,17 @@
"use server";
import React from "react";
const DashboardPage = () => {
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
<div className="grid gap-4">
<div className="bg-white p-4 rounded-lg shadow">
<p>Welcome to your dashboard</p>
</div>
</div>
</div>
);
};
export default DashboardPage;

BIN
src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

72
src/app/globals.css Normal file
View File

@@ -0,0 +1,72 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
font-family: Arial, Helvetica, sans-serif;
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 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: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--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%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

34
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Evyos Web App",
description: "Generated by evyos app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

View File

@@ -0,0 +1,18 @@
"use server";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import LoginWithEmail from "@/pages/LoginViaEmail/page";
import { redirect } from "next/navigation";
const LoginEmailPage = async () => {
if (await checkAccessTokenIsValid()) {
redirect("/login/select");
}
return (
<>
<LoginWithEmail />
</>
);
};
export default LoginEmailPage;

14
src/app/login/page.tsx Normal file
View File

@@ -0,0 +1,14 @@
"use server";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
const LoginPage = async () => {
if (await checkAccessTokenIsValid()) {
redirect("/login/select");
} else {
redirect("/login/email");
}
};
export default LoginPage;

View File

@@ -0,0 +1,17 @@
"use server";
import { checkAccessTokenIsValid } from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
import LoginWithPhone from "@/pages/LoginViaPhone/page";
const LoginPhonePage = async () => {
if (await checkAccessTokenIsValid()) {
redirect("/login/select");
}
return (
<>
<LoginWithPhone />
</>
);
};
export default LoginPhonePage;

View File

@@ -0,0 +1,27 @@
"use server";
import React from "react";
import { retrieveAccessObjects } from "@/apicalls/cookies/token";
import LoginSelectEmployeeCard from "@/pages/LoginSelectEmployee/page";
const LoginEmployeePage: React.FC = async () => {
const accessObject = await retrieveAccessObjects();
return accessObject ? (
<>
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex flex-wrap items-center">
<LoginSelectEmployeeCard
companyList={accessObject.companies_list}
/>
</div>
</div>
</div>
</>
) : (
<>
<h1>No register Employeer Company has found for this user</h1>
</>
);
};
export default LoginEmployeePage;

View File

@@ -0,0 +1,27 @@
"use server";
import React from "react";
import { retrieveAccessObjects } from "@/apicalls/cookies/token";
import LoginSelectOccupantCard from "@/pages/LoginSelectOccupant/page";
const LoginOccupantPage: React.FC = async () => {
const accessObject: any = await retrieveAccessObjects();
return accessObject ? (
<>
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="flex flex-wrap items-center">
<LoginSelectOccupantCard
availableOccupants={accessObject?.available_occupants}
/>
</div>
</div>
</div>
</>
) : (
<>
<h1>No register Occupant has found for this user</h1>
</>
);
};
export default LoginOccupantPage;

View File

@@ -0,0 +1,22 @@
"use server";
import {
checkAccessTokenIsValid,
retrieveUserType,
} from "@/apicalls/cookies/token";
import { redirect } from "next/navigation";
import LoginEmployeePage from "./LoginEmployeePage";
import LoginOccupantPage from "./LoginOccupantPage";
const SelectPage = async () => {
const token_is_valid = await checkAccessTokenIsValid();
const userType: "employee" | "occupant" = await retrieveUserType();
const isEmployee = userType === "employee";
if (!userType || !token_is_valid) {
redirect("/login/email");
}
return <>{isEmployee ? <LoginEmployeePage /> : <LoginOccupantPage />}</>;
};
export default SelectPage;

101
src/app/page.tsx Normal file
View File

@@ -0,0 +1,101 @@
import Image from "next/image";
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
src/app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

View File

@@ -0,0 +1,84 @@
"use client";
import React from "react";
import { useRouter } from "next/navigation";
const GoogleButton: React.FC = () => {
return (
<>
<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>
Google ile giriş yap
</button>
</>
);
};
const LoginButton: React.FC = () => {
return (
<div className="mb-5">
<input
type="submit"
value="Giriş Yap"
className="w-full cursor-pointer rounded-lg border border-primary bg-primary p-4 text-white transition hover:bg-opacity-90"
/>
</div>
);
};
interface InterfaceChangeSignTypeButton {
buttonType: "phone" | "email";
}
const ChangeSignTypeButton: React.FC<InterfaceChangeSignTypeButton> = ({
buttonType,
}) => {
const router = useRouter();
return (
<div className="my-5">
<button
className="w-full cursor-pointer rounded-lg border border-primary bg-primary p-4 text-white transition hover:bg-opacity-90"
onClick={() =>
router.push(buttonType === "phone" ? "/login/phone" : "/login/email")
}
>
{buttonType === "phone"
? "Telefon ile Giriş Yap"
: "Email ile Giriş Yap"}
</button>
</div>
);
};
export { GoogleButton, LoginButton, ChangeSignTypeButton };

View File

@@ -0,0 +1,33 @@
import React from "react";
import Image from "next/image";
interface InterfaceLeftSidePanel {
textLabel: string;
}
const LeftSidePanel: React.FC<InterfaceLeftSidePanel> = ({
textLabel
}) => {
return (
<>
<div className="hidden w-full xl:block xl:w-1/2">
<div className="text-center">
<p className="text-xl 2xl:px-20 my-5 text-black dark:text-white">
{textLabel}
</p>
<span className="mt-15 inline-block">
<Image
src="/green-house.webp"
alt="login-image"
width={480}
height={480}
className="w-108 h-108 mt-5"
/>
</span>
</div>
</div>
</>
);
};
export default LeftSidePanel;

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,76 @@
"use client"
import * as React from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
import { DayPicker } from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
export type CalendarProps = React.ComponentProps<typeof DayPicker>
function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
),
day: cn(
buttonVariants({ variant: "ghost" }),
"h-8 w-8 p-0 font-normal aria-selected:opacity-100"
),
day_range_start: "day-range-start",
day_range_end: "day-range-end",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: ({ className, ...props }) => (
<ChevronLeft className={cn("h-4 w-4", className)} {...props} />
),
IconRight: ({ className, ...props }) => (
<ChevronRight className={cn("h-4 w-4", className)} {...props} />
),
}}
{...props}
/>
)
}
Calendar.displayName = "Calendar"
export { Calendar }

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,117 @@
import * as React from "react"
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "@/lib/utils"
import { ButtonProps, buttonVariants } from "@/components/ui/button"
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
)
Pagination.displayName = "Pagination"
const PaginationContent = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
))
PaginationContent.displayName = "PaginationContent"
const PaginationItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
))
PaginationItem.displayName = "PaginationItem"
type PaginationLinkProps = {
isActive?: boolean
} & Pick<ButtonProps, "size"> &
React.ComponentProps<"a">
const PaginationLink = ({
className,
isActive,
size = "icon",
...props
}: PaginationLinkProps) => (
<a
aria-current={isActive ? "page" : undefined}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
size,
}),
className
)}
{...props}
/>
)
PaginationLink.displayName = "PaginationLink"
const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<span>Previous</span>
</PaginationLink>
)
PaginationPrevious.displayName = "PaginationPrevious"
const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>Next</span>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
)
PaginationNext.displayName = "PaginationNext"
const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
)
PaginationEllipsis.displayName = "PaginationEllipsis"
export {
Pagination,
PaginationContent,
PaginationLink,
PaginationItem,
PaginationPrevious,
PaginationNext,
PaginationEllipsis,
}

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,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 }

120
src/components/ui/table.tsx Normal file
View File

@@ -0,0 +1,120 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

View File

@@ -0,0 +1,78 @@
"use client";
import * as z from "zod";
import { ZodDecimal } from "./zodDecimal";
function convertApiValidationToZodValidation(apiValidation: any) {
let zodValidation: any = {};
Object.entries(apiValidation).forEach(([key, value]: any) => {
const fieldType: String = value.fieldType || "string";
const required = value.required || false;
if (fieldType === "string") {
zodValidation[key] = required
? z
.string()
.min(1)
.refine((val) => val !== "" || val !== null)
: z
.string()
.min(1)
.optional()
.refine((val) => val !== "" || val !== null);
} else if (fieldType === "integer") {
zodValidation[key] = required
? z.preprocess((value) => {
try {
const parsedValue = Number(value);
return isNaN(parsedValue) ? undefined : parsedValue;
} catch (error) {
return undefined;
}
}, z.number().min(1))
: z.preprocess((value) => {
try {
const parsedValue = Number(value);
return isNaN(parsedValue) ? undefined : parsedValue;
} catch (error) {
return undefined;
}
}, z.number().min(1).optional());
} else if (fieldType === "boolean") {
zodValidation[key] = required ? z.boolean() : z.boolean().optional();
} else if (fieldType === "datetime") {
zodValidation[key] = required ? z.date() : z.date().optional();
} else if (fieldType === "float") {
zodValidation[key] = required
? ZodDecimal.create({ coerce: true })
: ZodDecimal.create({ coerce: true }).optional();
}
});
const validSchemaZod = z.object({
...zodValidation,
});
return {
validSchemaZod: validSchemaZod,
zodValidation: zodValidation,
apiValidation: apiValidation,
};
}
function retrieveDataWhichHaveValidation(data: any, apiValidation: any) {
const apiValidated = apiValidation?.validated || {};
Object.entries(apiValidated).forEach(([key, value]: any) => {
const fieldType: String = value.fieldType || "string";
const required = value.required || false;
if (fieldType === "string") {
data[key] = required ? data[key] : data[key] || "";
} else if (fieldType === "integer") {
data[key] = required ? data[key] : data[key] || 0;
} else if (fieldType === "boolean") {
data[key] = required ? data[key] : data[key] || false;
} else if (fieldType === "datetime") {
data[key] = required ? data[key] : new Date(data[key]) || "";
} else if (fieldType === "float") {
data[key] = required ? data[key] : data[key] || 0.0;
}
});
}
export { convertApiValidationToZodValidation, retrieveDataWhichHaveValidation };

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))
}

306
src/lib/zodDecimal.ts Normal file
View File

@@ -0,0 +1,306 @@
import {
INVALID,
ParseContext,
ParseInput,
ParseReturnType,
ParseStatus,
RawCreateParams,
ZodIssueCode,
ZodParsedType,
ZodType,
ZodTypeDef,
addIssueToContext,
} from "zod";
export type ZodDecimalCheck =
| { kind: "precision"; value: number; message?: string }
| { kind: "wholeNumber"; value: number; message?: string }
| { kind: "min"; value: number; inclusive: boolean; message?: string }
| { kind: "max"; value: number; inclusive: boolean; message?: string }
| { kind: "finite"; message?: string };
const zodDecimalKind = "ZodDecimal";
export interface ZodDecimalDef extends ZodTypeDef {
checks: ZodDecimalCheck[];
typeName: typeof zodDecimalKind;
coerce: boolean;
}
const precisionRegex = /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class ZodDecimal extends ZodType<number, ZodDecimalDef, any> {
// eslint-disable-next-line @typescript-eslint/naming-convention
_parse(input: ParseInput): ParseReturnType<number> {
// detect decimal js object
if (
input.data !== null &&
typeof input.data === "object" &&
"toNumber" in input.data
) {
input.data = input.data.toNumber();
}
if (this._def.coerce) {
input.data = Number(input.data);
}
const parsedType = this._getType(input);
if (parsedType !== ZodParsedType.number) {
const ctx = this._getOrReturnCtx(input);
addIssueToContext(ctx, {
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.number,
received: ctx.parsedType,
});
return INVALID;
}
let ctx: undefined | ParseContext = undefined;
const status = new ParseStatus();
for (const check of this._def.checks) {
if (check.kind === "precision") {
const parts = input.data.toString().match(precisionRegex);
const decimals = Math.max(
(parts[1] ? parts[1].length : 0) -
(parts[2] ? parseInt(parts[2], 10) : 0),
0
);
if (decimals > check.value) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.custom,
message: check.message,
params: {
precision: check.value,
},
});
status.dirty();
}
} else if (check.kind === "wholeNumber") {
const wholeNumber = input.data.toString().split(".")[0];
const tooLong = wholeNumber.length > check.value;
if (tooLong) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.custom,
message: check.message,
params: {
wholeNumber: check.value,
},
});
status.dirty();
}
} else if (check.kind === "min") {
const tooSmall = check.inclusive
? input.data < check.value
: input.data <= check.value;
if (tooSmall) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_small,
minimum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "max") {
const tooBig = check.inclusive
? input.data > check.value
: input.data >= check.value;
if (tooBig) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.too_big,
maximum: check.value,
type: "number",
inclusive: check.inclusive,
exact: false,
message: check.message,
});
status.dirty();
}
} else if (check.kind === "finite") {
if (!Number.isFinite(input.data)) {
ctx = this._getOrReturnCtx(input, ctx);
addIssueToContext(ctx, {
code: ZodIssueCode.not_finite,
message: check.message,
});
status.dirty();
}
}
}
return { status: status.value, value: input.data };
}
static create = (
params?: RawCreateParams & { coerce?: true }
): ZodDecimal => {
return new ZodDecimal({
checks: [],
typeName: zodDecimalKind,
coerce: params?.coerce ?? false,
});
};
protected setLimit(
kind: "min" | "max",
value: number,
inclusive: boolean,
message?: string
): ZodDecimal {
return new ZodDecimal({
...this._def,
checks: [
...this._def.checks,
{
kind,
value,
inclusive,
message,
},
],
});
}
_addCheck(check: ZodDecimalCheck): ZodDecimal {
return new ZodDecimal({
...this._def,
checks: [...this._def.checks, check],
});
}
lte(value: number, message?: string): ZodDecimal {
return this.setLimit("max", value, true, message);
}
lt(value: number, message?: string): ZodDecimal {
return this.setLimit("max", value, false, message);
}
max = this.lte;
gt(value: number, message?: string): ZodDecimal {
return this.setLimit("min", value, false, message);
}
gte(value: number, message?: string): ZodDecimal {
return this.setLimit("min", value, true, message);
}
min = this.gte;
precision(value: number, message?: string): ZodDecimal {
return this._addCheck({
kind: "precision",
value,
message,
});
}
wholeNumber(value: number, message?: string): ZodDecimal {
return this._addCheck({
kind: "wholeNumber",
value,
message,
});
}
get minValue() {
let min: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "min") {
if (min === null || ch.value > min) min = ch.value;
}
}
return min;
}
get maxValue() {
let max: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "max") {
if (max === null || ch.value < max) max = ch.value;
}
}
return max;
}
positive(message?: string) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: false,
message,
});
}
negative(message?: string) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: false,
message,
});
}
nonpositive(message?: string) {
return this._addCheck({
kind: "max",
value: 0,
inclusive: true,
message,
});
}
nonnegative(message?: string) {
return this._addCheck({
kind: "min",
value: 0,
inclusive: true,
message,
});
}
finite(message?: string) {
return this._addCheck({
kind: "finite",
message,
});
}
safe(message?: string) {
return this._addCheck({
kind: "min",
inclusive: true,
value: Number.MIN_SAFE_INTEGER,
message,
})._addCheck({
kind: "max",
inclusive: true,
value: Number.MAX_SAFE_INTEGER,
message,
});
}
get isFinite() {
let max: number | null = null,
min: number | null = null;
for (const ch of this._def.checks) {
if (ch.kind === "finite") {
return true;
} else if (ch.kind === "min") {
if (min === null || ch.value > min) min = ch.value;
} else if (ch.kind === "max") {
if (max === null || ch.value < max) max = ch.value;
}
}
return Number.isFinite(min) && Number.isFinite(max);
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
export const zodDecimal = ZodDecimal.create;

View File

@@ -0,0 +1,34 @@
"use server";
import React from "react";
import SelectEmployeeFrom from "./selectFrom";
import { redirect } from "next/navigation";
// import LeftSidePanel from "@/components/login/leftsidepanel";
{
/* <LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz" /> */
}
interface InterfaceLoginSelectEmployee {
companyList: any;
}
const LoginSelectEmployeeCard: React.FC<InterfaceLoginSelectEmployee> = async (
companyList: any
) => {
if (!companyList) {
redirect("/login/email");
}
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>
<SelectEmployeeFrom companyList={companyList?.companyList} />
</div>
</div>
</>
);
};
export default LoginSelectEmployeeCard;

View File

@@ -0,0 +1,70 @@
"use client";
import React from "react";
import Image from "next/image";
import { loginSelectEmployee } from "@/apicalls/login/login";
import { useRouter } from "next/navigation";
interface InterfaceSelectEmployeeFrom {
companyList: any;
}
const SelectEmployeeFrom: React.FC<InterfaceSelectEmployeeFrom> = ({
companyList,
}) => {
const router = useRouter();
console.log("companyList", companyList);
function onClick(data: any) {
loginSelectEmployee({ company_uu_id: data?.uu_id })
.then((responseData: any) => {
if (responseData?.completed) {
router.push("/dashboard");
}
})
.catch((error) => {
console.error(error);
});
}
return (
<>
<div className="sm:grid sm:grid-cols-3 sm:gap-4">
{companyList.map((data: any) => (
<div className="flex" key={data.uu_id}>
<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="lg:w-1/4">
<Image
src={"/green-house.webp"}
alt={`Evyos ${data.uu_id}`}
className=" w-full h-full object-cover"
width={300}
height={300}
/>
</div>
<div className="lg: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>
</>
);
};
export default SelectEmployeeFrom;

View File

@@ -0,0 +1,30 @@
"use server";
import React from "react";
import SelectOccupantFrom from "./selectFrom";
// import LeftSidePanel from "@/components/login/leftsidepanel";
{
/* <LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz. Lütfen mail adresinizi ve şifreniz ile giriş yapınız." /> */
}
interface interfaceLoginSelectOccupant {
availableOccupants: any;
}
const LoginSelectOccupantCard: React.FC<interfaceLoginSelectOccupant> = async ({
availableOccupants,
}) => {
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>
<SelectOccupantFrom availableOccupants={availableOccupants} />
</div>
</div>
</>
);
};
export default LoginSelectOccupantCard;

View File

@@ -0,0 +1,154 @@
"use client";
import React from "react";
import { useRouter } from "next/navigation";
import { loginSelectOccupant } from "@/apicalls/login/login";
import Image from "next/image";
interface Building {
build_uu_id: string;
build_name: string;
build_no: string;
occupants: Array<any>;
}
interface InterfaceSelectOccupanyFrom {
availableOccupants: Record<string, Building>;
}
const SelectOccupantFrom: React.FC<InterfaceSelectOccupanyFrom> = ({
availableOccupants,
}) => {
const router = useRouter();
const [activeBuildingList, setActiveBuildingList] = React.useState<
Building[]
>([]);
const [activeOccupantList, setActiveOccupantList] = React.useState<any[]>([]);
const [isBuildingSelected, setIsBuildingSelected] = React.useState(false);
const [selectedBuilding, setSelectedBuilding] = React.useState("");
React.useEffect(() => {
if (!isBuildingSelected) {
const uniqueBuildings = Object.values(availableOccupants).map(
(value) => ({
build_uu_id: value.build_uu_id,
build_name: value.build_name,
build_no: value.build_no,
occupants: value.occupants,
})
);
setActiveBuildingList(uniqueBuildings);
}
}, [availableOccupants, isBuildingSelected]);
React.useEffect(() => {
if (isBuildingSelected && selectedBuilding) {
const selectedOccupants =
Object.values(availableOccupants).find(
(value) => value.build_uu_id === selectedBuilding
)?.occupants || [];
setActiveOccupantList(selectedOccupants);
}
}, [isBuildingSelected, selectedBuilding, availableOccupants]);
const onClickBuild = (data: Building) => {
setSelectedBuilding(data.build_uu_id);
setIsBuildingSelected(true);
};
const onClick = (data: any) => {
loginSelectOccupant({
build_part_uu_id: data?.part_uu_id,
occupant_uu_id: data?.uu_id,
selectedBuilding: selectedBuilding,
})
.then((responseData: { completed: boolean }) => {
console.log("responseData", responseData);
console.log("responseData.completed", responseData?.completed);
if (responseData?.completed) {
router.replace("/dashboard");
}
})
.catch((error) => {
console.error("Login error:", error);
});
};
return (
<>
{!isBuildingSelected ? (
<div className="3xl:grid 3xl:grid-cols-3 3xl: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="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="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="3xl:grid 3xl:grid-cols-3 3xl: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="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="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>
)}
</>
);
};
export default SelectOccupantFrom;

View File

@@ -0,0 +1,23 @@
"use client";
import React from "react";
import SignInForm from "./singInForm";
import LeftSidePanel from "@/components/login/leftsidepanel";
const LoginWithEmail: React.FC = () => {
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">
<LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz. Lütfen mail adresinizi ve şifreniz ile giriş yapınız." />
<SignInForm />
</div>
</div>
</div>
</div>
</>
);
};
export default LoginWithEmail;

View File

@@ -0,0 +1,17 @@
import * as z from "zod";
const loginSchema = 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),
});
export type LoginFormSchema = z.infer<typeof loginSchema>;
export { loginSchema };

View File

@@ -0,0 +1,154 @@
"use client";
import React from "react";
import { Eye, EyeOff, Mail } from "lucide-react";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { loginViaAccessKeys } from "@/apicalls/login/login";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Switch } from "@/components/ui/switch";
import { loginSchema, LoginFormSchema } from "./schema";
import {
GoogleButton,
LoginButton,
ChangeSignTypeButton,
} from "@/components/login/buttons";
const SignInForm: React.FC = () => {
const router = useRouter();
const [showPassword, setShowPassword] = React.useState(false);
const form = useForm<LoginFormSchema>({
resolver: zodResolver(loginSchema),
});
function onSubmit(values: LoginFormSchema) {
loginViaAccessKeys({
domain: "evyos.com.tr",
accessKey: values.loginEmailInput,
password: values.loginPassword,
rememberMe: values.loginRememberMe,
})
.then((res: any) => {
console.log("res", res);
if (res?.completed) {
setTimeout(() => {
router.push("/login/select");
}, 1000);
}
})
.catch((error) => {
console.log("error", error);
});
}
return (
<>
<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">
<h2 className="my-9 text-center text-2xl font-bold text-black dark:text-white sm:text-title-xl2">
Mail ile Giriş Yapın
</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}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span className="absolute right-4 top-4">
<Mail />
</span>
</div>
</div>
<div className="mb-6">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Password
</label>
<div className="relative">
<FormField
control={form.control}
name="loginPassword"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type={showPassword ? "text" : "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}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span
className="absolute right-4 top-4"
onClick={() => setShowPassword(!showPassword)}
>
{!showPassword ? <Eye /> : <EyeOff />}
</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>
)}
/>
<LoginButton />
<GoogleButton />
</form>
</Form>
<ChangeSignTypeButton buttonType="phone" />
</div>
</div>
</>
);
};
export default SignInForm;

View File

@@ -0,0 +1,23 @@
"use client";
import React from "react";
import SignPhoneInForm from "./singInForm";
import LeftSidePanel from "@/components/login/leftsidepanel";
const LoginWithPhone: React.FC = () => {
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">
<LeftSidePanel textLabel="Evyos Yönetim Modülüne hoşgeldiniz. Lütfen telefon ve şifreniz ile giriş yapınız." />
<SignPhoneInForm />
</div>
</div>
</div>
</div>
</>
);
};
export default LoginWithPhone;

View File

@@ -0,0 +1,17 @@
import * as z from "zod";
const loginPhoneSchema = 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),
});
export type LoginPhoneFormSchema = z.infer<typeof loginPhoneSchema>;
export { loginPhoneSchema };

View File

@@ -0,0 +1,153 @@
"use client";
import React from "react";
import { Eye, EyeOff, Mail } from "lucide-react";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { loginViaAccessKeys } from "@/apicalls/login/login";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Switch } from "@/components/ui/switch";
import { LoginPhoneFormSchema, loginPhoneSchema } from "./schema";
import {
GoogleButton,
LoginButton,
ChangeSignTypeButton,
} from "@/components/login/buttons";
const SignPhoneInForm: React.FC = () => {
const router = useRouter();
const [showPassword, setShowPassword] = React.useState(false);
const form = useForm<LoginPhoneFormSchema>({
resolver: zodResolver(loginPhoneSchema),
});
function onSubmit(values: LoginPhoneFormSchema) {
loginViaAccessKeys({
domain: "evyos.com.tr",
accessKey: values.loginPhone,
password: values.loginPassword,
rememberMe: values.loginRememberMe,
})
.then((res: any) => {
if (res?.completed) {
setTimeout(() => {
router.push("/login/select");
}, 1000);
}
})
.catch((error) => {
console.log("error", error);
});
}
return (
<>
<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">
<h2 className="my-9 text-center text-2xl font-bold text-black dark:text-white sm:text-title-xl2">
Mail ile Giriş Yapın
</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="loginPhone"
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}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span className="absolute right-4 top-4">
<Mail />
</span>
</div>
</div>
<div className="mb-6">
<label className="mb-2.5 block font-medium text-black dark:text-white">
Password
</label>
<div className="relative">
<FormField
control={form.control}
name="loginPassword"
render={({ field }) => (
<FormItem>
<FormControl>
<input
type={showPassword ? "text" : "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}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<span
className="absolute right-4 top-4"
onClick={() => setShowPassword(!showPassword)}
>
{!showPassword ? <Eye /> : <EyeOff />}
</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>
)}
/>
<LoginButton />
<GoogleButton />
</form>
</Form>
<ChangeSignTypeButton buttonType="email" />
</div>
</div>
</>
);
};
export default SignPhoneInForm;