fix group menu
This commit is contained in:
@ -41,12 +41,8 @@ const Login = () => {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
if (!login) {
|
||||
throw new Error('JWTProvider is required for this form.');
|
||||
}
|
||||
|
||||
if (!login) throw new Error('JWTProvider is required for this form.');
|
||||
await login(values.username, values.password);
|
||||
// console.log(login);
|
||||
|
||||
if (values.remember) {
|
||||
localStorage.setItem('username', values.username);
|
||||
|
||||
25
src/components/confirm.tsx
Normal file
25
src/components/confirm.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from "react";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
const ConfirmDialog = ({ open, onClose, title, content, onYes, onNo }: any) => {
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose}>
|
||||
{title && <DialogTitle>{title}</DialogTitle>}
|
||||
{content && <DialogContent>{content}</DialogContent>}
|
||||
<DialogActions>
|
||||
<Button onClick={onNo} color="secondary">
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={onYes} color="primary">
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmDialog;
|
||||
156
src/components/ui/DataTable.tsx
Normal file
156
src/components/ui/DataTable.tsx
Normal file
@ -0,0 +1,156 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
SortingState,
|
||||
useReactTable
|
||||
} from '@tanstack/react-table';
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '@/components/ui/table';
|
||||
import { useState } from 'react';
|
||||
import { Input } from './input';
|
||||
import { Button } from './button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger
|
||||
} from './dialog';
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
onUpdate: any;
|
||||
onDelete: any;
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
createGroup,
|
||||
onUpdate,
|
||||
onDelete
|
||||
}: DataTableProps<TData, TValue> & { createGroup?: () => void }) {
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-4 justify-between">
|
||||
<Input
|
||||
placeholder="Search"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
|
||||
{createGroup && (
|
||||
<Button variant="outline" onClick={createGroup}>
|
||||
Create Group
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
<TableHead>Actions</TableHead>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell>
|
||||
<div className="space-x-2">
|
||||
<Button
|
||||
className="bg-blue-500 text-white px-2 py-1 rounded"
|
||||
onClick={() => onUpdate(row.original)}>
|
||||
Update
|
||||
</Button>
|
||||
<Button
|
||||
className="bg-red-500 text-white px-2 py-1 rounded"
|
||||
onClick={() => onDelete(row.original)}>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,12 +1,16 @@
|
||||
interface apiConfigProps {
|
||||
service_dashboard: string;
|
||||
service_customer: string;
|
||||
service_master_data: string;
|
||||
}
|
||||
|
||||
const API_URL = import.meta.env.VITE_APP_API_URL;
|
||||
const apiConfig: apiConfigProps = {
|
||||
service_dashboard: `${API_URL}${import.meta.env.VITE_ENV != 'development' ? '/d' : ""}`,
|
||||
service_master_data: `${API_URL}/m`,
|
||||
// service_dashboard: `${API_URL}${import.meta.env.VITE_ENV != 'development' ? '/d' : ''}`,
|
||||
service_dashboard: `${API_URL}/d`,
|
||||
service_customer: `${API_URL}/c`,
|
||||
// service_master_data: `${API_URL}/m`
|
||||
service_master_data: `${API_URL}/t`
|
||||
};
|
||||
|
||||
export { apiConfig };
|
||||
|
||||
@ -19,7 +19,7 @@ const Header = () => {
|
||||
return (
|
||||
<header
|
||||
className={clsx(
|
||||
'flex items-center transition-[height] shrink-0 h-[--tw-header-height] bg-[length:600px] bg-no-repeat',
|
||||
'flex items-center transition-[height] shrink-0 h-[--tw-header-height] bg-[length:600px] bg-no-repeat bg-red-600',
|
||||
headerSticky &&
|
||||
'transition-[height] fixed z-10 top-0 left-0 right-0 shadow-sm backdrop-blur-md bg-white/70 dark:bg-coal-500/70 dark:border-b dark:border-b-coal-100'
|
||||
)}
|
||||
|
||||
@ -44,7 +44,7 @@ const HeaderLogo = () => {
|
||||
/> */}
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/app/app-logo.png')}
|
||||
className="dark:hidden h-10"
|
||||
className="dark:hidden h-14"
|
||||
alt="logo"
|
||||
/>
|
||||
<img
|
||||
@ -55,7 +55,7 @@ const HeaderLogo = () => {
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center">
|
||||
<h3 className="text-gray-700 text-base hidden md:block">TPAY Dashboard Portal</h3>
|
||||
<h3 className="text-gray-50 text-xl hidden md:block">TPAY Dashboard Portal</h3>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -3,7 +3,7 @@ import { NavbarMenu } from '../';
|
||||
|
||||
const Navbar = () => {
|
||||
return (
|
||||
<div className="border-b border-gray-200 pb-5 lg:pb-0 mb-5 lg:mb-5">
|
||||
<div className="border-b border-gray-200 lg:pb-0 py-2 mb-5 mt-3 lg:mt-5">
|
||||
<Container className="flex flex-wrap justify-between items-center gap-2">
|
||||
<NavbarMenu />
|
||||
</Container>
|
||||
|
||||
21
src/pages/access/access-type/AccessType.tsx
Normal file
21
src/pages/access/access-type/AccessType.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Container, DataGridInner } from '@/components';
|
||||
import {
|
||||
ManageAccessTypeContext,
|
||||
ManageAccessTypeContextProvider
|
||||
} from './hooks/ManageAccessTypeContext';
|
||||
import AddDialog from './blocks/AddDialog';
|
||||
|
||||
const AccessType = () => {
|
||||
return (
|
||||
<ManageAccessTypeContextProvider>
|
||||
<Container>
|
||||
<div className="grid gap-5 lg:gap-7.5">
|
||||
<DataGridInner />
|
||||
</div>
|
||||
<AddDialog />
|
||||
</Container>
|
||||
</ManageAccessTypeContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessType;
|
||||
141
src/pages/access/access-type/blocks/AddDialog.tsx
Normal file
141
src/pages/access/access-type/blocks/AddDialog.tsx
Normal file
@ -0,0 +1,141 @@
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useManageAccessTypeContext } from '../hooks/useManageAccessTypeContext';
|
||||
import { Alert, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import { Dialog, DialogBody, DialogContent, DialogHeader } from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const AddDialog = () => {
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const { showAddDialog, handleAddDialog, accessTypes } = useManageAccessTypeContext();
|
||||
const { reload } = useDataGrid();
|
||||
const { PostData, PutData } = useCallApi();
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
name: '',
|
||||
description: '',
|
||||
internal_name: ''
|
||||
};
|
||||
|
||||
const [formField, setFormField] = useState(initialState);
|
||||
const resetForm = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
// setIsSubmitting(true);
|
||||
console.log(formField);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
|
||||
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
||||
<DialogHeader className="p-5 border-0">
|
||||
<div className="flex items-center justify-between flex-wrap grow">
|
||||
<div className="flex flex-col justify-center">
|
||||
<h1 className="text-xl font-semibold leading-none text-gray-900">
|
||||
Create Access Type
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 text-sm font-normal text-gray-700"></div>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-100 opacity-50"
|
||||
onClick={() => {
|
||||
handleAddDialog(false);
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
<KeenIcon icon="cross" className="text-1.5xl" />
|
||||
</div>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<DialogBody className="scrollable-y px-0 pb-0" ref={parentRef}>
|
||||
<div className="flex flex-col px-0">
|
||||
{alert.show && (
|
||||
<Alert variant="danger" className="mb-3">
|
||||
<h3>{alert.message}</h3>
|
||||
</Alert>
|
||||
)}
|
||||
<form action="" onSubmit={handleSubmit}>
|
||||
<div className="card-body grid gap-5 p-0">
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">Name</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.name}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, name: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Description
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.description}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, description: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Internal Name
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.internal_name}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, internal_name: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-2.5 gap-5">
|
||||
<Button variant={'outline'} type="reset" onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant={'default'} type="submit">
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DialogBody>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDialog;
|
||||
49
src/pages/access/access-type/blocks/ListToolBar.tsx
Normal file
49
src/pages/access/access-type/blocks/ListToolBar.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useManageAccessTypeContext } from '../hooks/useManageAccessTypeContext';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const ListToolbar = () => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const { handleAddDialog, handleEditDialog } = useManageAccessTypeContext();
|
||||
|
||||
return (
|
||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex w-[50%] gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<KeenIcon icon="magnifier" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Access Type"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<DefaultTooltip title={'Filter'} placement={'top'}>
|
||||
<Button variant={'outline'} className="h-7.5 disabled:bg-gray-400">
|
||||
<KeenIcon icon="filter" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleAddDialog(true)}
|
||||
>
|
||||
Add Data
|
||||
</Button>
|
||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
|
||||
<KeenIcon icon="arrows-circle" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListToolbar;
|
||||
162
src/pages/access/access-type/hooks/ManageAccessTypeContext.tsx
Normal file
162
src/pages/access/access-type/hooks/ManageAccessTypeContext.tsx
Normal file
@ -0,0 +1,162 @@
|
||||
import { DataGridColumnHeader, DataGridProvider } from '@/components';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { createContext, useCallback, useMemo, useState } from 'react';
|
||||
import ListToolbar from '../blocks/ListToolBar';
|
||||
|
||||
interface SelectedUser {
|
||||
id: string;
|
||||
name: string;
|
||||
internal_name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface AccessTypeProps {
|
||||
id: string;
|
||||
name: string;
|
||||
internal_name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface ContextProps {
|
||||
showEditDialog: boolean;
|
||||
handleEditDialog: (show: boolean, selected_user: string | null) => void;
|
||||
showAddDialog: boolean;
|
||||
handleAddDialog: (show: boolean) => void;
|
||||
selectedUser: string | null;
|
||||
accessTypes: AccessTypeProps[];
|
||||
}
|
||||
|
||||
const initialProps: ContextProps = {
|
||||
showEditDialog: false,
|
||||
handleEditDialog: () => {},
|
||||
showAddDialog: false,
|
||||
handleAddDialog: () => {},
|
||||
selectedUser: null,
|
||||
accessTypes: []
|
||||
};
|
||||
|
||||
const ManageAccessTypeContext = createContext<ContextProps>(initialProps);
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const ManageAccessTypeContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [selectedUser, setSelectedUser] = useState<string | null>(null);
|
||||
const [accessTypes, setAccessTypes] = useState<AccessTypeProps[]>([]);
|
||||
const { GetData } = useCallApi();
|
||||
|
||||
const handleEditDialog = useCallback((show: boolean, selected_user: string | null) => {
|
||||
setSelectedUser(show ? selected_user : null);
|
||||
setShowEditDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleAddDialog = useCallback((show: boolean) => {
|
||||
setShowAddDialog(show);
|
||||
}, []);
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorFn: (row) => row.id,
|
||||
id: 'id',
|
||||
header: ({ column }) => <DataGridColumnHeader title="ID" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
id: 'name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.internal_name,
|
||||
id: 'internal_name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Internal Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.description,
|
||||
id: 'description',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Description" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableSorting: false,
|
||||
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-primary mr-2"
|
||||
onClick={() => handleEditDialog(true, row.original.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button className="btn btn-sm btn-primary" onClick={() => handleAddDialog(true)}>
|
||||
Add
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
},
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]',
|
||||
cellClassName: 'text-center'
|
||||
}
|
||||
}
|
||||
],
|
||||
[handleAddDialog, handleEditDialog]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto py-5">
|
||||
<h1>Manage Access Type</h1>
|
||||
</div>
|
||||
<ManageAccessTypeContext.Provider
|
||||
value={{
|
||||
showEditDialog,
|
||||
handleEditDialog,
|
||||
showAddDialog,
|
||||
handleAddDialog,
|
||||
selectedUser,
|
||||
accessTypes
|
||||
}}
|
||||
>
|
||||
<Toaster expand visibleToasts={9} duration={3000} />
|
||||
<DataGridProvider
|
||||
columns={columns}
|
||||
pagination={{ size: 10 }}
|
||||
layout={{ card: true }}
|
||||
toolbar={<ListToolbar />}
|
||||
sorting={[{ id: 'id', desc: true }]}
|
||||
serverSide={true}
|
||||
>
|
||||
{children}
|
||||
</DataGridProvider>
|
||||
</ManageAccessTypeContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ManageAccessTypeContext, ManageAccessTypeContextProvider };
|
||||
export type { SelectedUser };
|
||||
@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { ManageAccessTypeContext } from './ManageAccessTypeContext';
|
||||
|
||||
const useManageAccessTypeContext = () => {
|
||||
const context = useContext(ManageAccessTypeContext);
|
||||
|
||||
if (!context) throw new Error('useManageAccessTypeContext must be used within AuthProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { useManageAccessTypeContext };
|
||||
21
src/pages/access/member-credentials/MemberCredentials.tsx
Normal file
21
src/pages/access/member-credentials/MemberCredentials.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import MemberCredentialsForm from './blocks/MemberCredentialsForm';
|
||||
import { useMemberCredentials } from './hooks';
|
||||
|
||||
const MemberCredentials = () => {
|
||||
const { error, memberCredentials, handleChange, handleSubmit } = useMemberCredentials();
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Member Credential</h1>
|
||||
<MemberCredentialsForm
|
||||
form={memberCredentials}
|
||||
error={error}
|
||||
onChange={handleChange}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemberCredentials;
|
||||
@ -0,0 +1,97 @@
|
||||
import { Alert } from '@/components';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
|
||||
interface MemberCredentials {
|
||||
accessType: string | null;
|
||||
username: string;
|
||||
credential: string;
|
||||
confirmCredential: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
form: MemberCredentials;
|
||||
error: string | null;
|
||||
onChange: (key: keyof MemberCredentials, value: string) => void;
|
||||
onSubmit: () => void;
|
||||
}
|
||||
|
||||
const MemberCredentialsForm = ({ form, error, onChange, onSubmit }: Props) => {
|
||||
return (
|
||||
<div className="max-w-md mx-auto bg-white p-6 rounded-lg shadow-md">
|
||||
<h2 className="text-xl font-bold mb-4">Create Member Credentials</h2>
|
||||
|
||||
{error && (
|
||||
<Alert variant="danger" className="mb-5">
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Access Type */}
|
||||
<div>
|
||||
<label>
|
||||
<span className="text-red-500">*</span>Access Type
|
||||
</label>
|
||||
<Select onValueChange={(value) => onChange('accessType', value)}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select Access Type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Pin Credentials">Pin Credentials</SelectItem>
|
||||
<SelectItem value="Secret Auth">Secret Auth</SelectItem>
|
||||
<SelectItem value="APIkey">APIkey</SelectItem>
|
||||
<SelectItem value="Web Credentials">Web Credentials</SelectItem>
|
||||
<SelectItem value="OTM Tpay">OTM Tpay</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Username */}
|
||||
<div>
|
||||
<label>
|
||||
<span className="text-red-500">*</span>Username
|
||||
</label>
|
||||
<Input value={form.username} onChange={(e) => onChange('username', e.target.value)} />
|
||||
</div>
|
||||
|
||||
{/* Credentials */}
|
||||
<div>
|
||||
<label>
|
||||
<span className="text-red-500">*</span>Credentials
|
||||
</label>
|
||||
<Input
|
||||
type="password"
|
||||
value={form.credential}
|
||||
onChange={(e) => onChange('credential', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Confirm Credentials */}
|
||||
<div>
|
||||
<label>
|
||||
<span className="text-red-500">*</span>Confirm Credentials
|
||||
</label>
|
||||
<Input
|
||||
type="password"
|
||||
value={form.confirmCredential}
|
||||
onChange={(e) => onChange('confirmCredential', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button className="w-full" onClick={onSubmit}>
|
||||
Create Account
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemberCredentialsForm;
|
||||
1
src/pages/access/member-credentials/hooks/index.ts
Normal file
1
src/pages/access/member-credentials/hooks/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './useMemberCredentials';
|
||||
@ -0,0 +1,42 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
type AccessType = 'Pin Credentials' | 'Secret Auth' | 'APIKey' | 'Web Credentials' | 'OTM Tpay';
|
||||
|
||||
interface MemberCredentials {
|
||||
accessType: AccessType | null;
|
||||
username: string;
|
||||
credential: string;
|
||||
confirmCredential: string;
|
||||
}
|
||||
|
||||
export const useMemberCredentials = () => {
|
||||
const [memberCredentials, setMemberCredentials] = useState<MemberCredentials>({
|
||||
accessType: null,
|
||||
username: '',
|
||||
credential: '',
|
||||
confirmCredential: ''
|
||||
});
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleChange = (key: keyof MemberCredentials, value: string) => {
|
||||
setMemberCredentials((prevCredentials) => ({ ...prevCredentials, [key]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
setError(null);
|
||||
|
||||
if (memberCredentials.username === '' || memberCredentials.credential === '') {
|
||||
setError('Please fill in all fields');
|
||||
return;
|
||||
}
|
||||
|
||||
if (memberCredentials.credential !== memberCredentials.confirmCredential) {
|
||||
setError('Credentials do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Data Submitted: ', memberCredentials);
|
||||
};
|
||||
|
||||
return { memberCredentials, handleChange, handleSubmit, error };
|
||||
};
|
||||
71
src/pages/account/manage-account/Columns.tsx
Normal file
71
src/pages/account/manage-account/Columns.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { MoreHorizontal } from 'lucide-react';
|
||||
|
||||
export type Account = {
|
||||
createdDate: Date;
|
||||
creditLimit: number | null;
|
||||
currency: string | null;
|
||||
description: string;
|
||||
id: number;
|
||||
name: string;
|
||||
systemAccount: boolean;
|
||||
};
|
||||
|
||||
export const columns: ColumnDef<Account>[] = [
|
||||
{
|
||||
accessorKey: 'id',
|
||||
header: 'ID'
|
||||
},
|
||||
{
|
||||
accessorKey: 'name',
|
||||
header: 'Name'
|
||||
},
|
||||
{
|
||||
accessorKey: 'description',
|
||||
header: 'Description'
|
||||
},
|
||||
{
|
||||
accessorKey: 'systemAccount',
|
||||
header: 'System Account'
|
||||
},
|
||||
{
|
||||
accessorKey: 'createdDate',
|
||||
header: 'Created Date',
|
||||
cell: ({ row }) => new Date(row.original.createdDate).toLocaleDateString()
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
const dataAccount = row.original;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(dataAccount.id.toString())}
|
||||
>
|
||||
Copy account ID
|
||||
</DropdownMenuItem>
|
||||
{/* <DropdownMenuSeparator /> */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
83
src/pages/account/manage-account/ManageAccount.tsx
Normal file
83
src/pages/account/manage-account/ManageAccount.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { Account, columns } from './Columns';
|
||||
import { DataTable } from '@/components/ui/DataTable';
|
||||
import axios from 'axios';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getData } from '@/utils';
|
||||
import { useCallApi } from '@/hooks';
|
||||
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const dataAccount: Account[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'eMoney Account',
|
||||
description: 'Rekening Member eMoney',
|
||||
systemAccount: false,
|
||||
createdDate: new Date('2019-12-05'),
|
||||
creditLimit: null,
|
||||
currency: null
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Merchant Account',
|
||||
description: 'Rekening Merchant',
|
||||
systemAccount: false,
|
||||
createdDate: new Date(),
|
||||
creditLimit: null,
|
||||
currency: null
|
||||
}
|
||||
];
|
||||
|
||||
const ManageAccount = () => {
|
||||
const [accounts, setAccounts] = useState<Account[]>([]);
|
||||
const { GetData } = useCallApi();
|
||||
// console.log(accounts);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAccount = async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/user/list`);
|
||||
const data: Account[] = await response.json();
|
||||
setAccounts(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching data', error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchAccount();
|
||||
}, []);
|
||||
|
||||
// const fetchAccount = async (page: number, limit: number, sorting: any, filter: any) => {
|
||||
// try {
|
||||
// const response = await GetData(`${API_URL}/user/list`, {
|
||||
// limit: limit,
|
||||
// page: page + 1,
|
||||
// with_deleted: true,
|
||||
// order_field: sorting[0].id,
|
||||
// order_direction: sorting[0].desc == false ? 'ASC' : 'DESC',
|
||||
// filter: JSON.stringify(filter)
|
||||
// });
|
||||
|
||||
// setAccounts(response?.data.list);
|
||||
|
||||
// return {
|
||||
// data: response?.data.list,
|
||||
// totalCount: response?.data.total_count
|
||||
// };
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
// };
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900 p-5">Manage Account</h1>
|
||||
<DataTable columns={columns} data={dataAccount} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageAccount;
|
||||
@ -0,0 +1,3 @@
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
18
src/pages/account/manage-currency/ManageCurrency.tsx
Normal file
18
src/pages/account/manage-currency/ManageCurrency.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Container, DataGridInner } from '@/components';
|
||||
import { ManageCurrencyContextProvider } from './hooks/ManageCurrencyContext';
|
||||
import AddDialog from './blocks/AddDialog';
|
||||
|
||||
const ManageCurrency = () => {
|
||||
return (
|
||||
<ManageCurrencyContextProvider>
|
||||
<Container>
|
||||
<div className="grid gap-5 lg:gap-7.5">
|
||||
<DataGridInner />
|
||||
</div>
|
||||
<AddDialog />
|
||||
</Container>
|
||||
</ManageCurrencyContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageCurrency;
|
||||
240
src/pages/account/manage-currency/blocks/AddDialog.tsx
Normal file
240
src/pages/account/manage-currency/blocks/AddDialog.tsx
Normal file
@ -0,0 +1,240 @@
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useManageCurrencyContext } from '../hooks/useManageAccessTypeContext';
|
||||
import { Alert, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import {
|
||||
Dialog,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const AddDialog = () => {
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const {
|
||||
showAddDialog,
|
||||
handleAddDialog,
|
||||
currencies,
|
||||
showEditDialog,
|
||||
handleEditDialog,
|
||||
selectedUser
|
||||
} = useManageCurrencyContext();
|
||||
|
||||
const { reload } = useDataGrid();
|
||||
const { PostData, PutData } = useCallApi();
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
name: '',
|
||||
code: '',
|
||||
prefix: '',
|
||||
trailer: '',
|
||||
format: '',
|
||||
grouping_separator: '',
|
||||
decimal_separator: ''
|
||||
};
|
||||
|
||||
const [formField, setFormField] = useState(initialState);
|
||||
const resetForm = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (
|
||||
formField.name === '' ||
|
||||
formField.code === '' ||
|
||||
formField.prefix === '' ||
|
||||
formField.format === '' ||
|
||||
formField.decimal_separator === '' ||
|
||||
formField.grouping_separator === ''
|
||||
) {
|
||||
setAlert({ show: true, message: 'Please fill in all required fields.' });
|
||||
return;
|
||||
}
|
||||
console.log(formField);
|
||||
setAlert({ show: false, message: '' });
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
|
||||
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
||||
<DialogTitle></DialogTitle>
|
||||
<DialogDescription></DialogDescription>
|
||||
<DialogHeader className="p-5 border-0">
|
||||
<div className="flex items-center justify-between flex-wrap grow">
|
||||
<div className="flex flex-col justify-center">
|
||||
<h1 className="text-xl font-semibold leading-none text-gray-900">Create Currency</h1>
|
||||
<div className="flex items-center gap-2 text-sm font-normal text-gray-700"></div>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-100 opacity-50"
|
||||
onClick={() => {
|
||||
handleAddDialog(false);
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
<KeenIcon icon="cross" className="text-1.5xl" />
|
||||
</div>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<DialogBody className="scrollable-y px-0 pb-0" ref={parentRef}>
|
||||
<div className="flex flex-col px-0">
|
||||
{alert.show && (
|
||||
<Alert variant="danger" className="mb-5">
|
||||
{alert.message}
|
||||
</Alert>
|
||||
)}
|
||||
<form action="" onSubmit={handleSubmit}>
|
||||
<div className="card-body grid gap-5 p-0">
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Name<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.name}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, name: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Code<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.code}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, code: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Prefix<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.prefix}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, prefix: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">Trailer</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.trailer}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, trailer: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Format<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.format}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, format: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Grouping Separator<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.grouping_separator}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, grouping_separator: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Decimal Separator<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.decimal_separator}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, decimal_separator: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-2.5 gap-5">
|
||||
<Button variant={'outline'} type="reset" onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant={'default'} type="submit">
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DialogBody>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDialog;
|
||||
49
src/pages/account/manage-currency/blocks/ListToolBar.tsx
Normal file
49
src/pages/account/manage-currency/blocks/ListToolBar.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useManageCurrencyContext } from '../hooks/useManageAccessTypeContext';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const ListToolbar = () => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const { handleAddDialog, handleEditDialog } = useManageCurrencyContext();
|
||||
|
||||
return (
|
||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex w-[50%] gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<KeenIcon icon="magnifier" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Currency"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<DefaultTooltip title={'Filter'} placement={'top'}>
|
||||
<Button variant={'outline'} className="h-7.5 disabled:bg-gray-400">
|
||||
<KeenIcon icon="filter" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleAddDialog(true)}
|
||||
>
|
||||
Add Data
|
||||
</Button>
|
||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
|
||||
<KeenIcon icon="arrows-circle" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListToolbar;
|
||||
@ -0,0 +1,259 @@
|
||||
import { DataGridColumnHeader, DataGridProvider } from '@/components';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import ListToolbar from '../blocks/ListToolBar';
|
||||
|
||||
interface SelectedUser {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
prefix: string;
|
||||
trailer: string;
|
||||
format: string;
|
||||
grouping_separator: string;
|
||||
decimal_separator: string;
|
||||
}
|
||||
|
||||
interface CurrencyProps {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
prefix: string;
|
||||
trailer: string;
|
||||
format: string;
|
||||
grouping_separator: string;
|
||||
decimal_separator: string;
|
||||
}
|
||||
|
||||
interface ContextProps {
|
||||
showEditDialog: boolean;
|
||||
handleEditDialog: (show: boolean, selected_user: string | null) => void;
|
||||
showAddDialog: boolean;
|
||||
handleAddDialog: (show: boolean) => void;
|
||||
selectedUser: string | null;
|
||||
currencies: CurrencyProps[];
|
||||
}
|
||||
|
||||
const initialProps: ContextProps = {
|
||||
showEditDialog: false,
|
||||
handleEditDialog: () => {},
|
||||
showAddDialog: false,
|
||||
handleAddDialog: () => {},
|
||||
selectedUser: null,
|
||||
currencies: []
|
||||
};
|
||||
|
||||
const ManageCurrencyContext = createContext<ContextProps>(initialProps);
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const ManageCurrencyContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [selectedUser, setSelectedUser] = useState<string | null>(null);
|
||||
const [currencies, setCurrencies] = useState<CurrencyProps[]>([]);
|
||||
const { GetData } = useCallApi();
|
||||
|
||||
useEffect(() => {
|
||||
const dummyData: CurrencyProps[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'US Dollar',
|
||||
code: 'USD',
|
||||
prefix: '$',
|
||||
trailer: '',
|
||||
format: '#,##0.00',
|
||||
grouping_separator: ',',
|
||||
decimal_separator: '.'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Euro',
|
||||
code: 'EUR',
|
||||
prefix: '€',
|
||||
trailer: '',
|
||||
format: '#.##0,00',
|
||||
grouping_separator: '.',
|
||||
decimal_separator: ','
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Japanese Yen',
|
||||
code: 'JPY',
|
||||
prefix: '¥',
|
||||
trailer: '',
|
||||
format: '#,##0',
|
||||
grouping_separator: ',',
|
||||
decimal_separator: '.'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Indonesia Rupiah',
|
||||
code: 'IDR',
|
||||
prefix: 'Rp',
|
||||
trailer: '',
|
||||
format: '#,##0',
|
||||
grouping_separator: '.',
|
||||
decimal_separator: ','
|
||||
}
|
||||
];
|
||||
setCurrencies(dummyData);
|
||||
}, []);
|
||||
|
||||
const handleEditDialog = useCallback((show: boolean, selected_user: string | null) => {
|
||||
setSelectedUser(show ? selected_user : null);
|
||||
setShowEditDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleAddDialog = useCallback((show: boolean) => {
|
||||
setShowAddDialog(show);
|
||||
}, []);
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorFn: (row) => row.id,
|
||||
id: 'id',
|
||||
header: ({ column }) => <DataGridColumnHeader title="ID" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
id: 'name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.code,
|
||||
id: 'code',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Code" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.prefix,
|
||||
id: 'prefix',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Prefix" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.trailer,
|
||||
id: 'trailer',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Trailer" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.format,
|
||||
id: 'format',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Format" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.grouping_separator,
|
||||
id: 'grouping_separator',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Grouping Separator" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.decimal_separator,
|
||||
id: 'decimal_separator',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Decimal Separator" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableSorting: false,
|
||||
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-primary mr-2"
|
||||
onClick={() => handleEditDialog(true, row.original.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
},
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]',
|
||||
cellClassName: 'text-center'
|
||||
}
|
||||
}
|
||||
],
|
||||
[handleAddDialog, handleEditDialog]
|
||||
);
|
||||
console.log(currencies);
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto py-5">
|
||||
<h1>Manage Currency</h1>
|
||||
</div>
|
||||
<ManageCurrencyContext.Provider
|
||||
value={{
|
||||
showEditDialog,
|
||||
handleEditDialog,
|
||||
showAddDialog,
|
||||
handleAddDialog,
|
||||
selectedUser,
|
||||
currencies
|
||||
}}
|
||||
>
|
||||
<Toaster expand visibleToasts={9} duration={3000} />
|
||||
|
||||
{currencies.length > 0 ? (
|
||||
<DataGridProvider
|
||||
columns={columns}
|
||||
data={currencies}
|
||||
pagination={{ size: 10 }}
|
||||
layout={{ card: true }}
|
||||
toolbar={<ListToolbar />}
|
||||
sorting={[{ id: 'id', desc: true }]}
|
||||
serverSide={true}
|
||||
>
|
||||
{children}
|
||||
</DataGridProvider>
|
||||
) : (
|
||||
<p>loading</p>
|
||||
)}
|
||||
</ManageCurrencyContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ManageCurrencyContext, ManageCurrencyContextProvider };
|
||||
export type { SelectedUser };
|
||||
@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { ManageCurrencyContext } from './ManageCurrencyContext';
|
||||
|
||||
const useManageCurrencyContext = () => {
|
||||
const context = useContext(ManageCurrencyContext);
|
||||
|
||||
if (!context) throw new Error('useManageCurrencyContext must be used within AuthProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { useManageCurrencyContext };
|
||||
@ -14,6 +14,8 @@ import { useFetchCardData, useFetchChartData } from './hooks';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useFetchYear } from './hooks/useFetchYear';
|
||||
import { get5LastYear } from '@/utils/Date';
|
||||
import { staticChartData } from './staticChart';
|
||||
|
||||
// sum -> nominal, count-> total
|
||||
type CountType = 'sum' | 'count';
|
||||
type ChartType = 'line' | 'bar';
|
||||
@ -184,9 +186,9 @@ const DashboardHomePage = () => {
|
||||
<div className="lg:col-span-1">
|
||||
<Chart
|
||||
title="Overview"
|
||||
count={count}
|
||||
count={12}
|
||||
toolbar={toolbar}
|
||||
chartData={chartData}
|
||||
chartData={staticChartData.series.map((data: any) => data.data)}
|
||||
chartType={chartType}
|
||||
chartLegend={chartLegend}
|
||||
/>
|
||||
|
||||
@ -37,7 +37,7 @@ const Chart = ({
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: '50%'
|
||||
columnWidth: '30%'
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
|
||||
56
src/pages/dashboards/home/staticChart.tsx
Normal file
56
src/pages/dashboards/home/staticChart.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
export const staticChartData = {
|
||||
series: [
|
||||
{
|
||||
categories: 'Jan',
|
||||
data: [
|
||||
{
|
||||
x: 'Jan 01',
|
||||
y: 50
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
data: [
|
||||
{
|
||||
x: 'Jan 02',
|
||||
y: 75
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
data: [
|
||||
{
|
||||
x: 'Jan 03',
|
||||
y: 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
data: [
|
||||
{
|
||||
x: 'Jan 04',
|
||||
y: 125
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
data: [
|
||||
{
|
||||
x: 'Jan 05',
|
||||
y: 150
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
data: [
|
||||
{
|
||||
x: 'Jan 06',
|
||||
y: 175
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xaxis: {
|
||||
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
}
|
||||
};
|
||||
37
src/pages/groups/Column.tsx
Normal file
37
src/pages/groups/Column.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
export type Group = {
|
||||
id: number;
|
||||
createdDate: Date;
|
||||
name: string;
|
||||
status: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export const columns: ColumnDef<Group>[] = [
|
||||
{
|
||||
accessorKey: 'id',
|
||||
header: 'ID'
|
||||
},
|
||||
{
|
||||
accessorKey: 'createdDate',
|
||||
header: 'Created Date',
|
||||
cell: ({ row }) => new Date(row.original.createdDate).toLocaleDateString()
|
||||
},
|
||||
{
|
||||
accessorKey: 'name',
|
||||
header: 'Name'
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: 'Status'
|
||||
},
|
||||
{
|
||||
accessorKey: 'description',
|
||||
header: 'Description'
|
||||
},
|
||||
// {
|
||||
// id: 'actions',
|
||||
// header: 'Actions'
|
||||
// }
|
||||
];
|
||||
249
src/pages/groups/ManageGroups.tsx
Normal file
249
src/pages/groups/ManageGroups.tsx
Normal file
@ -0,0 +1,249 @@
|
||||
import { DataTable } from '@/components/ui/DataTable';
|
||||
import { columns, Group } from './Column';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import { DialogContent, MenuItem, Radio, RadioGroup, FormControlLabel, FormControl,
|
||||
Dialog, DialogActions, DialogTitle, Typography, Button, Box } from '@mui/material';
|
||||
import { useState, useEffect } from 'react';
|
||||
// import IconButton from '@mui/material/IconButton';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import ConfirmDialog from '@/components/confirm';
|
||||
// import { DialogHeader } from '@/components/ui/dialog';
|
||||
// import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
const BASE_URL = apiConfig.service_customer;
|
||||
|
||||
let initGroup = {
|
||||
id: '',
|
||||
groupName: '',
|
||||
status: '',
|
||||
description: '',
|
||||
pin_length: '',
|
||||
max_pin_attempts: '',
|
||||
default_notification: ''
|
||||
}
|
||||
|
||||
const ManageGroups = () => {
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const [dataGroup, setDataGroup] = useState([]);
|
||||
const [formData, setFormData] = useState(initGroup);
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [dialogType, setDialogType] = useState('');
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGroups()
|
||||
}, []);
|
||||
|
||||
async function fetchGroups() {
|
||||
try {
|
||||
let groups = await axios.get(`${BASE_URL}/groups/list`, {
|
||||
params: {
|
||||
limit: 10,
|
||||
page: 1,
|
||||
with_deleted: false,
|
||||
order_field: 'name',
|
||||
order_direction: 'ASC'
|
||||
}
|
||||
});
|
||||
setDataGroup(groups.data.data.list)
|
||||
} catch (error: any) {
|
||||
alert(error.message)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const openDialog = () => setIsDialogOpen(true);
|
||||
const closeDialog = () => {
|
||||
setIsDialogOpen(false)
|
||||
setFormData(initGroup)
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value
|
||||
})
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
function createGroup() {
|
||||
setDialogType('create');
|
||||
openDialog();
|
||||
}
|
||||
|
||||
const handleUpdate = (group: any) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
id: group.id,
|
||||
groupName: group.name,
|
||||
status: group.status,
|
||||
description: group.description
|
||||
})
|
||||
setDialogType('update');
|
||||
setIsDialogOpen(true)
|
||||
};
|
||||
|
||||
const handleDelete = (group: any) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
id: group.id,
|
||||
groupName: group.name,
|
||||
status: group.status,
|
||||
description: group.description
|
||||
})
|
||||
setDialogType('delete');
|
||||
setDialogOpen(true)
|
||||
};
|
||||
|
||||
const handleYes = async () => {
|
||||
try {
|
||||
if (dialogType === 'create') {
|
||||
await axios.post(`${BASE_URL}/groups/create`, {
|
||||
"name": formData.groupName,
|
||||
"status": formData.status,
|
||||
"created_at": new Date()
|
||||
})
|
||||
} else if (dialogType === 'update') {
|
||||
await axios.put(`${BASE_URL}/groups/update/${formData.id}`, {
|
||||
"name": formData.groupName,
|
||||
"status": formData.status,
|
||||
"updated_at": new Date()
|
||||
})
|
||||
} else if (dialogType === 'delete') {
|
||||
await axios.delete(`${BASE_URL}/groups/delete/${formData.id}/true`)
|
||||
}
|
||||
await fetchGroups();
|
||||
closeDialog();
|
||||
setDialogOpen(false);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
closeDialog();
|
||||
setDialogOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNo = () => {
|
||||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto pt-2">
|
||||
<ConfirmDialog
|
||||
open={dialogOpen}
|
||||
onClose={() => setDialogOpen(false)}
|
||||
title="Confirm Action"
|
||||
content={`Are you sure you want to `+( dialogType === 'create' ? "create?" : ( dialogType === 'update' ? "update?" : "delete?"))}
|
||||
onYes={handleYes}
|
||||
onNo={handleNo}
|
||||
/>
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900 p-5">Groups</h1>
|
||||
<DataTable data={dataGroup} columns={columns} createGroup={createGroup} onUpdate={handleUpdate} onDelete={handleDelete} />
|
||||
<Dialog open={isDialogOpen} onClose={setIsDialogOpen}>
|
||||
<DialogContent className="w-[600px]">
|
||||
<div className="flex justify-between">
|
||||
<DialogTitle>Create New Group</DialogTitle>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Button variant="outlined" sx={{ borderColor: "white", color: 'grey' }} onClick={closeDialog}><CloseIcon/></Button>
|
||||
</Box>
|
||||
</div>
|
||||
<Divider/>
|
||||
<div className="p-5 mt-5">
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-4 w-full">
|
||||
<div className="grid grid-cols-4 items-center gap-4 w-full">
|
||||
<label className="form-label text-sm">
|
||||
<span className="text-red-500">*</span>Group Name:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="groupName"
|
||||
className="input w-full col-span-3"
|
||||
value={formData.groupName}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4 w-full">
|
||||
<label className="form-label text-sm">
|
||||
<span className="text-red-500">*</span>Active Status:
|
||||
</label>
|
||||
<FormControl>
|
||||
<RadioGroup name='status' row value={formData.status} onChange={handleChange}>
|
||||
<FormControlLabel value="Y" checked={formData.status === 'Y'} control={<Radio />} label="Yes" />
|
||||
<FormControlLabel value="N" checked={formData.status === 'N'} control={<Radio />} label="No" />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4 w-full">
|
||||
<label className="form-label text-sm">
|
||||
<span className="text-red-500">*</span>Description:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="description"
|
||||
className="input w-full col-span-3"
|
||||
value={formData.description}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="grid grid-cols-4 items-center gap-4 w-full">
|
||||
<label className="form-label text-sm">
|
||||
<span className="text-red-500">*</span>PIN Length:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="pin_length"
|
||||
className="input w-full col-span-3"
|
||||
value={formData.pin_length}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4 w-full">
|
||||
<label className="form-label text-sm">
|
||||
<span className="text-red-500">*</span>Max Pin Attempts:
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="max_pin_attempts"
|
||||
className="input w-full col-span-3"
|
||||
value={formData.max_pin_attempts}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div> */}
|
||||
{/* <div className="grid grid-cols-4 items-center gap-4 w-full mb-5">
|
||||
<label className="form-label text-sm">Default Notification:</label>
|
||||
<Select
|
||||
onValueChange={(e) => setFormData({ ...formData, default_notification: e })}
|
||||
>
|
||||
<SelectTrigger className="w-full col-span-3">
|
||||
<SelectValue placeholder="Select Default Notification" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="w-full">
|
||||
<SelectItem value="1 - notifSender">1 - notifSender</SelectItem>
|
||||
<SelectItem value="2 - notifBenefeciary">2 - notifBenefeciary</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<input
|
||||
type="text"
|
||||
name="default_notification"
|
||||
className="input w-full col-span-3"
|
||||
value={formData.default_notification}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div> */}
|
||||
<Button type="submit">Submit</Button>
|
||||
</form>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageGroups;
|
||||
11
src/pages/master/MasterData.tsx
Normal file
11
src/pages/master/MasterData.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const MasterData = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Master Data Page</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MasterData;
|
||||
11
src/pages/master/aldeias/AldeiasMaster.tsx
Normal file
11
src/pages/master/aldeias/AldeiasMaster.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const AldeiasMaster = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Aldeias Master Data</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AldeiasMaster;
|
||||
21
src/pages/master/municipios/Municipios.tsx
Normal file
21
src/pages/master/municipios/Municipios.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Container, DataGridInner } from '@/components';
|
||||
import { ManageMunicipiosProvider } from './hooks/ManageMunicipiosContext';
|
||||
import AddDialog from './blocks/AddDialog';
|
||||
import SearchDialog from './blocks/SearchDialog';
|
||||
|
||||
const Municipios = () => {
|
||||
return (
|
||||
<ManageMunicipiosProvider>
|
||||
<Container className="mb-7">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900 mb-5">MUNICIPIOS</h1>
|
||||
<div className="grid gap-5 lg:gap-7.5">
|
||||
<DataGridInner />
|
||||
</div>
|
||||
<AddDialog />
|
||||
<SearchDialog />
|
||||
</Container>
|
||||
</ManageMunicipiosProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default Municipios;
|
||||
111
src/pages/master/municipios/blocks/AddDialog.tsx
Normal file
111
src/pages/master/municipios/blocks/AddDialog.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { useManageMunicipiosContext } from '../hooks/useManageMunicipiosContext';
|
||||
import {
|
||||
Dialog,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import { Alert, KeenIcon } from '@/components';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const AddDialog = () => {
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const { showAddDialog, handleAddDialog } = useManageMunicipiosContext();
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
const initialState = {
|
||||
name: ''
|
||||
};
|
||||
|
||||
const [formField, setFormField] = useState(initialState);
|
||||
const resetForm = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (formField.name === '') {
|
||||
setAlert({ show: true, message: 'Please fill name field.' });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(formField);
|
||||
setAlert({ show: false, message: '' });
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
|
||||
<DialogContent className="container-fixed max-w-96 flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
||||
<DialogTitle></DialogTitle>
|
||||
<DialogDescription></DialogDescription>
|
||||
<DialogHeader className="p-2 border-0">
|
||||
<div className="flex items-center justify-between flex-wrap grow">
|
||||
<div className="flex flex-col justify-center">
|
||||
<h1 className="text-xl font-semibold leading-none text-gray-900">Add Municipios</h1>
|
||||
<div className="flex items-center gap-2 text-sm font-normal text-gray-700"></div>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-100 opacity-50"
|
||||
onClick={() => {
|
||||
handleAddDialog(false);
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
<KeenIcon icon="cross" className="text-1.5xl" />
|
||||
</div>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<DialogBody className="scrollable-y px-0 pb-0" ref={parentRef}>
|
||||
<div className="flex flex-col px-0">
|
||||
{alert.show && (
|
||||
<Alert variant="danger" className="mb-5">
|
||||
{alert.message}
|
||||
</Alert>
|
||||
)}
|
||||
<form action="" onSubmit={handleSubmit}>
|
||||
<div className="card-body grid-cols-6 gap-5 p-0">
|
||||
<div className="grid grid-cols-8 gap-2 w-full items-center">
|
||||
<label className="form-label flex items-center gap-1 col-span-2">
|
||||
Name<span className="text-red-500">*</span>
|
||||
</label>
|
||||
|
||||
<Input
|
||||
className="input col-span-6"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.name}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, name: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-2.5 gap-5 col-span-6">
|
||||
<Button variant={'outline'} type="reset" onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant={'default'} type="submit">
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DialogBody>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDialog;
|
||||
62
src/pages/master/municipios/blocks/ListToolbar.tsx
Normal file
62
src/pages/master/municipios/blocks/ListToolbar.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useManageMunicipiosContext } from '../hooks/useManageMunicipiosContext';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const ListToolbar = () => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const { handleAddDialog, handleSearchDialog } = useManageMunicipiosContext();
|
||||
|
||||
return (
|
||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex w-[50%] gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<KeenIcon icon="magnifier" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Municipios"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<DefaultTooltip title={'Filter'} placement={'top'}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 disabled:bg-gray-400"
|
||||
// disabled={isLoading}
|
||||
// onClick={handleFilterData}
|
||||
>
|
||||
{/* {loadingButton === 'filter' ? <ContentLoader /> : <KeenIcon icon="filter" />} */}
|
||||
<KeenIcon icon="filter" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleSearchDialog(true)}
|
||||
>
|
||||
Search Postu Administrativo
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleAddDialog(true)}
|
||||
>
|
||||
Add Data
|
||||
</Button>
|
||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
|
||||
<KeenIcon icon="arrows-circle" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListToolbar;
|
||||
177
src/pages/master/municipios/blocks/SearchDialog.tsx
Normal file
177
src/pages/master/municipios/blocks/SearchDialog.tsx
Normal file
@ -0,0 +1,177 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import { Alert, KeenIcon } from '@/components';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useManageMunicipiosContext } from '../hooks/useManageMunicipiosContext';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
|
||||
interface PostoAdms {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const API_URL = apiConfig.service_master_data;
|
||||
|
||||
const SearchDialog = () => {
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const { showSearchDialog, handleSearchDialog, municipios } = useManageMunicipiosContext();
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
const initialState = {
|
||||
id: 0,
|
||||
name: ''
|
||||
};
|
||||
|
||||
const [formField, setFormField] = useState(initialState);
|
||||
const resetForm = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
const [postoadms, setPostoadms] = useState<PostoAdms[]>([]);
|
||||
const [isFound, setIsFound] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const id = Number(formField.id);
|
||||
|
||||
if (formField.id === 0) {
|
||||
setAlert({ show: true, message: 'Please fill name field.' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get(`${API_URL}/municipios/postoadms/${id}`);
|
||||
|
||||
if (response.data.status) {
|
||||
setPostoadms(response.data.data);
|
||||
setIsFound(true);
|
||||
console.log('Found postoadms: ', response.data.data);
|
||||
} else {
|
||||
setPostoadms([]);
|
||||
setIsFound(false);
|
||||
setAlert({ show: true, message: 'No postoadms found.' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching postoadms', error);
|
||||
setAlert({ show: true, message: 'Failed to fetch postoadms. Please try again.' });
|
||||
}
|
||||
setAlert({ show: false, message: '' });
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setFormField(initialState);
|
||||
setIsFound(false);
|
||||
setPostoadms([]);
|
||||
};
|
||||
// console.log(municipios);
|
||||
return (
|
||||
<Dialog open={showSearchDialog} onOpenChange={(open) => handleSearchDialog(open)}>
|
||||
<DialogContent className="container-fixed max-w-96 flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
||||
<DialogTitle></DialogTitle>
|
||||
<DialogDescription></DialogDescription>
|
||||
<DialogHeader className="p-2 border-0">
|
||||
<div className="flex items-center justify-between flex-wrap grow">
|
||||
<div className="flex flex-col justify-center">
|
||||
<h1 className="text-xl font-semibold leading-none text-gray-900">
|
||||
Search Postu Administravo
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 text-sm font-normal text-gray-700"></div>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-100 opacity-50"
|
||||
onClick={() => {
|
||||
handleSearchDialog(false);
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
<KeenIcon icon="cross" className="text-1.5xl" />
|
||||
</div>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<DialogBody className="scrollable-y px-0 pb-0" ref={parentRef}>
|
||||
<div className="flex flex-col px-0">
|
||||
{alert.show && (
|
||||
<Alert variant="danger" className="mb-5">
|
||||
{alert.message}
|
||||
</Alert>
|
||||
)}
|
||||
<form action="" onSubmit={handleSubmit}>
|
||||
<div className="card-body grid-cols-6 gap-5 p-0">
|
||||
<div className="grid grid-cols-8 gap-2 w-full items-center">
|
||||
<label className="form-label flex items-center gap-1 col-span-2">
|
||||
Name<span className="text-red-500">*</span>
|
||||
</label>
|
||||
|
||||
<Select
|
||||
value={formField.id.toString()}
|
||||
onValueChange={(target) => {
|
||||
const selectedMunicipio = municipios.find((m) => m.id.toString() === target);
|
||||
if (selectedMunicipio) {
|
||||
setFormField({
|
||||
...formField,
|
||||
id: selectedMunicipio.id,
|
||||
name: selectedMunicipio.name
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="col-span-6">
|
||||
<SelectValue placeholder="Select Municipios" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{municipios.map((municipio) => (
|
||||
<SelectItem key={municipio.id} value={municipio.id.toString()}>
|
||||
{municipio.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{isFound && postoadms.length > 0 && (
|
||||
<div className="mt-4 border-t pt-4">
|
||||
<h2 className="text-md font-semibold">Postu Administravo: </h2>
|
||||
<br />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm form-hint">
|
||||
{postoadms.map((posto) => posto.name).join(', ')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end pt-2.5 gap-5 col-span-6">
|
||||
<Button variant={'outline'} type="reset" onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant={'default'} type="submit">
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DialogBody>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchDialog;
|
||||
250
src/pages/master/municipios/hooks/ManageMunicipiosContext.tsx
Normal file
250
src/pages/master/municipios/hooks/ManageMunicipiosContext.tsx
Normal file
@ -0,0 +1,250 @@
|
||||
import { DataGridColumnHeader, DataGridProvider } from '@/components';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import axios from 'axios';
|
||||
import React, { createContext, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import ListToolbar from '../blocks/ListToolbar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
interface MunicipiosProps {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ContextProps {
|
||||
municipios: MunicipiosProps[];
|
||||
showSearchDialog: boolean;
|
||||
handleSearchDialog: (show: boolean) => void;
|
||||
showEditDialog: boolean;
|
||||
handleEditDialog: (show: boolean, selected_user: string | null) => void;
|
||||
showAddDialog: boolean;
|
||||
handleAddDialog: (show: boolean) => void;
|
||||
showDeleteDialog: boolean;
|
||||
handleDeleteDialog: (show: boolean, selected_user: string | null) => void;
|
||||
selectedMunicipios: string | null;
|
||||
getMunicipiosLists: (
|
||||
limit: number,
|
||||
page: number,
|
||||
with_deleted: boolean,
|
||||
order_field: any,
|
||||
order_direction: any
|
||||
) => Promise<{ data: MunicipiosProps[]; totalCount: number } | undefined>;
|
||||
}
|
||||
|
||||
const initialProps: ContextProps = {
|
||||
municipios: [],
|
||||
showSearchDialog: false,
|
||||
handleSearchDialog: (show: boolean) => {},
|
||||
showEditDialog: false,
|
||||
handleEditDialog: (show: boolean, selected_user: string | null) => {},
|
||||
showAddDialog: false,
|
||||
handleAddDialog: (show: boolean) => {},
|
||||
showDeleteDialog: false,
|
||||
handleDeleteDialog: (show: boolean, selected_user: string | null) => {},
|
||||
selectedMunicipios: null,
|
||||
getMunicipiosLists: async () => ({ data: [], totalCount: 0 })
|
||||
};
|
||||
|
||||
// interface MunicipiosContext {
|
||||
// municipios: MunicipiosProps[];
|
||||
// getMunicipiosLists: (
|
||||
// limit: number,
|
||||
// page: number,
|
||||
// with_deleted: boolean,
|
||||
// order_field: any,
|
||||
// order_direction: any
|
||||
// ) => Promise<void>;
|
||||
// getMunicipiosByName: (name: string) => Promise<void>;
|
||||
// createMunicipios: (data: Partial<MunicipiosProps>) => Promise<void>;
|
||||
// updateMunicipios: (id: number, data: Partial<MunicipiosProps>) => Promise<void>;
|
||||
// deleteMunicipios: (id: number, hardDelete?: boolean) => Promise<void>;
|
||||
// restoreMunicipios: (id: number) => Promise<void>;
|
||||
// }
|
||||
|
||||
const ManageMunicipiosContext = createContext<ContextProps>(initialProps);
|
||||
const API_URL = apiConfig.service_master_data;
|
||||
|
||||
const ManageMunicipiosProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showSearchDialog, setShowSearchDialog] = useState(false);
|
||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
const [selectedMunicipios, setSelectedMunicipios] = useState<string | null>(null);
|
||||
const [municipios, setMunicipios] = useState<MunicipiosProps[]>([]);
|
||||
const { GetData } = useCallApi();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSearchDialog = useCallback((show: boolean) => {
|
||||
setShowSearchDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleAddDialog = useCallback((show: boolean) => {
|
||||
setShowAddDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleEditDialog = useCallback((show: boolean, selected_municipios: string | null) => {
|
||||
setSelectedMunicipios(show ? selected_municipios : null);
|
||||
setShowEditDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleDeleteDialog = useCallback((show: boolean, selected_municipios: string | null) => {
|
||||
setSelectedMunicipios(show ? selected_municipios : null);
|
||||
setShowDeleteDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleNavigate = (path: string) => {
|
||||
const url = navigate(`${API_URL}/municipios/postoadms/${path}`);
|
||||
console.log(url);
|
||||
};
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorFn: (row) => row.id,
|
||||
id: 'id',
|
||||
header: ({ column }) => <DataGridColumnHeader title="ID" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
id: 'name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Municipios Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[1000px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />,
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px], text-center',
|
||||
cellClassName: 'text-center'
|
||||
},
|
||||
cell: (info) => (
|
||||
<Button
|
||||
variant={'outline'}
|
||||
onClick={() => navigate(`/master-data/municipios/postoadms/${info.row.original.id}`)}
|
||||
>
|
||||
Details
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
],
|
||||
[handleEditDialog, handleDeleteDialog]
|
||||
);
|
||||
|
||||
const getMunicipiosLists = async (page: number, limit: number, sorting: any, filter: any) => {
|
||||
try {
|
||||
sorting = sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting;
|
||||
filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() };
|
||||
const response = await axios.get(`${API_URL}/municipios/list`, {
|
||||
params: {
|
||||
limit: limit,
|
||||
page: page + 1,
|
||||
with_deleted: false,
|
||||
order_field: sorting[0].id,
|
||||
order_direction: sorting[0].desc == false ? 'ASC' : 'DESC'
|
||||
}
|
||||
});
|
||||
console.log(response.data);
|
||||
setMunicipios(response.data.data.list);
|
||||
return { data: response?.data.data.list, totalCount: response?.data.data.total_count };
|
||||
} catch (error) {
|
||||
console.error('Error fetching municipios', error);
|
||||
}
|
||||
};
|
||||
|
||||
const getPostoadmsByMunicipio = async (name: string) => {
|
||||
try {
|
||||
const response = await axios.get(`${API_URL}/municipios/postoadms/${name}`);
|
||||
const data = response.data;
|
||||
console.log(data);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching municipios by ${name}`, error);
|
||||
}
|
||||
};
|
||||
|
||||
const createMunicipios = async (data: Partial<MunicipiosProps>) => {
|
||||
try {
|
||||
await axios.post(`${API_URL}/municipios/create`, data);
|
||||
// getMunicipiosLists(10, 1, false, 'name', 'ASC');
|
||||
} catch (error) {
|
||||
console.error('Error creating municipios', error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateMunicipios = async (id: number, data: Partial<MunicipiosProps>) => {
|
||||
try {
|
||||
await axios.put(`${API_URL}/update/${id}`, data);
|
||||
// getMunicipiosLists(10, 1, false, 'name', 'ASC');
|
||||
} catch (error) {
|
||||
console.error('Error updating municipios', error);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteMunicipios = async (id: number, hardDelete?: boolean) => {
|
||||
try {
|
||||
await axios.delete(`${API_URL}/delete/${id}/${hardDelete}`);
|
||||
// getMunicipiosLists(10, 1, false, 'name', 'ASC');
|
||||
} catch (error) {
|
||||
console.error('Error deleting municipios', error);
|
||||
}
|
||||
};
|
||||
|
||||
const restoreMunicipios = async (id: number) => {
|
||||
try {
|
||||
await axios.put(`${API_URL}/restore/${id}`);
|
||||
// getMunicipiosLists(10, 1, false, 'name', 'ASC');
|
||||
} catch (error) {
|
||||
console.error('Error restoring municipios', error);
|
||||
}
|
||||
};
|
||||
console.log(municipios);
|
||||
return (
|
||||
<ManageMunicipiosContext.Provider
|
||||
value={{
|
||||
municipios,
|
||||
showSearchDialog,
|
||||
handleSearchDialog,
|
||||
showAddDialog,
|
||||
handleAddDialog,
|
||||
showDeleteDialog,
|
||||
handleDeleteDialog,
|
||||
showEditDialog,
|
||||
handleEditDialog,
|
||||
selectedMunicipios,
|
||||
getMunicipiosLists
|
||||
}}
|
||||
>
|
||||
<Toaster expand visibleToasts={9} duration={3000} />
|
||||
|
||||
<DataGridProvider
|
||||
columns={columns}
|
||||
pagination={{ size: 25 }}
|
||||
toolbar={<ListToolbar />}
|
||||
layout={{ card: true }}
|
||||
sorting={[{ id: 'id', desc: false }]}
|
||||
serverSide={true}
|
||||
onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) =>
|
||||
getMunicipiosLists(pageIndex, pageSize, sorting, columnFilters)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</DataGridProvider>
|
||||
</ManageMunicipiosContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { ManageMunicipiosProvider, ManageMunicipiosContext };
|
||||
export type { MunicipiosProps };
|
||||
@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { ManageMunicipiosContext } from './ManageMunicipiosContext';
|
||||
|
||||
const useManageMunicipiosContext = () => {
|
||||
const context = useContext(ManageMunicipiosContext);
|
||||
|
||||
if (!context) throw new Error('useManageMunicipiosContext must be used within AuthProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { useManageMunicipiosContext };
|
||||
17
src/pages/master/postoadms/PostoAdmsMaster.tsx
Normal file
17
src/pages/master/postoadms/PostoAdmsMaster.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { ManagePostoAdmsContextProvider } from './hooks/ManagePostoAdmsContext';
|
||||
import { Container, DataGridInner } from '@/components';
|
||||
|
||||
const PostoAdmsMaster = () => {
|
||||
return (
|
||||
<ManagePostoAdmsContextProvider>
|
||||
<Container>
|
||||
<h1>Postu Administrativo</h1>
|
||||
<div className="grid gap-5 lg:gap-7.5">
|
||||
<DataGridInner />
|
||||
</div>
|
||||
</Container>
|
||||
</ManagePostoAdmsContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostoAdmsMaster;
|
||||
63
src/pages/master/postoadms/blocks/ListToolbar.tsx
Normal file
63
src/pages/master/postoadms/blocks/ListToolbar.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useManagePostoAdmsContext } from '../hooks/useManagePostoAdmsContext';
|
||||
|
||||
const ListToolbar = () => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const { handleAddDialog, handleSearchDialog } = useManagePostoAdmsContext();
|
||||
|
||||
return (
|
||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex w-[50%] gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<KeenIcon icon="magnifier" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Postu Administrativo"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<DefaultTooltip title={'Filter'} placement={'top'}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 disabled:bg-gray-400"
|
||||
// disabled={isLoading}
|
||||
// onClick={handleFilterData}
|
||||
>
|
||||
{/* {loadingButton === 'filter' ? <ContentLoader /> : <KeenIcon icon="filter" />} */}
|
||||
<KeenIcon icon="filter" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleSearchDialog(true)}
|
||||
>
|
||||
Search Postu Administrativo
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleAddDialog(true)}
|
||||
>
|
||||
Add Data
|
||||
</Button>
|
||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
|
||||
<KeenIcon icon="arrows-circle" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListToolbar;
|
||||
178
src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx
Normal file
178
src/pages/master/postoadms/hooks/ManagePostoAdmsContext.tsx
Normal file
@ -0,0 +1,178 @@
|
||||
import { DataGridColumnHeader, DataGridProvider } from '@/components';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import axios from 'axios';
|
||||
import { createContext, useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router';
|
||||
import ListToolbar from '../blocks/ListToolbar';
|
||||
|
||||
interface PostoAdmsProps {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ContextProps {
|
||||
showSearchDialog: boolean;
|
||||
handleSearchDialog: (show: boolean) => void;
|
||||
showEditDialog: boolean;
|
||||
handleEditDialog: (show: boolean, selected_postoAdms: string | null) => void;
|
||||
showAddDialog: boolean;
|
||||
handleAddDialog: (show: boolean) => void;
|
||||
showDeleteDialog: boolean;
|
||||
handleDeleteDialog: (show: boolean, selected_postoAdms: string | null) => void;
|
||||
selectedPostoAdms: string | null;
|
||||
getPostoAdmsLists: (
|
||||
limit: number,
|
||||
page: number,
|
||||
with_deleted: boolean,
|
||||
order_field: any,
|
||||
order_direction: any
|
||||
) => Promise<{ data: PostoAdmsProps[]; totalCount: number } | undefined>;
|
||||
}
|
||||
|
||||
const initialProps: ContextProps = {
|
||||
showSearchDialog: false,
|
||||
handleSearchDialog: (show: boolean) => {},
|
||||
showEditDialog: false,
|
||||
handleEditDialog: (show: boolean, selected_postoAdms: string | null) => {},
|
||||
showAddDialog: false,
|
||||
handleAddDialog: (show: boolean) => {},
|
||||
showDeleteDialog: false,
|
||||
handleDeleteDialog: (show: boolean, selected_postoAdms: string | null) => {},
|
||||
selectedPostoAdms: null,
|
||||
getPostoAdmsLists: async () => ({ data: [], totalCount: 0 })
|
||||
};
|
||||
|
||||
const ManagePostoAdmsContext = createContext<ContextProps>(initialProps);
|
||||
const API_URL = apiConfig.service_master_data;
|
||||
|
||||
const ManagePostoAdmsContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showSearchDialog, setShowSearchDialog] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
const [selectedPostoAdms, setSelectedPostoAdms] = useState<string | null>(null);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { municipioId } = useParams();
|
||||
|
||||
const handleSearchDialog = useCallback((show: boolean) => {
|
||||
setShowSearchDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleAddDialog = useCallback((show: boolean) => {
|
||||
setShowAddDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleEditDialog = useCallback((show: boolean, selected_postoAdms: string | null) => {
|
||||
setSelectedPostoAdms(show ? selected_postoAdms : null);
|
||||
setShowEditDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleDeleteDialog = useCallback((show: boolean, selected_postoAdms: string | null) => {
|
||||
setSelectedPostoAdms(show ? selected_postoAdms : null);
|
||||
setShowDeleteDialog(show);
|
||||
}, []);
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorFn: (row) => row.id,
|
||||
id: 'id',
|
||||
header: ({ column }) => <DataGridColumnHeader title="ID" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
id: 'name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Municipios Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[1000px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />,
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px], text-center',
|
||||
cellClassName: 'text-center'
|
||||
},
|
||||
cell: (info) => (
|
||||
<Button
|
||||
variant={'outline'}
|
||||
onClick={() => navigate(`/master-data/municipios/postoadms/${info.row.original.id}`)}
|
||||
>
|
||||
Details
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
],
|
||||
[handleEditDialog, handleDeleteDialog]
|
||||
);
|
||||
|
||||
const getPostoAdmsLists = async (page: number, limit: number, sorting: any, filter: any) => {
|
||||
sorting: sorting.length == 0 ? [{ id: 'name', desc: false }] : sorting;
|
||||
filter = filter.length == 0 ? {} : { any: filter[0].value?.toLowerCase() };
|
||||
try {
|
||||
const response = await axios.get(`${API_URL}/municipios/postoadms/${municipioId}`, {
|
||||
params: {
|
||||
limit,
|
||||
page: page + 1,
|
||||
with_deleted: false,
|
||||
order_field: sorting[0].id,
|
||||
order_direction: sorting[0].desc ? 'DESC' : 'ASC'
|
||||
}
|
||||
});
|
||||
console.log(response.data);
|
||||
return { data: response.data.data, totalCount: response.data.data.total_count };
|
||||
} catch (error) {
|
||||
console.error('Error fetching Postu Administrativo', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ManagePostoAdmsContext.Provider
|
||||
value={{
|
||||
showSearchDialog,
|
||||
handleSearchDialog,
|
||||
showAddDialog,
|
||||
handleAddDialog,
|
||||
showEditDialog,
|
||||
handleEditDialog,
|
||||
showDeleteDialog,
|
||||
handleDeleteDialog,
|
||||
selectedPostoAdms,
|
||||
getPostoAdmsLists
|
||||
}}
|
||||
>
|
||||
<Toaster expand visibleToasts={9} duration={3000} />
|
||||
|
||||
<DataGridProvider
|
||||
columns={columns}
|
||||
pagination={{ size: 25 }}
|
||||
toolbar={<ListToolbar />}
|
||||
layout={{ card: true }}
|
||||
sorting={[{ id: 'id', desc: false }]}
|
||||
serverSide={true}
|
||||
onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) =>
|
||||
getPostoAdmsLists(pageIndex, pageSize, sorting, columnFilters)
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</DataGridProvider>
|
||||
</ManagePostoAdmsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { ManagePostoAdmsContextProvider, ManagePostoAdmsContext };
|
||||
export type { PostoAdmsProps };
|
||||
@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { ManagePostoAdmsContext } from './ManagePostoAdmsContext';
|
||||
|
||||
const useManagePostoAdmsContext = () => {
|
||||
const context = useContext(ManagePostoAdmsContext);
|
||||
|
||||
if (!context) throw new Error('useManagePostoAdmsContext must be used within AuthProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { useManagePostoAdmsContext };
|
||||
11
src/pages/master/sucos/SucosMaster.tsx
Normal file
11
src/pages/master/sucos/SucosMaster.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const SucosMaster = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Sucos Master Data</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SucosMaster;
|
||||
42
src/pages/members/kyc/Kyc.tsx
Normal file
42
src/pages/members/kyc/Kyc.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { DataGridInner, TDataGridProps } from '@/components';
|
||||
import { Table } from '@tanstack/react-table';
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import { ManageKycContextProvider } from './hooks';
|
||||
|
||||
export interface IDataGridContextProps<TData extends object> {
|
||||
props: TDataGridProps<TData>;
|
||||
table: Table<TData>;
|
||||
totalRows: number;
|
||||
loading: (state: boolean) => void;
|
||||
reload: () => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const DataGridContext = createContext<IDataGridContextProps<any> | undefined>(undefined);
|
||||
|
||||
export const useDataGrid = () => {
|
||||
const context = useContext(DataGridContext);
|
||||
if (!context) {
|
||||
throw new Error('useDataGrid must be used within a DataGridProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
const Kyc = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto py-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900 mb-7">Manage Member KYC</h1>
|
||||
<ManageKycContextProvider>
|
||||
<div className="grid gap-5 lg:gap-7.5">
|
||||
<DataGridInner />
|
||||
</div>
|
||||
</ManageKycContextProvider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Kyc;
|
||||
14
src/pages/members/kyc/blocks/AddDialog.tsx
Normal file
14
src/pages/members/kyc/blocks/AddDialog.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Dialog } from '@/components/ui/dialog';
|
||||
import { useRef } from 'react';
|
||||
import { useKycContext } from '../hooks';
|
||||
import { useDataGrid } from '@/components';
|
||||
|
||||
const AddDialog = () => {
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const { showAddDialog, handleAddDialog } = useKycContext();
|
||||
const { reload } = useDataGrid();
|
||||
|
||||
return <Dialog></Dialog>;
|
||||
};
|
||||
|
||||
export default AddDialog;
|
||||
48
src/pages/members/kyc/blocks/ListToolBar.tsx
Normal file
48
src/pages/members/kyc/blocks/ListToolBar.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useKycContext } from '../hooks';
|
||||
|
||||
const ListToolBar = () => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const { handleDetailDialog, handleAddDialog } = useKycContext();
|
||||
|
||||
return (
|
||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex w-[50%] gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<KeenIcon icon="magnifier" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search users"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<DefaultTooltip title={'Filter'} placement={'top'}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 disabled:bg-gray-400"
|
||||
// disabled={isLoading}
|
||||
// onClick={handleFilterData}
|
||||
>
|
||||
{/* {loadingButton === 'filter' ? <ContentLoader /> : <KeenIcon icon="filter" />} */}
|
||||
<KeenIcon icon="filter" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
|
||||
<KeenIcon icon="arrows-circle" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ListToolBar };
|
||||
145
src/pages/members/kyc/hooks/ManageKycContext.tsx
Normal file
145
src/pages/members/kyc/hooks/ManageKycContext.tsx
Normal file
@ -0,0 +1,145 @@
|
||||
import { DataGridColumnHeader, DataGridProvider, KeenIcon } from '@/components';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { createContext, useCallback, useMemo, useState } from 'react';
|
||||
import { ListToolBar } from '../blocks/ListToolBar';
|
||||
|
||||
interface SelectedUser {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
role: string;
|
||||
description: string;
|
||||
created_date: Date;
|
||||
}
|
||||
|
||||
interface ContextProps {
|
||||
showDetailDialog: boolean;
|
||||
handleDetailDialog: (show: boolean, selected_user: SelectedUser | null) => void;
|
||||
showAddDialog: boolean;
|
||||
handleAddDialog: (show: boolean) => void;
|
||||
selectedUser: SelectedUser | null;
|
||||
}
|
||||
|
||||
const initialProps: ContextProps = {
|
||||
showDetailDialog: false,
|
||||
handleDetailDialog: () => {},
|
||||
showAddDialog: false,
|
||||
handleAddDialog: () => {},
|
||||
selectedUser: null
|
||||
};
|
||||
|
||||
const ManageKycContext = createContext<ContextProps>(initialProps);
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const ManageKycContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showDetailDialog, setShowDetailDialog] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [selectedUser, setSelectedUser] = useState<SelectedUser | null>(null);
|
||||
|
||||
const handleDetailDialog = useCallback((show: boolean, selected_user: SelectedUser | null) => {
|
||||
setSelectedUser(show ? selected_user : null);
|
||||
setShowDetailDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleAddDialog = useCallback((show: boolean) => {
|
||||
setShowAddDialog(show);
|
||||
}, []);
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorFn: (row) => row.id,
|
||||
id: 'id',
|
||||
header: ({ column }) => <DataGridColumnHeader title="ID" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.created_date,
|
||||
id: 'created_date',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Created Date" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
id: 'name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[350px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.description,
|
||||
id: 'description',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Description" column={column} />,
|
||||
enableSorting: true,
|
||||
meta: {
|
||||
headerClassName: 'w-[350px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableSorting: false,
|
||||
header: ({ column }) => <DataGridColumnHeader title="Action" column={column} />,
|
||||
cell: (data: any) => {
|
||||
const row = data.row.original;
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-icon btn-clear btn-light"
|
||||
onClick={() => handleDetailDialog(true, row.id)}
|
||||
>
|
||||
<KeenIcon icon="notepad-edit" />
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
},
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]',
|
||||
cellClassName: 'text-center'
|
||||
}
|
||||
}
|
||||
],
|
||||
[handleDetailDialog]
|
||||
);
|
||||
|
||||
return (
|
||||
<ManageKycContext.Provider
|
||||
value={{
|
||||
showDetailDialog,
|
||||
handleDetailDialog,
|
||||
showAddDialog,
|
||||
handleAddDialog,
|
||||
selectedUser
|
||||
}}
|
||||
>
|
||||
<Toaster expand visibleToasts={9} duration={3000} />
|
||||
|
||||
<DataGridProvider
|
||||
columns={columns}
|
||||
pagination={{ size: 10 }}
|
||||
toolbar={<ListToolBar />}
|
||||
layout={{ card: true }}
|
||||
sorting={[{ id: 'name', desc: false }]}
|
||||
serverSide={true}
|
||||
>
|
||||
{children}
|
||||
</DataGridProvider>
|
||||
</ManageKycContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { ManageKycContextProvider, ManageKycContext };
|
||||
export type { SelectedUser };
|
||||
2
src/pages/members/kyc/hooks/index.ts
Normal file
2
src/pages/members/kyc/hooks/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './ManageKycContext';
|
||||
export * from './useKycContext';
|
||||
12
src/pages/members/kyc/hooks/useKycContext.tsx
Normal file
12
src/pages/members/kyc/hooks/useKycContext.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { ManageKycContext } from './ManageKycContext';
|
||||
|
||||
const useKycContext = () => {
|
||||
const context = useContext(ManageKycContext);
|
||||
|
||||
if (!context) throw new Error('useKycContext must be used within AuthProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { useKycContext };
|
||||
119
src/pages/members/manage-members/Columns.tsx
Normal file
119
src/pages/members/manage-members/Columns.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { ArrowUpDown, MoreHorizontal } from 'lucide-react';
|
||||
|
||||
export type Members = {
|
||||
id: number;
|
||||
username: number;
|
||||
name: string;
|
||||
group: string;
|
||||
email: string;
|
||||
createdDate: Date;
|
||||
};
|
||||
|
||||
export const columns: ColumnDef<Members>[] = [
|
||||
{
|
||||
accessorKey: 'id',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
ID
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: 'username',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
Username
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: 'name',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
Name
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
Email
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: 'createdDate',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
Created Date
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => new Date(row.original.createdDate).toLocaleDateString()
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
const dataMembers = row.original;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(dataMembers.id.toString())}
|
||||
>
|
||||
Copy account ID
|
||||
</DropdownMenuItem>
|
||||
{/* <DropdownMenuSeparator /> */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
42
src/pages/members/manage-members/ManageMembers.tsx
Normal file
42
src/pages/members/manage-members/ManageMembers.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { DataTable } from '@/components/ui/DataTable';
|
||||
import { columns, Members } from './Columns';
|
||||
|
||||
const dataMembers: Members[] = [
|
||||
{
|
||||
id: 37026,
|
||||
username: 67076807158,
|
||||
name: 'Jumentino Carlos Luis da Costa',
|
||||
group: 'REGULER',
|
||||
email: 'Jumentinocldacoata@gmail.com',
|
||||
createdDate: new Date('2025-02-21')
|
||||
},
|
||||
{
|
||||
id: 37027,
|
||||
username: 67071827345,
|
||||
name: 'Diego Costa',
|
||||
group: 'REGULER',
|
||||
email: 'DiegoCosta@gmail.com',
|
||||
createdDate: new Date('2025-02-21')
|
||||
},
|
||||
{
|
||||
id: 37028,
|
||||
username: 56123764212,
|
||||
name: 'Luis Da Vista',
|
||||
group: 'SUPERVISOR',
|
||||
email: 'LuisdaVista@gmail.com',
|
||||
createdDate: new Date('2024-01-21')
|
||||
}
|
||||
];
|
||||
|
||||
const ManageMembers = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Manage Members</h1>
|
||||
<DataTable data={dataMembers} columns={columns} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageMembers;
|
||||
11
src/pages/menu/manage-menu/ManageMenu.tsx
Normal file
11
src/pages/menu/manage-menu/ManageMenu.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const ManageMenu = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Manage Menu</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageMenu;
|
||||
11
src/pages/menu/menu-category/MenuCategory.tsx
Normal file
11
src/pages/menu/menu-category/MenuCategory.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const MenuCategory = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Menu Category</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuCategory;
|
||||
11
src/pages/menu/welcome/Welcome.tsx
Normal file
11
src/pages/menu/welcome/Welcome.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const Welcome = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Welcome</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Welcome;
|
||||
11
src/pages/message/Inbox.tsx
Normal file
11
src/pages/message/Inbox.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const Inbox = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Inbox</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Inbox;
|
||||
18
src/pages/notification/ManageNotification.tsx
Normal file
18
src/pages/notification/ManageNotification.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Container, DataGridInner } from '@/components';
|
||||
import { ManageNotifContextProvider } from './hooks/ManageNotificationContext';
|
||||
import AddDialog from './blocks/AddDialog';
|
||||
|
||||
const ManageNotification = () => {
|
||||
return (
|
||||
<ManageNotifContextProvider>
|
||||
<Container>
|
||||
<div className="grid gap-5 lg:gap-7.5">
|
||||
<DataGridInner />
|
||||
</div>
|
||||
<AddDialog />
|
||||
</Container>
|
||||
</ManageNotifContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageNotification;
|
||||
147
src/pages/notification/blocks/AddDialog.tsx
Normal file
147
src/pages/notification/blocks/AddDialog.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { useRef, useState } from 'react';
|
||||
import { Alert, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import {
|
||||
Dialog,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useManageNotificationContext } from '../hooks/useManageNotificationContext';
|
||||
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const AddDialog = () => {
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const {
|
||||
handleAddDialog,
|
||||
handleEditDialog,
|
||||
showAddDialog,
|
||||
showEditDialog,
|
||||
selectedNotification,
|
||||
notifications
|
||||
} = useManageNotificationContext();
|
||||
|
||||
const { reload } = useDataGrid();
|
||||
const { PostData, PutData } = useCallApi();
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
name: '',
|
||||
destination_module: ''
|
||||
};
|
||||
|
||||
const [formField, setFormField] = useState(initialState);
|
||||
const resetForm = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (formField.name === '' || formField.destination_module === '') {
|
||||
setAlert({ show: true, message: 'Please fill in all required fields.' });
|
||||
return;
|
||||
}
|
||||
console.log(formField);
|
||||
setAlert({ show: false, message: '' });
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
|
||||
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
||||
<DialogTitle></DialogTitle>
|
||||
<DialogDescription></DialogDescription>
|
||||
<DialogHeader className="p-5 border-0">
|
||||
<div className="flex items-center justify-between flex-wrap grow">
|
||||
<div className="flex flex-col justify-center">
|
||||
<h1 className="text-xl font-semibold leading-none text-gray-900">
|
||||
Create Notification
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 text-sm font-normal text-gray-700"></div>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-100 opacity-50"
|
||||
onClick={() => {
|
||||
handleAddDialog(false);
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
<KeenIcon icon="cross" className="text-1.5xl" />
|
||||
</div>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<DialogBody className="scrollable-y px-0 pb-0" ref={parentRef}>
|
||||
<div className="flex flex-col px-0">
|
||||
{alert.show && (
|
||||
<Alert variant="danger" className="mb-5">
|
||||
{alert.message}
|
||||
</Alert>
|
||||
)}
|
||||
<form action="" onSubmit={handleSubmit}>
|
||||
<div className="card-body grid gap-5 p-0">
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Name<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.name}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, name: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Destination Module<span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.destination_module}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, destination_module: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-2.5 gap-5">
|
||||
<Button variant={'outline'} type="reset" onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant={'default'} type="submit">
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DialogBody>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDialog;
|
||||
57
src/pages/notification/blocks/ListToolbar.tsx
Normal file
57
src/pages/notification/blocks/ListToolbar.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useManageNotificationContext } from '../hooks/useManageNotificationContext';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const ListToolBar = () => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const { handleAddDialog } = useManageNotificationContext();
|
||||
|
||||
return (
|
||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex w-[50%] gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<KeenIcon icon="magnifier" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search users"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) =>
|
||||
table.getColumn('name')?.setFilterValue(event.target.value)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<DefaultTooltip title={'Filter'} placement={'top'}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 disabled:bg-gray-400"
|
||||
// disabled={isLoading}
|
||||
// onClick={handleFilterData}
|
||||
>
|
||||
{/* {loadingButton === 'filter' ? <ContentLoader /> : <KeenIcon icon="filter" />} */}
|
||||
<KeenIcon icon="filter" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleAddDialog(true)}
|
||||
>
|
||||
Add Data
|
||||
</Button>
|
||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
|
||||
<KeenIcon icon="arrows-circle" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ListToolBar };
|
||||
142
src/pages/notification/hooks/ManageNotificationContext.tsx
Normal file
142
src/pages/notification/hooks/ManageNotificationContext.tsx
Normal file
@ -0,0 +1,142 @@
|
||||
import { DataGridColumnHeader, DataGridProvider } from '@/components';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import React, { createContext, useCallback, useMemo, useState } from 'react';
|
||||
import { ListToolBar } from '../blocks/ListToolbar';
|
||||
|
||||
interface ContextProps {
|
||||
showEditDialog: boolean;
|
||||
handleEditDialog: (show: boolean, selected_user: string | null) => void;
|
||||
showAddDialog: boolean;
|
||||
handleAddDialog: (show: boolean) => void;
|
||||
selectedNotification: string | null;
|
||||
notifications: NotificationProps[];
|
||||
}
|
||||
|
||||
interface SelectedNotification {
|
||||
id: string;
|
||||
name: string;
|
||||
destination_module: string;
|
||||
}
|
||||
|
||||
interface NotificationProps {
|
||||
id: string;
|
||||
name: string;
|
||||
destination_module: string;
|
||||
}
|
||||
|
||||
const initialProps: ContextProps = {
|
||||
showEditDialog: false,
|
||||
showAddDialog: false,
|
||||
handleEditDialog: () => {},
|
||||
handleAddDialog: () => {},
|
||||
selectedNotification: null,
|
||||
notifications: []
|
||||
};
|
||||
|
||||
const ManageNotifContext = createContext<ContextProps>(initialProps);
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const ManageNotifContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [selectedNotification, setSelectedNotification] = useState<string | null>(null);
|
||||
const [notifications, setNotifications] = useState<NotificationProps[]>([]);
|
||||
|
||||
const handleAddDialog = useCallback((show: boolean) => {
|
||||
setShowAddDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleEditDialog = useCallback((show: boolean, selected_notification: string | null) => {
|
||||
setSelectedNotification(show ? selected_notification : null);
|
||||
setShowEditDialog(show);
|
||||
}, []);
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorFn: (row) => row.id,
|
||||
id: 'id',
|
||||
header: ({ column }) => <DataGridColumnHeader title="ID" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
id: 'name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.destination_module,
|
||||
id: 'destination_module',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Destination Module" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]',
|
||||
cellClassName: 'text-center'
|
||||
},
|
||||
cell: (data: any) => {
|
||||
const row = data.row.original;
|
||||
|
||||
return (
|
||||
<div className="flex justify-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-center gap-2 text-sm font-medium leading-6 text-primary"
|
||||
onClick={() => handleEditDialog(true, row.id)}
|
||||
>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
[handleEditDialog, handleAddDialog]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto py-5">
|
||||
<h1>Manage Notifications</h1>
|
||||
</div>
|
||||
<ManageNotifContext.Provider
|
||||
value={{
|
||||
handleAddDialog,
|
||||
showAddDialog,
|
||||
handleEditDialog,
|
||||
showEditDialog,
|
||||
selectedNotification,
|
||||
notifications
|
||||
}}
|
||||
>
|
||||
<Toaster expand visibleToasts={9} duration={3000} />
|
||||
|
||||
<DataGridProvider
|
||||
columns={columns}
|
||||
pagination={{ size: 10 }}
|
||||
toolbar={<ListToolBar />}
|
||||
layout={{ card: true }}
|
||||
sorting={[{ id: 'username', desc: false }]}
|
||||
serverSide={true}
|
||||
>
|
||||
{children}
|
||||
</DataGridProvider>
|
||||
</ManageNotifContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ManageNotifContext, ManageNotifContextProvider };
|
||||
export type { SelectedNotification };
|
||||
@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { ManageNotifContext } from './ManageNotificationContext';
|
||||
|
||||
const useManageNotificationContext = () => {
|
||||
const context = useContext(ManageNotifContext);
|
||||
|
||||
if (!context) throw new Error('useManageNotificationContext must be used within AuthProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { useManageNotificationContext };
|
||||
@ -336,6 +336,7 @@ const AddDialog = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex justify-end pt-2.5">
|
||||
<Button className="btn btn-primary" type="submit" disabled={isButtonDisabled}>
|
||||
|
||||
21
src/pages/transfer/TransferType.tsx
Normal file
21
src/pages/transfer/TransferType.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Container, DataGridInner } from '@/components';
|
||||
import {
|
||||
ManageTransferTypeContext,
|
||||
ManageTransferTypeContextProvider
|
||||
} from './hooks/ManageTransferTypeContext';
|
||||
import AddDialog from './blocks/AddDialog';
|
||||
|
||||
const TransferType = () => {
|
||||
return (
|
||||
<ManageTransferTypeContextProvider>
|
||||
<Container>
|
||||
<div className="grid gap-5 lg:gap-7.5">
|
||||
<DataGridInner />
|
||||
</div>
|
||||
<AddDialog />
|
||||
</Container>
|
||||
</ManageTransferTypeContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransferType;
|
||||
294
src/pages/transfer/blocks/AddDialog.tsx
Normal file
294
src/pages/transfer/blocks/AddDialog.tsx
Normal file
@ -0,0 +1,294 @@
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useManageTransferTypeContext } from '../hooks/useManageTransferTypeContext';
|
||||
import { Alert, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import {
|
||||
Dialog,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
interface CreateTransferTypeParams {
|
||||
transfer_type_name: string;
|
||||
description: string;
|
||||
from_account: string;
|
||||
to_account: string;
|
||||
minimal_amount: number;
|
||||
maximum_amount: number;
|
||||
otp_threshold: number;
|
||||
maximum_transaction_perDay: number;
|
||||
}
|
||||
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const AddDialog = () => {
|
||||
const parentRef = useRef<any | null>(null);
|
||||
const { showAddDialog, handleAddDialog, accounts } = useManageTransferTypeContext();
|
||||
const { reload } = useDataGrid();
|
||||
const { PostData, PutData } = useCallApi();
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
transfer_type_name: '',
|
||||
description: '',
|
||||
from_account: '',
|
||||
to_account: '',
|
||||
minimal_amount: 0,
|
||||
maximum_amount: 0,
|
||||
otp_threshold: 0,
|
||||
maximum_transaction_perDay: 0
|
||||
};
|
||||
|
||||
const [formField, setFormField] = useState(initialState);
|
||||
const resetForm = () => {
|
||||
setFormField(initialState);
|
||||
};
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
// setIsSubmitting(true);
|
||||
const payload = {
|
||||
transfer_type_name: formField.transfer_type_name,
|
||||
description: formField.description,
|
||||
from_account: formField.from_account,
|
||||
to_account: formField.to_account,
|
||||
minimal_amount: formField.minimal_amount,
|
||||
maximum_amount: formField.maximum_amount,
|
||||
otp_threshold: formField.otp_threshold,
|
||||
maximum_transaction_perDay: formField.maximum_transaction_perDay
|
||||
};
|
||||
console.log(payload);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={showAddDialog} onOpenChange={(open) => handleAddDialog(open)}>
|
||||
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden [&>button]:hidden">
|
||||
<DialogHeader className="p-5 border-0">
|
||||
<div className="flex items-center justify-between flex-wrap grow">
|
||||
<div className="flex flex-col justify-center">
|
||||
<h1 className="text-xl font-semibold leading-none text-gray-900">
|
||||
Create Transfer Type
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 text-sm font-normal text-gray-700"></div>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer hover:opacity-100 opacity-50"
|
||||
onClick={() => {
|
||||
handleAddDialog(false);
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
<KeenIcon icon="cross" className="text-1.5xl" />
|
||||
</div>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<DialogBody className="scrollable-y px-0 pb-0" ref={parentRef}>
|
||||
<div className="flex flex-col px-0">
|
||||
{alert.show && (
|
||||
<Alert variant="danger" className="mb-3">
|
||||
<h3>{alert.message}</h3>
|
||||
</Alert>
|
||||
)}
|
||||
<form action="" onSubmit={handleSubmit}>
|
||||
<div className="card-body grid gap-5 p-0">
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Transfer Type Name
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.transfer_type_name}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, transfer_type_name: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Description
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
value={formField.description}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({ ...prev, description: target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-center flex-wrap gap-2.5">
|
||||
<label className="form-label max-w-56">From Account</label>
|
||||
|
||||
<div className="grow">
|
||||
<Select
|
||||
value={formField.from_account}
|
||||
onValueChange={(target) =>
|
||||
setFormField((prev) => ({ ...prev, from_account: target }))
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{accounts.map((account, idx) => (
|
||||
<SelectItem value={account.name} key={account.id}>
|
||||
{account.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-center flex-wrap gap-2.5">
|
||||
<label className="form-label max-w-56">To Account</label>
|
||||
|
||||
<div className="grow">
|
||||
<Select
|
||||
value={formField.to_account}
|
||||
onValueChange={(target) =>
|
||||
setFormField((prev) => ({ ...prev, to_account: target }))
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{accounts.map((account, idx) => (
|
||||
<SelectItem value={account.name} key={account.id}>
|
||||
{account.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Minimal Amount
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
value={formField.minimal_amount}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({
|
||||
...prev,
|
||||
minimal_amount: Number(target.value)
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Maximum Amount
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
value={formField.maximum_amount}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({
|
||||
...prev,
|
||||
maximum_amount: Number(target.value)
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
OTP Threshold
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
value={formField.otp_threshold}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({
|
||||
...prev,
|
||||
otp_threshold: Number(target.value)
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Maximum Transaction / day
|
||||
</label>
|
||||
<Input
|
||||
className="input"
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
value={formField.maximum_transaction_perDay}
|
||||
onChange={({ target }) =>
|
||||
setFormField((prev) => ({
|
||||
...prev,
|
||||
maximum_transaction_perDay: Number(target.value)
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-2.5 gap-5">
|
||||
<Button variant={'outline'} type="reset">
|
||||
Reset
|
||||
</Button>
|
||||
<Button variant={'default'} type="submit">
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DialogBody>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddDialog;
|
||||
49
src/pages/transfer/blocks/ListToolBar.tsx
Normal file
49
src/pages/transfer/blocks/ListToolBar.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components';
|
||||
import { useManageTransferTypeContext } from '../hooks/useManageTransferTypeContext';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const ListToolbar = () => {
|
||||
const { table, reload } = useDataGrid();
|
||||
const { handleAddDialog, handleEditDialog } = useManageTransferTypeContext();
|
||||
|
||||
return (
|
||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex w-[50%] gap-3 items-center">
|
||||
<label className="input input-sm w-1/3">
|
||||
<KeenIcon icon="magnifier" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Access Type"
|
||||
value={(table.getColumn('name')?.getFilterValue() as string) ?? ''}
|
||||
onChange={(event) => table.getColumn('name')?.setFilterValue(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<DefaultTooltip title={'Filter'} placement={'top'}>
|
||||
<Button variant={'outline'} className="h-7.5 disabled:bg-gray-400">
|
||||
<KeenIcon icon="filter" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-7.5 text-[0.8rem]"
|
||||
onClick={() => handleAddDialog(true)}
|
||||
>
|
||||
Add Data
|
||||
</Button>
|
||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||
<Button variant="outline" className="h-7.5" onClick={() => reload()}>
|
||||
<KeenIcon icon="arrows-circle" />
|
||||
</Button>
|
||||
</DefaultTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListToolbar;
|
||||
178
src/pages/transfer/hooks/ManageTransferTypeContext.tsx
Normal file
178
src/pages/transfer/hooks/ManageTransferTypeContext.tsx
Normal file
@ -0,0 +1,178 @@
|
||||
import { DataGridColumnHeader, DataGridProvider } from '@/components';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import { apiConfig } from '@/config/api.config';
|
||||
import { useCallApi } from '@/hooks';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { createContext, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import ListToolbar from '../blocks/ListToolBar';
|
||||
|
||||
interface SelectedUser {
|
||||
id: string;
|
||||
name: string;
|
||||
internal_name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface AccountProps {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const accounts: string[] = [
|
||||
'eMoney Account',
|
||||
'Topup Account',
|
||||
'Merchant Account',
|
||||
'Deposit Account',
|
||||
'Cash out/in'
|
||||
];
|
||||
|
||||
interface ContextProps {
|
||||
showEditDialog: boolean;
|
||||
handleEditDialog: (show: boolean, selected_user: string | null) => void;
|
||||
showAddDialog: boolean;
|
||||
handleAddDialog: (show: boolean) => void;
|
||||
selectedUser: string | null;
|
||||
accounts: AccountProps[];
|
||||
}
|
||||
|
||||
const initialProps: ContextProps = {
|
||||
showEditDialog: false,
|
||||
handleEditDialog: () => {},
|
||||
showAddDialog: false,
|
||||
handleAddDialog: () => {},
|
||||
selectedUser: null,
|
||||
accounts: []
|
||||
};
|
||||
|
||||
const ManageTransferTypeContext = createContext<ContextProps>(initialProps);
|
||||
const API_URL = apiConfig.service_dashboard;
|
||||
|
||||
const ManageTransferTypeContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||
const [showAddDialog, setShowAddDialog] = useState(false);
|
||||
const [selectedUser, setSelectedUser] = useState<string | null>(null);
|
||||
const [accounts, setAccount] = useState<AccountProps[]>([]);
|
||||
const { GetData } = useCallApi();
|
||||
|
||||
useEffect(() => {
|
||||
setAccount([
|
||||
{ id: '1', name: 'eMoney Account' },
|
||||
{ id: '2', name: 'Topup Account' },
|
||||
{ id: '3', name: 'Merchant Account' },
|
||||
{ id: '4', name: 'Deposit Account' },
|
||||
{ id: '5', name: 'Cash in/out Account' }
|
||||
]);
|
||||
}, []);
|
||||
|
||||
const handleEditDialog = useCallback((show: boolean, selected_user: string | null) => {
|
||||
setSelectedUser(show ? selected_user : null);
|
||||
setShowEditDialog(show);
|
||||
}, []);
|
||||
|
||||
const handleAddDialog = useCallback((show: boolean) => {
|
||||
setShowAddDialog(show);
|
||||
}, []);
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorFn: (row) => row.id,
|
||||
id: 'id',
|
||||
header: ({ column }) => <DataGridColumnHeader title="ID" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.name,
|
||||
id: 'name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.internal_name,
|
||||
id: 'internal_name',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Internal Name" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => row.description,
|
||||
id: 'description',
|
||||
header: ({ column }) => <DataGridColumnHeader title="Description" column={column} />,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
meta: {
|
||||
headerClassName: 'w-[250px]'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableSorting: false,
|
||||
header: ({ column }) => <DataGridColumnHeader title="Actions" column={column} />,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-primary mr-2"
|
||||
onClick={() => handleEditDialog(true, row.original.id)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button className="btn btn-sm btn-primary" onClick={() => handleAddDialog(true)}>
|
||||
Add
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
},
|
||||
meta: {
|
||||
headerClassName: 'w-[100px]',
|
||||
cellClassName: 'text-center'
|
||||
}
|
||||
}
|
||||
],
|
||||
[handleAddDialog, handleEditDialog]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto py-5">
|
||||
<h1>Manage Access Type</h1>
|
||||
</div>
|
||||
<ManageTransferTypeContext.Provider
|
||||
value={{
|
||||
showEditDialog,
|
||||
handleEditDialog,
|
||||
showAddDialog,
|
||||
handleAddDialog,
|
||||
selectedUser,
|
||||
accounts
|
||||
}}
|
||||
>
|
||||
<Toaster expand visibleToasts={9} duration={3000} />
|
||||
<DataGridProvider
|
||||
columns={columns}
|
||||
pagination={{ size: 10 }}
|
||||
layout={{ card: true }}
|
||||
toolbar={<ListToolbar />}
|
||||
sorting={[{ id: 'id', desc: true }]}
|
||||
serverSide={true}
|
||||
>
|
||||
{children}
|
||||
</DataGridProvider>
|
||||
</ManageTransferTypeContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ManageTransferTypeContext, ManageTransferTypeContextProvider };
|
||||
export type { SelectedUser };
|
||||
12
src/pages/transfer/hooks/useManageTransferTypeContext.tsx
Normal file
12
src/pages/transfer/hooks/useManageTransferTypeContext.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { ManageTransferTypeContext } from './ManageTransferTypeContext';
|
||||
|
||||
const useManageTransferTypeContext = () => {
|
||||
const context = useContext(ManageTransferTypeContext);
|
||||
|
||||
if (!context) throw new Error('useManageAccessTypeContext must be used within AuthProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export { useManageTransferTypeContext };
|
||||
11
src/pages/webservice/ManageWebServices.tsx
Normal file
11
src/pages/webservice/ManageWebServices.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
const ManageWebServices = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container mx-auto p-5">
|
||||
<h1 className="text-xl font-medium leading-none text-gray-900">Manage Web Services</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageWebServices;
|
||||
@ -11,6 +11,25 @@ import ManageUserPage from '@/pages/settings/user/manage-user/ManageUserPage';
|
||||
|
||||
import LogActivityPage from '@/pages/settings/user/log-activity/LogActivityPage';
|
||||
import ManagePositionPage from '@/pages/settings/user/manage-position/ManagePositionPage';
|
||||
import ManageAccount from '@/pages/account/manage-account/ManageAccount';
|
||||
import ManageGroups from '@/pages/groups/ManageGroups';
|
||||
import ManageMembers from '@/pages/members/manage-members/ManageMembers';
|
||||
import Kyc from '@/pages/members/kyc/Kyc';
|
||||
import AccessType from '@/pages/access/access-type/AccessType';
|
||||
import MemberCredential from '@/pages/access/member-credentials/MemberCredentials';
|
||||
import ManageCurrency from '@/pages/account/manage-currency/ManageCurrency';
|
||||
import ManageNotification from '@/pages/notification/ManageNotification';
|
||||
import MenuCategory from '@/pages/menu/menu-category/MenuCategory';
|
||||
import ManageMenu from '@/pages/menu/manage-menu/ManageMenu';
|
||||
import Welcome from '@/pages/menu/welcome/Welcome';
|
||||
import Inbox from '@/pages/message/Inbox';
|
||||
import ManageWebServices from '@/pages/webservice/ManageWebServices';
|
||||
import TransferType from '@/pages/transfer/TransferType';
|
||||
import MasterData from '@/pages/master/MasterData';
|
||||
import PostoAdmsMaster from '@/pages/master/postoadms/PostoAdmsMaster';
|
||||
import SucosMaster from '@/pages/master/sucos/SucosMaster';
|
||||
import AldeiasMaster from '@/pages/master/aldeias/AldeiasMaster';
|
||||
import Municipios from '@/pages/master/municipios/Municipios';
|
||||
|
||||
const AppRoutingSetup = (): ReactElement => {
|
||||
return (
|
||||
@ -18,8 +37,40 @@ const AppRoutingSetup = (): ReactElement => {
|
||||
<Route element={<RequireAuth />}>
|
||||
<Route element={<Demo2Layout />}>
|
||||
<Route path="/" element={<DashboardHomePage />} />
|
||||
|
||||
<Route path="/master-data" element={<MasterData />} />
|
||||
<Route path="/master-data/municipios" element={<Municipios />} />
|
||||
<Route path="/master-data/postoadms" element={<PostoAdmsMaster />} />
|
||||
<Route path="/master-data/sucos" element={<SucosMaster />} />
|
||||
<Route path="/master-data/aldeias" element={<AldeiasMaster />} />
|
||||
|
||||
<Route path="/master-data/municipios/postoadms/:municipioId" element={<PostoAdmsMaster />} />
|
||||
|
||||
<Route path="/account/home/user-profile" element={<AccountUserProfilePage />} />
|
||||
|
||||
<Route path="/groups/group-management" element={<ManageGroups />} />
|
||||
|
||||
<Route path="/members/kyc" element={<Kyc />} />
|
||||
<Route path="/members/member-management" element={<ManageMembers />} />
|
||||
|
||||
<Route path="/access/access-type-management" element={<AccessType />} />
|
||||
<Route path="/members/create-member-credential" element={<MemberCredential />} />
|
||||
|
||||
<Route path="/accounts/account-management" element={<ManageAccount />} />
|
||||
<Route path="/accounts/currency-management" element={<ManageCurrency />} />
|
||||
|
||||
<Route path="/transfer-type/transfer-type-management" element={<TransferType />} />
|
||||
|
||||
<Route path="/notification/notification-management" element={<ManageNotification />} />
|
||||
|
||||
<Route path="/menu/menu-category" element={<MenuCategory />} />
|
||||
<Route path="/menu/menu-management" element={<ManageMenu />} />
|
||||
<Route path="/menu/welcome" element={<Welcome />} />
|
||||
|
||||
<Route path="/message/inbox" element={<Inbox />} />
|
||||
|
||||
<Route path="/webservice/webservice-management" element={<ManageWebServices />} />
|
||||
|
||||
<Route path="/settings/user-management/manage-user" element={<ManageUserPage />} />
|
||||
<Route path="/settings/user-management/log-activity" element={<LogActivityPage />} />
|
||||
<Route
|
||||
|
||||
Reference in New Issue
Block a user