diff --git a/apps/backoffice/public/images/asd687hwq6nds4dfjj2983.webp b/apps/backoffice/public/images/asd687hwq6nds4dfjj2983.webp
new file mode 100644
index 0000000..b4555c9
Binary files /dev/null and b/apps/backoffice/public/images/asd687hwq6nds4dfjj2983.webp differ
diff --git a/apps/backoffice/src/app/(protected)/dashboard-dimentorin/_components/chart/session-status.tsx b/apps/backoffice/src/app/(protected)/dashboard-dimentorin/_components/chart/session-status.tsx
new file mode 100644
index 0000000..e0bfcd0
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/dashboard-dimentorin/_components/chart/session-status.tsx
@@ -0,0 +1,52 @@
+import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts"
+import { PieLabelProps } from "recharts/types/polar/Pie"
+
+type ChartProps = {
+ name: string
+ value: number
+ color: string
+}
+
+const chartData: ChartProps[] = [
+ { name: "Active", value: 49, color: "#23A1EB" },
+ { name: "Done", value: 24, color: "#81CBF8" },
+ { name: "Canceled", value: 27, color: "#BCE1FB" },
+]
+
+const RADIAN = Math.PI / 180;
+const renderCustomizedLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, percent }: PieLabelProps) => {
+ const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
+ const x = cx + radius * Math.cos(-(midAngle ?? 0) * RADIAN);
+ const y = cy + radius * Math.sin(-(midAngle ?? 0) * RADIAN);
+
+ return (
+ cx ? 'start' : 'end'} dominantBaseline="central">
+ {`${((percent ?? 1) * 100).toFixed(0)}%`}
+
+ );
+};
+
+export const SessionStatusChart = () => {
+ return (
+
+
+
+ {chartData.map((entry, index) => (
+ |
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/dashboard-dimentorin/_components/chart/user-growth.tsx b/apps/backoffice/src/app/(protected)/dashboard-dimentorin/_components/chart/user-growth.tsx
new file mode 100644
index 0000000..2f6dd71
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/dashboard-dimentorin/_components/chart/user-growth.tsx
@@ -0,0 +1,36 @@
+import { CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"
+
+type ChartProps = {
+ name: string
+ activeUser: number
+ activeSession: number
+}
+
+const chartData: ChartProps[] = [
+ { name: "2014", activeUser: 0, activeSession: 0 },
+ { name: "2015", activeUser: 15, activeSession: 25 },
+ { name: "2016", activeUser: 30, activeSession: 40 },
+ { name: "2017", activeUser: 45, activeSession: 55 },
+ { name: "2018", activeUser: 60, activeSession: 70 },
+ { name: "2019", activeUser: 75, activeSession: 85 },
+ { name: "2020", activeUser: 90, activeSession: 95 },
+ { name: "2021", activeUser: 85, activeSession: 80 },
+ { name: "2022", activeUser: 95, activeSession: 90 },
+ { name: "2023", activeUser: 100, activeSession: 100 },
+]
+
+export const UserGrowthChart = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/apps/backoffice/src/app/(protected)/dashboard-dimentorin/page.tsx b/apps/backoffice/src/app/(protected)/dashboard-dimentorin/page.tsx
new file mode 100644
index 0000000..e22b1d4
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/dashboard-dimentorin/page.tsx
@@ -0,0 +1,115 @@
+import { Button } from "@imphnen-frontend-service/ui/atoms";
+import { BackofficeWrapper } from "@imphnen-frontend-service/ui/organisms";
+import { For } from "@imphnen-frontend-service/utils";
+import { ReactElement } from "react";
+import { UserGrowthChart } from "./_components/chart/user-growth";
+import { SessionStatusChart } from "./_components/chart/session-status";
+
+const Overview = () => {
+ return (
+
+ )
+}
+
+export default function Components(): ReactElement {
+ return (
+
+ Overview
+
+
+
+
+
+
+
+ {(_, index) => }
+
+
+
+
+
+
+
+
+
+
+
Session Status
+
+
+
+
+
+
+
+
Top 5 Mentors
+
+
+
+
+
+ | No. |
+ Nama Lengkap |
+ Avg Rating |
+
+
+
+
+
+ {(_, index) => (
+
+ | {index + 1} |
+ Mursid Al-Catraz |
+ 4.9 |
+
+ )}
+
+
+
+
+
+
+
+
Top Booked Mentoring Topics
+
+
+
+
+
+ | No. |
+ Nama Lengkap |
+ Total Sesi |
+
+
+
+
+
+ {(_, index) => (
+
+ | {index + 1} |
+ Mursid Al-Catraz |
+ 1000 |
+
+ )}
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/apps/backoffice/src/app/(protected)/feedback-review-dimentorin/page.tsx b/apps/backoffice/src/app/(protected)/feedback-review-dimentorin/page.tsx
new file mode 100644
index 0000000..6fc556a
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/feedback-review-dimentorin/page.tsx
@@ -0,0 +1,181 @@
+import { SearchOutlined } from "@ant-design/icons";
+import { Button, Input, Select } from "@imphnen-frontend-service/ui/atoms";
+import { BackofficeWrapper, DataTable } from "@imphnen-frontend-service/ui/organisms";
+import { cn, For } from "@imphnen-frontend-service/utils";
+import { ColumnDef, getCoreRowModel, getPaginationRowModel, PaginationState, RowSelectionState, useReactTable } from "@tanstack/react-table";
+import { ReactElement, useState } from "react"
+
+const TABS = {
+ MENTORING: 'Mentoring',
+ PLATFORM: 'Platform'
+} as const
+type Tabs = typeof TABS[keyof typeof TABS]
+
+type FeedbackStatus = 'done' | 'todo';
+
+interface FeedbackType {
+ id: number
+ name: string
+ email: string
+ rating: number
+ status: FeedbackStatus
+}
+
+const mockData: FeedbackType[] = Array.from({ length: 90 }, (_, i) => ({
+ id: i + 1,
+ name: i % 3 === 0 ? 'Ahmad Wijuana' : 'Sofia Wijuana',
+ email: 'fullname23@gmail.com',
+ rating: 4.5,
+ status: i % 2 === 0 ? 'done' : 'todo',
+}))
+
+export default function Components(): ReactElement {
+ const [activeTab, setActiveTab] = useState(TABS.MENTORING)
+
+ const [rowSelection, setRowSelection] = useState({})
+ const [pagination, setPagination] = useState({
+ pageIndex: 0,
+ pageSize: 9,
+ });
+
+ const columns: ColumnDef[] = [
+ {
+ id: 'select',
+ meta: { cellClassName: cn("w-20") },
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ id: 'name',
+ header: 'Name',
+ accessorKey: 'name',
+ },
+ {
+ id: 'email',
+ header: 'Email',
+ accessorKey: 'email',
+ },
+ {
+ id: 'rating',
+ header: 'Rating',
+ accessorKey: 'rating',
+ },
+ {
+ id: 'status',
+ header: 'Status',
+ accessorKey: 'status',
+ cell: ({ row }) => {
+ const status = row.original.status;
+ const statusColors: Record = {
+ done: 'bg-success-200 text-success-500',
+ todo: 'bg-primary-200 text-primary-500',
+ };
+ const statusText: Record = {
+ done: 'Done',
+ todo: 'To Do',
+ };
+ return (
+
+ {statusText[status]}
+
+ );
+ },
+ },
+ {
+ header: 'Action',
+ meta: { cellClassName: cn("w-72") },
+ cell: ({ row }) => (
+
+ ),
+ },
+ ]
+
+ const table = useReactTable({
+ data: mockData,
+ columns,
+ state: {
+ pagination,
+ rowSelection,
+ },
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onPaginationChange: setPagination,
+ pageCount: Math.ceil(mockData.length / pagination.pageSize),
+ manualPagination: false,
+ });
+
+ return (
+
+
+
Feedback
+
+
+ {(tab) => (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/backoffice/src/app/(protected)/layout.tsx b/apps/backoffice/src/app/(protected)/layout.tsx
index a770c99..bc53fd2 100644
--- a/apps/backoffice/src/app/(protected)/layout.tsx
+++ b/apps/backoffice/src/app/(protected)/layout.tsx
@@ -7,7 +7,7 @@ export const AppLayout: FC = (): ReactElement => {
-
diff --git a/apps/backoffice/src/app/(protected)/roadmap-dimentorin/_components/modal/create-roadmap/index.tsx b/apps/backoffice/src/app/(protected)/roadmap-dimentorin/_components/modal/create-roadmap/index.tsx
new file mode 100644
index 0000000..0c4e3f1
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/roadmap-dimentorin/_components/modal/create-roadmap/index.tsx
@@ -0,0 +1,85 @@
+import { ArrowRightOutlined } from "@ant-design/icons";
+import { Button, Input, Select, Textarea } from "@imphnen-frontend-service/ui/atoms";
+import { Accordion, Modal, ModalProps } from "@imphnen-frontend-service/ui/molecules";
+import { cn, For } from "@imphnen-frontend-service/utils";
+import { FC } from "react";
+
+const labelClass = cn('text-neutral-800 text-[10px] font-medium mb-1.5 inline-block md:text-xs md:mb-2 xl:text-p3')
+
+export const ModalCreateRoadmap: FC
> = ({ isOpen, onClose }) => {
+ return (
+
+
+
+ Create Roadmaps
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Roadmap Preview
+
+
+
+ {(_, index) => (
+
+ )}
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/roadmap-dimentorin/page.tsx b/apps/backoffice/src/app/(protected)/roadmap-dimentorin/page.tsx
new file mode 100644
index 0000000..d2a0410
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/roadmap-dimentorin/page.tsx
@@ -0,0 +1,174 @@
+import { DeleteOutlined, EditOutlined, PlusOutlined, SearchOutlined } from "@ant-design/icons";
+import { Button, Input, Select } from "@imphnen-frontend-service/ui/atoms";
+import { BackofficeWrapper, DataTable } from "@imphnen-frontend-service/ui/organisms";
+import { cn } from "@imphnen-frontend-service/utils";
+import { ColumnDef, getCoreRowModel, getPaginationRowModel, PaginationState, RowSelectionState, useReactTable } from "@tanstack/react-table";
+import { useState } from "react";
+import { ModalCreateRoadmap } from "./_components/modal/create-roadmap";
+
+type LearningStatus = 'active' | 'inactive'
+
+type RoadmapType = {
+ id: number
+ name: string
+ learningLevel: string
+ status: LearningStatus
+}
+
+const mockData: RoadmapType[] = Array.from({ length: 90 }, (_, i) => ({
+ id: i + 1,
+ name: i === 0 ? 'Ahmad Wijuana' : 'Anna Wiguana',
+ learningLevel: ['Pemula', 'Menengah'][Math.floor(Math.random() * 2)],
+ status: i % 2 === 0 ? 'active' : 'inactive',
+}))
+
+export default function Components(): React.ReactElement {
+ const [openCreateModal, setOpenCreateModal] = useState(false)
+
+ const [rowSelection, setRowSelection] = useState({})
+ const [pagination, setPagination] = useState({
+ pageIndex: 0,
+ pageSize: 9,
+ });
+
+ const columns: ColumnDef[] = [
+ {
+ id: 'select',
+ meta: { cellClassName: cn("w-20") },
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ id: 'id',
+ header: 'No',
+ accessorKey: 'id',
+ },
+ {
+ id: 'name',
+ header: 'Nama Roadmap',
+ accessorKey: 'name',
+ },
+ {
+ id: 'learningLevel',
+ header: 'Tingkat Belajar',
+ accessorKey: 'learningLevel',
+ },
+ {
+ id: 'status',
+ header: 'Status',
+ accessorKey: 'status',
+ cell: ({ row }) => {
+ const status = row.original.status;
+ const statusColors: Record = {
+ inactive: 'bg-danger-200 text-danger-700',
+ active: 'bg-success-200 text-success-500',
+ };
+ const statusText: Record = {
+ inactive: 'Inactive',
+ active: 'Active',
+ };
+ return (
+
+ {statusText[status]}
+
+ );
+ },
+ },
+ {
+ header: 'Action',
+ meta: { cellClassName: cn("w-72") },
+ cell: ({ row }) => (
+
+
+
+
+ ),
+ },
+ ]
+
+ const table = useReactTable({
+ data: mockData,
+ columns,
+ state: {
+ pagination,
+ rowSelection,
+ },
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onPaginationChange: setPagination,
+ pageCount: Math.ceil(mockData.length / pagination.pageSize),
+ manualPagination: false,
+ });
+
+ return (
+
+ Content & Roadmap
+
+
+
+
AI Roadmaps
+
+
+
+
+
+
+
+
+
+
+
+ setOpenCreateModal(false)} />
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/session-dimentorin/_components/modal/detail/index.tsx b/apps/backoffice/src/app/(protected)/session-dimentorin/_components/modal/detail/index.tsx
new file mode 100644
index 0000000..38f0818
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/session-dimentorin/_components/modal/detail/index.tsx
@@ -0,0 +1,114 @@
+import { Icon } from "@iconify/react";
+import { Input, Select, Textarea } from "@imphnen-frontend-service/ui/atoms";
+import { Modal } from "@imphnen-frontend-service/ui/molecules";
+import { cn, For } from "@imphnen-frontend-service/utils";
+import { FC } from "react";
+
+const TOPICS = [
+ { id: 2, icon: '🏢', name: 'Industry Insight' },
+ { id: 4, icon: '🖥️', name: 'Basic IT' },
+]
+
+const placeholder = `Hi [Nama Mentor], Saya [Nama Kamu] & saya berharap dapat memiliki sesi mentoring dengan Anda.
+
+Saat ini, saya tertarik untuk mengejar __. Tujuan saya untuk sesi ini adalah __.
+
+Saya ingin tahu secara khusus tentang ___.
+1.Pertanyaan Anda
+2. ...
+3. ...`
+
+const labelClass = cn('text-neutral-800 text-[10px] font-semibold mb-1.5 inline-block md:text-xs md:mb-2 xl:text-[15px]')
+
+type ModalProps = {
+ open: boolean
+ setOpen: (open: boolean) => void
+}
+
+export const ModalDetailSession: FC = ({ open, setOpen }) => {
+ return (
+ setOpen(false)}
+ className="xl:max-w-[64rem] bg-white px-10 py-9"
+ closeButtonClassName="hidden"
+ >
+
+
+ Detail Sesi
+
+
+
+
+
+
Mentor
+
+
Muhammad Firdaus Oi Oi Oi, S.H., M.H.
+
UI Designer at Oray orayan Studios
+
+
+
+
+
+
+
+
Mentee
+
+
Muhammad Firdaus Oi Oi Oi, S.H., M.H.
+
UI Designer at Oray orayan Studios
+
+
+
+
Status
+
+ Finished
+
+
+
+
+
+
+
Topics
+
+
+ {(item, index) => (
+
+ {item.icon}
+ {item.name}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/session-dimentorin/page.tsx b/apps/backoffice/src/app/(protected)/session-dimentorin/page.tsx
new file mode 100644
index 0000000..98d77b8
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/session-dimentorin/page.tsx
@@ -0,0 +1,168 @@
+import { SearchOutlined } from "@ant-design/icons";
+import { Button, Input, Select } from "@imphnen-frontend-service/ui/atoms";
+import { BackofficeWrapper, DataTable } from "@imphnen-frontend-service/ui/organisms";
+import { cn } from "@imphnen-frontend-service/utils";
+import { ColumnDef, getCoreRowModel, getPaginationRowModel, PaginationState, RowSelectionState, useReactTable } from "@tanstack/react-table";
+import { ReactElement, useState } from "react";
+import { ModalDetailSession } from "./_components/modal/detail";
+
+type SessionStatus = 'ongoing' | 'finished';
+
+interface SessionType {
+ id: string
+ mentorName: string
+ menteeName: string
+ datetime: number
+ status: SessionStatus
+}
+
+const mockData: SessionType[] = Array.from({ length: 90 }, (_, i) => ({
+ id: `DS-${i + 1}`,
+ mentorName: 'Ahmad Wijuana',
+ menteeName: 'Sofia Wijuana',
+ datetime: new Date().getTime(),
+ status: i % 2 === 0 ? 'ongoing' : 'finished',
+}))
+
+export default function Components(): ReactElement {
+ const [openDetail, setOpenDetail] = useState(false);
+
+ const [rowSelection, setRowSelection] = useState({})
+ const [pagination, setPagination] = useState({
+ pageIndex: 0,
+ pageSize: 9,
+ });
+
+ const columns: ColumnDef[] = [
+ {
+ id: 'select',
+ meta: { cellClassName: cn("w-20") },
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ id: 'id',
+ header: 'ID Sesi',
+ accessorKey: 'id',
+ },
+ {
+ id: 'mentorName',
+ header: 'Nama Mentor',
+ accessorKey: 'name',
+ },
+ {
+ id: 'menteeName',
+ header: 'Nama Mentee',
+ accessorKey: 'name',
+ },
+ {
+ id: 'datetime',
+ header: 'Waktu',
+ accessorKey: 'datetime',
+ },
+ {
+ id: 'status',
+ header: 'Status',
+ accessorKey: 'status',
+ cell: ({ row }) => {
+ const status = row.original.status;
+ const statusColors: Record = {
+ ongoing: 'bg-warning-200 text-warning-700',
+ finished: 'bg-success-200 text-success-500',
+ };
+ const statusText: Record = {
+ ongoing: 'On Going',
+ finished: 'Finished',
+ };
+ return (
+
+ {statusText[status]}
+
+ );
+ },
+ },
+ {
+ header: 'Action',
+ meta: { cellClassName: cn("w-52") },
+ cell: ({ row }) => (
+
+ ),
+ },
+ ]
+
+ const table = useReactTable({
+ data: mockData,
+ columns,
+ state: {
+ pagination,
+ rowSelection,
+ },
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onPaginationChange: setPagination,
+ pageCount: Math.ceil(mockData.length / pagination.pageSize),
+ manualPagination: false,
+ });
+
+ return (
+
+ Session Management
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/general.tsx b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/general.tsx
new file mode 100644
index 0000000..69ebee8
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/general.tsx
@@ -0,0 +1,61 @@
+import { Button, Input, Select, ToggleInput } from "@imphnen-frontend-service/ui/atoms"
+import { cn } from "@imphnen-frontend-service/utils"
+import { FC } from "react"
+
+const labelClass = cn('text-neutral-800 text-[10px] font-medium mb-1.5 inline-block md:text-xs md:mb-2 xl:text-p3')
+
+export const GeneralSettings: FC = () => {
+ return (
+
+
General Settings
+
+
+
+
+
Platform Settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Legal Settings
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/notification.tsx b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/notification.tsx
new file mode 100644
index 0000000..10f010c
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/notification.tsx
@@ -0,0 +1,34 @@
+import { Button, Textarea, ToggleInput } from "@imphnen-frontend-service/ui/atoms"
+import { FC } from "react"
+
+export const NotificationSettings: FC = () => {
+ return (
+
+
Notification Settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/payment.tsx b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/payment.tsx
new file mode 100644
index 0000000..a2d3099
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/payment.tsx
@@ -0,0 +1,50 @@
+import { Button, Input, Select, Textarea } from "@imphnen-frontend-service/ui/atoms"
+import { cn } from "@imphnen-frontend-service/utils"
+import { FC } from "react"
+
+const labelClass = cn('text-neutral-800 text-[10px] font-medium mb-1.5 inline-block md:text-xs md:mb-2 xl:text-p3')
+
+export const PaymentSettings: FC = () => {
+ return (
+
+
Payment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Invoice
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/security.tsx b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/security.tsx
new file mode 100644
index 0000000..9e6f2af
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/security.tsx
@@ -0,0 +1,35 @@
+import { Button, Input } from "@imphnen-frontend-service/ui/atoms";
+import { cn } from "@imphnen-frontend-service/utils";
+import { FC } from "react";
+
+const labelClass = cn('text-neutral-800 text-[10px] font-medium mb-1.5 inline-block md:text-xs md:mb-2 xl:text-p3')
+
+export const SecuritySettings: FC = () => {
+ return (
+
+
Security Settings
+
+
+
+
+
+
Auto logout setelah X menit tidak aktif
+
+
+
+
+
Misal: 5 kali salah login = lock akun
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/user-roles-permission.tsx b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/user-roles-permission.tsx
new file mode 100644
index 0000000..037cd57
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/settings-dimentorin/_components/user-roles-permission.tsx
@@ -0,0 +1,118 @@
+import { DeleteOutlined, UserSwitchOutlined } from "@ant-design/icons";
+import { Button } from "@imphnen-frontend-service/ui/atoms";
+import { DataTable } from "@imphnen-frontend-service/ui/organisms";
+import { cn } from "@imphnen-frontend-service/utils";
+import { ColumnDef, getCoreRowModel, getPaginationRowModel, PaginationState, RowSelectionState, useReactTable } from "@tanstack/react-table";
+import { FC, useState } from "react";
+
+type UserRolesPermissionType = {
+ id: number
+ role: string
+ totalUser: number
+}
+
+const mockData: UserRolesPermissionType[] = Array.from({ length: 90 }, (_, i) => ({
+ id: i + 1,
+ role: ['Admin', 'Super Admin', 'Mentee', 'Mentor'][Math.floor(Math.random() * 4)],
+ totalUser: 10
+}))
+
+export const UserRolesPermission: FC = () => {
+ const [rowSelection, setRowSelection] = useState({})
+ const [pagination, setPagination] = useState({
+ pageIndex: 0,
+ pageSize: 9,
+ });
+
+ const columns: ColumnDef[] = [
+ {
+ id: 'select',
+ meta: { cellClassName: cn("w-20") },
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ id: 'role',
+ header: 'Role',
+ accessorKey: 'role',
+ },
+ {
+ id: 'totalUser',
+ header: 'Total User',
+ accessorKey: 'totalUser',
+ },
+ {
+ header: 'Action',
+ meta: { cellClassName: cn("w-96") },
+ cell: ({ row }) => (
+
+
+
+
+ ),
+ },
+ ]
+
+ const table = useReactTable({
+ data: mockData,
+ columns,
+ state: {
+ pagination,
+ rowSelection,
+ },
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onPaginationChange: setPagination,
+ pageCount: Math.ceil(mockData.length / pagination.pageSize),
+ manualPagination: false,
+ });
+
+ return (
+
+
+
User Roles & Permissions
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/settings-dimentorin/page.tsx b/apps/backoffice/src/app/(protected)/settings-dimentorin/page.tsx
new file mode 100644
index 0000000..7699fe9
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/settings-dimentorin/page.tsx
@@ -0,0 +1,53 @@
+import { BackofficeWrapper } from "@imphnen-frontend-service/ui/organisms"
+import { cn, For } from "@imphnen-frontend-service/utils"
+import { useState } from "react"
+import { GeneralSettings } from "./_components/general"
+import { UserRolesPermission } from "./_components/user-roles-permission"
+import { NotificationSettings } from "./_components/notification"
+import { SecuritySettings } from "./_components/security"
+import { PaymentSettings } from "./_components/payment"
+
+const TABS = {
+ general: "General Settings",
+ userRolePermissions: "User Roles & Permissions",
+ notification: "Notification Settings",
+ security: "Security",
+ payment: "Payment",
+} as const
+type Tabs = typeof TABS[keyof typeof TABS]
+
+export default function Components(): React.ReactElement {
+ const [activeTab, setActiveTab] = useState(TABS.general)
+
+ return (
+
+ Settings
+
+
+
+
+ {(tab) => (
+
+ )}
+
+
+
+ {activeTab === TABS.general &&
}
+ {activeTab === TABS.userRolePermissions &&
}
+ {activeTab === TABS.notification &&
}
+ {activeTab === TABS.security &&
}
+ {activeTab === TABS.payment &&
}
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/delete/index.tsx b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/delete/index.tsx
new file mode 100644
index 0000000..5a905e2
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/delete/index.tsx
@@ -0,0 +1,55 @@
+import { Button } from "@imphnen-frontend-service/ui/atoms";
+import { Modal } from "@imphnen-frontend-service/ui/molecules";
+import { FC } from "react";
+
+type ModalDeleteProps = {
+ open: boolean
+ setOpen: (open: boolean) => void
+ hadnleDelete?: () => void
+}
+
+export const ModalDelete: FC = ({ open, setOpen, hadnleDelete }) => {
+ return (
+ setOpen(false)}
+ closeButtonClassName="hidden"
+ >
+
+
+
+
+ Hapus Akun
+
+
+ Apakah kamu yakin untuk menghapus akun ini?
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/account-profile.tsx b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/account-profile.tsx
new file mode 100644
index 0000000..e5a91bc
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/account-profile.tsx
@@ -0,0 +1,86 @@
+import { Button, Input } from "@imphnen-frontend-service/ui/atoms";
+import { cn } from "@imphnen-frontend-service/utils";
+import { FC, useState } from "react";
+import { ModalDelete } from "../delete";
+import { ModalProps } from "../type";
+import { ModalSuspendOrBan } from "../suspend-or-ban";
+
+const labelClass = cn('text-neutral-800 text-[10px] font-semibold mb-1.5 inline-block md:text-xs md:mb-2 xl:text-[15px]')
+
+export const AccountProfile: FC = ({ setOpen }) => {
+ const [openDelete, setOpenDelete] = useState(false)
+ const [openSuspendOrBan, setOpenSuspendOrBan] = useState(false)
+
+ return (
+
+
+
+

+
+
+
+
+ Setidaknya rekomendasi ukuran 240x240 px.
+ .jpg, .jpeg, .png diperbolehkan
+
+
+
+
+
+
+
+
+
Status Akun
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/activity-log.tsx b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/activity-log.tsx
new file mode 100644
index 0000000..5f0de58
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/activity-log.tsx
@@ -0,0 +1,110 @@
+import { SearchOutlined } from "@ant-design/icons"
+import { Input, Select } from "@imphnen-frontend-service/ui/atoms"
+import { DataTable } from "@imphnen-frontend-service/ui/organisms"
+import { cn } from "@imphnen-frontend-service/utils"
+import { ColumnDef, getCoreRowModel, getPaginationRowModel, PaginationState, RowSelectionState, useReactTable } from "@tanstack/react-table"
+import { FC, useState } from "react"
+import dayjs from "dayjs"
+
+interface ActivityLogType {
+ id: number
+ date: string
+ menu: string
+ activity: string
+}
+
+const mockData: ActivityLogType[] = Array.from({ length: 90 }, (_, i) => ({
+ id: i + 1,
+ date: new Date().toISOString(),
+ menu: 'Mentoring',
+ activity: 'Mentoring Session',
+}))
+
+export const ActivityLog: FC = () => {
+ const [rowSelection, setRowSelection] = useState({})
+ const [pagination, setPagination] = useState({
+ pageIndex: 0,
+ pageSize: 9,
+ })
+
+ const columns: ColumnDef[] = [
+ {
+ id: 'select',
+ meta: { cellClassName: cn("w-16") },
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ id: 'date',
+ header: 'Waktu',
+ accessorKey: 'date',
+ cell: (info) => dayjs(info.row.original.date).format('DD MMMM YYYY, HH:mm WIB'),
+ },
+ {
+ id: 'menu',
+ header: 'Menu',
+ accessorKey: 'menu',
+ },
+ {
+ id: 'activity',
+ header: 'Activity',
+ accessorKey: 'activity',
+ }
+ ]
+
+ const table = useReactTable({
+ data: mockData,
+ columns,
+ state: {
+ pagination,
+ rowSelection,
+ },
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onPaginationChange: setPagination,
+ pageCount: Math.ceil(mockData.length / pagination.pageSize),
+ manualPagination: false,
+ });
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/detail-profile.tsx b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/detail-profile.tsx
new file mode 100644
index 0000000..4f753f6
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/detail-profile.tsx
@@ -0,0 +1,89 @@
+import { Icon } from "@iconify/react"
+import { Button } from "@imphnen-frontend-service/ui/atoms"
+import { For } from "@imphnen-frontend-service/utils"
+import { FC } from "react"
+
+const SOCIAL_LINKS = [
+ { icon: , url: "https://linkedin.com", label: "LinkedIn" },
+ { icon: , url: "https://github.com", label: "Github" },
+ { icon: , url: "https://facebook.com", label: "Facebook (Meta)" },
+ { icon: , url: "https://stackoverflow.com", label: "Stack Overflow" }
+]
+
+export const DetailProfile: FC = () => {
+ return (
+
+
+
+
+
+

+
+
+
Muhammad Firdaus Oiwobo
+
Mentor
+
+
+
+
+
+
+
+
+
+
+
+
Description
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut et massa mi. Aliquam in hendrerit urna. Pellentesque sit amet sapien fringilla, mattis ligula consectetur, ultrices mauris. Maecenas vitae mattis tellus. Nullam quis imperdiet augue. Vestibulum auctor ornare leo, non suscipit magna interdum eu. Curabitur pellentesque nibh nibh, at maximus ante fermentum sit amet. Pellentesque commodo lacus at sodales sodales. Quisque sagittis orci ut diam condimentum, vel euismod erat placerat. In iaculis arcu eros, eget tempus orci facilisis id.
+
+
+
+
Personal Informations
+
+
+
+
+
+
+
rzalaxib23@gmail.com
+
Email Address
+
+
+
+
+
+
+
+
+62 888 8888 8888
+
Phone Number
+
+
+
+
+
+
+
+
Jl. Mergosari, Kec. Suryakencana, Banjaran
+
Location
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/index.tsx b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/index.tsx
new file mode 100644
index 0000000..82279b8
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/index.tsx
@@ -0,0 +1,68 @@
+import { Button } from "@imphnen-frontend-service/ui/atoms"
+import { Modal } from "@imphnen-frontend-service/ui/molecules"
+import { cn, For, Show } from "@imphnen-frontend-service/utils"
+import { FC, useEffect, useState } from "react"
+import { AccountProfile } from "./account-profile"
+import { DetailProfile } from "./detail-profile"
+import { ActivityLog } from "./activity-log"
+import { ModalDetailUserProps } from "./type"
+
+const TABS = {
+ account: 'account profile',
+ detail: 'detail profile',
+ activity: 'activity logs',
+} as const
+type TabType = typeof TABS[keyof typeof TABS]
+
+export const ModalDetailUser: FC = ({
+ open,
+ setOpen,
+}) => {
+ const [activeTab, setActiveTab] = useState(TABS.account)
+
+ useEffect(() => {
+ if (!open) setActiveTab(TABS.account)
+ }, [open])
+
+ return (
+ setOpen(false)}
+ className="xl:max-w-[84rem] bg-white px-10 py-9"
+ closeButtonClassName="hidden"
+ >
+
+
+ Detail - Muhammad Firdaus Oiwobo
+
+
+
+
+ {(tab) => (
+
+ )}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/type.d.ts b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/type.d.ts
new file mode 100644
index 0000000..b443959
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/detail/type.d.ts
@@ -0,0 +1,5 @@
+import { ModalProps } from "../type";
+
+export interface ModalDetailUserProps extends ModalProps {
+ userId?: number | null
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/suspend-or-ban/index.tsx b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/suspend-or-ban/index.tsx
new file mode 100644
index 0000000..11843e1
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/suspend-or-ban/index.tsx
@@ -0,0 +1,42 @@
+import { FC } from "react";
+import { ModalProps } from "../type";
+import { Modal } from "@imphnen-frontend-service/ui/molecules";
+import { cn } from "@imphnen-frontend-service/utils";
+import { Button, Select, Textarea } from "@imphnen-frontend-service/ui/atoms";
+
+const labelClass = cn('text-neutral-800 text-[10px] font-semibold mb-1.5 inline-block md:text-xs md:mb-2 xl:text-[15px]')
+
+export const ModalSuspendOrBan: FC = ({ open, setOpen }) => {
+ return (
+ setOpen(false)}
+ className="bg-white px-10 py-9 xl:max-w-[32rem]"
+ closeButtonClassName="hidden"
+ >
+
+
+ Suspend/Ban
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/type.d.ts b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/type.d.ts
new file mode 100644
index 0000000..64b7327
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/_components/modal/type.d.ts
@@ -0,0 +1,4 @@
+export interface ModalProps {
+ open: boolean
+ setOpen: (open: boolean) => void
+}
diff --git a/apps/backoffice/src/app/(protected)/users-dimentorin/page.tsx b/apps/backoffice/src/app/(protected)/users-dimentorin/page.tsx
new file mode 100644
index 0000000..071da5f
--- /dev/null
+++ b/apps/backoffice/src/app/(protected)/users-dimentorin/page.tsx
@@ -0,0 +1,187 @@
+import { SearchOutlined } from "@ant-design/icons";
+import { Button, Input, Select } from "@imphnen-frontend-service/ui/atoms";
+import { BackofficeWrapper, DataTable } from "@imphnen-frontend-service/ui/organisms";
+import { cn, For } from "@imphnen-frontend-service/utils";
+import { ColumnDef, getCoreRowModel, getPaginationRowModel, PaginationState, RowSelectionState, useReactTable } from "@tanstack/react-table";
+import { ReactElement, useState } from "react";
+import { ModalDetailUser } from "./_components/modal/detail";
+
+type UserStatus = 'active' | 'inactive';
+
+interface UserType {
+ id: number
+ name: string
+ email: string
+ rating: number
+ status: UserStatus
+}
+
+const mockData: UserType[] = Array.from({ length: 90 }, (_, i) => ({
+ id: i + 1,
+ name: i % 3 === 0 ? 'Ahmad Wijuana' : 'Sofia Wijuana',
+ email: 'fullname23@gmail.com',
+ rating: 4.5,
+ status: i % 2 === 0 ? 'active' : 'inactive',
+}))
+
+export default function Components(): ReactElement {
+ const TABS = ['mentor', 'mentee'] as const
+ const [activeTab, setActiveTab] = useState<'mentor' | 'mentee'>('mentor')
+ const [showDetail, setShowDetail] = useState(false)
+ const [selectedUserId, setSelectedUserId] = useState(null)
+
+ const [rowSelection, setRowSelection] = useState({})
+ const [pagination, setPagination] = useState({
+ pageIndex: 0,
+ pageSize: 9,
+ });
+
+ const columns: ColumnDef[] = [
+ {
+ id: 'select',
+ meta: { cellClassName: cn("w-20") },
+ header: ({ table }) => (
+
+ ),
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ id: 'name',
+ header: 'Name',
+ accessorKey: 'name',
+ },
+ {
+ id: 'email',
+ header: 'Email',
+ accessorKey: 'email',
+ },
+ {
+ id: 'rating',
+ header: 'Rating',
+ accessorKey: 'rating',
+ },
+ {
+ id: 'status',
+ header: 'Status',
+ accessorKey: 'status',
+ cell: ({ row }) => {
+ const status = row.original.status;
+ const statusColors: Record = {
+ active: 'bg-success-200 text-success-500',
+ inactive: 'bg-danger-200 text-danger-500',
+ };
+ const statusText: Record = {
+ active: 'Active',
+ inactive: 'Inactive',
+ };
+ return (
+
+ {statusText[status]}
+
+ );
+ },
+ },
+ {
+ header: 'Action',
+ meta: { cellClassName: cn("w-72") },
+ cell: ({ row }) => (
+
+ ),
+ },
+ ]
+
+ const table = useReactTable({
+ data: mockData,
+ columns,
+ state: {
+ pagination,
+ rowSelection,
+ },
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onPaginationChange: setPagination,
+ pageCount: Math.ceil(mockData.length / pagination.pageSize),
+ manualPagination: false,
+ });
+
+ return (
+
+
+
User Management
+
+
+ {(tab) => (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/libs/ui/src/atoms/index.ts b/libs/ui/src/atoms/index.ts
index 7e95c4a..2d67752 100644
--- a/libs/ui/src/atoms/index.ts
+++ b/libs/ui/src/atoms/index.ts
@@ -1,4 +1,5 @@
export * from './button';
export * from './input';
export * from './textarea';
-export * from './select'
\ No newline at end of file
+export * from './select'
+export * from './toggle';
\ No newline at end of file
diff --git a/libs/ui/src/atoms/toggle/index.ts b/libs/ui/src/atoms/toggle/index.ts
new file mode 100644
index 0000000..ed0fbdd
--- /dev/null
+++ b/libs/ui/src/atoms/toggle/index.ts
@@ -0,0 +1 @@
+export * from './toggle'
diff --git a/libs/ui/src/atoms/toggle/toggle.tsx b/libs/ui/src/atoms/toggle/toggle.tsx
new file mode 100644
index 0000000..c0d8f40
--- /dev/null
+++ b/libs/ui/src/atoms/toggle/toggle.tsx
@@ -0,0 +1,27 @@
+import { cn } from "@imphnen-frontend-service/utils";
+import { DetailedHTMLProps, FC, HTMLAttributes } from "react";
+
+export type ToggleInputProps = DetailedHTMLProps<
+ HTMLAttributes,
+ HTMLInputElement
+> & {
+ label?: string
+ labelClassName?: string
+}
+
+export const ToggleInput: FC = ({ label, className, labelClassName, ...rest }) => {
+ return (
+
+ );
+}
diff --git a/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx b/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx
index 676dce0..ff42883 100644
--- a/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx
+++ b/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx
@@ -1,9 +1,13 @@
import {
AppstoreOutlined,
AuditOutlined,
+ BookOutlined,
+ CommentOutlined,
InboxOutlined,
LogoutOutlined,
ReloadOutlined,
+ ScheduleOutlined,
+ SettingOutlined,
UsergroupAddOutlined,
UserOutlined,
UserSwitchOutlined,
@@ -11,102 +15,53 @@ import {
import { Button } from '../../atoms';
import { FC, ReactElement } from 'react';
import { Link, useLocation } from 'react-router-dom';
-import { useSession } from '@imphnen-frontend-service/utils';
+import { cn, For, useSession } from '@imphnen-frontend-service/utils';
+
+const MENUS = [
+ { label: 'Dashboard & Set Gacha', href: '/dashboard', icon: },
+ { label: 'Dashboard - Dimentorin', href: '/dashboard-dimentorin', icon: },
+ { label: 'Gacha Roll', href: '/gacha-roll', icon: },
+ { label: 'Permissions', href: '/permissions', icon: },
+ { label: 'Roles', href: '/roles', icon: },
+ { label: 'Data Akun', href: '/accounts', icon: },
+ { label: 'Validasi Transaksi', href: '/transactions', icon: },
+ { label: 'Data Pengiriman Hadiah', href: '/prizes', icon: },
+ { label: 'User - Dimentorin', href: '/users-dimentorin', icon: },
+ { label: 'Session - Dimentorin', href: '/session-dimentorin', icon: },
+ { label: 'Content & Roadmap', href: '/roadmap-dimentorin', icon: },
+ { label: 'Feedback & Review', href: '/feedback-review-dimentorin', icon: },
+ { label: 'Settings - Dimentorin', href: '/settings-dimentorin', icon: },
+]
export const BackofficeSidebar: FC = (): ReactElement => {
const { signOut } = useSession();
const location = useLocation();
- const isActive = (path: string) => location.pathname.includes(path);
+ const isActive = (path: string) => {
+ if (path === '/dashboard' && location.pathname === '/dashboard-dimentorin') return false
+ return location.pathname.includes(path)
+ };
return (
-