diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..74baffc --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["denoland.vscode-deno"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0dccffe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "deno.enablePaths": ["supabase/functions"], + "deno.lint": true, + "deno.unstable": [ + "bare-node-builtins", + "byonm", + "sloppy-imports", + "unsafe-proto", + "webgpu", + "broadcast-channel", + "worker-options", + "cron", + "kv", + "ffi", + "fs", + "http", + "net" + ], + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/sigap-website/.env.example b/sigap-website/.env.example deleted file mode 100644 index 6937031..0000000 --- a/sigap-website/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# Update these with your Supabase details from your project settings > API -# https://app.supabase.com/project/_/settings/api -NEXT_PUBLIC_SUPABASE_URL=your-project-url -NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key diff --git a/sigap-website/actions/auth/forgot-password.ts b/sigap-website/actions/auth/forgot-password.ts new file mode 100644 index 0000000..bceba0b --- /dev/null +++ b/sigap-website/actions/auth/forgot-password.ts @@ -0,0 +1,38 @@ +import { encodedRedirect } from "@/utils/utils"; +import { createClient } from "@/utils/supabase/server"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const forgotPasswordAction = async (formData: FormData) => { + const email = formData.get("email")?.toString(); + const supabase = await createClient(); + const origin = (await headers()).get("origin"); + const callbackUrl = formData.get("callbackUrl")?.toString(); + + if (!email) { + return encodedRedirect("error", "/forgot-password", "Email is required"); + } + + const { error } = await supabase.auth.resetPasswordForEmail(email, { + redirectTo: `${origin}/auth/callback?redirect_to=/protected/reset-password`, + }); + + if (error) { + console.error(error.message); + return encodedRedirect( + "error", + "/forgot-password", + "Could not reset password" + ); + } + + if (callbackUrl) { + return redirect(callbackUrl); + } + + return encodedRedirect( + "success", + "/forgot-password", + "Check your email for a link to reset your password." + ); +}; diff --git a/sigap-website/actions/auth/reset-password.ts b/sigap-website/actions/auth/reset-password.ts new file mode 100644 index 0000000..b38bb95 --- /dev/null +++ b/sigap-website/actions/auth/reset-password.ts @@ -0,0 +1,41 @@ +import { encodedRedirect } from "@/utils/utils"; +import { createClient } from "@/utils/supabase/server"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const resetPasswordAction = async (formData: FormData) => { + const supabase = await createClient(); + + const password = formData.get("password") as string; + const confirmPassword = formData.get("confirmPassword") as string; + + if (!password || !confirmPassword) { + encodedRedirect( + "error", + "/protected/reset-password", + "Password and confirm password are required" + ); + } + + if (password !== confirmPassword) { + encodedRedirect( + "error", + "/protected/reset-password", + "Passwords do not match" + ); + } + + const { error } = await supabase.auth.updateUser({ + password: password, + }); + + if (error) { + encodedRedirect( + "error", + "/protected/reset-password", + "Password update failed" + ); + } + + encodedRedirect("success", "/protected/reset-password", "Password updated"); +}; diff --git a/sigap-website/actions/auth/sign-in.ts b/sigap-website/actions/auth/sign-in.ts new file mode 100644 index 0000000..bc0811c --- /dev/null +++ b/sigap-website/actions/auth/sign-in.ts @@ -0,0 +1,34 @@ +"use server"; +import { encodedRedirect } from "@/utils/utils"; +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; + +export const signInAction = async (formData: FormData) => { + const email = formData.get("email") as string; + const supabase = await createClient(); + + if (!email || email === "") { + return encodedRedirect("error", "/sign-in", "Email is required"); + } + + const { data, error } = await supabase.auth.signInWithOtp({ + email, + options: { + shouldCreateUser: true, + }, + }); + + if (error && error.message.includes("User not found")) { + // Encode email parameter untuk keamanan + const encodedEmail = encodeURIComponent(email); + return redirect(`/sign-in?email=${encodedEmail}`); + } + + if (error) { + return encodedRedirect("error", "/sign-in", error.message); + } + + // Redirect dengan email parameter + const encodedEmail = encodeURIComponent(email); + return redirect(`/verify-otp?email=${encodedEmail}`); +}; diff --git a/sigap-website/actions/auth/sign-out.ts b/sigap-website/actions/auth/sign-out.ts new file mode 100644 index 0000000..8d50d8c --- /dev/null +++ b/sigap-website/actions/auth/sign-out.ts @@ -0,0 +1,10 @@ +import { encodedRedirect } from "@/utils/utils"; +import { createClient } from "@/utils/supabase/server"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const signOutAction = async () => { + const supabase = await createClient(); + await supabase.auth.signOut(); + return redirect("/sign-in"); +}; diff --git a/sigap-website/actions/auth/sign-up-action.ts b/sigap-website/actions/auth/sign-up-action.ts new file mode 100644 index 0000000..5d279e4 --- /dev/null +++ b/sigap-website/actions/auth/sign-up-action.ts @@ -0,0 +1,38 @@ +import { encodedRedirect } from "@/utils/utils"; +import { createClient } from "@/utils/supabase/server"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const signUpAction = async (formData: FormData) => { + const email = formData.get("email")?.toString(); + const password = formData.get("password")?.toString(); + const supabase = await createClient(); + const origin = (await headers()).get("origin"); + + if (!email || !password) { + return encodedRedirect( + "error", + "/sign-up", + "Email and password are required", + ); + } + + const { error } = await supabase.auth.signUp({ + email, + password, + options: { + emailRedirectTo: `${origin}/auth/callback`, + }, + }); + + if (error) { + console.error(error.code + " " + error.message); + return encodedRedirect("error", "/sign-up", error.message); + } else { + return encodedRedirect( + "success", + "/sign-up", + "Thanks for signing up! Please check your email for a verification link.", + ); + } +}; \ No newline at end of file diff --git a/sigap-website/actions/auth/verify-otp.ts b/sigap-website/actions/auth/verify-otp.ts new file mode 100644 index 0000000..6f3ee91 --- /dev/null +++ b/sigap-website/actions/auth/verify-otp.ts @@ -0,0 +1,32 @@ +"use server"; +import { createClient } from "@/utils/supabase/server"; +import { encodedRedirect } from "@/utils/utils"; +import { redirect } from "next/navigation"; + +export const verifyOtpAction = async (formData: FormData) => { + const email = formData.get("email") as string; + const token = formData.get("token") as string; + const supabase = await createClient(); + + console.log("email", email); + console.log("token", token); + + if (!email || !token) { + redirect("/error?message=Email and OTP are required"); + } + + const { + data: { session }, + error, + } = await supabase.auth.verifyOtp({ + email, + token, + type: "email", + }); + + if (error) { + return redirect(`/verify-otp?error=${encodeURIComponent(error.message)}`); + } + + return redirect("/protected/dashboard?message=OTP verified successfully"); +}; diff --git a/sigap-website/app/(auth-pages)/contact-admin/page.tsx b/sigap-website/app/(auth-pages)/contact-admin/page.tsx new file mode 100644 index 0000000..f85f8be --- /dev/null +++ b/sigap-website/app/(auth-pages)/contact-admin/page.tsx @@ -0,0 +1,51 @@ +import { ContactAdminForm } from "@/components/auth/contact-admin"; +import { Button } from "@/components/ui/button"; +import { GalleryVerticalEnd, Globe } from "lucide-react"; + +export default async function ContactAdminPage() { + return ( +
+
+
+ +
+ +
+ Sigap Tech. +
+
+
+
+ +
+
+
+
+ +
+
"
+

+ @Sigap Tech. Is the best to manage your crime data and report. +

+
+ Profile +
+

@codewithbhargav

+
+
+
+
+
+ ); +} diff --git a/sigap-website/app/(auth-pages)/forgot-password/page.tsx b/sigap-website/app/(auth-pages)/forgot-password/page.tsx index bcf9725..dca53ed 100644 --- a/sigap-website/app/(auth-pages)/forgot-password/page.tsx +++ b/sigap-website/app/(auth-pages)/forgot-password/page.tsx @@ -1,10 +1,10 @@ -import { forgotPasswordAction } from "@/app/actions"; import { FormMessage, Message } from "@/components/form-message"; import { SubmitButton } from "@/components/submit-button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import Link from "next/link"; import { SmtpMessage } from "../smtp-message"; +import { forgotPasswordAction } from "@/actions/auth/forgot-password"; export default async function ForgotPassword(props: { searchParams: Promise; diff --git a/sigap-website/app/(auth-pages)/layout.tsx b/sigap-website/app/(auth-pages)/layout.tsx index e038de1..e3980ef 100644 --- a/sigap-website/app/(auth-pages)/layout.tsx +++ b/sigap-website/app/(auth-pages)/layout.tsx @@ -4,6 +4,6 @@ export default async function Layout({ children: React.ReactNode; }) { return ( -
{children}
+
{children}
); } diff --git a/sigap-website/app/(auth-pages)/sign-in/page.tsx b/sigap-website/app/(auth-pages)/sign-in/page.tsx index 7628cc7..68dfd0e 100644 --- a/sigap-website/app/(auth-pages)/sign-in/page.tsx +++ b/sigap-website/app/(auth-pages)/sign-in/page.tsx @@ -1,44 +1,58 @@ -import { signInAction } from "@/app/actions"; import { FormMessage, Message } from "@/components/form-message"; +import { LoginForm } from "@/components/auth/login-form"; import { SubmitButton } from "@/components/submit-button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Button } from "@/components/ui/button"; +import { GalleryVerticalEnd, Book, Globe } from "lucide-react"; import Link from "next/link"; +import { LoginForm2 } from "@/components/auth/login-form-2"; export default async function Login(props: { searchParams: Promise }) { const searchParams = await props.searchParams; return ( -
-

Sign in

-

- Don't have an account?{" "} - - Sign up - -

-
- - +
+
- - - Forgot Password? - + +
+ +
+ Sigap Tech. +
+
+
+
+ +
- - - Sign in - -
- +
+ +
+
"
+

+ @Sigap Tech. Is the best to manage your crime data and report. +

+
+ Profile +
+

@codewithbhargav

+
+
+
+
+
); } diff --git a/sigap-website/app/(auth-pages)/sign-up/page.tsx b/sigap-website/app/(auth-pages)/sign-up/page.tsx index 31b5a6d..9b8c18c 100644 --- a/sigap-website/app/(auth-pages)/sign-up/page.tsx +++ b/sigap-website/app/(auth-pages)/sign-up/page.tsx @@ -1,12 +1,12 @@ -import { signUpAction } from "@/app/actions"; import { FormMessage, Message } from "@/components/form-message"; import { SubmitButton } from "@/components/submit-button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import Link from "next/link"; import { SmtpMessage } from "../smtp-message"; +import { signUpAction } from "@/actions/auth/sign-up-action"; -export default async function Signup(props: { +export default async function SignupPage(props: { searchParams: Promise; }) { const searchParams = await props.searchParams; diff --git a/sigap-website/app/(auth-pages)/verify-otp/page.tsx b/sigap-website/app/(auth-pages)/verify-otp/page.tsx new file mode 100644 index 0000000..5580a19 --- /dev/null +++ b/sigap-website/app/(auth-pages)/verify-otp/page.tsx @@ -0,0 +1,27 @@ +import { ContactAdminForm } from "@/components/auth/contact-admin"; +import { InputOTPForm } from "@/components/auth/otp-form"; +import { VerifyOtpForm } from "@/components/auth/verify-otp-form"; + +import { GalleryVerticalEnd } from "lucide-react"; + +export default async function VerifyOtpPage() { + return ( +
+
+ +
+
+ +
+
+
+
+ ); +} diff --git a/sigap-website/app/actions.ts b/sigap-website/app/actions.ts deleted file mode 100644 index dbf8a26..0000000 --- a/sigap-website/app/actions.ts +++ /dev/null @@ -1,134 +0,0 @@ -"use server"; - -import { encodedRedirect } from "@/utils/utils"; -import { createClient } from "@/utils/supabase/server"; -import { headers } from "next/headers"; -import { redirect } from "next/navigation"; - -export const signUpAction = async (formData: FormData) => { - const email = formData.get("email")?.toString(); - const password = formData.get("password")?.toString(); - const supabase = await createClient(); - const origin = (await headers()).get("origin"); - - if (!email || !password) { - return encodedRedirect( - "error", - "/sign-up", - "Email and password are required", - ); - } - - const { error } = await supabase.auth.signUp({ - email, - password, - options: { - emailRedirectTo: `${origin}/auth/callback`, - }, - }); - - if (error) { - console.error(error.code + " " + error.message); - return encodedRedirect("error", "/sign-up", error.message); - } else { - return encodedRedirect( - "success", - "/sign-up", - "Thanks for signing up! Please check your email for a verification link.", - ); - } -}; - -export const signInAction = async (formData: FormData) => { - const email = formData.get("email") as string; - const password = formData.get("password") as string; - const supabase = await createClient(); - - const { error } = await supabase.auth.signInWithPassword({ - email, - password, - }); - - if (error) { - return encodedRedirect("error", "/sign-in", error.message); - } - - return redirect("/protected"); -}; - -export const forgotPasswordAction = async (formData: FormData) => { - const email = formData.get("email")?.toString(); - const supabase = await createClient(); - const origin = (await headers()).get("origin"); - const callbackUrl = formData.get("callbackUrl")?.toString(); - - if (!email) { - return encodedRedirect("error", "/forgot-password", "Email is required"); - } - - const { error } = await supabase.auth.resetPasswordForEmail(email, { - redirectTo: `${origin}/auth/callback?redirect_to=/protected/reset-password`, - }); - - if (error) { - console.error(error.message); - return encodedRedirect( - "error", - "/forgot-password", - "Could not reset password", - ); - } - - if (callbackUrl) { - return redirect(callbackUrl); - } - - return encodedRedirect( - "success", - "/forgot-password", - "Check your email for a link to reset your password.", - ); -}; - -export const resetPasswordAction = async (formData: FormData) => { - const supabase = await createClient(); - - const password = formData.get("password") as string; - const confirmPassword = formData.get("confirmPassword") as string; - - if (!password || !confirmPassword) { - encodedRedirect( - "error", - "/protected/reset-password", - "Password and confirm password are required", - ); - } - - if (password !== confirmPassword) { - encodedRedirect( - "error", - "/protected/reset-password", - "Passwords do not match", - ); - } - - const { error } = await supabase.auth.updateUser({ - password: password, - }); - - if (error) { - encodedRedirect( - "error", - "/protected/reset-password", - "Password update failed", - ); - } - - encodedRedirect("success", "/protected/reset-password", "Password updated"); -}; - -export const signOutAction = async () => { - const supabase = await createClient(); - await supabase.auth.signOut(); - return redirect("/sign-in"); -}; diff --git a/sigap-website/app/globals.css b/sigap-website/app/globals.css index f450d1e..d379bcb 100644 --- a/sigap-website/app/globals.css +++ b/sigap-website/app/globals.css @@ -5,57 +5,73 @@ @layer base { :root { --background: 0 0% 100%; - --foreground: 0 0% 3.9%; + --foreground: 240 10% 3.9%; --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; + --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; + --popover-foreground: 240 10% 3.9%; + --primary: 153 60% 53%; /* Supabase green */ --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%; + --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: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 3.9%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 153 60% 53%; /* Matching primary */ --radius: 0.5rem; - --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%; + --chart-1: 153 60% 53%; /* Supabase green */ + --chart-2: 183 65% 50%; + --chart-3: 213 70% 47%; + --chart-4: 243 75% 44%; + --chart-5: 273 80% 41%; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; } - + .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%; + --background: 0 0% 9%; /* #171717 */ + --foreground: 210 20% 98%; + --card: 0 0% 9%; /* #171717 */ + --card-foreground: 210 20% 98%; + --popover: 0 0% 9%; /* #171717 */ + --popover-foreground: 210 20% 98%; + --primary: 153 60% 53%; /* Supabase green */ + --primary-foreground: 210 20% 98%; + --secondary: 220 8% 15%; + --secondary-foreground: 210 20% 98%; + --muted: 220 8% 15%; + --muted-foreground: 217 10% 64%; + --accent: 220 8% 15%; + --accent-foreground: 210 20% 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%; + --destructive-foreground: 210 20% 98%; + --border: 220 8% 15%; + --input: 220 8% 15%; + --ring: 153 60% 53%; /* Matching primary */ + --chart-1: 153 60% 53%; /* Supabase green */ + --chart-2: 183 65% 50%; + --chart-3: 213 70% 47%; + --chart-4: 243 75% 44%; + --chart-5: 273 80% 41%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; } } diff --git a/sigap-website/app/layout.tsx b/sigap-website/app/layout.tsx index 8f6dcb7..172c969 100644 --- a/sigap-website/app/layout.tsx +++ b/sigap-website/app/layout.tsx @@ -7,6 +7,9 @@ import { Geist } from "next/font/google"; import { ThemeProvider } from "next-themes"; import Link from "next/link"; import "./globals.css"; +import SupabaseLogo from "@/components/logo/supabase-logo"; +import SigapLogo from "@/components/logo/sigap-logo"; +import { Toaster } from "@/components/ui/toaster"; const defaultUrl = process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` @@ -38,36 +41,41 @@ export default function RootLayout({ disableTransitionOnChange >
-
- */} +
{children} +
- */}
diff --git a/sigap-website/app/protected/(admin-pages)/dashboard/page.tsx b/sigap-website/app/protected/(admin-pages)/dashboard/page.tsx new file mode 100644 index 0000000..4bfe34b --- /dev/null +++ b/sigap-website/app/protected/(admin-pages)/dashboard/page.tsx @@ -0,0 +1,52 @@ +import { AppSidebar } from "@/components/app-sidebar"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb"; +import { Separator } from "@/components/ui/separator"; +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar"; + +export default function Page() { + return ( + + + +
+
+ + + + + + + Building Your Application + + + + + Data Fetching + + + +
+
+
+
+
+
+
+
+
+
+ + + ); +} diff --git a/sigap-website/app/protected/reset-password/page.tsx b/sigap-website/app/protected/reset-password/page.tsx index 9cd7084..0f104c3 100644 --- a/sigap-website/app/protected/reset-password/page.tsx +++ b/sigap-website/app/protected/reset-password/page.tsx @@ -1,4 +1,4 @@ -import { resetPasswordAction } from "@/app/actions"; +import { resetPasswordAction } from "@/actions/auth/reset-password"; import { FormMessage, Message } from "@/components/form-message"; import { SubmitButton } from "@/components/submit-button"; import { Input } from "@/components/ui/input"; diff --git a/sigap-website/components.example.json b/sigap-website/components.example.json new file mode 100644 index 0000000..ec9676b --- /dev/null +++ b/sigap-website/components.example.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} diff --git a/sigap-website/components.json b/sigap-website/components.json index ec9676b..a312865 100644 --- a/sigap-website/components.json +++ b/sigap-website/components.json @@ -1,17 +1,21 @@ { "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", + "style": "new-york", "rsc": true, "tsx": true, "tailwind": { "config": "tailwind.config.ts", "css": "app/globals.css", - "baseColor": "neutral", + "baseColor": "zinc", "cssVariables": true, "prefix": "" }, "aliases": { "components": "@/components", - "utils": "@/lib/utils" - } -} + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/sigap-website/components/app-sidebar.tsx b/sigap-website/components/app-sidebar.tsx new file mode 100644 index 0000000..94ad3a0 --- /dev/null +++ b/sigap-website/components/app-sidebar.tsx @@ -0,0 +1,175 @@ +"use client" + +import * as React from "react" +import { + AudioWaveform, + BookOpen, + Bot, + Command, + Frame, + GalleryVerticalEnd, + Map, + PieChart, + Settings2, + SquareTerminal, +} from "lucide-react" + +import { NavMain } from "@/components/nav-main" +import { NavProjects } from "@/components/nav-projects" +import { NavUser } from "@/components/nav-user" +import { TeamSwitcher } from "@/components/team-switcher" +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarRail, +} from "@/components/ui/sidebar" + +// This is sample data. +const data = { + user: { + name: "shadcn", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg", + }, + teams: [ + { + name: "Acme Inc", + logo: GalleryVerticalEnd, + plan: "Enterprise", + }, + { + name: "Acme Corp.", + logo: AudioWaveform, + plan: "Startup", + }, + { + name: "Evil Corp.", + logo: Command, + plan: "Free", + }, + ], + navMain: [ + { + title: "Playground", + url: "#", + icon: SquareTerminal, + isActive: true, + items: [ + { + title: "History", + url: "#", + }, + { + title: "Starred", + url: "#", + }, + { + title: "Settings", + url: "#", + }, + ], + }, + { + title: "Models", + url: "#", + icon: Bot, + items: [ + { + title: "Genesis", + url: "#", + }, + { + title: "Explorer", + url: "#", + }, + { + title: "Quantum", + url: "#", + }, + ], + }, + { + title: "Documentation", + url: "#", + icon: BookOpen, + items: [ + { + title: "Introduction", + url: "#", + }, + { + title: "Get Started", + url: "#", + }, + { + title: "Tutorials", + url: "#", + }, + { + title: "Changelog", + url: "#", + }, + ], + }, + { + title: "Settings", + url: "#", + icon: Settings2, + items: [ + { + title: "General", + url: "#", + }, + { + title: "Team", + url: "#", + }, + { + title: "Billing", + url: "#", + }, + { + title: "Limits", + url: "#", + }, + ], + }, + ], + projects: [ + { + name: "Design Engineering", + url: "#", + icon: Frame, + }, + { + name: "Sales & Marketing", + url: "#", + icon: PieChart, + }, + { + name: "Travel", + url: "#", + icon: Map, + }, + ], +} + +export function AppSidebar({ ...props }: React.ComponentProps) { + return ( + + + + + + + + + + + + + + ) +} diff --git a/sigap-website/components/auth/contact-admin.tsx b/sigap-website/components/auth/contact-admin.tsx new file mode 100644 index 0000000..cd6c719 --- /dev/null +++ b/sigap-website/components/auth/contact-admin.tsx @@ -0,0 +1,125 @@ +import * as React from "react"; + +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Textarea } from "../ui/textarea"; +import { SubmitButton } from "../submit-button"; +import Link from "next/link"; + +export function ContactAdminForm() { + const typeMessage = [ + { value: "1", label: "Request to become a user" }, + { value: "2", label: "OTP problem" }, + ]; + + return ( + + + Contact Us + + Deploy your new project in one-click. + + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +