Compare commits

...

3 Commits

Author SHA1 Message Date
Rama Priyanto b7d0f00def feat:spesific target mapping
continuous-integration/drone/push Build is passing Details
2026-04-17 18:18:39 +07:00
Rama Priyanto 1a87749950 feat:spesific target 2026-04-17 16:53:01 +07:00
Rama Priyanto d2484e8a49 fix new ui 2026-04-17 16:26:44 +07:00
13 changed files with 806 additions and 130 deletions

View File

@ -8,8 +8,8 @@ const Navbar = dynamic(() => import("@/components/ui/navbar"), {
export default function AccountManagement() {
return (
<div className="flex w-full flex-col gap-4 p-4">
<Navbar title="Multipool Account Management" />
<div className="flex w-full flex-col gap-4">
<Navbar title="Account Management" />
<AccountManagementTable />
</div>
)

View File

@ -1,5 +1,5 @@
import { AppSidebar } from "@/components/ui/app-sidebar"
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { SidebarProvider } from "@/components/ui/sidebar"
export default function DashboardLayout({
children,
@ -8,12 +8,10 @@ export default function DashboardLayout({
}>) {
return (
<SidebarProvider>
<div className="flex min-h-screen w-full gap-1 bg-black">
<div className="flex min-h-screen w-full bg-gray-50">
<AppSidebar />
<main className="flex w-full grow rounded-xl border border-white bg-black">
{children}
</main>
<main className="w-full border-2 bg-gray-50">{children}</main>
</div>
</SidebarProvider>
)

View File

@ -0,0 +1,16 @@
"use client"
import SpesificTargetMappingTable from "@/components/table/spesific-target-mapping/spesific-target-mapping-table"
import dynamic from "next/dynamic"
const Navbar = dynamic(() => import("@/components/ui/navbar"), {
ssr: false,
})
export default function SpesificTargetMapping() {
return (
<div className="flex w-full flex-col gap-4">
<Navbar title="Spesific Target Mapping" />
<SpesificTargetMappingTable />
</div>
)
}

View File

@ -0,0 +1,16 @@
"use client"
import SpesificTargetTable from "@/components/table/spesific-target/spesific-target-table"
import dynamic from "next/dynamic"
const Navbar = dynamic(() => import("@/components/ui/navbar"), {
ssr: false,
})
export default function SpesificTarget() {
return (
<div className="flex w-full flex-col gap-4">
<Navbar title="Spesific Target" />
<SpesificTargetTable />
</div>
)
}

View File

@ -8,7 +8,7 @@ const Navbar = dynamic(() => import("@/components/ui/navbar"), {
export default function UploadAccountData() {
return (
<div className="flex w-full flex-col gap-4 p-4">
<div className="flex w-full flex-col gap-4">
<Navbar title="Upload Account Data" />
<UploadCsvCard />
</div>

View File

@ -28,7 +28,7 @@ export default function RootLayout({
)}
>
<body className="bg-black">
<ThemeProvider defaultTheme="dark">{children}</ThemeProvider>
<ThemeProvider defaultTheme="light">{children}</ThemeProvider>
</body>
</html>
)

View File

@ -44,28 +44,17 @@ export default function Page() {
}
return (
<div className="flex min-h-svh flex-row bg-gray-50 text-black">
<div className="hidden w-1/3 lg:block"></div>
<div className="flex w-full flex-col items-center justify-center lg:w-1/3">
<Image
width={240}
height={240}
src="/multipool-logo.png"
alt="logo-multipool"
className="mt-5"
/>
<h1 className="bg-linear-to-t from-[#8e5c18] to-[#dbc17b] bg-clip-text text-5xl font-bold text-transparent">
AI Platform Kolaboratif
<div className="flex min-h-svh flex-row items-center justify-center bg-linear-to-r from-violet-50 to-violet-400 text-black">
<div className="flex h-fit w-[30vw] flex-col items-center justify-center rounded-2xl border border-white/30 bg-white/30 p-16 shadow-[0_20px_60px_rgba(0,0,0,0.25)] backdrop-blur-xl">
<h1 className="mb-12 self-start text-left text-4xl font-bold text-black">
Login
</h1>
<p className="text-md font-bold text-[#8e5c18]">
Produksi Narasi Media
</p>
<form
onSubmit={handleSubmit(onSubmit)}
className="mx-auto w-full max-w-md text-white"
>
<form onSubmit={handleSubmit(onSubmit)} className="w-full text-black">
<div className="space-y-8">
<div>
<p className="text-xs tracking-widest text-gray-800 uppercase">
Username
</p>{" "}
<Controller
name="username"
control={control}
@ -73,12 +62,11 @@ export default function Page() {
render={({ field }) => (
<Input
{...field}
placeholder="Username"
className="mt-2 h-10 w-full rounded-none border-2 border-gray-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none"
placeholder="Enter your username"
className="mt-2 h-12 w-full rounded-xl border-none bg-white/80! px-4 text-black placeholder:text-gray-500"
/>
)}
/>
{errors.username && (
<p className="mt-1 text-xs text-red-400">
{errors.username.message}
@ -94,12 +82,13 @@ export default function Page() {
required: "Required",
}}
render={({ field }) => (
<InputGroup className="mt-2 h-10 w-full rounded-none border-2 border-gray-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<InputGroup className="mt-2 h-12 w-full rounded-xl border-none bg-white/80! px-2 backdrop-blur">
<InputGroupInput
{...field}
id="inline-end-input"
type={viewPassword ? "text" : "password"}
placeholder="Enter password"
placeholder="Enter password here"
className="bg-white"
/>
<InputGroupAddon align="inline-end">
<a
@ -119,18 +108,20 @@ export default function Page() {
</p>
)}
</div>
<p className="cursor-pointer text-sm text-blue-500 hover:underline">
Forgot password?
</p>
<Button
type="submit"
disabled={isSubmitting}
className="h-12 w-full cursor-pointer bg-[#8e5c18] text-white hover:bg-[#b19c62]"
className="h-12 w-full rounded-xl bg-linear-to-r from-[#4c1d95] to-[#5b21b6] font-semibold tracking-wide text-white"
>
{isSubmitting ? "Loading..." : "Masuk"}
</Button>
</div>
</form>
</div>
<div className="w-1/3 lg:block"></div>
</div>
)
}

View File

@ -46,64 +46,67 @@ export default function UploadCsvCard() {
return (
<div className="w-full rounded-2xl bg-white p-6 shadow-md">
<h2 className="mb-4 text-lg font-semibold">Upload a CSV file</h2>
<p className="text-3xl font-semibold">Account Management</p>
<p className="text-sm text-gray-700">Manage your Account</p>
{/* DROP AREA */}
<div
onDragOver={(e) => {
e.preventDefault()
setDragActive(true)
}}
onDragLeave={() => setDragActive(false)}
onDrop={handleDrop}
className={`flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-10 text-center transition ${dragActive ? "border-blue-500 bg-blue-50" : "border-blue-400"} `}
>
<div className="text-4xl">📁</div>
<div className="mt-6 flex flex-col gap-2 rounded-xl border border-violet-300 p-4">
<h2 className="mb-4 text-sm font-light uppercase">Upload a CSV file</h2>
<p className="text-gray-700">Drag your file(s) to start uploading</p>
<div
onDragOver={(e) => {
e.preventDefault()
setDragActive(true)
}}
onDragLeave={() => setDragActive(false)}
onDrop={handleDrop}
className={`flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-10 text-center transition ${dragActive ? "border-blue-500 bg-blue-50" : "border-blue-400"} `}
>
<div className="text-4xl">📁</div>
<div className="flex items-center gap-2 text-sm text-gray-400">
<div className="h-px w-10 bg-gray-300" />
OR
<div className="h-px w-10 bg-gray-300" />
<p className="text-gray-700">Drag your file(s) to start uploading</p>
<div className="flex items-center gap-2 text-sm text-gray-400">
<div className="h-px w-10 bg-gray-300" />
OR
<div className="h-px w-10 bg-gray-300" />
</div>
<button
onClick={() => inputRef.current?.click()}
className="rounded-md border border-blue-500 px-4 py-2 text-sm font-medium text-blue-600 hover:bg-blue-50"
>
Browse files
</button>
<input
ref={inputRef}
type="file"
accept=".csv"
className="hidden"
onChange={(e) => handleFile(e.target.files?.[0] ?? null)}
/>
{file && (
<p className="mt-2 text-sm text-green-600">Selected: {file.name}</p>
)}
</div>
<button
onClick={() => inputRef.current?.click()}
className="rounded-md border border-blue-500 px-4 py-2 text-sm font-medium text-blue-600 hover:bg-blue-50"
>
Browse files
</button>
<input
ref={inputRef}
type="file"
accept=".csv"
className="hidden"
onChange={(e) => handleFile(e.target.files?.[0] ?? null)}
/>
{file && (
<p className="mt-2 text-sm text-green-600">Selected: {file.name}</p>
)}
</div>
{/* ACTION BUTTONS */}
<div className="mt-6 flex justify-end gap-3">
{/* <button
<div className="mt-6 flex justify-end gap-3">
{/* <button
onClick={() => setFile(null)}
className="rounded-lg border px-5 py-2 text-sm font-semibold text-gray-600 hover:bg-gray-100"
>
CANCEL
</button> */}
<button
disabled={!file}
onClick={onSave}
className={`rounded-lg px-5 py-2 text-sm font-semibold text-white ${file ? "bg-blue-600 hover:bg-blue-700" : "cursor-not-allowed bg-blue-300"} `}
>
SAVE
</button>
<button
disabled={!file}
onClick={onSave}
className={`rounded-lg px-5 py-2 text-sm font-semibold text-white ${file ? "bg-blue-600 hover:bg-blue-700" : "cursor-not-allowed bg-blue-300"} `}
>
SAVE
</button>
</div>
</div>
<FeedbackDialog
open={open}

View File

@ -82,7 +82,7 @@ export default function AccountManagementTable() {
const [search, setSearch] = useState("")
const [limit, setLimit] = useState("5")
const [page, setPage] = useState(1)
const [totalPage, setTotalPage] = useState(1)
// const [totalPage, setTotalPage] = useState(1)
const [selectedDataTable, setSelectedDataTable] = useState<string[]>([])
const getData = async () => {
@ -109,16 +109,20 @@ export default function AccountManagementTable() {
const getStatus = (id: number) => {
const color = [
"text-green-600 border-2 border-green-600 bg-green-300",
"text-violet-600 border-2 border-violet-600 bg-violet-300",
"text-yellow-600 border-2 border-yellow-600 bg-yellow-300",
"text-red-600 border-2 border-red-600 bg-red-300",
"text-green-600 border-2 border-green-600 bg-green-100",
"text-blue-600 border-2 border-blue-600 bg-blue-100",
"text-yellow-600 border-2 border-yellow-600 bg-yellow-100",
"text-red-600 border-2 border-red-600 bg-red-100",
]
const findStatusName = dummyStatus.find((a) => a.id == id)
return (
<p className={color[id - 1] + " w-48 rounded-lg p-1 text-center"}>
<p
className={
color[id - 1] + " w-60 rounded-lg p-1 text-center text-sm uppercase"
}
>
{findStatusName?.value}
</p>
)
@ -126,12 +130,14 @@ export default function AccountManagementTable() {
return (
<div className="flex flex-col gap-4 text-black">
<div className="flex flex-col gap-2 rounded-xl bg-white p-4">
<p className="text-lg font-bold">Search and Filter</p>
<div className="flex flex-row items-end gap-4">
<div className="flex flex-col gap-2 rounded-xl bg-white">
<p className="mx-4 text-3xl font-semibold">Account Management</p>
<p className="mx-4 text-sm text-gray-700">Manage your Account</p>
<div className="m-4 flex flex-row items-end gap-4 rounded-xl border border-violet-200 p-4">
<div className="flex flex-col gap-1">
<p className="text-sm">Identifier</p>
<InputGroup className="h-10 w-100 rounded-lg border-2 border-gray-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<p className="text-xs font-light uppercase">Search</p>
<InputGroup className="h-10 w-100 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<InputGroupInput
value={search}
onChange={(e) => setSearch(e.target.value)}
@ -150,12 +156,12 @@ export default function AccountManagementTable() {
</InputGroup>
</div>
<div className="flex flex-col gap-1">
<p className="text-sm">Proxy Location</p>
<p className="text-xs font-light uppercase">Proxy Location</p>
<Select
value={selectedLocation}
onValueChange={setSelectedLocation}
>
<SelectTrigger className="h-10! w-70 rounded-lg border-2 border-gray-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<SelectTrigger className="h-10! w-70 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<SelectValue placeholder="Select Location" />
</SelectTrigger>
<SelectContent>
@ -172,9 +178,9 @@ export default function AccountManagementTable() {
</Select>
</div>
<div className="flex flex-col gap-1">
<p className="text-sm">Status</p>
<p className="text-xs font-light uppercase">Status</p>
<Select value={selectedStatus} onValueChange={setSelectedStatus}>
<SelectTrigger className="h-10! w-70 rounded-lg border-2 border-gray-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<SelectTrigger className="h-10! w-70 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<SelectValue placeholder="Select Status" />
</SelectTrigger>
<SelectContent>
@ -192,19 +198,19 @@ export default function AccountManagementTable() {
</div>
<Button
onClick={getData}
className="h-10 w-24 bg-[#8e5c18] text-white"
className="h-10 w-24 bg-blue-600 text-white"
>
FILTER
Filter
</Button>
<Button
onClick={resetFilter}
className="bg-grey-50 h-10 w-24 border-2 border-gray-300 text-black"
>
RESET
Reset
</Button>
</div>
</div>
<div className="flex-roew flex items-center gap-2 rounded-xl bg-white p-4">
<div className="flex-roew mx-4 flex items-center gap-2 rounded-xl border border-violet-200 bg-white p-4">
<p className="text-lg font-bold">X Server</p>
<Input
defaultValue="1111.1111.1111"
@ -212,9 +218,9 @@ export default function AccountManagementTable() {
className="h-10 w-17/18 rounded-lg border-2 border-gray-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none"
/>
</div>
<div className="flex-roew flex items-center gap-2 rounded-xl bg-white p-4">
<div className="flex-roew m-4 flex items-center gap-2 rounded-xl border border-violet-200 bg-white p-4">
<Table>
<TableHeader className="bg-gray-100">
<TableHeader className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableHead className="w-25 text-black"></TableHead>
<TableHead className="text-black">Identifier</TableHead>
@ -254,7 +260,7 @@ export default function AccountManagementTable() {
onClick={() => {
console.log("selectData", invoice.id)
}}
className="cursor-pointer bg-blue-500 text-white"
className="cursor-pointer bg-blue-900 text-white"
>
<ReloadIcon /> Process
</Button>
@ -262,7 +268,7 @@ export default function AccountManagementTable() {
</TableRow>
))}
</TableBody>
<TableFooter className="bg-gray-100">
<TableFooter className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableCell colSpan={3}>
<Button
@ -270,7 +276,7 @@ export default function AccountManagementTable() {
onClick={() => {
console.log("select", selectedDataTable)
}}
className="cursor-pointer bg-blue-500 text-white"
className="cursor-pointer bg-blue-900 text-white"
>
<ReloadIcon /> Process All
</Button>

View File

@ -0,0 +1,370 @@
"use client"
import { ReloadIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { Input } from "@/components/ui/input"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group"
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { ChevronLeft, ChevronRight, SearchIcon } from "lucide-react"
import { useEffect, useState } from "react"
const dummyProxy = [
{ id: 1, value: "Jakarta" },
{ id: 2, value: "Bandung" },
{ id: 3, value: "Surabaya" },
{ id: 4, value: "Medan" },
]
const dummyStatus = [
{ id: 1, value: "Negative" },
{ id: 2, value: "Positive" },
{ id: 3, value: "Neutral" },
]
const dummyData = [
{
id: "1",
identifier: "MyUsername123",
proxy: "Jakarta",
sentiment: 1,
},
{
id: "2",
identifier: "MyUsername12223",
proxy: "Bandung",
sentiment: 2,
},
{
id: "3",
identifier: "MyUsername1132123",
proxy: "Jakarta",
sentiment: 3,
},
{
id: "4",
identifier: "MyUsername422123",
proxy: "Medan",
sentiment: 4,
},
{
id: "5",
identifier: "12MyUsername422123",
proxy: "Surabaya",
sentiment: 1,
},
]
const dummyDataTarget = [
{
id: "1",
name: "POLRI - DIV Humas",
status: 1,
},
{
id: "2",
name: "POLRI - Korlantas",
status: 2,
},
{
id: "3",
name: "TNI",
status: 3,
},
{
id: "4",
name: "DPR",
status: 3,
},
]
export default function SpesificTargetMappingTable() {
const [selectedStatus, setSelectedStatus] = useState("")
const [selectedLocation, setSelectedLocation] = useState("")
const [search, setSearch] = useState("")
const [limit, setLimit] = useState("5")
const [page, setPage] = useState(1)
// const [totalPage, setTotalPage] = useState(1)
const [selectedDataTable, setSelectedDataTable] = useState<string[]>([])
const getData = async () => {
const req = {
search: search,
sentiment: selectedStatus,
proxy: selectedLocation,
page: page,
limit: limit,
}
console.log("request", req)
}
useEffect(() => {
getData()
}, [page, limit])
const resetFilter = () => {
setSelectedLocation("")
setSelectedStatus("")
setSearch("")
setPage(1)
getData()
}
return (
<div className="flex flex-col gap-4 text-black">
<div className="flex flex-col gap-2 rounded-xl bg-white">
<p className="mx-4 text-3xl font-semibold">Spesific Target Mapping</p>
<p className="mx-4 text-sm text-gray-700">
Manage your Spesific Target Mapping
</p>
<div className="m-4 flex flex-row items-end gap-4 rounded-xl border border-violet-200 p-4">
<div className="flex flex-col gap-1">
<p className="text-xs font-light uppercase">Search</p>
<InputGroup className="h-10 w-100 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<InputGroupInput
value={search}
onChange={(e) => setSearch(e.target.value)}
id="inline-end-input"
type="text"
placeholder="Search e.g username, email or ID"
/>
<InputGroupAddon align="inline-start">
<a
className="cursor-pointer"
// onClick={() => setViewPassword(!viewPassword)}
>
<SearchIcon />
</a>
</InputGroupAddon>
</InputGroup>
</div>
<div className="flex flex-col gap-1">
<p className="text-xs font-light uppercase">Proxy Location</p>
<Select
value={selectedLocation}
onValueChange={setSelectedLocation}
>
<SelectTrigger className="h-10! w-70 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<SelectValue placeholder="Select Location" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{dummyProxy.map((proxy) => (
<div key={proxy.id}>
<SelectItem value={String(proxy.id)}>
{proxy.value}
</SelectItem>
</div>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
<Button
onClick={getData}
className="h-10 w-24 bg-blue-600 text-white"
>
Filter
</Button>
<Button
onClick={resetFilter}
className="bg-grey-50 h-10 w-24 border-2 border-gray-300 text-black"
>
Reset
</Button>
</div>
</div>
<div className="m-4 flex flex-col gap-2 rounded-xl border border-violet-200 bg-white p-4">
<p className="text-lg font-bold text-black">Accounts</p>
<Table className="border border-violet-300">
<TableHeader className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableHead className="w-25 text-black"></TableHead>
<TableHead className="text-black">Identifier</TableHead>
<TableHead className="text-black">Proxy Location</TableHead>
<TableHead className="text-black">Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{dummyData.map((invoice, index) => (
<TableRow className="hover:bg-gray-200" key={invoice.id}>
<TableCell className="font-medium">{index + 1}</TableCell>
<TableCell>{invoice.identifier}</TableCell>
<TableCell>{invoice.proxy}</TableCell>
<TableCell className="">
<Button
disabled={invoice.sentiment == 2 || invoice.sentiment == 4}
onClick={() => {
console.log("selectData", invoice.id)
}}
className="cursor-pointer bg-blue-900 text-white"
>
<ReloadIcon /> Mapping
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableCell colSpan={4} className="">
<div className="flex flex-row items-center justify-end gap-2">
<p className="text-blue-400">ROWS PER PAGE: </p>
<Select value={limit} onValueChange={setLimit}>
<SelectTrigger className="w-20">
<SelectValue placeholder="value" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="25">25</SelectItem>
<SelectItem value="50">50</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<a className="cursor-pointer">
<ChevronLeft />
</a>
<a className="cursor-pointer">
<ChevronRight />
</a>
</div>
</TableCell>
</TableRow>
</TableFooter>
</Table>
<div className="my-4 flex justify-between">
<p className="text-lg font-bold text-black">
Spesific Targets Mapping
</p>
<InputGroup className="h-10 w-80 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<InputGroupInput
value={search}
onChange={(e) => setSearch(e.target.value)}
id="inline-end-input"
type="text"
placeholder="Search"
/>
<InputGroupAddon align="inline-start">
<a
className="cursor-pointer"
// onClick={() => setViewPassword(!viewPassword)}
>
<SearchIcon />
</a>
</InputGroupAddon>
</InputGroup>
</div>
<Table className="border border-violet-300">
<TableHeader className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableHead className="text-black">NO</TableHead>
<TableHead className="text-black">Name</TableHead>
<TableHead className="text-black">Sentiment</TableHead>
<TableHead className="text-black">Selected</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{dummyDataTarget.map((target, index) => (
<TableRow className="hover:bg-gray-200" key={target.id}>
<TableCell className="font-medium">{index + 1}</TableCell>
<TableCell>{target.name}</TableCell>
<TableCell>
<Select
value={String(target.status)}
onValueChange={setSelectedLocation}
>
<SelectTrigger className="h-10! w-70 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<SelectValue placeholder="Select Location" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{dummyStatus.map((proxy) => (
<div key={proxy.id}>
<SelectItem value={String(proxy.id)}>
{proxy.value}
</SelectItem>
</div>
))}
</SelectGroup>
</SelectContent>
</Select>
</TableCell>
<TableCell className="space-x-2">
<Checkbox
checked={selectedDataTable.includes(target.id)}
onCheckedChange={(e) => {
if (e) {
setSelectedDataTable([...selectedDataTable, target.id])
} else {
const temp = []
for (const element of selectedDataTable) {
if (element !== target.id) {
temp.push(element)
}
}
setSelectedDataTable(temp)
}
}}
className="border-gray-300"
/>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableCell colSpan={4} className="">
<div className="flex flex-row items-center justify-end gap-2">
<p className="text-blue-400">ROWS PER PAGE: </p>
<Select value={limit} onValueChange={setLimit}>
<SelectTrigger className="w-20">
<SelectValue placeholder="value" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="25">25</SelectItem>
<SelectItem value="50">50</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<a className="cursor-pointer">
<ChevronLeft />
</a>
<a className="cursor-pointer">
<ChevronRight />
</a>
</div>
</TableCell>
</TableRow>
</TableFooter>
</Table>
</div>
</div>
)
}

View File

@ -0,0 +1,252 @@
"use client"
import { ReloadIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { Input } from "@/components/ui/input"
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group"
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { ChevronLeft, ChevronRight, PlusIcon, SearchIcon } from "lucide-react"
import { useEffect, useState } from "react"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
const dummyData = [
{
id: "1",
name: "POLRI - DIV Humas",
status: 1,
},
{
id: "2",
name: "POLRI - Korlantas",
status: 2,
},
{
id: "3",
name: "TNI",
status: 3,
},
{
id: "4",
name: "DPR",
status: 4,
},
]
export default function SpesificTargetTable() {
const [selectedTarget, setSelectedTarget] = useState("")
const [search, setSearch] = useState("")
const [limit, setLimit] = useState("5")
const [page, setPage] = useState(1)
// const [totalPage, setTotalPage] = useState(1)
const [selectedDataTable, setSelectedDataTable] = useState<string[]>([])
const [isOpen, setIsOpen] = useState(false)
const [formStatus, setFormStatus] = useState<"create" | "edit">("create")
const getData = async () => {
const req = {
search: search,
page: page,
limit: limit,
}
console.log("request", req)
}
useEffect(() => {
getData()
}, [page, limit])
const resetFilter = () => {
setSearch("")
setPage(1)
getData()
}
return (
<div className="flex flex-col gap-4 text-black">
<div className="flex flex-col gap-2 rounded-xl bg-white">
<p className="mx-4 text-3xl font-semibold">Spesific Target</p>
<p className="mx-4 text-sm text-gray-700">
Manage your Spesific Target
</p>
<div className="m-4 flex flex-row items-end gap-4 rounded-xl border border-violet-200 p-4">
<div className="flex flex-col gap-1">
<p className="text-xs font-light uppercase">Search</p>
<InputGroup className="h-10 w-100 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none">
<InputGroupInput
value={search}
onChange={(e) => setSearch(e.target.value)}
id="inline-end-input"
type="text"
placeholder="Search e.g name"
/>
<InputGroupAddon align="inline-start">
<a
className="cursor-pointer"
// onClick={() => setViewPassword(!viewPassword)}
>
<SearchIcon />
</a>
</InputGroupAddon>
</InputGroup>
</div>
<Button
onClick={getData}
className="h-10 w-24 bg-blue-600 text-white"
>
Filter
</Button>
<Button
onClick={resetFilter}
className="bg-grey-50 h-10 w-24 border-2 border-gray-300 text-black"
>
Reset
</Button>
</div>
</div>
<Button
onClick={() => {
if (!isOpen) {
setSelectedTarget("")
setFormStatus("create")
setIsOpen(true)
}
}}
className="mx-4 h-10 w-50 bg-blue-600 text-white"
>
<PlusIcon /> Add Spesific Target
</Button>
<div className="flex-roew m-4 flex items-center gap-2 rounded-xl border border-violet-200 bg-white p-4">
<Table>
<TableHeader className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableHead className="text-black">NO</TableHead>
<TableHead className="text-black">Name</TableHead>
<TableHead className="text-black">Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{dummyData.map((target, index) => (
<TableRow className="hover:bg-gray-200" key={target.id}>
<TableCell className="font-medium">{index + 1}</TableCell>
<TableCell>{target.name}</TableCell>
<TableCell className="space-x-2">
<Button
onClick={() => {
setFormStatus("edit")
setSelectedTarget(target.name)
setIsOpen(true)
console.log("selectData", target.id)
}}
className="cursor-pointer border border-gray-300 bg-white text-black"
>
<ReloadIcon /> Edit
</Button>
<Button
onClick={() => {
console.log("selectData", target.id)
}}
className="cursor-pointer bg-red-700 text-white"
>
<ReloadIcon /> Delete
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter className="bg-violet-50">
<TableRow className="hover:bg-transparent">
<TableCell colSpan={3} className="">
<div className="flex flex-row items-center justify-end gap-2">
<p className="text-blue-400">ROWS PER PAGE: </p>
<Select value={limit} onValueChange={setLimit}>
<SelectTrigger className="w-20">
<SelectValue placeholder="value" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="25">25</SelectItem>
<SelectItem value="50">50</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<a className="cursor-pointer">
<ChevronLeft />
</a>
<a className="cursor-pointer">
<ChevronRight />
</a>
</div>
</TableCell>
</TableRow>
</TableFooter>
</Table>
</div>
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="max-w-sm lg:max-w-lg">
<DialogHeader>
<DialogTitle>
{formStatus == "create"
? "Add New Spesific Target"
: "Edit Spesific Target"}
</DialogTitle>
</DialogHeader>
<div className="my-5 flex flex-col gap-2">
<p className="text-xs font-light uppercase">name</p>
<Input
value={selectedTarget}
onChange={(e) => setSelectedTarget(e.target.value)}
className="h-10 rounded-lg border border-violet-300 bg-transparent text-black outline-none focus:border-transparent focus:ring-0 focus:outline-none"
/>
</div>
<DialogFooter>
<DialogClose asChild>
<Button className="h-10 w-24" variant="outline">
Cancel
</Button>
</DialogClose>
<Button className="h-10 w-24 bg-blue-600">
{formStatus == "create" ? "Submit" : "Save"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}

View File

@ -1,3 +1,4 @@
"use client"
import {
Sidebar,
SidebarContent,
@ -9,14 +10,32 @@ import {
import Link from "next/link"
import { ManagementIcon, UploadAccount } from "../icons"
import Image from "next/image"
import { usePathname } from "next/navigation"
const menu = [
{
title: "account-management",
icon: <ManagementIcon className="h-5 w-5 shrink-0" />,
},
{
title: "upload-account-data",
icon: <UploadAccount className="h-5 w-5 shrink-0" />,
},
{
title: "spesific-target",
icon: <UploadAccount className="h-5 w-5 shrink-0" />,
},
{
title: "spesific-target-mapping",
icon: <UploadAccount className="h-5 w-5 shrink-0" />,
},
]
export function AppSidebar() {
const pathname = usePathname()
return (
<Sidebar
collapsible="icon"
className="group w-65 data-[state=collapsed]:w-24"
>
<div className="relative h-full w-full rounded-xl border border-white bg-black text-white">
<Sidebar collapsible="icon">
<div className="bg-gray-60 h-full border-2 border-violet-300 text-black">
{/* TRIGGER */}
<div className="absolute top-4 right-4 z-10 transition-all duration-300 group-data-[state=collapsed]:right-auto group-data-[state=collapsed]:left-1/2 group-data-[state=collapsed]:-translate-x-1/2">
<SidebarTrigger className="rounded-md p-2 transition hover:bg-white/10" />
@ -24,14 +43,15 @@ export function AppSidebar() {
{/* LOGO */}
<div className="flex justify-center pt-16 transition-all duration-300 group-data-[state=collapsed]:pt-14">
<Image
{/* <Image
src="/multipool-logo.png"
loading="eager"
alt="logo"
width={240}
height={240}
className="transition-all duration-300 group-data-[state=collapsed]:h-6 group-data-[state=collapsed]:w-6"
/>
/> */}
<div className="h-24 w-24 bg-gray-500"></div>
</div>
<SidebarHeader />
@ -39,17 +59,20 @@ export function AppSidebar() {
{/* MENU */}
<SidebarContent className="mt-10">
<SidebarGroup className="flex flex-col items-stretch space-y-2 px-2">
<Link
href="/dashboard/management-account"
className="flex w-full items-center justify-start gap-3 rounded-lg p-3 transition group-data-[state=collapsed]:justify-center hover:bg-white/10"
>
<ManagementIcon className="h-5 w-5 shrink-0" />
<span className="group-data-[state=collapsed]:hidden">
Management Account
</span>
</Link>
{menu.map((item) => (
<Link
key={item.title}
href={"/dashboard/" + item.title}
className={`flex w-full items-center justify-start gap-3 rounded-lg p-3 text-sm ${pathname.split("/")[2] == item.title ? "border-2 border-violet-600 bg-violet-100 font-bold text-violet-600" : "text-gray-600"} transition group-data-[state=collapsed]:justify-center hover:bg-white/10`}
>
{item.icon}
<span className="capitalize group-data-[state=collapsed]:hidden">
{item.title.split("-").join(" ")}
</span>
</Link>
))}
<Link
{/* <Link
href="/dashboard/upload-account-data"
className="flex w-full items-center justify-start gap-3 rounded-lg p-3 transition group-data-[state=collapsed]:justify-center hover:bg-white/10"
>
@ -57,7 +80,7 @@ export function AppSidebar() {
<span className="group-data-[state=collapsed]:hidden">
Upload Account Data
</span>
</Link>
</Link> */}
</SidebarGroup>
</SidebarContent>

View File

@ -11,15 +11,16 @@ export default function Navbar(props: { title: string }) {
}, [])
return (
<div className="flex flex-col gap-1">
<div className="flex flex-row items-center justify-end gap-2">
<div className="flex flex-col gap-1 border border-violet-300 p-4">
<div className="flex flex-row items-center justify-between gap-2">
<p className="text-sm text-gray-500">{title}</p>
<a>
<NotificationIcon />
<NotificationIcon size={16} />
</a>
<UserFillIcon size={36} />
<p>{username}</p>
{/* <UserFillIcon size={36} />
<p>{username}</p> */}
</div>
<p className="text-2xl font-bold">{title}</p>
</div>
)
}