From 446f8cce486f4528888038651c0da3929c2a7adf Mon Sep 17 00:00:00 2001 From: sirin Date: Wed, 23 Oct 2024 16:18:12 +0700 Subject: [PATCH] feat: add admin page with reject/approve functionalities --- package.json | 1 + pnpm-lock.yaml | 44 ++++++++++ public/favicon.ico | Bin 0 -> 8383 bytes src/app/admin/BusinessActionButtons.tsx | 104 ++++++++++++++++++++++ src/app/admin/page.tsx | 109 ++++++++++++++++++++++++ src/components/ui/checkbox.tsx | 30 +++++++ src/lib/data/applicationMutate.ts | 23 +++++ src/lib/data/userQuery.ts | 6 +- 8 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 public/favicon.ico create mode 100644 src/app/admin/BusinessActionButtons.tsx create mode 100644 src/app/admin/page.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/lib/data/applicationMutate.ts diff --git a/package.json b/package.json index 2bed454..4b6bbcb 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@hookform/resolvers": "^3.9.0", "@radix-ui/react-avatar": "^1.1.0", + "@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-hover-card": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e7705f..7299522 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@radix-ui/react-avatar': specifier: ^1.1.0 version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-checkbox': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-dialog': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) @@ -119,6 +122,9 @@ dependencies: stripe: specifier: ^17.1.0 version: 17.2.0 + sweetalert2: + specifier: ^11.14.3 + version: 11.14.4 tailwind-merge: specifier: ^2.5.2 version: 2.5.2 @@ -151,6 +157,9 @@ devDependencies: '@types/react-dom': specifier: ^18 version: 18.3.0 + '@types/react-select-country-list': + specifier: ^2.2.3 + version: 2.2.3 eslint: specifier: ^8 version: 8.57.0 @@ -668,6 +677,33 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-checkbox@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} peerDependencies: @@ -1847,6 +1883,10 @@ packages: dependencies: '@types/react': 18.3.4 + /@types/react-select-country-list@2.2.3: + resolution: {integrity: sha512-nffcYOwuun+5B0EWqubK+amHpPdK9Xj20xkLYNqYrzmESd8FnpLwHsS79ClLAWA9y+icVA8gWPkbwBp1gpjSwA==} + dev: true + /@types/react@18.3.4: resolution: {integrity: sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==} dependencies: @@ -5690,6 +5730,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /sweetalert2@11.14.4: + resolution: {integrity: sha512-8QMzjxCuinwm18EK5AtYvuhP+lRMRxTWVXy8om9wGlULsXSI4TD29kyih3VYrSXMMBlD4EShFvNC7slhTC7j0w==} + dev: false + /tailwind-merge@2.5.2: resolution: {integrity: sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==} dev: false diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..f6bd5cb254e49d820cfed4512de5f49ba8a2ff2a GIT binary patch literal 8383 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 Q{|iS$O;@#2*)rmP0Qo~J@&Et; literal 0 HcmV?d00001 diff --git a/src/app/admin/BusinessActionButtons.tsx b/src/app/admin/BusinessActionButtons.tsx new file mode 100644 index 0000000..ef213db --- /dev/null +++ b/src/app/admin/BusinessActionButtons.tsx @@ -0,0 +1,104 @@ +"use client"; + +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Check, X } from "lucide-react"; +import { useState } from "react"; +import { createSupabaseClient } from "@/lib/supabase/clientComponentClient"; +import { rejectBusiness, approveBusiness } from "@/lib/data/applicationMutate"; +import toast from "react-hot-toast"; + +interface BusinessActionButtonsProps { + businessApplicationId: number; +} + +export function BusinessActionButtons({ businessApplicationId }: BusinessActionButtonsProps) { + const [isRejectLoading, setIsRejectLoading] = useState(false); + const [isApproveLoading, setIsApproveLoading] = useState(false); + const [isRejectOpen, setIsRejectOpen] = useState(false); + const [isApproveOpen, setIsApproveOpen] = useState(false); + + const onRejectBusiness = async () => { + try { + setIsRejectLoading(true); + const client = createSupabaseClient(); + + const { error } = await rejectBusiness(client, businessApplicationId); + if (error) throw error; + + toast.success("Rejected successfully"); + window.location.reload(); + } catch (error) { + toast.error("Failed to reject business"); + console.error("Failed to reject business:", error); + } finally { + setIsRejectLoading(false); + setIsRejectOpen(false); + } + }; + + const onApproveBusiness = async () => { + try { + setIsApproveLoading(true); + const client = createSupabaseClient(); + + const { error } = await approveBusiness(client, businessApplicationId); + if (error) throw error; + + toast.success("Approved successfully"); + window.location.reload(); + } catch (error) { + toast.error("Failed to approve business"); + console.error("Failed to approve business:", error); + } finally { + setIsApproveLoading(false); + setIsApproveOpen(false); + } + }; + + return ( +
+ + + + + + + Approve this Business + Are you sure that you will approve this business? + + + + + + + + + + + + + + Reject this Business + Are you sure that you will reject this business? + + + + + + +
+ ); +} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx new file mode 100644 index 0000000..0a381fa --- /dev/null +++ b/src/app/admin/page.tsx @@ -0,0 +1,109 @@ +import { getUserRole } from "@/lib/data/userQuery"; +import { createSupabaseClient } from "@/lib/supabase/serverComponentClient"; +import { redirect } from "next/navigation"; +import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { Checkbox } from "@/components/ui/checkbox"; +import Link from "next/link"; +import { FolderOpenDot } from "lucide-react"; +import { getAllBusinessApplicationQuery } from "@/lib/data/applicationQuery"; +import { BusinessActionButtons } from "./BusinessActionButtons"; + +export default async function AdminPage() { + const client = createSupabaseClient(); + const { data: userData, error: userDataError } = await client.auth.getUser(); + + if (userDataError) { + redirect("/"); + } + const uid = userData.user!.id; + const { data: roleData, error: roleDataError } = await getUserRole(client, uid); + + if (roleDataError) { + redirect("/"); + } + + if (roleData!.role != "admin") { + redirect("/"); + } + + const { data: businessApplicationData, error: businessApplicationError } = + await getAllBusinessApplicationQuery(client); + + if (businessApplicationError) { + console.log(businessApplicationError); + } + + return ( +
+
+ + A list of business applications. + + + Business Name + User Account + Pitch Deck URL + Is In US? + Is For Sale? + Generate Revenue + Community Size + Money raised to date + Location + Project + + + + + {businessApplicationData && businessApplicationData.length > 0 ? ( + businessApplicationData.map((application) => ( + + {application.business_name} + + + {application.username} + + + + {application.pitch_deck_url && ( + + {application.pitch_deck_url} + + )} + + + + + + + + + + + {application.community_size} + {application.money_raised_to_date} + {application.location} + + {application.project_application_id && ( + + + + )} + + + + + + )) + ) : ( + + + No business applications found + + + )} + +
+
+
+ ); +} diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..df61a13 --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } diff --git a/src/lib/data/applicationMutate.ts b/src/lib/data/applicationMutate.ts new file mode 100644 index 0000000..fb879fa --- /dev/null +++ b/src/lib/data/applicationMutate.ts @@ -0,0 +1,23 @@ +import { SupabaseClient } from "@supabase/supabase-js"; + +export async function rejectBusiness( + client: SupabaseClient, + businessApplicationId: Number, +) { + return client.from("business_application") + .update({ + status: "reject", + }) + .eq("id", businessApplicationId); +} + +export async function approveBusiness( + client: SupabaseClient, + businessApplicationId: Number, +) { + return client.from("business_application") + .update({ + status: "approve", + }) + .eq("id", businessApplicationId); +} diff --git a/src/lib/data/userQuery.ts b/src/lib/data/userQuery.ts index f693786..a272fda 100644 --- a/src/lib/data/userQuery.ts +++ b/src/lib/data/userQuery.ts @@ -20,4 +20,8 @@ async function getUserProfile(client: SupabaseClient, userId: string) { } } -export { getUserProfile }; +function getUserRole(client: SupabaseClient, userId: string) { + return client.from("user_role").select("role").eq("user_id", userId).single(); +} + +export { getUserProfile, getUserRole };