-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from TEDx-SJEC/admin
Admin
- Loading branch information
Showing
7 changed files
with
332 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { SearchableInfiniteScrollTable } from "@/components/searchable-infinite-scroll-table"; | ||
import React from "react"; | ||
|
||
export default async function Payments() { | ||
return ( | ||
<> | ||
<div className="pt-20 flex min-h-screen w-full flex-col bg-background"> | ||
<SearchableInfiniteScrollTable /> | ||
</div> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,34 @@ | ||
import UsersList from "@/components/Admin/user-list"; | ||
import prisma from "@/server/db"; | ||
import React from "react"; | ||
|
||
export default async function Users() { | ||
let initialUserData = await prisma.user.findMany({ | ||
select: { | ||
id: true, | ||
name: true, | ||
email: true, | ||
role: true, | ||
image: true, | ||
}, | ||
take: 10, | ||
}); | ||
if (initialUserData === null) { | ||
initialUserData = [ | ||
{ | ||
id: "1", | ||
name: "Test name", | ||
email: "[email protected]", | ||
role: "PARTICIPANT", | ||
image: "https://i.pravatar.cc/300?img=1", | ||
}, | ||
]; | ||
} | ||
return ( | ||
<> | ||
<div className="pt-20 flex min-h-screen w-full flex-col bg-background"> | ||
<UsersList initialUsers={initialUserData} initialPage={1} /> | ||
</div> | ||
</> | ||
); | ||
let initialUserData = await prisma.user.findMany({ | ||
select: { | ||
id: true, | ||
name: true, | ||
email: true, | ||
role: true, | ||
image: true, | ||
}, | ||
take: 10, | ||
}); | ||
if (initialUserData === null) { | ||
initialUserData = [ | ||
{ | ||
id: "1", | ||
name: "Test name", | ||
email: "[email protected]", | ||
role: "PARTICIPANT", | ||
image: "https://i.pravatar.cc/300?img=1", | ||
}, | ||
]; | ||
} | ||
return ( | ||
<> | ||
<div className="pt-20 flex min-h-screen w-full flex-col bg-background"> | ||
<UsersList initialUsers={initialUserData} initialPage={1} /> | ||
</div> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,33 @@ | ||
import { getServerSideSession } from "@/lib/get-server-session"; | ||
import { getServerSession } from "next-auth"; | ||
import { createUploadthing, type FileRouter } from "uploadthing/next"; | ||
import { UploadThingError } from "uploadthing/server"; | ||
|
||
const f = createUploadthing(); | ||
|
||
const auth = async (req: Request) => { | ||
const session = await getServerSideSession(); | ||
if (session && session.user) { | ||
return session.user; | ||
} else return null; | ||
const session = await getServerSession(); | ||
|
||
return session?.user; | ||
}; | ||
|
||
export const ourFileRouter = { | ||
imageUploader: f({ image: { maxFileSize: "4MB" } }) | ||
.middleware(async ({ req }) => { | ||
console.log("Middleware for imageUploader", req.url); | ||
const user = await auth(req); | ||
imageUploader: f({ image: { maxFileSize: "4MB" } }) | ||
.middleware(async ({ req }) => { | ||
console.log("Middleware for imageUploader", req.url); | ||
const user = await auth(req); | ||
|
||
if (!user) throw new UploadThingError("Unauthorized"); | ||
if (!user) throw new UploadThingError("Unauthorized"); | ||
|
||
return { userId: user.id }; | ||
}) | ||
.onUploadComplete(async ({ metadata, file }) => { | ||
// console.log("Upload complete for userId:", metadata.userId); | ||
return { userId: user.id }; | ||
}) | ||
.onUploadComplete(async ({ metadata, file }) => { | ||
// console.log("Upload complete for userId:", metadata.userId); | ||
|
||
// console.log("file url", file.url); | ||
// console.log("file url", file.url); | ||
|
||
// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback | ||
return { uploadedBy: metadata.userId }; | ||
}), | ||
// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback | ||
return { uploadedBy: metadata.userId }; | ||
}), | ||
} satisfies FileRouter; | ||
|
||
export type OurFileRouter = typeof ourFileRouter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
"use client"; | ||
|
||
import { useEffect, useRef, useState } from "react"; | ||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Loader2, Search } from "lucide-react"; | ||
|
||
interface TableData { | ||
name: string; | ||
email: string; | ||
usn: string; | ||
paymentId: string; | ||
contactNumber: string; | ||
amount: number; | ||
} | ||
|
||
function generateMockData(count: number): TableData[] { | ||
return Array.from({ length: count }, (_, i) => ({ | ||
name: `User ${i + 1}`, | ||
email: `user${i + 1}@example.com`, | ||
usn: `USN${(1000 + i).toString().padStart(4, "0")}`, | ||
paymentId: `PAY${(10000 + i).toString().padStart(5, "0")}`, | ||
contactNumber: `+1 ${Math.floor(1000000000 + Math.random() * 9000000000)}`, | ||
amount: Math.floor(10 + Math.random() * 990), | ||
})); | ||
} | ||
|
||
export function SearchableInfiniteScrollTable() { | ||
const [data, setData] = useState<TableData[]>([]); | ||
const [filteredData, setFilteredData] = useState<TableData[]>([]); | ||
const [isLoading, setIsLoading] = useState(false); | ||
const [page, setPage] = useState(1); | ||
const [searchTerm, setSearchTerm] = useState(""); | ||
const loaderRef = useRef(null); | ||
|
||
const loadMoreData = () => { | ||
setIsLoading(true); | ||
setTimeout(() => { | ||
const newData = generateMockData(20); | ||
setData((prevData) => [...prevData, ...newData]); | ||
setPage((prevPage) => prevPage + 1); | ||
setIsLoading(false); | ||
}, 1000); // Simulating API delay | ||
}; | ||
|
||
useEffect(() => { | ||
loadMoreData(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
const filtered = data.filter((item) => | ||
Object.values(item).some((value) => | ||
value.toString().toLowerCase().includes(searchTerm.toLowerCase()) | ||
) | ||
); | ||
setFilteredData(filtered); | ||
}, [data, searchTerm]); | ||
|
||
useEffect(() => { | ||
const observer = new IntersectionObserver( | ||
(entries) => { | ||
if (entries[0].isIntersecting && !isLoading && searchTerm === "") { | ||
loadMoreData(); | ||
} | ||
}, | ||
{ threshold: 1.0 } | ||
); | ||
|
||
if (loaderRef.current) { | ||
observer.observe(loaderRef.current); | ||
} | ||
|
||
return () => { | ||
if (loaderRef.current) { | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
observer.unobserve(loaderRef.current); | ||
} | ||
observer.disconnect(); | ||
}; | ||
}, [isLoading, searchTerm]); | ||
|
||
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
setSearchTerm(event.target.value); | ||
}; | ||
|
||
return ( | ||
<div className="container mx-auto py-10"> | ||
<div className="mb-4 relative"> | ||
<Input | ||
type="text" | ||
placeholder="Search..." | ||
value={searchTerm} | ||
onChange={handleSearch} | ||
className="pl-10" | ||
/> | ||
<Search | ||
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" | ||
size={20} | ||
/> | ||
</div> | ||
<Table> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead>Name</TableHead> | ||
<TableHead>Email</TableHead> | ||
<TableHead>USN</TableHead> | ||
<TableHead>Payment ID</TableHead> | ||
<TableHead>Contact Number</TableHead> | ||
<TableHead>Amount</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{filteredData.map((item, index) => ( | ||
<TableRow key={index}> | ||
<TableCell>{item.name}</TableCell> | ||
<TableCell>{item.email}</TableCell> | ||
<TableCell>{item.usn}</TableCell> | ||
<TableCell>{item.paymentId}</TableCell> | ||
<TableCell>{item.contactNumber}</TableCell> | ||
<TableCell>${item.amount.toFixed(2)}</TableCell> | ||
</TableRow> | ||
))} | ||
</TableBody> | ||
</Table> | ||
{searchTerm === "" && ( | ||
<div ref={loaderRef} className="flex justify-center py-4"> | ||
{isLoading && <Loader2 className="h-6 w-6 animate-spin" />} | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} |
Oops, something went wrong.