Feat/backoffice dimentorin (#53)
* feat(backoffice): slicing backoffice dimentorin * feat(backoffice): slicing backoffice dimentorin - user management * feat(backoffice): slicing backoffice dimentorin - modal detail user management * feat(backoffice): slicing backoffice dimentorin - session management * feat(backoffice): slicing backoffice dimentorin - modal detail session * feat(backoffice): slicing backoffice dimentorin - modal detail session * feat(backoffice): slicing backoffice dimentorin - setting page * feat(backoffice): slicing backoffice dimentorin - content & roadmap * feat(backoffice): slicing backoffice dimentorin - feedback & review
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
export * from './button';
|
||||
export * from './input';
|
||||
export * from './textarea';
|
||||
export * from './select'
|
||||
export * from './select'
|
||||
export * from './toggle';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './toggle'
|
||||
@@ -0,0 +1,27 @@
|
||||
import { cn } from "@imphnen-frontend-service/utils";
|
||||
import { DetailedHTMLProps, FC, HTMLAttributes } from "react";
|
||||
|
||||
export type ToggleInputProps = DetailedHTMLProps<
|
||||
HTMLAttributes<HTMLInputElement>,
|
||||
HTMLInputElement
|
||||
> & {
|
||||
label?: string
|
||||
labelClassName?: string
|
||||
}
|
||||
|
||||
export const ToggleInput: FC<ToggleInputProps> = ({ label, className, labelClassName, ...rest }) => {
|
||||
return (
|
||||
<label className={cn("flex items-center gap-5 cursor-pointer mb-8", className)}>
|
||||
<span className={cn("text-p3 text-gray-800 font-medium", labelClassName)}>{label}</span>
|
||||
<input type="checkbox" className="sr-only peer" {...rest} />
|
||||
<div
|
||||
className={cn(
|
||||
"w-14 h-7 bg-gray-300 rounded-3xl relative transition-colors",
|
||||
"peer-checked:bg-blue-600 peer-focus:outline peer-focus:outline-blue-500 peer-checked:[&>div]:translate-x-6.5",
|
||||
)}
|
||||
>
|
||||
<div className="absolute left-0.5 top-0.5 h-6 w-6 bg-white rounded-full shadow transition-transform peer-checked:translate-x-6.5" />
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
@@ -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: <AppstoreOutlined className="text-[20px]" /> },
|
||||
{ label: 'Dashboard - Dimentorin', href: '/dashboard-dimentorin', icon: <AppstoreOutlined className="text-[20px]" /> },
|
||||
{ label: 'Gacha Roll', href: '/gacha-roll', icon: <ReloadOutlined className="text-[20px]" /> },
|
||||
{ label: 'Permissions', href: '/permissions', icon: <UserSwitchOutlined className="text-[20px]" /> },
|
||||
{ label: 'Roles', href: '/roles', icon: <UsergroupAddOutlined className="text-[20px]" /> },
|
||||
{ label: 'Data Akun', href: '/accounts', icon: <UserOutlined className="text-[20px]" /> },
|
||||
{ label: 'Validasi Transaksi', href: '/transactions', icon: <AuditOutlined className="text-[20px]" /> },
|
||||
{ label: 'Data Pengiriman Hadiah', href: '/prizes', icon: <InboxOutlined className="text-[20px]" /> },
|
||||
{ label: 'User - Dimentorin', href: '/users-dimentorin', icon: <UserSwitchOutlined className="text-[20px]" /> },
|
||||
{ label: 'Session - Dimentorin', href: '/session-dimentorin', icon: <ScheduleOutlined className="text-[20px]" /> },
|
||||
{ label: 'Content & Roadmap', href: '/roadmap-dimentorin', icon: <BookOutlined className="text-[20px]" /> },
|
||||
{ label: 'Feedback & Review', href: '/feedback-review-dimentorin', icon: <CommentOutlined className="text-[20px]" /> },
|
||||
{ label: 'Settings - Dimentorin', href: '/settings-dimentorin', icon: <SettingOutlined className="text-[20px]" /> },
|
||||
]
|
||||
|
||||
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 (
|
||||
<aside className="sticky top-0 left-0 w-[280px] bg-white min-h-screen py-[60px] px-[28px] shadow-xl flex flex-col justify-between">
|
||||
<aside className="sticky top-0 left-0 w-[280px] bg-white h-svh py-[60px] px-[28px] shadow-xl flex flex-col justify-between">
|
||||
<div className="flex flex-col gap-20 justify-between items-center">
|
||||
<img src="/logos/simple.svg" alt="IMPHNEN Logo" className="w-[150px]" />
|
||||
|
||||
<nav className="flex flex-col gap-4 w-full">
|
||||
<Link
|
||||
to="/dashboard"
|
||||
className={`flex items-center justify-items-start gap-3 px-[8px] py-[10px] ${
|
||||
isActive('/dashboard')
|
||||
? 'bg-primary-500 text-white rounded-md'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<AppstoreOutlined className="text-[20px]" />
|
||||
<span className="text-p3 font-medium">Dashboard & Set Gacha</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/gacha-roll"
|
||||
className={`flex items-center justify-items-start gap-3 px-[8px] py-[10px] ${
|
||||
isActive('/gacha-roll')
|
||||
? 'bg-primary-500 text-white rounded-md'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<ReloadOutlined className="text-[20px]" />
|
||||
<span className="text-p3 font-medium">Gacha Roll</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/permissions"
|
||||
className={`flex items-center justify-items-start gap-3 px-[8px] py-[10px] ${
|
||||
isActive('/permissions')
|
||||
? 'bg-primary-500 text-white rounded-md'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<UserSwitchOutlined className="text-[20px]" />
|
||||
<span className="text-p3 font-medium">Permissions</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/roles"
|
||||
className={`flex items-center justify-items-start gap-3 px-[8px] py-[10px] ${
|
||||
isActive('/roles')
|
||||
? 'bg-primary-500 text-white rounded-md'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<UsergroupAddOutlined className="text-[20px]" />
|
||||
<span className="text-p3 font-medium">Roles</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/accounts"
|
||||
className={`flex items-center justify-items-start gap-3 px-[8px] py-[10px] ${
|
||||
isActive('/accounts')
|
||||
? 'bg-primary-500 text-white rounded-md'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<UserOutlined className="text-[20px]" />
|
||||
<span className="text-p3 font-medium">Data Akun</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/transactions"
|
||||
className={`flex items-center justify-items-start gap-3 px-[8px] py-[10px] ${
|
||||
isActive('/transactions')
|
||||
? 'bg-primary-500 text-white rounded-md'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<AuditOutlined className="text-[20px]" />
|
||||
<span className="text-p3 font-medium">Validasi Transaksi</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/prizes"
|
||||
className={`flex items-center justify-items-start gap-3 px-[8px] py-[10px] ${
|
||||
isActive('/prizes')
|
||||
? 'bg-primary-500 text-white rounded-md'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<InboxOutlined className="text-[20px]" />
|
||||
<span className="text-p3 font-medium">Data Pengiriman Hadiah</span>
|
||||
</Link>
|
||||
<nav className="flex flex-col gap-4 w-full h-[calc(100svh-20rem)] overflow-y-auto">
|
||||
<For data={MENUS}>
|
||||
{({ label, href, icon }) => (
|
||||
<Link
|
||||
key={href}
|
||||
to={href}
|
||||
className={cn(
|
||||
"flex items-center justify-items-start gap-3 px-[8px] py-[10px]",
|
||||
isActive(href) ? "bg-primary-500 text-white rounded-md" : "text-gray-700 hover:bg-gray-100"
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
<span className="text-p3 font-medium">{label}</span>
|
||||
</Link>
|
||||
)}
|
||||
</For>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { cn } from "@imphnen-frontend-service/utils"
|
||||
import { Icon } from '@iconify/react'
|
||||
import { FC, ReactElement, ReactNode } from "react"
|
||||
import { Button } from "../../atoms"
|
||||
|
||||
export type TBackofficeWrapperProps = {
|
||||
children: ReactNode
|
||||
title?: string
|
||||
className?: string
|
||||
classHeader?: string
|
||||
classTitle?: string
|
||||
}
|
||||
|
||||
export const BackofficeWrapper: FC<TBackofficeWrapperProps> = ({
|
||||
children,
|
||||
title,
|
||||
className,
|
||||
classHeader,
|
||||
classTitle,
|
||||
}): ReactElement => {
|
||||
return (
|
||||
<main className={cn("w-full px-[48px] py-[40px] flex flex-col gap-8", className)}>
|
||||
<header className={cn("bg-white py-5 px-7 rounded-md shadow flex items-center justify-between", classHeader)}>
|
||||
<h1 className={cn("text-[19px] text-primary-500 font-semibold", classTitle)}>{title}</h1>
|
||||
|
||||
<div className="flex items-center gap-x-6">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
className="max-h-full p-3"
|
||||
>
|
||||
<Icon icon="mdi:bell-outline" className="size-6" />
|
||||
</Button>
|
||||
<div className="flex items-center gap-x-6">
|
||||
<div className="text-neutral-600 font-medium">
|
||||
<p className="text-p3">Rizal Syaepulloh</p>
|
||||
<p className="text-label1">Super Admin</p>
|
||||
</div>
|
||||
<div className="size-12 rounded-full overflow-hidden">
|
||||
<img src="/images/asd687hwq6nds4dfjj2983.webp" alt="Profile" className="size-full object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
{children}
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './backoffice-wrapper';
|
||||
@@ -6,10 +6,12 @@ import {
|
||||
flexRender,
|
||||
ColumnDef,
|
||||
Table,
|
||||
RowData,
|
||||
} from '@tanstack/react-table';
|
||||
import { Pagination } from '../../molecules';
|
||||
|
||||
import React from 'react';
|
||||
import { cn } from '@imphnen-frontend-service/utils';
|
||||
|
||||
interface DataTableProps<T> {
|
||||
data: T[];
|
||||
@@ -18,7 +20,7 @@ interface DataTableProps<T> {
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export const DataTable = <T,>({
|
||||
export const DataTable = <T extends RowData>({
|
||||
data,
|
||||
columns,
|
||||
pageSize = 9,
|
||||
@@ -49,7 +51,7 @@ export const DataTable = <T,>({
|
||||
{headerGroup.headers.map((header) => (
|
||||
<th
|
||||
key={header.id}
|
||||
className="py-4 px-5 font-normal first:rounded-l-lg last:rounded-r-lg"
|
||||
className={cn("py-4 px-5 font-normal first:rounded-l-lg last:rounded-r-lg", header?.column?.columnDef?.meta?.headerClassName)}
|
||||
>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
@@ -68,7 +70,7 @@ export const DataTable = <T,>({
|
||||
{row.getVisibleCells().map((cell, index) => (
|
||||
<td
|
||||
key={cell.id}
|
||||
className="py-3 px-5 first:rounded-l-lg last:rounded-r-lg"
|
||||
className={cn("py-3 px-5 first:rounded-l-lg last:rounded-r-lg", cell?.column?.columnDef?.meta?.cellClassName)}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './datatable';
|
||||
export * from './type.d';
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import "@tanstack/react-table"
|
||||
|
||||
declare module "@tanstack/react-table" {
|
||||
interface ColumnMeta {
|
||||
headerClassName?: string
|
||||
cellClassName?: string
|
||||
}
|
||||
}
|
||||
@@ -5,3 +5,4 @@ export * from './backoffice-sidebar';
|
||||
export * from './datatable';
|
||||
export * from './filter';
|
||||
export * from './controlled-field';
|
||||
export * from './backoffice-wrapper';
|
||||
|
||||
Reference in New Issue
Block a user